diff --git a/cmd/acceptguard/report.go b/cmd/acceptguard/report.go
index c82d902d5f..2b6c6e50d4 100644
--- a/cmd/acceptguard/report.go
+++ b/cmd/acceptguard/report.go
@@ -1,9 +1,12 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"os"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func writeJSON(path string, findings []finding) error {
@@ -13,10 +16,9 @@ func writeJSON(path string, findings []finding) error {
}
defer f.Close()
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- return enc.Encode(findings)
+ return json.MarshalEncode(enc, findings)
}
func printReport(findings []finding) {
diff --git a/cmd/bdaudit/issues.go b/cmd/bdaudit/issues.go
index 90a478ad36..fd1108e3cc 100644
--- a/cmd/bdaudit/issues.go
+++ b/cmd/bdaudit/issues.go
@@ -2,10 +2,12 @@ package main
import (
"bufio"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"os"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const statusClosed = "closed"
@@ -61,7 +63,7 @@ func loadIssues(path string) (map[string]issue, error) {
}
var iss issue
- if unmarshalErr := json.Unmarshal([]byte(text), &iss); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal([]byte(text), &iss, jsonx.V1()); unmarshalErr != nil {
return nil, fmt.Errorf("%s:%d: %w", path, line, unmarshalErr)
}
if iss.ID == "" {
diff --git a/cmd/bdaudit/report.go b/cmd/bdaudit/report.go
index 416fab8122..4a11afecde 100644
--- a/cmd/bdaudit/report.go
+++ b/cmd/bdaudit/report.go
@@ -1,11 +1,14 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"os"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type report struct {
@@ -23,10 +26,9 @@ func writeJSONReport(path string, rep report) error {
}
defer f.Close()
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- if encErr := enc.Encode(rep); encErr != nil {
+ if encErr := json.MarshalEncode(enc, rep); encErr != nil {
return fmt.Errorf("encode json: %w", encErr)
}
diff --git a/cmd/bodyclass/report.go b/cmd/bodyclass/report.go
index b188fb33f1..5af8390219 100644
--- a/cmd/bodyclass/report.go
+++ b/cmd/bodyclass/report.go
@@ -1,10 +1,13 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"os"
"sort"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type serviceReport struct {
@@ -68,10 +71,9 @@ func writeJSON(path string, results []serviceReport) {
}
defer f.Close()
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- if encErr := enc.Encode(results); encErr != nil {
+ if encErr := json.MarshalEncode(enc, results); encErr != nil {
fatal(encErr)
}
diff --git a/cmd/cfnattrgen/main.go b/cmd/cfnattrgen/main.go
index a4635c83b2..0742bd56f5 100644
--- a/cmd/cfnattrgen/main.go
+++ b/cmd/cfnattrgen/main.go
@@ -26,7 +26,8 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"flag"
"fmt"
"go/ast"
@@ -37,10 +38,12 @@ import (
"sort"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type resourceTypeSpec struct {
- Attributes map[string]json.RawMessage `json:"Attributes"`
+ Attributes map[string]jsontext.Value `json:"Attributes"`
}
type resourceSpec struct {
@@ -158,15 +161,15 @@ func runTrimSpec(specPath, outPath string) error {
continue
}
- attrs := make(map[string]json.RawMessage, len(rt.Attributes))
+ attrs := make(map[string]jsontext.Value, len(rt.Attributes))
for attr := range rt.Attributes {
- attrs[attr] = json.RawMessage("{}")
+ attrs[attr] = jsontext.Value("{}")
}
trimmed.ResourceTypes[name] = resourceTypeSpec{Attributes: attrs}
}
- out, err := json.MarshalIndent(trimmed, "", " ")
+ out, err := json.Marshal(trimmed, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
if err != nil {
return fmt.Errorf("marshal trimmed spec: %w", err)
}
@@ -187,7 +190,7 @@ func loadSpec(path string) (*resourceSpec, error) {
}
var spec resourceSpec
- if unmarshalErr := json.Unmarshal(data, &spec); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(data, &spec, jsonx.V1()); unmarshalErr != nil {
return nil, unmarshalErr
}
@@ -295,7 +298,7 @@ func buildTable(spec *resourceSpec, supportedTypes map[string]struct{}) map[stri
}
func renderJSON(table map[string][]string) ([]byte, error) {
- out, err := json.MarshalIndent(table, "", " ")
+ out, err := json.Marshal(table, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
if err != nil {
return nil, err
}
@@ -359,7 +362,7 @@ func buildServices(spec *resourceSpec, literals map[string]struct{}) []string {
const minTypeNameParts = 3
func renderServices(services []string) ([]byte, error) {
- out, err := json.MarshalIndent(services, "", " ")
+ out, err := json.Marshal(services, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
if err != nil {
return nil, err
}
diff --git a/cmd/cfnattrgen/regen_test.go b/cmd/cfnattrgen/regen_test.go
index 34f33b3e74..8d788dc1b8 100644
--- a/cmd/cfnattrgen/regen_test.go
+++ b/cmd/cfnattrgen/regen_test.go
@@ -1,12 +1,14 @@
package main
import (
- "encoding/json"
+ "encoding/json/v2"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -90,7 +92,7 @@ func TestRenderJSON_Deterministic(t *testing.T) {
assert.Equal(t, first, second)
var roundTrip map[string][]string
- require.NoError(t, json.Unmarshal(first, &roundTrip))
+ require.NoError(t, json.Unmarshal(first, &roundTrip, jsonx.V1()))
assert.Equal(t, table, roundTrip)
}
diff --git a/cmd/cfnpropgen/main.go b/cmd/cfnpropgen/main.go
index 59e8584569..fc0546e135 100644
--- a/cmd/cfnpropgen/main.go
+++ b/cmd/cfnpropgen/main.go
@@ -8,11 +8,14 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"flag"
"fmt"
"os"
"slices"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type propertySpec struct {
@@ -53,7 +56,7 @@ func readJSON(path string, v any) error {
return err
}
- return json.Unmarshal(data, v)
+ return json.Unmarshal(data, v, jsonx.V1())
}
func run(specPath, typesPath, outPath, reqPath string) error {
@@ -67,7 +70,12 @@ func run(specPath, typesPath, outPath, reqPath string) error {
return fmt.Errorf("load types: %w", err)
}
- out, err := json.MarshalIndent(buildSchemas(&spec, types), "", " ")
+ out, err := json.Marshal(
+ buildSchemas(&spec, types),
+ jsonx.V1(),
+ jsontext.Multiline(true),
+ jsontext.WithIndent(" "),
+ )
if err != nil {
return fmt.Errorf("render: %w", err)
}
@@ -80,7 +88,12 @@ func run(specPath, typesPath, outPath, reqPath string) error {
return nil
}
- req, err := json.MarshalIndent(buildRequired(&spec, types), "", " ")
+ req, err := json.Marshal(
+ buildRequired(&spec, types),
+ jsonx.V1(),
+ jsontext.Multiline(true),
+ jsontext.WithIndent(" "),
+ )
if err != nil {
return fmt.Errorf("render required: %w", err)
}
diff --git a/cmd/clientcoverage/main.go b/cmd/clientcoverage/main.go
index aa8c7d7863..479738cd11 100644
--- a/cmd/clientcoverage/main.go
+++ b/cmd/clientcoverage/main.go
@@ -36,7 +36,8 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"flag"
"fmt"
"go/ast"
@@ -49,6 +50,8 @@ import (
"regexp"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -650,7 +653,7 @@ func loadOpcensus(path string) ([]opService, error) {
}
var services []opService
- if unmarshalErr := json.Unmarshal(data, &services); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(data, &services, jsonx.V1()); unmarshalErr != nil {
return nil, fmt.Errorf("parse %s: %w", path, unmarshalErr)
}
@@ -692,7 +695,7 @@ func main() {
rpt := buildReport(services, result)
if *jsonPath != "" {
- data, marshalErr := json.MarshalIndent(rpt, "", " ")
+ data, marshalErr := json.Marshal(rpt, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
if marshalErr != nil {
fmt.Fprintln(os.Stderr, "marshal report:", marshalErr)
os.Exit(1)
diff --git a/cmd/enumcheck/report.go b/cmd/enumcheck/report.go
index aaad7c39e6..7d692353fb 100644
--- a/cmd/enumcheck/report.go
+++ b/cmd/enumcheck/report.go
@@ -1,9 +1,12 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"os"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func writeJSON(path string, findings []finding) error {
@@ -12,10 +15,9 @@ func writeJSON(path string, findings []finding) error {
return err
}
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- if encErr := enc.Encode(findings); encErr != nil {
+ if encErr := json.MarshalEncode(enc, findings); encErr != nil {
_ = f.Close()
return encErr
diff --git a/cmd/errcodeaudit/report.go b/cmd/errcodeaudit/report.go
index 8d564cd3c2..ea37dc3a9e 100644
--- a/cmd/errcodeaudit/report.go
+++ b/cmd/errcodeaudit/report.go
@@ -1,9 +1,12 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"os"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func writeJSON(path string, findings []finding) error {
@@ -13,10 +16,9 @@ func writeJSON(path string, findings []finding) error {
}
defer f.Close()
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- return enc.Encode(findings)
+ return json.MarshalEncode(enc, findings)
}
func printReport(findings []finding) {
diff --git a/cmd/errtargetaudit/report.go b/cmd/errtargetaudit/report.go
index 6d8f01b33a..6e072770b2 100644
--- a/cmd/errtargetaudit/report.go
+++ b/cmd/errtargetaudit/report.go
@@ -1,11 +1,14 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"os"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// lowResolutionThreshold gates the implausible-resolution guard --
@@ -211,10 +214,9 @@ func writeJSON(path string, scans []serviceScan) error {
}
defer f.Close()
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- return enc.Encode(scans)
+ return json.MarshalEncode(enc, scans)
}
func printServiceScan(sr serviceScan) {
diff --git a/cmd/jsonv2mod/main.go b/cmd/jsonv2mod/main.go
new file mode 100644
index 0000000000..2bfff044be
--- /dev/null
+++ b/cmd/jsonv2mod/main.go
@@ -0,0 +1,141 @@
+// Command jsonv2mod rewrites encoding/json (v1) call sites to
+// encoding/json/v2 plus jsontext, passing jsonx.V1() so behavior is unchanged.
+//
+// Usage:
+//
+// go run ./cmd/jsonv2mod [-n] [-skip frag,...] path...
+//
+// Each path is a Go file or a directory walked recursively. Files are
+// rewritten in place (-n lists them instead). Constructs that cannot be
+// rewritten safely are left untouched and printed as file:line: reason; the
+// exit status is 1 when any are reported. See pkgs/jsonx for the mapping.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+const (
+ exitLeftovers = 1
+ exitFailure = 2
+ fileMode = 0o600
+)
+
+type options struct {
+ out, errOut io.Writer
+ skips []string
+ dry bool
+}
+
+func main() {
+ dry := flag.Bool("n", false, "list files that would change without writing")
+ skip := flag.String("skip", "", "comma-separated path fragments to skip")
+ flag.Parse()
+
+ opts := options{out: os.Stdout, errOut: os.Stderr, skips: strings.Split(*skip, ","), dry: *dry}
+
+ sum, err := opts.run(flag.Args())
+ if err != nil {
+ fmt.Fprintln(os.Stderr, "jsonv2mod:", err)
+ os.Exit(exitFailure)
+ }
+
+ fmt.Fprintf(os.Stderr, "jsonv2mod: %d files changed, %d leftovers\n", sum.changed, sum.leftovers)
+
+ if sum.leftovers > 0 {
+ os.Exit(exitLeftovers)
+ }
+}
+
+type summary struct{ changed, leftovers int }
+
+func (o options) run(roots []string) (summary, error) {
+ var (
+ files []string
+ sum summary
+ )
+
+ for _, root := range roots {
+ walkErr := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
+ if err == nil && !d.IsDir() && strings.HasSuffix(path, ".go") && !skipped(path, o.skips) {
+ files = append(files, path)
+ }
+
+ return err
+ })
+ if walkErr != nil {
+ return sum, walkErr
+ }
+ }
+
+ for _, path := range files {
+ one, err := o.process(path)
+ if err != nil {
+ return sum, err
+ }
+
+ sum.changed += one.changed
+ sum.leftovers += one.leftovers
+ }
+
+ return sum, nil
+}
+
+func (o options) process(path string) (summary, error) {
+ var sum summary
+
+ res, err := processFile(path)
+ if err != nil {
+ return sum, err
+ }
+
+ for _, f := range res.findings {
+ fmt.Fprintf(o.errOut, "%s:%d: %s\n", path, f.line, f.msg)
+
+ sum.leftovers++
+ }
+
+ if res.out == nil {
+ return sum, nil
+ }
+
+ sum.changed++
+
+ fmt.Fprintln(o.out, path)
+
+ if o.dry {
+ return sum, nil
+ }
+
+ return sum, os.WriteFile(path, res.out, fileMode)
+}
+
+func skipped(path string, skips []string) bool {
+ slash := filepath.ToSlash(path)
+ if strings.Contains(slash, "pkgs/jsonx/") || strings.Contains(slash, "cmd/jsonv2mod/") {
+ return true
+ }
+
+ for _, s := range skips {
+ if s != "" && strings.Contains(slash, s) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func processFile(path string) (*result, error) {
+ src, err := os.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+
+ return rewrite(path, src)
+}
diff --git a/cmd/jsonv2mod/rewrite.go b/cmd/jsonv2mod/rewrite.go
new file mode 100644
index 0000000000..80e77dc8ad
--- /dev/null
+++ b/cmd/jsonv2mod/rewrite.go
@@ -0,0 +1,653 @@
+package main
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "go/ast"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+const (
+ v1Path = "encoding/json"
+ v2Path = "encoding/json/v2"
+ jsontextPath = "encoding/json/jsontext"
+ jsonxPath = "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+ localPrefix = "github.com/blackbirdworks/gopherstack"
+ optV1 = "jsonx.V1()"
+)
+
+var errOverlap = errors.New("overlapping edits")
+
+type finding struct {
+ msg string
+ line int
+}
+
+type result struct {
+ out []byte
+ findings []finding
+}
+
+type edit struct {
+ text string
+ start, end int
+}
+
+type stream struct {
+ call *ast.CallExpr
+ block *ast.BlockStmt
+ name string
+ opts []string
+ edits []edit
+ decodes []*ast.CallExpr
+ decoder bool
+ bad bool
+}
+
+type rewriter struct {
+ fset *token.FileSet
+ file *ast.File
+ src []byte
+ jsonName string
+ consumed map[ast.Node]bool
+ streams map[string]*stream
+ edits []edit
+ findings []finding
+}
+
+func (r *rewriter) off(p token.Pos) int { return r.fset.Position(p).Offset }
+
+func (r *rewriter) text(a, b ast.Node) string { return string(r.src[r.off(a.Pos()):r.off(b.End())]) }
+
+func (r *rewriter) add(start, end int, text string) {
+ r.edits = append(r.edits, edit{start: start, end: end, text: text})
+}
+
+func (r *rewriter) reportf(p token.Pos, format string, a ...any) {
+ r.findings = append(r.findings, finding{line: r.fset.Position(p).Line, msg: fmt.Sprintf(format, a...)})
+}
+
+func (r *rewriter) isJSON(e ast.Expr, name string) bool {
+ sel, ok := e.(*ast.SelectorExpr)
+ if !ok {
+ return false
+ }
+
+ id, ok := sel.X.(*ast.Ident)
+
+ return ok && id.Name == r.jsonName && (name == "" || sel.Sel.Name == name)
+}
+
+func (r *rewriter) hasComment(start, end int) bool {
+ for _, cg := range r.file.Comments {
+ if r.off(cg.Pos()) < end && r.off(cg.End()) > start {
+ return true
+ }
+ }
+
+ return false
+}
+
+func rewrite(path string, src []byte) (*result, error) {
+ fset := token.NewFileSet()
+
+ file, err := parser.ParseFile(fset, path, src, parser.ParseComments)
+ if err != nil {
+ return nil, err
+ }
+
+ name, ok := jsonImportName(file)
+ if !ok {
+ return &result{}, nil
+ }
+
+ r := &rewriter{fset: fset, file: file, src: src, jsonName: name, consumed: map[ast.Node]bool{}}
+
+ for _, d := range file.Decls {
+ r.streams = map[string]*stream{}
+ r.collectStreams(d)
+ r.rewriteStreamUses(d)
+ r.rewriteCalls(d)
+ r.rewriteSelectors(d)
+ r.emitStreams()
+ }
+
+ out, err := r.apply()
+ if err != nil {
+ return nil, fmt.Errorf("%s: %w", path, err)
+ }
+
+ out, err = fixImports(path, out, name)
+ if err != nil {
+ return nil, fmt.Errorf("%s: %w", path, err)
+ }
+
+ res := &result{findings: r.findings}
+ if !bytes.Equal(out, src) {
+ res.out = out
+ }
+
+ return res, nil
+}
+
+func jsonImportName(file *ast.File) (string, bool) {
+ for _, imp := range file.Imports {
+ if p, _ := strconv.Unquote(imp.Path.Value); p != v1Path {
+ continue
+ }
+
+ if imp.Name == nil {
+ return "json", true
+ }
+
+ if imp.Name.Name == "_" || imp.Name.Name == "." {
+ return "", false
+ }
+
+ return imp.Name.Name, true
+ }
+
+ return "", false
+}
+
+func (r *rewriter) collectStreams(d ast.Decl) {
+ register := func(lhs ast.Expr, rhs ast.Expr, block *ast.BlockStmt) {
+ id, ok := lhs.(*ast.Ident)
+ call, isCall := rhs.(*ast.CallExpr)
+
+ if !ok || !isCall || len(call.Args) != 1 {
+ return
+ }
+
+ switch {
+ case r.isJSON(call.Fun, "NewDecoder"):
+ r.streams[id.Name] = &stream{name: id.Name, call: call, decoder: true, block: block}
+ case r.isJSON(call.Fun, "NewEncoder"):
+ r.streams[id.Name] = &stream{name: id.Name, call: call, block: block}
+ default:
+ return
+ }
+
+ r.consumed[call.Fun] = true
+ }
+
+ ast.Inspect(d, func(n ast.Node) bool {
+ switch x := n.(type) {
+ case *ast.AssignStmt:
+ if len(x.Lhs) == 1 && len(x.Rhs) == 1 {
+ register(x.Lhs[0], x.Rhs[0], enclosing(d, x))
+ }
+ case *ast.ValueSpec:
+ if len(x.Names) == 1 && len(x.Values) == 1 {
+ register(x.Names[0], x.Values[0], enclosing(d, x))
+ }
+ }
+
+ return true
+ })
+}
+
+// enclosing returns the innermost block statement of d containing n.
+func enclosing(d ast.Decl, n ast.Node) *ast.BlockStmt {
+ var best *ast.BlockStmt
+
+ ast.Inspect(d, func(c ast.Node) bool {
+ if b, ok := c.(*ast.BlockStmt); ok && b.Pos() <= n.Pos() && n.End() <= b.End() {
+ best = b
+ }
+
+ return true
+ })
+
+ return best
+}
+
+func (r *rewriter) declIdents(d ast.Decl) map[*ast.Ident]bool {
+ out := map[*ast.Ident]bool{}
+
+ ast.Inspect(d, func(n ast.Node) bool {
+ switch x := n.(type) {
+ case *ast.AssignStmt:
+ for _, l := range x.Lhs {
+ if id, ok := l.(*ast.Ident); ok && r.streams[id.Name] != nil {
+ out[id] = true
+ }
+ }
+ case *ast.ValueSpec:
+ for _, id := range x.Names {
+ out[id] = true
+ }
+ }
+
+ return true
+ })
+
+ return out
+}
+
+func (r *rewriter) rewriteStreamUses(d ast.Decl) {
+ if len(r.streams) == 0 {
+ return
+ }
+
+ decls := r.declIdents(d)
+
+ var stack []ast.Node
+
+ ast.Inspect(d, func(n ast.Node) bool {
+ if n == nil {
+ stack = stack[:len(stack)-1]
+
+ return true
+ }
+
+ stack = append(stack, n)
+
+ if id, ok := n.(*ast.Ident); ok && !decls[id] && r.streams[id.Name] != nil {
+ r.streamUse(r.streams[id.Name], id, stack)
+ }
+
+ return true
+ })
+}
+
+// Stack offsets from the identifier at the top: selector, call, statement, block.
+const (
+ stackSel = 2
+ stackCall = 3
+ stackStmt = 4
+ stackBlock = 5
+)
+
+func (r *rewriter) streamUse(s *stream, id *ast.Ident, stack []ast.Node) {
+ n := len(stack)
+ if n < stackCall {
+ s.bad = true
+
+ return
+ }
+
+ sel, isSel := stack[n-stackSel].(*ast.SelectorExpr)
+ call, isCall := stack[n-stackCall].(*ast.CallExpr)
+
+ if !isSel || sel.X != id || !isCall || call.Fun != sel {
+ s.bad = true
+
+ r.reportf(id.Pos(), "%s escapes (not a plain method call); rewrite by hand", s.name)
+
+ return
+ }
+
+ var stmt *ast.ExprStmt
+
+ if n >= stackBlock && stack[n-stackBlock] == ast.Node(s.block) {
+ stmt, _ = stack[n-stackStmt].(*ast.ExprStmt)
+ }
+
+ var handled bool
+ if s.decoder {
+ handled = r.decoderMethod(s, call, sel.Sel.Name, stmt)
+ } else {
+ handled = r.encoderMethod(s, call, sel.Sel.Name, stmt)
+ }
+
+ if !handled {
+ s.bad = true
+
+ r.reportf(call.Pos(), "%s.%s has no mechanical v2 equivalent; rewrite by hand", s.name, sel.Sel.Name)
+ }
+}
+
+func (r *rewriter) decoderMethod(s *stream, call *ast.CallExpr, method string, stmt *ast.ExprStmt) bool {
+ argc := len(call.Args)
+
+ switch {
+ case method == "Decode" && argc == 1:
+ s.decodes = append(s.decodes, call)
+ case method == "UseNumber" && argc == 0 && stmt != nil:
+ s.opts = append(s.opts, "jsonx.UseNumber()")
+ s.edits = append(s.edits, r.stmtEdit(stmt))
+ case method == "DisallowUnknownFields" && argc == 0 && stmt != nil:
+ s.opts = append(s.opts, r.jsonName+".RejectUnknownMembers(true)")
+ s.edits = append(s.edits, r.stmtEdit(stmt))
+ default:
+ return false
+ }
+
+ return true
+}
+
+func (r *rewriter) encoderMethod(s *stream, call *ast.CallExpr, method string, stmt *ast.ExprStmt) bool {
+ argc := len(call.Args)
+
+ switch {
+ case method == "Encode" && argc == 1:
+ s.edits = append(s.edits, edit{
+ start: r.off(call.Pos()), end: r.off(call.Args[0].Pos()),
+ text: r.jsonName + ".MarshalEncode(" + s.name + ", ",
+ })
+ case method == "SetEscapeHTML" && argc == 1 && stmt != nil:
+ s.opts = append(s.opts, "jsontext.EscapeForHTML("+r.text(call.Args[0], call.Args[0])+")")
+ s.edits = append(s.edits, r.stmtEdit(stmt))
+ case method == "SetIndent" && argc == 2 && stmt != nil:
+ s.opts = append(s.opts, r.indentOpts(call.Args[0], call.Args[1])...)
+ s.edits = append(s.edits, r.stmtEdit(stmt))
+ default:
+ return false
+ }
+
+ return true
+}
+
+func (r *rewriter) indentOpts(prefix, indent ast.Expr) []string {
+ opts := []string{"jsontext.Multiline(true)"}
+
+ if lit, ok := prefix.(*ast.BasicLit); !ok || lit.Value != `""` {
+ opts = append(opts, "jsontext.WithIndentPrefix("+r.text(prefix, prefix)+")")
+ }
+
+ if lit, ok := indent.(*ast.BasicLit); !ok || lit.Value != `""` {
+ opts = append(opts, "jsontext.WithIndent("+r.text(indent, indent)+")")
+ }
+
+ return opts
+}
+
+func (r *rewriter) stmtEdit(s *ast.ExprStmt) edit {
+ start, end := r.off(s.Pos()), r.off(s.End())
+
+ for start > 0 && (r.src[start-1] == ' ' || r.src[start-1] == '\t') {
+ start--
+ }
+
+ if end < len(r.src) && r.src[end] == '\n' {
+ end++
+ }
+
+ return edit{start: start, end: end}
+}
+
+func (r *rewriter) emitStreams() {
+ for _, s := range r.streams {
+ fun, ok := s.call.Fun.(*ast.SelectorExpr)
+ if s.bad || !ok {
+ continue
+ }
+
+ r.edits = append(r.edits, s.edits...)
+
+ opts := ", " + optV1
+ if len(s.opts) > 0 {
+ opts += ", " + strings.Join(s.opts, ", ")
+ }
+
+ if !s.decoder {
+ r.add(r.off(fun.Pos()), r.off(fun.End()), "jsontext.NewEncoder")
+ end := r.off(s.call.Args[0].End())
+ r.add(end, end, opts)
+
+ continue
+ }
+
+ r.add(r.off(fun.Pos()), r.off(fun.End()), "jsontext.NewDecoder")
+
+ for _, c := range s.decodes {
+ r.add(r.off(c.Pos()), r.off(c.Args[0].Pos()), r.jsonName+".UnmarshalDecode("+s.name+", ")
+ r.add(r.off(c.Args[0].End()), r.off(c.End()), opts+")")
+ }
+ }
+}
+
+func (r *rewriter) rewriteCalls(d ast.Decl) {
+ ast.Inspect(d, func(n ast.Node) bool {
+ call, ok := n.(*ast.CallExpr)
+ if !ok {
+ return true
+ }
+
+ r.callExpr(call)
+
+ return true
+ })
+}
+
+func (r *rewriter) callExpr(call *ast.CallExpr) {
+ argc := len(call.Args)
+
+ switch {
+ case r.isJSON(call.Fun, "Marshal") && argc == 1, r.isJSON(call.Fun, "Unmarshal") && argc == 2:
+ r.consumed[call.Fun] = true
+ end := r.off(call.Args[len(call.Args)-1].End())
+ r.add(end, end, ", "+optV1)
+ case r.isJSON(call.Fun, "MarshalIndent") && argc == 3:
+ r.marshalIndent(call)
+ case r.isJSON(call.Fun, "Valid") && argc == 1:
+ r.consumed[call.Fun] = true
+ r.add(r.off(call.Pos()), r.off(call.Args[0].Pos()), "jsontext.Value(")
+ r.add(r.off(call.Args[0].End()), r.off(call.End()), ").IsValid("+optV1+")")
+ default:
+ r.chain(call)
+ }
+}
+
+func (r *rewriter) marshalIndent(call *ast.CallExpr) {
+ for _, arg := range call.Args[1:] {
+ lit, isLit := arg.(*ast.BasicLit)
+ if !isLit {
+ continue
+ }
+
+ if s, err := strconv.Unquote(lit.Value); err == nil && strings.Trim(s, " \t\r\n") != "" {
+ r.reportf(call.Pos(), "MarshalIndent with non-whitespace indent %s; v2 panics, rewrite by hand", lit.Value)
+
+ return
+ }
+ }
+
+ sel, ok := call.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+
+ r.consumed[call.Fun] = true
+ r.add(r.off(sel.Sel.Pos()), r.off(sel.Sel.End()), "Marshal")
+
+ opts := append([]string{optV1}, r.indentOpts(call.Args[1], call.Args[2])...)
+ r.add(r.off(call.Args[0].End()), r.off(call.Args[2].End()), ", "+strings.Join(opts, ", "))
+}
+
+func (r *rewriter) chain(call *ast.CallExpr) {
+ outer, ok := call.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+
+ inner, ok := outer.X.(*ast.CallExpr)
+ if !ok || len(inner.Args) != 1 || len(call.Args) != 1 {
+ return
+ }
+
+ start, end := r.off(inner.Args[0].End()), r.off(call.Args[0].Pos())
+
+ switch {
+ case r.isJSON(inner.Fun, "NewDecoder") && outer.Sel.Name == "Decode":
+ if r.hasComment(r.off(call.Pos()), r.off(call.End())) {
+ r.reportf(call.Pos(), "comment inside NewDecoder(...).Decode chain; rewrite by hand")
+
+ return
+ }
+
+ r.consumed[inner.Fun] = true
+ r.add(r.off(call.Pos()), r.off(inner.Args[0].Pos()), r.jsonName+".UnmarshalDecode(jsontext.NewDecoder(")
+ r.add(start, end, "), ")
+ r.add(r.off(call.Args[0].End()), r.off(call.End()), ", "+optV1+")")
+ case r.isJSON(inner.Fun, "NewEncoder") && outer.Sel.Name == "Encode":
+ if r.hasComment(r.off(call.Pos()), r.off(call.End())) {
+ r.reportf(call.Pos(), "comment inside NewEncoder(...).Encode chain; rewrite by hand")
+
+ return
+ }
+
+ r.consumed[inner.Fun] = true
+ r.add(r.off(call.Pos()), r.off(inner.Args[0].Pos()), r.jsonName+".MarshalEncode(jsontext.NewEncoder(")
+ r.add(start, end, ", "+optV1+"), ")
+ r.add(r.off(call.Args[0].End()), r.off(call.End()), ")")
+ }
+}
+
+func (r *rewriter) rewriteSelectors(d ast.Decl) {
+ ast.Inspect(d, func(n ast.Node) bool {
+ sel, ok := n.(*ast.SelectorExpr)
+ if !ok || r.consumed[sel] || !r.isJSON(sel, "") {
+ return true
+ }
+
+ switch sel.Sel.Name {
+ case "RawMessage":
+ r.add(r.off(sel.Pos()), r.off(sel.End()), "jsontext.Value")
+ case "Number":
+ r.add(r.off(sel.Pos()), r.off(sel.End()), "jsonx.Number")
+ case "Marshaler", "Unmarshaler":
+ default:
+ r.reportf(sel.Pos(), "%s.%s has no mechanical v2 equivalent; rewrite by hand", r.jsonName, sel.Sel.Name)
+ }
+
+ return true
+ })
+}
+
+func (r *rewriter) apply() ([]byte, error) {
+ sort.SliceStable(r.edits, func(i, j int) bool { return r.edits[i].start > r.edits[j].start })
+
+ out := append([]byte(nil), r.src...)
+ prev := len(out) + 1
+
+ for _, e := range r.edits {
+ if e.end > prev {
+ return nil, fmt.Errorf("%w at offset %d", errOverlap, e.start)
+ }
+
+ out = append(out[:e.start], append([]byte(e.text), out[e.end:]...)...)
+ prev = e.start
+ }
+
+ return out, nil
+}
+
+type importInfo struct {
+ v1Spec *ast.ImportSpec
+ firstLocal *ast.ImportSpec
+ last *ast.ImportSpec
+ have map[string]bool
+ used map[string]bool
+}
+
+func scanImports(file *ast.File) importInfo {
+ info := importInfo{have: map[string]bool{}, used: map[string]bool{}}
+
+ for _, imp := range file.Imports {
+ p, _ := strconv.Unquote(imp.Path.Value)
+ info.have[p] = true
+ info.last = imp
+
+ if p == v1Path {
+ info.v1Spec = imp
+ }
+
+ if info.firstLocal == nil && strings.HasPrefix(p, localPrefix) {
+ info.firstLocal = imp
+ }
+ }
+
+ ast.Inspect(file, func(n ast.Node) bool {
+ if sel, ok := n.(*ast.SelectorExpr); ok {
+ if id, isID := sel.X.(*ast.Ident); isID {
+ info.used[id.Name] = true
+ }
+ }
+
+ return true
+ })
+
+ return info
+}
+
+// fixImports swaps the v1 import for v2 (or drops it when unused after the
+// rewrite) and adds jsontext / jsonx when referenced.
+func fixImports(path string, src []byte, jsonName string) ([]byte, error) {
+ fset := token.NewFileSet()
+
+ file, err := parser.ParseFile(fset, path, src, parser.ParseComments)
+ if err != nil {
+ return nil, fmt.Errorf("rewritten file does not parse: %w", err)
+ }
+
+ info := scanImports(file)
+ off := func(p token.Pos) int { return fset.Position(p).Offset }
+ spec := info.v1Spec
+
+ if decl := importDecl(file, spec); !decl.Lparen.IsValid() {
+ block := "import (\n\t" + string(src[off(spec.Pos()):off(spec.End())]) + "\n)"
+ out := append(append(append([]byte(nil), src[:off(decl.Pos())]...), block...), src[off(decl.End()):]...)
+
+ return fixImports(path, out, jsonName)
+ }
+
+ var edits []edit
+
+ if info.used[jsonName] {
+ edits = append(edits, edit{start: off(spec.Path.Pos()), end: off(spec.Path.End()), text: strconv.Quote(v2Path)})
+ } else {
+ start, end := off(spec.Pos()), off(spec.End())
+ for start > 0 && (src[start-1] == ' ' || src[start-1] == '\t') {
+ start--
+ }
+
+ if end < len(src) && src[end] == '\n' {
+ end++
+ }
+
+ edits = append(edits, edit{start: start, end: end})
+ }
+
+ if info.used["jsontext"] && !info.have[jsontextPath] {
+ edits = append(
+ edits,
+ edit{start: off(spec.End()), end: off(spec.End()), text: "\n\t" + strconv.Quote(jsontextPath)},
+ )
+ }
+
+ if info.used["jsonx"] && !info.have[jsonxPath] {
+ at, text := off(info.last.End()), "\n\n\t"+strconv.Quote(jsonxPath)
+ if info.firstLocal != nil {
+ at, text = off(info.firstLocal.Pos()), strconv.Quote(jsonxPath)+"\n\t"
+ }
+
+ edits = append(edits, edit{start: at, end: at, text: text})
+ }
+
+ sort.SliceStable(edits, func(i, j int) bool { return edits[i].start > edits[j].start })
+
+ out := append([]byte(nil), src...)
+ for _, e := range edits {
+ out = append(out[:e.start], append([]byte(e.text), out[e.end:]...)...)
+ }
+
+ return format.Source(out)
+}
+
+func importDecl(file *ast.File, spec *ast.ImportSpec) *ast.GenDecl {
+ for _, d := range file.Decls {
+ if g, ok := d.(*ast.GenDecl); ok && g.Tok == token.IMPORT && g.Pos() <= spec.Pos() && spec.End() <= g.End() {
+ return g
+ }
+ }
+
+ return nil
+}
diff --git a/cmd/jsonv2mod/rewrite_test.go b/cmd/jsonv2mod/rewrite_test.go
new file mode 100644
index 0000000000..3c8ad3ddd9
--- /dev/null
+++ b/cmd/jsonv2mod/rewrite_test.go
@@ -0,0 +1,185 @@
+package main
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+const header = `package p
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/arn"
+)
+
+var _ = http.StatusOK
+var _ = arn.X
+
+`
+
+func TestRewrite(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ body string
+ want []string
+ wantFindings int
+ }{
+ {
+ name: "marshal unmarshal",
+ body: `func f(b []byte) {
+ _, _ = json.Marshal(b)
+ _ = json.Unmarshal(b, &b)
+}`,
+ want: []string{
+ "json.Marshal(b, jsonx.V1())", "json.Unmarshal(b, &b, jsonx.V1())",
+ `"encoding/json/v2"`, `pkgs/jsonx"`,
+ },
+ },
+ {
+ name: "multiline trailing comma",
+ body: `func f(b []byte) {
+ _, _ = json.Marshal(
+ b,
+ )
+}`,
+ want: []string{"b, jsonx.V1(),"},
+ },
+ {
+ name: "marshal indent",
+ body: `func f(v any) { _, _ = json.MarshalIndent(v, "", " ") }`,
+ want: []string{
+ `json.Marshal(v, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))`,
+ "encoding/json/jsontext",
+ },
+ },
+ {
+ name: "decode chain",
+ body: `func f(r *http.Request, v any) error { return json.NewDecoder(r.Body).Decode(&v) }`,
+ want: []string{"json.UnmarshalDecode(jsontext.NewDecoder(r.Body), &v, jsonx.V1())"},
+ },
+ {
+ name: "encode chain",
+ body: `func f(w http.ResponseWriter, v any) { _ = json.NewEncoder(w).Encode(v) }`,
+ want: []string{"json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), v)"},
+ },
+ {
+ name: "decoder var",
+ body: `func f(r *http.Request, v any) error {
+ dec := json.NewDecoder(r.Body)
+ dec.UseNumber()
+ dec.DisallowUnknownFields()
+
+ return dec.Decode(&v)
+}`,
+ want: []string{
+ "dec := jsontext.NewDecoder(r.Body)",
+ "json.UnmarshalDecode(dec, &v, jsonx.V1(), jsonx.UseNumber(), json.RejectUnknownMembers(true))",
+ },
+ },
+ {
+ name: "encoder var",
+ body: `func f(w http.ResponseWriter, v any) error {
+ enc := json.NewEncoder(w)
+ enc.SetEscapeHTML(false)
+ enc.SetIndent("", "\t")
+
+ return enc.Encode(v)
+}`,
+ want: []string{
+ `jsontext.NewEncoder(w, jsonx.V1(), jsontext.EscapeForHTML(false), jsontext.Multiline(true), ` +
+ `jsontext.WithIndent("\t"))`,
+ "json.MarshalEncode(enc, v)",
+ },
+ },
+ {
+ name: "types",
+ body: `type T struct {
+ R json.RawMessage
+ N json.Number
+}
+
+var _ json.Marshaler
+
+func f(b []byte) bool { return json.Valid(b) }`,
+ want: []string{
+ "R jsontext.Value",
+ "N jsonx.Number",
+ "json.Marshaler",
+ "jsontext.Value(b).IsValid(jsonx.V1())",
+ },
+ },
+ {
+ name: "bad stream stays untouched",
+ body: `func f(r *http.Request, v any) {
+ dec := json.NewDecoder(r.Body)
+ _ = dec.Decode(&v)
+ _ = dec.Buffered()
+}`,
+ want: []string{"_ = dec.Decode(&v)", "json.NewDecoder(r.Body)"},
+ wantFindings: 1,
+ },
+ {
+ name: "conditional setindent is not hoisted",
+ body: `func f(w http.ResponseWriter, v any, c bool) {
+ enc := json.NewEncoder(w)
+ if c {
+ enc.SetIndent("", " ")
+ }
+ _ = enc.Encode(v)
+}`,
+ want: []string{"enc.SetIndent"},
+ wantFindings: 1,
+ },
+ {
+ name: "leftovers",
+ body: `func f(r *http.Request) {
+ d := json.NewDecoder(r.Body)
+ _ = d.More()
+ var e *json.SyntaxError
+ _ = e
+}`,
+ want: []string{"json.NewDecoder(r.Body)"},
+ wantFindings: 2,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ res, err := rewrite("x.go", []byte(header+tt.body+"\n"))
+ require.NoError(t, err)
+ assert.Len(t, res.findings, tt.wantFindings)
+ require.NotNil(t, res.out)
+
+ for _, w := range tt.want {
+ assert.Contains(t, string(res.out), w)
+ }
+ })
+ }
+}
+
+func TestRewriteSingleLineImport(t *testing.T) {
+ t.Parallel()
+
+ src := "package p\n\nimport \"encoding/json\"\n\nfunc f(b []byte) bool { return json.Valid(b) }\n"
+ res, err := rewrite("x.go", []byte(src))
+ require.NoError(t, err)
+ require.NotNil(t, res.out)
+ assert.Contains(t, string(res.out), "jsontext.Value(b).IsValid(jsonx.V1())")
+ assert.Contains(t, string(res.out), "encoding/json/jsontext")
+}
+
+func TestRewriteNoV1Import(t *testing.T) {
+ t.Parallel()
+
+ res, err := rewrite("x.go", []byte("package p\n\nimport \"fmt\"\n\nvar _ = fmt.Sprint\n"))
+ require.NoError(t, err)
+ assert.Nil(t, res.out)
+}
diff --git a/cmd/opcensus/main.go b/cmd/opcensus/main.go
index 5e76a6ee15..aa2d4350c4 100644
--- a/cmd/opcensus/main.go
+++ b/cmd/opcensus/main.go
@@ -40,7 +40,8 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"flag"
"fmt"
"go/ast"
@@ -52,6 +53,8 @@ import (
"slices"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -210,9 +213,8 @@ func writeJSON(path string, results []serviceResult) error {
}
defer f.Close()
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
- if encErr := enc.Encode(results); encErr != nil {
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
+ if encErr := json.MarshalEncode(enc, results); encErr != nil {
return fmt.Errorf("encode json: %w", encErr)
}
diff --git a/cmd/overwidecandidates/main.go b/cmd/overwidecandidates/main.go
index e3bc546c5e..75ae2aab5a 100644
--- a/cmd/overwidecandidates/main.go
+++ b/cmd/overwidecandidates/main.go
@@ -26,7 +26,8 @@ package main
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"flag"
"fmt"
@@ -37,6 +38,8 @@ import (
"slices"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// dirModuleOverride maps services/
to its aws-sdk-go-v2/service module
@@ -370,10 +373,9 @@ func writeJSON(path string, results map[string]serviceResult) {
}
defer f.Close()
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- if encErr := enc.Encode(results); encErr != nil {
+ if encErr := json.MarshalEncode(enc, results); encErr != nil {
fmt.Fprintln(os.Stderr, "error:", encErr)
}
diff --git a/cmd/parityfmtcheck/report.go b/cmd/parityfmtcheck/report.go
index c9c4127bd8..cdf6b66522 100644
--- a/cmd/parityfmtcheck/report.go
+++ b/cmd/parityfmtcheck/report.go
@@ -1,10 +1,13 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"os"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const jsonFileMode = 0o644
@@ -28,7 +31,7 @@ func writeJSON(path string, results []result) error {
})
}
- data, err := json.MarshalIndent(out, "", " ")
+ data, err := json.Marshal(out, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
if err != nil {
return fmt.Errorf("marshal results: %w", err)
}
diff --git a/cmd/parityfmtcheck/report_test.go b/cmd/parityfmtcheck/report_test.go
index 4fdf65d14a..aa62f38eb9 100644
--- a/cmd/parityfmtcheck/report_test.go
+++ b/cmd/parityfmtcheck/report_test.go
@@ -2,13 +2,15 @@ package main
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestPrintReport(t *testing.T) {
@@ -68,7 +70,7 @@ func TestWriteJSON(t *testing.T) {
require.NoError(t, err)
var got []jsonResult
- require.NoError(t, json.Unmarshal(data, &got))
+ require.NoError(t, json.Unmarshal(data, &got, jsonx.V1()))
require.Len(t, got, 2)
assert.Equal(t, "dlm", got[0].Service)
diff --git a/cmd/paritylint/report.go b/cmd/paritylint/report.go
index d1684e217f..0873068351 100644
--- a/cmd/paritylint/report.go
+++ b/cmd/paritylint/report.go
@@ -1,10 +1,13 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"os"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func printReport(w io.Writer, findings []finding) {
@@ -35,7 +38,7 @@ func printReport(w io.Writer, findings []finding) {
}
func writeJSON(path string, findings []finding) error {
- data, err := json.MarshalIndent(findings, "", " ")
+ data, err := json.Marshal(findings, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
if err != nil {
return err
}
diff --git a/cmd/pgoload/stepfunctions.go b/cmd/pgoload/stepfunctions.go
index 44a7b242f7..ee4e8e1e0b 100644
--- a/cmd/pgoload/stepfunctions.go
+++ b/cmd/pgoload/stepfunctions.go
@@ -2,7 +2,7 @@ package main
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"log/slog"
@@ -10,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/sfn"
sfntypes "github.com/aws/aws-sdk-go-v2/service/sfn/types"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const sfnStateMachineName = "pgoload-sm"
@@ -30,7 +32,7 @@ func sfnDefinition() string {
"States": map[string]any{
sfnPassState: map[string]any{"Type": "Pass", "End": true},
},
- })
+ }, jsonx.V1())
return string(def)
}
diff --git a/cmd/reqfielddiff/main.go b/cmd/reqfielddiff/main.go
index 2eab5a822f..8d1c0fd1a2 100644
--- a/cmd/reqfielddiff/main.go
+++ b/cmd/reqfielddiff/main.go
@@ -420,7 +420,8 @@ package main
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"flag"
"fmt"
"os"
@@ -428,6 +429,8 @@ import (
"path/filepath"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -586,8 +589,7 @@ func writeJSON(path string, reports []serviceReport) error {
}
defer f.Close()
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- return enc.Encode(reports)
+ return json.MarshalEncode(enc, reports)
}
diff --git a/cmd/reqfieldscan/main.go b/cmd/reqfieldscan/main.go
index 4ff94473bb..aaa25c397b 100644
--- a/cmd/reqfieldscan/main.go
+++ b/cmd/reqfieldscan/main.go
@@ -206,7 +206,8 @@ package main
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"flag"
"fmt"
"os"
@@ -214,6 +215,8 @@ import (
"path/filepath"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -335,8 +338,7 @@ func writeJSON(path string, reports []serviceReport) error {
}
defer f.Close()
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- return enc.Encode(reports)
+ return json.MarshalEncode(enc, reports)
}
diff --git a/cmd/requiredoutputfields/main.go b/cmd/requiredoutputfields/main.go
index fe90fbfb32..f912c4e4b3 100644
--- a/cmd/requiredoutputfields/main.go
+++ b/cmd/requiredoutputfields/main.go
@@ -23,7 +23,8 @@ package main
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"flag"
"fmt"
"os"
@@ -33,6 +34,8 @@ import (
"slices"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// dirModuleOverride maps services/ to its aws-sdk-go-v2/service module
@@ -395,10 +398,9 @@ func writeJSON(path string, results map[string]serviceResult) {
}
defer f.Close()
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- if encErr := enc.Encode(results); encErr != nil {
+ if encErr := json.MarshalEncode(enc, results); encErr != nil {
fmt.Fprintln(os.Stderr, "error:", encErr)
}
diff --git a/cmd/retypesgen/main.go b/cmd/retypesgen/main.go
index 098d93e5a4..85dd0cc4b9 100644
--- a/cmd/retypesgen/main.go
+++ b/cmd/retypesgen/main.go
@@ -3,7 +3,8 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"flag"
"fmt"
@@ -16,6 +17,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const fetchTimeout = time.Minute
@@ -88,7 +90,12 @@ func run(out string) (int, error) {
types := parse(string(b))
- data, err := json.MarshalIndent(catalog{Source: source, Types: types}, "", " ")
+ data, err := json.Marshal(
+ catalog{Source: source, Types: types},
+ jsonx.V1(),
+ jsontext.Multiline(true),
+ jsontext.WithIndent(" "),
+ )
if err != nil {
return 0, err
}
diff --git a/cmd/routecollisions/main.go b/cmd/routecollisions/main.go
index 7ae9f2918e..0847cd3198 100644
--- a/cmd/routecollisions/main.go
+++ b/cmd/routecollisions/main.go
@@ -39,7 +39,8 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"flag"
"fmt"
"go/ast"
@@ -50,6 +51,8 @@ import (
"sort"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const cliGoPath = "internal/app/cli.go"
@@ -169,10 +172,9 @@ func writeJSONReport(path string, results []svcInfo) error {
}
defer f.Close()
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- return enc.Encode(results)
+ return json.MarshalEncode(enc, results)
}
func listServiceDirs(servicesDir string) ([]string, error) {
diff --git a/cmd/sfnschemagen/main.go b/cmd/sfnschemagen/main.go
index 2394fd94ea..4b6191583e 100644
--- a/cmd/sfnschemagen/main.go
+++ b/cmd/sfnschemagen/main.go
@@ -8,7 +8,7 @@ import (
"bytes"
"compress/gzip"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"go/format"
"log"
@@ -20,6 +20,7 @@ import (
"sort"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions/sdkwire/ir"
)
@@ -208,7 +209,7 @@ func queryVersion(dir string) string {
}
func writeModel(key string, model *ir.Model) {
- raw, err := json.Marshal(model)
+ raw, err := json.Marshal(model, jsonx.V1())
if err != nil {
log.Fatal(err)
}
diff --git a/cmd/sqdocsgen/main.go b/cmd/sqdocsgen/main.go
index a29490a8d8..2086d692eb 100644
--- a/cmd/sqdocsgen/main.go
+++ b/cmd/sqdocsgen/main.go
@@ -6,7 +6,8 @@ package main
import (
"bytes"
"compress/gzip"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"flag"
"fmt"
@@ -18,6 +19,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const fetchTimeout = time.Minute
@@ -208,7 +210,7 @@ func main() {
slices.SortFunc(svcs, func(a, b service) int { return strings.Compare(a.Code, b.Code) })
- b, err := json.MarshalIndent(svcs, "", " ")
+ b, err := json.Marshal(svcs, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
diff --git a/cmd/staleclaims/report.go b/cmd/staleclaims/report.go
index 0dfc74f075..b040237b38 100644
--- a/cmd/staleclaims/report.go
+++ b/cmd/staleclaims/report.go
@@ -1,10 +1,13 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"os"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const disclaimer = `NOTE ON READING THIS REPORT
@@ -59,10 +62,9 @@ func writeJSON(path string, findings []finding) error {
}
defer f.Close()
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- if encErr := enc.Encode(findings); encErr != nil {
+ if encErr := json.MarshalEncode(enc, findings); encErr != nil {
return fmt.Errorf("encode json: %w", encErr)
}
diff --git a/cmd/stampaudit/report.go b/cmd/stampaudit/report.go
index 2d8bdca390..877d9b4a9a 100644
--- a/cmd/stampaudit/report.go
+++ b/cmd/stampaudit/report.go
@@ -1,10 +1,13 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"os"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -130,10 +133,9 @@ func writeJSON(path string, results []result) error {
}
defer f.Close()
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- if encErr := enc.Encode(out); encErr != nil {
+ if encErr := json.MarshalEncode(enc, out); encErr != nil {
return fmt.Errorf("encode json: %w", encErr)
}
diff --git a/cmd/structfielddiff/main.go b/cmd/structfielddiff/main.go
index 56f3b0bfba..b20830bfdd 100644
--- a/cmd/structfielddiff/main.go
+++ b/cmd/structfielddiff/main.go
@@ -38,7 +38,8 @@ package main
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"flag"
"fmt"
@@ -50,6 +51,7 @@ import (
"strings"
"github.com/blackbirdworks/gopherstack/cmd/internal/sdkshape"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// dirModuleOverride maps services/ to its aws-sdk-go-v2/service module
@@ -261,10 +263,9 @@ func writeJSON(path string, dumps []opDump) {
}
defer f.Close()
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- if encErr := enc.Encode(dumps); encErr != nil {
+ if encErr := json.MarshalEncode(enc, dumps); encErr != nil {
fatal(encErr)
}
diff --git a/cmd/xmlitemwrap/report.go b/cmd/xmlitemwrap/report.go
index b14492b0af..c978689606 100644
--- a/cmd/xmlitemwrap/report.go
+++ b/cmd/xmlitemwrap/report.go
@@ -1,9 +1,12 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"os"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func writeJSON(path string, findings []finding) error {
@@ -13,10 +16,9 @@ func writeJSON(path string, findings []finding) error {
}
defer f.Close()
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- return enc.Encode(findings)
+ return json.MarshalEncode(enc, findings)
}
func printReport(findings []finding) {
diff --git a/cmd/zeroguard/report.go b/cmd/zeroguard/report.go
index 68ce9764d5..aaa85ea99f 100644
--- a/cmd/zeroguard/report.go
+++ b/cmd/zeroguard/report.go
@@ -1,9 +1,12 @@
package main
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"os"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func writeJSON(path string, findings []finding) error {
@@ -12,10 +15,9 @@ func writeJSON(path string, findings []finding) error {
return err
}
- enc := json.NewEncoder(f)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(f, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- if encErr := enc.Encode(findings); encErr != nil {
+ if encErr := json.MarshalEncode(enc, findings); encErr != nil {
_ = f.Close()
return encErr
diff --git a/dashboard/azureaks_proxy_test.go b/dashboard/azureaks_proxy_test.go
index 19ad9faac0..8ebfa6a889 100644
--- a/dashboard/azureaks_proxy_test.go
+++ b/dashboard/azureaks_proxy_test.go
@@ -1,7 +1,7 @@
package dashboard_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/dashboard"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -120,13 +121,13 @@ func TestAzureAKSProxy(t *testing.T) {
require.NoError(t, h.Handler()(echo.NewContext(httptest.NewRequest(http.MethodGet, pool, nil), rec)))
var doc map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &doc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &doc, jsonx.V1()))
props, ok := doc["properties"].(map[string]any)
require.True(t, ok)
props["count"] = 5
- body, err := json.Marshal(map[string]any{"properties": props})
+ body, err := json.Marshal(map[string]any{"properties": props}, jsonx.V1())
require.NoError(t, err)
runGCPSteps(t, h, []gcpStep{
diff --git a/dashboard/azureapim_proxy.go b/dashboard/azureapim_proxy.go
index e2cd376069..a8c83b5223 100644
--- a/dashboard/azureapim_proxy.go
+++ b/dashboard/azureapim_proxy.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"crypto/tls"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -14,6 +14,8 @@ import (
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -253,14 +255,14 @@ func (t *apimTry) subscriptionKey(ctx context.Context, svc, sub string) (string,
Properties map[string]any `json:"properties"`
}
- if json.Unmarshal(rec.Body.Bytes(), &doc) != nil || doc.Properties["state"] != apimPxActive {
+ if json.Unmarshal(rec.Body.Bytes(), &doc, jsonx.V1()) != nil || doc.Properties["state"] != apimPxActive {
return "", http.StatusConflict, "Subscription is not active"
}
rec = t.arm(ctx, http.MethodPost, base+"/listSecrets")
var keys map[string]any
- if rec.Code != http.StatusOK || json.Unmarshal(rec.Body.Bytes(), &keys) != nil {
+ if rec.Code != http.StatusOK || json.Unmarshal(rec.Body.Bytes(), &keys, jsonx.V1()) != nil {
return "", http.StatusConflict, "Subscription key is unavailable"
}
@@ -298,7 +300,7 @@ func (t *apimTry) apiInfo(ctx context.Context, svc, api string) apimAPIInfo {
} `json:"properties"`
}
- if rec.Code != http.StatusOK || json.Unmarshal(rec.Body.Bytes(), &doc) != nil {
+ if rec.Code != http.StatusOK || json.Unmarshal(rec.Body.Bytes(), &doc, jsonx.V1()) != nil {
return def
}
diff --git a/dashboard/azureapim_proxy_test.go b/dashboard/azureapim_proxy_test.go
index 4f0d2a1a71..9c6022ddeb 100644
--- a/dashboard/azureapim_proxy_test.go
+++ b/dashboard/azureapim_proxy_test.go
@@ -2,7 +2,8 @@ package dashboard_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -16,6 +17,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/dashboard"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/azurearm"
)
@@ -56,7 +58,10 @@ func newAPIMDashboard(t *testing.T) *apimEnv {
be := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("X-Echo-Key", r.Header.Get("Ocp-Apim-Subscription-Key"))
- _ = json.NewEncoder(w).Encode(map[string]any{"path": r.URL.Path, "header": r.Header, "query": r.URL.RawQuery})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]any{"path": r.URL.Path, "header": r.Header, "query": r.URL.RawQuery},
+ )
}))
t.Cleanup(be.Close)
@@ -99,7 +104,7 @@ func newAPIMDashboard(t *testing.T) *apimEnv {
Secondary string `json:"secondaryKey"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &keys))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &keys, jsonx.V1()))
require.NotEmpty(t, keys.Primary)
dash := echo.New()
@@ -222,7 +227,7 @@ func TestAzureAPIMTryIt(t *testing.T) {
var o out
if rec.Code == http.StatusOK {
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &o))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &o, jsonx.V1()))
}
return rec, o
diff --git a/dashboard/azurearm_inproc_proxy.go b/dashboard/azurearm_inproc_proxy.go
index 6b036ebd61..a591e35a02 100644
--- a/dashboard/azurearm_inproc_proxy.go
+++ b/dashboard/azurearm_inproc_proxy.go
@@ -2,7 +2,7 @@ package dashboard
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -11,6 +11,8 @@ import (
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -152,7 +154,7 @@ func armProxyRelay(c *echo.Context, rec *httptest.ResponseRecorder, view func(an
// armScrubBody applies the optional view and the generic scrub to a JSON body; anything else is returned as is.
func armScrubBody(body []byte, view func(any)) []byte {
var doc any
- if json.Unmarshal(body, &doc) != nil {
+ if json.Unmarshal(body, &doc, jsonx.V1()) != nil {
return body
}
@@ -162,7 +164,7 @@ func armScrubBody(body []byte, view func(any)) []byte {
armScrub(doc)
- if out, err := json.Marshal(doc); err == nil {
+ if out, err := json.Marshal(doc, jsonx.V1()); err == nil {
return out
}
diff --git a/dashboard/azureflexdb_proxy.go b/dashboard/azureflexdb_proxy.go
index 47a6b191df..17e3ea7f09 100644
--- a/dashboard/azureflexdb_proxy.go
+++ b/dashboard/azureflexdb_proxy.go
@@ -1,12 +1,14 @@
package dashboard
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/netip"
"regexp"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -115,7 +117,7 @@ func flexProxyBody(method string, p []string, in []byte) ([]byte, bool) {
return nil, false
}
- b, err := json.Marshal(map[string]any{"properties": props})
+ b, err := json.Marshal(map[string]any{"properties": props}, jsonx.V1())
return b, err == nil
}
@@ -129,7 +131,7 @@ type flexPropsIn struct {
func flexDatabaseProps(in []byte) map[string]any {
var req flexPropsIn
- if json.Unmarshal(in, &req) != nil {
+ if json.Unmarshal(in, &req, jsonx.V1()) != nil {
return nil
}
@@ -153,7 +155,7 @@ func flexDatabaseProps(in []byte) map[string]any {
func flexFirewallProps(in []byte) map[string]any {
var req flexPropsIn
- if json.Unmarshal(in, &req) != nil {
+ if json.Unmarshal(in, &req, jsonx.V1()) != nil {
return nil
}
diff --git a/dashboard/azureinproc_proxy_test.go b/dashboard/azureinproc_proxy_test.go
index c1faf8b550..6cef6221f2 100644
--- a/dashboard/azureinproc_proxy_test.go
+++ b/dashboard/azureinproc_proxy_test.go
@@ -2,7 +2,7 @@ package dashboard_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/dashboard"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/azurearm"
)
@@ -110,7 +111,7 @@ func jsonField(t *testing.T, raw []byte, path ...any) string {
t.Helper()
var doc any
- require.NoError(t, json.Unmarshal(raw, &doc))
+ require.NoError(t, json.Unmarshal(raw, &doc, jsonx.V1()))
for _, p := range path {
switch k := p.(type) {
diff --git a/dashboard/azurekeyvault_proxy.go b/dashboard/azurekeyvault_proxy.go
index 700a5d4876..ddfa9f5971 100644
--- a/dashboard/azurekeyvault_proxy.go
+++ b/dashboard/azurekeyvault_proxy.go
@@ -2,7 +2,8 @@ package dashboard
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"regexp"
"slices"
@@ -11,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
azurekeyvaultbackend "github.com/blackbirdworks/gopherstack/services/azurekeyvault"
)
@@ -74,7 +76,7 @@ func azureKeyVaultInternalToken() string {
claims, _ := json.Marshal(map[string]any{
"iss": "gopherstack-dashboard",
"exp": time.Now().Add(azureKeyVaultTokenTTL).Unix(),
- })
+ }, jsonx.V1())
return header + "." + enc.EncodeToString(claims) + "."
}
@@ -88,8 +90,12 @@ func (h *DashboardHandler) azureKeyVaultCreateVault(c *echo.Context) error {
Name string `json:"name"`
}
- if err := json.NewDecoder(http.MaxBytesReader(c.Response(), c.Request().Body, azureKeyVaultMaxBodyBytes)).
- Decode(&in); err != nil || !azureKeyVaultNameRe.MatchString(in.Name) {
+ if err := json.UnmarshalDecode(
+ jsontext.NewDecoder(http.MaxBytesReader(c.Response(), c.Request().Body, azureKeyVaultMaxBodyBytes)),
+ &in,
+ jsonx.V1(),
+ ); err != nil ||
+ !azureKeyVaultNameRe.MatchString(in.Name) {
return c.JSON(http.StatusBadRequest, map[string]string{
"error": "vault name must be 3-24 characters: letters, digits and hyphens, starting with a letter",
})
diff --git a/dashboard/azuremonitor_proxy_test.go b/dashboard/azuremonitor_proxy_test.go
index ae486bfc51..28c87568be 100644
--- a/dashboard/azuremonitor_proxy_test.go
+++ b/dashboard/azuremonitor_proxy_test.go
@@ -1,7 +1,7 @@
package dashboard_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/dashboard"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azuremonitor"
)
@@ -148,7 +149,7 @@ func TestAzureMonitorProxy(t *testing.T) {
} `json:"innererror"`
} `json:"error"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "BadArgumentError", body.Error.Code)
assert.Equal(t, "SemanticError", body.Error.Innererror.Code)
assert.NotEmpty(t, body.Error.Innererror.Innererror.Message)
diff --git a/dashboard/cloud_json.go b/dashboard/cloud_json.go
index eb8055b440..fb753f34d3 100644
--- a/dashboard/cloud_json.go
+++ b/dashboard/cloud_json.go
@@ -1,10 +1,13 @@
package dashboard
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -33,8 +36,8 @@ func cloudUnavailable(c *echo.Context, name string) error {
// cloudJSONDecode reads a size-bounded JSON body into v; on failure it has already replied.
func cloudJSONDecode(c *echo.Context, v any) bool {
- dec := json.NewDecoder(http.MaxBytesReader(c.Response(), c.Request().Body, cloudJSONMaxBody))
- if err := dec.Decode(v); err != nil {
+ dec := jsontext.NewDecoder(http.MaxBytesReader(c.Response(), c.Request().Body, cloudJSONMaxBody))
+ if err := json.UnmarshalDecode(dec, v, jsonx.V1()); err != nil {
_ = cloudJSONFail(c, http.StatusBadRequest, cloudStatusBadArg, "invalid request body")
return false
diff --git a/dashboard/gcpapigateway_proxy.go b/dashboard/gcpapigateway_proxy.go
index 42cfd4d2f6..275b00128b 100644
--- a/dashboard/gcpapigateway_proxy.go
+++ b/dashboard/gcpapigateway_proxy.go
@@ -2,7 +2,7 @@ package dashboard
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -17,6 +17,7 @@ import (
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/gcpapigateway"
)
@@ -194,7 +195,7 @@ func (a *gcpAPIGWProxy) tryIt(ctx context.Context, r gcpRESTCall) (proto.Message
bad := status.Error(codes.InvalidArgument, "invalid API Gateway try-it request")
var in gcpAPIGWTryIn
- if len(r.body) > gcpAPIGWMaxBody || json.Unmarshal(r.body, &in) != nil {
+ if len(r.body) > gcpAPIGWMaxBody || json.Unmarshal(r.body, &in, jsonx.V1()) != nil {
return nil, bad
}
@@ -257,7 +258,7 @@ func gcpAPIGWTryTarget(project, location, gateway string, in gcpAPIGWTryIn) (str
// structOf converts a JSON-compatible value into the protobuf Struct the REST proxy renders.
func structOf(v any) (proto.Message, error) {
- raw, err := json.Marshal(v)
+ raw, err := json.Marshal(v, jsonx.V1())
if err != nil {
return nil, status.Error(codes.Internal, "unreadable response")
}
diff --git a/dashboard/gcpapigateway_proxy_test.go b/dashboard/gcpapigateway_proxy_test.go
index 4aeeb4659f..673c5f69a6 100644
--- a/dashboard/gcpapigateway_proxy_test.go
+++ b/dashboard/gcpapigateway_proxy_test.go
@@ -2,7 +2,8 @@ package dashboard_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -15,6 +16,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/dashboard"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/gcpapigateway"
)
@@ -42,7 +44,10 @@ func newAPIGatewayDashboard(t *testing.T) *dashboard.DashboardHandler {
be := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("X-Echo-Query", r.URL.RawQuery)
- _ = json.NewEncoder(w).Encode(map[string]string{"path": r.URL.Path, "query": r.URL.RawQuery})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]string{"path": r.URL.Path, "query": r.URL.RawQuery},
+ )
}))
t.Cleanup(be.Close)
@@ -124,7 +129,7 @@ func TestGCPAPIGatewayProxy(t *testing.T) {
} `json:"openapiDocuments"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cfg))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cfg, jsonx.V1()))
require.Len(t, cfg.Docs, 1)
raw, err := base64.StdEncoding.DecodeString(cfg.Docs[0].Document.Contents)
@@ -211,7 +216,7 @@ func TestGCPAPIGatewayTryIt(t *testing.T) {
var o out
if rec.Code == http.StatusOK {
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &o))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &o, jsonx.V1()))
}
return rec, o
diff --git a/dashboard/gcpbigtable_proxy.go b/dashboard/gcpbigtable_proxy.go
index 4eb2ddca46..9f647a052c 100644
--- a/dashboard/gcpbigtable_proxy.go
+++ b/dashboard/gcpbigtable_proxy.go
@@ -2,7 +2,7 @@ package dashboard
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"cloud.google.com/go/bigtable/admin/apiv2/adminpb"
@@ -13,6 +13,7 @@ import (
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
gcpbigtablebackend "github.com/blackbirdworks/gopherstack/services/gcpbigtable"
)
@@ -171,7 +172,7 @@ func (a *gcpBigtableAPI) readRows(r gcpRESTCall, table string) (proto.Message, e
}
var v any
- if uErr := json.Unmarshal(b, &v); uErr != nil {
+ if uErr := json.Unmarshal(b, &v, jsonx.V1()); uErr != nil {
return nil, status.Error(codes.Internal, uErr.Error())
}
diff --git a/dashboard/gcpfunctions_proxy.go b/dashboard/gcpfunctions_proxy.go
index b73380fcbe..c4278c8558 100644
--- a/dashboard/gcpfunctions_proxy.go
+++ b/dashboard/gcpfunctions_proxy.go
@@ -3,7 +3,7 @@ package dashboard
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"cloud.google.com/go/functions/apiv2/functionspb"
@@ -12,6 +12,7 @@ import (
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/gcpfunctions"
)
@@ -139,7 +140,7 @@ type invokeInput struct {
func (a *gcpFunctionsAPI) invoke(ctx context.Context, name string, raw []byte) (proto.Message, error) {
var in invokeInput
- if err := json.Unmarshal(raw, &in); err != nil {
+ if err := json.Unmarshal(raw, &in, jsonx.V1()); err != nil {
return nil, status.Error(codes.InvalidArgument, "invalid request body: "+err.Error())
}
diff --git a/dashboard/gcpidentitytoolkit_proxy.go b/dashboard/gcpidentitytoolkit_proxy.go
index a9d25ddcab..5e9e96b23e 100644
--- a/dashboard/gcpidentitytoolkit_proxy.go
+++ b/dashboard/gcpidentitytoolkit_proxy.go
@@ -2,7 +2,7 @@ package dashboard
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -13,6 +13,8 @@ import (
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -147,7 +149,7 @@ func (p *gcpIDPRequest) call(method, path, rawQuery string, body any) (*httptest
var payload []byte
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
if err != nil {
return nil, err //nolint:wrapcheck // surfaced as a 400 by the caller
}
@@ -262,7 +264,7 @@ func (p *gcpIDPRequest) userAction(uid, action string) error {
return ociInvalid(p.c, "claims must be a JSON object")
}
- raw, err := json.Marshal(in.Claims)
+ raw, err := json.Marshal(in.Claims, jsonx.V1())
if err != nil || len(raw) > gcpIDPMaxClaims {
return ociInvalid(p.c, "claims must be a JSON object under 1000 bytes")
}
diff --git a/dashboard/gcpidentitytoolkit_views.go b/dashboard/gcpidentitytoolkit_views.go
index 796dc2625a..6f56f26885 100644
--- a/dashboard/gcpidentitytoolkit_views.go
+++ b/dashboard/gcpidentitytoolkit_views.go
@@ -1,9 +1,11 @@
package dashboard
import (
- "encoding/json"
+ "encoding/json/v2"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type idpProvider struct {
@@ -45,7 +47,7 @@ func idpUsersView(raw []byte) (any, error) {
Users []idpUserWire `json:"userInfo"`
}
- if err := json.Unmarshal(raw, &in); err != nil {
+ if err := json.Unmarshal(raw, &in, jsonx.V1()); err != nil {
return nil, err //nolint:wrapcheck // surfaced as a 502 by the caller
}
@@ -73,7 +75,7 @@ func idpLocalIDView(raw []byte) (any, error) {
LocalID string `json:"localId"`
}
- if err := json.Unmarshal(raw, &in); err != nil {
+ if err := json.Unmarshal(raw, &in, jsonx.V1()); err != nil {
return nil, err //nolint:wrapcheck // surfaced as a 502 by the caller
}
@@ -92,7 +94,7 @@ func idpOOBView(raw []byte) (any, error) {
} `json:"oobCodes"`
}
- if err := json.Unmarshal(raw, &in); err != nil {
+ if err := json.Unmarshal(raw, &in, jsonx.V1()); err != nil {
return nil, err //nolint:wrapcheck // surfaced as a 502 by the caller
}
@@ -123,7 +125,7 @@ func idpTenantsView(raw []byte) (any, error) {
} `json:"tenants"`
}
- if err := json.Unmarshal(raw, &in); err != nil {
+ if err := json.Unmarshal(raw, &in, jsonx.V1()); err != nil {
return nil, err //nolint:wrapcheck // surfaced as a 502 by the caller
}
@@ -164,7 +166,7 @@ func idpConfigView(raw []byte) (any, error) {
} `json:"signIn"`
}
- if err := json.Unmarshal(raw, &in); err != nil {
+ if err := json.Unmarshal(raw, &in, jsonx.V1()); err != nil {
return nil, err //nolint:wrapcheck // surfaced as a 502 by the caller
}
diff --git a/dashboard/gcpresourcemanager_proxy_test.go b/dashboard/gcpresourcemanager_proxy_test.go
index 7471e3cd87..19a7196dca 100644
--- a/dashboard/gcpresourcemanager_proxy_test.go
+++ b/dashboard/gcpresourcemanager_proxy_test.go
@@ -1,7 +1,7 @@
package dashboard_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/blackbirdworks/gopherstack/dashboard"
"github.com/blackbirdworks/gopherstack/pkgs/gcpenforce"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/gcpresourcemanager"
)
@@ -106,7 +107,7 @@ func TestGCPResourceManagerProxy(t *testing.T) {
Etag string `json:"etag"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pol))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pol, jsonx.V1()))
add := `{"role":"roles/viewer","member":"user:a@example.com","etag":"` + pol.Etag + `"}`
@@ -122,7 +123,7 @@ func TestGCPResourceManagerProxy(t *testing.T) {
Etag string `json:"etag"`
}
- require.NoError(t, json.Unmarshal(first.Body.Bytes(), &cur))
+ require.NoError(t, json.Unmarshal(first.Body.Bytes(), &cur, jsonx.V1()))
del := `{"role":"roles/viewer","member":"user:a@example.com","etag":"` + cur.Etag + `"}`
runGCPSteps(t, h, []gcpStep{
diff --git a/dashboard/oci_inproc_proxy.go b/dashboard/oci_inproc_proxy.go
index c6f96ed0c0..d5c6200e33 100644
--- a/dashboard/oci_inproc_proxy.go
+++ b/dashboard/oci_inproc_proxy.go
@@ -2,7 +2,8 @@ package dashboard
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -10,6 +11,8 @@ import (
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -57,12 +60,20 @@ func ociDispatch(c *echo.Context, inner *echo.Echo, apiPath string, body []byte)
func ociDecodeBody(c *echo.Context, v any) bool {
r := c.Request()
- return json.NewDecoder(http.MaxBytesReader(c.Response(), r.Body, ociInprocMaxBody)).Decode(v) == nil
+ return json.UnmarshalDecode(
+ jsontext.NewDecoder(http.MaxBytesReader(c.Response(), r.Body, ociInprocMaxBody)),
+ v,
+ jsonx.V1(),
+ ) == nil
}
// ociDecodeBodyN is ociDecodeBody with an explicit size cap.
func ociDecodeBodyN(c *echo.Context, v any, limit int64) bool {
- return json.NewDecoder(http.MaxBytesReader(c.Response(), c.Request().Body, limit)).Decode(v) == nil
+ return json.UnmarshalDecode(
+ jsontext.NewDecoder(http.MaxBytesReader(c.Response(), c.Request().Body, limit)),
+ v,
+ jsonx.V1(),
+ ) == nil
}
// ociInvalid answers 400 for a request outside the proxy's allow-list.
diff --git a/dashboard/ociapigateway_proxy_test.go b/dashboard/ociapigateway_proxy_test.go
index 8e546a6a4a..d56148759a 100644
--- a/dashboard/ociapigateway_proxy_test.go
+++ b/dashboard/ociapigateway_proxy_test.go
@@ -6,7 +6,7 @@ import (
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"math/big"
"net/http"
@@ -20,6 +20,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/dashboard"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ociapigateway"
)
@@ -63,7 +64,7 @@ func newGatewayDashboard(t *testing.T) (*dashboard.DashboardHandler, string, str
e.Any("/*", ops.Trusted())
post := func(path string, body map[string]any) []byte {
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
rec := httptest.NewRecorder()
@@ -79,7 +80,7 @@ func newGatewayDashboard(t *testing.T) (*dashboard.DashboardHandler, string, str
require.NoError(t, json.Unmarshal(post("/20190501/gateways", map[string]any{
"compartmentId": gwComp, "endpointType": "PUBLIC", "displayName": "edge", "subnetId": "ocid1.subnet.oc1..s",
- }), &gw))
+ }), &gw, jsonx.V1()))
var dep struct {
ID string `json:"id"`
@@ -91,7 +92,7 @@ func newGatewayDashboard(t *testing.T) (*dashboard.DashboardHandler, string, str
"path": "/hello", "methods": []string{"GET"},
"backend": map[string]any{"type": "STOCK_RESPONSE_BACKEND", "status": 200, "body": "hi there"},
}}},
- }), &dep))
+ }), &dep, jsonx.V1()))
certPEM, keyPEM := gwCert(t)
post("/20190501/certificates", map[string]any{
@@ -166,7 +167,7 @@ func TestOCIAPIGatewayProxy(t *testing.T) {
Status int `json:"status"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, 200, out.Status)
assert.Equal(t, "hi there", out.Body)
assert.Equal(t, "utf8", out.Encoding)
diff --git a/dashboard/ociartifacts_proxy.go b/dashboard/ociartifacts_proxy.go
index 778c1bf188..d127b0fac0 100644
--- a/dashboard/ociartifacts_proxy.go
+++ b/dashboard/ociartifacts_proxy.go
@@ -2,7 +2,8 @@ package dashboard
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -13,6 +14,8 @@ import (
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -306,7 +309,7 @@ func (in *ociArtReadmeIn) build() (map[string]any, bool) {
}
func ociArtMarshal(v map[string]any) ([]byte, bool) {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
return b, err == nil
}
@@ -383,7 +386,11 @@ func ociArtRestoreBody(c *echo.Context) ([]byte, bool) {
Version *string `json:"version"`
}
- err := json.NewDecoder(http.MaxBytesReader(c.Response(), c.Request().Body, ociInprocMaxBody)).Decode(&in)
+ err := json.UnmarshalDecode(
+ jsontext.NewDecoder(http.MaxBytesReader(c.Response(), c.Request().Body, ociInprocMaxBody)),
+ &in,
+ jsonx.V1(),
+ )
if err != nil && !errors.Is(err, io.EOF) {
return nil, false
}
@@ -442,7 +449,7 @@ func ociArtScrubSignatures(raw []byte) []byte {
Items []map[string]any `json:"items"`
}
- if json.Unmarshal(raw, &env) != nil {
+ if json.Unmarshal(raw, &env, jsonx.V1()) != nil {
return []byte("{}")
}
@@ -455,7 +462,7 @@ func ociArtScrubSignatures(raw []byte) []byte {
env.Items = []map[string]any{}
}
- out, err := json.Marshal(env)
+ out, err := json.Marshal(env, jsonx.V1())
if err != nil {
return []byte("{}")
}
@@ -476,7 +483,7 @@ func ociArtDownload(c *echo.Context, inner *echo.Echo, contentPath string) error
Size int64 `json:"sizeInBytes"`
}
- _ = json.Unmarshal(meta.Body.Bytes(), &art)
+ _ = json.Unmarshal(meta.Body.Bytes(), &art, jsonx.V1())
if art.Size > ociArtMaxDownload {
return cloudJSONFail(c, http.StatusRequestEntityTooLarge, "PayloadTooLarge",
diff --git a/dashboard/ociartifacts_proxy_test.go b/dashboard/ociartifacts_proxy_test.go
index 8bfe628fe5..af15e1e844 100644
--- a/dashboard/ociartifacts_proxy_test.go
+++ b/dashboard/ociartifacts_proxy_test.go
@@ -2,7 +2,7 @@ package dashboard_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/dashboard"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociregistry/ocitest"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/ociartifacts"
@@ -88,7 +89,7 @@ func newArtDashboard(t *testing.T) artFixture {
} `json:"items"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &list))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &list, jsonx.V1()))
require.Len(t, list.Items, 1)
f.imageID = list.Items[0].ID
@@ -105,7 +106,7 @@ func artTenancy(t *testing.T, f artFixture) string {
Compartments []string `json:"compartments"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.Compartments)
return out.Compartments[0]
@@ -161,7 +162,7 @@ func TestOCIArtifactsProxyRepositoryActions(t *testing.T) {
Immutable bool `json:"isImmutable"`
}
- require.NoError(t, json.Unmarshal(create.Body.Bytes(), &repo))
+ require.NoError(t, json.Unmarshal(create.Body.Bytes(), &repo, jsonx.V1()))
assert.NotEqual(t, "forged", repo.ID)
assert.NotEqual(t, "forged", repo.CreatedBy)
assert.Empty(t, repo.Tags)
@@ -172,7 +173,7 @@ func TestOCIArtifactsProxyRepositoryActions(t *testing.T) {
upd := artDo(t, f.h, http.MethodPut, artAPI+"/containerRepositories/"+repo.ID,
`{"isImmutable":true,"isPublic":false,"freeformTags":{"forged":"yes"},"compartmentId":"x"}`)
require.Equal(t, http.StatusOK, upd.Code, upd.Body.String())
- require.NoError(t, json.Unmarshal(upd.Body.Bytes(), &repo))
+ require.NoError(t, json.Unmarshal(upd.Body.Bytes(), &repo, jsonx.V1()))
assert.True(t, repo.Immutable)
assert.False(t, repo.Public)
assert.Empty(t, repo.Tags)
@@ -222,7 +223,7 @@ func TestOCIArtifactsProxySignaturesAreScrubbed(t *testing.T) {
"compartmentId": tenancy, "imageId": f.imageID, "kmsKeyId": "ocid1.key.oc1..kmskey0001",
"kmsKeyVersionId": "ocid1.keyversion.oc1..ver", "message": enc([]byte(artMsg)),
"signature": enc([]byte(artSig)), "signingAlgorithm": "SHA_256_RSA_PKCS_PSS",
- })
+ }, jsonx.V1())
require.NoError(t, err)
made := f.direct(t, http.MethodPost, "/20160918/container/imageSignatures", string(body))
@@ -254,7 +255,7 @@ func TestOCIArtifactsProxyGenericLifecycleAndDownload(t *testing.T) {
ID string `json:"id"`
}
- require.NoError(t, json.Unmarshal(made.Body.Bytes(), &repo))
+ require.NoError(t, json.Unmarshal(made.Body.Bytes(), &repo, jsonx.V1()))
assert.NotEqual(t, "forged", repo.ID)
put := f.direct(t, http.MethodPut,
@@ -273,7 +274,7 @@ func TestOCIArtifactsProxyGenericLifecycleAndDownload(t *testing.T) {
} `json:"items"`
}
- require.NoError(t, json.Unmarshal(list.Body.Bytes(), &arts))
+ require.NoError(t, json.Unmarshal(list.Body.Bytes(), &arts, jsonx.V1()))
require.Len(t, arts.Items, 1)
assert.NotEmpty(t, arts.Items[0].Sha256)
diff --git a/dashboard/ocicontainerengine_proxy.go b/dashboard/ocicontainerengine_proxy.go
index 8986db70ac..e71c32ae48 100644
--- a/dashboard/ocicontainerengine_proxy.go
+++ b/dashboard/ocicontainerengine_proxy.go
@@ -2,7 +2,8 @@ package dashboard
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -11,6 +12,8 @@ import (
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -131,7 +134,7 @@ func ociOKEResizeBody(in io.Reader) ([]byte, bool) {
Quantity *int `json:"quantityPerSubnet"`
}
- if err := json.NewDecoder(in).Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(in), &req, jsonx.V1()); err != nil {
return nil, false
}
@@ -146,7 +149,7 @@ func ociOKEResizeBody(in io.Reader) ([]byte, bool) {
return nil, false
}
- body, err := json.Marshal(out)
+ body, err := json.Marshal(out, jsonx.V1())
return body, err == nil
}
diff --git a/dashboard/ocicontainerengine_proxy_test.go b/dashboard/ocicontainerengine_proxy_test.go
index ff1f28aa91..edf7683712 100644
--- a/dashboard/ocicontainerengine_proxy_test.go
+++ b/dashboard/ocicontainerengine_proxy_test.go
@@ -1,7 +1,7 @@
package dashboard_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/dashboard"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ocicontainerengine"
)
@@ -50,7 +51,7 @@ func newOKEDashboard(t *testing.T) (*dashboard.DashboardHandler, string, string)
}
require.NoError(t, json.Unmarshal(okeSeed(t, e, http.MethodGet,
- "/20180222/clusters?compartmentId="+okeComp, ""), &clusters))
+ "/20180222/clusters?compartmentId="+okeComp, ""), &clusters, jsonx.V1()))
require.Len(t, clusters, 1)
okeSeed(t, e, http.MethodPost, "/20180222/nodePools",
@@ -66,7 +67,7 @@ func newOKEDashboard(t *testing.T) (*dashboard.DashboardHandler, string, string)
}
require.NoError(t, json.Unmarshal(okeSeed(t, e, http.MethodGet,
- "/20180222/nodePools?compartmentId="+okeComp, ""), &pools))
+ "/20180222/nodePools?compartmentId="+okeComp, ""), &pools, jsonx.V1()))
require.Len(t, pools, 1)
return dashboard.NewHandler(dashboard.Config{OCIContainerEngineOps: ops}), clusters[0].ID, pools[0].ID
diff --git a/dashboard/ocicontainerinstances_proxy.go b/dashboard/ocicontainerinstances_proxy.go
index 95cb0e859d..323a703670 100644
--- a/dashboard/ocicontainerinstances_proxy.go
+++ b/dashboard/ocicontainerinstances_proxy.go
@@ -3,7 +3,7 @@ package dashboard
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -15,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
idbackend "github.com/blackbirdworks/gopherstack/services/ociidentity"
)
@@ -445,13 +446,13 @@ func ociCIScrubJSON(raw []byte) []byte {
}
var doc any
- if json.Unmarshal(raw, &doc) != nil {
+ if json.Unmarshal(raw, &doc, jsonx.V1()) != nil {
return []byte("{}")
}
ociCIScrub(doc)
- out, err := json.Marshal(doc)
+ out, err := json.Marshal(doc, jsonx.V1())
if err != nil {
return []byte("{}")
}
@@ -610,7 +611,7 @@ func (h *DashboardHandler) ociCISubnets(c *echo.Context) error {
}
var subnets []map[string]any
- if json.Unmarshal(rec.Body.Bytes(), &subnets) != nil {
+ if json.Unmarshal(rec.Body.Bytes(), &subnets, jsonx.V1()) != nil {
return c.JSON(http.StatusOK, map[string]any{ociCIKeyItems: []any{}})
}
diff --git a/dashboard/ocicontainerinstances_proxy_test.go b/dashboard/ocicontainerinstances_proxy_test.go
index 19ad734e66..70eff49427 100644
--- a/dashboard/ocicontainerinstances_proxy_test.go
+++ b/dashboard/ocicontainerinstances_proxy_test.go
@@ -3,7 +3,7 @@ package dashboard_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -16,6 +16,7 @@ import (
"github.com/blackbirdworks/gopherstack/dashboard"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ocicontainerinstances"
"github.com/blackbirdworks/gopherstack/services/ocicore"
"github.com/blackbirdworks/gopherstack/services/ociidentity"
@@ -114,7 +115,7 @@ func (f ciFixture) settle(t *testing.T, id, state string) {
State string `json:"lifecycleState"`
}
- return json.Unmarshal(rec.Body.Bytes(), &got) == nil && got.State == state
+ return json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()) == nil && got.State == state
}, 10*time.Second, 5*time.Millisecond)
}
@@ -141,7 +142,7 @@ func ciCreateDirect(t *testing.T, f ciFixture) string {
ID string `json:"id"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &inst))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &inst, jsonx.V1()))
f.settle(t, inst.ID, "ACTIVE")
return inst.ID
@@ -164,7 +165,7 @@ func TestOCIContainerInstancesProxyScrubbing(t *testing.T) {
rec := f.proxy(t, http.MethodGet, ciAPI+"/containers?compartmentId="+ciComp+"&containerInstanceId="+id, "")
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ctrList))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ctrList, jsonx.V1()))
require.Len(t, ctrList.Items, 1)
rawCtr := f.direct(t, http.MethodGet, "/20210415/containers/"+ctrList.Items[0].ID, "")
@@ -240,7 +241,7 @@ func TestOCIContainerInstancesProxyCreateRebuild(t *testing.T) {
ID string `json:"id"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &inst))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &inst, jsonx.V1()))
raw := f.direct(t, http.MethodGet, "/20210415/containerInstances/"+inst.ID, "").Body.String()
for _, dropped := range []string{"mallory", "securityContext", "EMPTYDIR", "registryEndpoint", "runAsUser"} {
@@ -410,7 +411,7 @@ func TestOCIContainerInstancesProxyLogsStateEngine(t *testing.T) {
}
rec := f.proxy(t, http.MethodGet, ciAPI+"/containers?compartmentId="+ciComp+"&containerInstanceId="+id, "")
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ctrs))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ctrs, jsonx.V1()))
require.Len(t, ctrs.Items, 1)
rec = f.proxy(t, http.MethodPost, ciAPI+"/containers/"+ctrs.Items[0].ID+"/actions/retrieveLogs", "")
@@ -442,7 +443,7 @@ func TestOCIContainerInstancesProxyLogsDockerEngine(t *testing.T) {
}
rec := f.proxy(t, http.MethodGet, ciAPI+"/containers?compartmentId="+ciComp+"&containerInstanceId="+id, "")
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ctrs))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ctrs, jsonx.V1()))
require.Len(t, ctrs.Items, 1)
rec = f.proxy(t, http.MethodPost, ciAPI+"/containers/"+ctrs.Items[0].ID+"/actions/retrieveLogs", "")
@@ -496,14 +497,14 @@ func TestOCIContainerInstancesProxySubnets(t *testing.T) {
)
require.Nil(t, aerr)
- vcnJSON, err := json.Marshal(vcn)
+ vcnJSON, err := json.Marshal(vcn, jsonx.V1())
require.NoError(t, err)
var v struct {
ID string `json:"id"`
}
- require.NoError(t, json.Unmarshal(vcnJSON, &v))
+ require.NoError(t, json.Unmarshal(vcnJSON, &v, jsonx.V1()))
_, _, aerr = be.CreateSubnet(ocicore.CreateSubnetIn{
CompartmentID: ciComp, VcnID: v.ID, DisplayName: "sub-a", CidrBlock: "10.0.1.0/24",
diff --git a/dashboard/ocicore_proxy.go b/dashboard/ocicore_proxy.go
index 0436952892..192944e5bd 100644
--- a/dashboard/ocicore_proxy.go
+++ b/dashboard/ocicore_proxy.go
@@ -1,7 +1,7 @@
package dashboard
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -9,6 +9,8 @@ import (
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -93,10 +95,10 @@ func ociCoreRelay(c *echo.Context, rec *httptest.ResponseRecorder) error {
if rec.Code < http.StatusMultipleChoices && len(body) > 0 {
var doc any
- if err := json.Unmarshal(body, &doc); err == nil {
+ if err := json.Unmarshal(body, &doc, jsonx.V1()); err == nil {
ociCoreScrub(doc)
- if out, merr := json.Marshal(doc); merr == nil {
+ if out, merr := json.Marshal(doc, jsonx.V1()); merr == nil {
body = out
}
}
diff --git a/dashboard/ocievents_proxy.go b/dashboard/ocievents_proxy.go
index 6e08fcb14a..292d9b9516 100644
--- a/dashboard/ocievents_proxy.go
+++ b/dashboard/ocievents_proxy.go
@@ -1,12 +1,14 @@
package dashboard
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -161,7 +163,7 @@ func ociEventsCreateBody(c *echo.Context) ([]byte, bool) {
out["description"] = in.Description
}
- b, err := json.Marshal(out)
+ b, err := json.Marshal(out, jsonx.V1())
return b, err == nil
}
@@ -175,7 +177,7 @@ func ociEventsToggleBody(c *echo.Context) ([]byte, bool) {
return nil, false
}
- b, err := json.Marshal(map[string]any{ociEventsEnabledKey: *in.IsEnabled})
+ b, err := json.Marshal(map[string]any{ociEventsEnabledKey: *in.IsEnabled}, jsonx.V1())
return b, err == nil
}
diff --git a/dashboard/ocifunctions_proxy.go b/dashboard/ocifunctions_proxy.go
index 334beef992..9528584260 100644
--- a/dashboard/ocifunctions_proxy.go
+++ b/dashboard/ocifunctions_proxy.go
@@ -2,7 +2,7 @@ package dashboard
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net"
"net/http"
"net/url"
@@ -13,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
fnbackend "github.com/blackbirdworks/gopherstack/services/ocifunctions"
)
@@ -72,13 +73,13 @@ func (h *DashboardHandler) registerOCIFunctionsRoutes() {
// maskedConfig renders a wire value as JSON with secret-looking config values replaced.
func maskedConfig(v any) (map[string]any, error) {
- raw, err := json.Marshal(v)
+ raw, err := json.Marshal(v, jsonx.V1())
if err != nil {
return nil, err
}
out := map[string]any{}
- if err = json.Unmarshal(raw, &out); err != nil {
+ if err = json.Unmarshal(raw, &out, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/dashboard/ocifunctions_proxy_test.go b/dashboard/ocifunctions_proxy_test.go
index 99f0327c39..1085089838 100644
--- a/dashboard/ocifunctions_proxy_test.go
+++ b/dashboard/ocifunctions_proxy_test.go
@@ -1,7 +1,7 @@
package dashboard_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/dashboard"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ocifunctions"
)
@@ -36,7 +37,7 @@ func ociFnCall(
require.NoError(t, h.Handler()(echo.NewContext(req, rec)))
out := map[string]any{}
- _ = json.Unmarshal(rec.Body.Bytes(), &out)
+ _ = json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1())
return rec, out
}
diff --git a/dashboard/ociloadbalancer_proxy.go b/dashboard/ociloadbalancer_proxy.go
index 4dc6e13ad8..24fff74adb 100644
--- a/dashboard/ociloadbalancer_proxy.go
+++ b/dashboard/ociloadbalancer_proxy.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"crypto/x509"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"io"
"maps"
@@ -19,6 +19,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ociloadbalancer"
)
@@ -241,7 +242,7 @@ func ociLBCreateBody(c *echo.Context) ([]byte, bool) {
}}
}
- b, err := json.Marshal(out)
+ b, err := json.Marshal(out, jsonx.V1())
return b, err == nil
}
@@ -311,7 +312,7 @@ func ociLBBackendBody(c *echo.Context, update bool) ([]byte, bool) {
return nil, false
}
- b, err := json.Marshal(out)
+ b, err := json.Marshal(out, jsonx.V1())
return b, err == nil
}
@@ -320,7 +321,7 @@ func ociLBBackendBody(c *echo.Context, update bool) ([]byte, bool) {
// subjects and validity; PEM text never reaches the browser.
func ociLBScrubCerts(raw []byte) []byte {
var v any
- if json.Unmarshal(raw, &v) != nil {
+ if json.Unmarshal(raw, &v, jsonx.V1()) != nil {
return raw
}
@@ -337,7 +338,7 @@ func ociLBScrubCerts(raw []byte) []byte {
}
}
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return []byte("{}")
}
diff --git a/dashboard/ociloadbalancer_proxy_test.go b/dashboard/ociloadbalancer_proxy_test.go
index cd91f7903a..1e409f5829 100644
--- a/dashboard/ociloadbalancer_proxy_test.go
+++ b/dashboard/ociloadbalancer_proxy_test.go
@@ -6,7 +6,7 @@ import (
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"fmt"
"math/big"
@@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/dashboard"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ociloadbalancer"
)
@@ -123,7 +124,7 @@ func newLBDashboard(t *testing.T) lbFixture {
"sslConfiguration": map[string]any{"certificateName": "tls"},
},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := httptest.NewRecorder()
@@ -139,7 +140,7 @@ func newLBDashboard(t *testing.T) lbFixture {
ID string `json:"id"`
}
- require.NoError(t, json.Unmarshal(list.Body.Bytes(), &items), list.Body.String())
+ require.NoError(t, json.Unmarshal(list.Body.Bytes(), &items, jsonx.V1()), list.Body.String())
require.Len(t, items, 1)
f.id = items[0].ID
@@ -287,7 +288,7 @@ func TestOCILoadBalancerProxyActions(t *testing.T) {
} `json:"backendSets"`
}
- require.NoError(t, json.Unmarshal(lbDo(t, f.h, http.MethodGet, lb, "").Body.Bytes(), &detail))
+ require.NoError(t, json.Unmarshal(lbDo(t, f.h, http.MethodGet, lb, "").Body.Bytes(), &detail, jsonx.V1()))
var found bool
@@ -320,7 +321,7 @@ func TestOCILoadBalancerProxyActions(t *testing.T) {
DeleteProtected bool `json:"isDeleteProtectionEnabled"`
}
- require.NoError(t, json.Unmarshal(list.Body.Bytes(), &items), list.Body.String())
+ require.NoError(t, json.Unmarshal(list.Body.Bytes(), &items, jsonx.V1()), list.Body.String())
require.Len(t, items, 1)
assert.Equal(t, "flexible", items[0].Shape)
assert.False(t, items[0].DeleteProtected)
@@ -349,7 +350,7 @@ func TestOCILoadBalancerProxyTryItRoundRobin(t *testing.T) {
Status int `json:"status"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, 200, out.Status)
assert.NotContains(t, out.Headers, "Connection")
assert.NotContains(t, out.Body, "evil.example")
diff --git a/dashboard/ocilogging_proxy.go b/dashboard/ocilogging_proxy.go
index 4810406047..655593c604 100644
--- a/dashboard/ocilogging_proxy.go
+++ b/dashboard/ocilogging_proxy.go
@@ -1,13 +1,15 @@
package dashboard
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"time"
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -95,7 +97,7 @@ func ociLoggingToggle(c *echo.Context, parts []string, raw string) (string, []by
return "", nil, false
}
- b, err := json.Marshal(map[string]any{"isEnabled": *in.Enabled})
+ b, err := json.Marshal(map[string]any{"isEnabled": *in.Enabled}, jsonx.V1())
return ociLoggingMgmt + raw, b, err == nil
}
@@ -140,7 +142,7 @@ func ociLoggingSearchRequest(c *echo.Context) (string, []byte, bool) {
b, err := json.Marshal(map[string]any{
"timeStart": in.TimeStart, "timeEnd": in.TimeEnd, "searchQuery": in.SearchQuery, "isReturnFieldInfo": true,
- })
+ }, jsonx.V1())
return ociLoggingSearch, b, err == nil
}
@@ -161,7 +163,7 @@ func ociLoggingPushRequest(c *echo.Context, logID string) (string, []byte, bool)
"source": "dashboard", "type": "dashboard.test", "defaultlogentrytime": now,
"entries": []any{map[string]any{"data": in.Data, "id": uuid.NewString(), "time": now}},
}},
- })
+ }, jsonx.V1())
return ociLoggingIngest + "/logs/" + logID + "/actions/push", b, err == nil
}
diff --git a/dashboard/ocilogging_proxy_test.go b/dashboard/ocilogging_proxy_test.go
index 730f221f06..b6d29eb996 100644
--- a/dashboard/ocilogging_proxy_test.go
+++ b/dashboard/ocilogging_proxy_test.go
@@ -1,7 +1,7 @@
package dashboard_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/dashboard"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ocilogging"
)
@@ -49,7 +50,7 @@ func newLoggingDashboard(t *testing.T) (*dashboard.DashboardHandler, string, str
}
require.NoError(t, json.Unmarshal(logSeed(t, e, http.MethodGet,
- "/20200531/logGroups?compartmentId="+logComp, ""), &groups))
+ "/20200531/logGroups?compartmentId="+logComp, ""), &groups, jsonx.V1()))
require.Len(t, groups, 1)
logSeed(t, e, http.MethodPost, "/20200531/logGroups/"+groups[0].ID+"/logs",
@@ -60,7 +61,7 @@ func newLoggingDashboard(t *testing.T) (*dashboard.DashboardHandler, string, str
}
require.NoError(t, json.Unmarshal(logSeed(t, e, http.MethodGet,
- "/20200531/logGroups/"+groups[0].ID+"/logs", ""), &logs))
+ "/20200531/logGroups/"+groups[0].ID+"/logs", ""), &logs, jsonx.V1()))
require.Len(t, logs, 1)
return dashboard.NewHandler(dashboard.Config{OCILoggingOps: ops}), groups[0].ID, logs[0].ID
diff --git a/dashboard/ocimonitoring_proxy.go b/dashboard/ocimonitoring_proxy.go
index 101a08faf2..44c441c88e 100644
--- a/dashboard/ocimonitoring_proxy.go
+++ b/dashboard/ocimonitoring_proxy.go
@@ -1,12 +1,14 @@
package dashboard
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -91,7 +93,7 @@ func ociMonitoringListBody(c *echo.Context) ([]byte, bool) {
b, err := json.Marshal(map[string]any{
ociNamespaceKey: in.Namespace, "name": in.Name, "resourceGroup": in.ResourceGroup,
- })
+ }, jsonx.V1())
return b, err == nil
}
@@ -110,7 +112,7 @@ func ociMonitoringSummarizeBody(c *echo.Context) ([]byte, bool) {
return nil, false
}
- b, err := json.Marshal(in)
+ b, err := json.Marshal(in, jsonx.V1())
return b, err == nil
}
diff --git a/dashboard/ocimonitoring_proxy_test.go b/dashboard/ocimonitoring_proxy_test.go
index d05bef329b..696c8c5a9c 100644
--- a/dashboard/ocimonitoring_proxy_test.go
+++ b/dashboard/ocimonitoring_proxy_test.go
@@ -1,7 +1,7 @@
package dashboard_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strconv"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/dashboard"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ocimonitoring"
)
@@ -59,7 +60,7 @@ func newMonitoringDashboard(t *testing.T) (*dashboard.DashboardHandler, string)
}
require.NoError(t, json.Unmarshal(monSeed(t, e, http.MethodGet,
- "/20180401/alarms?compartmentId="+monComp, ""), &alarms))
+ "/20180401/alarms?compartmentId="+monComp, ""), &alarms, jsonx.V1()))
require.Len(t, alarms, 1)
return dashboard.NewHandler(dashboard.Config{OCIMonitoringOps: ops}), alarms[0].ID
diff --git a/dashboard/ocimysql_proxy.go b/dashboard/ocimysql_proxy.go
index 549ad8f913..2f1d816300 100644
--- a/dashboard/ocimysql_proxy.go
+++ b/dashboard/ocimysql_proxy.go
@@ -1,12 +1,14 @@
package dashboard
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -102,7 +104,7 @@ func ociMySQLBody(c *echo.Context, method string, parts []string) ([]byte, bool)
out["displayName"] = in.DisplayName
}
- b, err := json.Marshal(out)
+ b, err := json.Marshal(out, jsonx.V1())
return b, err == nil
}
diff --git a/dashboard/ocimysql_proxy_test.go b/dashboard/ocimysql_proxy_test.go
index 3b9b6ce1f8..d390012fe9 100644
--- a/dashboard/ocimysql_proxy_test.go
+++ b/dashboard/ocimysql_proxy_test.go
@@ -1,7 +1,7 @@
package dashboard_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/dashboard"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ocimysql"
)
@@ -50,7 +51,7 @@ func newMySQLDashboard(t *testing.T) (*dashboard.DashboardHandler, string) {
}
require.NoError(t, json.Unmarshal(mysqlSeed(t, e, http.MethodGet,
- "/20190415/dbSystems?compartmentId="+mysqlComp, ""), &systems))
+ "/20190415/dbSystems?compartmentId="+mysqlComp, ""), &systems, jsonx.V1()))
require.Len(t, systems, 1)
return dashboard.NewHandler(dashboard.Config{OCIMySQLOps: ops}), systems[0].ID
diff --git a/dashboard/ocinosql_proxy.go b/dashboard/ocinosql_proxy.go
index 43197493d6..760f5af674 100644
--- a/dashboard/ocinosql_proxy.go
+++ b/dashboard/ocinosql_proxy.go
@@ -2,11 +2,14 @@ package dashboard
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -135,16 +138,16 @@ func ociNoSQLCreateBody(c *echo.Context) ([]byte, bool) {
b, err := json.Marshal(map[string]any{
ociKeyCompartment: in.CompartmentID, ociKeyName: in.Name, "ddlStatement": in.DDL, "tableLimits": limits,
- })
+ }, jsonx.V1())
return b, err == nil
}
func ociNoSQLRowBody(c *echo.Context) ([]byte, bool) {
var in struct {
- CompartmentID string `json:"compartmentId"`
- Option string `json:"option"`
- Value json.RawMessage `json:"value"`
+ CompartmentID string `json:"compartmentId"`
+ Option string `json:"option"`
+ Value jsontext.Value `json:"value"`
}
if !ociDecodeBody(c, &in) || in.CompartmentID == "" || !bytes.HasPrefix(bytes.TrimSpace(in.Value), []byte("{")) {
@@ -160,7 +163,7 @@ func ociNoSQLRowBody(c *echo.Context) ([]byte, bool) {
out["option"] = in.Option
}
- b, err := json.Marshal(out)
+ b, err := json.Marshal(out, jsonx.V1())
return b, err == nil
}
@@ -182,7 +185,7 @@ func ociNoSQLQueryBody(c *echo.Context) ([]byte, bool) {
out["consistency"] = in.Consistency
}
- b, err := json.Marshal(out)
+ b, err := json.Marshal(out, jsonx.V1())
return b, err == nil
}
diff --git a/dashboard/region_test.go b/dashboard/region_test.go
index c8b9122648..a2cd4d4c4a 100644
--- a/dashboard/region_test.go
+++ b/dashboard/region_test.go
@@ -1,7 +1,7 @@
package dashboard_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/dashboard"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appconfigdata"
)
@@ -30,7 +31,7 @@ func profileCount(t *testing.T, h *dashboard.DashboardHandler, query string) int
Profiles []any `json:"profiles"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return len(out.Profiles)
}
diff --git a/dashboard/ui.go b/dashboard/ui.go
index 21a764e3a4..2f213152f0 100644
--- a/dashboard/ui.go
+++ b/dashboard/ui.go
@@ -3,7 +3,8 @@ package dashboard
import (
"context"
"embed"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"html"
"io"
@@ -29,6 +30,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/chaos"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc"
"github.com/blackbirdworks/gopherstack/pkgs/service"
acmbackend "github.com/blackbirdworks/gopherstack/services/acm"
@@ -846,7 +848,7 @@ func (h *DashboardHandler) logEmbeddedSPABuildStamp() {
}
var stamp spaBuildStamp
- if jsonErr := json.Unmarshal(data, &stamp); jsonErr != nil {
+ if jsonErr := json.Unmarshal(data, &stamp, jsonx.V1()); jsonErr != nil {
h.Logger.Warn("embedded dashboard SPA build-stamp.json is malformed", "error", jsonErr)
return
@@ -1087,7 +1089,7 @@ func (h *DashboardHandler) registerCodeStarConnectionsMutationRoutes() {
ProviderType string `json:"providerType"`
HostArn string `json:"hostArn"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &req, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{keyError: err.Error()})
}
@@ -1130,7 +1132,7 @@ func (h *DashboardHandler) registerCodeStarConnectionsMutationRoutes() {
ProviderType string `json:"providerType"`
ProviderEndpoint string `json:"providerEndpoint"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &req, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{keyError: err.Error()})
}
@@ -1178,7 +1180,7 @@ func (h *DashboardHandler) registerCognitoUserPoolRoutes() {
var req struct {
Name string `json:"name"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &req, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{keyError: err.Error()})
}
@@ -1239,7 +1241,7 @@ func (h *DashboardHandler) registerCognitoUserPoolClientRoutes() {
var req struct {
ClientName string `json:"clientName"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &req, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{keyError: err.Error()})
}
@@ -1304,7 +1306,7 @@ func (h *DashboardHandler) registerCognitoUserRoutes() {
Username string `json:"username"`
TempPassword string `json:"tempPassword"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &req, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{keyError: err.Error()})
}
@@ -1356,7 +1358,7 @@ func (h *DashboardHandler) registerCognitoUserLifecycleRoutes() {
var req struct {
Enabled bool `json:"enabled"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &req, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{keyError: err.Error()})
}
@@ -1409,7 +1411,7 @@ func (h *DashboardHandler) registerCognitoGroupRoutes() {
GroupName string `json:"groupName"`
Description string `json:"description"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &req, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{keyError: err.Error()})
}
@@ -1461,7 +1463,7 @@ func (h *DashboardHandler) registerCognitoUserPoolConfigRoutes() {
var req struct {
MfaConfiguration string `json:"mfaConfiguration"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &req, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{keyError: err.Error()})
}
@@ -1677,7 +1679,7 @@ func (h *DashboardHandler) registerAppConfigDataProfileRoutes() {
}
var req setProfileRequest
- if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &req, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
map[string]string{keyMessage: "invalid request body"},
diff --git a/dashboard/ui_test.go b/dashboard/ui_test.go
index 21027f50b3..119b78fbf5 100644
--- a/dashboard/ui_test.go
+++ b/dashboard/ui_test.go
@@ -1,7 +1,7 @@
package dashboard_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"regexp"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/dashboard"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -72,7 +73,7 @@ func TestSystemSettings_HappyPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "123456789012", body["accountID"])
assert.Equal(t, "eu-west-1", body["region"])
@@ -134,7 +135,7 @@ func TestSystemSettings_NoSecretShapedKeys(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
secretLike := regexp.MustCompile(`(?i)secret|password|token|credential|accesskey`)
for key := range body {
@@ -184,7 +185,7 @@ func TestSystemRegions(t *testing.T) {
var body struct {
Regions []string `json:"regions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
for _, want := range tt.wantContains {
assert.Contains(t, body.Regions, want)
diff --git a/demo/wire.go b/demo/wire.go
index ff1e4d9338..ad726732fd 100644
--- a/demo/wire.go
+++ b/demo/wire.go
@@ -2,12 +2,13 @@ package demo
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"github.com/aws/aws-sdk-go-v2/aws"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions/sdkwire"
)
@@ -35,13 +36,13 @@ func NewWireCaller(key string, handler http.Handler, creds aws.CredentialsProvid
}
func (c *wireCaller) Call(ctx context.Context, op string, params any) (map[string]any, error) {
- b, err := json.Marshal(params)
+ b, err := json.Marshal(params, jsonx.V1())
if err != nil {
return nil, err
}
var in map[string]any
- if err = json.Unmarshal(b, &in); err != nil {
+ if err = json.Unmarshal(b, &in, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/examples/azure-keyvault-app/azure-keyvault-app b/examples/azure-keyvault-app/azure-keyvault-app
new file mode 100755
index 0000000000..369aba431c
Binary files /dev/null and b/examples/azure-keyvault-app/azure-keyvault-app differ
diff --git a/examples/azure-keyvault-app/main.go b/examples/azure-keyvault-app/main.go
index 35a0d12d10..d3f598897c 100644
--- a/examples/azure-keyvault-app/main.go
+++ b/examples/azure-keyvault-app/main.go
@@ -9,7 +9,7 @@ import (
"crypto/sha256"
"crypto/tls"
"crypto/x509"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"math/big"
diff --git a/examples/azure-servicebus-cosmos-worker/azure-servicebus-cosmos-worker b/examples/azure-servicebus-cosmos-worker/azure-servicebus-cosmos-worker
new file mode 100755
index 0000000000..79988d5bdb
Binary files /dev/null and b/examples/azure-servicebus-cosmos-worker/azure-servicebus-cosmos-worker differ
diff --git a/examples/azure-servicebus-cosmos-worker/feed.go b/examples/azure-servicebus-cosmos-worker/feed.go
index 2746facd95..01484efef5 100644
--- a/examples/azure-servicebus-cosmos-worker/feed.go
+++ b/examples/azure-servicebus-cosmos-worker/feed.go
@@ -2,7 +2,7 @@ package main
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
diff --git a/examples/azure-servicebus-cosmos-worker/servicebus.go b/examples/azure-servicebus-cosmos-worker/servicebus.go
index 777f72d7f7..d6eeb8db53 100644
--- a/examples/azure-servicebus-cosmos-worker/servicebus.go
+++ b/examples/azure-servicebus-cosmos-worker/servicebus.go
@@ -2,7 +2,7 @@ package main
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
diff --git a/examples/azure-servicebus-cosmos-worker/worker.go b/examples/azure-servicebus-cosmos-worker/worker.go
index 88e86c8be6..39474d1a2f 100644
--- a/examples/azure-servicebus-cosmos-worker/worker.go
+++ b/examples/azure-servicebus-cosmos-worker/worker.go
@@ -2,7 +2,7 @@ package main
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"time"
diff --git a/examples/extensions/hello/main.go b/examples/extensions/hello/main.go
index 62d69c0a5a..ebfec4c503 100644
--- a/examples/extensions/hello/main.go
+++ b/examples/extensions/hello/main.go
@@ -4,13 +4,16 @@ package main
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"log"
"net"
"net/http"
"os"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -33,7 +36,7 @@ func main() {
mux.HandleFunc("/hooks/s3/pre", func(_ http.ResponseWriter, r *http.Request) {
var req hookRequest
- _ = json.NewDecoder(r.Body).Decode(&req)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(r.Body), &req, jsonx.V1())
log.Printf("s3 request %q %q", req.Method, req.Path)
})
@@ -66,7 +69,7 @@ func register() error {
{"services": []string{"S3"}, "pre": "/hooks/s3/pre", "timeout_ms": hookTimeoutMs},
},
},
- })
+ }, jsonx.V1())
if err != nil {
return err
}
diff --git a/examples/gcp-run-pubsub-push/service/main.go b/examples/gcp-run-pubsub-push/service/main.go
index 041882a684..7deaa6eaff 100644
--- a/examples/gcp-run-pubsub-push/service/main.go
+++ b/examples/gcp-run-pubsub-push/service/main.go
@@ -4,7 +4,7 @@ package main
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"log"
"net/http"
"os"
@@ -33,7 +33,7 @@ type receiver struct {
func (r *receiver) push(w http.ResponseWriter, req *http.Request) {
var env pushEnvelope
- if err := json.NewDecoder(req.Body).Decode(&env); err != nil {
+ if err := json.UnmarshalRead(req.Body, &env); err != nil {
http.Error(w, "bad push envelope", http.StatusBadRequest)
return
@@ -62,7 +62,7 @@ func (r *receiver) list(w http.ResponseWriter, _ *http.Request) {
r.mu.Unlock()
w.Header().Set("Content-Type", "application/json")
- _ = json.NewEncoder(w).Encode(out)
+ _ = json.MarshalWrite(w, out)
}
func main() {
diff --git a/internal/app/adapters_cosmos.go b/internal/app/adapters_cosmos.go
index 97f06b39a7..3581308e24 100644
--- a/internal/app/adapters_cosmos.go
+++ b/internal/app/adapters_cosmos.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
@@ -106,9 +106,9 @@ func (a *azureCosmosAdapter) DeleteDatabase(id string) error {
func containerSpec(c azurearm.CosmosContainer) cosmosdb.ContainerSpec {
return cosmosdb.ContainerSpec{
ID: c.ID, PartitionKeyPaths: c.PartitionKeyPaths, DefaultTTL: c.DefaultTTL,
- IndexingPolicy: json.RawMessage(c.IndexingPolicy),
- ConflictResolutionPolicy: json.RawMessage(c.ConflictResolutionPolicy),
- UniqueKeyPolicy: json.RawMessage(c.UniqueKeyPolicy),
+ IndexingPolicy: jsontext.Value(c.IndexingPolicy),
+ ConflictResolutionPolicy: jsontext.Value(c.ConflictResolutionPolicy),
+ UniqueKeyPolicy: jsontext.Value(c.UniqueKeyPolicy),
}
}
diff --git a/internal/app/adapters_cosmos_throughput_test.go b/internal/app/adapters_cosmos_throughput_test.go
index 5b4d5abca2..bf3ad78dfb 100644
--- a/internal/app/adapters_cosmos_throughput_test.go
+++ b/internal/app/adapters_cosmos_throughput_test.go
@@ -2,7 +2,7 @@ package app
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azurearm"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -40,7 +41,7 @@ func armCosmosDo(t *testing.T, h http.Handler, method, path string, body any) (i
if body != nil {
var err error
- raw, err = json.Marshal(body)
+ raw, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -53,7 +54,7 @@ func armCosmosDo(t *testing.T, h http.Handler, method, path string, body any) (i
var out map[string]any
if rec.Body.Len() > 0 {
- _ = json.Unmarshal(rec.Body.Bytes(), &out)
+ _ = json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1())
}
return rec.Code, out
diff --git a/internal/app/app_inspector_fanout_test.go b/internal/app/app_inspector_fanout_test.go
index 5256ff6622..365a519b36 100644
--- a/internal/app/app_inspector_fanout_test.go
+++ b/internal/app/app_inspector_fanout_test.go
@@ -2,7 +2,7 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -20,6 +20,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/apptrace"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const fanoutQueueName = "fanout-queue"
@@ -55,7 +56,7 @@ func (f *fanoutEnv) spans(t *testing.T, query string) []apptrace.Span {
require.Equal(t, http.StatusOK, rec.Code)
var res apptrace.ListResult
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &res))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &res, jsonx.V1()))
return res.Items
}
diff --git a/internal/app/app_inspector_test.go b/internal/app/app_inspector_test.go
index 508d8f6ee3..cbf8686132 100644
--- a/internal/app/app_inspector_test.go
+++ b/internal/app/app_inspector_test.go
@@ -2,7 +2,7 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/apptrace"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func inspectorServer(t *testing.T) (*echo.Echo, *apptrace.Tracer) {
@@ -69,7 +70,7 @@ func TestAppInspectorEndpoints(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var res apptrace.ListResult
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &res))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &res, jsonx.V1()))
assert.Equal(t, tc.total, res.Total)
})
}
@@ -93,7 +94,7 @@ func TestAppInspectorDetailTraceAndConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var detail appInspectorDetail
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &detail))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &detail, jsonx.V1()))
require.NotNil(t, detail.Span.Detail)
assert.NotContains(t, detail.Span.Detail.RequestBody, "hunter2")
require.Len(t, detail.Trace, 2)
diff --git a/internal/app/appsync_datasources_wiring.go b/internal/app/appsync_datasources_wiring.go
index fc57744235..00919a74dc 100644
--- a/internal/app/appsync_datasources_wiring.go
+++ b/internal/app/appsync_datasources_wiring.go
@@ -3,7 +3,7 @@ package app
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -13,6 +13,7 @@ import (
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
appsyncbackend "github.com/blackbirdworks/gopherstack/services/appsync"
eventbridgebackend "github.com/blackbirdworks/gopherstack/services/eventbridge"
@@ -173,14 +174,14 @@ func (a appsyncRDSData) ExecuteRDSStatement(
raw, err := json.Marshal(map[string]any{
"records": nonNil(records), "columnMetadata": nonNil(cols),
"numberOfRecordsUpdated": updated, "generatedFields": nonNil(generated),
- })
+ }, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("encode result: %w", err)
}
var out map[string]any
- return out, json.Unmarshal(raw, &out)
+ return out, json.Unmarshal(raw, &out, jsonx.V1())
}
func nonNil[T any](s []T) []T {
diff --git a/internal/app/appsync_datasources_wiring_test.go b/internal/app/appsync_datasources_wiring_test.go
index fc773e9481..e3d012f116 100644
--- a/internal/app/appsync_datasources_wiring_test.go
+++ b/internal/app/appsync_datasources_wiring_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -17,6 +17,8 @@ import (
sqstypes "github.com/aws/aws-sdk-go-v2/service/sqs/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -95,7 +97,7 @@ func (f *asDSFixture) query(t *testing.T, field string, vars map[string]any) any
query = "query(" + strings.Join(decls, ", ") + ") { " + field + " }"
}
- body, err := json.Marshal(map[string]any{"query": query, "variables": vars})
+ body, err := json.Marshal(map[string]any{"query": query, "variables": vars}, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/v1/apis/"+f.apiID+"/graphql", strings.NewReader(string(body)))
@@ -112,7 +114,7 @@ func (f *asDSFixture) query(t *testing.T, field string, vars map[string]any) any
Message string `json:"message"`
} `json:"errors"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out), rec.Body.String())
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()), rec.Body.String())
require.Empty(t, out.Errors, rec.Body.String())
name, _, _ := strings.Cut(field, "(")
@@ -129,7 +131,7 @@ func asJSONValue(t *testing.T, v any) any {
}
var out any
- require.NoError(t, json.Unmarshal([]byte(s), &out), s)
+ require.NoError(t, json.Unmarshal([]byte(s), &out, jsonx.V1()), s)
return out
}
diff --git a/internal/app/appsync_dynamodb_resolvers_test.go b/internal/app/appsync_dynamodb_resolvers_test.go
index a7c70f051f..613ec4fc2e 100644
--- a/internal/app/appsync_dynamodb_resolvers_test.go
+++ b/internal/app/appsync_dynamodb_resolvers_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -14,6 +14,8 @@ import (
ddbsdktypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const asDDBSchema = `
@@ -256,7 +258,7 @@ type asGQLResponse struct {
func (f *asDDBFixture) do(t *testing.T, query string) asGQLResponse {
t.Helper()
- body, err := json.Marshal(map[string]any{"query": query})
+ body, err := json.Marshal(map[string]any{"query": query}, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/v1/apis/"+f.apiID+"/graphql", strings.NewReader(string(body)))
@@ -268,7 +270,7 @@ func (f *asDDBFixture) do(t *testing.T, query string) asGQLResponse {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out asGQLResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out), rec.Body.String())
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()), rec.Body.String())
return out
}
diff --git a/internal/app/azureeventgrid_domains_sdk_test.go b/internal/app/azureeventgrid_domains_sdk_test.go
index f4f52fdfb2..a82377c15c 100644
--- a/internal/app/azureeventgrid_domains_sdk_test.go
+++ b/internal/app/azureeventgrid_domains_sdk_test.go
@@ -1,7 +1,8 @@
package app
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/azurearm"
"github.com/blackbirdworks/gopherstack/services/azureeventgrid"
@@ -42,9 +44,9 @@ func newEGReceiver(t *testing.T) *egReceiver {
if req.Header.Get("Aeg-Event-Type") == "SubscriptionValidation" {
var evs []map[string]any
- _ = json.Unmarshal(body, &evs)
+ _ = json.Unmarshal(body, &evs, jsonx.V1())
code, _ := evs[0]["data"].(map[string]any)["validationCode"].(string)
- _ = json.NewEncoder(w).Encode(map[string]string{"validationResponse": code})
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), map[string]string{"validationResponse": code})
return
}
@@ -70,7 +72,7 @@ func (r *egReceiver) events(t *testing.T) ([]map[string]any, int) {
for _, b := range r.bodies {
var arr []map[string]any
- require.NoError(t, json.Unmarshal(b, &arr))
+ require.NoError(t, json.Unmarshal(b, &arr, jsonx.V1()))
out = append(out, arr...)
}
diff --git a/internal/app/azureeventgrid_scopes_sdk_test.go b/internal/app/azureeventgrid_scopes_sdk_test.go
index 1d16bb84dc..c9f7aec17b 100644
--- a/internal/app/azureeventgrid_scopes_sdk_test.go
+++ b/internal/app/azureeventgrid_scopes_sdk_test.go
@@ -3,7 +3,7 @@ package app
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"reflect"
"strings"
@@ -14,6 +14,8 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/eventgrid/armeventgrid/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -25,7 +27,7 @@ const (
func (s *egDomainStack) armDo(t *testing.T, method, path string, body any) *http.Response {
t.Helper()
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(t.Context(), method, s.armSrv.URL+path+"?api-version=2021-04-01",
diff --git a/internal/app/azureeventgrid_wiring_test.go b/internal/app/azureeventgrid_wiring_test.go
index 6b456b3bb1..90bf09095d 100644
--- a/internal/app/azureeventgrid_wiring_test.go
+++ b/internal/app/azureeventgrid_wiring_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/azureblob"
@@ -129,7 +130,7 @@ func jsonField(t *testing.T, raw []byte, key string) string {
var m map[string]any
- require.NoError(t, json.Unmarshal(raw, &m))
+ require.NoError(t, json.Unmarshal(raw, &m, jsonx.V1()))
s, _ := m[key].(string)
diff --git a/internal/app/azuregraph_wiring_test.go b/internal/app/azuregraph_wiring_test.go
index 30ff349c3c..ea6b891ed1 100644
--- a/internal/app/azuregraph_wiring_test.go
+++ b/internal/app/azuregraph_wiring_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/azurearm"
"github.com/blackbirdworks/gopherstack/services/azuregraph"
@@ -86,7 +87,7 @@ func do(
require.NoError(t, err)
var out map[string]any
- _ = json.Unmarshal(raw, &out)
+ _ = json.Unmarshal(raw, &out, jsonx.V1())
return resp.StatusCode, out
}
diff --git a/internal/app/cli.go b/internal/app/cli.go
index acc58566b0..49a8b8004b 100644
--- a/internal/app/cli.go
+++ b/internal/app/cli.go
@@ -8,12 +8,14 @@ import (
sqssdk "github.com/blackbirdworks/gopherstack/internal/awswire/sqs"
ssmsdk "github.com/blackbirdworks/gopherstack/internal/awswire/ssm"
"github.com/blackbirdworks/gopherstack/internal/routegates"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/internal/cliconfig"
"context"
"crypto/tls"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"log/slog"
@@ -1391,7 +1393,7 @@ func (c *CLI) SaveConfig() error {
StepFunctions: c.StepFunctions,
}
- data, err := json.MarshalIndent(cfg, "", " ")
+ data, err := json.Marshal(cfg, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
if err != nil {
return fmt.Errorf("failed to marshal config: %w", err)
}
@@ -1415,7 +1417,7 @@ func (c *CLI) LoadConfig() error {
}
var cfg cliconfig.PersistableConfig
- if unmarshalErr := json.Unmarshal(data, &cfg); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(data, &cfg, jsonx.V1()); unmarshalErr != nil {
return fmt.Errorf("failed to unmarshal config: %w", unmarshalErr)
}
@@ -3287,7 +3289,7 @@ func buildResetHandler(services []service.Registerable) echo.HandlerFunc {
// payload stays human-readable without an extra base64 layer.
type snapshotBundle struct {
// Services maps each registered service name to its raw JSON snapshot.
- Services map[string]json.RawMessage `json:"services"`
+ Services map[string]jsontext.Value `json:"services"`
// Format is a fixed version tag; currently "gopherstack-snapshot/v1".
Format string `json:"format"`
}
@@ -3326,9 +3328,9 @@ func buildSnapshotHandler(m *persistence.Manager) echo.HandlerFunc {
return func(c *echo.Context) error {
raw := m.ExportAll()
- services := make(map[string]json.RawMessage, len(raw))
+ services := make(map[string]jsontext.Value, len(raw))
for name, data := range raw {
- services[name] = json.RawMessage(data)
+ services[name] = jsontext.Value(data)
}
resp := snapshotResponse{
@@ -3352,7 +3354,7 @@ func buildLoadHandler(m *persistence.Manager) echo.HandlerFunc {
return func(c *echo.Context) error {
var bundle snapshotBundle
- if err := json.NewDecoder(c.Request().Body).Decode(&bundle); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &bundle, jsonx.V1()); err != nil {
return echo.NewHTTPError(http.StatusBadRequest,
fmt.Sprintf("invalid snapshot bundle: %s", err.Error()))
}
@@ -5969,7 +5971,7 @@ func buildECSRunInput(
) ecsbackend.RunTaskInput {
var input map[string]any
if len(payload) > 0 {
- _ = json.Unmarshal(payload, &input)
+ _ = json.Unmarshal(payload, &input, jsonx.V1())
}
runInput := ecsbackend.RunTaskInput{
@@ -6605,7 +6607,7 @@ func (a *cognitoLambdaTriggerAdapter) InvokeTrigger(
functionARN string,
event map[string]any,
) (map[string]any, error) {
- payload, err := json.Marshal(event)
+ payload, err := json.Marshal(event, jsonx.V1())
if err != nil {
return nil, err
}
@@ -6618,7 +6620,7 @@ func (a *cognitoLambdaTriggerAdapter) InvokeTrigger(
}
var resp map[string]any
- if err = json.Unmarshal(result, &resp); err != nil {
+ if err = json.Unmarshal(result, &resp, jsonx.V1()); err != nil {
return nil, err
}
@@ -14614,7 +14616,7 @@ func (a *iotAnalyticsThingShadowAdapter) GetThingShadow(thingName string) (map[s
}
var data map[string]any
- if unmarshalErr := json.Unmarshal(doc, &data); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(doc, &data, jsonx.V1()); unmarshalErr != nil {
return nil, fmt.Errorf("iotanalytics: unmarshal thing shadow document: %w", unmarshalErr)
}
@@ -14697,7 +14699,7 @@ func (a *ddbKinesisEmitterAdapter) EmitDynamoDBStreamRecord(
ctx, streamName := kinesisRefContext(context.Background(), streamARN)
partitionKey := tableName
- if keys, err := json.Marshal(record.Keys); err == nil {
+ if keys, err := json.Marshal(record.Keys, jsonx.V1()); err == nil {
partitionKey = string(keys)
}
@@ -14717,7 +14719,7 @@ func (a *ddbKinesisEmitterAdapter) EmitDynamoDBStreamRecord(
ApproximateCreationDateTime: record.ApproximateCreationDateTime,
SizeBytes: record.SizeBytes,
},
- })
+ }, jsonx.V1())
if err != nil {
return
}
diff --git a/internal/app/cli_test.go b/internal/app/cli_test.go
index c3403cb41f..59c5e09539 100644
--- a/internal/app/cli_test.go
+++ b/internal/app/cli_test.go
@@ -3,7 +3,8 @@ package app
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -21,6 +22,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/dns/dnstest"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svctags "github.com/blackbirdworks/gopherstack/pkgs/tags"
@@ -895,7 +897,7 @@ func TestWireResourceGroupsTagging_CrossServiceResources(t *testing.T) {
wafBk := wafv2backend.NewInMemoryBackend(accountID, region)
webACL, err := wafBk.CreateWebACL(
context.Background(), "wiring-test-acl", "REGIONAL", "",
- json.RawMessage(`{"Allow":{}}`), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ jsontext.Value(`{"Allow":{}}`), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
)
require.NoError(t, err)
require.NoError(t, wafBk.TagResource(
@@ -1527,7 +1529,7 @@ func TestWireResourceGroupsTagging_CrossServiceResources(t *testing.T) {
"SubnetIds": []string{"subnet-1", "subnet-2"},
"SecurityGroupIds": []string{"sg-1"},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPut, "/environments/wiring-test-env", bytes.NewReader(body))
@@ -2355,7 +2357,7 @@ func TestWireResourceGroupsTagging_TagResourcesRoundTrip(t *testing.T) {
wafBk := wafv2backend.NewInMemoryBackend(accountID, region)
webACL, err := wafBk.CreateWebACL(
context.Background(), "roundtrip-acl", "REGIONAL", "",
- json.RawMessage(`{"Allow":{}}`), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ jsontext.Value(`{"Allow":{}}`), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
)
require.NoError(t, err)
@@ -3102,7 +3104,7 @@ func TestHealthEndpoint_GoroutineAndMemStats(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var body map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&body))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &body, jsonx.V1()))
// Verify the new observability fields are present.
assert.Contains(t, body, "goroutines", "health response must include goroutines count")
@@ -3208,7 +3210,8 @@ func TestLocalstackCompatibilityEndpoints(t *testing.T) {
waitForEndpoint(t, fmt.Sprintf("http://localhost:%d/_localstack/init/ready", port), func(resp *http.Response) bool {
var body map[string]any
- return json.NewDecoder(resp.Body).Decode(&body) == nil && body["completed"] == true
+ return json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &body, jsonx.V1()) == nil &&
+ body["completed"] == true
})
for _, tt := range tests {
@@ -3219,7 +3222,7 @@ func TestLocalstackCompatibilityEndpoints(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var body map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&body))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &body, jsonx.V1()))
tt.validate(t, body)
})
}
@@ -3384,7 +3387,7 @@ func TestBuildSnapshotHandler_EmptyManager(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp snapshotResponse
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, snapshotBundleFormat, resp.Format)
assert.Equal(t, 0, resp.Exported)
assert.Empty(t, resp.Services)
@@ -3445,7 +3448,7 @@ func TestBuildSnapshotHandler(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp snapshotResponse
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, snapshotBundleFormat, resp.Format)
assert.Equal(t, "ok", resp.Status)
@@ -3469,14 +3472,14 @@ func TestBuildSnapshotHandler_ResponseIsValidJSON(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp snapshotResponse
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
// Service snapshot must be embedded as raw JSON, not base64.
rawSvc, ok := resp.Services["svc"]
require.True(t, ok, "svc key must be present")
var parsed map[string]any
- require.NoError(t, json.Unmarshal(rawSvc, &parsed))
+ require.NoError(t, json.Unmarshal(rawSvc, &parsed, jsonx.V1()))
assert.Equal(t, []any{float64(1), float64(2), float64(3)}, parsed["items"])
}
@@ -3490,14 +3493,14 @@ func TestBuildLoadHandler_EmptyBundle(t *testing.T) {
body, _ := json.Marshal(snapshotBundle{
Format: snapshotBundleFormat,
- Services: map[string]json.RawMessage{},
- })
+ Services: map[string]jsontext.Value{},
+ }, jsonx.V1())
rec := postJSON(t, handler, body)
assert.Equal(t, http.StatusOK, rec.Code)
var resp loadResponse
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, "ok", resp.Status)
assert.Equal(t, 0, resp.Loaded)
}
@@ -3505,7 +3508,7 @@ func TestBuildLoadHandler_EmptyBundle(t *testing.T) {
func TestBuildLoadHandler(t *testing.T) {
t.Parallel()
- snap := json.RawMessage(`{"restored":true}`)
+ snap := jsontext.Value(`{"restored":true}`)
tests := []struct {
setupErr error
@@ -3518,7 +3521,7 @@ func TestBuildLoadHandler(t *testing.T) {
name: "single_service_loaded",
bundle: snapshotBundle{
Format: snapshotBundleFormat,
- Services: map[string]json.RawMessage{"svc": snap},
+ Services: map[string]jsontext.Value{"svc": snap},
},
wantStatusCode: http.StatusOK,
wantLoaded: 1,
@@ -3527,7 +3530,7 @@ func TestBuildLoadHandler(t *testing.T) {
name: "unknown_service_skipped",
bundle: snapshotBundle{
Format: snapshotBundleFormat,
- Services: map[string]json.RawMessage{"unknown": snap},
+ Services: map[string]jsontext.Value{"unknown": snap},
},
wantStatusCode: http.StatusOK,
wantLoaded: 1,
@@ -3537,7 +3540,7 @@ func TestBuildLoadHandler(t *testing.T) {
setupErr: errRestoreFailure,
bundle: snapshotBundle{
Format: snapshotBundleFormat,
- Services: map[string]json.RawMessage{"svc": snap},
+ Services: map[string]jsontext.Value{"svc": snap},
},
wantStatusCode: http.StatusInternalServerError,
},
@@ -3555,7 +3558,7 @@ func TestBuildLoadHandler(t *testing.T) {
e.HTTPErrorHandler = buildHTTPErrorHandler()
e.POST("/", handler)
- body, marshalErr := json.Marshal(tt.bundle)
+ body, marshalErr := json.Marshal(tt.bundle, jsonx.V1())
require.NoError(t, marshalErr)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
@@ -3567,7 +3570,7 @@ func TestBuildLoadHandler(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp loadResponse
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, "ok", resp.Status)
assert.Equal(t, tt.wantLoaded, resp.Loaded)
} else {
@@ -3649,7 +3652,7 @@ func TestSnapshotLoadRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, snapRec.Code)
var snapResp snapshotResponse
- require.NoError(t, json.NewDecoder(snapRec.Body).Decode(&snapResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(snapRec.Body), &snapResp, jsonx.V1()))
assert.Equal(t, len(tt.initial), snapResp.Exported)
// Step 2: Reset all services.
@@ -3662,7 +3665,7 @@ func TestSnapshotLoadRoundTrip(t *testing.T) {
}
// Step 3: POST /_gopherstack/load with the exported snapshot.
- loadBody, err := json.Marshal(snapResp.snapshotBundle)
+ loadBody, err := json.Marshal(snapResp.snapshotBundle, jsonx.V1())
require.NoError(t, err)
loadReq := httptest.NewRequest(
@@ -3678,7 +3681,7 @@ func TestSnapshotLoadRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, loadRec.Code)
var loadResp loadResponse
- require.NoError(t, json.NewDecoder(loadRec.Body).Decode(&loadResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(loadRec.Body), &loadResp, jsonx.V1()))
assert.Equal(t, "ok", loadResp.Status)
assert.Equal(t, len(tt.initial), loadResp.Loaded)
@@ -3711,10 +3714,10 @@ func TestSnapshotLoadRoundTrip_CrossManagerLoad(t *testing.T) {
require.Equal(t, http.StatusOK, snapRec.Code)
var snapResp snapshotResponse
- require.NoError(t, json.NewDecoder(snapRec.Body).Decode(&snapResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(snapRec.Body), &snapResp, jsonx.V1()))
// Load into mgrB.
- loadBody, err := json.Marshal(snapResp.snapshotBundle)
+ loadBody, err := json.Marshal(snapResp.snapshotBundle, jsonx.V1())
require.NoError(t, err)
loadReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(loadBody))
@@ -3741,7 +3744,7 @@ func TestSnapshotLoadRoundTrip_EmptyServicesProduceEmptyBundle(t *testing.T) {
require.Equal(t, http.StatusOK, snapRec.Code)
var snapResp snapshotResponse
- require.NoError(t, json.NewDecoder(snapRec.Body).Decode(&snapResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(snapRec.Body), &snapResp, jsonx.V1()))
assert.Equal(t, 0, snapResp.Exported, "nil-snapshot service must not be exported")
assert.Empty(t, snapResp.Services)
}
@@ -3762,7 +3765,7 @@ func TestBuildEchoServer_SnapshotLoadRoutes(t *testing.T) {
e.ServeHTTP(snapshotRec, snapshotReq)
assert.NotEqual(t, http.StatusNotFound, snapshotRec.Code, "snapshot route must be registered")
- loadBody, _ := json.Marshal(snapshotBundle{Format: snapshotBundleFormat, Services: nil})
+ loadBody, _ := json.Marshal(snapshotBundle{Format: snapshotBundleFormat, Services: nil}, jsonx.V1())
loadReq := httptest.NewRequest(http.MethodPost, "/_gopherstack/load", bytes.NewReader(loadBody))
loadReq.Header.Set("Content-Type", "application/json")
loadRec := httptest.NewRecorder()
@@ -3791,7 +3794,7 @@ func TestBuildSnapshotHandler_MultipleServices_StatePreservedIndependently(t *te
require.Equal(t, http.StatusOK, snapRec.Code)
var snapResp snapshotResponse
- require.NoError(t, json.NewDecoder(snapRec.Body).Decode(&snapResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(snapRec.Body), &snapResp, jsonx.V1()))
assert.Equal(t, 3, snapResp.Exported)
// Reset.
@@ -3800,7 +3803,7 @@ func TestBuildSnapshotHandler_MultipleServices_StatePreservedIndependently(t *te
}
// Load.
- loadBody, err := json.Marshal(snapResp.snapshotBundle)
+ loadBody, err := json.Marshal(snapResp.snapshotBundle, jsonx.V1())
require.NoError(t, err)
loadRec := httptest.NewRecorder()
diff --git a/internal/app/cloudcontrol_handlers.go b/internal/app/cloudcontrol_handlers.go
index 78b3e3bc6b..d46ebc30ad 100644
--- a/internal/app/cloudcontrol_handlers.go
+++ b/internal/app/cloudcontrol_handlers.go
@@ -2,7 +2,7 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -28,6 +28,7 @@ import (
"github.com/blackbirdworks/gopherstack/internal/awswire/cloudwatchlogs"
"github.com/blackbirdworks/gopherstack/internal/awswire/sqs"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
cloudcontrolbackend "github.com/blackbirdworks/gopherstack/services/cloudcontrol"
cfnbackend "github.com/blackbirdworks/gopherstack/services/cloudformation"
@@ -82,12 +83,12 @@ func tagsProperty(m map[string]string) []map[string]string {
func ccDecode[T any](raw any) (T, error) {
var out T
- b, err := json.Marshal(raw)
+ b, err := json.Marshal(raw, jsonx.V1())
if err != nil {
return out, fmt.Errorf("%w: %w", cloudcontrolbackend.ErrValidation, err)
}
- if err = json.Unmarshal(b, &out); err != nil {
+ if err = json.Unmarshal(b, &out, jsonx.V1()); err != nil {
return out, fmt.Errorf("%w: %w", cloudcontrolbackend.ErrValidation, err)
}
@@ -147,13 +148,13 @@ func ccRejectUnsupportedChanges(current, desired map[string]any, allowed ...stri
}
func normalizeJSON(v any) any {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return v
}
var out any
- if json.Unmarshal(b, &out) != nil {
+ if json.Unmarshal(b, &out, jsonx.V1()) != nil {
return v
}
diff --git a/internal/app/cloudcontrol_handlers_security.go b/internal/app/cloudcontrol_handlers_security.go
index dcdd987b3e..11524dd0c4 100644
--- a/internal/app/cloudcontrol_handlers_security.go
+++ b/internal/app/cloudcontrol_handlers_security.go
@@ -2,7 +2,7 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"math"
"net/url"
@@ -17,6 +17,7 @@ import (
"github.com/blackbirdworks/gopherstack/internal/awswire/kms"
"github.com/blackbirdworks/gopherstack/internal/awswire/secretsmanager"
"github.com/blackbirdworks/gopherstack/internal/awswire/ssm"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
cloudcontrolbackend "github.com/blackbirdworks/gopherstack/services/cloudcontrol"
)
@@ -78,7 +79,7 @@ func ccPolicyJSON(v any) (string, error) {
return s, nil
}
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return "", fmt.Errorf("%w: %w", cloudcontrolbackend.ErrValidation, err)
}
@@ -94,7 +95,7 @@ func ccPolicyObject(doc string, unescape bool) any {
}
var out any
- if json.Unmarshal([]byte(doc), &out) != nil {
+ if json.Unmarshal([]byte(doc), &out, jsonx.V1()) != nil {
return doc
}
diff --git a/internal/app/cloudcontrol_handlers_test.go b/internal/app/cloudcontrol_handlers_test.go
index 3e43835ca7..fb235a2991 100644
--- a/internal/app/cloudcontrol_handlers_test.go
+++ b/internal/app/cloudcontrol_handlers_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"net/netip"
"slices"
"strings"
@@ -12,6 +12,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type ccDelegationCase struct {
@@ -120,7 +122,7 @@ func ccAssertGet(t *testing.T, cc *cloudcontrol.Client, tt ccDelegationCase, id
assert.Equal(t, id, aws.ToString(got.ResourceDescription.Identifier))
var props map[string]any
- require.NoError(t, json.Unmarshal([]byte(aws.ToString(got.ResourceDescription.Properties)), &props))
+ require.NoError(t, json.Unmarshal([]byte(aws.ToString(got.ResourceDescription.Properties)), &props, jsonx.V1()))
for _, k := range tt.wantKeys {
assert.Contains(t, props, k)
@@ -206,7 +208,7 @@ func ccDeleteAndVerify(t *testing.T, cc *cloudcontrol.Client, fx *sfnFixture, tt
}
func jsonScalar(v any) string {
- b, _ := json.Marshal(v)
+ b, _ := json.Marshal(v, jsonx.V1())
return string(b)
}
@@ -215,9 +217,9 @@ func subsetJSON(t *testing.T, doc, key string) string {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal([]byte(doc), &m))
+ require.NoError(t, json.Unmarshal([]byte(doc), &m, jsonx.V1()))
- b, err := json.Marshal(map[string]any{key: m[key]})
+ b, err := json.Marshal(map[string]any{key: m[key]}, jsonx.V1())
require.NoError(t, err)
return string(b)
@@ -334,7 +336,10 @@ func TestCloudControlSubnetIPv6(t *testing.T) {
require.NoError(t, err)
var props map[string]any
- require.NoError(t, json.Unmarshal([]byte(aws.ToString(got.ResourceDescription.Properties)), &props))
+ require.NoError(
+ t,
+ json.Unmarshal([]byte(aws.ToString(got.ResourceDescription.Properties)), &props, jsonx.V1()),
+ )
assert.Equal(t, []any{v6}, props["Ipv6CidrBlocks"])
assert.Equal(t, azID, props["AvailabilityZoneId"])
assert.Equal(t, true, props["AssignIpv6AddressOnCreation"])
diff --git a/internal/app/cloudcontrol_provisioned_test.go b/internal/app/cloudcontrol_provisioned_test.go
index cf6101d017..caa2de93e5 100644
--- a/internal/app/cloudcontrol_provisioned_test.go
+++ b/internal/app/cloudcontrol_provisioned_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
cfnbackend "github.com/blackbirdworks/gopherstack/services/cloudformation"
)
@@ -42,7 +43,7 @@ func TestCloudControlProvisionedType_Lifecycle(t *testing.T) {
require.NoError(t, err)
var props map[string]any
- require.NoError(t, json.Unmarshal([]byte(aws.ToString(got.ResourceDescription.Properties)), &props))
+ require.NoError(t, json.Unmarshal([]byte(aws.ToString(got.ResourceDescription.Properties)), &props, jsonx.V1()))
assert.Equal(t, "cc-prov-user", props["UserName"])
listed, err := cc.ListResources(t.Context(), &cloudcontrol.ListResourcesInput{TypeName: aws.String(typeName)})
diff --git a/internal/app/cloudcontrol_queue_url_test.go b/internal/app/cloudcontrol_queue_url_test.go
index bff2f402d8..323d48b0e6 100644
--- a/internal/app/cloudcontrol_queue_url_test.go
+++ b/internal/app/cloudcontrol_queue_url_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
"time"
@@ -13,6 +13,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/sqs"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCloudControlQueueURLMatchesSQS(t *testing.T) {
@@ -68,7 +70,7 @@ func TestCloudFormationQueueURLMatchesSQS(t *testing.T) {
"Q": map[string]any{"Type": "AWS::SQS::Queue", "Properties": map[string]any{"QueueName": "cfn-url-q"}},
},
"Outputs": map[string]any{"Url": map[string]any{"Value": map[string]any{"Ref": "Q"}}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
fx := newSFNFixture(t)
diff --git a/internal/app/cloudcontrol_wire_client.go b/internal/app/cloudcontrol_wire_client.go
index 05210b39f2..b8d7f5862e 100644
--- a/internal/app/cloudcontrol_wire_client.go
+++ b/internal/app/cloudcontrol_wire_client.go
@@ -2,12 +2,13 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"github.com/aws/aws-sdk-go-v2/aws"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
cloudcontrolbackend "github.com/blackbirdworks/gopherstack/services/cloudcontrol"
"github.com/blackbirdworks/gopherstack/services/stepfunctions/sdkwire"
)
@@ -40,12 +41,12 @@ func wireCall[O any](ctx context.Context, c *ccWireClient, op string, in any) (*
params := map[string]any{}
if in != nil {
- b, err := json.Marshal(in)
+ b, err := json.Marshal(in, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("%w: %w", cloudcontrolbackend.ErrValidation, err)
}
- if err = json.Unmarshal(b, ¶ms); err != nil {
+ if err = json.Unmarshal(b, ¶ms, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", cloudcontrolbackend.ErrValidation, err)
}
}
@@ -57,12 +58,12 @@ func wireCall[O any](ctx context.Context, c *ccWireClient, op string, in any) (*
var out O
- b, err := json.Marshal(raw)
+ b, err := json.Marshal(raw, jsonx.V1())
if err != nil {
return nil, err
}
- if err = json.Unmarshal(b, &out); err != nil {
+ if err = json.Unmarshal(b, &out, jsonx.V1()); err != nil {
return nil, fmt.Errorf("decode %s response: %w", op, err)
}
diff --git a/internal/app/cloudformation_cloudhsm_wiring_test.go b/internal/app/cloudformation_cloudhsm_wiring_test.go
index 29e03794d2..b88ea086f8 100644
--- a/internal/app/cloudformation_cloudhsm_wiring_test.go
+++ b/internal/app/cloudformation_cloudhsm_wiring_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -11,6 +11,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCloudFormationProvisionsCloudHSMCluster(t *testing.T) {
@@ -42,7 +44,7 @@ func TestCloudFormationProvisionsCloudHSMCluster(t *testing.T) {
"Outputs": map[string]any{
"Id": map[string]any{"Value": map[string]any{"Ref": "Cluster"}},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
_, err = cfn.CreateStack(t.Context(), &cloudformation.CreateStackInput{
diff --git a/internal/app/cloudformation_connectfamily_wiring_test.go b/internal/app/cloudformation_connectfamily_wiring_test.go
index 04eeda4073..85f6b53067 100644
--- a/internal/app/cloudformation_connectfamily_wiring_test.go
+++ b/internal/app/cloudformation_connectfamily_wiring_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
@@ -13,6 +13,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/connect"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCloudFormationProvisionsAppIntegrationsAndCodeGuru(t *testing.T) {
@@ -44,7 +46,7 @@ func TestCloudFormationProvisionsAppIntegrationsAndCodeGuru(t *testing.T) {
"EventArn": map[string]any{"Value": map[string]any{"Fn::GetAtt": []string{"Event", "EventIntegrationArn"}}},
"AssocArn": map[string]any{"Value": map[string]any{"Fn::GetAtt": []string{"Assoc", "AssociationArn"}}},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
fx := newSFNFixture(t)
@@ -122,7 +124,7 @@ func TestCloudFormationProvisionsConnect(t *testing.T) {
"InstanceId": map[string]any{"Value": map[string]any{"Fn::GetAtt": []string{"Instance", "Id"}}},
"QueueArn": map[string]any{"Value": map[string]any{"Fn::GetAtt": []string{"Queue", "QueueArn"}}},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
fx := newSFNFixture(t)
diff --git a/internal/app/cloudformation_directconnect_test.go b/internal/app/cloudformation_directconnect_test.go
index 81540aecf5..51f8661272 100644
--- a/internal/app/cloudformation_directconnect_test.go
+++ b/internal/app/cloudformation_directconnect_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
@@ -11,6 +11,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/directconnect"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCloudFormationProvisionsDirectConnect(t *testing.T) {
@@ -48,7 +50,7 @@ func TestCloudFormationProvisionsDirectConnect(t *testing.T) {
"ConnArn": map[string]any{"Value": map[string]any{"Fn::GetAtt": []string{"Conn", "ConnectionArn"}}},
"VifArn": map[string]any{"Value": map[string]any{"Fn::GetAtt": []string{"Vif", "VirtualInterfaceArn"}}},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
fx := newSFNFixture(t)
diff --git a/internal/app/cloudformation_networkmanager_test.go b/internal/app/cloudformation_networkmanager_test.go
index 073b0cb4dc..c2c17ca7e8 100644
--- a/internal/app/cloudformation_networkmanager_test.go
+++ b/internal/app/cloudformation_networkmanager_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
@@ -11,6 +11,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/networkmanager"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCloudFormationProvisionsNetworkManager(t *testing.T) {
@@ -55,7 +57,7 @@ func TestCloudFormationProvisionsNetworkManager(t *testing.T) {
"SiteArn": map[string]any{"Value": map[string]any{"Fn::GetAtt": []string{"Site", "SiteArn"}}},
"GNArn": map[string]any{"Value": map[string]any{"Fn::GetAtt": []string{"GN", "Arn"}}},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
fx := newSFNFixture(t)
diff --git a/internal/app/cloudformation_oam_wiring_test.go b/internal/app/cloudformation_oam_wiring_test.go
index 82e9605ee4..1e2e537653 100644
--- a/internal/app/cloudformation_oam_wiring_test.go
+++ b/internal/app/cloudformation_oam_wiring_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -10,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/oam"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCloudFormationProvisionsOAMSink(t *testing.T) {
@@ -25,7 +27,7 @@ func TestCloudFormationProvisionsOAMSink(t *testing.T) {
"Outputs": map[string]any{
"Arn": map[string]any{"Value": map[string]any{"Fn::GetAtt": []string{"Sink", "Arn"}}},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
fx := newSFNFixture(t)
diff --git a/internal/app/cloudformation_rum_wiring_test.go b/internal/app/cloudformation_rum_wiring_test.go
index b336061833..4c6d17b278 100644
--- a/internal/app/cloudformation_rum_wiring_test.go
+++ b/internal/app/cloudformation_rum_wiring_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
@@ -11,6 +11,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/rum"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCloudFormationProvisionsRUMAppMonitor(t *testing.T) {
@@ -30,7 +32,7 @@ func TestCloudFormationProvisionsRUMAppMonitor(t *testing.T) {
"Name": map[string]any{"Value": map[string]any{"Ref": "Mon"}},
"Id": map[string]any{"Value": map[string]any{"Fn::GetAtt": []string{"Mon", "Id"}}},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
fx := newSFNFixture(t)
diff --git a/internal/app/codepipeline_actions_test.go b/internal/app/codepipeline_actions_test.go
index 14103e0709..f0a50e51e1 100644
--- a/internal/app/codepipeline_actions_test.go
+++ b/internal/app/codepipeline_actions_test.go
@@ -3,7 +3,7 @@ package app
import (
"archive/zip"
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"maps"
"testing"
@@ -21,6 +21,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const cfnPipelineTemplate = `{"Parameters":{"Env":{"Type":"String","Default":"dev"}},` +
@@ -509,7 +511,7 @@ func TestCodePipelineECRSource(t *testing.T) {
}
var detail map[string]any
- require.NoError(t, json.Unmarshal([]byte(env.object(t, "out/imageDetail.json")), &detail))
+ require.NoError(t, json.Unmarshal([]byte(env.object(t, "out/imageDetail.json")), &detail, jsonx.V1()))
imgs, err := ecrc.DescribeImages(ctx, &ecr.DescribeImagesInput{
RepositoryName: aws.String(
diff --git a/internal/app/dev_endpoints_test.go b/internal/app/dev_endpoints_test.go
index 522d97d25c..934aecdc01 100644
--- a/internal/app/dev_endpoints_test.go
+++ b/internal/app/dev_endpoints_test.go
@@ -2,7 +2,8 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"encoding/xml"
"io"
"net/http"
@@ -24,6 +25,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/chaos"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -143,7 +145,7 @@ func TestSESRetrospectionEndpoint(t *testing.T) {
var got struct {
Messages []map[string]any `json:"messages"`
}
- require.NoError(t, json.Unmarshal(body, &got))
+ require.NoError(t, json.Unmarshal(body, &got, jsonx.V1()))
require.Len(t, got.Messages, tt.wantN)
if tt.wantN == 0 {
@@ -225,7 +227,7 @@ func TestSQSInspectEndpoint(t *testing.T) {
Body string `json:"Body"`
} `json:"Messages"`
}
- require.NoError(t, json.Unmarshal(body, &got))
+ require.NoError(t, json.Unmarshal(body, &got, jsonx.V1()))
require.Len(t, got.Messages, 1)
assert.Equal(t, "payload", got.Messages[0].Body)
assert.Equal(t, "0", got.Messages[0].Attributes["ApproximateReceiveCount"])
@@ -316,7 +318,7 @@ func TestCloudWatchRawMetricsEndpoint(t *testing.T) {
var got struct {
Metrics []map[string]any `json:"metrics"`
}
- require.NoError(t, json.Unmarshal(body, &got))
+ require.NoError(t, json.Unmarshal(body, &got, jsonx.V1()))
var found map[string]any
@@ -419,7 +421,7 @@ func TestLambdaRuntimesEndpoint(t *testing.T) {
Runtimes []string `json:"Runtimes"`
}
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
assert.Contains(t, out.Runtimes, tt.has)
if tt.hasNot != "" {
diff --git a/internal/app/dms_engine_docker_test.go b/internal/app/dms_engine_docker_test.go
index 888f126beb..8bc7014c10 100644
--- a/internal/app/dms_engine_docker_test.go
+++ b/internal/app/dms_engine_docker_test.go
@@ -3,7 +3,7 @@ package app
import (
"context"
"database/sql"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"net"
@@ -30,6 +30,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/chaos"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -698,7 +699,7 @@ func TestDMSDockerMySQLToInProcessTargets(t *testing.T) {
for _, r := range out.Records {
var x dmsKinesisRec
- require.NoError(t, json.Unmarshal(r.Data, &x))
+ require.NoError(t, json.Unmarshal(r.Data, &x, jsonx.V1()))
got = append(got, x)
}
diff --git a/internal/app/ecs_runtime_test.go b/internal/app/ecs_runtime_test.go
index 75aead18e7..74c1a73544 100644
--- a/internal/app/ecs_runtime_test.go
+++ b/internal/app/ecs_runtime_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -14,6 +14,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func newECSTaskFixture(t *testing.T, role string) (*sfnFixture, *ecs.Client, string) {
@@ -87,7 +89,7 @@ func TestECSTaskStateEventsReachRuleTarget(t *testing.T) {
for _, m := range out.Messages {
var env map[string]any
- uErr := json.Unmarshal([]byte(aws.ToString(m.Body)), &env)
+ uErr := json.Unmarshal([]byte(aws.ToString(m.Body)), &env, jsonx.V1())
if uErr != nil || env["detail-type"] != "ECS Task State Change" {
continue
}
diff --git a/internal/app/error_wire_guards_test.go b/internal/app/error_wire_guards_test.go
index 1f551b32b4..07a9689bc6 100644
--- a/internal/app/error_wire_guards_test.go
+++ b/internal/app/error_wire_guards_test.go
@@ -1,7 +1,8 @@
package app
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"encoding/xml"
"fmt"
"net/http"
@@ -15,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -41,7 +43,7 @@ func probeJSONBody() string {
for _, k := range keys {
m[k] = "gs-probe-missing"
}
- b, _ := json.Marshal(m)
+ b, _ := json.Marshal(m, jsonx.V1())
return string(b)
}
@@ -172,8 +174,8 @@ func parseErrorBody(p errorProbe) (parsedError, bool) {
}
func parseJSONError(p errorProbe, body string) (parsedError, bool) {
- var m map[string]json.RawMessage
- if json.Unmarshal([]byte(body), &m) != nil {
+ var m map[string]jsontext.Value
+ if json.Unmarshal([]byte(body), &m, jsonx.V1()) != nil {
return parsedError{}, false
}
@@ -184,7 +186,7 @@ func parseJSONError(p errorProbe, body string) (parsedError, bool) {
}
var s string
- _ = json.Unmarshal(raw, &s)
+ _ = json.Unmarshal(raw, &s, jsonx.V1())
return s, true
}
diff --git a/internal/app/extensions_test.go b/internal/app/extensions_test.go
index 09700b9906..8695152bdc 100644
--- a/internal/app/extensions_test.go
+++ b/internal/app/extensions_test.go
@@ -2,7 +2,7 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/extensions"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
@@ -76,7 +77,7 @@ func TestExtensionRoutingAndHooks(t *testing.T) {
"hooks": map[string]any{"request": []map[string]any{
{"services": []string{"Dash"}, "pre": "/hook/pre", "post": "/hook/post"},
}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
require.NoError(t, os.WriteFile(regFile, reg, 0o600))
@@ -87,7 +88,7 @@ func TestExtensionRoutingAndHooks(t *testing.T) {
cfgFile := filepath.Join(dir, "ext.json")
cfg, err := json.Marshal(map[string]any{"extensions": []map[string]any{
{"name": "t", "command": []string{"sh", "-c", script}, "health_interval": "1h"},
- }})
+ }}, jsonx.V1())
require.NoError(t, err)
require.NoError(t, os.WriteFile(cfgFile, cfg, 0o600))
diff --git a/internal/app/fis_cloudwatch_stopcondition_wiring_test.go b/internal/app/fis_cloudwatch_stopcondition_wiring_test.go
index af77938f0b..fc7d50adca 100644
--- a/internal/app/fis_cloudwatch_stopcondition_wiring_test.go
+++ b/internal/app/fis_cloudwatch_stopcondition_wiring_test.go
@@ -2,7 +2,7 @@ package app
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
cwbackend "github.com/blackbirdworks/gopherstack/services/cloudwatch"
fisbackend "github.com/blackbirdworks/gopherstack/services/fis"
)
@@ -25,7 +26,7 @@ func fisWiringRequest(t *testing.T, h *fisbackend.Handler, method, path string,
var bodyBytes []byte
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
bodyBytes = b
}
@@ -94,7 +95,7 @@ func TestWireFISStopConditions_AlarmStopsRealExperiment(t *testing.T) {
ID string `json:"id"`
} `json:"experimentTemplate"`
}
- require.NoError(t, json.Unmarshal(tplRec.Body.Bytes(), &tplResp))
+ require.NoError(t, json.Unmarshal(tplRec.Body.Bytes(), &tplResp, jsonx.V1()))
expRec := fisWiringRequest(t, fisH, http.MethodPost, "/experiments", map[string]any{
"experimentTemplateId": tplResp.ExperimentTemplate.ID,
@@ -106,7 +107,7 @@ func TestWireFISStopConditions_AlarmStopsRealExperiment(t *testing.T) {
ID string `json:"id"`
} `json:"experiment"`
}
- require.NoError(t, json.Unmarshal(expRec.Body.Bytes(), &expResp))
+ require.NoError(t, json.Unmarshal(expRec.Body.Bytes(), &expResp, jsonx.V1()))
expID := expResp.Experiment.ID
// Advance past the pending -> initiating -> running transition. This
diff --git a/internal/app/gcpenforce_wiring_test.go b/internal/app/gcpenforce_wiring_test.go
index 505d087570..9b97a00c42 100644
--- a/internal/app/gcpenforce_wiring_test.go
+++ b/internal/app/gcpenforce_wiring_test.go
@@ -2,7 +2,7 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"log/slog"
"net/http"
@@ -21,6 +21,7 @@ import (
"google.golang.org/grpc/status"
"github.com/blackbirdworks/gopherstack/pkgs/gcpenforce"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
@@ -233,7 +234,7 @@ func getREST(t *testing.T, url string) (int, restEnvelope) {
var env restEnvelope
- _ = json.Unmarshal(body, &env)
+ _ = json.Unmarshal(body, &env, jsonx.V1())
return resp.StatusCode, env
}
diff --git a/internal/app/gcpfunctions_events_wiring_test.go b/internal/app/gcpfunctions_events_wiring_test.go
index 15da9b5bff..c07cde1c51 100644
--- a/internal/app/gcpfunctions_events_wiring_test.go
+++ b/internal/app/gcpfunctions_events_wiring_test.go
@@ -4,7 +4,8 @@ import (
"archive/zip"
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"net"
@@ -21,6 +22,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/container"
"github.com/blackbirdworks/gopherstack/pkgs/gcpserve"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
gcpfirestorebackend "github.com/blackbirdworks/gopherstack/services/gcpfirestore"
gcpfunctionsbackend "github.com/blackbirdworks/gopherstack/services/gcpfunctions"
@@ -130,9 +132,9 @@ func TestWireGCPFunctions_StorageFirestoreAndSecrets(t *testing.T) {
"GCPFunctions": fns, "GCPStorage": gcs, "GCPFirestore": fs, "GCPSecretManager": sm,
})
- _, err := gcs.Backend.CreateBucket("p", gcpstoragebackend.Patch{"name": json.RawMessage(`"src"`)})
+ _, err := gcs.Backend.CreateBucket("p", gcpstoragebackend.Patch{"name": jsontext.Value(`"src"`)})
require.NoError(t, err)
- _, err = gcs.Backend.CreateBucket("p", gcpstoragebackend.Patch{"name": json.RawMessage(`"inbox"`)})
+ _, err = gcs.Backend.CreateBucket("p", gcpstoragebackend.Patch{"name": jsontext.Value(`"inbox"`)})
require.NoError(t, err)
_, err = sm.Backend.CreateSecret(&secretmanagerpb.CreateSecretRequest{
@@ -204,7 +206,7 @@ func TestWireGCPFunctions_StorageFirestoreAndSecrets(t *testing.T) {
var obj map[string]any
- require.NoError(t, json.Unmarshal(ev.body, &obj))
+ require.NoError(t, json.Unmarshal(ev.body, &obj, jsonx.V1()))
assert.Equal(t, "inbox", obj["bucket"])
assert.Equal(t, "a.txt", obj["name"])
diff --git a/internal/app/gcpfunctions_wiring_test.go b/internal/app/gcpfunctions_wiring_test.go
index 10d37f049b..931bef9003 100644
--- a/internal/app/gcpfunctions_wiring_test.go
+++ b/internal/app/gcpfunctions_wiring_test.go
@@ -3,7 +3,7 @@ package app
import (
"archive/zip"
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
"testing"
"cloud.google.com/go/functions/apiv2/functionspb"
@@ -56,7 +56,7 @@ func TestWireGCPFunctions(t *testing.T) {
_, err := ps.Backend.CreateTopic(&pubsubpb.Topic{Name: topic})
require.NoError(t, err)
- _, err = gcs.Backend.CreateBucket("p", gcpstoragebackend.Patch{"name": json.RawMessage(`"src-bucket"`)})
+ _, err = gcs.Backend.CreateBucket("p", gcpstoragebackend.Patch{"name": jsontext.Value(`"src-bucket"`)})
require.NoError(t, err)
var buf bytes.Buffer
diff --git a/internal/app/globalaccelerator_wiring_test.go b/internal/app/globalaccelerator_wiring_test.go
index bcb0011217..835bca1ffa 100644
--- a/internal/app/globalaccelerator_wiring_test.go
+++ b/internal/app/globalaccelerator_wiring_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
@@ -17,6 +17,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func waitStack(t *testing.T, cfn *cloudformation.Client, name string) cfntypes.Stack {
@@ -80,7 +82,7 @@ func TestCloudFormationProvisionsGlobalAccelerator(t *testing.T) {
"GrpArn": map[string]any{"Value": map[string]any{"Fn::GetAtt": []string{"Grp", "EndpointGroupArn"}}},
"AttArn": map[string]any{"Value": map[string]any{"Fn::GetAtt": []string{"Att", "AttachmentArn"}}},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
fx := newSFNFixture(t)
diff --git a/internal/app/http_targets_wiring.go b/internal/app/http_targets_wiring.go
index c3c0100fe6..66ed7fa7f6 100644
--- a/internal/app/http_targets_wiring.go
+++ b/internal/app/http_targets_wiring.go
@@ -2,11 +2,12 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
apigwbackend "github.com/blackbirdworks/gopherstack/services/apigateway"
ebbackend "github.com/blackbirdworks/gopherstack/services/eventbridge"
@@ -115,7 +116,7 @@ func (a *schedUniversalAdapter) InvokeUniversalTarget(
ctx context.Context, region, roleARN, svc, action string, input []byte,
) error {
var params any
- if err := json.Unmarshal(input, ¶ms); err != nil {
+ if err := json.Unmarshal(input, ¶ms, jsonx.V1()); err != nil {
return fmt.Errorf("universal target input is not valid JSON: %w", err)
}
diff --git a/internal/app/iam_policy_stream.go b/internal/app/iam_policy_stream.go
index fd798e3b8b..c948530803 100644
--- a/internal/app/iam_policy_stream.go
+++ b/internal/app/iam_policy_stream.go
@@ -2,7 +2,7 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/iampolicystream"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
iambackend "github.com/blackbirdworks/gopherstack/services/iam"
organizationsbackend "github.com/blackbirdworks/gopherstack/services/organizations"
@@ -116,7 +117,7 @@ func followIAMPolicyStream(c *echo.Context, rec *iampolicystream.Recorder, f iam
_ = rc.Flush()
send := func(ev iampolicystream.Event) error {
- b, err := json.Marshal(ev)
+ b, err := json.Marshal(ev, jsonx.V1())
if err != nil {
return err
}
diff --git a/internal/app/iam_policy_stream_e2e_test.go b/internal/app/iam_policy_stream_e2e_test.go
index 8486a83441..7ae78dd1e7 100644
--- a/internal/app/iam_policy_stream_e2e_test.go
+++ b/internal/app/iam_policy_stream_e2e_test.go
@@ -1,7 +1,8 @@
package app
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http/httptest"
"testing"
@@ -15,6 +16,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/chaos"
"github.com/blackbirdworks/gopherstack/pkgs/iampolicystream"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -114,7 +116,7 @@ func TestIAMPolicyStreamEndToEnd(t *testing.T) {
var body struct {
Events []iampolicystream.Event `json:"events"`
}
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&body))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &body, jsonx.V1()))
require.Len(t, body.Events, 2)
allow, deny := body.Events[0], body.Events[1]
diff --git a/internal/app/iam_policy_stream_test.go b/internal/app/iam_policy_stream_test.go
index 8ca5ac932d..1d8df3357a 100644
--- a/internal/app/iam_policy_stream_test.go
+++ b/internal/app/iam_policy_stream_test.go
@@ -3,7 +3,7 @@ package app
import (
"bufio"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/iampolicystream"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -73,7 +74,7 @@ func TestIAMPolicyStreamList(t *testing.T) {
var body struct {
Events []iampolicystream.Event `json:"events"`
}
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body.Events, tt.wantCount)
})
}
@@ -88,7 +89,7 @@ func TestIAMPolicySuggest(t *testing.T) {
require.Equal(t, http.StatusOK, w.Code)
var s iampolicystream.Suggestion
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &s))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &s, jsonx.V1()))
require.Len(t, s.Policy.Statement, 1)
assert.Equal(t, []string{"s3:GetObject"}, s.Policy.Statement[0].Action)
}
diff --git a/internal/app/init_stages.go b/internal/app/init_stages.go
index f3fc3f24d3..797370df5b 100644
--- a/internal/app/init_stages.go
+++ b/internal/app/init_stages.go
@@ -2,7 +2,8 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"log/slog"
"net"
@@ -13,6 +14,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/inithooks"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -136,8 +138,11 @@ func buildInitRunHandler(st *localstackInitState) echo.HandlerFunc {
}
var req initRunRequest
- if err := json.NewDecoder(http.MaxBytesReader(c.Response(), c.Request().Body, initRunMaxBody)).
- Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(
+ jsontext.NewDecoder(http.MaxBytesReader(c.Response(), c.Request().Body, initRunMaxBody)),
+ &req,
+ jsonx.V1(),
+ ); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{keyMessageField: "invalid request body"})
}
diff --git a/internal/app/iot_rule_actions_test.go b/internal/app/iot_rule_actions_test.go
index c6835607b1..994ded4ad3 100644
--- a/internal/app/iot_rule_actions_test.go
+++ b/internal/app/iot_rule_actions_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"strings"
@@ -23,6 +23,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
firehosebackend "github.com/blackbirdworks/gopherstack/services/firehose"
iotbackend "github.com/blackbirdworks/gopherstack/services/iot"
iotanalyticsbackend "github.com/blackbirdworks/gopherstack/services/iotanalytics"
@@ -267,7 +268,7 @@ func iotCaseSNS(t *testing.T, f *iotRuleFixture, format, payload, want string) {
Message string `json:"Message"`
}
- require.NoError(t, json.Unmarshal([]byte(f.bodies(t, url, 1)[0]), &env))
+ require.NoError(t, json.Unmarshal([]byte(f.bodies(t, url, 1)[0]), &env, jsonx.V1()))
assert.Contains(t, env.Message, want)
}
@@ -648,7 +649,7 @@ func iotCaseTemplateError(t *testing.T, f *iotRuleFixture) {
f.publish(t, "tpl/topic", `{"present":1}`)
var env map[string]string
- require.NoError(t, json.Unmarshal([]byte(f.bodies(t, errURL, 1)[0]), &env))
+ require.NoError(t, json.Unmarshal([]byte(f.bodies(t, errURL, 1)[0]), &env, jsonx.V1()))
assert.Equal(t, "KinesisAction", env["failedAction"])
assert.Equal(t, "r", env["ruleName"])
assert.Empty(t, kinesisRecords(t, f.fx, "iot-tpl-stream"))
diff --git a/internal/app/iot_rule_http_test.go b/internal/app/iot_rule_http_test.go
index 2cf7c45a6e..d77943e8ae 100644
--- a/internal/app/iot_rule_http_test.go
+++ b/internal/app/iot_rule_http_test.go
@@ -2,7 +2,7 @@ package app
import (
"cmp"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -109,7 +110,7 @@ func decodeConfirm(t *testing.T, hit iotHTTPHit) confirmBody {
t.Helper()
var b confirmBody
- require.NoError(t, json.Unmarshal(hit.body, &b))
+ require.NoError(t, json.Unmarshal(hit.body, &b, jsonx.V1()))
return b
}
@@ -119,7 +120,7 @@ func (f *iotRuleFixture) confirmViaSDK(t *testing.T) func(hit iotHTTPHit) {
return func(hit iotHTTPHit) {
var b confirmBody
- if json.Unmarshal(hit.body, &b) != nil {
+ if json.Unmarshal(hit.body, &b, jsonx.V1()) != nil {
return
}
@@ -185,7 +186,7 @@ func TestIoTTopicRuleDestinationConfirmation(t *testing.T) {
return func(hit iotHTTPHit) {
var b confirmBody
- if json.Unmarshal(hit.body, &b) != nil {
+ if json.Unmarshal(hit.body, &b, jsonx.V1()) != nil {
return
}
@@ -427,7 +428,7 @@ func runIoTHTTPCase(t *testing.T, tt iotHTTPCase) {
if tt.wantReason != "" {
var env map[string]string
- require.NoError(t, json.Unmarshal([]byte(f.bodies(t, errURL, 1)[0]), &env))
+ require.NoError(t, json.Unmarshal([]byte(f.bodies(t, errURL, 1)[0]), &env, jsonx.V1()))
assert.Equal(t, "HttpAction", env["failedAction"])
assert.Contains(t, env["failedActionReason"], tt.wantReason)
}
@@ -555,7 +556,7 @@ func TestIoTRuleHTTPActionSigV4Enforcement(t *testing.T) {
if !tt.wantHit {
var env map[string]string
- require.NoError(t, json.Unmarshal([]byte(f.bodies(t, errURL, 1)[0]), &env))
+ require.NoError(t, json.Unmarshal([]byte(f.bodies(t, errURL, 1)[0]), &env, jsonx.V1()))
assert.Equal(t, "HttpAction", env["failedAction"])
assert.Empty(t, ep.deliveries())
diff --git a/internal/app/iot_rule_lookups_test.go b/internal/app/iot_rule_lookups_test.go
index 860d094f6f..8fea90a493 100644
--- a/internal/app/iot_rule_lookups_test.go
+++ b/internal/app/iot_rule_lookups_test.go
@@ -3,7 +3,7 @@ package app
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net"
"strconv"
"strings"
@@ -23,6 +23,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
iotbackend "github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -484,7 +485,7 @@ func TestIoTRuleConnectionFunctions(t *testing.T) {
require.True(t, tok.WaitTimeout(authzDeadline))
var msg map[string]string
- require.NoError(t, json.Unmarshal([]byte(f.bodies(t, url, 1)[0]), &msg))
+ require.NoError(t, json.Unmarshal([]byte(f.bodies(t, url, 1)[0]), &msg, jsonx.V1()))
assert.Equal(t, "device-7", msg["cid"])
assert.NotNil(t, net.ParseIP(msg["ip"]))
diff --git a/internal/app/iotanalytics_lambda_iot_wiring_test.go b/internal/app/iotanalytics_lambda_iot_wiring_test.go
index 50adaf0c81..01ad0894c3 100644
--- a/internal/app/iotanalytics_lambda_iot_wiring_test.go
+++ b/internal/app/iotanalytics_lambda_iot_wiring_test.go
@@ -2,8 +2,9 @@ package app
import (
"github.com/blackbirdworks/gopherstack/internal/cliconfig"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http/httptest"
"testing"
@@ -116,7 +117,7 @@ func TestInitializeServices_IoTAnalyticsLambdaIoTWiring(t *testing.T) {
require.Len(t, out.Payloads, 1)
var decoded map[string]any
- require.NoError(t, json.Unmarshal(out.Payloads[0], &decoded))
+ require.NoError(t, json.Unmarshal(out.Payloads[0], &decoded, jsonx.V1()))
registryData, isMap := decoded["registry"].(map[string]any)
require.True(t, isMap, "registry attribute must be populated from the real IoT backend")
@@ -155,7 +156,7 @@ func TestInitializeServices_IoTAnalyticsLambdaIoTWiring(t *testing.T) {
require.Len(t, out.Payloads, 1)
var decoded map[string]any
- require.NoError(t, json.Unmarshal(out.Payloads[0], &decoded))
+ require.NoError(t, json.Unmarshal(out.Payloads[0], &decoded, jsonx.V1()))
shadowData, isMap := decoded["shadow"].(map[string]any)
require.True(t, isMap, "shadow attribute must be populated from the real IoT backend")
diff --git a/internal/app/lifecycle_cmd_test.go b/internal/app/lifecycle_cmd_test.go
index a943b50c5a..a3781d22f4 100644
--- a/internal/app/lifecycle_cmd_test.go
+++ b/internal/app/lifecycle_cmd_test.go
@@ -3,7 +3,8 @@ package app
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -23,6 +24,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/container"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -104,7 +106,7 @@ func healthServer(t *testing.T, code int, h healthResponse) (*httptest.Server, s
switch {
case r.URL.Path == "/_gopherstack/health":
w.WriteHeader(code)
- _ = json.NewEncoder(w).Encode(h)
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), h)
case r.URL.Path == "/_gopherstack/reset" && r.Method == http.MethodPost:
mu.Lock()
posts = append(posts, r.URL.RawQuery)
@@ -607,7 +609,7 @@ func TestLifecycleStatus(t *testing.T) {
require.NoError(t, cmd.run(t.Context(), d))
var rep statusReport
- require.NoError(t, json.Unmarshal(out.Bytes(), &rep))
+ require.NoError(t, json.Unmarshal(out.Bytes(), &rep, jsonx.V1()))
assert.True(t, rep.Running)
assert.Equal(t, 4, rep.EnabledCount)
assert.Equal(t, 1, rep.DisabledCount)
diff --git a/internal/app/lifecycle_foreground_test.go b/internal/app/lifecycle_foreground_test.go
index b9d30e8e1a..838aaa3c59 100644
--- a/internal/app/lifecycle_foreground_test.go
+++ b/internal/app/lifecycle_foreground_test.go
@@ -2,7 +2,8 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -17,6 +18,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errKillFake = errors.New("no console")
@@ -79,7 +81,7 @@ func readyServer(t *testing.T, ready *atomic.Bool) string {
return
}
- _ = json.NewEncoder(w).Encode(healthResponse{Status: "ok"})
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), healthResponse{Status: "ok"})
}))
t.Cleanup(srv.Close)
@@ -315,12 +317,15 @@ func TestLifecycleStatusServiceHealth(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/_gopherstack/health":
- _ = json.NewEncoder(w).Encode(healthResponse{
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), healthResponse{
Status: "ok", Version: "v1", UptimeSecs: 5, Goroutines: 12, HeapAllocB: 3 << 20,
Services: []string{"S3", "SQS"}, Disabled: []string{"SNS"},
})
case "/_localstack/health":
- _ = json.NewEncoder(w).Encode(localstackHealthResponse{Services: map[string]string{"s3": "available"}})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ localstackHealthResponse{Services: map[string]string{"s3": "available"}},
+ )
default:
http.NotFound(w, r)
}
@@ -335,7 +340,7 @@ func TestLifecycleStatusServiceHealth(t *testing.T) {
require.NoError(t, cmd.run(t.Context(), d))
var rep statusReport
- require.NoError(t, json.Unmarshal(out.Bytes(), &rep))
+ require.NoError(t, json.Unmarshal(out.Bytes(), &rep, jsonx.V1()))
got := map[string]serviceState{}
for _, s := range rep.Clouds["aws"] {
diff --git a/internal/app/lifecycle_run.go b/internal/app/lifecycle_run.go
index 9ba2fe5363..d520e5681e 100644
--- a/internal/app/lifecycle_run.go
+++ b/internal/app/lifecycle_run.go
@@ -3,7 +3,8 @@ package app
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -17,6 +18,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type lifecycleState struct {
@@ -41,7 +43,7 @@ func (d *lifecycleDeps) loadState(t LifecycleTarget) (lifecycleState, bool) {
}
var st lifecycleState
- if json.Unmarshal(b, &st) != nil {
+ if json.Unmarshal(b, &st, jsonx.V1()) != nil {
return lifecycleState{}, false
}
@@ -49,7 +51,7 @@ func (d *lifecycleDeps) loadState(t LifecycleTarget) (lifecycleState, bool) {
}
func (d *lifecycleDeps) saveState(t LifecycleTarget, st lifecycleState) error {
- b, err := json.Marshal(st)
+ b, err := json.Marshal(st, jsonx.V1())
if err != nil {
return fmt.Errorf("encode state: %w", err)
}
@@ -92,7 +94,7 @@ func (d *lifecycleDeps) health(ctx context.Context, endpoint string) (healthResp
return h, fmt.Errorf("%w: health status %d", errLifecycleRequest, code)
}
- if err = json.Unmarshal(body, &h); err != nil {
+ if err = json.Unmarshal(body, &h, jsonx.V1()); err != nil {
return h, fmt.Errorf("decode health: %w", err)
}
@@ -485,7 +487,7 @@ func (d *lifecycleDeps) serviceHealth(ctx context.Context, endpoint string) map[
}
var r localstackHealthResponse
- if json.Unmarshal(body, &r) != nil {
+ if json.Unmarshal(body, &r, jsonx.V1()) != nil {
return nil
}
@@ -566,7 +568,10 @@ func (c *StatusCmd) run(ctx context.Context, d *lifecycleDeps) error {
h, err := d.health(ctx, endpoint)
if err != nil {
if c.JSON {
- _ = json.NewEncoder(d.Out).Encode(statusReport{Endpoint: endpoint, Managed: managed})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(d.Out, jsonx.V1()),
+ statusReport{Endpoint: endpoint, Managed: managed},
+ )
} else {
fmt.Fprintf(d.Out, "gopherstack is not running (%s)\n", endpoint)
}
@@ -576,10 +581,9 @@ func (c *StatusCmd) run(ctx context.Context, d *lifecycleDeps) error {
rep := buildStatus(endpoint, h, managed, d.serviceHealth(ctx, endpoint))
if c.JSON {
- enc := json.NewEncoder(d.Out)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(d.Out, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- return enc.Encode(rep)
+ return json.MarshalEncode(enc, rep)
}
rep.print(d.Out)
@@ -652,7 +656,7 @@ func (c *ResetCmd) run(ctx context.Context, d *lifecycleDeps) error {
Message string `json:"message"`
}
- _ = json.Unmarshal(body, &resp)
+ _ = json.Unmarshal(body, &resp, jsonx.V1())
fmt.Fprintln(d.Out, resp.Message)
}
diff --git a/internal/app/localstack_compat_test.go b/internal/app/localstack_compat_test.go
index 1d0715c54b..4b186dd25d 100644
--- a/internal/app/localstack_compat_test.go
+++ b/internal/app/localstack_compat_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/inithooks"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestLocalstackEnvPlan(t *testing.T) {
@@ -355,7 +356,7 @@ func TestLocalstackInternalEndpoints(t *testing.T) {
}
var body map[string]any
- require.NoError(t, json.Unmarshal(raw, &body))
+ require.NoError(t, json.Unmarshal(raw, &body, jsonx.V1()))
tt.check(t, body)
})
}
diff --git a/internal/app/logs_policy.go b/internal/app/logs_policy.go
index b201342f5c..935fe01afd 100644
--- a/internal/app/logs_policy.go
+++ b/internal/app/logs_policy.go
@@ -2,9 +2,11 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
cwlogsbackend "github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -29,7 +31,7 @@ func (a *logsPolicyAdapter) GetResourcePolicy(_ context.Context, resourceARN str
}
var (
- statements []json.RawMessage
+ statements []jsontext.Value
token string
)
@@ -55,7 +57,7 @@ func (a *logsPolicyAdapter) GetResourcePolicy(_ context.Context, resourceARN str
return "", nil
}
- doc, err := json.Marshal(map[string]any{"Version": "2012-10-17", "Statement": statements})
+ doc, err := json.Marshal(map[string]any{"Version": "2012-10-17", "Statement": statements}, jsonx.V1())
if err != nil {
return "", err
}
@@ -63,19 +65,19 @@ func (a *logsPolicyAdapter) GetResourcePolicy(_ context.Context, resourceARN str
return string(doc), nil
}
-func policyStatements(doc string) []json.RawMessage {
+func policyStatements(doc string) []jsontext.Value {
var parsed struct {
- Statement json.RawMessage `json:"Statement"`
+ Statement jsontext.Value `json:"Statement"`
}
- if json.Unmarshal([]byte(doc), &parsed) != nil || len(parsed.Statement) == 0 {
+ if json.Unmarshal([]byte(doc), &parsed, jsonx.V1()) != nil || len(parsed.Statement) == 0 {
return nil
}
- var list []json.RawMessage
- if json.Unmarshal(parsed.Statement, &list) == nil {
+ var list []jsontext.Value
+ if json.Unmarshal(parsed.Statement, &list, jsonx.V1()) == nil {
return list
}
- return []json.RawMessage{parsed.Statement}
+ return []jsontext.Value{parsed.Statement}
}
diff --git a/internal/app/mcp_wiring_test.go b/internal/app/mcp_wiring_test.go
index 224befdc8e..76ac9713c3 100644
--- a/internal/app/mcp_wiring_test.go
+++ b/internal/app/mcp_wiring_test.go
@@ -2,7 +2,7 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"os"
@@ -22,6 +22,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/chaos"
"github.com/blackbirdworks/gopherstack/pkgs/iampolicystream"
"github.com/blackbirdworks/gopherstack/pkgs/inithooks"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
@@ -127,7 +128,7 @@ func callTool(t *testing.T, cs *mcp.ClientSession, name string, args map[string]
res, err := cs.CallTool(t.Context(), &mcp.CallToolParams{Name: name, Arguments: args})
require.NoError(t, err)
- raw, err := json.Marshal(res)
+ raw, err := json.Marshal(res, jsonx.V1())
require.NoError(t, err)
require.False(t, res.IsError, "tool %s: %s", name, raw)
diff --git a/internal/app/persist_modes_test.go b/internal/app/persist_modes_test.go
index a2dd217ea3..759a706677 100644
--- a/internal/app/persist_modes_test.go
+++ b/internal/app/persist_modes_test.go
@@ -2,7 +2,7 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -48,7 +49,7 @@ func (k *kvPersistSvc) Snapshot(context.Context) []byte {
k.mu.Lock()
defer k.mu.Unlock()
- b, _ := json.Marshal(k.vals)
+ b, _ := json.Marshal(k.vals, jsonx.V1())
return b
}
@@ -57,7 +58,7 @@ func (k *kvPersistSvc) Restore(_ context.Context, data []byte) error {
k.mu.Lock()
defer k.mu.Unlock()
- return json.Unmarshal(data, &k.vals)
+ return json.Unmarshal(data, &k.vals, jsonx.V1())
}
func (k *kvPersistSvc) getA() string {
diff --git a/internal/app/pods_apply.go b/internal/app/pods_apply.go
index 2cd087d1af..da30e553c7 100644
--- a/internal/app/pods_apply.go
+++ b/internal/app/pods_apply.go
@@ -2,13 +2,14 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/pods"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -32,7 +33,7 @@ func podApplyHTTPError(err error) error {
// validateSnaps rejects any section that is not well-formed JSON.
func validateSnaps(snaps map[string][]byte) error {
for n, d := range snaps {
- if !json.Valid(d) {
+ if !jsontext.Value(d).IsValid(jsonx.V1()) {
return podInvalidError{fmt.Errorf("%w: service %q section is not valid JSON", pods.ErrCorrupt, n)}
}
}
diff --git a/internal/app/pods_cmd.go b/internal/app/pods_cmd.go
index e43e23c55b..229e2745b3 100644
--- a/internal/app/pods_cmd.go
+++ b/internal/app/pods_cmd.go
@@ -3,7 +3,8 @@ package app
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -14,6 +15,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const podCmdTimeout = 10 * time.Minute
@@ -85,7 +87,7 @@ func (t PodTarget) call(method, path string, query url.Values, body any) error {
var rdr io.Reader
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
if err != nil {
return fmt.Errorf("encode request: %w", err)
}
@@ -118,9 +120,9 @@ func (t PodTarget) call(method, path string, query url.Values, body any) error {
return fmt.Errorf("%w: %d: %s", errPodServer, resp.StatusCode, strings.TrimSpace(string(out)))
}
- var pretty bytes.Buffer
- if json.Indent(&pretty, out, "", " ") == nil {
- out = pretty.Bytes()
+ pretty := jsontext.Value(bytes.Clone(out))
+ if pretty.Indent(jsonx.V1(), jsontext.WithIndent(" ")) == nil {
+ out = pretty
}
fmt.Fprintln(os.Stdout, strings.TrimSpace(string(out)))
diff --git a/internal/app/pods_localstack.go b/internal/app/pods_localstack.go
index 6308cf8a2b..0239eadfa5 100644
--- a/internal/app/pods_localstack.go
+++ b/internal/app/pods_localstack.go
@@ -1,12 +1,14 @@
package app
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/pods"
)
@@ -118,10 +120,10 @@ func (a *podAPI) lsDelete(c *echo.Context) error {
func (a *podAPI) lsStateExport(c *echo.Context) error {
raw := a.manager.ExportAll()
- services := make(map[string]json.RawMessage, len(raw))
+ services := make(map[string]jsontext.Value, len(raw))
for n, d := range raw {
- services[n] = json.RawMessage(d)
+ services[n] = jsontext.Value(d)
}
return c.JSON(http.StatusOK, snapshotResponse{
@@ -131,7 +133,7 @@ func (a *podAPI) lsStateExport(c *echo.Context) error {
func (a *podAPI) lsStateImport(c *echo.Context) error {
var bundle snapshotBundle
- if err := json.NewDecoder(c.Request().Body).Decode(&bundle); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &bundle, jsonx.V1()); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "invalid snapshot bundle: "+err.Error())
}
diff --git a/internal/app/region_wiring_test.go b/internal/app/region_wiring_test.go
index f6cd071af2..7366bc5160 100644
--- a/internal/app/region_wiring_test.go
+++ b/internal/app/region_wiring_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/chaos"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc"
"github.com/blackbirdworks/gopherstack/pkgs/service"
athenabackend "github.com/blackbirdworks/gopherstack/services/athena"
@@ -59,7 +60,7 @@ func regionTargetCall(
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
out := map[string]any{}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/internal/app/regional_platform_wiring_test.go b/internal/app/regional_platform_wiring_test.go
index 22646b5770..dbc1b689a1 100644
--- a/internal/app/regional_platform_wiring_test.go
+++ b/internal/app/regional_platform_wiring_test.go
@@ -2,7 +2,7 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
awsconfigbackend "github.com/blackbirdworks/gopherstack/services/awsconfig"
cfnbackend "github.com/blackbirdworks/gopherstack/services/cloudformation"
codebuildbackend "github.com/blackbirdworks/gopherstack/services/codebuild"
@@ -37,7 +38,7 @@ func regionRESTCall(t *testing.T, h echo.HandlerFunc, region, method, path, body
out := map[string]any{}
if rec.Body.Len() > 0 {
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
}
return out
diff --git a/internal/app/registry_webhook_test.go b/internal/app/registry_webhook_test.go
index f0c85c0922..c6ec715bdc 100644
--- a/internal/app/registry_webhook_test.go
+++ b/internal/app/registry_webhook_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociregistry/ocitest"
)
@@ -31,7 +32,7 @@ func newHookSink(t *testing.T) *hookSink {
raw, _ := io.ReadAll(r.Body)
var m map[string]any
- _ = json.Unmarshal(raw, &m)
+ _ = json.Unmarshal(raw, &m, jsonx.V1())
s.mu.Lock()
s.got = append(s.got, m)
diff --git a/internal/app/registry_wiring.go b/internal/app/registry_wiring.go
index 4d7297309a..dd15fe638c 100644
--- a/internal/app/registry_wiring.go
+++ b/internal/app/registry_wiring.go
@@ -1,10 +1,11 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"path/filepath"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/azurearm"
acr "github.com/blackbirdworks/gopherstack/services/azurecontainerregistry"
@@ -103,13 +104,13 @@ func (a *azureContainerRegistryAdapter) WebhookEvents(registry, name string) ([]
out := make([]map[string]any, 0, len(events))
for _, ev := range events {
- raw, marshalErr := json.Marshal(ev)
+ raw, marshalErr := json.Marshal(ev, jsonx.V1())
if marshalErr != nil {
return nil, marshalErr
}
var m map[string]any
- if unmarshalErr := json.Unmarshal(raw, &m); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(raw, &m, jsonx.V1()); unmarshalErr != nil {
return nil, unmarshalErr
}
diff --git a/internal/app/replicate_cmd.go b/internal/app/replicate_cmd.go
index 8e65a403f7..a41b1c348a 100644
--- a/internal/app/replicate_cmd.go
+++ b/internal/app/replicate_cmd.go
@@ -2,7 +2,8 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -12,6 +13,7 @@ import (
"text/tabwriter"
"github.com/blackbirdworks/gopherstack/internal/replicator"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -67,10 +69,9 @@ func (c *ReplicateCmd) run(ctx context.Context, out io.Writer, fn replicateFunc)
}
if c.JSON {
- enc := json.NewEncoder(out)
- enc.SetIndent("", " ")
+ enc := jsontext.NewEncoder(out, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
- if err = enc.Encode(rep); err != nil {
+ if err = json.MarshalEncode(enc, rep); err != nil {
return fmt.Errorf("encode report: %w", err)
}
} else {
diff --git a/internal/app/replicate_test.go b/internal/app/replicate_test.go
index c67e62daa9..26fc865454 100644
--- a/internal/app/replicate_test.go
+++ b/internal/app/replicate_test.go
@@ -3,7 +3,7 @@ package app
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/internal/replicator"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func fakeReplicate(rep *replicator.Report, err error, seen *replicator.Config) replicateFunc {
@@ -86,7 +87,7 @@ func TestReplicateAPIJobLifecycle(t *testing.T) {
}
var started map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &started))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &started, jsonx.V1()))
var job replicateJob
@@ -94,7 +95,7 @@ func TestReplicateAPIJobLifecycle(t *testing.T) {
r := replicateDo(e, http.MethodGet, replicateBasePath+"/"+started["id"], "")
job = replicateJob{}
- return r.Code == http.StatusOK && json.Unmarshal(r.Body.Bytes(), &job) == nil &&
+ return r.Code == http.StatusOK && json.Unmarshal(r.Body.Bytes(), &job, jsonx.V1()) == nil &&
job.Status != replicateRunning
}, 5*time.Second, 5*time.Millisecond)
@@ -131,7 +132,7 @@ func TestReplicateAPIDoesNotEchoCredentials(t *testing.T) {
require.Equal(t, http.StatusAccepted, rec.Code)
var started map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &started))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &started, jsonx.V1()))
got := replicateDo(e, http.MethodGet, replicateBasePath+"/"+started["id"], "")
assert.NotContains(t, got.Body.String(), "topsecret")
diff --git a/internal/app/requests_endpoint_test.go b/internal/app/requests_endpoint_test.go
index d9626b1a6a..15c0bc8601 100644
--- a/internal/app/requests_endpoint_test.go
+++ b/internal/app/requests_endpoint_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strconv"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -64,14 +65,14 @@ func TestRequestsHandler(t *testing.T) {
if tt.wantCode != http.StatusOK {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.NotEmpty(t, body["message"])
return
}
var got []logger.CapturedRequest
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
paths := make([]string, 0, len(got))
for _, r := range got {
diff --git a/internal/app/s3_snapshot_options_test.go b/internal/app/s3_snapshot_options_test.go
index 48d7885ac8..fd26aa31ad 100644
--- a/internal/app/s3_snapshot_options_test.go
+++ b/internal/app/s3_snapshot_options_test.go
@@ -2,7 +2,7 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/chaos"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -164,7 +165,7 @@ func TestPersistManualEndpoints(t *testing.T) {
var out map[string]any
- require.NoError(t, json.Unmarshal(load.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(load.Body.Bytes(), &out, jsonx.V1()))
assert.InDelta(t, 1, out["loaded"], 0)
assert.Equal(t, "7", h3.kvs[0].getA())
}
diff --git a/internal/app/s3_sns_subject_wiring_test.go b/internal/app/s3_sns_subject_wiring_test.go
index db4a12339e..853f7860c7 100644
--- a/internal/app/s3_sns_subject_wiring_test.go
+++ b/internal/app/s3_sns_subject_wiring_test.go
@@ -1,13 +1,14 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
s3backend "github.com/blackbirdworks/gopherstack/services/s3"
snsbackend "github.com/blackbirdworks/gopherstack/services/sns"
sqsbackend "github.com/blackbirdworks/gopherstack/services/sqs"
@@ -83,7 +84,7 @@ func TestS3SNSPublisherAdapter_ForwardsSubject(t *testing.T) {
var envelope struct {
Subject string `json:"Subject"`
}
- require.NoError(t, json.Unmarshal([]byte(receivedBody), &envelope))
+ require.NoError(t, json.Unmarshal([]byte(receivedBody), &envelope, jsonx.V1()))
require.Equal(t, "S3Notification", envelope.Subject,
"s3SNSPublisherAdapter must forward the S3 notification dispatcher's subject to SNS "+
"Publish instead of hardcoding an empty one")
diff --git a/internal/app/s3tables_s3_wiring_test.go b/internal/app/s3tables_s3_wiring_test.go
index dbdc75ccfa..bcd2b1d887 100644
--- a/internal/app/s3tables_s3_wiring_test.go
+++ b/internal/app/s3tables_s3_wiring_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"strings"
"testing"
@@ -12,6 +12,8 @@ import (
s3tablestypes "github.com/aws/aws-sdk-go-v2/service/s3tables/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestS3TablesInitialMetadataMaterialized(t *testing.T) {
@@ -84,7 +86,7 @@ func TestS3TablesInitialMetadataMaterialized(t *testing.T) {
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(raw, &doc))
+ require.NoError(t, json.Unmarshal(raw, &doc, jsonx.V1()))
assert.InDelta(t, 2, doc["format-version"], 0)
assert.Equal(t, aws.ToString(loc.WarehouseLocation), doc["location"])
assert.InDelta(t, 2, doc["last-column-id"], 0)
diff --git a/internal/app/scheduler_universal_targets_test.go b/internal/app/scheduler_universal_targets_test.go
index 126c15b6c7..909b2065a0 100644
--- a/internal/app/scheduler_universal_targets_test.go
+++ b/internal/app/scheduler_universal_targets_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -10,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/scheduler"
schedtypes "github.com/aws/aws-sdk-go-v2/service/scheduler/types"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const universalPrefix = "arn:aws:scheduler:::aws-sdk:"
@@ -46,7 +48,7 @@ func TestSchedulerUniversalTargets(t *testing.T) {
t.Helper()
url, _ := authzQueue(t, fx, "universal-q")
- in, err := json.Marshal(map[string]string{"QueueUrl": url, "MessageBody": "hello"})
+ in, err := json.Marshal(map[string]string{"QueueUrl": url, "MessageBody": "hello"}, jsonx.V1())
require.NoError(t, err)
return string(in), url
@@ -97,7 +99,7 @@ func TestSchedulerUniversalTargets(t *testing.T) {
jd := setupBatchQueue(t, fx)
in, err := json.Marshal(map[string]string{
"JobName": "universal-job", "JobQueue": "eb-queue", "JobDefinition": jd,
- })
+ }, jsonx.V1())
require.NoError(t, err)
return string(in), ""
diff --git a/internal/app/service_role_authz_test.go b/internal/app/service_role_authz_test.go
index 6f2d10ceab..3508ce1c20 100644
--- a/internal/app/service_role_authz_test.go
+++ b/internal/app/service_role_authz_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
"time"
@@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -182,7 +183,7 @@ func TestServiceRoleAuthzStepFunctionsLegacy(t *testing.T) {
func jsonAtOptional(raw *string, path ...string) (string, bool) {
var cur any
- if raw == nil || json.Unmarshal([]byte(*raw), &cur) != nil {
+ if raw == nil || json.Unmarshal([]byte(*raw), &cur, jsonx.V1()) != nil {
return "", false
}
diff --git a/internal/app/ses_inbound_wiring_test.go b/internal/app/ses_inbound_wiring_test.go
index 1714c7a163..1a43609466 100644
--- a/internal/app/ses_inbound_wiring_test.go
+++ b/internal/app/ses_inbound_wiring_test.go
@@ -4,7 +4,7 @@ import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/smtp"
"testing"
@@ -18,6 +18,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
sesbackend "github.com/blackbirdworks/gopherstack/services/ses"
)
@@ -110,7 +111,7 @@ func TestSESInboundKMSAndAuthWiring(t *testing.T) {
require.NoError(t, err)
var matdesc map[string]string
- require.NoError(t, json.Unmarshal([]byte(obj.Metadata["x-amz-matdesc"]), &matdesc))
+ require.NoError(t, json.Unmarshal([]byte(obj.Metadata["x-amz-matdesc"]), &matdesc, jsonx.V1()))
dec, err := kmsC.Decrypt(ctx, &kms.DecryptInput{CiphertextBlob: wrapped, EncryptionContext: matdesc})
require.NoError(t, err)
diff --git a/internal/app/sesv2_sns_event_wiring_test.go b/internal/app/sesv2_sns_event_wiring_test.go
index b4f0c02e8a..def710603b 100644
--- a/internal/app/sesv2_sns_event_wiring_test.go
+++ b/internal/app/sesv2_sns_event_wiring_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
@@ -13,6 +13,8 @@ import (
sqstypes "github.com/aws/aws-sdk-go-v2/service/sqs/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestSESv2SNSEventDestinationWiring(t *testing.T) {
@@ -114,7 +116,7 @@ func TestSESv2SNSEventDestinationWiring(t *testing.T) {
Message string `json:"Message"`
}
- if json.Unmarshal([]byte(aws.ToString(m.Body)), &env) != nil {
+ if json.Unmarshal([]byte(aws.ToString(m.Body)), &env, jsonx.V1()) != nil {
continue
}
@@ -122,7 +124,7 @@ func TestSESv2SNSEventDestinationWiring(t *testing.T) {
EventType string `json:"eventType"`
}
- if json.Unmarshal([]byte(env.Message), &ev) == nil {
+ if json.Unmarshal([]byte(env.Message), &ev, jsonx.V1()) == nil {
got[ev.EventType] = true
}
}
diff --git a/internal/app/sfn_sdk_integration_test.go b/internal/app/sfn_sdk_integration_test.go
index 348dc98d78..55eb090404 100644
--- a/internal/app/sfn_sdk_integration_test.go
+++ b/internal/app/sfn_sdk_integration_test.go
@@ -2,7 +2,7 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net"
"net/http"
@@ -26,6 +26,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/chaos"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
sfnbackend "github.com/blackbirdworks/gopherstack/services/stepfunctions"
)
@@ -157,7 +158,7 @@ func jsonAt(t *testing.T, raw *string, path ...string) any {
t.Helper()
var cur any
- require.NoError(t, json.Unmarshal([]byte(aws.ToString(raw)), &cur))
+ require.NoError(t, json.Unmarshal([]byte(aws.ToString(raw)), &cur, jsonx.V1()))
for _, p := range path {
m, ok := cur.(map[string]any)
diff --git a/internal/app/sns_wiring_test.go b/internal/app/sns_wiring_test.go
index b4b3d9cab8..2772233fe2 100644
--- a/internal/app/sns_wiring_test.go
+++ b/internal/app/sns_wiring_test.go
@@ -2,7 +2,7 @@ package app
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
firehosebackend "github.com/blackbirdworks/gopherstack/services/firehose"
lambdabackend "github.com/blackbirdworks/gopherstack/services/lambda"
snsbackend "github.com/blackbirdworks/gopherstack/services/sns"
@@ -124,7 +125,7 @@ func TestWireSNSToLambdaFirehose_EndToEndDelivery(t *testing.T) {
require.NoError(t, err)
dlqARN := arn.Build("sqs", config.DefaultRegion, config.DefaultAccountID, "lambda-dlq")
- redrivePolicy, err := json.Marshal(map[string]string{"deadLetterTargetArn": dlqARN})
+ redrivePolicy, err := json.Marshal(map[string]string{"deadLetterTargetArn": dlqARN}, jsonx.V1())
require.NoError(t, err)
require.NoError(t, snsBk.SetSubscriptionAttributes(sub.SubscriptionArn, "RedrivePolicy", string(redrivePolicy)))
diff --git a/internal/app/sso_test.go b/internal/app/sso_test.go
index 3cc196dbd4..d422c28f06 100644
--- a/internal/app/sso_test.go
+++ b/internal/app/sso_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/chaos"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
identitystorebackend "github.com/blackbirdworks/gopherstack/services/identitystore"
@@ -194,7 +195,7 @@ func writeSSOTokenCache(t *testing.T, l ssoLogin, expiresAt time.Time) string {
"accessToken": l.accessToken, "expiresAt": expiresAt.UTC().Format(time.RFC3339),
"refreshToken": l.refreshToken, "clientId": l.clientID, "clientSecret": l.clientSecret,
"region": "us-east-1", "startUrl": ssoStartURL,
- })
+ }, jsonx.V1())
require.NoError(t, err)
require.NoError(t, os.WriteFile(path, data, 0o600))
diff --git a/internal/app/textract_rekognition_s3_wiring_test.go b/internal/app/textract_rekognition_s3_wiring_test.go
index c58743b500..039acde657 100644
--- a/internal/app/textract_rekognition_s3_wiring_test.go
+++ b/internal/app/textract_rekognition_s3_wiring_test.go
@@ -2,7 +2,7 @@ package app
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/chaos"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc"
"github.com/blackbirdworks/gopherstack/pkgs/service"
rekognitionbackend "github.com/blackbirdworks/gopherstack/services/rekognition"
@@ -31,7 +32,7 @@ func doJSONTargetRequest(
) (int, map[string]any) {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
@@ -44,7 +45,7 @@ func doJSONTargetRequest(
require.NoError(t, h(c))
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return rec.Code, resp
}
diff --git a/internal/app/timestreaminfluxdb_wiring_test.go b/internal/app/timestreaminfluxdb_wiring_test.go
index 0f83425e6d..7aa0526db0 100644
--- a/internal/app/timestreaminfluxdb_wiring_test.go
+++ b/internal/app/timestreaminfluxdb_wiring_test.go
@@ -1,7 +1,7 @@
package app
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -11,6 +11,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/timestreaminfluxdb/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestTimestreamInfluxDBAuthSecretWiring(t *testing.T) {
@@ -55,7 +57,7 @@ func TestTimestreamInfluxDBAuthSecretWiring(t *testing.T) {
require.NoError(t, err)
var secret map[string]string
- require.NoError(t, json.Unmarshal([]byte(aws.ToString(val.SecretString)), &secret))
+ require.NoError(t, json.Unmarshal([]byte(aws.ToString(val.SecretString)), &secret, jsonx.V1()))
assert.Equal(t, tt.wantUsername, secret["username"])
assert.Equal(t, "Passw0rdPassw0rd", secret["password"])
assert.Len(t, secret["token"], 64)
diff --git a/internal/app/verifiedpermissions_jwt_test.go b/internal/app/verifiedpermissions_jwt_test.go
index 681d4dc078..c2598cabe9 100644
--- a/internal/app/verifiedpermissions_jwt_test.go
+++ b/internal/app/verifiedpermissions_jwt_test.go
@@ -6,7 +6,8 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"log/slog"
"math/big"
@@ -28,6 +29,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/chaos"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
cognitoidpbackend "github.com/blackbirdworks/gopherstack/services/cognitoidp"
verifiedpermissionsbackend "github.com/blackbirdworks/gopherstack/services/verifiedpermissions"
@@ -304,11 +306,14 @@ func newOIDCServer(t *testing.T) *oidcServer {
t.Cleanup(o.srv.Close)
mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, _ *http.Request) {
- _ = json.NewEncoder(w).Encode(map[string]string{"issuer": o.srv.URL, "jwks_uri": o.srv.URL + "/keys"})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]string{"issuer": o.srv.URL, "jwks_uri": o.srv.URL + "/keys"},
+ )
})
mux.HandleFunc("/keys", func(w http.ResponseWriter, _ *http.Request) {
o.jwksHits.Add(1)
- _ = json.NewEncoder(w).Encode(map[string]any{"keys": []map[string]string{{
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), map[string]any{"keys": []map[string]string{{
"kty": "RSA", "use": "sig", "alg": "RS256", "kid": "k1",
"n": base64.RawURLEncoding.EncodeToString(key.N.Bytes()),
"e": base64.RawURLEncoding.EncodeToString(big.NewInt(int64(key.E)).Bytes()),
diff --git a/internal/awstest/awstest.go b/internal/awstest/awstest.go
index 827c45e81f..6ef1be0d87 100644
--- a/internal/awstest/awstest.go
+++ b/internal/awstest/awstest.go
@@ -8,13 +8,15 @@ package awstest
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http/httptest"
"testing"
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// NewContext builds an echo context and response recorder for an httptest
@@ -38,7 +40,7 @@ func NewJSONContext(
var body io.Reader
if payload != nil {
- raw, err := json.Marshal(payload)
+ raw, err := json.Marshal(payload, jsonx.V1())
require.NoError(t, err)
body = bytes.NewReader(raw)
}
@@ -53,5 +55,5 @@ func NewJSONContext(
func DecodeJSON(t *testing.T, rec *httptest.ResponseRecorder, v any) {
t.Helper()
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), v))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), v, jsonx.V1()))
}
diff --git a/internal/awswire/awswire.go b/internal/awswire/awswire.go
index f06527c661..e1a016f3a9 100644
--- a/internal/awswire/awswire.go
+++ b/internal/awswire/awswire.go
@@ -5,7 +5,7 @@ package awswire
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -17,6 +17,7 @@ import (
"github.com/aws/aws-sdk-go-v2/aws/retry"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions/sdkwire"
)
@@ -142,12 +143,12 @@ func Do[O any](ctx context.Context, c *Core, op string, in any) (*O, error) {
params := map[string]any{}
if in != nil {
- b, err := json.Marshal(in)
+ b, err := json.Marshal(in, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("encode %s request: %w", op, err)
}
- if err = json.Unmarshal(b, ¶ms); err != nil {
+ if err = json.Unmarshal(b, ¶ms, jsonx.V1()); err != nil {
return nil, fmt.Errorf("encode %s request: %w", op, err)
}
}
@@ -159,12 +160,12 @@ func Do[O any](ctx context.Context, c *Core, op string, in any) (*O, error) {
var out O
- b, err := json.Marshal(raw)
+ b, err := json.Marshal(raw, jsonx.V1())
if err != nil {
return nil, err
}
- if err = json.Unmarshal(b, &out); err != nil {
+ if err = json.Unmarshal(b, &out, jsonx.V1()); err != nil {
return nil, fmt.Errorf("decode %s response: %w", op, err)
}
diff --git a/internal/awswire/awswire_test.go b/internal/awswire/awswire_test.go
index e6b05e068f..80dd480feb 100644
--- a/internal/awswire/awswire_test.go
+++ b/internal/awswire/awswire_test.go
@@ -2,7 +2,7 @@ package awswire_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -25,6 +25,7 @@ import (
"github.com/blackbirdworks/gopherstack/internal/awswire/kms"
"github.com/blackbirdworks/gopherstack/internal/awswire/lambda"
"github.com/blackbirdworks/gopherstack/internal/awswire/sqs"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func testConfig(base string) aws.Config {
@@ -56,7 +57,7 @@ func TestClientSignsAndDecodes(t *testing.T) {
auth, token = r.Header.Get("Authorization"), r.Header.Get("X-Amz-Security-Token")
target = r.Header.Get("X-Amz-Target")
raw, _ := io.ReadAll(r.Body)
- _ = json.Unmarshal(raw, &body)
+ _ = json.Unmarshal(raw, &body, jsonx.V1())
w.Header().Set("Content-Type", "application/x-amz-json-1.1")
_, _ = w.Write([]byte(`{"KeyMetadata":{"KeyId":"k-1","Arn":"arn:aws:kms:us-west-2:1:key/k-1","Enabled":true}}`))
}))
@@ -166,7 +167,7 @@ func TestPaginator(t *testing.T) {
raw, _ := io.ReadAll(r.Body)
var in map[string]any
- _ = json.Unmarshal(raw, &in)
+ _ = json.Unmarshal(raw, &in, jsonx.V1())
marker, _ := in["Marker"].(string)
seen = append(seen, marker)
diff --git a/internal/dockercompat/client/client.go b/internal/dockercompat/client/client.go
index 7c6119f4ff..97ab333273 100644
--- a/internal/dockercompat/client/client.go
+++ b/internal/dockercompat/client/client.go
@@ -3,7 +3,8 @@ package client
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -19,6 +20,7 @@ import (
compatcontainer "github.com/blackbirdworks/gopherstack/internal/dockercompat/api/types/container"
compatimage "github.com/blackbirdworks/gopherstack/internal/dockercompat/api/types/image"
compatnetwork "github.com/blackbirdworks/gopherstack/internal/dockercompat/api/types/network"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Opt aliases the Moby client option type.
@@ -104,13 +106,13 @@ var errPushRejected = errors.New("image push failed")
// pushAnonymousAuth is the base64 of "{}": the X-Registry-Auth header for a registry without credentials.
const pushAnonymousAuth = "e30="
-func pushDigest(aux *json.RawMessage, statusLine, prev string) string {
+func pushDigest(aux *jsontext.Value, statusLine, prev string) string {
if aux != nil {
var a struct {
Digest string `json:"Digest"`
}
- if json.Unmarshal(*aux, &a) == nil && a.Digest != "" {
+ if json.Unmarshal(*aux, &a, jsonx.V1()) == nil && a.Digest != "" {
return a.Digest
}
}
diff --git a/internal/dockercompat/client/client_runtime_test.go b/internal/dockercompat/client/client_runtime_test.go
index 4a2bf77a1d..39a75ae8d1 100644
--- a/internal/dockercompat/client/client_runtime_test.go
+++ b/internal/dockercompat/client/client_runtime_test.go
@@ -1,7 +1,8 @@
package client_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +14,7 @@ import (
compatcontainer "github.com/blackbirdworks/gopherstack/internal/dockercompat/api/types/container"
"github.com/blackbirdworks/gopherstack/internal/dockercompat/client"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type fakeDaemon struct {
@@ -26,7 +28,7 @@ func (f *fakeDaemon) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch {
case strings.HasSuffix(r.URL.Path, "/containers/create"):
f.mu.Lock()
- _ = json.NewDecoder(r.Body).Decode(&f.createBody)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(r.Body), &f.createBody, jsonx.V1())
f.mu.Unlock()
w.WriteHeader(http.StatusCreated)
_, _ = w.Write([]byte(`{"Id":"abc","Warnings":[]}`))
diff --git a/internal/mcpserver/backend.go b/internal/mcpserver/backend.go
index be0feaa9c2..bafc7b6b7f 100644
--- a/internal/mcpserver/backend.go
+++ b/internal/mcpserver/backend.go
@@ -4,13 +4,15 @@ package mcpserver
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -40,7 +42,7 @@ func (b *backend) call(ctx context.Context, method, path string, q url.Values, i
var body io.Reader = http.NoBody
if in != nil {
- raw, err := json.Marshal(in)
+ raw, err := json.Marshal(in, jsonx.V1())
if err != nil {
return fmt.Errorf("encode request: %w", err)
}
@@ -81,7 +83,7 @@ func (b *backend) call(ctx context.Context, method, path string, q url.Values, i
return nil
}
- if err = json.Unmarshal(raw, out); err != nil {
+ if err = json.Unmarshal(raw, out, jsonx.V1()); err != nil {
return fmt.Errorf("decode response: %w", err)
}
diff --git a/internal/mcpserver/server.go b/internal/mcpserver/server.go
index 4a748d7aec..78e18adac3 100644
--- a/internal/mcpserver/server.go
+++ b/internal/mcpserver/server.go
@@ -2,10 +2,12 @@ package mcpserver
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/modelcontextprotocol/go-sdk/mcp"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -51,7 +53,7 @@ func (b *backend) infoResource(ctx context.Context, req *mcp.ReadResourceRequest
return nil, err
}
- raw, err := json.Marshal(h)
+ raw, err := json.Marshal(h, jsonx.V1())
if err != nil {
return nil, err
}
diff --git a/internal/replicator/sqs.go b/internal/replicator/sqs.go
index 516303ed42..bca02c3b0d 100644
--- a/internal/replicator/sqs.go
+++ b/internal/replicator/sqs.go
@@ -2,7 +2,7 @@ package replicator
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"path"
"slices"
@@ -10,6 +10,7 @@ import (
sqstypes "github.com/aws/aws-sdk-go-v2/service/sqs/types"
"github.com/blackbirdworks/gopherstack/internal/awswire/sqs"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func sqsCreateAttrs() []sqstypes.QueueAttributeName {
@@ -79,7 +80,7 @@ func dlqName(redrive string) string {
Arn string `json:"deadLetterTargetArn"`
}
- if json.Unmarshal([]byte(redrive), &rp) != nil || rp.Arn == "" {
+ if json.Unmarshal([]byte(redrive), &rp, jsonx.V1()) != nil || rp.Arn == "" {
return ""
}
diff --git a/pkgs/apptrace/middleware.go b/pkgs/apptrace/middleware.go
index 84f503ed5d..8afaa1187d 100644
--- a/pkgs/apptrace/middleware.go
+++ b/pkgs/apptrace/middleware.go
@@ -2,7 +2,7 @@ package apptrace
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -317,7 +318,7 @@ func (r *redactor) errorCode(header string, body []byte) string {
if b[0] == '{' {
var env errEnvelope
- if json.Unmarshal(b, &env) == nil {
+ if json.Unmarshal(b, &env, jsonx.V1()) == nil {
return cleanCode(env.pick())
}
diff --git a/pkgs/awserr/respond.go b/pkgs/awserr/respond.go
index 9679259561..6fc0f5e0fb 100644
--- a/pkgs/awserr/respond.go
+++ b/pkgs/awserr/respond.go
@@ -1,7 +1,7 @@
package awserr
import (
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"errors"
"hash/crc32"
@@ -9,6 +9,8 @@ import (
"strconv"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Protocol identifies the AWS wire protocol used to encode an error envelope.
@@ -129,7 +131,7 @@ func Write(c *echo.Context, proto Protocol, e APIError) error {
}
func writeJSON(c *echo.Context, contentType string, e APIError) error {
- body, _ := json.Marshal(jsonErrorEnvelope{Type: e.Code, Message: e.Message, MessageCap: e.Message})
+ body, _ := json.Marshal(jsonErrorEnvelope{Type: e.Code, Message: e.Message, MessageCap: e.Message}, jsonx.V1())
checksum := crc32.ChecksumIEEE(body)
c.Response().Header().Set("X-Amz-Crc32", strconv.FormatUint(uint64(checksum), 10))
c.Response().Header().Set("Content-Type", contentType)
diff --git a/pkgs/awserr/respond_test.go b/pkgs/awserr/respond_test.go
index 0fd7806b93..4c5caa2a0a 100644
--- a/pkgs/awserr/respond_test.go
+++ b/pkgs/awserr/respond_test.go
@@ -1,7 +1,7 @@
package awserr_test
import (
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestClassifyMatchesWrappedSentinel(t *testing.T) {
@@ -67,7 +68,7 @@ func TestWriteJSON10(t *testing.T) {
Type string `json:"__type"`
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", env.Type)
assert.Equal(t, "nope", env.Message)
}
diff --git a/pkgs/awstime/awstime_test.go b/pkgs/awstime/awstime_test.go
index b7d24971ff..2e016c78ef 100644
--- a/pkgs/awstime/awstime_test.go
+++ b/pkgs/awstime/awstime_test.go
@@ -1,11 +1,12 @@
package awstime_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestEpoch(t *testing.T) {
@@ -61,7 +62,7 @@ func TestEpochTimeJSON(t *testing.T) {
in := doc{At: awstime.NewEpochTime(time.Unix(1_700_000_000, 500_000_000).UTC())}
- raw, err := json.Marshal(in)
+ raw, err := json.Marshal(in, jsonx.V1())
if err != nil {
t.Fatal(err)
}
@@ -71,7 +72,7 @@ func TestEpochTimeJSON(t *testing.T) {
}
var out doc
- if err = json.Unmarshal(raw, &out); err != nil {
+ if err = json.Unmarshal(raw, &out, jsonx.V1()); err != nil {
t.Fatal(err)
}
@@ -79,7 +80,7 @@ func TestEpochTimeJSON(t *testing.T) {
t.Fatalf("round trip lost data: %+v", out)
}
- if err = json.Unmarshal([]byte(`{"at":"2026-01-01"}`), &out); err == nil {
+ if err = json.Unmarshal([]byte(`{"at":"2026-01-01"}`), &out, jsonx.V1()); err == nil {
t.Fatal("string timestamp must be rejected")
}
}
diff --git a/pkgs/chaos/handler.go b/pkgs/chaos/handler.go
index 2f15e1ada6..4ec74576c9 100644
--- a/pkgs/chaos/handler.go
+++ b/pkgs/chaos/handler.go
@@ -1,11 +1,13 @@
package chaos
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -83,7 +85,7 @@ func (h *apiHandler) getFaults(c *echo.Context) error {
// postFaults replaces the entire fault configuration.
func (h *apiHandler) postFaults(c *echo.Context) error {
var rules []FaultRule
- if err := json.NewDecoder(c.Request().Body).Decode(&rules); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &rules, jsonx.V1()); err != nil {
return c.String(http.StatusBadRequest, "invalid JSON: "+err.Error())
}
@@ -99,7 +101,7 @@ func (h *apiHandler) postFaults(c *echo.Context) error {
// patchFaults appends rules to the existing fault configuration.
func (h *apiHandler) patchFaults(c *echo.Context) error {
var rules []FaultRule
- if err := json.NewDecoder(c.Request().Body).Decode(&rules); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &rules, jsonx.V1()); err != nil {
return c.String(http.StatusBadRequest, "invalid JSON: "+err.Error())
}
@@ -115,7 +117,7 @@ func (h *apiHandler) patchFaults(c *echo.Context) error {
// deleteFaults removes rules matching the provided list.
func (h *apiHandler) deleteFaults(c *echo.Context) error {
var rules []FaultRule
- if err := json.NewDecoder(c.Request().Body).Decode(&rules); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &rules, jsonx.V1()); err != nil {
return c.String(http.StatusBadRequest, "invalid JSON: "+err.Error())
}
@@ -132,7 +134,7 @@ func (h *apiHandler) getEffects(c *echo.Context) error {
// postEffects updates the network effect configuration.
func (h *apiHandler) postEffects(c *echo.Context) error {
var effects NetworkEffects
- if err := json.NewDecoder(c.Request().Body).Decode(&effects); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &effects, jsonx.V1()); err != nil {
return c.String(http.StatusBadRequest, "invalid JSON: "+err.Error())
}
@@ -246,7 +248,7 @@ func (h *apiHandler) clearFaults(c *echo.Context) error {
// deleteFaultByIndex removes the fault rule at the given index.
func (h *apiHandler) deleteFaultByIndex(c *echo.Context) error {
var req deleteByIndexRequest
- if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &req, jsonx.V1()); err != nil {
return c.String(http.StatusBadRequest, "invalid JSON: "+err.Error())
}
diff --git a/pkgs/chaos/handler_test.go b/pkgs/chaos/handler_test.go
index 44233f6838..c984319328 100644
--- a/pkgs/chaos/handler_test.go
+++ b/pkgs/chaos/handler_test.go
@@ -2,7 +2,8 @@ package chaos_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/chaos"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -77,7 +79,7 @@ func TestChaosHandler_Faults_GetEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var rules []chaos.FaultRule
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rules))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rules, jsonx.V1()))
assert.Empty(t, rules)
}
@@ -138,14 +140,14 @@ func TestChaosHandler_Faults_PostReplacesRules(t *testing.T) {
reg := service.NewRegistry()
e := buildChaosAPI(t, store, reg)
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := callGroup(t, e, http.MethodPost, "/_gopherstack/chaos/faults", bodyBytes)
assert.Equal(t, tt.wantStatus, rec.Code)
var rules []chaos.FaultRule
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rules))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rules, jsonx.V1()))
assert.Equal(t, tt.wantRules, rules)
})
}
@@ -188,14 +190,14 @@ func TestChaosHandler_Faults_PatchAppendsRules(t *testing.T) {
reg := service.NewRegistry()
e := buildChaosAPI(t, store, reg)
- bodyBytes, err := json.Marshal(tt.patch)
+ bodyBytes, err := json.Marshal(tt.patch, jsonx.V1())
require.NoError(t, err)
rec := callGroup(t, e, http.MethodPatch, "/_gopherstack/chaos/faults", bodyBytes)
assert.Equal(t, tt.wantStatus, rec.Code)
var rules []chaos.FaultRule
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rules))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rules, jsonx.V1()))
assert.Len(t, rules, tt.wantCount)
})
}
@@ -242,14 +244,14 @@ func TestChaosHandler_Faults_DeleteRemovesRules(t *testing.T) {
reg := service.NewRegistry()
e := buildChaosAPI(t, store, reg)
- bodyBytes, err := json.Marshal(tt.delete)
+ bodyBytes, err := json.Marshal(tt.delete, jsonx.V1())
require.NoError(t, err)
rec := callGroup(t, e, http.MethodDelete, "/_gopherstack/chaos/faults", bodyBytes)
assert.Equal(t, tt.wantStatus, rec.Code)
var rules []chaos.FaultRule
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rules))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rules, jsonx.V1()))
assert.Len(t, rules, tt.wantCount)
})
}
@@ -301,7 +303,7 @@ func TestChaosHandler_Effects(t *testing.T) {
e := buildChaosAPI(t, store, reg)
if tt.postBody != nil {
- bodyBytes, err := json.Marshal(tt.postBody)
+ bodyBytes, err := json.Marshal(tt.postBody, jsonx.V1())
require.NoError(t, err)
postRec := callGroup(t, e, http.MethodPost, "/_gopherstack/chaos/effects", bodyBytes)
@@ -312,7 +314,7 @@ func TestChaosHandler_Effects(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var effects chaos.NetworkEffects
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &effects))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &effects, jsonx.V1()))
assert.Equal(t, tt.wantEffects, effects)
})
}
@@ -407,7 +409,7 @@ func TestChaosHandler_Targets(t *testing.T) {
}
var resp targetsResp
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
names := make([]string, 0, len(resp.Services))
for _, s := range resp.Services {
@@ -503,7 +505,7 @@ func TestChaosHandler_TargetsMerge(t *testing.T) {
}
var resp targetsResp
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// Find the entry for the expected service name.
var found *targetEntry
@@ -599,8 +601,8 @@ func TestChaosHandler_Activity_GetEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "application/json", rec.Header().Get("Content-Type"))
- var events []json.RawMessage
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&events))
+ var events []jsontext.Value
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &events, jsonx.V1()))
assert.Empty(t, events)
}
@@ -655,7 +657,7 @@ func TestChaosHandler_Activity_GetWithEvents(t *testing.T) {
var events []struct {
Service string `json:"service"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&events))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &events, jsonx.V1()))
require.Len(t, events, tt.wantCount)
if tt.wantFirst != "" {
@@ -676,11 +678,11 @@ func TestChaosHandler_Query_Empty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp struct {
- Faults []json.RawMessage `json:"faults"`
- Effects json.RawMessage `json:"effects"`
- Activity []json.RawMessage `json:"activity"`
+ Faults []jsontext.Value `json:"faults"`
+ Effects jsontext.Value `json:"effects"`
+ Activity []jsontext.Value `json:"activity"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Empty(t, resp.Faults)
assert.Empty(t, resp.Activity)
}
@@ -704,7 +706,7 @@ func TestChaosHandler_Query_WithData(t *testing.T) {
Effects chaos.NetworkEffects `json:"effects"`
Activity []chaos.ActivityEvent `json:"activity"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
require.Len(t, resp.Faults, 1)
assert.Equal(t, "s3", resp.Faults[0].Service)
require.NotNil(t, resp.Faults[0].Probability)
@@ -730,7 +732,7 @@ func TestChaosHandler_ClearFaults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var rules []chaos.FaultRule
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&rules))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &rules, jsonx.V1()))
assert.Empty(t, rules)
// Confirm store is empty.
@@ -801,7 +803,7 @@ func TestChaosHandler_DeleteFaultByIndex(t *testing.T) {
if tt.wantCode == http.StatusOK {
var rules []chaos.FaultRule
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&rules))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &rules, jsonx.V1()))
assert.Len(t, rules, tt.wantCount)
}
})
@@ -821,7 +823,7 @@ func TestChaosHandler_ResetEffects(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var effects chaos.NetworkEffects
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&effects))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &effects, jsonx.V1()))
assert.Zero(t, effects.Latency)
assert.Zero(t, effects.Jitter)
diff --git a/pkgs/chaos/localstack.go b/pkgs/chaos/localstack.go
index 07c601ce66..48c99f693c 100644
--- a/pkgs/chaos/localstack.go
+++ b/pkgs/chaos/localstack.go
@@ -1,11 +1,14 @@
package chaos
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -41,7 +44,7 @@ func (h *lsHandler) rules() []FaultRule { return h.store.GetRules() }
func decodeRules(c *echo.Context) ([]FaultRule, error) {
var rules []FaultRule
- if err := json.NewDecoder(c.Request().Body).Decode(&rules); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &rules, jsonx.V1()); err != nil {
return nil, err
}
@@ -111,7 +114,12 @@ func (h *lsHandler) getEffects(c *echo.Context) error {
func (h *lsHandler) postEffects(c *echo.Context) error {
var in localstackEffects
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil || in.Latency < 0 {
+ if err := json.UnmarshalDecode(
+ jsontext.NewDecoder(c.Request().Body),
+ &in,
+ jsonx.V1(),
+ ); err != nil ||
+ in.Latency < 0 {
return badRequest(c, errInvalidEffects)
}
diff --git a/pkgs/container/credentials_resources_test.go b/pkgs/container/credentials_resources_test.go
index 6221e76a26..a1a20df73f 100644
--- a/pkgs/container/credentials_resources_test.go
+++ b/pkgs/container/credentials_resources_test.go
@@ -3,7 +3,7 @@ package container_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"strings"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/internal/dockercompat/api/types/image"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type authRecordingAPI struct {
@@ -54,7 +55,7 @@ func TestPullImageWithCredentials(t *testing.T) {
var got map[string]string
- require.NoError(t, json.Unmarshal(raw, &got))
+ require.NoError(t, json.Unmarshal(raw, &got, jsonx.V1()))
assert.Equal(t, "puller", got["username"])
assert.Equal(t, "s3cret", got["password"])
assert.Contains(t, got["serveraddress"], strings.TrimPrefix(tc.wantServer, "docker.io"))
diff --git a/pkgs/deltalog/deltalog.go b/pkgs/deltalog/deltalog.go
index c2f896ebf2..dbfe6c1381 100644
--- a/pkgs/deltalog/deltalog.go
+++ b/pkgs/deltalog/deltalog.go
@@ -7,7 +7,8 @@ import (
"bufio"
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/url"
@@ -16,6 +17,7 @@ import (
"strconv"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/parquetio"
)
@@ -82,7 +84,7 @@ type action struct {
type addAction struct {
PartitionValues map[string]*string `json:"partitionValues"`
Path string `json:"path"`
- DeletionVector json.RawMessage `json:"deletionVector"`
+ DeletionVector jsontext.Value `json:"deletionVector"`
Size int64 `json:"size"`
}
@@ -210,7 +212,7 @@ func (s *state) loadCheckpoint(ctx context.Context, st Storage, cps map[int64][]
Version int64 `json:"version"`
}
- if json.Unmarshal(raw, &lc) == nil {
+ if json.Unmarshal(raw, &lc, jsonx.V1()) == nil {
if _, ok := cps[lc.Version]; ok {
best = lc.Version
}
@@ -260,13 +262,13 @@ func (s *state) applyCheckpoint(body []byte) error {
continue
}
- raw, merr := json.Marshal(v)
+ raw, merr := json.Marshal(v, jsonx.V1())
if merr != nil {
return merr
}
part := map[int]string{0: "add", 1: "metaData", 2: "protocol", 3: "remove"}[i]
- if uerr := json.Unmarshal([]byte(`{"`+part+`":`+string(raw)+`}`), &a); uerr != nil {
+ if uerr := json.Unmarshal([]byte(`{"`+part+`":`+string(raw)+`}`), &a, jsonx.V1()); uerr != nil {
return fmt.Errorf("%w: %w", ErrBadLog, uerr)
}
}
@@ -291,7 +293,7 @@ func (s *state) applyCommit(body []byte) error {
}
var a action
- if err := json.Unmarshal(line, &a); err != nil {
+ if err := json.Unmarshal(line, &a, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", ErrBadLog, err)
}
@@ -362,9 +364,9 @@ func (s *state) snapshot() (*Snapshot, error) {
}
type schemaField struct {
- Metadata map[string]any `json:"metadata"`
- Name string `json:"name"`
- Type json.RawMessage `json:"type"`
+ Metadata map[string]any `json:"metadata"`
+ Name string `json:"name"`
+ Type jsontext.Value `json:"type"`
}
func parseSchema(s, mode string) ([]Column, error) {
@@ -372,7 +374,7 @@ func parseSchema(s, mode string) ([]Column, error) {
Fields []schemaField `json:"fields"`
}
- if err := json.Unmarshal([]byte(s), &st); err != nil {
+ if err := json.Unmarshal([]byte(s), &st, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: schemaString: %w", ErrBadLog, err)
}
@@ -402,21 +404,21 @@ func parseSchema(s, mode string) ([]Column, error) {
return cols, nil
}
-func hiveType(raw json.RawMessage) (string, error) {
+func hiveType(raw jsontext.Value) (string, error) {
var prim string
- if err := json.Unmarshal(raw, &prim); err == nil {
+ if err := json.Unmarshal(raw, &prim, jsonx.V1()); err == nil {
return hivePrimitive(prim)
}
var n struct {
- Type string `json:"type"`
- ElementType json.RawMessage `json:"elementType"`
- KeyType json.RawMessage `json:"keyType"`
- ValueType json.RawMessage `json:"valueType"`
- Fields []schemaField `json:"fields"`
+ Type string `json:"type"`
+ ElementType jsontext.Value `json:"elementType"`
+ KeyType jsontext.Value `json:"keyType"`
+ ValueType jsontext.Value `json:"valueType"`
+ Fields []schemaField `json:"fields"`
}
- if err := json.Unmarshal(raw, &n); err != nil {
+ if err := json.Unmarshal(raw, &n, jsonx.V1()); err != nil {
return "", fmt.Errorf("%w: type %s", ErrBadLog, raw)
}
diff --git a/pkgs/dmsengine/kinesis_target.go b/pkgs/dmsengine/kinesis_target.go
index 377ca82500..67e6a014a1 100644
--- a/pkgs/dmsengine/kinesis_target.go
+++ b/pkgs/dmsengine/kinesis_target.go
@@ -3,12 +3,15 @@ package dmsengine
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errNoStreamARN = errors.New("KinesisSettings.StreamArn is required")
@@ -36,7 +39,7 @@ func newKinesisTarget(ep EndpointConfig, store StreamStore) (Target, error) {
var o kinesisOptions
if ep.KinesisSettings != "" {
- if err := json.Unmarshal([]byte(ep.KinesisSettings), &o); err != nil {
+ if err := json.Unmarshal([]byte(ep.KinesisSettings), &o, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid KinesisSettings: %w", err)
}
}
@@ -85,7 +88,7 @@ func (t *kinesisTarget) value(v any, k Kind) any {
if isNumeric(k) {
if _, err := strconv.ParseFloat(x, 64); err == nil {
- return json.Number(x)
+ return jsonx.Number(x)
}
}
@@ -163,14 +166,14 @@ type recordOut struct {
func (t *kinesisTarget) encode(data, meta map[string]any) ([]byte, error) {
var buf bytes.Buffer
- enc := json.NewEncoder(&buf)
- enc.SetEscapeHTML(false)
-
+ opts := []jsontext.Options{jsonx.V1(), jsontext.EscapeForHTML(false)}
if !strings.EqualFold(t.opts.MessageFormat, "json-unformatted") {
- enc.SetIndent("", " ")
+ opts = append(opts, jsontext.Multiline(true), jsontext.WithIndent(" "))
}
- if err := enc.Encode(recordOut{Data: data, Metadata: meta}); err != nil {
+ enc := jsontext.NewEncoder(&buf, opts...)
+
+ if err := json.MarshalEncode(enc, recordOut{Data: data, Metadata: meta}); err != nil {
return nil, err
}
diff --git a/pkgs/dmsengine/mapping.go b/pkgs/dmsengine/mapping.go
index ee83aa71d7..9abc2d3ba2 100644
--- a/pkgs/dmsengine/mapping.go
+++ b/pkgs/dmsengine/mapping.go
@@ -1,13 +1,15 @@
package dmsengine
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"regexp"
"sort"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrMapping reports an invalid table-mappings document.
@@ -30,15 +32,15 @@ const (
type flexString string
func (f *flexString) UnmarshalJSON(b []byte) error {
- var n json.Number
- if err := json.Unmarshal(b, &n); err == nil {
+ var n jsonx.Number
+ if err := json.Unmarshal(b, &n, jsonx.V1()); err == nil {
*f = flexString(n.String())
return nil
}
var s string
- if err := json.Unmarshal(b, &s); err != nil {
+ if err := json.Unmarshal(b, &s, jsonx.V1()); err != nil {
return err
}
@@ -75,7 +77,7 @@ func ParseMappings(doc string) (Mappings, error) {
return m, nil
}
- if err := json.Unmarshal([]byte(doc), &m); err != nil {
+ if err := json.Unmarshal([]byte(doc), &m, jsonx.V1()); err != nil {
return m, fmt.Errorf("%w: %w", ErrMapping, err)
}
diff --git a/pkgs/dmsengine/s3_target.go b/pkgs/dmsengine/s3_target.go
index dd650cf20a..c6a2d373f0 100644
--- a/pkgs/dmsengine/s3_target.go
+++ b/pkgs/dmsengine/s3_target.go
@@ -4,12 +4,14 @@ import (
"bytes"
"context"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"strings"
"sync"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -71,7 +73,7 @@ func newS3Target(ep EndpointConfig, store ObjectStore) (Target, error) {
var o s3Options
if ep.S3Settings != "" {
- if err := json.Unmarshal([]byte(ep.S3Settings), &o); err != nil {
+ if err := json.Unmarshal([]byte(ep.S3Settings), &o, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid S3Settings: %w", err)
}
}
diff --git a/pkgs/dmsengine/settings.go b/pkgs/dmsengine/settings.go
index 62a678aa5b..be03f552b3 100644
--- a/pkgs/dmsengine/settings.go
+++ b/pkgs/dmsengine/settings.go
@@ -1,10 +1,12 @@
package dmsengine
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrInvalidSettings reports an unusable ReplicationTaskSettings document.
@@ -34,7 +36,7 @@ func ParseTaskSettings(doc string) (TaskSettings, error) {
} `json:"FullLoadSettings"`
}
- if err := json.Unmarshal([]byte(doc), &raw); err != nil {
+ if err := json.Unmarshal([]byte(doc), &raw, jsonx.V1()); err != nil {
return ts, fmt.Errorf("%w: %w", ErrInvalidSettings, err)
}
diff --git a/pkgs/dmsengine/targets_test.go b/pkgs/dmsengine/targets_test.go
index 0e17e83597..86e20204ec 100644
--- a/pkgs/dmsengine/targets_test.go
+++ b/pkgs/dmsengine/targets_test.go
@@ -2,7 +2,7 @@ package dmsengine_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sort"
"strings"
"sync"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/dmsengine"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/parquetio"
)
@@ -239,9 +240,9 @@ func TestKinesisTargetRecords(t *testing.T) {
var first, second, del map[string]map[string]any
- require.NoError(t, json.Unmarshal([]byte(store.recs[0]), &first))
- require.NoError(t, json.Unmarshal([]byte(store.recs[1]), &second))
- require.NoError(t, json.Unmarshal([]byte(store.recs[2]), &del))
+ require.NoError(t, json.Unmarshal([]byte(store.recs[0]), &first, jsonx.V1()))
+ require.NoError(t, json.Unmarshal([]byte(store.recs[1]), &second, jsonx.V1()))
+ require.NoError(t, json.Unmarshal([]byte(store.recs[2]), &del, jsonx.V1()))
assert.Equal(t, map[string]any{"id": float64(1), "name": "alpha"}, first["data"])
assert.Equal(t, map[string]any{"id": float64(2)}, second["data"], "nulls are omitted by default")
diff --git a/pkgs/docker/pull.go b/pkgs/docker/pull.go
index 11b6650f85..d52ac6ba48 100644
--- a/pkgs/docker/pull.go
+++ b/pkgs/docker/pull.go
@@ -1,10 +1,14 @@
package docker
import (
- "encoding/json"
+ "bytes"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrImagePull wraps failures the daemon reports in an image pull stream.
@@ -20,12 +24,12 @@ type pullMessage struct {
// ConsumePullStream reads an ImagePull response to EOF (closing early cancels
// the pull) and returns the first error the daemon reports in the stream.
func ConsumePullStream(r io.Reader) error {
- dec := json.NewDecoder(r)
+ dec := jsontext.NewDecoder(r)
for {
var msg pullMessage
- err := dec.Decode(&msg)
+ err := json.UnmarshalDecode(dec, &msg, jsonx.V1())
if errors.Is(err, io.EOF) {
return nil
}
@@ -37,13 +41,13 @@ func ConsumePullStream(r io.Reader) error {
}
if msg.ErrorDetail != nil && msg.ErrorDetail.Message != "" {
- _, _ = io.Copy(io.Discard, io.MultiReader(dec.Buffered(), r))
+ _, _ = io.Copy(io.Discard, io.MultiReader(bytes.NewReader(dec.UnreadBuffer()), r))
return fmt.Errorf("%w: %s", ErrImagePull, msg.ErrorDetail.Message)
}
if msg.Error != "" {
- _, _ = io.Copy(io.Discard, io.MultiReader(dec.Buffered(), r))
+ _, _ = io.Copy(io.Discard, io.MultiReader(bytes.NewReader(dec.UnreadBuffer()), r))
return fmt.Errorf("%w: %s", ErrImagePull, msg.Error)
}
diff --git a/pkgs/dynamoattr/attr.go b/pkgs/dynamoattr/attr.go
index 39e782c0c1..d0e3b4d4cd 100644
--- a/pkgs/dynamoattr/attr.go
+++ b/pkgs/dynamoattr/attr.go
@@ -1,12 +1,13 @@
package dynamoattr
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strconv"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/dynamonum"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// UnwrapAttributeValue converts a DynamoDB wire attribute map into a bare value when possible.
@@ -163,7 +164,7 @@ func ToString(v any) string {
case nil:
return ""
default:
- b, err := json.Marshal(s)
+ b, err := json.Marshal(s, jsonx.V1())
if err != nil {
return fmt.Sprintf("%v", s)
}
diff --git a/pkgs/extensions/control.go b/pkgs/extensions/control.go
index 0036f52527..a4c4ee3d34 100644
--- a/pkgs/extensions/control.go
+++ b/pkgs/extensions/control.go
@@ -2,7 +2,8 @@ package extensions
import (
"crypto/subtle"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net"
@@ -11,6 +12,8 @@ import (
"slices"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -115,8 +118,8 @@ func (m *Manager) ServeRegister(w http.ResponseWriter, r *http.Request) {
var reg Registration
- dec := json.NewDecoder(http.MaxBytesReader(w, r.Body, maxRegistrationBytes))
- if err := dec.Decode(®); err != nil {
+ dec := jsontext.NewDecoder(http.MaxBytesReader(w, r.Body, maxRegistrationBytes))
+ if err := json.UnmarshalDecode(dec, ®, jsonx.V1()); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{keyMessage: "invalid JSON: " + err.Error()})
return
@@ -287,5 +290,5 @@ func overlap(a, b []string) bool {
func writeJSON(w http.ResponseWriter, code int, v any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
- _ = json.NewEncoder(w).Encode(v)
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), v)
}
diff --git a/pkgs/extensions/hooks.go b/pkgs/extensions/hooks.go
index 4490e254f7..d45ff90f5c 100644
--- a/pkgs/extensions/hooks.go
+++ b/pkgs/extensions/hooks.go
@@ -3,7 +3,7 @@ package extensions
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -11,6 +11,7 @@ import (
"net/url"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -320,12 +321,12 @@ func (h *HookSet) Post(r *http.Request, rec *Recorder) *HookResponse {
func (h *HookSet) call(ctx context.Context, b boundHook, path string, req hookRequest, rep any) *HookResponse {
req.Extension = b.name
- payload, err := json.Marshal(req)
+ payload, err := json.Marshal(req, jsonx.V1())
if err == nil {
var out []byte
if out, err = h.m.post(ctx, b.ep, path, payload, b.timeout); err == nil && len(bytes.TrimSpace(out)) > 0 {
- err = json.Unmarshal(out, rep)
+ err = json.Unmarshal(out, rep, jsonx.V1())
}
}
diff --git a/pkgs/extensions/hooks_internal_test.go b/pkgs/extensions/hooks_internal_test.go
index d7be9176cb..cc12696239 100644
--- a/pkgs/extensions/hooks_internal_test.go
+++ b/pkgs/extensions/hooks_internal_test.go
@@ -1,7 +1,8 @@
package extensions
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -10,6 +11,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func hookManager(t *testing.T, failOpen *bool, hook RequestHook, handler http.HandlerFunc) *Manager {
@@ -129,7 +132,7 @@ func TestPreHookPayload(t *testing.T) {
var got hookRequest
m := hookManager(t, nil, RequestHook{Pre: "/pre"}, func(_ http.ResponseWriter, r *http.Request) {
- _ = json.NewDecoder(r.Body).Decode(&got)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(r.Body), &got, jsonx.V1())
})
req := httptest.NewRequest(http.MethodPut, "/bkt/key?x=1", strings.NewReader("data"))
@@ -172,7 +175,7 @@ func TestPostHook(t *testing.T) {
var seen hookRequest
m := hookManager(t, nil, RequestHook{Post: "/post"}, func(w http.ResponseWriter, r *http.Request) {
- _ = json.NewDecoder(r.Body).Decode(&seen)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(r.Body), &seen, jsonx.V1())
_, _ = io.WriteString(w, tt.reply)
})
diff --git a/pkgs/extensions/main_internal_test.go b/pkgs/extensions/main_internal_test.go
index 6daa0cd011..93302615e3 100644
--- a/pkgs/extensions/main_internal_test.go
+++ b/pkgs/extensions/main_internal_test.go
@@ -2,7 +2,7 @@ package extensions
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net"
"net/http"
@@ -12,6 +12,7 @@ import (
"testing"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testleak"
)
@@ -90,7 +91,7 @@ func runHelper() {
}
func mustJSON(v any) string {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
panic(err)
}
diff --git a/pkgs/gcpdispatch/gcpdispatch.go b/pkgs/gcpdispatch/gcpdispatch.go
index 28f1c349db..acbf61761e 100644
--- a/pkgs/gcpdispatch/gcpdispatch.go
+++ b/pkgs/gcpdispatch/gcpdispatch.go
@@ -3,12 +3,14 @@ package gcpdispatch
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"math"
"net/http"
"time"
"google.golang.org/genproto/googleapis/rpc/code"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -65,7 +67,7 @@ func Backoff(lo, hi time.Duration, doublings, failures int32) time.Duration {
}
func b64(v any) string {
- raw, _ := json.Marshal(v)
+ raw, _ := json.Marshal(v, jsonx.V1())
return base64.RawURLEncoding.EncodeToString(raw)
}
diff --git a/pkgs/gcpdispatch/gcpdispatch_test.go b/pkgs/gcpdispatch/gcpdispatch_test.go
index 6a5ac8b8aa..1be6edd4eb 100644
--- a/pkgs/gcpdispatch/gcpdispatch_test.go
+++ b/pkgs/gcpdispatch/gcpdispatch_test.go
@@ -2,7 +2,7 @@ package gcpdispatch_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -13,6 +13,7 @@ import (
"google.golang.org/genproto/googleapis/rpc/code"
"github.com/blackbirdworks/gopherstack/pkgs/gcpdispatch"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHTTPToRPC(t *testing.T) {
@@ -81,7 +82,7 @@ func TestTokens(t *testing.T) {
require.NoError(t, err)
var claims map[string]any
- require.NoError(t, json.Unmarshal(raw, &claims))
+ require.NoError(t, json.Unmarshal(raw, &claims, jsonx.V1()))
assert.Equal(t, "https://x.test/y", claims["aud"])
assert.Equal(t, "sa@p.iam", claims["email"])
diff --git a/pkgs/gcplro/store.go b/pkgs/gcplro/store.go
index 82353d6b47..508a2fe6ee 100644
--- a/pkgs/gcplro/store.go
+++ b/pkgs/gcplro/store.go
@@ -4,7 +4,7 @@ package gcplro
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"slices"
"strings"
@@ -103,11 +103,11 @@ func (s *Store) Reset() {
}
// Snapshot serialises every operation as protojson.
-func (s *Store) Snapshot() map[string]json.RawMessage {
+func (s *Store) Snapshot() map[string]jsontext.Value {
s.mu.RLock("Snapshot")
defer s.mu.RUnlock()
- out := make(map[string]json.RawMessage, len(s.ops))
+ out := make(map[string]jsontext.Value, len(s.ops))
for name, op := range s.ops {
if raw, err := protojson.Marshal(op); err == nil {
@@ -119,7 +119,7 @@ func (s *Store) Snapshot() map[string]json.RawMessage {
}
// Restore replaces the stored operations with a Snapshot result.
-func (s *Store) Restore(snap map[string]json.RawMessage) error {
+func (s *Store) Restore(snap map[string]jsontext.Value) error {
ops := make(map[string]*longrunningpb.Operation, len(snap))
for name, raw := range snap {
diff --git a/pkgs/gcprest/errors.go b/pkgs/gcprest/errors.go
index 1a87408b98..1792922049 100644
--- a/pkgs/gcprest/errors.go
+++ b/pkgs/gcprest/errors.go
@@ -1,12 +1,15 @@
package gcprest
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/encoding/protojson"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type codeInfo struct {
@@ -40,10 +43,10 @@ type errorBody struct {
}
type errorDetail struct {
- Message string `json:"message"`
- Status string `json:"status"`
- Details []json.RawMessage `json:"details"`
- Code int `json:"code"`
+ Message string `json:"message"`
+ Status string `json:"status"`
+ Details []jsontext.Value `json:"details"`
+ Code int `json:"code"`
}
// HTTPStatus maps a gRPC code to the HTTP status Google APIs use for it.
@@ -63,7 +66,7 @@ func writeError(w http.ResponseWriter, err error) {
info = codeInfo{"UNKNOWN", http.StatusInternalServerError}
}
- details := make([]json.RawMessage, 0, len(st.Proto().GetDetails()))
+ details := make([]jsontext.Value, 0, len(st.Proto().GetDetails()))
for _, d := range st.Proto().GetDetails() {
if b, mErr := protojson.Marshal(d); mErr == nil {
@@ -77,7 +80,7 @@ func writeError(w http.ResponseWriter, err error) {
}
func writeJSON(w http.ResponseWriter, code int, v any) {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
diff --git a/pkgs/gcpserve/logentry.go b/pkgs/gcpserve/logentry.go
index c3ba1b2a5c..986ad7c253 100644
--- a/pkgs/gcpserve/logentry.go
+++ b/pkgs/gcpserve/logentry.go
@@ -1,7 +1,7 @@
package gcpserve
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"strconv"
"strings"
@@ -9,6 +9,8 @@ import (
"cloud.google.com/go/logging/apiv2/loggingpb"
ltype "google.golang.org/genproto/googleapis/logging/type"
"google.golang.org/protobuf/types/known/structpb"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Special JSON fields the structured-logging agent lifts out of jsonPayload.
@@ -49,7 +51,7 @@ func jsonObject(line string) (map[string]any, bool) {
}
var obj map[string]any
- if err := json.Unmarshal([]byte(s), &obj); err != nil || obj == nil {
+ if err := json.Unmarshal([]byte(s), &obj, jsonx.V1()); err != nil || obj == nil {
return nil, false
}
diff --git a/pkgs/handler/handler_test.go b/pkgs/handler/handler_test.go
index 7d59017efe..507292359b 100644
--- a/pkgs/handler/handler_test.go
+++ b/pkgs/handler/handler_test.go
@@ -2,7 +2,7 @@ package handler_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/handler"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- OperationContext ---
@@ -158,7 +159,7 @@ func TestWriteJSON_SetsHeadersAndBody(t *testing.T) {
assert.Equal(t, "application/json", rec.Header().Get("Content-Type"))
var got map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "value", got["key"])
}
diff --git a/pkgs/handler/response.go b/pkgs/handler/response.go
index f91d696f21..1968af6d81 100644
--- a/pkgs/handler/response.go
+++ b/pkgs/handler/response.go
@@ -2,20 +2,21 @@ package handler
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"hash/crc32"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
// WriteJSON marshals the payload to JSON, sets standard headers, and writes the response.
// Sets Content-Type to "application/json" and Content-Length.
func WriteJSON(w http.ResponseWriter, code int, payload any) error {
- body, err := json.Marshal(payload)
+ body, err := json.Marshal(payload, jsonx.V1())
if err != nil {
return err
}
@@ -33,7 +34,7 @@ func WriteJSON(w http.ResponseWriter, code int, payload any) error {
// and writes the response. The checksum is required by the DynamoDB wire protocol.
// Sets Content-Type to "application/x-amz-json-1.0" and X-Amz-Crc32.
func WriteJSONWithChecksum(w http.ResponseWriter, code int, payload any) error {
- body, err := json.Marshal(payload)
+ body, err := json.Marshal(payload, jsonx.V1())
if err != nil {
return err
}
diff --git a/pkgs/handler/wire_golden_test.go b/pkgs/handler/wire_golden_test.go
new file mode 100644
index 0000000000..5264b79685
--- /dev/null
+++ b/pkgs/handler/wire_golden_test.go
@@ -0,0 +1,141 @@
+package handler_test
+
+import (
+ "encoding/json/jsontext"
+ "net/http/httptest"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/handler"
+)
+
+type goldenAttr struct {
+ S *string `json:"S,omitempty"`
+ N *string `json:"N,omitempty"`
+ B []byte `json:"B,omitempty"`
+ NULL *bool `json:"NULL,omitempty"`
+ M map[string]*goldenAttr `json:"M,omitempty"`
+ L []*goldenAttr `json:"L,omitempty"`
+}
+
+type goldenScanOutput struct {
+ LastEvaluatedKey map[string]*goldenAttr `json:"LastEvaluatedKey,omitempty"`
+ Items []map[string]*goldenAttr `json:"Items"`
+ Count int32 `json:"Count"`
+ ScannedCount int32 `json:"ScannedCount"`
+}
+
+type goldenSQSMessage struct {
+ Attributes map[string]string `json:"Attributes"`
+ MessageID string `json:"MessageId"`
+ Body string `json:"Body"`
+ MD5 string `json:"MD5OfBody"`
+}
+
+type goldenSQSOutput struct {
+ Messages []goldenSQSMessage `json:"Messages"`
+}
+
+type goldenMetricResult struct {
+ ID string `json:"Id"`
+ Label string `json:"Label"`
+ StatusCode string `json:"StatusCode"`
+ Timestamps []float64 `json:"Timestamps"`
+ Values []float64 `json:"Values"`
+ Messages []string `json:"Messages"`
+ Extra jsontext.Value `json:"Extra,omitempty"`
+}
+
+type goldenMetricOutput struct {
+ NextToken *string `json:"NextToken,omitempty"`
+ Results []goldenMetricResult `json:"MetricDataResults"`
+}
+
+func TestWireGoldenJSONRPC(t *testing.T) {
+ t.Parallel()
+
+ ts := time.Date(2025, 3, 4, 5, 6, 7, 123_000_000, time.UTC)
+ yes := true
+
+ tests := []struct {
+ payload any
+ name string
+ body string
+ crc string
+ }{
+ {
+ name: "dynamodb scan",
+ payload: goldenScanOutput{
+ Items: []map[string]*goldenAttr{{
+ "id": {S: new("a&c
\"q\"")},
+ "n": {N: new("1.50")},
+ "bin": {B: []byte{1, 2, 3}},
+ "nul": {NULL: &yes},
+ "m": {M: map[string]*goldenAttr{"z": {S: new("1")}, "a": {S: new("2")}}},
+ "list": {L: []*goldenAttr{{S: new("x")}, {N: new("2")}}},
+ }},
+ Count: 1,
+ ScannedCount: 1,
+ },
+ body: `{"Items":[{"bin":{"B":"AQID"},"id":{"S":"a\u003cb\u003e\u0026c\u2028\"q\""},` +
+ `"list":{"L":[{"S":"x"},{"N":"2"}]},"m":{"M":{"a":{"S":"2"},"z":{"S":"1"}}},` +
+ `"n":{"N":"1.50"},"nul":{"NULL":true}}],"Count":1,"ScannedCount":1}`,
+ crc: "2457307943",
+ },
+ {
+ name: "dynamodb empty scan",
+ payload: goldenScanOutput{},
+ body: `{"Items":null,"Count":0,"ScannedCount":0}`,
+ crc: "4196347728",
+ },
+ {
+ name: "sqs receive",
+ payload: goldenSQSOutput{Messages: []goldenSQSMessage{{
+ MessageID: "m-1",
+ Body: `{"k":""}`,
+ MD5: "abc",
+ }}},
+ body: `{"Messages":[{"Attributes":null,"MessageId":"m-1",` +
+ `"Body":"{\"k\":\"\u003cv\u003e\"}","MD5OfBody":"abc"}]}`,
+ crc: "2547812670",
+ },
+ {
+ name: "sqs empty",
+ payload: goldenSQSOutput{},
+ body: `{"Messages":null}`,
+ crc: "1433687114",
+ },
+ {
+ name: "cloudwatch get metric data",
+ payload: goldenMetricOutput{Results: []goldenMetricResult{{
+ ID: "q1",
+ Label: "CPU ",
+ StatusCode: "Complete",
+ Timestamps: []float64{awstime.Epoch(ts), awstime.Epoch(ts.Add(time.Minute))},
+ Values: []float64{1.5, 2, 1e21, 0.000001, 1e-7, 100, 123456789012},
+ Extra: jsontext.Value(` { "a" : [1, 2] } `),
+ }}},
+ body: `{"MetricDataResults":[{"Id":"q1","Label":"CPU \u003cavg\u003e",` +
+ `"StatusCode":"Complete","Timestamps":[1741064767.1230001,1741064827.1230001],` +
+ `"Values":[1.5,2,1e+21,0.000001,1e-7,100,123456789012],"Messages":null,` +
+ `"Extra":{"a":[1,2]}}]}`,
+ crc: "2076035819",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ rec := httptest.NewRecorder()
+ require.NoError(t, handler.WriteJSONWithChecksum(rec, 200, tt.payload))
+
+ assert.Equal(t, tt.body, rec.Body.String())
+ assert.Equal(t, tt.crc, rec.Header().Get("X-Amz-Crc32"))
+ })
+ }
+}
diff --git a/pkgs/httputils/httputils.go b/pkgs/httputils/httputils.go
index cd7f4741e8..dd328e7244 100644
--- a/pkgs/httputils/httputils.go
+++ b/pkgs/httputils/httputils.go
@@ -3,7 +3,7 @@ package httputils
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"hash/crc32"
"io"
@@ -18,6 +18,7 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -163,7 +164,7 @@ func ParseFormBody(r *http.Request) (url.Values, error) {
func WriteJSON(ctx context.Context, w http.ResponseWriter, code int, payload any) {
log := logger.Load(ctx)
- body, err := json.Marshal(payload)
+ body, err := json.Marshal(payload, jsonx.V1())
if err != nil {
log.ErrorContext(ctx, "failed to marshal JSON response", "error", err)
http.Error(w, "internal server error", http.StatusInternalServerError)
@@ -209,7 +210,7 @@ func WriteXML(ctx context.Context, w http.ResponseWriter, code int, payload any)
func WriteDynamoDBResponse(ctx context.Context, w http.ResponseWriter, code int, payload any) {
log := logger.Load(ctx)
- body, err := json.Marshal(payload)
+ body, err := json.Marshal(payload, jsonx.V1())
if err != nil {
log.ErrorContext(ctx, "failed to marshal DynamoDB response", "error", err)
http.Error(w,
@@ -644,3 +645,19 @@ func ReadBodyLimit(r *http.Request, limit int64) ([]byte, error) {
return body, nil
}
+
+// WriteJSONBytes writes an already-encoded JSON body with a JSON content type.
+func WriteJSONBytes(w http.ResponseWriter, code int, body []byte) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Header().Set("X-Content-Type-Options", "nosniff")
+ w.WriteHeader(code)
+ _, _ = w.Write(body)
+}
+
+// WriteAmzJSON11Bytes writes an already-encoded body as application/x-amz-json-1.1.
+func WriteAmzJSON11Bytes(w http.ResponseWriter, code int, body []byte) {
+ w.Header().Set("Content-Type", "application/x-amz-json-1.1")
+ w.Header().Set("X-Content-Type-Options", "nosniff")
+ w.WriteHeader(code)
+ _, _ = w.Write(body)
+}
diff --git a/pkgs/httputils/httputils_test.go b/pkgs/httputils/httputils_test.go
index 468845aaa2..1cebcab8f8 100644
--- a/pkgs/httputils/httputils_test.go
+++ b/pkgs/httputils/httputils_test.go
@@ -458,3 +458,43 @@ func TestWriteS3ErrorResponse(t *testing.T) {
assert.Equal(t, "application/xml", w.Header().Get("Content-Type"))
assert.Contains(t, w.Body.String(), "NoSuchKey")
}
+
+func TestWriteJSONBytes(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ wantBody string
+ body []byte
+ code int
+ }{
+ {name: "with_body", body: []byte(`{"a":1}`), wantBody: `{"a":1}`, code: http.StatusCreated},
+ {name: "no_body", wantBody: "", code: http.StatusOK},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ rec := httptest.NewRecorder()
+ httputils.WriteJSONBytes(rec, tt.code, tt.body)
+
+ assert.Equal(t, tt.code, rec.Code)
+ assert.Equal(t, "application/json", rec.Header().Get("Content-Type"))
+ assert.Equal(t, "nosniff", rec.Header().Get("X-Content-Type-Options"))
+ assert.Equal(t, tt.wantBody, rec.Body.String())
+ })
+ }
+}
+
+func TestWriteAmzJSON11Bytes(t *testing.T) {
+ t.Parallel()
+
+ rec := httptest.NewRecorder()
+ httputils.WriteAmzJSON11Bytes(rec, http.StatusAccepted, []byte(`{}`))
+
+ assert.Equal(t, http.StatusAccepted, rec.Code)
+ assert.Equal(t, "application/x-amz-json-1.1", rec.Header().Get("Content-Type"))
+ assert.Equal(t, "nosniff", rec.Header().Get("X-Content-Type-Options"))
+ assert.JSONEq(t, `{}`, rec.Body.String())
+}
diff --git a/pkgs/iceberg/iceberg_test.go b/pkgs/iceberg/iceberg_test.go
index 92f85282af..08905f07e4 100644
--- a/pkgs/iceberg/iceberg_test.go
+++ b/pkgs/iceberg/iceberg_test.go
@@ -1,7 +1,8 @@
package iceberg_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"time"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/iceberg"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestTransformSpecVectors(t *testing.T) {
@@ -184,8 +186,8 @@ func TestMetadataPreservesUnknownFields(t *testing.T) {
out, err := iceberg.EncodeMetadata(m)
require.NoError(t, err)
- var generic map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(out, &generic))
+ var generic map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(out, &generic, jsonx.V1()))
assert.Contains(t, generic, "statistics")
_, err = iceberg.ParseMetadata([]byte(`{"format-version":9}`))
@@ -206,7 +208,7 @@ func TestHiveType(t *testing.T) {
t.Run(tc.want, func(t *testing.T) {
t.Parallel()
- got, err := iceberg.HiveType(json.RawMessage(tc.in))
+ got, err := iceberg.HiveType(jsontext.Value(tc.in))
require.NoError(t, err)
assert.Equal(t, tc.want, got)
})
diff --git a/pkgs/iceberg/manifest.go b/pkgs/iceberg/manifest.go
index 867264f0aa..23224e48aa 100644
--- a/pkgs/iceberg/manifest.go
+++ b/pkgs/iceberg/manifest.go
@@ -2,13 +2,15 @@ package iceberg
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"strconv"
"github.com/iskorotkov/avro/v2"
"github.com/iskorotkov/avro/v2/ocf"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Manifest entry statuses and content types from the table spec.
@@ -399,7 +401,7 @@ func manifestSchema(meta *TableMetadata, spec PartitionSpec) (string, error) {
},
}
- b, err := json.Marshal(schema)
+ b, err := json.Marshal(schema, jsonx.V1())
return string(b), err
}
@@ -415,8 +417,8 @@ func EncodeManifest(meta *TableMetadata, spec PartitionSpec, content int, entrie
return nil, fmt.Errorf("%w: %w", ErrInvalidManifest, err)
}
- cur, _ := json.Marshal(meta.CurrentSchema())
- specJSON, _ := json.Marshal(spec.Fields)
+ cur, _ := json.Marshal(meta.CurrentSchema(), jsonx.V1())
+ specJSON, _ := json.Marshal(spec.Fields, jsonx.V1())
contentName := "data"
if content != ContentData {
diff --git a/pkgs/iceberg/metadata.go b/pkgs/iceberg/metadata.go
index b977bf3ee4..8f0c3c74b8 100644
--- a/pkgs/iceberg/metadata.go
+++ b/pkgs/iceberg/metadata.go
@@ -3,7 +3,8 @@
package iceberg
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -12,6 +13,8 @@ import (
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -35,11 +38,11 @@ var (
// Field is one schema field; Type is a primitive name or a nested type object.
type Field struct {
- Name string `json:"name"`
- Doc string `json:"doc,omitempty"`
- Type json.RawMessage `json:"type"`
- ID int `json:"id"`
- Required bool `json:"required"`
+ Name string `json:"name"`
+ Doc string `json:"doc,omitempty"`
+ Type jsontext.Value `json:"type"`
+ ID int `json:"id"`
+ Required bool `json:"required"`
}
// Schema is an Iceberg struct schema.
@@ -98,12 +101,12 @@ type Ref struct {
type TableMetadata struct {
Properties map[string]string `json:"properties"`
Refs map[string]Ref `json:"refs,omitempty"`
- extra map[string]json.RawMessage
+ extra map[string]jsontext.Value
TableUUID string `json:"table-uuid"`
Location string `json:"location"`
Schemas []Schema `json:"schemas"`
PartitionSpecs []PartitionSpec `json:"partition-specs"`
- SortOrders []json.RawMessage `json:"sort-orders"`
+ SortOrders []jsontext.Value `json:"sort-orders"`
Snapshots []Snapshot `json:"snapshots"`
SnapshotLog []SnapshotLogEntry `json:"snapshot-log"`
MetadataLog []MetadataLogEntry `json:"metadata-log"`
@@ -140,11 +143,11 @@ func (m *TableMetadata) UnmarshalJSON(b []byte) error {
a.CurrentSnapshotID = NoSnapshot
- if err := json.Unmarshal(b, &a); err != nil {
+ if err := json.Unmarshal(b, &a, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", ErrInvalidMetadata, err)
}
- if err := json.Unmarshal(b, &v1); err != nil {
+ if err := json.Unmarshal(b, &v1, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", ErrInvalidMetadata, err)
}
@@ -157,8 +160,8 @@ func (m *TableMetadata) UnmarshalJSON(b []byte) error {
a.PartitionSpecs = []PartitionSpec{{SpecID: 0, Fields: v1.PartitionSpecV1}}
}
- raw := map[string]json.RawMessage{}
- _ = json.Unmarshal(b, &raw)
+ raw := map[string]jsontext.Value{}
+ _ = json.Unmarshal(b, &raw, jsonx.V1())
for _, k := range modeledKeys() {
delete(raw, k)
@@ -174,28 +177,28 @@ func (m *TableMetadata) UnmarshalJSON(b []byte) error {
func (m *TableMetadata) MarshalJSON() ([]byte, error) {
out := maps.Clone(m.extra)
if out == nil {
- out = map[string]json.RawMessage{}
+ out = map[string]jsontext.Value{}
}
- typed, err := json.Marshal(metadataAlias(*m))
+ typed, err := json.Marshal(metadataAlias(*m), jsonx.V1())
if err != nil {
return nil, err
}
- var fields map[string]json.RawMessage
- if err = json.Unmarshal(typed, &fields); err != nil {
+ var fields map[string]jsontext.Value
+ if err = json.Unmarshal(typed, &fields, jsonx.V1()); err != nil {
return nil, err
}
maps.Copy(out, fields)
- return json.Marshal(out)
+ return json.Marshal(out, jsonx.V1())
}
// ParseMetadata decodes and validates a metadata.json document.
func ParseMetadata(data []byte) (*TableMetadata, error) {
var m TableMetadata
- if err := json.Unmarshal(data, &m); err != nil {
+ if err := json.Unmarshal(data, &m, jsonx.V1()); err != nil {
return nil, err
}
@@ -310,7 +313,7 @@ type NewParams struct {
Now time.Time
Properties map[string]string
Location string
- SortOrder json.RawMessage
+ SortOrder jsontext.Value
Schema Schema
Spec PartitionSpec
}
@@ -333,7 +336,7 @@ func NewTableMetadata(p NewParams) *TableMetadata {
order := p.SortOrder
if order == nil {
- order = json.RawMessage(`{"order-id":0,"fields":[]}`)
+ order = jsontext.Value(`{"order-id":0,"fields":[]}`)
}
props := map[string]string{}
@@ -349,7 +352,7 @@ func NewTableMetadata(p NewParams) *TableMetadata {
DefaultSpecID: p.Spec.SpecID,
PartitionSpecs: []PartitionSpec{p.Spec},
LastPartitionID: lastPart,
- SortOrders: []json.RawMessage{order},
+ SortOrders: []jsontext.Value{order},
Properties: props,
CurrentSnapshotID: NoSnapshot,
Snapshots: []Snapshot{},
@@ -359,12 +362,12 @@ func NewTableMetadata(p NewParams) *TableMetadata {
}
}
-func sortOrderID(order json.RawMessage) int {
+func sortOrderID(order jsontext.Value) int {
var o struct {
ID int `json:"order-id"`
}
- _ = json.Unmarshal(order, &o)
+ _ = json.Unmarshal(order, &o, jsonx.V1())
return o.ID
}
diff --git a/pkgs/iceberg/types.go b/pkgs/iceberg/types.go
index 0cb5bdded9..8d5565a26a 100644
--- a/pkgs/iceberg/types.go
+++ b/pkgs/iceberg/types.go
@@ -1,39 +1,42 @@
package iceberg
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrUnsupportedType is returned for a type that cannot be converted or written.
var ErrUnsupportedType = errors.New("unsupported iceberg type")
type nestedType struct {
- Element json.RawMessage `json:"element"`
- Key json.RawMessage `json:"key"`
- Value json.RawMessage `json:"value"`
- Type string `json:"type"`
- Fields []Field `json:"fields"`
- ElementID int `json:"element-id"`
- KeyID int `json:"key-id"`
- ValueID int `json:"value-id"`
- ElementNeeded bool `json:"element-required"`
- ValueNeeded bool `json:"value-required"`
+ Element jsontext.Value `json:"element"`
+ Key jsontext.Value `json:"key"`
+ Value jsontext.Value `json:"value"`
+ Type string `json:"type"`
+ Fields []Field `json:"fields"`
+ ElementID int `json:"element-id"`
+ KeyID int `json:"key-id"`
+ ValueID int `json:"value-id"`
+ ElementNeeded bool `json:"element-required"`
+ ValueNeeded bool `json:"value-required"`
}
// HiveType renders an Iceberg type as the Athena/Hive type name used in the
// Glue catalog (long -> bigint, list -> array<..>, struct -> struct).
-func HiveType(raw json.RawMessage) (string, error) {
+func HiveType(raw jsontext.Value) (string, error) {
var prim string
- if err := json.Unmarshal(raw, &prim); err == nil {
+ if err := json.Unmarshal(raw, &prim, jsonx.V1()); err == nil {
return hivePrimitive(prim)
}
var n nestedType
- if err := json.Unmarshal(raw, &n); err != nil {
+ if err := json.Unmarshal(raw, &n, jsonx.V1()); err != nil {
return "", fmt.Errorf("%w: %s", ErrUnsupportedType, raw)
}
@@ -105,9 +108,9 @@ func hivePrimitive(p string) (string, error) {
}
// PrimitiveName returns the primitive type name of a field type ("" for nested types).
-func PrimitiveName(raw json.RawMessage) string {
+func PrimitiveName(raw jsontext.Value) string {
var prim string
- if err := json.Unmarshal(raw, &prim); err != nil {
+ if err := json.Unmarshal(raw, &prim, jsonx.V1()); err != nil {
return ""
}
@@ -176,7 +179,7 @@ func SchemaFromColumns(cols []ColumnSpec) (Schema, error) {
return Schema{}, err
}
- raw, _ := json.Marshal(t)
+ raw, _ := json.Marshal(t, jsonx.V1())
fields = append(fields, Field{ID: i + 1, Name: c.Name, Type: raw, Doc: c.Comment})
}
diff --git a/pkgs/idempotency/idempotency.go b/pkgs/idempotency/idempotency.go
index 97cc663498..9d755b4792 100644
--- a/pkgs/idempotency/idempotency.go
+++ b/pkgs/idempotency/idempotency.go
@@ -3,11 +3,12 @@
package idempotency
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
)
@@ -64,7 +65,7 @@ func New(name string, opts ...Option) *Memo {
// Fingerprint renders request parameters for comparison across retries.
func Fingerprint(v any) string {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Sprintf("%+v", v)
}
diff --git a/pkgs/jsonx/jsonx.go b/pkgs/jsonx/jsonx.go
new file mode 100644
index 0000000000..b8fbc13ca0
--- /dev/null
+++ b/pkgs/jsonx/jsonx.go
@@ -0,0 +1,222 @@
+// Package jsonx holds the shared option sets for encoding/json/v2 and the few
+// helpers that have no 1:1 v2 equivalent.
+//
+// Every call site uses encoding/json/v2 and encoding/json/jsontext directly;
+// V1 is passed so a mechanical swap from encoding/json keeps byte-identical
+// wire output and error text (nil slices and maps as null, HTML escaping,
+// case-insensitive member matching, tolerated duplicate names and invalid
+// UTF-8, v1 error messages). Drop V1 per call only where the v2 default is
+// wanted and verified.
+//
+// This is the only package allowed to import the v1 encoding/json package:
+// DefaultOptionsV1 and the legacy-semantics options are exported from there.
+//
+// v1 to v2 mapping:
+//
+// json.Marshal(v) json.Marshal(v, jsonx.V1())
+// json.Unmarshal(b, &v) json.Unmarshal(b, &v, jsonx.V1())
+// json.MarshalIndent(v, p, i) json.Marshal(v, jsonx.V1(), jsontext.Multiline(true),
+// jsontext.WithIndentPrefix(p), jsontext.WithIndent(i))
+// json.NewDecoder(r).Decode(&v) json.UnmarshalDecode(jsontext.NewDecoder(r), &v, jsonx.V1())
+// json.NewEncoder(w).Encode(v) json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), v)
+// enc.SetIndent / SetEscapeHTML jsontext.Multiline, WithIndent, EscapeForHTML on NewEncoder
+// dec.DisallowUnknownFields() json.RejectUnknownMembers(true)
+// dec.UseNumber(), json.Number jsonx.UseNumber(), jsonx.Number
+// json.RawMessage jsontext.Value
+// dec.Token() dec.ReadToken() (Kind: '{' '[' '"' '0' 't' 'f' 'n')
+// dec.More() inside a container dec.PeekKind() not '}' or ']'; at top level
+// compare InputOffset against the input (PeekKind()!=0 accepts garbage)
+// dec.Buffered() bytes.NewReader(dec.UnreadBuffer())
+// json.Delim jsontext.Kind
+// json.Valid(b) jsontext.Value(b).IsValid(jsonx.V1())
+// json.Compact / Indent jsontext.Value.Compact / Indent
+// json.Marshaler, Unmarshaler same names in v2 (MarshalJSON / UnmarshalJSON still honored)
+// *json.SyntaxError jsonx.SyntaxError / IsSyntaxError
+// *json.UnmarshalTypeError jsonx.TypeError / IsTypeError
+//
+// Error types: with V1 the v2 engine returns the v1 types (*json.SyntaxError,
+// *json.UnmarshalTypeError), not jsontext.SyntacticError or json.SemanticError,
+// so errors.As on the v2 types silently misses and turns client errors into
+// 500s. Always classify with SyntaxError, IsSyntaxError, TypeError and
+// IsTypeError, which accept both families.
+//
+// A jsontext.Token is invalidated by the next decoder call; copy String()
+// before reading on. Whitespace options (Multiline, WithIndent) belong on
+// jsontext.NewEncoder, not on MarshalEncode. SDKDecoder returns a v1 Decoder
+// for SDK helpers that require one.
+//
+// Use UnmarshalRead instead of UnmarshalDecode only when trailing data after
+// the first value must be an error; v1 Decoder.Decode ignored it.
+package jsonx
+
+import (
+ jsonv1 "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
+ "errors"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+)
+
+var (
+ errInvalidNumber = errors.New("json: invalid number literal")
+ errNotNumber = errors.New("json: cannot unmarshal into jsonx.Number")
+)
+
+// V1 is the full option set that reproduces encoding/json v1 behavior.
+func V1() json.Options {
+ return jsonv1.DefaultOptionsV1()
+}
+
+// SyntaxError reports whether err is a JSON syntax error and its byte offset.
+// With V1 options the v2 engine returns the v1 error types, so both are accepted.
+func SyntaxError(err error) (int64, bool) {
+ if se, ok := errors.AsType[*jsonv1.SyntaxError](err); ok {
+ return se.Offset, true
+ }
+
+ if se, ok := errors.AsType[*jsontext.SyntacticError](err); ok {
+ return se.ByteOffset, true
+ }
+
+ return 0, false
+}
+
+// IsSyntaxError reports whether err is a JSON syntax error.
+func IsSyntaxError(err error) bool {
+ _, ok := SyntaxError(err)
+
+ return ok
+}
+
+// TypeError reports whether err is a JSON value/type mismatch and the dotted
+// path of the offending member (the v1 UnmarshalTypeError.Field form).
+func TypeError(err error) (string, bool) {
+ if te, ok := errors.AsType[*jsonv1.UnmarshalTypeError](err); ok {
+ return te.Field, true
+ }
+
+ if se, ok := errors.AsType[*json.SemanticError](err); ok {
+ return strings.ReplaceAll(strings.TrimPrefix(string(se.JSONPointer), "/"), "/", "."), true
+ }
+
+ return "", false
+}
+
+// IsTypeError reports whether err is a JSON value/type mismatch.
+func IsTypeError(err error) bool {
+ _, ok := TypeError(err)
+
+ return ok
+}
+
+// SDKDecoder returns a v1 decoder for AWS SDK helpers that still take
+// *encoding/json.Decoder (e.g. restjson.GetErrorInfo).
+func SDKDecoder(r io.Reader) *jsonv1.Decoder {
+ return jsonv1.NewDecoder(r)
+}
+
+// Number is a JSON number kept in its textual form, the v2 stand-in for
+// v1 json.Number. It is produced for untyped numbers when UseNumber is set.
+type Number string
+
+// String returns the literal text of the number.
+func (n Number) String() string { return string(n) }
+
+// Float64 returns the number as a float64.
+func (n Number) Float64() (float64, error) { return strconv.ParseFloat(string(n), 64) }
+
+// Int64 returns the number as an int64.
+func (n Number) Int64() (int64, error) { return strconv.ParseInt(string(n), 10, 64) }
+
+// Int returns the number as an int, failing when it is out of int range.
+func (n Number) Int() (int, error) { return strconv.Atoi(string(n)) }
+
+// MarshalJSON emits the number literal; an empty Number is 0 like v1.
+func (n Number) MarshalJSON() ([]byte, error) {
+ if n == "" {
+ return []byte("0"), nil
+ }
+
+ if !jsontext.Value(n).IsValid() || jsontext.Value(n).Kind() != '0' {
+ return nil, fmt.Errorf("%w %q", errInvalidNumber, string(n))
+ }
+
+ return []byte(n), nil
+}
+
+// UnmarshalJSON accepts a JSON number, or a string holding a valid number as v1 did.
+func (n *Number) UnmarshalJSON(b []byte) error {
+ v := jsontext.Value(b)
+
+ switch v.Kind() {
+ case '0':
+ *n = Number(b)
+ case '"':
+ s, err := jsontext.AppendUnquote(nil, b)
+ if err != nil {
+ return err
+ }
+
+ if lit := jsontext.Value(s); !lit.IsValid() || lit.Kind() != '0' {
+ return fmt.Errorf("%w %q", errInvalidNumber, string(s))
+ }
+
+ *n = Number(s)
+ case 'n':
+ default:
+ return fmt.Errorf("%w: %s", errNotNumber, v.Kind())
+ }
+
+ return nil
+}
+
+// UseNumber makes every untyped (any) JSON number decode as Number instead of
+// float64, replacing v1 Decoder.UseNumber.
+func UseNumber() json.Options {
+ return json.WithUnmarshalers(json.UnmarshalFromFunc(unmarshalAnyNumber))
+}
+
+func unmarshalAnyNumber(dec *jsontext.Decoder, v *any) error {
+ switch dec.PeekKind() {
+ case '0':
+ val, err := dec.ReadValue()
+ if err != nil {
+ return err
+ }
+
+ *v = Number(val)
+ case '{':
+ var m map[string]any
+ if err := json.UnmarshalDecode(dec, &m, V1(), UseNumber()); err != nil {
+ return err
+ }
+
+ *v = m
+ case '[':
+ var s []any
+ if err := json.UnmarshalDecode(dec, &s, V1(), UseNumber()); err != nil {
+ return err
+ }
+
+ *v = s
+ default:
+ tok, err := dec.ReadToken()
+ if err != nil {
+ return err
+ }
+
+ switch tok.Kind() {
+ case 'n':
+ *v = nil
+ case 't', 'f':
+ *v = tok.Bool()
+ default:
+ *v = tok.String()
+ }
+ }
+
+ return nil
+}
diff --git a/pkgs/jsonx/jsonx_test.go b/pkgs/jsonx/jsonx_test.go
new file mode 100644
index 0000000000..8393123ace
--- /dev/null
+++ b/pkgs/jsonx/jsonx_test.go
@@ -0,0 +1,192 @@
+package jsonx_test
+
+import (
+ "encoding/json/jsontext"
+ "encoding/json/v2"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
+
+func TestV1Semantics(t *testing.T) {
+ t.Parallel()
+
+ type in struct {
+ H string `json:"h"`
+ M map[string]int `json:"m"`
+ S []int `json:"s"`
+ }
+
+ b, err := json.Marshal(in{H: "<&>"}, jsonx.V1())
+ require.NoError(t, err)
+ assert.JSONEq(t, `{"s":null,"m":null,"h":"<&>"}`, string(b))
+ assert.Contains(t, string(b), "\\u003c")
+
+ var out in
+
+ require.NoError(t, json.Unmarshal([]byte(`{"S":[1],"H":"x","dup":1,"dup":2}`), &out, jsonx.V1()))
+ assert.Equal(t, []int{1}, out.S)
+ assert.Equal(t, "x", out.H)
+
+ err = json.Unmarshal([]byte(`{"s":`), &out, jsonx.V1())
+ require.EqualError(t, err, "unexpected end of JSON input")
+}
+
+func TestUseNumber(t *testing.T) {
+ t.Parallel()
+
+ var got any
+
+ doc := `{"a":1.50,"b":[2,"x",true,null,{"c":3}],"d":"s"}`
+ err := json.Unmarshal([]byte(doc), &got, jsonx.V1(), jsonx.UseNumber())
+ require.NoError(t, err)
+
+ m, ok := got.(map[string]any)
+ require.True(t, ok)
+ assert.Equal(t, jsonx.Number("1.50"), m["a"])
+ assert.Equal(t, "s", m["d"])
+ assert.Equal(t, []any{jsonx.Number("2"), "x", true, nil, map[string]any{"c": jsonx.Number("3")}}, m["b"])
+
+ var typed struct{ N int }
+
+ require.NoError(t, json.Unmarshal([]byte(`{"N":7}`), &typed, jsonx.V1(), jsonx.UseNumber()))
+ assert.Equal(t, 7, typed.N)
+}
+
+func TestNumber(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ in string
+ want string
+ wantErr bool
+ }{
+ {name: "number", in: `12.5`, want: "12.5"},
+ {name: "quoted number", in: `"7"`, want: "7"},
+ {name: "quoted junk", in: `"x"`, wantErr: true},
+ {name: "bool", in: `true`, wantErr: true},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ var n jsonx.Number
+
+ err := json.Unmarshal([]byte(tt.in), &n, jsonx.V1())
+ if tt.wantErr {
+ require.Error(t, err)
+
+ return
+ }
+
+ require.NoError(t, err)
+ assert.Equal(t, tt.want, n.String())
+
+ out, err := json.Marshal(n, jsonx.V1())
+ require.NoError(t, err)
+ assert.Equal(t, tt.want, string(out))
+ })
+ }
+
+ out, err := json.Marshal(jsonx.Number(""), jsonx.V1())
+ require.NoError(t, err)
+ assert.Equal(t, "0", string(out))
+}
+
+func TestDecoderStreamTolerantOfTrailingData(t *testing.T) {
+ t.Parallel()
+
+ var v map[string]int
+
+ dec := jsontext.NewDecoder(strings.NewReader(`{"a":1} trailing`))
+ require.NoError(t, json.UnmarshalDecode(dec, &v, jsonx.V1()))
+ assert.Equal(t, map[string]int{"a": 1}, v)
+}
+
+func TestErrorClassifiers(t *testing.T) {
+ t.Parallel()
+
+ type doc struct {
+ A struct{ B int } `json:"a"`
+ }
+
+ tests := []struct {
+ name string
+ body string
+ field string
+ wantSyn bool
+ wantType bool
+ wantNoErr bool
+ }{
+ {name: "garbage", body: `not json`, wantSyn: true},
+ {name: "truncated", body: `{"a":`, wantSyn: true},
+ {name: "type_mismatch", body: `{"a":{"B":"x"}}`, wantType: true, field: "a.B"},
+ {name: "ok", body: `{"a":{"B":1}}`, wantNoErr: true},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ var d doc
+
+ err := json.Unmarshal([]byte(tt.body), &d, jsonx.V1())
+ if tt.wantNoErr {
+ require.NoError(t, err)
+
+ return
+ }
+
+ require.Error(t, err)
+ assert.Equal(t, tt.wantSyn, jsonx.IsSyntaxError(err))
+ assert.Equal(t, tt.wantType, jsonx.IsTypeError(err))
+
+ if tt.wantType {
+ field, _ := jsonx.TypeError(err)
+ assert.Equal(t, tt.field, field)
+ }
+ })
+ }
+
+ _, ok := jsonx.SyntaxError(json.Unmarshal([]byte(`{"a":1}x`), new(doc), jsonx.V1()))
+ assert.True(t, ok)
+}
+
+func TestNumberInt(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ in jsonx.Number
+ want int
+ wantErr bool
+ }{
+ {name: "positive", in: "42", want: 42},
+ {name: "negative", in: "-7", want: -7},
+ {name: "fraction", in: "1.5", wantErr: true},
+ {name: "overflow", in: "99999999999999999999", wantErr: true},
+ {name: "empty", in: "", wantErr: true},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ got, err := tt.in.Int()
+ if tt.wantErr {
+ require.Error(t, err)
+
+ return
+ }
+
+ require.NoError(t, err)
+ assert.Equal(t, tt.want, got)
+ })
+ }
+}
diff --git a/pkgs/jsruntime/convert.go b/pkgs/jsruntime/convert.go
index 0fb0f7be5c..1ffb658642 100644
--- a/pkgs/jsruntime/convert.go
+++ b/pkgs/jsruntime/convert.go
@@ -1,11 +1,13 @@
package jsruntime
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"github.com/dop251/goja"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrNotJSON means a value could not be converted to or from JSON.
@@ -13,7 +15,7 @@ var ErrNotJSON = errors.New("value is not JSON-serializable")
// ToJS builds a fresh JS value from plain Go data via JSON.parse.
func (s *Sandbox) ToJS(v any) (goja.Value, error) {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("%w: %w", ErrNotJSON, err)
}
@@ -56,7 +58,7 @@ func (s *Sandbox) FromJS(v goja.Value) (any, error) {
}
var out any
- if err = json.Unmarshal([]byte(str), &out); err != nil {
+ if err = json.Unmarshal([]byte(str), &out, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", ErrNotJSON, err)
}
diff --git a/pkgs/ociregistry/client.go b/pkgs/ociregistry/client.go
index 1acc9e8f7f..c2a15a2d2f 100644
--- a/pkgs/ociregistry/client.go
+++ b/pkgs/ociregistry/client.go
@@ -3,13 +3,15 @@ package ociregistry
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrNotFound is returned when a repository, manifest or blob does not exist.
@@ -140,7 +142,7 @@ func (c Client) pages(ctx context.Context, base string, pick func(listResponse)
}
var lr listResponse
- if err = json.Unmarshal(rec.body.Bytes(), &lr); err != nil {
+ if err = json.Unmarshal(rec.body.Bytes(), &lr, jsonx.V1()); err != nil {
return nil, fmt.Errorf("ociregistry: decode list: %w", err)
}
diff --git a/pkgs/ociregistry/describe.go b/pkgs/ociregistry/describe.go
index 34ef8ad382..4cfaeae79c 100644
--- a/pkgs/ociregistry/describe.go
+++ b/pkgs/ociregistry/describe.go
@@ -2,8 +2,10 @@ package ociregistry
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const maxConfigBytes = 1 << 20
@@ -44,7 +46,7 @@ func (c Client) Describe(ctx context.Context, repo string, m Manifest) Info {
info := Info{Digest: m.Digest, MediaType: m.MediaType}
var doc manifestDoc
- if json.Unmarshal(m.Body, &doc) != nil {
+ if json.Unmarshal(m.Body, &doc, jsonx.V1()) != nil {
return info
}
@@ -85,7 +87,7 @@ func (c Client) platform(ctx context.Context, repo, configDigest string) (string
OS string `json:"os"`
}
- if json.Unmarshal(raw, &cfg) != nil {
+ if json.Unmarshal(raw, &cfg, jsonx.V1()) != nil {
return "", ""
}
diff --git a/pkgs/ociregistry/ocitest/ocitest.go b/pkgs/ociregistry/ocitest/ocitest.go
index 48bdc3f699..e0a7df4eb1 100644
--- a/pkgs/ociregistry/ocitest/ocitest.go
+++ b/pkgs/ociregistry/ocitest/ocitest.go
@@ -6,7 +6,8 @@ import (
"bytes"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -20,6 +21,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -174,7 +176,7 @@ func (c Client) PushImage(repo, tag string) Image {
cfg, err := json.Marshal(map[string]any{
"architecture": "amd64", "os": "linux",
"rootfs": map[string]any{"type": "layers", "diff_ids": []string{layerDigest}},
- })
+ }, jsonx.V1())
require.NoError(c.T, err)
cfgDigest := c.PushBlob(repo, cfg, configChunks)
@@ -188,7 +190,7 @@ func (c Client) PushImage(repo, tag string) Image {
keyMediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", "size": len(layer),
"digest": layerDigest,
}},
- })
+ }, jsonx.V1())
require.NoError(c.T, err)
put := c.Do(
@@ -215,11 +217,11 @@ func (c Client) Names(path, field string, n int) []string {
resp := c.Do(http.MethodGet, next, nil, nil)
require.Equal(c.T, http.StatusOK, resp.Status, string(resp.Body))
- var doc map[string]json.RawMessage
- require.NoError(c.T, json.Unmarshal(resp.Body, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(c.T, json.Unmarshal(resp.Body, &doc, jsonx.V1()))
var page []string
- require.NoError(c.T, json.Unmarshal(doc[field], &page))
+ require.NoError(c.T, json.Unmarshal(doc[field], &page, jsonx.V1()))
all = append(all, page...)
diff --git a/pkgs/odatatable/batchwire.go b/pkgs/odatatable/batchwire.go
index 267c8e62a0..d0be53c09c 100644
--- a/pkgs/odatatable/batchwire.go
+++ b/pkgs/odatatable/batchwire.go
@@ -2,7 +2,7 @@ package odatatable
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -12,6 +12,8 @@ import (
"net/url"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -456,5 +458,5 @@ func FailureBody(index int, f BatchFailure, requestID, timestamp string) ([]byte
env.Error.Message.Lang = "en-US"
env.Error.Message.Value = fmt.Sprintf("%d:%s\nRequestId:%s\nTime:%s", index, f.Message, requestID, timestamp)
- return json.Marshal(env)
+ return json.Marshal(env, jsonx.V1())
}
diff --git a/pkgs/odatatable/codec.go b/pkgs/odatatable/codec.go
index c5bd574d8d..93f5c15b67 100644
--- a/pkgs/odatatable/codec.go
+++ b/pkgs/odatatable/codec.go
@@ -2,12 +2,15 @@ package odatatable
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"math"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// OData metadata level names, negotiated by each caller from its own
@@ -66,9 +69,9 @@ func ParseTop(raw string) (int, error) {
// supplied value never overwrites it -- the server always wins). Any
// "odata.*"/"@odata.type" metadata key is skipped.
func DecodeEntityBody(body []byte) (string, string, bool, bool, map[string]EntityProperty, error) {
- var raw map[string]json.RawMessage
+ var raw map[string]jsontext.Value
- if err := json.Unmarshal(body, &raw); err != nil {
+ if err := json.Unmarshal(body, &raw, jsonx.V1()); err != nil {
return "", "", false, false, nil, fmt.Errorf("%w: %w", ErrInvalidEntityProperty, err)
}
@@ -92,14 +95,14 @@ func DecodeEntityBody(body []byte) (string, string, bool, bool, map[string]Entit
// decodeSystemKeyProperty extracts the string-valued system property name
// (PartitionKey or RowKey) from raw, if present.
-func decodeSystemKeyProperty(raw map[string]json.RawMessage, name string) (string, bool, error) {
+func decodeSystemKeyProperty(raw map[string]jsontext.Value, name string) (string, bool, error) {
rawVal, ok := raw[name]
if !ok {
return "", false, nil
}
var s string
- if err := json.Unmarshal(rawVal, &s); err != nil {
+ if err := json.Unmarshal(rawVal, &s, jsonx.V1()); err != nil {
return "", false, fmt.Errorf("%w: %s must be a string", ErrInvalidEntityProperty, name)
}
@@ -125,7 +128,7 @@ func isSystemOrMetadataKey(key string) bool {
// decodeCustomProperties decodes every user-defined (non-system,
// non-metadata) property in raw into its typed EntityProperty.
-func decodeCustomProperties(raw map[string]json.RawMessage) (map[string]EntityProperty, error) {
+func decodeCustomProperties(raw map[string]jsontext.Value) (map[string]EntityProperty, error) {
props := make(map[string]EntityProperty, len(raw))
for key, rawVal := range raw {
@@ -136,7 +139,7 @@ func decodeCustomProperties(raw map[string]json.RawMessage) (map[string]EntityPr
edmType := ""
if annRaw, ok := raw[key+"@odata.type"]; ok {
- if err := json.Unmarshal(annRaw, &edmType); err != nil {
+ if err := json.Unmarshal(annRaw, &edmType, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: malformed %s@odata.type", ErrInvalidEntityProperty, key)
}
}
@@ -154,7 +157,7 @@ func decodeCustomProperties(raw map[string]json.RawMessage) (map[string]EntityPr
// decodeProperty decodes raw into a typed EntityProperty per its (possibly
// empty) "@odata.type" annotation value edmType.
-func decodeProperty(edmType string, raw json.RawMessage) (EntityProperty, error) {
+func decodeProperty(edmType string, raw jsontext.Value) (EntityProperty, error) {
switch EdmType(edmType) {
case EdmString, EdmInt32, EdmDouble, EdmBoolean:
return decodeScalarProperty(EdmType(edmType), raw)
@@ -169,32 +172,32 @@ func decodeProperty(edmType string, raw json.RawMessage) (EntityProperty, error)
// decodeScalarProperty decodes the four EDM types whose wire representation
// is a bare, natively-typed JSON value (string/number/number/bool).
-func decodeScalarProperty(edmType EdmType, raw json.RawMessage) (EntityProperty, error) {
+func decodeScalarProperty(edmType EdmType, raw jsontext.Value) (EntityProperty, error) {
switch edmType {
case EdmString:
var s string
- if err := json.Unmarshal(raw, &s); err != nil {
+ if err := json.Unmarshal(raw, &s, jsonx.V1()); err != nil {
return EntityProperty{}, fmt.Errorf("%w: not a string", ErrInvalidEntityProperty)
}
return EntityProperty{Type: EdmString, Value: s}, nil
case EdmInt32:
var n int32
- if err := json.Unmarshal(raw, &n); err != nil {
+ if err := json.Unmarshal(raw, &n, jsonx.V1()); err != nil {
return EntityProperty{}, fmt.Errorf("%w: not an Int32", ErrInvalidEntityProperty)
}
return EntityProperty{Type: EdmInt32, Value: n}, nil
case EdmDouble:
var f float64
- if err := json.Unmarshal(raw, &f); err != nil {
+ if err := json.Unmarshal(raw, &f, jsonx.V1()); err != nil {
return EntityProperty{}, fmt.Errorf("%w: not a Double", ErrInvalidEntityProperty)
}
return EntityProperty{Type: EdmDouble, Value: f}, nil
case EdmBoolean:
var b bool
- if err := json.Unmarshal(raw, &b); err != nil {
+ if err := json.Unmarshal(raw, &b, jsonx.V1()); err != nil {
return EntityProperty{}, fmt.Errorf("%w: not a Boolean", ErrInvalidEntityProperty)
}
@@ -208,9 +211,9 @@ func decodeScalarProperty(edmType EdmType, raw json.RawMessage) (EntityProperty,
// representation is always a JSON string carrying an encoded value (decimal
// digits, an RFC3339 timestamp, a UUID, or base64), requiring the
// "@odata.type" annotation to disambiguate from Edm.String.
-func decodeStringEncodedProperty(edmType EdmType, raw json.RawMessage) (EntityProperty, error) {
+func decodeStringEncodedProperty(edmType EdmType, raw jsontext.Value) (EntityProperty, error) {
var s string
- if err := json.Unmarshal(raw, &s); err != nil {
+ if err := json.Unmarshal(raw, &s, jsonx.V1()); err != nil {
return EntityProperty{}, fmt.Errorf("%w: %s must be a string", ErrInvalidEntityProperty, edmType)
}
@@ -247,24 +250,24 @@ func decodeStringEncodedProperty(edmType EdmType, raw json.RawMessage) (EntityPr
// mirroring aztables' own client-side inference exactly: try Int32 first
// (so a decimal-point-free number becomes Edm.Int32), then fall back to the
// JSON value's natural Go type.
-func decodeUnannotatedProperty(raw json.RawMessage) (EntityProperty, error) {
+func decodeUnannotatedProperty(raw jsontext.Value) (EntityProperty, error) {
var i32 int32
- if err := json.Unmarshal(raw, &i32); err == nil {
+ if err := json.Unmarshal(raw, &i32, jsonx.V1()); err == nil {
return EntityProperty{Type: EdmInt32, Value: i32}, nil
}
var f float64
- if err := json.Unmarshal(raw, &f); err == nil {
+ if err := json.Unmarshal(raw, &f, jsonx.V1()); err == nil {
return EntityProperty{Type: EdmDouble, Value: f}, nil
}
var b bool
- if err := json.Unmarshal(raw, &b); err == nil {
+ if err := json.Unmarshal(raw, &b, jsonx.V1()); err == nil {
return EntityProperty{Type: EdmBoolean, Value: b}, nil
}
var s string
- if err := json.Unmarshal(raw, &s); err == nil {
+ if err := json.Unmarshal(raw, &s, jsonx.V1()); err == nil {
return EntityProperty{Type: EdmString, Value: s}, nil
}
diff --git a/pkgs/odatatable/models.go b/pkgs/odatatable/models.go
index 88040ae50d..99dfea3fd9 100644
--- a/pkgs/odatatable/models.go
+++ b/pkgs/odatatable/models.go
@@ -19,12 +19,14 @@ package odatatable
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"math"
"strconv"
"sync/atomic"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// System entity property names, shared across path-predicate parsing
@@ -106,7 +108,7 @@ func (p EntityProperty) MarshalJSON() ([]byte, error) {
return nil, err
}
- return json.Marshal(entityPropertyWire{Type: p.Type, Value: v})
+ return json.Marshal(entityPropertyWire{Type: p.Type, Value: v}, jsonx.V1())
}
// marshalValue dispatches to marshalScalarValue or marshalStringEncodedValue
@@ -219,7 +221,7 @@ func (p EntityProperty) marshalStringEncodedValue() (any, error) {
func (p *EntityProperty) UnmarshalJSON(data []byte) error {
var wire entityPropertyWire
- if err := json.Unmarshal(data, &wire); err != nil {
+ if err := json.Unmarshal(data, &wire, jsonx.V1()); err != nil {
return err
}
@@ -428,7 +430,7 @@ const entityCompositeKeyElemCount = 2
// encoding can't reintroduce the same collision class the struct key itself
// was introduced to close.
func (k entityCompositeKey) MarshalText() ([]byte, error) {
- return json.Marshal([entityCompositeKeyElemCount]string{k.PartitionKey, k.RowKey})
+ return json.Marshal([entityCompositeKeyElemCount]string{k.PartitionKey, k.RowKey}, jsonx.V1())
}
// UnmarshalText implements encoding.TextUnmarshaler, the inverse of
@@ -442,7 +444,7 @@ func (k *entityCompositeKey) UnmarshalText(text []byte) error {
// two elements".
var pair []string
- if err := json.Unmarshal(text, &pair); err != nil {
+ if err := json.Unmarshal(text, &pair, jsonx.V1()); err != nil {
return fmt.Errorf("odatatable: malformed entity composite key %q: %w", text, err)
}
diff --git a/pkgs/persistence/manager_test.go b/pkgs/persistence/manager_test.go
index 3506b97446..eca03bf1af 100644
--- a/pkgs/persistence/manager_test.go
+++ b/pkgs/persistence/manager_test.go
@@ -2,7 +2,7 @@ package persistence_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"sync"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -821,7 +822,7 @@ func (s *statefulPersistable) Restore(_ context.Context, data []byte) error {
State string `json:"state"`
}
- if err := json.Unmarshal(data, &v); err != nil {
+ if err := json.Unmarshal(data, &v, jsonx.V1()); err != nil {
return err
}
diff --git a/pkgs/persistence/modes_test.go b/pkgs/persistence/modes_test.go
index 9d3c241e8e..4463e18cea 100644
--- a/pkgs/persistence/modes_test.go
+++ b/pkgs/persistence/modes_test.go
@@ -2,7 +2,7 @@ package persistence_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"path/filepath"
"sync"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -40,7 +41,7 @@ func (c *counterSvc) Snapshot(context.Context) []byte {
c.mu.Lock()
defer c.mu.Unlock()
- b, _ := json.Marshal(c.vals)
+ b, _ := json.Marshal(c.vals, jsonx.V1())
return b
}
@@ -49,7 +50,7 @@ func (c *counterSvc) Restore(_ context.Context, data []byte) error {
c.mu.Lock()
defer c.mu.Unlock()
- return json.Unmarshal(data, &c.vals)
+ return json.Unmarshal(data, &c.vals, jsonx.V1())
}
func newMgr(
diff --git a/pkgs/persistence/snapshot.go b/pkgs/persistence/snapshot.go
index aa56ce56c9..968e7be6f9 100644
--- a/pkgs/persistence/snapshot.go
+++ b/pkgs/persistence/snapshot.go
@@ -2,8 +2,9 @@ package persistence
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -15,7 +16,7 @@ import (
// persistence Manager). Centralising this here keeps every backend's Snapshot
// method consistent and gives the ctx parameter a purpose.
func MarshalSnapshot(ctx context.Context, service string, v any) []byte {
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx,
"persistence: snapshot marshal failed",
@@ -37,7 +38,7 @@ func MarshalSnapshot(ctx context.Context, service string, v any) []byte {
// the decode error. Centralising this here keeps every backend's Restore method
// consistent and gives the ctx parameter a purpose.
func UnmarshalSnapshot(ctx context.Context, service string, data []byte, v any) error {
- if err := json.Unmarshal(data, v); err != nil {
+ if err := json.Unmarshal(data, v, jsonx.V1()); err != nil {
logger.Load(ctx).WarnContext(ctx,
"persistence: snapshot unmarshal failed",
"service", service, "error", err)
diff --git a/pkgs/persistence/snapshotversion_guard_test.go b/pkgs/persistence/snapshotversion_guard_test.go
index 16adc151e8..7997b7e934 100644
--- a/pkgs/persistence/snapshotversion_guard_test.go
+++ b/pkgs/persistence/snapshotversion_guard_test.go
@@ -2,7 +2,8 @@ package persistence_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"flag"
"fmt"
@@ -20,6 +21,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errNoVersionStruct = errors.New("no struct with an int-typed Version field")
@@ -480,7 +483,7 @@ func loadGolden(t *testing.T) map[string]snapshotInfo {
var golden map[string]snapshotInfo
- require.NoError(t, json.Unmarshal(data, &golden))
+ require.NoError(t, json.Unmarshal(data, &golden, jsonx.V1()))
return golden
}
@@ -488,7 +491,7 @@ func loadGolden(t *testing.T) map[string]snapshotInfo {
func writeGolden(t *testing.T, live map[string]snapshotInfo) {
t.Helper()
- data, err := json.MarshalIndent(live, "", " ")
+ data, err := json.Marshal(live, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
require.NoError(t, err)
require.NoError(t, os.WriteFile(goldenFile, append(data, '\n'), 0o644))
diff --git a/pkgs/persistence/store_test.go b/pkgs/persistence/store_test.go
index 0c3a091fc4..f3361ea76f 100644
--- a/pkgs/persistence/store_test.go
+++ b/pkgs/persistence/store_test.go
@@ -1,7 +1,7 @@
package persistence_test
import (
- "encoding/json"
+ "encoding/json/v2"
"os"
"path/filepath"
"sync"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -220,7 +221,7 @@ func TestFileStore_ConcurrentAccess(t *testing.T) {
go func(i int) {
defer wg.Done()
- data, _ := json.Marshal(map[string]int{"i": i})
+ data, _ := json.Marshal(map[string]int{"i": i}, jsonx.V1())
if err := s.Save("concurrent", "snap", data); err != nil {
errs <- err
}
@@ -239,7 +240,7 @@ func TestFileStore_ConcurrentAccess(t *testing.T) {
require.NoError(t, err)
var out map[string]int
- require.NoError(t, json.Unmarshal(data, &out))
+ require.NoError(t, json.Unmarshal(data, &out, jsonx.V1()))
assert.Contains(t, out, "i")
}
diff --git a/pkgs/persistence/testdata/snapshot_inventory.json b/pkgs/persistence/testdata/snapshot_inventory.json
index 298ca89f5d..6ed03a42f5 100644
--- a/pkgs/persistence/testdata/snapshot_inventory.json
+++ b/pkgs/persistence/testdata/snapshot_inventory.json
@@ -2,7 +2,7 @@
"accessanalyzer": {
"fields": [
"AccessPreview.AnalyzerArn string",
- "AccessPreview.Configurations map[string]json.RawMessage",
+ "AccessPreview.Configurations map[string]jsontext.Value",
"AccessPreview.CreatedAt time.Time",
"AccessPreview.ID string",
"AccessPreview.Status AccessPreviewStatus",
@@ -14,7 +14,7 @@
"AnalyzedResource.ResourceType string",
"AnalyzedResource.UpdatedAt time.Time",
"Analyzer.Arn string `json:\"arn\"`",
- "Analyzer.Configuration json.RawMessage `json:\"configuration,omitempty\"`",
+ "Analyzer.Configuration jsontext.Value `json:\"configuration,omitempty\"`",
"Analyzer.CreatedAt time.Time `json:\"createdAt\"`",
"Analyzer.LastResourceAnalyzed string `json:\"lastResourceAnalyzed,omitempty\"`",
"Analyzer.LastResourceAnalyzedAt *time.Time `json:\"lastResourceAnalyzedAt,omitempty\"`",
@@ -69,7 +69,7 @@
"archiveRuleSnapshot.UpdatedAt time.Time `json:\"updatedAt\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`"
],
"version": 1
@@ -108,7 +108,7 @@
"backendSnapshot.PrimaryEmailUpdateStatus PrimaryEmailUpdateStatus `json:\"primaryEmailUpdateStatus,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.Regions []*Region `json:\"regions\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 2
},
@@ -228,7 +228,7 @@
"backendSnapshot.EndpointIdempotency map[string]map[string]acmeIdempotencyEntry `json:\"endpointIdempotency,omitempty\"`",
"backendSnapshot.IdempotencyMap map[string]map[string]certIdempotencyEntry `json:\"idempotencyMap,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"certIdempotencyEntry.ARN string `json:\"arn\"`",
"certIdempotencyEntry.CreatedAt time.Time `json:\"createdAt\"`",
"certTagsEntry.ResourceID string",
@@ -361,7 +361,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Policies map[string]map[string]string `json:\"policies\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"caDTO.ARN string `json:\"arn\"`",
"caDTO.PrivKeyPEM string `json:\"privKeyPEM,omitempty\"`",
"caDTO.Region string `json:\"region\"`",
@@ -479,7 +479,7 @@
"WorkspaceConfig.RuleQueryOffsetSet bool `json:\"ruleQueryOffsetSet,omitempty\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -613,7 +613,7 @@
"Webhook.WebhookURL string `json:\"webhookUrl\"`",
"backendSnapshot.AccountID string `json:\"accountId\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -871,7 +871,7 @@
"authorizerSnapshot.RestAPIID string `json:\"restApiId\"`",
"authorizerSnapshot.Type string `json:\"type\"`",
"backendSnapshot.Account *Account `json:\"account,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.UsageOverrides map[string]map[string]int64 `json:\"usageOverrides,omitempty\"`",
"deploymentSnapshot.APISummary map[string]map[string]MethodSnapshot `json:\"apiSummary,omitempty\"`",
"deploymentSnapshot.Config *DeploymentConfig `json:\"config,omitempty\"`",
@@ -1212,7 +1212,7 @@
"authorizerSnapshot.JwtConfiguration *JwtConfiguration `json:\"jwtConfiguration,omitempty\"`",
"authorizerSnapshot.Name string `json:\"name\"`",
"backendSnapshot.PortalProductSharingPolicies map[string]string `json:\"portalProductSharingPolicies,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"deploymentSnapshot.APIID string `json:\"apiId\"`",
"deploymentSnapshot.AutoDeployed bool `json:\"autoDeployed\"`",
"deploymentSnapshot.CreatedDate isoTime `json:\"createdDate\"`",
@@ -1488,7 +1488,7 @@
"backendSnapshot.ExperimentRunEvents map[string][]ExperimentRunEvent `json:\"experimentRunEvents\"`",
"backendSnapshot.FetchedConfigs map[string]*HostedConfigurationVersion `json:\"fetchedConfigs,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.TagsByArn map[string]map[string]string `json:\"tagsByArn\"`",
"backendSnapshot.VersionCounters map[string]map[string]int32 `json:\"versionCounters\"`"
],
@@ -1526,7 +1526,7 @@
"Session.PreviousContentHash string `json:\"previousContentHash\"`",
"Session.Token string `json:\"token\"`",
"Session.TokenFamilyID string `json:\"tokenFamilyId\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"graceEntry.Content []byte",
"graceEntry.ContentHash string",
"graceEntry.ContentType string",
@@ -1621,7 +1621,7 @@
"Subscription.Event string `json:\"Event\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -1674,7 +1674,7 @@
"SuspendedState.ScheduledScalingSuspended bool `json:\"scheduledScalingSuspended\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -1683,12 +1683,12 @@
"GatewayRoute.GatewayRouteName string",
"GatewayRoute.MeshName string",
"GatewayRoute.Meta ResourceMeta",
- "GatewayRoute.Spec json.RawMessage",
+ "GatewayRoute.Spec jsontext.Value",
"GatewayRoute.Status string",
"GatewayRoute.VirtualGatewayName string",
"Mesh.Meta ResourceMeta",
"Mesh.Name string",
- "Mesh.Spec json.RawMessage",
+ "Mesh.Spec jsontext.Value",
"Mesh.Status string",
"ResourceMeta.Arn string",
"ResourceMeta.CreatedAt time.Time",
@@ -1699,32 +1699,32 @@
"Route.MeshName string",
"Route.Meta ResourceMeta",
"Route.RouteName string",
- "Route.Spec json.RawMessage",
+ "Route.Spec jsontext.Value",
"Route.Status string",
"Route.VirtualRouterName string",
"VirtualGateway.MeshName string",
"VirtualGateway.Meta ResourceMeta",
- "VirtualGateway.Spec json.RawMessage",
+ "VirtualGateway.Spec jsontext.Value",
"VirtualGateway.Status string",
"VirtualGateway.VirtualGatewayName string",
"VirtualNode.MeshName string",
"VirtualNode.Meta ResourceMeta",
- "VirtualNode.Spec json.RawMessage",
+ "VirtualNode.Spec jsontext.Value",
"VirtualNode.Status string",
"VirtualNode.VirtualNodeName string",
"VirtualRouter.MeshName string",
"VirtualRouter.Meta ResourceMeta",
- "VirtualRouter.Spec json.RawMessage",
+ "VirtualRouter.Spec jsontext.Value",
"VirtualRouter.Status string",
"VirtualRouter.VirtualRouterName string",
"VirtualService.MeshName string",
"VirtualService.Meta ResourceMeta",
- "VirtualService.Spec json.RawMessage",
+ "VirtualService.Spec jsontext.Value",
"VirtualService.Status string",
"VirtualService.VirtualServiceName string",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`"
],
"version": 1
@@ -1773,7 +1773,7 @@
"SourceConfig.ConnectionArn string `json:\"connectionArn,omitempty\"`",
"SourceConfig.ImageRepository *ImageSource `json:\"imageRepository,omitempty\"`",
"backendSnapshot.CustomDomains map[string][]*storedCustomDomain `json:\"customDomains\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"storedAutoScalingConfiguration.AutoScalingConfigurationArn string `json:\"arn\"`",
"storedAutoScalingConfiguration.AutoScalingConfigurationName string `json:\"name\"`",
@@ -1902,7 +1902,7 @@
"backendSnapshot.ExportTaskSeq int `json:\"exportTaskSeq\"`",
"backendSnapshot.SessionSeq int `json:\"sessionSeq\"`",
"backendSnapshot.SoftwareAssoc map[string]map[string]bool `json:\"softwareAssoc\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"backendSnapshot.UsageReport *storedUsageReportSubscription `json:\"usageReport\"`",
"backendSnapshot.UserStackAssoc map[string]map[string]bool `json:\"userStackAssoc\"`",
@@ -2316,7 +2316,7 @@
"UserPoolConfig.DefaultAction string `json:\"defaultAction\"`",
"UserPoolConfig.UserPoolID string `json:\"userPoolId\"`",
"backendSnapshot.APIKeys map[string]map[string]*APIKey `json:\"apiKeys,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 2
},
@@ -2343,7 +2343,7 @@
"Shift.Resource string `json:\"resource\"`",
"Shift.StartTime time.Time `json:\"startTime\"`",
"Shift.Type string `json:\"type\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -2539,7 +2539,7 @@
"backendSnapshot.QueryResults map[string]*sqlResultSnapshot `json:\"queryResults,omitempty\"`",
"backendSnapshot.ResourceTags map[string]map[string]string `json:\"resourceTags,omitempty\"`",
"backendSnapshot.TableData map[string][]map[string]any `json:\"tableData,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"databaseSnapshot.Catalog string `json:\"catalog\"`",
"databaseSnapshot.Record Database `json:\"record\"`",
"sqlColumnSnapshot.Name string `json:\"name\"`",
@@ -2829,7 +2829,7 @@
"backendSnapshot.DeletedActivities []ScalingActivity `json:\"deletedActivities,omitempty\"`",
"backendSnapshot.InstanceRefreshes map[string][]*InstanceRefresh `json:\"instanceRefreshes\"`",
"backendSnapshot.NotificationConfigs map[string][]*NotificationConfiguration `json:\"notificationConfigs\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"pendingHookAction.DefaultResult string",
"pendingHookAction.Disposition terminationDisposition",
"pendingHookAction.GroupName string",
@@ -3038,7 +3038,7 @@
"StoredQuery.QueryArn string `json:\"QueryArn,omitempty\"`",
"StoredQuery.QueryID string `json:\"QueryId,omitempty\"`",
"StoredQuery.QueryName string `json:\"QueryName\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"deliveryChannelStatusState.ChannelName string `json:\"channelName\"`",
"deliveryChannelStatusState.Snapshot *exportDeliveryState `json:\"snapshot,omitempty\"`",
"deliveryChannelStatusState.Stream *streamDeliveryState `json:\"stream,omitempty\"`",
@@ -3122,7 +3122,7 @@
"aciContainerPropsWire.Command []string `json:\"command,omitempty\"`",
"aciContainerPropsWire.EnvironmentVariables []aciEnvWire `json:\"environmentVariables,omitempty\"`",
"aciContainerPropsWire.Image string `json:\"image\"`",
- "aciContainerPropsWire.InstanceView json.RawMessage `json:\"instanceView,omitempty\"`",
+ "aciContainerPropsWire.InstanceView jsontext.Value `json:\"instanceView,omitempty\"`",
"aciContainerPropsWire.LivenessProbe *aciProbeWire `json:\"livenessProbe,omitempty\"`",
"aciContainerPropsWire.Ports []aciPortWire `json:\"ports,omitempty\"`",
"aciContainerPropsWire.ReadinessProbe *aciProbeWire `json:\"readinessProbe,omitempty\"`",
@@ -3158,7 +3158,7 @@
"aciInitPropsWire.Command []string `json:\"command,omitempty\"`",
"aciInitPropsWire.EnvironmentVariables []aciEnvWire `json:\"environmentVariables,omitempty\"`",
"aciInitPropsWire.Image string `json:\"image\"`",
- "aciInitPropsWire.InstanceView json.RawMessage `json:\"instanceView,omitempty\"`",
+ "aciInitPropsWire.InstanceView jsontext.Value `json:\"instanceView,omitempty\"`",
"aciInitPropsWire.VolumeMounts []aciMountWire `json:\"volumeMounts,omitempty\"`",
"aciInitWire.Name string `json:\"name\"`",
"aciInitWire.Properties aciInitPropsWire `json:\"properties\"`",
@@ -3185,7 +3185,7 @@
"aciPropsWire.IPAddress *aciIPWire `json:\"ipAddress,omitempty\"`",
"aciPropsWire.ImageRegistryCredentials []aciCredWire `json:\"imageRegistryCredentials,omitempty\"`",
"aciPropsWire.InitContainers []aciInitWire `json:\"initContainers,omitempty\"`",
- "aciPropsWire.InstanceView json.RawMessage `json:\"instanceView,omitempty\"`",
+ "aciPropsWire.InstanceView jsontext.Value `json:\"instanceView,omitempty\"`",
"aciPropsWire.OSType string `json:\"osType,omitempty\"`",
"aciPropsWire.Priority string `json:\"priority,omitempty\"`",
"aciPropsWire.ProvisioningState string `json:\"provisioningState,omitempty\"`",
@@ -3200,7 +3200,7 @@
"aciSubnetWire.ID string `json:\"id\"`",
"aciSubnetWire.Name string `json:\"name,omitempty\"`",
"aciVolumeWire.AzureFile *aciAzureFileWire `json:\"azureFile,omitempty\"`",
- "aciVolumeWire.EmptyDir *json.RawMessage `json:\"emptyDir,omitempty\"`",
+ "aciVolumeWire.EmptyDir *jsontext.Value `json:\"emptyDir,omitempty\"`",
"aciVolumeWire.GitRepo *aciGitRepoWire `json:\"gitRepo,omitempty\"`",
"aciVolumeWire.Name string `json:\"name\"`",
"aciVolumeWire.Secret map[string]string `json:\"secret,omitempty\"`",
@@ -3216,8 +3216,8 @@
"acrRecord.ResourceGroup string `json:\"resourceGroup\"`",
"acrRecord.SKU string `json:\"sku,omitempty\"`",
"acrRecord.Tags map[string]string `json:\"tags,omitempty\"`",
- "apimSnapshot.Operations json.RawMessage `json:\"operations,omitempty\"`",
- "apimSnapshot.Services json.RawMessage `json:\"services,omitempty\"`",
+ "apimSnapshot.Operations jsontext.Value `json:\"operations,omitempty\"`",
+ "apimSnapshot.Services jsontext.Value `json:\"services,omitempty\"`",
"appConfigDeletedRecord.DeletedAt time.Time `json:\"deletedAt\"`",
"appConfigDeletedRecord.Location string `json:\"location\"`",
"appConfigDeletedRecord.Name string `json:\"name\"`",
@@ -3819,7 +3819,7 @@
"backendSnapshot.Topics map[string]*topic `json:\"topics\"`",
"delivery.Attempts int `json:\"attempts\"`",
"delivery.Due time.Time `json:\"due\"`",
- "delivery.Event json.RawMessage `json:\"event\"`",
+ "delivery.Event jsontext.Value `json:\"event\"`",
"delivery.ID string `json:\"id\"`",
"delivery.InFlight bool `json:\"-\"`",
"delivery.LastAttempt time.Time `json:\"lastAttempt\"`",
@@ -3848,7 +3848,7 @@
"nsTopic.Subs map[string]*pullSub `json:\"subs\"`",
"pullEvent.AvailableAt time.Time `json:\"availableAt\"`",
"pullEvent.Count int `json:\"count\"`",
- "pullEvent.Event json.RawMessage `json:\"event\"`",
+ "pullEvent.Event jsontext.Value `json:\"event\"`",
"pullEvent.ExpiresAt time.Time `json:\"expiresAt\"`",
"pullEvent.LockToken string `json:\"lockToken,omitempty\"`",
"pullEvent.LockedUntil time.Time `json:\"lockedUntil\"`",
@@ -4059,28 +4059,28 @@
"AppRoleAssignment.ResourceDisplayName string `json:\"resourceDisplayName\"`",
"AppRoleAssignment.ResourceID string `json:\"resourceId\"`",
"Application.API APIApplication `json:\"api\"`",
- "Application.AddIns json.RawMessage `json:\"addIns,omitempty\"`",
+ "Application.AddIns jsontext.Value `json:\"addIns,omitempty\"`",
"Application.AppID string `json:\"appId\"`",
"Application.AppRoles []AppRole `json:\"appRoles\"`",
"Application.ApplicationTemplateID string `json:\"applicationTemplateId,omitempty\"`",
- "Application.AuthenticationBehaviors json.RawMessage `json:\"authenticationBehaviors,omitempty\"`",
- "Application.Certification json.RawMessage `json:\"certification,omitempty\"`",
+ "Application.AuthenticationBehaviors jsontext.Value `json:\"authenticationBehaviors,omitempty\"`",
+ "Application.Certification jsontext.Value `json:\"certification,omitempty\"`",
"Application.CreatedDateTime time.Time `json:\"createdDateTime\"`",
"Application.DefaultRedirectURI string `json:\"defaultRedirectUri,omitempty\"`",
"Application.Description string `json:\"description,omitempty\"`",
- "Application.DisabledByMicrosoftStatus json.RawMessage `json:\"disabledByMicrosoftStatus,omitempty\"`",
+ "Application.DisabledByMicrosoftStatus jsontext.Value `json:\"disabledByMicrosoftStatus,omitempty\"`",
"Application.DisplayName string `json:\"displayName\"`",
"Application.GroupMembershipClaims string `json:\"groupMembershipClaims,omitempty\"`",
"Application.ID string `json:\"id\"`",
"Application.IdentifierURIs []string `json:\"identifierUris\"`",
- "Application.Info json.RawMessage `json:\"info,omitempty\"`",
+ "Application.Info jsontext.Value `json:\"info,omitempty\"`",
"Application.IsDeviceOnlyAuthSupported *bool `json:\"isDeviceOnlyAuthSupported\"`",
"Application.IsFallbackPublicClient bool `json:\"isFallbackPublicClient\"`",
"Application.KeyCredentials []KeyCredential `json:\"keyCredentials\"`",
"Application.Notes string `json:\"notes,omitempty\"`",
"Application.OAuth2RequirePostResponse bool `json:\"oauth2RequirePostResponse\"`",
- "Application.OptionalClaims json.RawMessage `json:\"optionalClaims,omitempty\"`",
- "Application.ParentalControlSettings json.RawMessage `json:\"parentalControlSettings,omitempty\"`",
+ "Application.OptionalClaims jsontext.Value `json:\"optionalClaims,omitempty\"`",
+ "Application.ParentalControlSettings jsontext.Value `json:\"parentalControlSettings,omitempty\"`",
"Application.PasswordCredentials []PasswordCredential `json:\"passwordCredentials\"`",
"Application.PublicClient RedirectFacet `json:\"publicClient\"`",
"Application.PublisherDomain string `json:\"publisherDomain\"`",
@@ -4092,7 +4092,7 @@
"Application.Tags []string `json:\"tags\"`",
"Application.TokenEncryptionKeyID string `json:\"tokenEncryptionKeyId,omitempty\"`",
"Application.UniqueName string `json:\"uniqueName,omitempty\"`",
- "Application.VerifiedPublisher json.RawMessage `json:\"verifiedPublisher,omitempty\"`",
+ "Application.VerifiedPublisher jsontext.Value `json:\"verifiedPublisher,omitempty\"`",
"Application.Web WebApplication `json:\"web\"`",
"Group.Classification string `json:\"classification,omitempty\"`",
"Group.CreatedDateTime time.Time `json:\"createdDateTime\"`",
@@ -4153,7 +4153,7 @@
"ResourceAccess.ID string `json:\"id\"`",
"ResourceAccess.Type string `json:\"type\"`",
"ServicePrincipal.AccountEnabled bool `json:\"accountEnabled\"`",
- "ServicePrincipal.AddIns json.RawMessage `json:\"addIns,omitempty\"`",
+ "ServicePrincipal.AddIns jsontext.Value `json:\"addIns,omitempty\"`",
"ServicePrincipal.AlternativeNames []string `json:\"alternativeNames\"`",
"ServicePrincipal.AppDescription string `json:\"appDescription,omitempty\"`",
"ServicePrincipal.AppDisplayName string `json:\"appDisplayName\"`",
@@ -4163,13 +4163,13 @@
"ServicePrincipal.AppRoles []AppRole `json:\"appRoles\"`",
"ServicePrincipal.ApplicationTemplateID string `json:\"applicationTemplateId,omitempty\"`",
"ServicePrincipal.CreatedDateTime time.Time `json:\"createdDateTime\"`",
- "ServicePrincipal.CustomSecurityAttributes json.RawMessage `json:\"customSecurityAttributes,omitempty\"`",
+ "ServicePrincipal.CustomSecurityAttributes jsontext.Value `json:\"customSecurityAttributes,omitempty\"`",
"ServicePrincipal.Description string `json:\"description,omitempty\"`",
- "ServicePrincipal.DisabledByMicrosoftStatus json.RawMessage `json:\"disabledByMicrosoftStatus,omitempty\"`",
+ "ServicePrincipal.DisabledByMicrosoftStatus jsontext.Value `json:\"disabledByMicrosoftStatus,omitempty\"`",
"ServicePrincipal.DisplayName string `json:\"displayName\"`",
"ServicePrincipal.Homepage string `json:\"homepage,omitempty\"`",
"ServicePrincipal.ID string `json:\"id\"`",
- "ServicePrincipal.Info json.RawMessage `json:\"info,omitempty\"`",
+ "ServicePrincipal.Info jsontext.Value `json:\"info,omitempty\"`",
"ServicePrincipal.KeyCredentials []KeyCredential `json:\"keyCredentials\"`",
"ServicePrincipal.LoginURL string `json:\"loginUrl,omitempty\"`",
"ServicePrincipal.LogoutURL string `json:\"logoutUrl,omitempty\"`",
@@ -4177,20 +4177,20 @@
"ServicePrincipal.NotificationEmailAddresses []string `json:\"notificationEmailAddresses\"`",
"ServicePrincipal.OAuth2PermissionScopes []PermissionScope `json:\"oauth2PermissionScopes\"`",
"ServicePrincipal.PasswordCredentials []PasswordCredential `json:\"passwordCredentials\"`",
- "ServicePrincipal.PasswordSSOSettings json.RawMessage `json:\"passwordSingleSignOnSettings,omitempty\"`",
- "ServicePrincipal.PreferredKeyEndDateTime json.RawMessage `json:\"preferredTokenSigningKeyEndDateTime,omitempty\"`",
+ "ServicePrincipal.PasswordSSOSettings jsontext.Value `json:\"passwordSingleSignOnSettings,omitempty\"`",
+ "ServicePrincipal.PreferredKeyEndDateTime jsontext.Value `json:\"preferredTokenSigningKeyEndDateTime,omitempty\"`",
"ServicePrincipal.PreferredSingleSignOnMode string `json:\"preferredSingleSignOnMode,omitempty\"`",
"ServicePrincipal.PreferredTokenSigningKeyThumb string `json:\"preferredTokenSigningKeyThumbprint,omitempty\"`",
"ServicePrincipal.ReplyURLs []string `json:\"replyUrls\"`",
- "ServicePrincipal.ResourceSpecificPermissions json.RawMessage `json:\"resourceSpecificApplicationPermissions,omitempty\"`",
+ "ServicePrincipal.ResourceSpecificPermissions jsontext.Value `json:\"resourceSpecificApplicationPermissions,omitempty\"`",
"ServicePrincipal.SAMLMetadataURL string `json:\"samlMetadataUrl,omitempty\"`",
- "ServicePrincipal.SAMLSingleSignOnSettings json.RawMessage `json:\"samlSingleSignOnSettings,omitempty\"`",
+ "ServicePrincipal.SAMLSingleSignOnSettings jsontext.Value `json:\"samlSingleSignOnSettings,omitempty\"`",
"ServicePrincipal.ServicePrincipalNames []string `json:\"servicePrincipalNames\"`",
"ServicePrincipal.ServicePrincipalType string `json:\"servicePrincipalType\"`",
"ServicePrincipal.SignInAudience string `json:\"signInAudience\"`",
"ServicePrincipal.Tags []string `json:\"tags\"`",
"ServicePrincipal.TokenEncryptionKeyID string `json:\"tokenEncryptionKeyId,omitempty\"`",
- "ServicePrincipal.VerifiedPublisher json.RawMessage `json:\"verifiedPublisher,omitempty\"`",
+ "ServicePrincipal.VerifiedPublisher jsontext.Value `json:\"verifiedPublisher,omitempty\"`",
"User.AccountEnabled bool `json:\"accountEnabled\"`",
"User.BusinessPhones []string `json:\"businessPhones\"`",
"User.City string `json:\"city,omitempty\"`",
@@ -4633,7 +4633,7 @@
"RecoveryPoint.ResourceArn string `json:\"resourceArn,omitempty\"`",
"RecoveryPoint.ResourceName string `json:\"resourceName,omitempty\"`",
"RecoveryPoint.ResourceType string `json:\"resourceType,omitempty\"`",
- "RecoveryPoint.Snapshot json.RawMessage `json:\"snapshot,omitempty\"`",
+ "RecoveryPoint.Snapshot jsontext.Value `json:\"snapshot,omitempty\"`",
"RecoveryPoint.SourceBackupVaultArn string `json:\"sourceBackupVaultArn,omitempty\"`",
"RecoveryPoint.Status string `json:\"status\"`",
"RecoveryPoint.StorageClass string `json:\"storageClass,omitempty\"`",
@@ -4809,11 +4809,11 @@
"backendSnapshot.RecoveryPointIndexStatus map[string]string `json:\"recoveryPointIndexStatus,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.RegionSettings *RegionSettings `json:\"regionSettings,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"ddbSnapshot.Attributes []ddbtypes.AttributeDefinition `json:\"attributes\"`",
"ddbSnapshot.BillingMode ddbtypes.BillingMode `json:\"billingMode\"`",
"ddbSnapshot.GSIs []ddbtypes.GlobalSecondaryIndex `json:\"gsis,omitempty\"`",
- "ddbSnapshot.Items []map[string]json.RawMessage `json:\"items\"`",
+ "ddbSnapshot.Items []map[string]jsontext.Value `json:\"items\"`",
"ddbSnapshot.KeySchema []ddbtypes.KeySchemaElement `json:\"keySchema\"`",
"ddbSnapshot.LSIs []ddbtypes.LocalSecondaryIndex `json:\"lsis,omitempty\"`",
"ddbSnapshot.Provisioned *ddbtypes.ProvisionedThroughput `json:\"provisioned,omitempty\"`",
@@ -5237,7 +5237,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.JobDefRevisions map[string]map[string]int32 `json:\"jobDefRevisions\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"nodeRun.attempts []JobAttempt",
"nodeRun.startedAt *int64",
"nodeRun.status string",
@@ -5279,7 +5279,7 @@
"Export.Tags map[string]string `json:\"tags\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -5313,7 +5313,7 @@
"AdvancedPromptOptimizationJob.OutputConfig AdvancedPromptOptimizationOutputConfig",
"AdvancedPromptOptimizationJob.Tags []Tag",
"AdvancedPromptOptimizationOutputConfig.S3URI string `json:\"s3Uri\"`",
- "AutomatedEvaluationConfig.CustomMetricConfig json.RawMessage `json:\"customMetricConfig,omitempty\"`",
+ "AutomatedEvaluationConfig.CustomMetricConfig jsontext.Value `json:\"customMetricConfig,omitempty\"`",
"AutomatedEvaluationConfig.DatasetMetricConfigs []EvaluationDatasetMetricConfig `json:\"datasetMetricConfigs,omitempty\"`",
"AutomatedEvaluationConfig.EvaluatorModelConfig *EvaluatorModelConfig `json:\"evaluatorModelConfig,omitempty\"`",
"AutomatedReasoningPolicy.CreatedAt time.Time `json:\"createdAt\"`",
@@ -5322,7 +5322,7 @@
"AutomatedReasoningPolicy.KmsKeyArn string `json:\"kmsKeyArn,omitempty\"`",
"AutomatedReasoningPolicy.Name string `json:\"name\"`",
"AutomatedReasoningPolicy.PolicyArn string `json:\"policyArn\"`",
- "AutomatedReasoningPolicy.PolicyDefinition json.RawMessage `json:\"policyDefinition,omitempty\"`",
+ "AutomatedReasoningPolicy.PolicyDefinition jsontext.Value `json:\"policyDefinition,omitempty\"`",
"AutomatedReasoningPolicy.Status string `json:\"status\"`",
"AutomatedReasoningPolicy.Tags []Tag `json:\"tags,omitempty\"`",
"AutomatedReasoningPolicy.UpdatedAt time.Time `json:\"updatedAt\"`",
@@ -5330,7 +5330,7 @@
"AutomatedReasoningPolicyBuildWorkflow.BuildWorkflowType string `json:\"buildWorkflowType,omitempty\"`",
"AutomatedReasoningPolicyBuildWorkflow.CreatedAt time.Time `json:\"createdAt\"`",
"AutomatedReasoningPolicyBuildWorkflow.PolicyArn string `json:\"policyArn\"`",
- "AutomatedReasoningPolicyBuildWorkflow.SourceContent json.RawMessage `json:\"sourceContent,omitempty\"`",
+ "AutomatedReasoningPolicyBuildWorkflow.SourceContent jsontext.Value `json:\"sourceContent,omitempty\"`",
"AutomatedReasoningPolicyBuildWorkflow.Status string `json:\"status\"`",
"AutomatedReasoningPolicyBuildWorkflow.UpdatedAt time.Time `json:\"updatedAt\"`",
"AutomatedReasoningPolicyTestCase.ConfidenceThreshold *float64 `json:\"confidenceThreshold,omitempty\"`",
@@ -5584,8 +5584,8 @@
"ProvisionedModelThroughput.ProvisionedModelName string `json:\"provisionedModelName\"`",
"ProvisionedModelThroughput.Status string `json:\"status\"`",
"ProvisionedModelThroughput.Tags []Tag `json:\"tags,omitempty\"`",
- "RAGConfig.KnowledgeBaseConfig json.RawMessage `json:\"knowledgeBaseConfig,omitempty\"`",
- "RAGConfig.PrecomputedRagSourceConfig json.RawMessage `json:\"precomputedRagSourceConfig,omitempty\"`",
+ "RAGConfig.KnowledgeBaseConfig jsontext.Value `json:\"knowledgeBaseConfig,omitempty\"`",
+ "RAGConfig.PrecomputedRagSourceConfig jsontext.Value `json:\"precomputedRagSourceConfig,omitempty\"`",
"ResourcePolicy.CreatedAt time.Time",
"ResourcePolicy.PolicyDocument string",
"ResourcePolicy.ResourceArn string",
@@ -5639,7 +5639,7 @@
"backendSnapshot.ProvisionedCounter int `json:\"provisionedCounter\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ResourcePolicyRevisionCounter int `json:\"resourcePolicyRevisionCounter\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.UseCaseFormData []byte `json:\"useCaseFormData,omitempty\"`"
],
"version": 3
@@ -5879,7 +5879,7 @@
"backendSnapshot.PromptVersionCtrs map[string]int `json:\"promptVersionCtrs\"`",
"backendSnapshot.PromptsByName map[string]string `json:\"promptsByName\"`",
"backendSnapshot.ResourcePolicyCounter int `json:\"resourcePolicyCounter\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`"
],
"version": 3
@@ -5927,7 +5927,7 @@
"ToolSession.Token string `json:\"clientToken,omitempty\"`",
"ToolSession.ToolID string `json:\"toolId\"`",
"ToolSession.Updated time.Time `json:\"updated\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -5961,7 +5961,7 @@
"Version.Doc map[string]any `json:\"doc\"`",
"Version.ID string `json:\"id\"`",
"Version.Parents []string `json:\"parents,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -5987,7 +5987,7 @@
"backendSnapshot.AsyncInvokeCounter int `json:\"asyncInvokeCounter\"`",
"backendSnapshot.Invocations []*Invocation `json:\"invocations\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.TokenIndex map[string]string `json:\"tokenIndex\"`"
],
"version": 1
@@ -6097,7 +6097,7 @@
"ValuesFilter.Values []string `json:\"Values,omitempty\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -6179,7 +6179,7 @@
"backendSnapshot.AnomalyTTL time.Duration `json:\"anomalyTTL\"`",
"backendSnapshot.BackfillJobs []*BackfillJob `json:\"backfillJobs\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"ceCostCategoryValues.Key string `json:\"Key\"`",
"ceCostCategoryValues.Values []string `json:\"Values\"`",
"ceDimensionValues.Key string `json:\"Key\"`",
@@ -6497,7 +6497,7 @@
"SchemaAnalysisRule.UpdateTime float64 `json:\"updateTime,omitempty\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.TagsByArn map[string]map[string]string `json:\"tagsByArn\"`"
],
"version": 1
@@ -6522,7 +6522,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.ClientTokens map[string]clientTokenEntry `json:\"clientTokens\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"clientTokenEntry.CreatedAt time.Time `json:\"createdAt,omitzero\"`",
"clientTokenEntry.Fingerprint string `json:\"fingerprint\"`",
"clientTokenEntry.RequestToken string `json:\"requestToken\"`",
@@ -6771,7 +6771,7 @@
"backendSnapshot.StackPolicies map[string]string `json:\"stackPolicies\"`",
"backendSnapshot.StackSetOpResults map[string]map[string][]StackSetOperationResult `json:\"stackSetOpResults\"`",
"backendSnapshot.StackSetOperations map[string]map[string]*StackSetOperation `json:\"stackSetOperations\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.TypeConfigs map[string]string `json:\"typeConfigs\"`",
"backendSnapshot.TypeVersions map[string][]*RegisteredTypeVersion `json:\"typeVersions\"`",
"pendingStackOp.DisableValidation bool `json:\"disableValidation,omitempty\"`",
@@ -7106,7 +7106,7 @@
"backendSnapshot.MonitoringSubscriptions map[string]*MonitoringSubscription `json:\"monitoringSubscriptions,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ResourcePolicies map[string]*resourcePolicyEntry `json:\"resourcePolicies,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"invalidationSnapshot.CallerRef string `json:\"callerRef,omitempty\"`",
"invalidationSnapshot.CreateTime time.Time `json:\"createTime\"`",
"invalidationSnapshot.ID string `json:\"id\"`",
@@ -7178,7 +7178,7 @@
"Hsm.Zone string `json:\"zone\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -7331,7 +7331,7 @@
"backendSnapshot.ImportCounter int `json:\"importCounter\"`",
"backendSnapshot.QueryCounter int `json:\"queryCounter\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -7526,7 +7526,7 @@
"backendSnapshot.AlarmHistory map[string][]AlarmHistoryItem `json:\"alarmHistory\"`",
"backendSnapshot.Metrics map[string]map[string]*metricRecord `json:\"metrics\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.TotalMetrics int `json:\"totalMetrics\"`",
"dashboardRecord.Body string `json:\"Body\"`",
"dashboardRecord.LastModified time.Time `json:\"LastModified\"`",
@@ -7802,7 +7802,7 @@
"Transformer.Processors []map[string]any `json:\"transformerConfig\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"cwlIntegrationSnapshot.CreatedAt time.Time `json:\"createdAt,omitzero\"`",
"cwlIntegrationSnapshot.Name string `json:\"integrationName\"`",
"cwlIntegrationSnapshot.OpenSearchResourceConfig *OpenSearchResourceConfig `json:\"openSearchResourceConfig,omitempty\"`",
@@ -7938,7 +7938,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.ExternalConnections map[string]map[string][]ExternalConnection `json:\"externalConnections\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"domainPolicyDTO.DomainName string `json:\"domainName\"`",
"domainPolicyDTO.Region string `json:\"region\"`",
"domainPolicyDTO.Value *DomainPermissionsPolicy `json:\"value\"`",
@@ -8303,7 +8303,7 @@
"backendSnapshot.BuildBatchNumbers map[string]int64 `json:\"buildBatchNumbers\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ResourcePolicies map[string]string `json:\"resourcePolicies\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"reportDetail.Coverages []CodeCoverage `json:\"coverages,omitempty\"`",
"reportDetail.ReportArn string `json:\"reportArn\"`",
"reportDetail.TestCases []TestCase `json:\"testCases,omitempty\"`"
@@ -8419,7 +8419,7 @@
"backendSnapshot.PROverrides map[string]bool `json:\"prOverrides\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.RepoTemplateAssoc map[string]map[string]struct{} `json:\"repoTemplateAssoc\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Triggers map[string][]RepositoryTrigger `json:\"triggers\"`",
"commentSnapshot.AfterCommitID string `json:\"afterCommitId,omitempty\"`",
"commentSnapshot.AuthorARN string `json:\"authorArn\"`",
@@ -8508,7 +8508,7 @@
"VpcConfiguration.VpcID string `json:\"vpcId\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -8658,7 +8658,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.GitHubTokens []string `json:\"githubTokens,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"deploymentGroupSnapshot.TagsMap map[string]string `json:\"tagsMap\"`",
"onPremisesInstanceSnapshot.TagsMap map[string]string `json:\"tagsMap\"`"
],
@@ -8709,7 +8709,7 @@
"associationRecord.Updated awstime.EpochTime `json:\"updated\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"feedbackRecord.CodeReviewARN string `json:\"codeReviewArn\"`",
"feedbackRecord.Created awstime.EpochTime `json:\"created\"`",
"feedbackRecord.Reactions []string `json:\"reactions\"`",
@@ -8984,7 +8984,7 @@
"backendSnapshot.ConditionRuns []conditionRunEntry `json:\"conditionRuns,omitempty\"`",
"backendSnapshot.Executions []executionEntry `json:\"executions\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"conditionRunEntry.PipelineName string `json:\"pipelineName\"`",
"conditionRunEntry.Region string `json:\"region\"`",
"conditionRunEntry.Runs []*ConditionRun `json:\"runs\"`",
@@ -9067,7 +9067,7 @@
"VpcConfiguration.VpcID string `json:\"VpcId\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"repositorySyncStatusDTO.Branch string `json:\"branch\"`",
"repositorySyncStatusDTO.Region string `json:\"region\"`",
"repositorySyncStatusDTO.RepositoryLinkID string `json:\"repositoryLinkID\"`",
@@ -9125,7 +9125,7 @@
"RulesConfiguration.Rules []MappingRule `json:\"rules\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"principalTagDTO.PoolID string `json:\"poolID\"`",
"principalTagDTO.ProviderName string `json:\"providerName\"`",
"principalTagDTO.Region string `json:\"region\"`",
@@ -9387,7 +9387,7 @@
"backendSnapshot.ResourceTags map[string]map[string]string `json:\"resourceTags,omitempty\"`",
"backendSnapshot.RevokedChains map[string]time.Time `json:\"revokedChains,omitempty\"`",
"backendSnapshot.RiskConfigurations map[string]*RiskConfiguration `json:\"riskConfigurations,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables,omitempty\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables,omitempty\"`",
"backendSnapshot.TokenRevokedBefore map[string]time.Time `json:\"tokenRevokedBefore,omitempty\"`",
"backendSnapshot.TokenRevokedBeforeSeq map[string]int64 `json:\"tokenRevokedBeforeSeq,omitempty\"`",
"backendSnapshot.TokenSeq int64 `json:\"tokenSeq,omitempty\"`",
@@ -9503,7 +9503,7 @@
"backendSnapshot.PolicyModifiedAt map[string]time.Time `json:\"policyModifiedAt\"`",
"backendSnapshot.PolicyRevisions map[string]string `json:\"policyRevisions\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`"
],
"version": 1
@@ -9570,7 +9570,7 @@
"StorageAssociation.ResourceType string `json:\"ResourceType\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"hierarchyStructureRec.InstanceID string `json:\"instanceId\"`",
"hierarchyStructureRec.Structure HierarchyStructure `json:\"structure\"`",
"instanceRec.Attributes map[string]string `json:\"attributes\"`",
@@ -9606,7 +9606,7 @@
"ThroughputSpec.Manual int `json:\"Throughput,omitempty\"`",
"backendSnapshot.Databases map[string]*storedDatabase `json:\"databases\"`",
"backendSnapshot.Regions *AccountRegions `json:\"regions,omitempty\"`",
- "backendSnapshot.TableAPI json.RawMessage `json:\"tableApi,omitempty\"`",
+ "backendSnapshot.TableAPI jsontext.Value `json:\"tableApi,omitempty\"`",
"changeRecord.Current *storedDocument `json:\"Current,omitempty\"`",
"changeRecord.ID string `json:\"ID\"`",
"changeRecord.Op string `json:\"Op\"`",
@@ -9652,15 +9652,15 @@
"storedContainer.ChangeFloor int64 `json:\"ChangeFloor,omitempty\"`",
"storedContainer.ChangeLog []*changeRecord `json:\"ChangeLog,omitempty\"`",
"storedContainer.ChangeSeq int64 `json:\"ChangeSeq,omitempty\"`",
- "storedContainer.ComputedProperties json.RawMessage `json:\"ComputedProperties,omitempty\"`",
- "storedContainer.ConflictResolutionPolicy json.RawMessage `json:\"ConflictResolutionPolicy,omitempty\"`",
+ "storedContainer.ComputedProperties jsontext.Value `json:\"ComputedProperties,omitempty\"`",
+ "storedContainer.ConflictResolutionPolicy jsontext.Value `json:\"ConflictResolutionPolicy,omitempty\"`",
"storedContainer.Conflicts []*conflictRecord `json:\"Conflicts,omitempty\"`",
"storedContainer.DefaultTTL *int `json:\"DefaultTTL,omitempty\"`",
"storedContainer.Documents map[documentCompositeKey]*storedDocument",
- "storedContainer.FullTextPolicy json.RawMessage `json:\"FullTextPolicy,omitempty\"`",
- "storedContainer.GeospatialConfig json.RawMessage `json:\"GeospatialConfig,omitempty\"`",
+ "storedContainer.FullTextPolicy jsontext.Value `json:\"FullTextPolicy,omitempty\"`",
+ "storedContainer.GeospatialConfig jsontext.Value `json:\"GeospatialConfig,omitempty\"`",
"storedContainer.ID string",
- "storedContainer.IndexingPolicy json.RawMessage `json:\"IndexingPolicy,omitempty\"`",
+ "storedContainer.IndexingPolicy jsontext.Value `json:\"IndexingPolicy,omitempty\"`",
"storedContainer.Modified time.Time `json:\"Modified\"`",
"storedContainer.Offer *storedOffer `json:\"Offer,omitempty\"`",
"storedContainer.PKLayout *pkLayout `json:\"PKLayout,omitempty\"`",
@@ -9669,8 +9669,8 @@
"storedContainer.Sprocs map[string]*storedScript `json:\"Sprocs,omitempty\"`",
"storedContainer.Triggers map[string]*storedScript `json:\"Triggers,omitempty\"`",
"storedContainer.UDFs map[string]*storedScript `json:\"UDFs,omitempty\"`",
- "storedContainer.UniqueKeyPolicy json.RawMessage `json:\"UniqueKeyPolicy,omitempty\"`",
- "storedContainer.VectorEmbeddingPolicy json.RawMessage `json:\"VectorEmbeddingPolicy,omitempty\"`",
+ "storedContainer.UniqueKeyPolicy jsontext.Value `json:\"UniqueKeyPolicy,omitempty\"`",
+ "storedContainer.VectorEmbeddingPolicy jsontext.Value `json:\"VectorEmbeddingPolicy,omitempty\"`",
"storedContainer.keep time.Duration",
"storedContainer.repl *replState",
"storedContainer.sorted atomic.Pointer[[]*storedDocument]",
@@ -9694,7 +9694,7 @@
"storedPermission.Link string",
"storedPermission.Mode string",
"storedPermission.Modified time.Time `json:\"Modified\"`",
- "storedPermission.PartitionKey json.RawMessage `json:\"PartitionKey,omitempty\"`",
+ "storedPermission.PartitionKey jsontext.Value `json:\"PartitionKey,omitempty\"`",
"storedPermission.PartitionKeyJSON string `json:\"PartitionKeyJSON,omitempty\"`",
"storedPermission.Resource string",
"storedScript.Body string",
@@ -9732,7 +9732,7 @@
"Report.TimeUnit string `json:\"timeUnit\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -9940,7 +9940,7 @@
"backendSnapshot.JobRuns map[string]map[string][]*JobRun `json:\"jobRuns\"`",
"backendSnapshot.RecipeVersions map[string]map[string][]*Recipe `json:\"recipeVersions\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -9951,7 +9951,7 @@
"PrivateLinkConfig.VpcEndpointID string `json:\"vpcEndpointId,omitempty\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"storedAgent.AgentArn string `json:\"agentArn\"`",
"storedAgent.CreationTime time.Time `json:\"creationTime\"`",
@@ -10164,7 +10164,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Events []*Event `json:\"events\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`"
],
"version": 1
@@ -10175,7 +10175,7 @@
"backendSnapshot.Datasources map[string]map[string]string `json:\"datasources\"`",
"backendSnapshot.OrgAdmins []*storedOrgAdmin `json:\"orgAdmins\"`",
"backendSnapshot.OrgConfigs map[string]bool `json:\"orgConfigs\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"storedGraph.Arn string `json:\"arn\"`",
"storedGraph.CreatedTime time.Time `json:\"createdTime\"`",
@@ -10362,7 +10362,7 @@
"backendSnapshot.AccountID string `json:\"accountId\"`",
"backendSnapshot.NextASN int64 `json:\"nextAmazonSideAsn\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -10386,7 +10386,7 @@
"backendSnapshot.DirSettings map[string]map[string][]*storedDirectorySetting `json:\"dirSettings\"`",
"backendSnapshot.IPRoutes map[string]map[string][]storedIpRoute `json:\"ipRoutes\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.UpdateInfoEntries map[string]map[string][]*storedUpdateInfo `json:\"updateInfoEntries\"`",
"storedADAssessment.AssessType string `json:\"assessmentType\"`",
"storedADAssessment.AssessmentID string `json:\"assessmentId\"`",
@@ -10588,7 +10588,7 @@
"dlm": {
"fields": [
"backendSnapshot.Counter int `json:\"counter\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"managedResource.ArchivedAt time.Time `json:\"archivedAt,omitzero\"`",
"managedResource.CopyOf string `json:\"copyOf,omitempty\"`",
"managedResource.CreatedAt time.Time `json:\"createdAt\"`",
@@ -10883,7 +10883,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ResourceTags map[string]map[string]string `json:\"resourceTags,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -11041,7 +11041,7 @@
"backendSnapshot.EventsLog map[string][]Event `json:\"eventsLog\"`",
"backendSnapshot.PendingMaintenanceActions map[string]map[string]PendingMaintenanceAction `json:\"pendingMaintenanceActions\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string][]Tag `json:\"tags\"`",
"xmlAvailabilityZoneList.Members []string `xml:\"AvailabilityZone\"`",
"xmlDBClusterSnapshot.AvailabilityZones xmlAvailabilityZoneList `xml:\"AvailabilityZones\"`",
@@ -11095,7 +11095,7 @@
"Stream.Target *StreamTarget",
"StreamTarget.RoleArn string",
"StreamTarget.StreamArn string",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -13056,7 +13056,7 @@
"backendSnapshot.SpotFleetHistory map[string][]SpotFleetHistoryRecord `json:\"spotFleetHistory\"`",
"backendSnapshot.SubnetCIDRAssociations map[string][]*SubnetCIDRAssociation `json:\"subnetCIDRAssociations\"`",
"backendSnapshot.SubnetCIDRReservations map[string][]*SubnetCIDRReservation `json:\"subnetCIDRReservations\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags,omitempty\"`",
"backendSnapshot.TgwRTPropagations map[string]map[string]*snapTGWRTProp `json:\"tgwRTPropagations,omitempty\"`",
"backendSnapshot.UsageReportEntries map[string][]*UsageReportEntry `json:\"usageReportEntries,omitempty\"`",
@@ -13234,7 +13234,7 @@
"backendSnapshot.ReplicationConfig *ReplicationConfig `json:\"replicationConfig,omitempty\"`",
"backendSnapshot.RepoTags map[string]map[string]string `json:\"repoTags,omitempty\"`",
"backendSnapshot.SigningConfig *SigningSettings `json:\"signingConfig,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.UploadedLayers map[string]map[string]int64 `json:\"uploadedLayers,omitempty\"`",
"lifecyclePolicyEntry.PolicyText string `json:\"policyText\"`",
"lifecyclePolicyEntry.RepositoryName string `json:\"repositoryName\"`",
@@ -13271,7 +13271,7 @@
"Repository.RepositoryURI string",
"Repository.Tags map[string]string",
"backendSnapshot.RegistryCatalogData RegistryCatalogData `json:\"registryCatalogData\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.TagIndex map[string]map[string]tagBinding `json:\"tagIndex,omitempty\"`",
"backendSnapshot.UploadedLayers map[string]map[string]int64 `json:\"uploadedLayers,omitempty\"`",
"tagBinding.CreatedAt time.Time",
@@ -13881,7 +13881,7 @@
"backendSnapshot.Attributes map[string]map[string]*Attribute `json:\"attributes\"`",
"backendSnapshot.DaemonTaskDefinitions map[string][]*DaemonTaskDefinition `json:\"daemonTaskDefinitions\"`",
"backendSnapshot.ResourceTags map[string][]Tag `json:\"resourceTags\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.TaskDefinitions map[string][]*TaskDefinition `json:\"taskDefinitions\"`",
"rollingPlan.currentLive int",
"rollingPlan.runningTotal int",
@@ -13974,7 +13974,7 @@
"backendSnapshot.FileSystemPolicies map[string]map[string]string `json:\"fileSystemPolicies\"`",
"backendSnapshot.LifecyclePolicies map[string]map[string][]LifecyclePolicy `json:\"lifecyclePolicies\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -14079,7 +14079,7 @@
"Cluster.CertificateAuthority string `json:\"certificateAuthority,omitempty\"`",
"Cluster.ClusterLogging []ClusterLogEntry `json:\"clusterLogging,omitempty\"`",
"Cluster.ComputeConfig *ComputeConfig `json:\"computeConfig,omitempty\"`",
- "Cluster.ConfigBlocks map[string]json.RawMessage `json:\"configBlocks,omitempty\"`",
+ "Cluster.ConfigBlocks map[string]jsontext.Value `json:\"configBlocks,omitempty\"`",
"Cluster.ConnectorConfig *ConnectorConfig `json:\"connectorConfig,omitempty\"`",
"Cluster.CreatedAt time.Time `json:\"createdAt\"`",
"Cluster.CreatorARN string `json:\"creatorArn,omitempty\"`",
@@ -14238,8 +14238,8 @@
"backendSnapshot.AccountID string `json:\"accountId\"`",
"backendSnapshot.EncryptionConfigs map[string][]EncryptionConfig `json:\"encryptionConfigs,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
- "idempotencyRecord.Body json.RawMessage `json:\"body\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
+ "idempotencyRecord.Body jsontext.Value `json:\"body\"`",
"idempotencyRecord.CreatedAt time.Time `json:\"createdAt\"`",
"idempotencyRecord.Fingerprint string `json:\"fingerprint\"`",
"idempotencyRecord.Op string `json:\"op\"`",
@@ -14528,7 +14528,7 @@
"backendSnapshot.ServerlessCaches map[string][]*ServerlessCache `json:\"serverlessCaches,omitempty\"`",
"backendSnapshot.Snapshots map[string][]*CacheSnapshot `json:\"snapshots\"`",
"backendSnapshot.SubnetGroups map[string][]*CacheSubnetGroup `json:\"subnetGroups\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.UserGroups map[string][]*UserGroup `json:\"userGroups,omitempty\"`",
"backendSnapshot.Users map[string][]*User `json:\"users,omitempty\"`",
"clusterGroupState.AZMode string `json:\"azMode,omitempty\"`",
@@ -14679,7 +14679,7 @@
"backendSnapshot.Events map[string][]*EventRecord `json:\"events,omitempty\"`",
"backendSnapshot.ManagedActionHistory map[string]map[string][]*ManagedActionHistory `json:\"managedActionHistory,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -14822,7 +14822,7 @@
"backendSnapshot.PackageAssociations map[string]map[string][]string `json:\"packageAssociations\"`",
"backendSnapshot.PackagesByName map[string]map[string]string `json:\"packagesByName\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.VpcAccess map[string]map[string][]string `json:\"vpcAccess\"`"
],
"version": 1
@@ -15071,7 +15071,7 @@
"backendSnapshot.ResourcePolicies map[string]string `json:\"resourcePolicies\"`",
"backendSnapshot.RevocationIDCounter int64 `json:\"revocationIdCounter\"`",
"backendSnapshot.RuleCounter int `json:\"ruleCounter\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.TargetDrainingUntil map[string]map[string]time.Time `json:\"targetDrainingUntil\"`",
"backendSnapshot.TargetReadyAt map[string]map[string]time.Time `json:\"targetReadyAt\"`"
],
@@ -15325,7 +15325,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.ArnIndex map[string]map[string]string `json:\"arnIndex\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"blockPublicAccessMeta.CreatedByArn string `json:\"CreatedByArn,omitempty\"`",
"blockPublicAccessMeta.CreationDateTime float64 `json:\"CreationDateTime\"`",
"blockPublicAccessMeta.region string",
@@ -15397,7 +15397,7 @@
"backendSnapshot.JobRunTokens map[string]map[string]string `json:\"jobRunTokens\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.SessionTokens map[string]map[string]string `json:\"sessionTokens\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -15431,7 +15431,7 @@
"backendSnapshot.BusPolicies map[string]map[string]*EventBusPolicy `json:\"busPolicies,omitempty\"`",
"backendSnapshot.EventLog []EventLogEntry `json:\"eventLog\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"handlerSnapshot.Backend []byte `json:\"backend\"`",
"handlerSnapshot.Tags map[string]map[string]string `json:\"tags,omitempty\"`"
],
@@ -15718,7 +15718,7 @@
"VpcConfigurationDescription.VpcID string `json:\"VpcId\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"flushSnapshot.backupRecords [][]byte",
"flushSnapshot.elasticsearchDest *ElasticsearchDestinationDescription",
"flushSnapshot.httpDest *HTTPEndpointDestinationDescription",
@@ -15864,7 +15864,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.SafetyLever *SafetyLever `json:\"safetyLever\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -15891,7 +15891,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Evaluations map[string][]MonitorEvaluation `json:\"evaluations\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`"
],
"version": 1
@@ -15948,7 +15948,7 @@
"backendSnapshot.Aliases map[string][]string `json:\"aliases\"`",
"backendSnapshot.CreateFileSystemTokens map[string]fsCreateTokenEntry `json:\"createFileSystemTokens\"`",
"backendSnapshot.SharedVpcEnabled string `json:\"sharedVpcEnabled\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"fsCreateTokenEntry.CreatedAt time.Time `json:\"createdAt,omitzero\"`",
"fsCreateTokenEntry.FileSystemID string `json:\"fileSystemId\"`",
@@ -16044,16 +16044,16 @@
"storedFileSystem.WeeklyMaintenanceStartTime string `json:\"weeklyMaintenanceStartTime,omitempty\"`",
"storedLustreExtras.AutoImportPolicy string `json:\"autoImportPolicy,omitempty\"`",
"storedLustreExtras.DataCompressionType string `json:\"dataCompression,omitempty\"`",
- "storedLustreExtras.DataReadCacheConfiguration json.RawMessage `json:\"dataReadCache,omitempty\"`",
+ "storedLustreExtras.DataReadCacheConfiguration jsontext.Value `json:\"dataReadCache,omitempty\"`",
"storedLustreExtras.DriveCacheType string `json:\"driveCache,omitempty\"`",
"storedLustreExtras.EfaEnabled bool `json:\"efa,omitempty\"`",
"storedLustreExtras.ExportPath string `json:\"exportPath,omitempty\"`",
"storedLustreExtras.ImportPath string `json:\"importPath,omitempty\"`",
"storedLustreExtras.ImportedFileChunkSize int32 `json:\"importedChunk,omitempty\"`",
- "storedLustreExtras.LogConfiguration json.RawMessage `json:\"log,omitempty\"`",
- "storedLustreExtras.MetadataConfiguration json.RawMessage `json:\"metadata,omitempty\"`",
+ "storedLustreExtras.LogConfiguration jsontext.Value `json:\"log,omitempty\"`",
+ "storedLustreExtras.MetadataConfiguration jsontext.Value `json:\"metadata,omitempty\"`",
"storedLustreExtras.PerUnitStorageThroughput int32 `json:\"perUnitThroughput,omitempty\"`",
- "storedLustreExtras.RootSquashConfiguration json.RawMessage `json:\"rootSquash,omitempty\"`",
+ "storedLustreExtras.RootSquashConfiguration jsontext.Value `json:\"rootSquash,omitempty\"`",
"storedS3AccessPoint.Alias string `json:\"alias\"`",
"storedS3AccessPoint.CreationTime time.Time `json:\"creationTime\"`",
"storedS3AccessPoint.Lifecycle string `json:\"lifecycle\"`",
@@ -16119,8 +16119,8 @@
"opRec.Target string `json:\"target\"`",
"opRec.Verb string `json:\"verb\"`",
"recSnap.Life life `json:\"life\"`",
- "recSnap.Policy json.RawMessage `json:\"policy,omitempty\"`",
- "recSnap.Resource json.RawMessage `json:\"resource\"`"
+ "recSnap.Policy jsontext.Value `json:\"policy,omitempty\"`",
+ "recSnap.Resource jsontext.Value `json:\"resource\"`"
],
"version": 1
},
@@ -16131,20 +16131,20 @@
"backendSnapshot.GenericVers map[string]*resMeta `json:\"genericVersions,omitempty\"`",
"backendSnapshot.Ledger ociregistry.Ledger `json:\"ledger\"`",
"backendSnapshot.Meta map[string]*resMeta `json:\"meta,omitempty\"`",
- "backendSnapshot.Operations map[string]json.RawMessage `json:\"operations\"`",
+ "backendSnapshot.Operations map[string]jsontext.Value `json:\"operations\"`",
"backendSnapshot.PackageTags map[string]map[string]string `json:\"packageTags,omitempty\"`",
"backendSnapshot.Repositories map[string]repoSnap `json:\"repositories\"`",
- "backendSnapshot.Rules map[string]json.RawMessage `json:\"rules,omitempty\"`",
- "backendSnapshot.Settings map[string]json.RawMessage `json:\"projectSettings,omitempty\"`",
- "backendSnapshot.VPCSC map[string]json.RawMessage `json:\"vpcscConfigs,omitempty\"`",
+ "backendSnapshot.Rules map[string]jsontext.Value `json:\"rules,omitempty\"`",
+ "backendSnapshot.Settings map[string]jsontext.Value `json:\"projectSettings,omitempty\"`",
+ "backendSnapshot.VPCSC map[string]jsontext.Value `json:\"vpcscConfigs,omitempty\"`",
"genFile.Annotations map[string]string `json:\"annotations,omitempty\"`",
"genFile.Created time.Time `json:\"created\"`",
"genFile.Data []byte `json:\"data\"`",
"genFile.Doc []byte `json:\"doc,omitempty\"`",
"genFile.Owner string `json:\"owner\"`",
"genFile.Updated time.Time `json:\"updated\"`",
- "repoSnap.IAM json.RawMessage `json:\"iam,omitempty\"`",
- "repoSnap.Repository json.RawMessage `json:\"repository\"`",
+ "repoSnap.IAM jsontext.Value `json:\"iam,omitempty\"`",
+ "repoSnap.Repository jsontext.Value `json:\"repository\"`",
"resMeta.Annotations map[string]string `json:\"annotations,omitempty\"`",
"resMeta.Created time.Time `json:\"created\"`",
"resMeta.Description string `json:\"description,omitempty\"`",
@@ -16177,7 +16177,7 @@
"jobRec.TaskRun int64 `json:\"taskRun\"`",
"jobRec.Tasks []*taskRec `json:\"tasks\"`",
"jobRec.pb *batchpb.Job",
- "jobSnap.Job json.RawMessage `json:\"job\"`",
+ "jobSnap.Job jsontext.Value `json:\"job\"`",
"jobSnap.Rec *jobRec `json:\"rec\"`",
"opRec.Created time.Time `json:\"created\"`",
"opRec.EndAt time.Time `json:\"endAt\"`",
@@ -16240,10 +16240,10 @@
"gcpbigtable": {
"fields": [
"backendSnapshot.Instances map[string]instSnap `json:\"instances\"`",
- "backendSnapshot.Operations map[string]json.RawMessage `json:\"operations\"`",
+ "backendSnapshot.Operations map[string]jsontext.Value `json:\"operations\"`",
"backendSnapshot.Seq uint64 `json:\"seq\"`",
- "backupSnap.Backup json.RawMessage `json:\"backup\"`",
- "backupSnap.Policy json.RawMessage `json:\"policy,omitempty\"`",
+ "backupSnap.Backup jsontext.Value `json:\"backup\"`",
+ "backupSnap.Policy jsontext.Value `json:\"policy,omitempty\"`",
"backupSnap.Table tableSnap `json:\"table\"`",
"cellSnap.TS int64 `json:\"ts\"`",
"cellSnap.Val []byte `json:\"val,omitempty\"`",
@@ -16251,43 +16251,43 @@
"columnSnap.Family string `json:\"family\"`",
"columnSnap.Qualifier []byte `json:\"qualifier,omitempty\"`",
"instSnap.Backups map[string]backupSnap `json:\"backups,omitempty\"`",
- "instSnap.Clusters map[string]json.RawMessage `json:\"clusters,omitempty\"`",
- "instSnap.Instance json.RawMessage `json:\"instance\"`",
- "instSnap.Policy json.RawMessage `json:\"policy,omitempty\"`",
- "instSnap.Profiles map[string]json.RawMessage `json:\"profiles,omitempty\"`",
+ "instSnap.Clusters map[string]jsontext.Value `json:\"clusters,omitempty\"`",
+ "instSnap.Instance jsontext.Value `json:\"instance\"`",
+ "instSnap.Policy jsontext.Value `json:\"policy,omitempty\"`",
+ "instSnap.Profiles map[string]jsontext.Value `json:\"profiles,omitempty\"`",
"instSnap.Tables map[string]tableSnap `json:\"tables,omitempty\"`",
"rowSnap.Columns []columnSnap `json:\"columns\"`",
"rowSnap.Key []byte `json:\"key\"`",
- "tableSnap.Policy json.RawMessage `json:\"policy,omitempty\"`",
+ "tableSnap.Policy jsontext.Value `json:\"policy,omitempty\"`",
"tableSnap.Rows []rowSnap `json:\"rows,omitempty\"`",
"tableSnap.Splits [][]byte `json:\"splits,omitempty\"`",
- "tableSnap.Table json.RawMessage `json:\"table\"`"
+ "tableSnap.Table jsontext.Value `json:\"table\"`"
],
"version": 1
},
"gcpcloudbuild": {
"fields": [
"backendSnapshot.Builds map[string]buildSnap `json:\"builds\"`",
- "backendSnapshot.Operations map[string]json.RawMessage `json:\"operations\"`",
- "backendSnapshot.Pools map[string]json.RawMessage `json:\"workerPools\"`",
- "backendSnapshot.Triggers map[string]json.RawMessage `json:\"triggers\"`",
- "buildSnap.Build json.RawMessage `json:\"build\"`",
+ "backendSnapshot.Operations map[string]jsontext.Value `json:\"operations\"`",
+ "backendSnapshot.Pools map[string]jsontext.Value `json:\"workerPools\"`",
+ "backendSnapshot.Triggers map[string]jsontext.Value `json:\"triggers\"`",
+ "buildSnap.Build jsontext.Value `json:\"build\"`",
"buildSnap.OpName string `json:\"opName\"`",
- "buildSnap.Orig json.RawMessage `json:\"orig\"`"
+ "buildSnap.Orig jsontext.Value `json:\"orig\"`"
],
"version": 1
},
"gcpcloudtasks": {
"fields": [
- "backendSnapshot.Cmek map[string]json.RawMessage `json:\"cmek\"`",
+ "backendSnapshot.Cmek map[string]jsontext.Value `json:\"cmek\"`",
"backendSnapshot.EtagSeq uint64 `json:\"etagSeq\"`",
- "backendSnapshot.Ops map[string]json.RawMessage `json:\"operations,omitempty\"`",
- "backendSnapshot.Policies map[string]json.RawMessage `json:\"policies\"`",
+ "backendSnapshot.Ops map[string]jsontext.Value `json:\"operations,omitempty\"`",
+ "backendSnapshot.Policies map[string]jsontext.Value `json:\"policies\"`",
"backendSnapshot.Queues map[string]queueSnap `json:\"queues\"`",
"backendSnapshot.TaskSeq uint64 `json:\"taskSeq\"`",
"backendSnapshot.Tombstones map[string]time.Time `json:\"tombstones\"`",
- "queueSnap.Queue json.RawMessage `json:\"queue\"`",
- "queueSnap.Tasks map[string]json.RawMessage `json:\"tasks,omitempty\"`"
+ "queueSnap.Queue jsontext.Value `json:\"queue\"`",
+ "queueSnap.Tasks map[string]jsontext.Value `json:\"tasks,omitempty\"`"
],
"version": 1
},
@@ -16353,7 +16353,7 @@
"backendSnapshot.Clusters map[string]clusterSnap `json:\"clusters\"`",
"backendSnapshot.Ops map[string]opSnap `json:\"operations\"`",
"backendSnapshot.Seq uint64 `json:\"seq\"`",
- "clusterSnap.Cluster json.RawMessage `json:\"cluster\"`",
+ "clusterSnap.Cluster jsontext.Value `json:\"cluster\"`",
"clusterSnap.DeleteOp string `json:\"deleteOp\"`",
"clusterSnap.Failure string `json:\"failure\"`",
"clusterSnap.Pools []poolSnap `json:\"pools\"`",
@@ -16366,11 +16366,11 @@
"opSnap.Kind opKind `json:\"kind\"`",
"opSnap.Location string `json:\"location\"`",
"opSnap.Manual bool `json:\"manual\"`",
- "opSnap.Op json.RawMessage `json:\"op\"`",
+ "opSnap.Op jsontext.Value `json:\"op\"`",
"opSnap.Pool string `json:\"pool\"`",
"opSnap.Project string `json:\"project\"`",
"poolSnap.DeleteOp string `json:\"deleteOp\"`",
- "poolSnap.Pool json.RawMessage `json:\"pool\"`",
+ "poolSnap.Pool jsontext.Value `json:\"pool\"`",
"poolSnap.PrevVersion string `json:\"prevVersion\"`",
"poolSnap.Size int32 `json:\"size\"`",
"rotationRec.Credentials bool",
@@ -16381,13 +16381,13 @@
"gcpdatastore": {
"fields": [
"backendSnapshot.Entities map[string]entitySnap `json:\"entities\"`",
- "backendSnapshot.Indexes map[string]json.RawMessage `json:\"indexes\"`",
+ "backendSnapshot.Indexes map[string]jsontext.Value `json:\"indexes\"`",
"backendSnapshot.LastCommit time.Time `json:\"lastCommit\"`",
"backendSnapshot.NextIndex int64 `json:\"nextIndex\"`",
- "backendSnapshot.Operations map[string]json.RawMessage `json:\"operations\"`",
+ "backendSnapshot.Operations map[string]jsontext.Value `json:\"operations\"`",
"backendSnapshot.UsedIDs map[string][]int64 `json:\"usedIds\"`",
"entitySnap.Created time.Time `json:\"created\"`",
- "entitySnap.Entity json.RawMessage `json:\"entity\"`",
+ "entitySnap.Entity jsontext.Value `json:\"entity\"`",
"entitySnap.Updated time.Time `json:\"updated\"`"
],
"version": 1
@@ -16411,22 +16411,22 @@
"gcpeventarc": {
"fields": [
"backendSnapshot.Channels map[string]channelSnap `json:\"channels\"`",
- "backendSnapshot.Configs map[string]json.RawMessage `json:\"configs\"`",
- "backendSnapshot.Connections map[string]json.RawMessage `json:\"connections\"`",
- "backendSnapshot.Operations map[string]json.RawMessage `json:\"operations\"`",
+ "backendSnapshot.Configs map[string]jsontext.Value `json:\"configs\"`",
+ "backendSnapshot.Connections map[string]jsontext.Value `json:\"connections\"`",
+ "backendSnapshot.Operations map[string]jsontext.Value `json:\"operations\"`",
"backendSnapshot.Seq uint64 `json:\"seq\"`",
"backendSnapshot.Triggers map[string]triggerSnap `json:\"triggers\"`",
- "channelSnap.Channel json.RawMessage `json:\"channel\"`",
- "channelSnap.Policy json.RawMessage `json:\"policy,omitempty\"`",
+ "channelSnap.Channel jsontext.Value `json:\"channel\"`",
+ "channelSnap.Policy jsontext.Value `json:\"policy,omitempty\"`",
"triggerSnap.OwnedTopic bool `json:\"ownedTopic,omitempty\"`",
- "triggerSnap.Policy json.RawMessage `json:\"policy,omitempty\"`",
- "triggerSnap.Trigger json.RawMessage `json:\"trigger\"`"
+ "triggerSnap.Policy jsontext.Value `json:\"policy,omitempty\"`",
+ "triggerSnap.Trigger jsontext.Value `json:\"trigger\"`"
],
"version": 1
},
"gcpfirestore": {
"fields": [
- "backendSnapshot.Docs map[string]json.RawMessage `json:\"docs\"`",
+ "backendSnapshot.Docs map[string]jsontext.Value `json:\"docs\"`",
"backendSnapshot.LastCommit time.Time `json:\"lastCommit\"`"
],
"version": 1
@@ -16434,10 +16434,10 @@
"gcpfunctions": {
"fields": [
"backendSnapshot.Functions map[string]fnSnap `json:\"functions\"`",
- "backendSnapshot.Operations map[string]json.RawMessage `json:\"operations\"`",
+ "backendSnapshot.Operations map[string]jsontext.Value `json:\"operations\"`",
"backendSnapshot.Uploads map[string]uploadSnap `json:\"uploads\"`",
"fnSnap.Failure string `json:\"failure\"`",
- "fnSnap.Function json.RawMessage `json:\"function\"`",
+ "fnSnap.Function jsontext.Value `json:\"function\"`",
"fnSnap.RevSeq int `json:\"revSeq\"`",
"fnSnap.SubName string `json:\"subName\"`",
"fnSnap.Zip []byte `json:\"zip\"`",
@@ -16448,20 +16448,20 @@
},
"gcpiam": {
"fields": [
- "accountSnap.Account json.RawMessage `json:\"account\"`",
+ "accountSnap.Account jsontext.Value `json:\"account\"`",
"accountSnap.DeletedAt time.Time `json:\"deletedAt\"`",
"accountSnap.Keys map[string]keySnap `json:\"keys,omitempty\"`",
- "accountSnap.Policy json.RawMessage `json:\"policy,omitempty\"`",
+ "accountSnap.Policy jsontext.Value `json:\"policy,omitempty\"`",
"accountSnap.System *keySnap `json:\"system,omitempty\"`",
"backendSnapshot.Accounts map[string]accountSnap `json:\"accounts\"`",
"backendSnapshot.EtagSeq uint64 `json:\"etagSeq\"`",
"backendSnapshot.Roles map[string]roleSnap `json:\"roles\"`",
"keySnap.Cert []byte `json:\"cert,omitempty\"`",
- "keySnap.Key json.RawMessage `json:\"key\"`",
+ "keySnap.Key jsontext.Value `json:\"key\"`",
"keySnap.Private []byte `json:\"private,omitempty\"`",
"keySnap.Public []byte `json:\"public,omitempty\"`",
"roleSnap.DeletedAt time.Time `json:\"deletedAt\"`",
- "roleSnap.Role json.RawMessage `json:\"role\"`"
+ "roleSnap.Role jsontext.Value `json:\"role\"`"
],
"version": 1
},
@@ -16529,13 +16529,13 @@
"backendSnapshot.EtagSeq uint64 `json:\"etagSeq\"`",
"backendSnapshot.Jobs map[string]jobSnap `json:\"jobs\"`",
"backendSnapshot.Keys map[string]keySnap `json:\"keys\"`",
- "backendSnapshot.Ops map[string]json.RawMessage `json:\"operations,omitempty\"`",
- "backendSnapshot.Policies map[string]json.RawMessage `json:\"policies\"`",
- "backendSnapshot.Retired map[string]json.RawMessage `json:\"retired,omitempty\"`",
- "backendSnapshot.Rings map[string]json.RawMessage `json:\"rings\"`",
- "jobSnap.Job json.RawMessage `json:\"job\"`",
+ "backendSnapshot.Ops map[string]jsontext.Value `json:\"operations,omitempty\"`",
+ "backendSnapshot.Policies map[string]jsontext.Value `json:\"policies\"`",
+ "backendSnapshot.Retired map[string]jsontext.Value `json:\"retired,omitempty\"`",
+ "backendSnapshot.Rings map[string]jsontext.Value `json:\"rings\"`",
+ "jobSnap.Job jsontext.Value `json:\"job\"`",
"jobSnap.Private []byte `json:\"private,omitempty\"`",
- "keySnap.Key json.RawMessage `json:\"key\"`",
+ "keySnap.Key jsontext.Value `json:\"key\"`",
"keySnap.Primary int64 `json:\"primary\"`",
"keySnap.Versions []versionSnap `json:\"versions,omitempty\"`",
"versionSnap.Deleted bool `json:\"deleted,omitempty\"`",
@@ -16546,20 +16546,20 @@
"gcplogging": {
"fields": [
"backendSnapshot.Buckets map[string]bucketSnap `json:\"buckets\"`",
- "backendSnapshot.Cmek map[string]json.RawMessage `json:\"cmek\"`",
- "backendSnapshot.Entries []json.RawMessage `json:\"entries\"`",
- "backendSnapshot.Exclusions map[string]json.RawMessage `json:\"exclusions\"`",
+ "backendSnapshot.Cmek map[string]jsontext.Value `json:\"cmek\"`",
+ "backendSnapshot.Entries []jsontext.Value `json:\"entries\"`",
+ "backendSnapshot.Exclusions map[string]jsontext.Value `json:\"exclusions\"`",
"backendSnapshot.Metrics map[string]metricSnap `json:\"metrics\"`",
- "backendSnapshot.Ops map[string]json.RawMessage `json:\"ops\"`",
+ "backendSnapshot.Ops map[string]jsontext.Value `json:\"ops\"`",
"backendSnapshot.Seeded []string `json:\"seeded\"`",
"backendSnapshot.Seq uint64 `json:\"seq\"`",
- "backendSnapshot.Settings map[string]json.RawMessage `json:\"settings\"`",
- "backendSnapshot.Sinks map[string]json.RawMessage `json:\"sinks\"`",
- "backendSnapshot.Views map[string]json.RawMessage `json:\"views\"`",
- "bucketSnap.Bucket json.RawMessage `json:\"bucket\"`",
+ "backendSnapshot.Settings map[string]jsontext.Value `json:\"settings\"`",
+ "backendSnapshot.Sinks map[string]jsontext.Value `json:\"sinks\"`",
+ "backendSnapshot.Views map[string]jsontext.Value `json:\"views\"`",
+ "bucketSnap.Bucket jsontext.Value `json:\"bucket\"`",
"bucketSnap.DeleteRequested time.Time `json:\"deleteRequested\"`",
"bucketSnap.Entries []int `json:\"entries,omitempty\"`",
- "metricSnap.Metric json.RawMessage `json:\"metric\"`",
+ "metricSnap.Metric jsontext.Value `json:\"metric\"`",
"metricSnap.Series []seriesSnap `json:\"series,omitempty\"`",
"seriesSnap.Buckets []int64 `json:\"buckets,omitempty\"`",
"seriesSnap.Count int64 `json:\"count\"`",
@@ -16589,23 +16589,23 @@
"Incident.State string `json:\"state\"`",
"Incident.Threshold float64 `json:\"threshold\"`",
"backendSnapshot.Channels map[string]channelSnap `json:\"channels\"`",
- "backendSnapshot.Descriptors map[string]json.RawMessage `json:\"descriptors\"`",
- "backendSnapshot.Groups map[string]json.RawMessage `json:\"groups\"`",
+ "backendSnapshot.Descriptors map[string]jsontext.Value `json:\"descriptors\"`",
+ "backendSnapshot.Groups map[string]jsontext.Value `json:\"groups\"`",
"backendSnapshot.Incidents []Incident `json:\"incidents\"`",
- "backendSnapshot.Policies map[string]json.RawMessage `json:\"policies\"`",
- "backendSnapshot.SLOs map[string]json.RawMessage `json:\"slos,omitempty\"`",
+ "backendSnapshot.Policies map[string]jsontext.Value `json:\"policies\"`",
+ "backendSnapshot.SLOs map[string]jsontext.Value `json:\"slos,omitempty\"`",
"backendSnapshot.Series []seriesSnap `json:\"series\"`",
- "backendSnapshot.Services map[string]json.RawMessage `json:\"services,omitempty\"`",
- "backendSnapshot.Snoozes map[string]json.RawMessage `json:\"snoozes,omitempty\"`",
+ "backendSnapshot.Services map[string]jsontext.Value `json:\"services,omitempty\"`",
+ "backendSnapshot.Snoozes map[string]jsontext.Value `json:\"snoozes,omitempty\"`",
"backendSnapshot.Uptime map[string]uptimeSnap `json:\"uptime\"`",
- "channelSnap.Channel json.RawMessage `json:\"channel\"`",
+ "channelSnap.Channel jsontext.Value `json:\"channel\"`",
"channelSnap.Code string `json:\"code,omitempty\"`",
"channelSnap.CodeAt time.Time `json:\"codeAt\"`",
"channelSnap.Sensitive map[string]string `json:\"sensitive,omitempty\"`",
"seriesSnap.Project string `json:\"project\"`",
- "seriesSnap.Series json.RawMessage `json:\"series\"`",
+ "seriesSnap.Series jsontext.Value `json:\"series\"`",
"seriesSnap.Unit string `json:\"unit,omitempty\"`",
- "uptimeSnap.Config json.RawMessage `json:\"config\"`",
+ "uptimeSnap.Config jsontext.Value `json:\"config\"`",
"uptimeSnap.LastRun time.Time `json:\"lastRun\"`"
],
"version": 1
@@ -16614,7 +16614,7 @@
"fields": [
"backendSnapshot.EtagSeq uint64 `json:\"etagSeq,omitempty\"`",
"backendSnapshot.LastMsg int64 `json:\"lastMsg\"`",
- "backendSnapshot.Policies map[string]json.RawMessage `json:\"policies,omitempty\"`",
+ "backendSnapshot.Policies map[string]jsontext.Value `json:\"policies,omitempty\"`",
"backendSnapshot.Schemas map[string]*schemaRec `json:\"schemas\"`",
"backendSnapshot.Snapshots map[string]pbSnapSnap `json:\"snapshots\"`",
"backendSnapshot.Subs map[string]subSnap `json:\"subs\"`",
@@ -16625,7 +16625,7 @@
"message.OrderingKey string `json:\"orderingKey,omitempty\"`",
"message.PublishTime time.Time `json:\"publishTime\"`",
"pbSnapSnap.Created time.Time `json:\"created\"`",
- "pbSnapSnap.Snapshot json.RawMessage `json:\"snapshot\"`",
+ "pbSnapSnap.Snapshot jsontext.Value `json:\"snapshot\"`",
"pbSnapSnap.Unacked []string `json:\"unacked,omitempty\"`",
"schemaRec.Name string `json:\"name\"`",
"schemaRec.Revisions []schemaRevision `json:\"revisions\"`",
@@ -16645,18 +16645,18 @@
"subMsg.owner string",
"subSnap.LastActive time.Time `json:\"lastActive\"`",
"subSnap.Msgs []*subMsg `json:\"msgs,omitempty\"`",
- "subSnap.Sub json.RawMessage `json:\"sub\"`",
+ "subSnap.Sub jsontext.Value `json:\"sub\"`",
"topicSnap.Retained []*message `json:\"retained,omitempty\"`",
- "topicSnap.Topic json.RawMessage `json:\"topic\"`"
+ "topicSnap.Topic jsontext.Value `json:\"topic\"`"
],
"version": 1
},
"gcpredis": {
"fields": [
"backendSnapshot.Instances map[string]instSnap `json:\"instances\"`",
- "backendSnapshot.Operations map[string]json.RawMessage `json:\"operations\"`",
+ "backendSnapshot.Operations map[string]jsontext.Value `json:\"operations\"`",
"instSnap.AuthString string `json:\"authString,omitempty\"`",
- "instSnap.Instance json.RawMessage `json:\"instance\"`",
+ "instSnap.Instance jsontext.Value `json:\"instance\"`",
"instSnap.Keyspace []byte `json:\"keyspace,omitempty\"`"
],
"version": 1
@@ -16666,8 +16666,8 @@
"backendSnapshot.Bindings map[string]*bindingRec `json:\"tagBindings\"`",
"backendSnapshot.Enabled map[string]map[string]time.Time `json:\"enabledServices\"`",
"backendSnapshot.Folders map[string]*folderRec `json:\"folders\"`",
- "backendSnapshot.Ops map[string]json.RawMessage `json:\"operations\"`",
- "backendSnapshot.Policies map[string]json.RawMessage `json:\"policies\"`",
+ "backendSnapshot.Ops map[string]jsontext.Value `json:\"operations\"`",
+ "backendSnapshot.Policies map[string]jsontext.Value `json:\"policies\"`",
"backendSnapshot.PolicySeq uint64 `json:\"policySeq\"`",
"backendSnapshot.Projects map[string]*projectRec `json:\"projects\"`",
"backendSnapshot.Seed string `json:\"seed\"`",
@@ -16702,25 +16702,25 @@
},
"gcprun": {
"fields": [
- "backendSnapshot.DeletedExe map[string]json.RawMessage `json:\"deletedExecutions\"`",
- "backendSnapshot.DeletedJob map[string]json.RawMessage `json:\"deletedJobs\"`",
- "backendSnapshot.DeletedSvc map[string]json.RawMessage `json:\"deletedServices\"`",
- "backendSnapshot.Executions map[string]json.RawMessage `json:\"executions\"`",
- "backendSnapshot.IAM map[string]json.RawMessage `json:\"iamPolicies\"`",
+ "backendSnapshot.DeletedExe map[string]jsontext.Value `json:\"deletedExecutions\"`",
+ "backendSnapshot.DeletedJob map[string]jsontext.Value `json:\"deletedJobs\"`",
+ "backendSnapshot.DeletedSvc map[string]jsontext.Value `json:\"deletedServices\"`",
+ "backendSnapshot.Executions map[string]jsontext.Value `json:\"executions\"`",
+ "backendSnapshot.IAM map[string]jsontext.Value `json:\"iamPolicies\"`",
"backendSnapshot.IAMSeq uint64 `json:\"iamSeq\"`",
- "backendSnapshot.Jobs map[string]json.RawMessage `json:\"jobs\"`",
- "backendSnapshot.Operations map[string]json.RawMessage `json:\"operations\"`",
+ "backendSnapshot.Jobs map[string]jsontext.Value `json:\"jobs\"`",
+ "backendSnapshot.Operations map[string]jsontext.Value `json:\"operations\"`",
"backendSnapshot.Revisions map[string]revSnap `json:\"revisions\"`",
"backendSnapshot.Services map[string]svcSnap `json:\"services\"`",
"backendSnapshot.Tasks map[string]taskSnap `json:\"tasks\"`",
- "revSnap.Revision json.RawMessage `json:\"revision\"`",
+ "revSnap.Revision jsontext.Value `json:\"revision\"`",
"revSnap.Seq int `json:\"seq\"`",
"revSnap.StartErr string `json:\"startErr\"`",
"svcSnap.Failure string `json:\"failure\"`",
"svcSnap.RevSeq int `json:\"revSeq\"`",
- "svcSnap.Service json.RawMessage `json:\"service\"`",
+ "svcSnap.Service jsontext.Value `json:\"service\"`",
"taskSnap.State string `json:\"state\"`",
- "taskSnap.Task json.RawMessage `json:\"task\"`"
+ "taskSnap.Task jsontext.Value `json:\"task\"`"
],
"version": 1
},
@@ -16730,7 +16730,7 @@
"backendSnapshot.Jobs map[string]jobSnap `json:\"jobs\"`",
"jobSnap.ExecStart time.Time `json:\"execStart\"`",
"jobSnap.Failures int32 `json:\"failures\"`",
- "jobSnap.Job json.RawMessage `json:\"job\"`",
+ "jobSnap.Job jsontext.Value `json:\"job\"`",
"jobSnap.RetryAt time.Time `json:\"retryAt\"`"
],
"version": 1
@@ -16740,8 +16740,8 @@
"backendSnapshot.EtagSeq uint64 `json:\"etagSeq\"`",
"backendSnapshot.Secrets map[string]secretSnap `json:\"secrets\"`",
"secretSnap.NextVer int64 `json:\"nextVer\"`",
- "secretSnap.Policy json.RawMessage `json:\"policy,omitempty\"`",
- "secretSnap.Secret json.RawMessage `json:\"secret\"`",
+ "secretSnap.Policy jsontext.Value `json:\"policy,omitempty\"`",
+ "secretSnap.Secret jsontext.Value `json:\"secret\"`",
"secretSnap.Versions []versionSnap `json:\"versions,omitempty\"`",
"versionSnap.Data []byte `json:\"data,omitempty\"`",
"versionSnap.Num int64 `json:\"num\"`"
@@ -16752,12 +16752,12 @@
"fields": [
"backendSnapshot.EtagSeq uint64 `json:\"etagSeq\"`",
"backendSnapshot.Instances map[string]instSnap `json:\"instances\"`",
- "backendSnapshot.Operations map[string]json.RawMessage `json:\"operations\"`",
+ "backendSnapshot.Operations map[string]jsontext.Value `json:\"operations\"`",
"dbSnap.DDL []string `json:\"ddl,omitempty\"`",
- "dbSnap.Database json.RawMessage `json:\"database\"`",
+ "dbSnap.Database jsontext.Value `json:\"database\"`",
"instSnap.Databases map[string]dbSnap `json:\"databases,omitempty\"`",
- "instSnap.Instance json.RawMessage `json:\"instance\"`",
- "instSnap.Policy json.RawMessage `json:\"policy,omitempty\"`"
+ "instSnap.Instance jsontext.Value `json:\"instance\"`",
+ "instSnap.Policy jsontext.Value `json:\"policy,omitempty\"`"
],
"version": 1
},
@@ -16901,7 +16901,7 @@
"ACLEntry.Entity string `json:\"entity\"`",
"ACLEntry.Role string `json:\"role\"`",
"Bucket.ACL []ACLEntry `json:\"acl,omitempty\"`",
- "Bucket.Config map[string]json.RawMessage `json:\"config,omitempty\"`",
+ "Bucket.Config map[string]jsontext.Value `json:\"config,omitempty\"`",
"Bucket.Created time.Time `json:\"created\"`",
"Bucket.DefaultEventBasedHold bool `json:\"defaultEventBasedHold,omitempty\"`",
"Bucket.DefaultObjectACL []ACLEntry `json:\"defaultObjectAcl,omitempty\"`",
@@ -16915,7 +16915,7 @@
"Bucket.StorageClass string `json:\"storageClass\"`",
"Bucket.Updated time.Time `json:\"updated\"`",
"Bucket.Versioning bool `json:\"versioning,omitempty\"`",
- "IAMBinding.Condition json.RawMessage `json:\"condition,omitempty\"`",
+ "IAMBinding.Condition jsontext.Value `json:\"condition,omitempty\"`",
"IAMBinding.Members []string `json:\"members\"`",
"IAMBinding.Role string `json:\"role\"`",
"IAMPolicy.Bindings []IAMBinding `json:\"bindings\"`",
@@ -16949,10 +16949,10 @@
},
"gcpworkflows": {
"fields": [
- "backendSnapshot.Executions map[string]json.RawMessage `json:\"executions\"`",
- "backendSnapshot.Operations map[string]json.RawMessage `json:\"operations\"`",
+ "backendSnapshot.Executions map[string]jsontext.Value `json:\"executions\"`",
+ "backendSnapshot.Operations map[string]jsontext.Value `json:\"operations\"`",
"backendSnapshot.Workflows map[string]workflowSnap `json:\"workflows\"`",
- "workflowSnap.Revisions []json.RawMessage `json:\"revisions\"`"
+ "workflowSnap.Revisions []jsontext.Value `json:\"revisions\"`"
],
"version": 1
},
@@ -17027,7 +17027,7 @@
"backendSnapshot.DataRetrievalPolicies map[string]string `json:\"dataRetrievalPolicies,omitempty\"`",
"backendSnapshot.MultipartParts []multipartPartSnapshot `json:\"multipartParts\"`",
"backendSnapshot.ProvisionedCapacity []provisionedCapacitySnapshot `json:\"provisionedCapacity\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"csvInputDTO.Comments string `json:\"Comments,omitempty\"`",
"csvInputDTO.FieldDelimiter string `json:\"FieldDelimiter,omitempty\"`",
"csvInputDTO.FileHeaderInfo string `json:\"FileHeaderInfo,omitempty\"`",
@@ -17156,7 +17156,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.Seq int64 `json:\"seq\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -17551,7 +17551,7 @@
"JSONClassifier.JSONPath string `json:\"JsonPath\"`",
"JSONClassifier.Name string `json:\"Name\"`",
"Job.ARN string `json:\"Arn,omitempty\"`",
- "Job.CodeGenConfigurationNodes map[string]json.RawMessage `json:\"CodeGenConfigurationNodes,omitempty\"`",
+ "Job.CodeGenConfigurationNodes map[string]jsontext.Value `json:\"CodeGenConfigurationNodes,omitempty\"`",
"Job.Command JobCommand `json:\"Command,omitzero\"`",
"Job.Connections ConnectionsList `json:\"Connections,omitzero\"`",
"Job.CreatedOn float64 `json:\"CreatedOn,omitempty\"`",
@@ -17924,7 +17924,7 @@
"backendSnapshot.SchemaVersions map[string][]*SchemaVersion `json:\"schemaVersions\"`",
"backendSnapshot.SessionStatements map[string][]*Statement `json:\"sessionStatements\"`",
"backendSnapshot.TableColumnStats map[string]*ColumnStatistics `json:\"tableColumnStats\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.WorkflowRuns map[string][]*WorkflowRun `json:\"workflowRuns\"`",
"resourcePolicyEntry.CreateTime float64 `json:\"CreateTime,omitempty\"`",
"resourcePolicyEntry.EnableHybrid string `json:\"EnableHybrid,omitempty\"`",
@@ -18017,7 +18017,7 @@
"backendSnapshot.NextServiceAccountID uint64 `json:\"nextServiceAccountId\"`",
"backendSnapshot.NextTokenID uint64 `json:\"nextTokenId\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -18201,7 +18201,7 @@
"backendSnapshot.AccountID string `json:\"accountId\"`",
"backendSnapshot.MemberSeq int64 `json:\"memberSeq\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`"
],
"version": 2
@@ -18401,7 +18401,7 @@
"backendSnapshot.RoleByARN map[string]string `json:\"roleByARN,omitempty\"`",
"backendSnapshot.RoleInlinePolicies map[string]map[string]string `json:\"roleInlinePolicies,omitempty\"`",
"backendSnapshot.RolePolicies map[string][]string `json:\"rolePolicies,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.UserInlinePolicies map[string]map[string]string `json:\"userInlinePolicies,omitempty\"`",
"backendSnapshot.UserPolicies map[string][]string `json:\"userPolicies,omitempty\"`",
"comprehensiveSnapshot.AccessAdvisorJobs map[string]*accessAdvisorJob `json:\"accessAdvisorJobs,omitempty\"`",
@@ -18409,8 +18409,8 @@
"comprehensiveSnapshot.OrgReportJobs map[string]time.Time `json:\"orgReportJobs,omitempty\"`",
"comprehensiveSnapshot.SSHPublicKeys map[string]SSHPublicKey `json:\"sshPublicKeys,omitempty\"`",
"comprehensiveSnapshot.ServiceLastAccessed map[string]map[string]ServiceLastAccessedDetail `json:\"serviceLastAccessed,omitempty\"`",
- "handlerSnapshot.Accounts map[string]json.RawMessage `json:\"accounts,omitempty\"`",
- "handlerSnapshot.Backend json.RawMessage `json:\"backend,omitempty\"`",
+ "handlerSnapshot.Accounts map[string]jsontext.Value `json:\"accounts,omitempty\"`",
+ "handlerSnapshot.Backend jsontext.Value `json:\"backend,omitempty\"`",
"handlerSnapshot.Tags map[string]map[string]string `json:\"tags,omitempty\"`",
"policyAttachmentRefs.groups map[string]struct{}",
"policyAttachmentRefs.roles map[string]struct{}",
@@ -18498,7 +18498,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Counter int `json:\"counter\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -18762,7 +18762,7 @@
"backendSnapshot.OrgConfig OrgConfiguration `json:\"orgConfig\"`",
"backendSnapshot.OrgEc2Config OrgEc2DeepInspectionConfig `json:\"orgEc2Config\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"codeSnippet.EndLine int32 `json:\"endLine,omitempty\"`",
"codeSnippet.FindingArn string `json:\"findingArn\"`",
@@ -19230,7 +19230,7 @@
"RoleAlias.RoleAliasARN string `json:\"roleAliasArn\"`",
"RoleAlias.Tags map[string]string `json:\"tags,omitempty\"`",
"RuleAction.Lambda *LambdaAction `json:\"lambda,omitempty\"`",
- "RuleAction.Other map[string]json.RawMessage `json:\"-\"`",
+ "RuleAction.Other map[string]jsontext.Value `json:\"-\"`",
"RuleAction.SNS *SNSAction `json:\"sns,omitempty\"`",
"RuleAction.SQS *SQSAction `json:\"sqs,omitempty\"`",
"RuleAction.{MarshalJSON}",
@@ -19428,7 +19428,7 @@
"backendSnapshot.ResourceTags map[string]map[string]string `json:\"resourceTags\"`",
"backendSnapshot.SbomValidationResults map[string][]*SbomValidationResult `json:\"sbomValidationResults\"`",
"backendSnapshot.SecurityProfileTargets map[string][]string `json:\"securityProfileTargets\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.ThingBillingGroups map[string]string `json:\"thingBillingGroups\"`",
"backendSnapshot.ThingConnectivity map[string]*ThingConnectivityData `json:\"thingConnectivity\"`",
"backendSnapshot.ThingGroupIndexingConfiguration *ThingGroupIndexingConfiguration `json:\"thingGroupIndexingConfiguration\"`",
@@ -19650,7 +19650,7 @@
"backendSnapshot.ChannelMessages map[string][]ChannelMessage `json:\"channelMessages\"`",
"backendSnapshot.DatasetContents map[string][]*DatasetContent `json:\"datasetContents\"`",
"backendSnapshot.LoggingOptions *LoggingOptions `json:\"loggingOptions\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`"
],
"version": 2
@@ -19662,7 +19662,7 @@
"RetainedMessage.Qos int32",
"RetainedMessage.Topic string",
"RetainedMessage.UserProperties []byte",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"connectionEntry.clientID string",
"connectionEntry.connectedAt time.Time",
"connectionEntry.disconnectReason string",
@@ -19674,19 +19674,19 @@
"connectionEntrySnap.DisconnectedAt time.Time `json:\"disconnectedAt,omitzero\"`",
"connectionEntrySnap.SourceIP string `json:\"sourceIp,omitempty\"`",
"shadowEntry.deleted bool",
- "shadowEntry.desired map[string]json.RawMessage",
+ "shadowEntry.desired map[string]jsontext.Value",
"shadowEntry.metaDesired map[string]int64",
"shadowEntry.metaReported map[string]int64",
- "shadowEntry.reported map[string]json.RawMessage",
+ "shadowEntry.reported map[string]jsontext.Value",
"shadowEntry.shadowName string",
"shadowEntry.thingName string",
"shadowEntry.updatedAt time.Time",
"shadowEntry.version int",
"shadowEntrySnap.Deleted bool `json:\"deleted,omitempty\"`",
- "shadowEntrySnap.Desired map[string]json.RawMessage `json:\"desired,omitempty\"`",
+ "shadowEntrySnap.Desired map[string]jsontext.Value `json:\"desired,omitempty\"`",
"shadowEntrySnap.MetaDesired map[string]int64 `json:\"metaDesired,omitempty\"`",
"shadowEntrySnap.MetaReported map[string]int64 `json:\"metaReported,omitempty\"`",
- "shadowEntrySnap.Reported map[string]json.RawMessage `json:\"reported,omitempty\"`",
+ "shadowEntrySnap.Reported map[string]jsontext.Value `json:\"reported,omitempty\"`",
"shadowEntrySnap.ShadowName string `json:\"shadowName\"`",
"shadowEntrySnap.ThingName string `json:\"thingName\"`",
"shadowEntrySnap.UpdatedAt time.Time `json:\"updatedAt\"`"
@@ -19974,7 +19974,7 @@
"backendSnapshot.QueuedMessages map[string][]QueuedMessage `json:\"queuedMessages,omitempty\"`",
"backendSnapshot.ResourceLogLevels map[string]string `json:\"resourceLogLevels,omitempty\"`",
"backendSnapshot.ResourceTags map[string]map[string]string `json:\"resourceTags,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.WirelessDeviceThings map[string]string `json:\"wirelessDeviceThings,omitempty\"`",
"backendSnapshot.WirelessGatewayCerts map[string]string `json:\"wirelessGatewayCerts,omitempty\"`",
"backendSnapshot.WirelessGatewayThings map[string]string `json:\"wirelessGatewayThings,omitempty\"`",
@@ -20273,7 +20273,7 @@
"backendSnapshot.PolicyVersions map[string]string `json:\"clusterPolicyVersions,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ScramSecrets map[string][]string `json:\"scramSecrets\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -20366,7 +20366,7 @@
"WorkerLogDelivery.CloudWatchLogs *CloudWatchLogsDelivery",
"WorkerLogDelivery.Firehose *FirehoseDelivery",
"WorkerLogDelivery.S3 *S3LogDelivery",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -20469,7 +20469,7 @@
"backendSnapshot.OnDemandStreamCountLimit int `json:\"onDemandStreamCountLimit,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ResourcePolicies map[string]map[string]string `json:\"resourcePolicies,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"channelBufferSnapshot.Failed []failedChannelRecordSnapshot `json:\"failed,omitempty\"`",
"channelBufferSnapshot.Records [][]byte `json:\"records,omitempty\"`",
"channelFlushSnapshot.channel *Channel",
@@ -20565,7 +20565,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.NextID int64 `json:\"next_id\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -20717,7 +20717,7 @@
"ZeppelinMonitoringConfigDesc.LogLevel string `json:\"LogLevel\"`",
"backendSnapshot.FlinkStopSaves map[string]persistedStopSave `json:\"flink_stop_saves,omitempty\"`",
"backendSnapshot.NextID int64 `json:\"next_id\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"persistedApplication.ApplicationARN string `json:\"ApplicationARN\"`",
"persistedApplication.ApplicationDescription string `json:\"ApplicationDescription,omitempty\"`",
"persistedApplication.ApplicationMode string `json:\"ApplicationMode,omitempty\"`",
@@ -20822,7 +20822,7 @@
"Stream.PendingUntil time.Time",
"Stream.Status string",
"Stream.Tags map[string]string",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -20904,8 +20904,8 @@
"backendSnapshot.PendingMaterials map[string]map[string]serializedKeyMaterial `json:\"pending_key_materials,omitempty\"`",
"backendSnapshot.Policies map[string]map[string]string `json:\"policies\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
- "handlerSnapshot.Backend json.RawMessage `json:\"backend,omitempty\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
+ "handlerSnapshot.Backend jsontext.Value `json:\"backend,omitempty\"`",
"handlerSnapshot.HandlerFormat int `json:\"handlerFormat\"`",
"handlerSnapshot.Tags map[string]map[string]string `json:\"tags,omitempty\"`",
"serializedKeyMaterial.PrivKeyDER []byte `json:\"priv_key_der,omitempty\"`",
@@ -21028,7 +21028,7 @@
"backendSnapshot.Queries map[string]string `json:\"queries,omitempty\"`",
"backendSnapshot.ResourceLFTags map[string][]LFTagPair `json:\"resourceLFTags\"`",
"backendSnapshot.TableStorageOptimizers map[string][]StorageOptimizer `json:\"tableStorageOptimizers,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"transactionInfo.EndTime string `json:\"EndTime,omitempty\"`",
"transactionInfo.ID string `json:\"-\"`",
"transactionInfo.LastExtended string `json:\"LastExtended,omitempty\"`",
@@ -21312,7 +21312,7 @@
"backendSnapshot.Layers map[string][]*LayerVersion `json:\"layers\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ResourcePolicyOverrides map[string]*ResourcePolicyOverride `json:\"resourcePolicyOverrides,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.VersionCounters map[string]int `json:\"versionCounters\"`",
"backendSnapshot.Versions map[string][]*FunctionVersion `json:\"versions\"`",
"functionConfigurationSnapshot.CreatedAt time.Time `json:\"createdAt,omitzero\"`",
@@ -21794,7 +21794,7 @@
"backendSnapshot.NextAccessKeySeq int `json:\"nextAccessKeySeq\"`",
"backendSnapshot.Partner *PartnerProfile `json:\"partner,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.VpcPeered bool `json:\"vpcPeered\"`",
"instanceHealth.Reason string",
"instanceHealth.State string"
@@ -22011,7 +22011,7 @@
"backendSnapshot.RetrievalConfig *RetrievalConfiguration `json:\"retrievalConfig,omitempty\"`",
"backendSnapshot.RevealConfig *RevealConfiguration `json:\"revealConfig,omitempty\"`",
"backendSnapshot.Session *Session `json:\"session,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`"
],
"version": 3
@@ -22126,7 +22126,7 @@
"ProposalVote.proposalID string",
"RemoveAction.MemberID string `json:\"memberId\"`",
"VotingPolicy.ApprovalThresholdPolicy *ApprovalThresholdPolicy `json:\"approvalThresholdPolicy,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"proposalVoteDTO.ProposalID string `json:\"proposalID\"`",
"proposalVoteDTO.Value *ProposalVote `json:\"value\"`"
],
@@ -22147,7 +22147,7 @@
"Value.StringValue *string `json:\"StringValue,omitempty\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -22187,7 +22187,7 @@
"UsageAllocation.Tags []Tag `json:\"Tags,omitempty\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -22295,7 +22295,7 @@
"backendSnapshot.Certificates map[string]struct{} `json:\"certificates\"`",
"backendSnapshot.Policy *Policy `json:\"policy,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"counterSnapshot.Counter queueJobCounter `json:\"counter\"`",
"counterSnapshot.QueueArn string `json:\"queueArn\"`",
@@ -23225,7 +23225,7 @@
"backendSnapshot.AccountKmsKeyID string `json:\"accountKmsKeyId\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ScheduleActions map[string][]*storedScheduleAction `json:\"scheduleActions\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"phase.label string",
"phase.until time.Time",
@@ -23449,7 +23449,7 @@
"StreamSelection.StreamOrder string `json:\"streamOrder,omitempty\"`",
"backendSnapshot.AccountID string `json:\"accountId\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"storedChannel.ARN string `json:\"arn\"`",
"storedChannel.CreatedAt string `json:\"createdAt\"`",
@@ -23637,7 +23637,7 @@
"TimeSignalMessage.SegmentationDescriptors []map[string]any",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"storedChannel.ARN string `json:\"arn\"`",
"storedChannel.Audiences []string `json:\"audiences,omitempty\"`",
@@ -23815,7 +23815,7 @@
"backendSnapshot.ReservedNodes map[string][]*ReservedNode `json:\"reservedNodes\"`",
"backendSnapshot.Snapshots map[string][]*Snapshot `json:\"snapshots\"`",
"backendSnapshot.SubnetGroups map[string][]*SubnetGroup `json:\"subnetGroups\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Users map[string][]*User `json:\"users\"`",
"recurringChargeObject.RecurringChargeAmount float64 `json:\"RecurringChargeAmount,omitempty\"`",
"recurringChargeObject.RecurringChargeFrequency string `json:\"RecurringChargeFrequency,omitempty\"`",
@@ -24222,7 +24222,7 @@
"backendSnapshot.AccountID string `json:\"accountId\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ServiceInitialized bool `json:\"serviceInitialized\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"countPair.CreatedCount int64",
"countPair.ModifiedCount int64",
"ssmDocument.ActionName string",
@@ -24344,7 +24344,7 @@
"WeeklyStartTime.TimeZone string `json:\"timeZone,omitempty\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`"
],
"version": 1
@@ -24418,7 +24418,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Metrics map[string]map[string][]MetricDatum `json:\"metrics\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -24599,11 +24599,11 @@
"backendSnapshot.ClusterParameterOverrides map[string]map[string]ParameterValue `json:\"clusterParameterOverrides\"`",
"backendSnapshot.ClusterRoles map[string]map[string][]string `json:\"clusterRoles\"`",
"backendSnapshot.EventsLog map[string][]Event `json:\"eventsLog\"`",
- "backendSnapshot.OpenCypherGraphs map[string]json.RawMessage `json:\"openCypherGraphs,omitempty\"`",
+ "backendSnapshot.OpenCypherGraphs map[string]jsontext.Value `json:\"openCypherGraphs,omitempty\"`",
"backendSnapshot.ParameterOverrides map[string]map[string]ParameterValue `json:\"parameterOverrides\"`",
"backendSnapshot.PendingMaintenanceActions map[string]map[string]PendingMaintenanceAction `json:\"pendingMaintenanceActions\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string][]Tag `json:\"tags\"`",
"xmlDBClusterSnapshot.AllocatedStorage int `xml:\"AllocatedStorage,omitempty\"`",
"xmlDBClusterSnapshot.ClusterCreateTime string `xml:\"ClusterCreateTime,omitempty\"`",
@@ -24756,7 +24756,7 @@
"analysisReportRecord.Key string `json:\"key\"`",
"analysisReportRecord.ReportTime time.Time `json:\"reportTime\"`",
"analysisReportRecord.Type string `json:\"type\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"containerRecord.ARN string `json:\"arn\"`",
"containerRecord.Configs []ContainerMonitoringConfiguration `json:\"configs\"`",
"containerRecord.DeleteUntil time.Time `json:\"deleteUntil\"`",
@@ -25100,7 +25100,7 @@
"backendSnapshot.AccountID string `json:\"accountId\"`",
"backendSnapshot.OrgStatus *organizationStatus `json:\"orgStatus\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"networkResourceMetadata.Metadata map[string]string",
"networkResourceMetadata.ResourceArn string",
"organizationStatus.AccountStatusList []accountStatus",
@@ -25137,7 +25137,7 @@
"Probe.Tags map[string]string `json:\"tags,omitempty\"`",
"Probe.VpcID string `json:\"vpcId,omitempty\"`",
"backendSnapshot.NextProbeSeq int64 `json:\"next_probe_seq\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -25167,7 +25167,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.Seq int64 `json:\"seq\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -25179,7 +25179,7 @@
"Certificate.Etag string `json:\"etag\"`",
"Certificate.ID string `json:\"id\"`",
"Certificate.Intermediate string `json:\"intermediateCertificates,omitempty\"`",
- "Certificate.Locks json.RawMessage `json:\"locks,omitempty\"`",
+ "Certificate.Locks jsontext.Value `json:\"locks,omitempty\"`",
"Certificate.State string `json:\"state\"`",
"Certificate.SubjectNames []string `json:\"subjectNames\"`",
"Certificate.Tags Tags `json:\"tags\"`",
@@ -25192,15 +25192,15 @@
"Deployment.Etag string `json:\"etag\"`",
"Deployment.GatewayID string `json:\"gatewayId\"`",
"Deployment.ID string `json:\"id\"`",
- "Deployment.Locks json.RawMessage `json:\"locks,omitempty\"`",
+ "Deployment.Locks jsontext.Value `json:\"locks,omitempty\"`",
"Deployment.PathPrefix string `json:\"pathPrefix\"`",
- "Deployment.Spec json.RawMessage `json:\"specification\"`",
+ "Deployment.Spec jsontext.Value `json:\"specification\"`",
"Deployment.State string `json:\"state\"`",
"Deployment.Tags Tags `json:\"tags\"`",
"Deployment.TimeCreated time.Time `json:\"timeCreated\"`",
"Deployment.TimeUpdated time.Time `json:\"timeUpdated\"`",
"Deployment.TransitionAt *time.Time `json:\"transitionAt,omitempty\"`",
- "Gateway.CaBundles json.RawMessage `json:\"caBundles,omitempty\"`",
+ "Gateway.CaBundles jsontext.Value `json:\"caBundles,omitempty\"`",
"Gateway.CertificateID string `json:\"certificateId,omitempty\"`",
"Gateway.CompartmentID string `json:\"compartmentId\"`",
"Gateway.DisplayName string `json:\"displayName\"`",
@@ -25209,9 +25209,9 @@
"Gateway.Hostname string `json:\"hostname\"`",
"Gateway.ID string `json:\"id\"`",
"Gateway.IPMode string `json:\"ipMode,omitempty\"`",
- "Gateway.Locks json.RawMessage `json:\"locks,omitempty\"`",
+ "Gateway.Locks jsontext.Value `json:\"locks,omitempty\"`",
"Gateway.NSGs []string `json:\"networkSecurityGroupIds,omitempty\"`",
- "Gateway.ResponseCache json.RawMessage `json:\"responseCache,omitempty\"`",
+ "Gateway.ResponseCache jsontext.Value `json:\"responseCache,omitempty\"`",
"Gateway.State string `json:\"state\"`",
"Gateway.SubnetID string `json:\"subnetId\"`",
"Gateway.Tags Tags `json:\"tags\"`",
@@ -25356,8 +25356,8 @@
"ClusterOptions.Admission *Admission `json:\"admissionControllerOptions,omitempty\"`",
"ClusterOptions.IPFamilies []string `json:\"ipFamilies,omitempty\"`",
"ClusterOptions.K8sNetwork *NetworkConfig `json:\"kubernetesNetworkConfig,omitempty\"`",
- "ClusterOptions.OIDCAuth json.RawMessage `json:\"openIdConnectTokenAuthenticationConfig,omitempty\"`",
- "ClusterOptions.OIDCDiscovery json.RawMessage `json:\"openIdConnectDiscovery,omitempty\"`",
+ "ClusterOptions.OIDCAuth jsontext.Value `json:\"openIdConnectTokenAuthenticationConfig,omitempty\"`",
+ "ClusterOptions.OIDCDiscovery jsontext.Value `json:\"openIdConnectDiscovery,omitempty\"`",
"ClusterOptions.PersistentVolume *TagCfg `json:\"persistentVolumeConfig,omitempty\"`",
"ClusterOptions.ServiceLb *TagCfg `json:\"serviceLbConfig,omitempty\"`",
"ClusterOptions.ServiceLbSubnetID []string `json:\"serviceLbSubnetIds,omitempty\"`",
@@ -25393,9 +25393,9 @@
"NodeConfig.Size int `json:\"size\"`",
"NodePool.ClusterID string `json:\"clusterId\"`",
"NodePool.CompartmentID string `json:\"compartmentId\"`",
- "NodePool.Cycling json.RawMessage `json:\"cycling,omitempty\"`",
+ "NodePool.Cycling jsontext.Value `json:\"cycling,omitempty\"`",
"NodePool.Etag string `json:\"etag\"`",
- "NodePool.Eviction json.RawMessage `json:\"eviction,omitempty\"`",
+ "NodePool.Eviction jsontext.Value `json:\"eviction,omitempty\"`",
"NodePool.ID string `json:\"id\"`",
"NodePool.ImageName string `json:\"imageName,omitempty\"`",
"NodePool.Labels []KeyValue `json:\"labels,omitempty\"`",
@@ -25420,7 +25420,7 @@
"Placement.AvailabilityDom string `json:\"availabilityDomain\"`",
"Placement.CapacityReserveID string `json:\"capacityReservationId,omitempty\"`",
"Placement.FaultDomains []string `json:\"faultDomains,omitempty\"`",
- "Placement.PreemptibleConfig json.RawMessage `json:\"preemptibleNodeConfig,omitempty\"`",
+ "Placement.PreemptibleConfig jsontext.Value `json:\"preemptibleNodeConfig,omitempty\"`",
"Placement.SubnetID string `json:\"subnetId\"`",
"PodNet.CniType string `json:\"cniType\"`",
"PodNet.MaxPodsPerNode *int `json:\"maxPodsPerNode,omitempty\"`",
@@ -25471,7 +25471,7 @@
"Container.NoResourcePrn bool `json:\"isResourcePrincipalDisabled\"`",
"Container.Resource *ResourceConfig `json:\"resourceConfig,omitempty\"`",
"Container.RestartCount int `json:\"restartCount\"`",
- "Container.Security json.RawMessage `json:\"securityContext,omitempty\"`",
+ "Container.Security jsontext.Value `json:\"securityContext,omitempty\"`",
"Container.State string `json:\"lifecycleState\"`",
"Container.Terminated *time.Time `json:\"timeTerminated,omitempty\"`",
"Container.Updated *time.Time `json:\"timeUpdated,omitempty\"`",
@@ -25511,7 +25511,7 @@
"Instance.PullSecrets []PullSecret `json:\"imagePullSecrets,omitempty\"`",
"Instance.Restart string `json:\"containerRestartPolicy\"`",
"Instance.RetryToken string `json:\"retryToken,omitempty\"`",
- "Instance.Security json.RawMessage `json:\"securityContext,omitempty\"`",
+ "Instance.Security jsontext.Value `json:\"securityContext,omitempty\"`",
"Instance.Shape string `json:\"shape\"`",
"Instance.ShapeConfig ShapeConfig `json:\"shapeConfig\"`",
"Instance.State string `json:\"lifecycleState\"`",
@@ -25898,7 +25898,7 @@
"Function.Config map[string]string `json:\"config,omitempty\"`",
"Function.Detached *int `json:\"detachedModeTimeoutInSeconds,omitempty\"`",
"Function.Etag string `json:\"etag\"`",
- "Function.Failure json.RawMessage `json:\"failureDestination,omitempty\"`",
+ "Function.Failure jsontext.Value `json:\"failureDestination,omitempty\"`",
"Function.ID string `json:\"id\"`",
"Function.Image string `json:\"image\"`",
"Function.ImageDigest string `json:\"imageDigest,omitempty\"`",
@@ -25907,7 +25907,7 @@
"Function.Provisioned Concurrency `json:\"provisionedConcurrencyConfig\"`",
"Function.Seq int64 `json:\"seq\"`",
"Function.State string `json:\"state\"`",
- "Function.Success json.RawMessage `json:\"successDestination,omitempty\"`",
+ "Function.Success jsontext.Value `json:\"successDestination,omitempty\"`",
"Function.Tags Tags `json:\"tags\"`",
"Function.TimeCreated time.Time `json:\"timeCreated\"`",
"Function.TimeUpdated time.Time `json:\"timeUpdated\"`",
@@ -26201,7 +26201,7 @@
"AgentConfig.ID string `json:\"id\"`",
"AgentConfig.Name string `json:\"displayName\"`",
"AgentConfig.Seq int64 `json:\"seq\"`",
- "AgentConfig.ServiceConfig json.RawMessage `json:\"serviceConfiguration\"`",
+ "AgentConfig.ServiceConfig jsontext.Value `json:\"serviceConfiguration\"`",
"AgentConfig.State string `json:\"state\"`",
"AgentConfig.Tags Tags `json:\"tags\"`",
"AgentConfig.TimeCreated time.Time `json:\"timeCreated\"`",
@@ -26285,7 +26285,7 @@
"Alarm.MetricCompartmentID string `json:\"metricCompartmentId\"`",
"Alarm.Name string `json:\"displayName\"`",
"Alarm.Namespace string `json:\"namespace\"`",
- "Alarm.Overrides []json.RawMessage `json:\"overrides,omitempty\"`",
+ "Alarm.Overrides []jsontext.Value `json:\"overrides,omitempty\"`",
"Alarm.PendingDuration string `json:\"pendingDuration\"`",
"Alarm.PendingSince *time.Time `json:\"pendingSince,omitempty\"`",
"Alarm.PerDimension *bool `json:\"isNotificationsPerMetricDimensionEnabled,omitempty\"`",
@@ -26350,7 +26350,7 @@
"Backup.TimeCreated time.Time `json:\"timeCreated\"`",
"Backup.TimeUpdated time.Time `json:\"timeUpdated\"`",
"Backup.TransitionAt *time.Time `json:\"transitionAt,omitempty\"`",
- "BackupPolicy.CopyPolicies json.RawMessage `json:\"copyPolicies,omitempty\"`",
+ "BackupPolicy.CopyPolicies jsontext.Value `json:\"copyPolicies,omitempty\"`",
"BackupPolicy.IsEnabled bool `json:\"isEnabled\"`",
"BackupPolicy.PitrEnabled bool `json:\"pitrEnabled\"`",
"BackupPolicy.RetentionInDays int `json:\"retentionInDays\"`",
@@ -26373,7 +26373,7 @@
"Channel.TimeCreated time.Time `json:\"timeCreated\"`",
"Channel.TimeUpdated time.Time `json:\"timeUpdated\"`",
"Channel.TransitionAt *time.Time `json:\"transitionAt,omitempty\"`",
- "ChannelSource.Extra map[string]json.RawMessage `json:\"extra,omitempty\"`",
+ "ChannelSource.Extra map[string]jsontext.Value `json:\"extra,omitempty\"`",
"ChannelSource.HasPassword bool `json:\"hasPassword\"`",
"ChannelSource.Hostname string `json:\"hostname\"`",
"ChannelSource.Port int `json:\"port\"`",
@@ -26384,7 +26384,7 @@
"ChannelTarget.ChannelName string `json:\"channelName\"`",
"ChannelTarget.DBSystemID string `json:\"dbSystemId\"`",
"ChannelTarget.DelayInSeconds int `json:\"delayInSeconds\"`",
- "ChannelTarget.Filters json.RawMessage `json:\"filters,omitempty\"`",
+ "ChannelTarget.Filters jsontext.Value `json:\"filters,omitempty\"`",
"ChannelTarget.TablesWithoutPrimaryKeyHandling string `json:\"tablesWithoutPrimaryKeyHandling\"`",
"ChannelTarget.TargetType string `json:\"targetType\"`",
"Configuration.CompartmentID string `json:\"compartmentId\"`",
@@ -26421,7 +26421,7 @@
"DBSystem.EngineHost string `json:\"engineHost,omitempty\"`",
"DBSystem.EnginePort int `json:\"enginePort,omitempty\"`",
"DBSystem.Etag string `json:\"etag\"`",
- "DBSystem.Extra map[string]json.RawMessage `json:\"extra,omitempty\"`",
+ "DBSystem.Extra map[string]jsontext.Value `json:\"extra,omitempty\"`",
"DBSystem.FD string `json:\"faultDomain\"`",
"DBSystem.HA bool `json:\"isHighlyAvailable\"`",
"DBSystem.Hostname string `json:\"hostnameLabel,omitempty\"`",
@@ -26474,7 +26474,7 @@
"ocinosql": {
"fields": [
"Column.AsUUID bool `json:\"asUuid,omitempty\"`",
- "Column.Default json.RawMessage `json:\"default,omitempty\"`",
+ "Column.Default jsontext.Value `json:\"default,omitempty\"`",
"Column.Gen bool `json:\"generated,omitempty\"`",
"Column.Name string `json:\"name\"`",
"Column.NotNull bool `json:\"notNull,omitempty\"`",
@@ -26497,7 +26497,7 @@
"Limits.StorageGB int `json:\"storageGb\"`",
"Limits.WriteUnits int `json:\"writeUnits\"`",
"Row.Expire *time.Time `json:\"expire,omitempty\"`",
- "Row.Value json.RawMessage `json:\"value\"`",
+ "Row.Value jsontext.Value `json:\"value\"`",
"Table.AutoReclaim bool `json:\"autoReclaimable,omitempty\"`",
"Table.Columns []Column `json:\"columns\"`",
"Table.CompartmentID string `json:\"compartmentId\"`",
@@ -26877,7 +26877,7 @@
"Secret.Name string `json:\"name\"`",
"Secret.Pending int64 `json:\"pending\"`",
"Secret.Previous int64 `json:\"previous\"`",
- "Secret.Rules []json.RawMessage `json:\"rules,omitempty\"`",
+ "Secret.Rules []jsontext.Value `json:\"rules,omitempty\"`",
"Secret.Seq int64 `json:\"seq\"`",
"Secret.State string `json:\"state\"`",
"Secret.TimeCreated time.Time `json:\"timeCreated\"`",
@@ -27270,7 +27270,7 @@
"backendSnapshot.ReadSetBytes map[string]map[string][]byte `json:\"readSetBytes\"`",
"backendSnapshot.ReferenceBytes map[string]map[string][]byte `json:\"referenceBytes\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"backendSnapshot.UploadPartData map[string]map[string]map[string]map[int][]byte `json:\"uploadPartData\"`",
"backendSnapshot.UploadParts map[string]map[string][]*ReadSetUploadPart `json:\"uploadParts\"`"
@@ -27340,7 +27340,7 @@
"CollectionGroupCapacityLimits.MaxSearchCapacityInOCU *float64 `json:\"maxSearchCapacityInOCU,omitempty\"`",
"CollectionGroupCapacityLimits.MinIndexingCapacityInOCU *float64 `json:\"minIndexingCapacityInOCU,omitempty\"`",
"CollectionGroupCapacityLimits.MinSearchCapacityInOCU *float64 `json:\"minSearchCapacityInOCU,omitempty\"`",
- "DataSource.DataSourceType json.RawMessage `json:\"dataSourceType,omitempty\"`",
+ "DataSource.DataSourceType jsontext.Value `json:\"dataSourceType,omitempty\"`",
"DataSource.Description string `json:\"description\"`",
"DataSource.DomainName string `json:\"-\"`",
"DataSource.Name string `json:\"name\"`",
@@ -27352,7 +27352,7 @@
"DataSourceAttachment.Status string `json:\"status\"`",
"DirectQueryDataSource.DataSourceAccessPolicy string `json:\"dataSourceAccessPolicy,omitempty\"`",
"DirectQueryDataSource.DataSourceArn string `json:\"dataSourceArn\"`",
- "DirectQueryDataSource.DataSourceType json.RawMessage `json:\"dataSourceType,omitempty\"`",
+ "DirectQueryDataSource.DataSourceType jsontext.Value `json:\"dataSourceType,omitempty\"`",
"DirectQueryDataSource.Description string `json:\"description\"`",
"DirectQueryDataSource.Name string `json:\"name\"`",
"DirectQueryDataSource.OpenSearchArns []string `json:\"openSearchArns\"`",
@@ -27645,7 +27645,7 @@
"backendSnapshot.SlCollGroupCounter int `json:\"slCollGroupCounter\"`",
"backendSnapshot.SlSecConfigCounter int `json:\"slSecConfigCounter\"`",
"backendSnapshot.SlVpcEndpointCounter int `json:\"slVpcEndpointCounter\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.UpgradeHistory map[string][]*UpgradeHistory `json:\"upgradeHistory\"`",
"backendSnapshot.VpcAuthorizations map[string][]AuthorizedPrincipal `json:\"vpcAuthorizations\"`",
"backendSnapshot.VpcEndpointCounter int `json:\"vpcEndpointCounter\"`",
@@ -27655,7 +27655,7 @@
"dataSourceAttachmentSnapshot.CreatedAt time.Time `json:\"createdAt,omitzero\"`",
"dataSourceAttachmentSnapshot.DataSourceArn string `json:\"dataSourceArn\"`",
"dataSourceAttachmentSnapshot.Status string `json:\"status\"`",
- "dataSourceSnapshot.DataSourceType json.RawMessage `json:\"dataSourceType,omitempty\"`",
+ "dataSourceSnapshot.DataSourceType jsontext.Value `json:\"dataSourceType,omitempty\"`",
"dataSourceSnapshot.Description string `json:\"description\"`",
"dataSourceSnapshot.DomainName string `json:\"domainName\"`",
"dataSourceSnapshot.Name string `json:\"name\"`",
@@ -27748,17 +27748,17 @@
"LayerSettings.Attributes map[string]string `json:\"Attributes,omitempty\"`",
"LayerSettings.AutoAssignElasticIps *bool `json:\"AutoAssignElasticIps,omitempty\"`",
"LayerSettings.AutoAssignPublicIps *bool `json:\"AutoAssignPublicIps,omitempty\"`",
- "LayerSettings.CloudWatchLogsConfiguration json.RawMessage `json:\"CloudWatchLogsConfiguration,omitempty\"`",
+ "LayerSettings.CloudWatchLogsConfiguration jsontext.Value `json:\"CloudWatchLogsConfiguration,omitempty\"`",
"LayerSettings.CustomInstanceProfileArn string `json:\"CustomInstanceProfileArn,omitempty\"`",
"LayerSettings.CustomJSON string `json:\"CustomJson,omitempty\"`",
- "LayerSettings.CustomRecipes json.RawMessage `json:\"CustomRecipes,omitempty\"`",
+ "LayerSettings.CustomRecipes jsontext.Value `json:\"CustomRecipes,omitempty\"`",
"LayerSettings.CustomSecurityGroupIDs []string `json:\"CustomSecurityGroupIds,omitempty\"`",
"LayerSettings.EnableAutoHealing *bool `json:\"EnableAutoHealing,omitempty\"`",
"LayerSettings.InstallUpdatesOnBoot *bool `json:\"InstallUpdatesOnBoot,omitempty\"`",
- "LayerSettings.LifecycleEventConfiguration json.RawMessage `json:\"LifecycleEventConfiguration,omitempty\"`",
+ "LayerSettings.LifecycleEventConfiguration jsontext.Value `json:\"LifecycleEventConfiguration,omitempty\"`",
"LayerSettings.Packages []string `json:\"Packages,omitempty\"`",
"LayerSettings.UseEbsOptimizedInstances *bool `json:\"UseEbsOptimizedInstances,omitempty\"`",
- "LayerSettings.VolumeConfigurations json.RawMessage `json:\"VolumeConfigurations,omitempty\"`",
+ "LayerSettings.VolumeConfigurations jsontext.Value `json:\"VolumeConfigurations,omitempty\"`",
"ScalingParameters.CPUThreshold float64 `json:\"CpuThreshold\"`",
"ScalingParameters.IgnoreMetricsTime int32 `json:\"IgnoreMetricsTime\"`",
"ScalingParameters.InstanceCount int32 `json:\"InstanceCount\"`",
@@ -27769,7 +27769,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.MyUserProfileSSHKey string `json:\"myUserProfileSshKey,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"storedApp.AppID string `json:\"appId\"`",
"storedApp.Arn string `json:\"arn\"`",
@@ -28004,7 +28004,7 @@
"backendSnapshot.ResourcePolicy *ResourcePolicy `json:\"resourcePolicy,omitempty\"`",
"backendSnapshot.Root *Root `json:\"root\"`",
"backendSnapshot.StatusCounter int `json:\"statusCounter\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"backendSnapshot.TargetPolicies map[string][]string `json:\"targetPolicies\"`",
"delegatedAdminSnapshot.Admin DelegatedAdmin `json:\"admin\"`",
@@ -28166,7 +28166,7 @@
"VpcInformation.VpcID string `json:\"vpcId,omitempty\"`",
"backendSnapshot.AccountID string `json:\"accountId\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"runningInstance.AccountID string",
"runningInstance.AssetID string",
"runningInstance.InstanceID string",
@@ -28391,7 +28391,7 @@
"TrainingDataConfig.IncludedDatasetColumns map[string][]string `json:\"includedDatasetColumns,omitempty\"`",
"backendSnapshot.AccountID string `json:\"accountId\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"lifecycle.delay time.Duration",
"storedFeatureTransformation.ARN string",
@@ -28605,9 +28605,9 @@
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.SegmentVersions map[string][]*Segment `json:\"segmentVersions\"`",
"backendSnapshot.SentMessages map[string]int `json:\"sentMessages\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.TemplateActiveVersion map[string]string `json:\"templateActiveVersion\"`",
- "backendSnapshot.TemplateVersionData map[string]map[string]json.RawMessage `json:\"templateVersionData\"`",
+ "backendSnapshot.TemplateVersionData map[string]map[string]jsontext.Value `json:\"templateVersionData\"`",
"backendSnapshot.TemplateVersionHistory map[string][]templateVersionItem `json:\"templateVersionHistory\"`",
"campaignActivity.ApplicationID string `json:\"ApplicationId\"`",
"campaignActivity.CampaignID string `json:\"CampaignId\"`",
@@ -28862,7 +28862,7 @@
"TimestreamSingleMeasureMapping.MeasureValueType string `json:\"MeasureValueType,omitempty\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"enrichmentCounter.Count int64 `json:\"count\"`",
"enrichmentCounter.Name string `json:\"name\"`"
],
@@ -28899,7 +28899,7 @@
"SynthesisOptions.VoiceID string",
"backendSnapshot.AccountID string `json:\"accountId\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 2
},
@@ -28987,7 +28987,7 @@
"backendSnapshot.RoleMemberships map[string]bool `json:\"roleMemberships\"`",
"backendSnapshot.SPICECapacity map[string]string `json:\"spiceCapacity\"`",
"backendSnapshot.SelfUpgradeConfig map[string]string `json:\"selfUpgradeConfig\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"backendSnapshot.UserCustomPermissions map[string]string `json:\"userCustomPermissions\"`",
"storedAccountCustomization.DefaultEmailCustomizationTemplate string `json:\"defaultEmailCustomizationTemplate,omitempty\"`",
@@ -29442,7 +29442,7 @@
"backendSnapshot.Associations []*ResourceShareAssociation `json:\"associations\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.SharePermissions map[string]map[string]int32 `json:\"sharePermissions\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -29890,7 +29890,7 @@
"backendSnapshot.ProxyTargets map[string][]DBProxyTarget `json:\"proxyTargets\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.SnapshotTenantDatabases map[string][]*DBSnapshotTenantDatabase `json:\"snapshotTenantDatabases\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string][]Tag `json:\"tags\"`",
"xmlDBClusterSnapshot.AllocatedStorage int `xml:\"AllocatedStorage,omitempty\"`",
"xmlDBClusterSnapshot.ClusterCreateTime string `xml:\"ClusterCreateTime,omitempty\"`",
@@ -30256,13 +30256,13 @@
"ServerlessScheduledAction.EndTime time.Time `json:\"endTime,omitzero\"`",
"ServerlessScheduledAction.NamespaceName string `json:\"namespaceName,omitempty\"`",
"ServerlessScheduledAction.RoleArn string `json:\"roleArn,omitempty\"`",
- "ServerlessScheduledAction.Schedule json.RawMessage `json:\"schedule,omitempty\"`",
+ "ServerlessScheduledAction.Schedule jsontext.Value `json:\"schedule,omitempty\"`",
"ServerlessScheduledAction.ScheduledActionDescription string `json:\"scheduledActionDescription,omitempty\"`",
"ServerlessScheduledAction.ScheduledActionName string `json:\"scheduledActionName\"`",
"ServerlessScheduledAction.ScheduledActionUUID string `json:\"scheduledActionUuid,omitempty\"`",
"ServerlessScheduledAction.StartTime time.Time `json:\"startTime,omitzero\"`",
"ServerlessScheduledAction.State string `json:\"state\"`",
- "ServerlessScheduledAction.TargetAction json.RawMessage `json:\"targetAction,omitempty\"`",
+ "ServerlessScheduledAction.TargetAction jsontext.Value `json:\"targetAction,omitempty\"`",
"ServerlessSnapshot.AccountsWithRestoreAccess []string `json:\"accountsWithRestoreAccess,omitempty\"`",
"ServerlessSnapshot.AdminUsername string `json:\"adminUsername,omitempty\"`",
"ServerlessSnapshot.NamespaceArn string `json:\"namespaceArn\"`",
@@ -30379,7 +30379,7 @@
"backendSnapshot.LoggingStatuses map[string]*LoggingStatus `json:\"loggingStatuses\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.SnapshotCopyConfigs map[string]*SnapshotCopyConfig `json:\"snapshotCopyConfigs\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"slResourceTagSet.ResourceArn string `json:\"resourceArn\"`",
"slResourceTagSet.Tags map[string]string `json:\"tags\"`",
"xmlAccountWithRestoreAccess.AccountAlias string `xml:\"AccountAlias,omitempty\"`",
@@ -30491,7 +30491,7 @@
"backendSnapshot.AccountID string `json:\"accountId\"`",
"backendSnapshot.DatasetEntries map[string][]string `json:\"datasetEntries\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"storedAsyncJob.ClientRequestToken string `json:\"clientRequestToken,omitempty\"`",
"storedAsyncJob.CollectionID string `json:\"collectionId\"`",
@@ -30755,7 +30755,7 @@
"TerraformSource.S3StateFileURL string",
"backendSnapshot.AccountID string `json:\"accountId\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -30797,7 +30797,7 @@
"View.Region string `json:\"region\"`",
"View.Scope string `json:\"scope\"`",
"View.Tags map[string]string `json:\"tags,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -30824,7 +30824,7 @@
"backendSnapshot.GroupResources map[string]map[string][]string `json:\"groupResources\"`",
"backendSnapshot.GroupingStatuses map[string]map[string][]GroupingStatusItem `json:\"groupingStatuses\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"groupSnapshot.ARN string `json:\"arn\"`",
"groupSnapshot.ApplicationTag map[string]string `json:\"applicationTag,omitempty\"`",
"groupSnapshot.Criticality int `json:\"criticality,omitempty\"`",
@@ -30841,7 +30841,7 @@
"fields": [
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"reportCreationState.Region string `json:\"-\"`",
"reportCreationState.S3Location string `json:\"s3Location\"`",
"reportCreationState.StartDate string `json:\"startDate\"`",
@@ -30910,7 +30910,7 @@
"TrustAnchorSource.SourceType string `json:\"sourceType\"`",
"backendSnapshot.AttributeMappings map[string]map[string][]AttributeMapping `json:\"attributeMappings\"`",
"backendSnapshot.NotificationSettings map[string]map[string][]NotificationSetting `json:\"notificationSettings\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string][]TagEntry `json:\"tags\"`"
],
"version": 2
@@ -31031,7 +31031,7 @@
"TrafficPolicyInstance.TrafficPolicyVersion int32 `json:\"trafficPolicyVersion\"`",
"VPCAssociationAuthorization.VPCID string `json:\"vpcId\"`",
"VPCAssociationAuthorization.VPCRegion string `json:\"vpcRegion\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]*svcTags.Tags `json:\"tags,omitempty\"`",
"backendSnapshot.TrafficPolicies map[string][]*TrafficPolicy `json:\"trafficPolicies,omitempty\"`",
"backendSnapshot.VPCAssocAuthorizations map[string][]VPCAssociationAuthorization `json:\"vpcAssocAuthorizations,omitempty\"`",
@@ -31083,7 +31083,7 @@
"SafetyRule.Targets []string `json:\"targets,omitempty\"`",
"SafetyRule.Threshold int32 `json:\"threshold\"`",
"SafetyRule.WaitPeriodMs int32 `json:\"waitPeriodMs\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tokens map[string]string `json:\"tokens,omitempty\"`"
],
"version": 1
@@ -31123,7 +31123,7 @@
"ResourceSet.Type string `json:\"type\"`",
"TargetResource.NLB *NLBResource `json:\"nLBResource,omitempty\"`",
"TargetResource.R53 *R53Resource `json:\"r53Resource,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -31304,7 +31304,7 @@
"backendSnapshot.QueryLogConfigPolicies map[string]map[string]string `json:\"queryLogConfigPolicies\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ResolverRulePolicies map[string]map[string]string `json:\"resolverRulePolicies\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string][]svcTags.KV `json:\"tags\"`"
],
"version": 1
@@ -31368,7 +31368,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.Seq int64 `json:\"seq\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -31523,7 +31523,7 @@
"StoredPart.PartNumber int32 `json:\"partNumber\"`",
"StoredPart.Size int64 `json:\"size\"`",
"backendSnapshot.DefaultRegion string `json:\"defaultRegion\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string][]types.Tag `json:\"tags\"`",
"keyIndex.chunks [][]string",
"multipartObjectHeaders.CacheControl string `json:\"cacheControl,omitempty\"`",
@@ -31659,7 +31659,7 @@
"backendSnapshot.ResourceTags map[string]map[string]string `json:\"resourceTags\"`",
"backendSnapshot.StorageLensConfigTags map[string]TagSet `json:\"storageLensConfigTags\"`",
"backendSnapshot.StorageLensConfigs map[string]string `json:\"storageLensConfigs\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"mrapRequestSnapshot.Request MultiRegionAccessPointRequest `json:\"request\"`",
"mrapRequestSnapshot.Token string `json:\"token\"`"
],
@@ -31720,7 +31720,7 @@
"SyncConfiguration.ImportDataRules []ImportDataRule `json:\"importDataRules\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -31777,7 +31777,7 @@
"TableReplicationConfig.VersionToken string `json:\"versionToken\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`"
],
"version": 2
@@ -31813,7 +31813,7 @@
"Vector.Meta map[string]any `json:\"meta,omitempty\"`",
"VectorSet.Key string `json:\"key\"`",
"VectorSet.Vectors map[string]*Vector `json:\"vectors\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -31823,12 +31823,12 @@
"AIBenchmarkJob.AIBenchmarkJobName string `json:\"AIBenchmarkJobName\"`",
"AIBenchmarkJob.AIBenchmarkJobStatus string `json:\"AIBenchmarkJobStatus\"`",
"AIBenchmarkJob.AIWorkloadConfigIdentifier string `json:\"AIWorkloadConfigIdentifier\"`",
- "AIBenchmarkJob.BenchmarkTarget json.RawMessage `json:\"BenchmarkTarget\"`",
+ "AIBenchmarkJob.BenchmarkTarget jsontext.Value `json:\"BenchmarkTarget\"`",
"AIBenchmarkJob.CreationTime time.Time `json:\"CreationTime\"`",
"AIBenchmarkJob.EndTime *time.Time `json:\"EndTime,omitempty\"`",
"AIBenchmarkJob.FailureReason string `json:\"FailureReason,omitempty\"`",
- "AIBenchmarkJob.NetworkConfig json.RawMessage `json:\"NetworkConfig,omitempty\"`",
- "AIBenchmarkJob.OutputConfig json.RawMessage `json:\"OutputConfig\"`",
+ "AIBenchmarkJob.NetworkConfig jsontext.Value `json:\"NetworkConfig,omitempty\"`",
+ "AIBenchmarkJob.OutputConfig jsontext.Value `json:\"OutputConfig\"`",
"AIBenchmarkJob.RoleArn string `json:\"RoleArn\"`",
"AIBenchmarkJob.StartTime *time.Time `json:\"StartTime,omitempty\"`",
"AIBenchmarkJob.Tags map[string]string `json:\"Tags,omitempty\"`",
@@ -31838,16 +31838,16 @@
"AIRecommendationJob.AIRecommendationJobName string `json:\"AIRecommendationJobName\"`",
"AIRecommendationJob.AIRecommendationJobStatus string `json:\"AIRecommendationJobStatus\"`",
"AIRecommendationJob.AIWorkloadConfigIdentifier string `json:\"AIWorkloadConfigIdentifier\"`",
- "AIRecommendationJob.AdapterSource json.RawMessage `json:\"AdapterSource,omitempty\"`",
- "AIRecommendationJob.ComputeSpec json.RawMessage `json:\"ComputeSpec,omitempty\"`",
+ "AIRecommendationJob.AdapterSource jsontext.Value `json:\"AdapterSource,omitempty\"`",
+ "AIRecommendationJob.ComputeSpec jsontext.Value `json:\"ComputeSpec,omitempty\"`",
"AIRecommendationJob.CreationTime time.Time `json:\"CreationTime\"`",
"AIRecommendationJob.EndTime *time.Time `json:\"EndTime,omitempty\"`",
"AIRecommendationJob.FailureReason string `json:\"FailureReason,omitempty\"`",
- "AIRecommendationJob.InferenceSpecification json.RawMessage `json:\"InferenceSpecification,omitempty\"`",
- "AIRecommendationJob.ModelSource json.RawMessage `json:\"ModelSource\"`",
+ "AIRecommendationJob.InferenceSpecification jsontext.Value `json:\"InferenceSpecification,omitempty\"`",
+ "AIRecommendationJob.ModelSource jsontext.Value `json:\"ModelSource\"`",
"AIRecommendationJob.OptimizeModel *bool `json:\"OptimizeModel,omitempty\"`",
- "AIRecommendationJob.OutputConfig json.RawMessage `json:\"OutputConfig\"`",
- "AIRecommendationJob.PerformanceTarget json.RawMessage `json:\"PerformanceTarget,omitempty\"`",
+ "AIRecommendationJob.OutputConfig jsontext.Value `json:\"OutputConfig\"`",
+ "AIRecommendationJob.PerformanceTarget jsontext.Value `json:\"PerformanceTarget,omitempty\"`",
"AIRecommendationJob.RoleArn string `json:\"RoleArn\"`",
"AIRecommendationJob.StartTime *time.Time `json:\"StartTime,omitempty\"`",
"AIRecommendationJob.Tags map[string]string `json:\"Tags,omitempty\"`",
@@ -31856,9 +31856,9 @@
"AIWorkloadConfig.AIWorkloadConfigArn string `json:\"AIWorkloadConfigArn\"`",
"AIWorkloadConfig.AIWorkloadConfigName string `json:\"AIWorkloadConfigName\"`",
"AIWorkloadConfig.CreationTime time.Time `json:\"CreationTime\"`",
- "AIWorkloadConfig.DatasetConfig json.RawMessage `json:\"DatasetConfig,omitempty\"`",
+ "AIWorkloadConfig.DatasetConfig jsontext.Value `json:\"DatasetConfig,omitempty\"`",
"AIWorkloadConfig.Tags map[string]string `json:\"Tags,omitempty\"`",
- "AIWorkloadConfig.WorkloadSpec json.RawMessage `json:\"AIWorkloadConfigs,omitempty\"`",
+ "AIWorkloadConfig.WorkloadSpec jsontext.Value `json:\"AIWorkloadConfigs,omitempty\"`",
"AIWorkloadConfig.{MarshalJSON}",
"AIWorkloadConfig.{UnmarshalJSON}",
"AcceleratorPartitionConfig.Count int32 `json:\"Count\"`",
@@ -31884,11 +31884,11 @@
"Algorithm.AlgorithmStatusDetails AlgorithmStatusDetails `json:\"AlgorithmStatusDetails\"`",
"Algorithm.CertifyForMarketplace bool `json:\"CertifyForMarketplace,omitempty\"`",
"Algorithm.CreationTime time.Time `json:\"CreationTime\"`",
- "Algorithm.InferenceSpecification json.RawMessage `json:\"InferenceSpecification,omitempty\"`",
+ "Algorithm.InferenceSpecification jsontext.Value `json:\"InferenceSpecification,omitempty\"`",
"Algorithm.ProductID string `json:\"ProductId,omitempty\"`",
"Algorithm.Tags map[string]string `json:\"Tags,omitempty\"`",
- "Algorithm.TrainingSpecification json.RawMessage `json:\"TrainingSpecification,omitempty\"`",
- "Algorithm.ValidationSpecification json.RawMessage `json:\"ValidationSpecification,omitempty\"`",
+ "Algorithm.TrainingSpecification jsontext.Value `json:\"TrainingSpecification,omitempty\"`",
+ "Algorithm.ValidationSpecification jsontext.Value `json:\"ValidationSpecification,omitempty\"`",
"AlgorithmSpecification.AlgorithmName string `json:\"AlgorithmName,omitempty\"`",
"AlgorithmSpecification.ContainerArguments []string `json:\"ContainerArguments,omitempty\"`",
"AlgorithmSpecification.ContainerEntrypoint []string `json:\"ContainerEntrypoint,omitempty\"`",
@@ -31907,17 +31907,17 @@
"App.CreationTime time.Time `json:\"CreationTime\"`",
"App.DomainID string `json:\"DomainId\"`",
"App.RecoveryMode bool `json:\"RecoveryMode,omitempty\"`",
- "App.ResourceSpec json.RawMessage `json:\"ResourceSpec,omitempty\"`",
+ "App.ResourceSpec jsontext.Value `json:\"ResourceSpec,omitempty\"`",
"App.SpaceName string `json:\"SpaceName,omitempty\"`",
"App.Status string `json:\"Status\"`",
"App.Tags map[string]string `json:\"Tags,omitempty\"`",
"App.UserProfileName string `json:\"UserProfileName,omitempty\"`",
"AppImageConfig.AppImageConfigArn string `json:\"AppImageConfigArn\"`",
"AppImageConfig.AppImageConfigName string `json:\"AppImageConfigName\"`",
- "AppImageConfig.CodeEditorAppImageConfig json.RawMessage `json:\"CodeEditorAppImageConfig,omitempty\"`",
+ "AppImageConfig.CodeEditorAppImageConfig jsontext.Value `json:\"CodeEditorAppImageConfig,omitempty\"`",
"AppImageConfig.CreationTime time.Time `json:\"CreationTime\"`",
- "AppImageConfig.JupyterLabAppImageConfig json.RawMessage `json:\"JupyterLabAppImageConfig,omitempty\"`",
- "AppImageConfig.KernelGatewayImageConfig json.RawMessage `json:\"KernelGatewayImageConfig,omitempty\"`",
+ "AppImageConfig.JupyterLabAppImageConfig jsontext.Value `json:\"JupyterLabAppImageConfig,omitempty\"`",
+ "AppImageConfig.KernelGatewayImageConfig jsontext.Value `json:\"KernelGatewayImageConfig,omitempty\"`",
"AppImageConfig.LastModifiedTime time.Time `json:\"LastModifiedTime\"`",
"AppImageConfig.Tags map[string]string `json:\"Tags,omitempty\"`",
"AppImageConfig.{MarshalJSON}",
@@ -31975,7 +31975,7 @@
"AutoMLJob.AutoMLJobObjective *AutoMLJobObjective `json:\"AutoMLJobObjective,omitempty\"`",
"AutoMLJob.AutoMLJobSecondaryStatus string `json:\"AutoMLJobSecondaryStatus\"`",
"AutoMLJob.AutoMLJobStatus string `json:\"AutoMLJobStatus\"`",
- "AutoMLJob.AutoMLProblemTypeConfig json.RawMessage `json:\"AutoMLProblemTypeConfig,omitempty\"`",
+ "AutoMLJob.AutoMLProblemTypeConfig jsontext.Value `json:\"AutoMLProblemTypeConfig,omitempty\"`",
"AutoMLJob.CreationTime time.Time `json:\"CreationTime\"`",
"AutoMLJob.DataSplitConfig *AutoMLDataSplitConfig `json:\"DataSplitConfig,omitempty\"`",
"AutoMLJob.EndTime *time.Time `json:\"EndTime,omitempty\"`",
@@ -32001,8 +32001,8 @@
"AutoMLSecurityConfig.EnableInterContainerTrafficEncryption *bool `json:\"EnableInterContainerTrafficEncryption,omitempty\"`",
"AutoMLSecurityConfig.VolumeKmsKeyID string `json:\"VolumeKmsKeyId,omitempty\"`",
"AutoMLSecurityConfig.VpcConfig *VpcConfig `json:\"VpcConfig,omitempty\"`",
- "AutoMLV1Options.CandidateGenerationConfig json.RawMessage `json:\"CandidateGenerationConfig,omitempty\"`",
- "AutoMLV1Options.CompletionCriteria json.RawMessage `json:\"CompletionCriteria,omitempty\"`",
+ "AutoMLV1Options.CandidateGenerationConfig jsontext.Value `json:\"CandidateGenerationConfig,omitempty\"`",
+ "AutoMLV1Options.CompletionCriteria jsontext.Value `json:\"CompletionCriteria,omitempty\"`",
"AutoMLV1Options.GenerateCandidateDefinitionsOnly bool `json:\"GenerateCandidateDefinitionsOnly,omitempty\"`",
"AutoMLV1Options.Mode string `json:\"Mode,omitempty\"`",
"AutoMLV1Options.ProblemType string `json:\"ProblemType,omitempty\"`",
@@ -32128,7 +32128,7 @@
"CompilationJob.RoleArn string `json:\"RoleArn,omitempty\"`",
"CompilationJob.StoppingCondition *StoppingCondition `json:\"StoppingCondition,omitempty\"`",
"CompilationJob.Tags map[string]string `json:\"Tags,omitempty\"`",
- "CompilationJob.VpcConfig json.RawMessage `json:\"VpcConfig,omitempty\"`",
+ "CompilationJob.VpcConfig jsontext.Value `json:\"VpcConfig,omitempty\"`",
"CompilationJob.{MarshalJSON}",
"CompilationJob.{UnmarshalJSON}",
"CompilationOutputConfig.KmsKeyID string `json:\"KmsKeyId,omitempty\"`",
@@ -32233,12 +32233,12 @@
"Domain.AppSecurityGroupManagement string `json:\"AppSecurityGroupManagement,omitempty\"`",
"Domain.AuthMode string `json:\"AuthMode,omitempty\"`",
"Domain.CreationTime time.Time `json:\"CreationTime\"`",
- "Domain.DefaultSpaceSettings json.RawMessage `json:\"DefaultSpaceSettings,omitempty\"`",
- "Domain.DefaultUserSettings json.RawMessage `json:\"DefaultUserSettings,omitempty\"`",
+ "Domain.DefaultSpaceSettings jsontext.Value `json:\"DefaultSpaceSettings,omitempty\"`",
+ "Domain.DefaultUserSettings jsontext.Value `json:\"DefaultUserSettings,omitempty\"`",
"Domain.DomainArn string `json:\"DomainArn\"`",
"Domain.DomainID string `json:\"DomainId\"`",
"Domain.DomainName string `json:\"DomainName\"`",
- "Domain.DomainSettings json.RawMessage `json:\"DomainSettings,omitempty\"`",
+ "Domain.DomainSettings jsontext.Value `json:\"DomainSettings,omitempty\"`",
"Domain.HomeEfsFileSystemCreation string `json:\"HomeEfsFileSystemCreation,omitempty\"`",
"Domain.HomeEfsFileSystemKmsKeyID string `json:\"HomeEfsFileSystemKmsKeyId,omitempty\"`",
"Domain.KmsKeyID string `json:\"KmsKeyId,omitempty\"`",
@@ -32259,9 +32259,9 @@
"EdgeDeploymentPlan.ModelConfigs []EdgeDeploymentModelConfig `json:\"ModelConfigs,omitempty\"`",
"EdgeDeploymentPlan.Stages []*EdgeDeploymentStage `json:\"Stages,omitempty\"`",
"EdgeDeploymentPlan.Tags map[string]string `json:\"Tags,omitempty\"`",
- "EdgeDeploymentStage.DeploymentConfig json.RawMessage `json:\"DeploymentConfig,omitempty\"`",
+ "EdgeDeploymentStage.DeploymentConfig jsontext.Value `json:\"DeploymentConfig,omitempty\"`",
"EdgeDeploymentStage.DeploymentStatus string `json:\"DeploymentStatus\"`",
- "EdgeDeploymentStage.DeviceSelectionConfig json.RawMessage `json:\"DeviceSelectionConfig,omitempty\"`",
+ "EdgeDeploymentStage.DeviceSelectionConfig jsontext.Value `json:\"DeviceSelectionConfig,omitempty\"`",
"EdgeDeploymentStage.StageName string `json:\"StageName\"`",
"EdgeOutputConfig.KmsKeyID string `json:\"KmsKeyId,omitempty\"`",
"EdgeOutputConfig.PresetDeploymentConfig string `json:\"PresetDeploymentConfig,omitempty\"`",
@@ -32285,7 +32285,7 @@
"Endpoint.AsyncInferenceConfig *AsyncInferenceConfig `json:\"AsyncInferenceConfig,omitempty\"`",
"Endpoint.CreationTime time.Time `json:\"CreationTime\"`",
"Endpoint.DataCaptureConfig *DataCaptureConfig `json:\"DataCaptureConfig,omitempty\"`",
- "Endpoint.DeploymentConfig json.RawMessage `json:\"DeploymentConfig,omitempty\"`",
+ "Endpoint.DeploymentConfig jsontext.Value `json:\"DeploymentConfig,omitempty\"`",
"Endpoint.EndpointArn string `json:\"EndpointArn\"`",
"Endpoint.EndpointConfigName string `json:\"EndpointConfigName\"`",
"Endpoint.EndpointName string `json:\"EndpointName\"`",
@@ -32302,9 +32302,9 @@
"EndpointConfig.EndpointConfigARN string `json:\"EndpointConfigARN\"`",
"EndpointConfig.EndpointConfigName string `json:\"EndpointConfigName\"`",
"EndpointConfig.ExecutionRoleArn string `json:\"ExecutionRoleArn,omitempty\"`",
- "EndpointConfig.ExplainerConfig json.RawMessage `json:\"ExplainerConfig,omitempty\"`",
+ "EndpointConfig.ExplainerConfig jsontext.Value `json:\"ExplainerConfig,omitempty\"`",
"EndpointConfig.KmsKeyID string `json:\"KmsKeyId,omitempty\"`",
- "EndpointConfig.MetricsConfig json.RawMessage `json:\"MetricsConfig,omitempty\"`",
+ "EndpointConfig.MetricsConfig jsontext.Value `json:\"MetricsConfig,omitempty\"`",
"EndpointConfig.ProductionVariants []ProductionVariant `json:\"ProductionVariants,omitempty\"`",
"EndpointConfig.ShadowProductionVariants []ProductionVariant `json:\"ShadowProductionVariants,omitempty\"`",
"EndpointConfig.Tags map[string]string `json:\"Tags,omitempty\"`",
@@ -32433,11 +32433,11 @@
"HumanTaskUI.UITemplateContentSha256 string `json:\"-\"`",
"HumanTaskUI.{MarshalJSON}",
"HumanTaskUI.{UnmarshalJSON}",
- "HyperParameterTuningJob.Autotune json.RawMessage `json:\"Autotune,omitempty\"`",
+ "HyperParameterTuningJob.Autotune jsontext.Value `json:\"Autotune,omitempty\"`",
"HyperParameterTuningJob.CreationTime time.Time `json:\"CreationTime\"`",
"HyperParameterTuningJob.HyperParameterTuningEndTime *time.Time `json:\"HyperParameterTuningEndTime,omitempty\"`",
"HyperParameterTuningJob.HyperParameterTuningJobArn string `json:\"HyperParameterTuningJobArn\"`",
- "HyperParameterTuningJob.HyperParameterTuningJobConfig json.RawMessage `json:\"HyperParameterTuningJobConfig,omitempty\"`",
+ "HyperParameterTuningJob.HyperParameterTuningJobConfig jsontext.Value `json:\"HyperParameterTuningJobConfig,omitempty\"`",
"HyperParameterTuningJob.HyperParameterTuningJobName string `json:\"HyperParameterTuningJobName\"`",
"HyperParameterTuningJob.HyperParameterTuningJobStatus string `json:\"HyperParameterTuningJobStatus\"`",
"HyperParameterTuningJob.LastModifiedTime time.Time `json:\"LastModifiedTime\"`",
@@ -32445,10 +32445,10 @@
"HyperParameterTuningJob.ResourceLimits HPResourceLimits `json:\"ResourceLimits\"`",
"HyperParameterTuningJob.Strategy string `json:\"Strategy,omitempty\"`",
"HyperParameterTuningJob.Tags map[string]string `json:\"Tags,omitempty\"`",
- "HyperParameterTuningJob.TrainingJobDefinition json.RawMessage `json:\"TrainingJobDefinition,omitempty\"`",
- "HyperParameterTuningJob.TrainingJobDefinitions json.RawMessage `json:\"TrainingJobDefinitions,omitempty\"`",
+ "HyperParameterTuningJob.TrainingJobDefinition jsontext.Value `json:\"TrainingJobDefinition,omitempty\"`",
+ "HyperParameterTuningJob.TrainingJobDefinitions jsontext.Value `json:\"TrainingJobDefinitions,omitempty\"`",
"HyperParameterTuningJob.TrainingJobStatusCounters HPTrainingJobStatusCounters `json:\"TrainingJobStatusCounters\"`",
- "HyperParameterTuningJob.WarmStartConfig json.RawMessage `json:\"WarmStartConfig,omitempty\"`",
+ "HyperParameterTuningJob.WarmStartConfig jsontext.Value `json:\"WarmStartConfig,omitempty\"`",
"IamIdentity.Arn string `json:\"Arn,omitempty\"`",
"IamIdentity.PrincipalID string `json:\"PrincipalId,omitempty\"`",
"IamIdentity.SourceIdentity string `json:\"SourceIdentity,omitempty\"`",
@@ -32476,7 +32476,7 @@
"InferenceComponent.CopyCount int32",
"InferenceComponent.CreationTime time.Time",
"InferenceComponent.CurrentCopyCount int32",
- "InferenceComponent.DeploymentConfig json.RawMessage",
+ "InferenceComponent.DeploymentConfig jsontext.Value",
"InferenceComponent.EndpointArn string",
"InferenceComponent.EndpointName string",
"InferenceComponent.FailureReason string",
@@ -32489,17 +32489,17 @@
"InferenceComponent.Tags map[string]string",
"InferenceComponent.VariantName string",
"InferenceComponentContainerSpec.ArtifactURL string `json:\"ArtifactUrl,omitempty\"`",
- "InferenceComponentContainerSpec.ContainerMetricsConfig json.RawMessage `json:\"ContainerMetricsConfig,omitempty\"`",
+ "InferenceComponentContainerSpec.ContainerMetricsConfig jsontext.Value `json:\"ContainerMetricsConfig,omitempty\"`",
"InferenceComponentContainerSpec.Environment map[string]string `json:\"Environment,omitempty\"`",
"InferenceComponentContainerSpec.Image string `json:\"Image,omitempty\"`",
"InferenceComponentSpecification.BaseInferenceComponentName string `json:\"BaseInferenceComponentName,omitempty\"`",
- "InferenceComponentSpecification.ComputeResourceRequirements json.RawMessage `json:\"ComputeResourceRequirements,omitempty\"`",
+ "InferenceComponentSpecification.ComputeResourceRequirements jsontext.Value `json:\"ComputeResourceRequirements,omitempty\"`",
"InferenceComponentSpecification.Container *InferenceComponentContainerSpec `json:\"Container,omitempty\"`",
- "InferenceComponentSpecification.DataCacheConfig json.RawMessage `json:\"DataCacheConfig,omitempty\"`",
+ "InferenceComponentSpecification.DataCacheConfig jsontext.Value `json:\"DataCacheConfig,omitempty\"`",
"InferenceComponentSpecification.InstanceType string `json:\"InstanceType,omitempty\"`",
"InferenceComponentSpecification.ModelName string `json:\"ModelName,omitempty\"`",
- "InferenceComponentSpecification.SchedulingConfig json.RawMessage `json:\"SchedulingConfig,omitempty\"`",
- "InferenceComponentSpecification.StartupParameters json.RawMessage `json:\"StartupParameters,omitempty\"`",
+ "InferenceComponentSpecification.SchedulingConfig jsontext.Value `json:\"SchedulingConfig,omitempty\"`",
+ "InferenceComponentSpecification.StartupParameters jsontext.Value `json:\"StartupParameters,omitempty\"`",
"InferenceExecutionConfig.Mode string `json:\"Mode,omitempty\"`",
"InferenceExperiment.Arn string `json:\"Arn\"`",
"InferenceExperiment.CompletionTime *time.Time `json:\"CompletionTime,omitempty\"`",
@@ -32531,16 +32531,16 @@
"InferenceExperimentSchedule.EndTime *float64 `json:\"EndTime,omitempty\"`",
"InferenceExperimentSchedule.StartTime *float64 `json:\"StartTime,omitempty\"`",
"InferenceRecommendationsJob.CreationTime time.Time `json:\"CreationTime\"`",
- "InferenceRecommendationsJob.InputConfig json.RawMessage `json:\"InputConfig,omitempty\"`",
+ "InferenceRecommendationsJob.InputConfig jsontext.Value `json:\"InputConfig,omitempty\"`",
"InferenceRecommendationsJob.JobArn string `json:\"JobArn\"`",
"InferenceRecommendationsJob.JobDescription string `json:\"JobDescription,omitempty\"`",
"InferenceRecommendationsJob.JobName string `json:\"JobName\"`",
"InferenceRecommendationsJob.JobType string `json:\"JobType,omitempty\"`",
"InferenceRecommendationsJob.LastModifiedTime time.Time `json:\"LastModifiedTime\"`",
- "InferenceRecommendationsJob.OutputConfig json.RawMessage `json:\"OutputConfig,omitempty\"`",
+ "InferenceRecommendationsJob.OutputConfig jsontext.Value `json:\"OutputConfig,omitempty\"`",
"InferenceRecommendationsJob.RoleArn string `json:\"RoleArn,omitempty\"`",
"InferenceRecommendationsJob.Status string `json:\"Status\"`",
- "InferenceRecommendationsJob.StoppingConditions json.RawMessage `json:\"StoppingConditions,omitempty\"`",
+ "InferenceRecommendationsJob.StoppingConditions jsontext.Value `json:\"StoppingConditions,omitempty\"`",
"InferenceRecommendationsJob.Tags map[string]string `json:\"Tags,omitempty\"`",
"InstanceGroup.InstanceCount int32 `json:\"InstanceCount\"`",
"InstanceGroup.InstanceGroupName string `json:\"InstanceGroupName\"`",
@@ -32566,7 +32566,7 @@
"Job.Tags map[string]string `json:\"Tags,omitempty\"`",
"Job.{MarshalJSON}",
"Job.{UnmarshalJSON}",
- "JobDefinition.Config map[string]json.RawMessage `json:\"Config,omitempty\"`",
+ "JobDefinition.Config map[string]jsontext.Value `json:\"Config,omitempty\"`",
"JobDefinition.CreationTime time.Time `json:\"CreationTime\"`",
"JobDefinition.EndpointName string `json:\"EndpointName,omitempty\"`",
"JobDefinition.JobDefinitionArn string `json:\"JobDefinitionArn\"`",
@@ -32707,23 +32707,23 @@
"ModelDeployConfig.EndpointName string `json:\"EndpointName,omitempty\"`",
"ModelInfrastructureConfig.InfrastructureType string `json:\"InfrastructureType,omitempty\"`",
"ModelInfrastructureConfig.RealTimeInferenceConfig *RealTimeInferenceConfig `json:\"RealTimeInferenceConfig,omitempty\"`",
- "ModelPackage.AdditionalInferenceSpecifications json.RawMessage `json:\"AdditionalInferenceSpecifications,omitempty\"`",
+ "ModelPackage.AdditionalInferenceSpecifications jsontext.Value `json:\"AdditionalInferenceSpecifications,omitempty\"`",
"ModelPackage.ApprovalDescription string `json:\"ApprovalDescription,omitempty\"`",
"ModelPackage.CertifyForMarketplace bool `json:\"CertifyForMarketplace,omitempty\"`",
"ModelPackage.CreatedBy *UserContext `json:\"CreatedBy,omitempty\"`",
"ModelPackage.CreationTime time.Time `json:\"CreationTime\"`",
"ModelPackage.CustomerMetadataProperties map[string]string `json:\"CustomerMetadataProperties,omitempty\"`",
"ModelPackage.Domain string `json:\"Domain,omitempty\"`",
- "ModelPackage.DriftCheckBaselines json.RawMessage `json:\"DriftCheckBaselines,omitempty\"`",
- "ModelPackage.InferenceSpecification json.RawMessage `json:\"InferenceSpecification,omitempty\"`",
+ "ModelPackage.DriftCheckBaselines jsontext.Value `json:\"DriftCheckBaselines,omitempty\"`",
+ "ModelPackage.InferenceSpecification jsontext.Value `json:\"InferenceSpecification,omitempty\"`",
"ModelPackage.LastModifiedBy *UserContext `json:\"LastModifiedBy,omitempty\"`",
"ModelPackage.LastModifiedTime time.Time `json:\"LastModifiedTime,omitzero\"`",
"ModelPackage.ManagedStorageType string `json:\"ManagedStorageType,omitempty\"`",
- "ModelPackage.MetadataProperties json.RawMessage `json:\"MetadataProperties,omitempty\"`",
+ "ModelPackage.MetadataProperties jsontext.Value `json:\"MetadataProperties,omitempty\"`",
"ModelPackage.ModelApprovalStatus string `json:\"ModelApprovalStatus,omitempty\"`",
- "ModelPackage.ModelCard json.RawMessage `json:\"ModelCard,omitempty\"`",
- "ModelPackage.ModelLifeCycle json.RawMessage `json:\"ModelLifeCycle,omitempty\"`",
- "ModelPackage.ModelMetrics json.RawMessage `json:\"ModelMetrics,omitempty\"`",
+ "ModelPackage.ModelCard jsontext.Value `json:\"ModelCard,omitempty\"`",
+ "ModelPackage.ModelLifeCycle jsontext.Value `json:\"ModelLifeCycle,omitempty\"`",
+ "ModelPackage.ModelMetrics jsontext.Value `json:\"ModelMetrics,omitempty\"`",
"ModelPackage.ModelPackageArn string `json:\"ModelPackageArn\"`",
"ModelPackage.ModelPackageDescription string `json:\"ModelPackageDescription,omitempty\"`",
"ModelPackage.ModelPackageGroupName string `json:\"ModelPackageGroupName,omitempty\"`",
@@ -32733,13 +32733,13 @@
"ModelPackage.ModelPackageStatusDetails ModelPackageStatusDetails `json:\"ModelPackageStatusDetails\"`",
"ModelPackage.ModelPackageVersion int32 `json:\"ModelPackageVersion,omitempty\"`",
"ModelPackage.SamplePayloadURL string `json:\"SamplePayloadUrl,omitempty\"`",
- "ModelPackage.SecurityConfig json.RawMessage `json:\"SecurityConfig,omitempty\"`",
+ "ModelPackage.SecurityConfig jsontext.Value `json:\"SecurityConfig,omitempty\"`",
"ModelPackage.SkipModelValidation string `json:\"SkipModelValidation,omitempty\"`",
- "ModelPackage.SourceAlgorithmSpecification json.RawMessage `json:\"SourceAlgorithmSpecification,omitempty\"`",
+ "ModelPackage.SourceAlgorithmSpecification jsontext.Value `json:\"SourceAlgorithmSpecification,omitempty\"`",
"ModelPackage.SourceURI string `json:\"SourceUri,omitempty\"`",
"ModelPackage.Tags map[string]string `json:\"Tags,omitempty\"`",
"ModelPackage.Task string `json:\"Task,omitempty\"`",
- "ModelPackage.ValidationSpecification json.RawMessage `json:\"ValidationSpecification,omitempty\"`",
+ "ModelPackage.ValidationSpecification jsontext.Value `json:\"ValidationSpecification,omitempty\"`",
"ModelPackage.{MarshalJSON}",
"ModelPackage.{UnmarshalJSON}",
"ModelPackageGroup.CreatedBy *UserContext `json:\"CreatedBy,omitempty\"`",
@@ -32793,7 +32793,7 @@
"MonitoringSchedule.Tags map[string]string `json:\"Tags,omitempty\"`",
"MonitoringSchedule.{MarshalJSON}",
"MonitoringSchedule.{UnmarshalJSON}",
- "MonitoringScheduleConfig.MonitoringJobDefinition json.RawMessage `json:\"MonitoringJobDefinition,omitempty\"`",
+ "MonitoringScheduleConfig.MonitoringJobDefinition jsontext.Value `json:\"MonitoringJobDefinition,omitempty\"`",
"MonitoringScheduleConfig.MonitoringJobDefinitionName string `json:\"MonitoringJobDefinitionName,omitempty\"`",
"MonitoringScheduleConfig.MonitoringType string `json:\"MonitoringType,omitempty\"`",
"MonitoringScheduleConfig.ScheduleConfig *MonitoringScheduleSchedule `json:\"ScheduleConfig,omitempty\"`",
@@ -32856,27 +32856,27 @@
"OptimizationJob.DeploymentInstanceType string `json:\"DeploymentInstanceType,omitempty\"`",
"OptimizationJob.LastModifiedTime time.Time `json:\"LastModifiedTime\"`",
"OptimizationJob.MaxInstanceCount int32 `json:\"MaxInstanceCount,omitempty\"`",
- "OptimizationJob.ModelSource json.RawMessage `json:\"ModelSource,omitempty\"`",
- "OptimizationJob.OptimizationConfigs json.RawMessage `json:\"OptimizationConfigs,omitempty\"`",
+ "OptimizationJob.ModelSource jsontext.Value `json:\"ModelSource,omitempty\"`",
+ "OptimizationJob.OptimizationConfigs jsontext.Value `json:\"OptimizationConfigs,omitempty\"`",
"OptimizationJob.OptimizationEndTime *time.Time `json:\"OptimizationEndTime,omitempty\"`",
"OptimizationJob.OptimizationEnvironment map[string]string `json:\"OptimizationEnvironment,omitempty\"`",
"OptimizationJob.OptimizationJobArn string `json:\"OptimizationJobArn\"`",
"OptimizationJob.OptimizationJobName string `json:\"OptimizationJobName\"`",
"OptimizationJob.OptimizationJobStatus string `json:\"OptimizationJobStatus\"`",
"OptimizationJob.OptimizationStartTime *time.Time `json:\"OptimizationStartTime,omitempty\"`",
- "OptimizationJob.OutputConfig json.RawMessage `json:\"OutputConfig,omitempty\"`",
+ "OptimizationJob.OutputConfig jsontext.Value `json:\"OutputConfig,omitempty\"`",
"OptimizationJob.RoleArn string `json:\"RoleArn,omitempty\"`",
"OptimizationJob.StoppingCondition *StoppingCondition `json:\"StoppingCondition,omitempty\"`",
"OptimizationJob.Tags map[string]string `json:\"Tags,omitempty\"`",
"OptimizationJob.TrainingPlanArns []string `json:\"TrainingPlanArns,omitempty\"`",
- "OptimizationJob.VpcConfig json.RawMessage `json:\"VpcConfig,omitempty\"`",
+ "OptimizationJob.VpcConfig jsontext.Value `json:\"VpcConfig,omitempty\"`",
"OptimizationJob.{MarshalJSON}",
"OptimizationJob.{UnmarshalJSON}",
"OutputDataConfig.CompressionType string `json:\"CompressionType,omitempty\"`",
"OutputDataConfig.KmsKeyID string `json:\"KmsKeyId,omitempty\"`",
"OutputDataConfig.S3OutputPath string `json:\"S3OutputPath\"`",
"ParallelismConfiguration.MaxParallelExecutionSteps int32 `json:\"MaxParallelExecutionSteps\"`",
- "PartnerApp.ApplicationConfig json.RawMessage `json:\"ApplicationConfig,omitempty\"`",
+ "PartnerApp.ApplicationConfig jsontext.Value `json:\"ApplicationConfig,omitempty\"`",
"PartnerApp.Arn string `json:\"Arn\"`",
"PartnerApp.AuthType string `json:\"AuthType,omitempty\"`",
"PartnerApp.BaseURL string `json:\"-\"`",
@@ -33039,9 +33039,9 @@
"Project.ProjectName string `json:\"ProjectName\"`",
"Project.ProjectStatus string `json:\"ProjectStatus\"`",
"Project.ProvisionedProductID string `json:\"ProvisionedProductId,omitempty\"`",
- "Project.ServiceCatalogProvisioningDetails json.RawMessage `json:\"ServiceCatalogProvisioningDetails,omitempty\"`",
+ "Project.ServiceCatalogProvisioningDetails jsontext.Value `json:\"ServiceCatalogProvisioningDetails,omitempty\"`",
"Project.Tags map[string]string `json:\"Tags,omitempty\"`",
- "Project.TemplateProviders json.RawMessage `json:\"TemplateProviders,omitempty\"`",
+ "Project.TemplateProviders jsontext.Value `json:\"TemplateProviders,omitempty\"`",
"Project.{MarshalJSON}",
"Project.{UnmarshalJSON}",
"RealTimeInferenceConfig.InstanceCount int32 `json:\"InstanceCount,omitempty\"`",
@@ -33138,12 +33138,12 @@
"Space.CreationTime time.Time `json:\"CreationTime\"`",
"Space.DomainID string `json:\"DomainId\"`",
"Space.LastModifiedTime time.Time `json:\"LastModifiedTime\"`",
- "Space.OwnershipSettings json.RawMessage `json:\"OwnershipSettings,omitempty\"`",
+ "Space.OwnershipSettings jsontext.Value `json:\"OwnershipSettings,omitempty\"`",
"Space.SpaceArn string `json:\"SpaceArn\"`",
"Space.SpaceDisplayName string `json:\"SpaceDisplayName,omitempty\"`",
"Space.SpaceName string `json:\"SpaceName\"`",
- "Space.SpaceSettings json.RawMessage `json:\"SpaceSettings,omitempty\"`",
- "Space.SpaceSharingSettings json.RawMessage `json:\"SpaceSharingSettings,omitempty\"`",
+ "Space.SpaceSettings jsontext.Value `json:\"SpaceSettings,omitempty\"`",
+ "Space.SpaceSharingSettings jsontext.Value `json:\"SpaceSharingSettings,omitempty\"`",
"Space.SpaceStatus string `json:\"Status\"`",
"Space.Tags map[string]string `json:\"Tags,omitempty\"`",
"Space.{MarshalJSON}",
@@ -33331,7 +33331,7 @@
"UserProfile.Tags map[string]string `json:\"Tags,omitempty\"`",
"UserProfile.UserProfileArn string `json:\"UserProfileArn\"`",
"UserProfile.UserProfileName string `json:\"UserProfileName\"`",
- "UserProfile.UserSettings json.RawMessage `json:\"UserSettings,omitempty\"`",
+ "UserProfile.UserSettings jsontext.Value `json:\"UserSettings,omitempty\"`",
"VariantRoutingConfig.RoutingStrategy string `json:\"RoutingStrategy\"`",
"VpcConfig.SecurityGroupIDs []string `json:\"SecurityGroupIds\"`",
"VpcConfig.Subnets []string `json:\"Subnets\"`",
@@ -33370,7 +33370,7 @@
"backendSnapshot.PipelineVersions map[string]map[string][]*PipelineVersion `json:\"pipelineVersions,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.SCPortfolioEnabled map[string]bool `json:\"servicecatalogPortfolioEnabled,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"pendingTrainingPlanExtension.CurrencyCode string `json:\"CurrencyCode\"`",
"pendingTrainingPlanExtension.DurationHours int32 `json:\"DurationHours\"`",
"pendingTrainingPlanExtension.EndDate time.Time `json:\"EndDate\"`",
@@ -33400,7 +33400,7 @@
"Session.LastInvokedAt time.Time `json:\"lastInvokedAt\"`",
"backendSnapshot.Invocations []*Invocation `json:\"invocations\"`",
"backendSnapshot.NextID uint64 `json:\"nextId\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -33473,7 +33473,7 @@
"Target.SqsParameters *SqsParameters `json:\"sqsParameters,omitempty\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"persistedSchedule.ARN string `json:\"arn\"`",
"persistedSchedule.AccountID string `json:\"accountID\"`",
"persistedSchedule.ActionAfterCompletion string `json:\"actionAfterCompletion,omitempty\"`",
@@ -33546,7 +33546,7 @@
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ReplicationConfigs map[string]map[string][]ReplicationStatusType `json:\"replicationConfigs,omitempty\"`",
"backendSnapshot.ResourcePolicies map[string]map[string]string `json:\"resourcePolicies,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"secretSnapshot.ARN string `json:\"arn\"`",
"secretSnapshot.CreatedDate *float64 `json:\"createdDate,omitempty\"`",
"secretSnapshot.CurrentVersionID string `json:\"currentVersionID\"`",
@@ -33755,7 +33755,7 @@
"snapshot.OrgConfig *OrgConfig `json:\"orgConfig\"`",
"snapshot.ProductSubscriptions map[string]string `json:\"productSubscriptions\"`",
"snapshot.StandardsSeq int `json:\"standardsSeq\"`",
- "snapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "snapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"snapshot.Tags map[string]map[string]string `json:\"tags\"`",
"snapshot.TicketV2Seq int `json:\"ticketV2Seq\"`"
],
@@ -33827,7 +33827,7 @@
"backendSnapshot.AppDependencies map[string]map[string][]*ApplicationDependency `json:\"appDependencies\"`",
"backendSnapshot.AppPolicies map[string][]*ApplicationPolicyStatement `json:\"appPolicies\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"cfChangeSetSnapshot.AppName string `json:\"appName\"`",
"cfChangeSetSnapshot.ChangeSet CloudFormationChangeSet `json:\"changeSet\"`",
"cfTemplateSnapshot.AppName string `json:\"appName\"`",
@@ -34014,7 +34014,7 @@
"TagOptionLink.Key string `json:\"key\"`",
"TagOptionLink.ResourceID string `json:\"resourceID\"`",
"TagOptionLink.TagOptionID string `json:\"tagOptionID\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -34076,7 +34076,7 @@
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ServiceAttributes map[string]map[string]string `json:\"serviceAttributes\"`",
"backendSnapshot.SvcCounter int `json:\"svcCounter\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -34140,7 +34140,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.Seq int64 `json:\"seq\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -34244,7 +34244,7 @@
"backendSnapshot.Emails []Email `json:\"emails\"`",
"backendSnapshot.Policies map[string]map[string]string `json:\"policies,omitempty\"`",
"backendSnapshot.Region string `json:\"region,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"configSetSnapshot.Name string `json:\"name\"`",
"configSetSnapshot.Set ConfigurationSet `json:\"set\"`",
"eventDestinationSnapshot.ConfigSetName string `json:\"configSetName\"`",
@@ -34417,7 +34417,7 @@
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ResourceTags map[string]map[string]string `json:\"resourceTags\"`",
"backendSnapshot.ResourceTenants map[string][]string `json:\"resourceTenants\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.TenantResources map[string][]string `json:\"tenantResources\"`",
"backendSnapshot.Tenants map[string]map[string]any `json:\"tenants\"`"
],
@@ -34476,7 +34476,7 @@
"backendSnapshot.ProactiveEngagementStatus string `json:\"proactiveEngagementStatus,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.Subscription *Subscription `json:\"subscription,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -34534,7 +34534,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.SigningKey string `json:\"signingKey\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -34594,7 +34594,7 @@
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.SMSAttributes map[string]string `json:\"smsAttributes,omitempty\"`",
"backendSnapshot.SMSSandboxEnabled *bool `json:\"smsSandboxEnabled,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.TopicMessageArchive map[string][]*ArchivedMessage `json:\"topicMessageArchive,omitempty\"`",
"backendSnapshot.TopicTags map[string]*svcTags.Tags `json:\"topicTags\"`"
],
@@ -34657,7 +34657,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.RecentlyDeleted map[string]int64 `json:\"recentlyDeleted,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"encodedMessageAttribute.Bytes []byte",
"encodedMessageAttribute.Name string",
"fifoRateWindow.calls []time.Time",
@@ -34889,7 +34889,7 @@
"backendSnapshot.PatchGroupToBaseline map[string]map[string]string `json:\"patch_group_to_baseline\"`",
"backendSnapshot.ResourceIDToOpsMetadataArn map[string]map[string]string `json:\"resource_id_to_ops_metadata_arn\"`",
"backendSnapshot.ResourcePolicies map[string]map[string][]*ResourcePolicy `json:\"resource_policies\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]*tags.Tags `json:\"tags\"`",
"idempotencyRecord.CreatedAt time.Time `json:\"createdAt,omitzero\"`",
"idempotencyRecord.Fingerprint string `json:\"fingerprint\"`",
@@ -34985,8 +34985,8 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.ApplicationAssignConfig map[string]bool `json:\"applicationAssignConfig\"`",
"backendSnapshot.ApplicationAssignments map[string][]*ApplicationAssignment `json:\"applicationAssignments\"`",
- "backendSnapshot.ApplicationAuthMethods map[string]map[string]json.RawMessage `json:\"applicationAuthMethods\"`",
- "backendSnapshot.ApplicationGrants map[string]map[string]json.RawMessage `json:\"applicationGrants\"`",
+ "backendSnapshot.ApplicationAuthMethods map[string]map[string]jsontext.Value `json:\"applicationAuthMethods\"`",
+ "backendSnapshot.ApplicationGrants map[string]map[string]jsontext.Value `json:\"applicationGrants\"`",
"backendSnapshot.ApplicationScopes map[string]map[string][]string `json:\"applicationScopes\"`",
"backendSnapshot.ApplicationSessions map[string]string `json:\"applicationSessions\"`",
"backendSnapshot.AssignmentCreationIDs map[string]string `json:\"assignmentCreationIDs\"`",
@@ -34995,7 +34995,7 @@
"backendSnapshot.InstanceRegions map[string][]RegionMetadata `json:\"instanceRegions\"`",
"backendSnapshot.ProvisionedAt map[string]time.Time `json:\"provisionedAt\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"instanceACASnapshot.Config ABACConfig `json:\"config\"`",
"instanceACASnapshot.InstanceArn string `json:\"instanceArn\"`",
"permissionsBoundarySnapshot.Boundary PermissionsBoundary `json:\"boundary\"`",
@@ -35041,7 +35041,7 @@
"Token.StartURL string `json:\"startUrl,omitempty\"`",
"Token.Subject Subject `json:\"subject\"`",
"Token.Token string `json:\"token\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -35208,7 +35208,7 @@
"TracingConfiguration.Enabled bool `json:\"enabled\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"executionSnapshot.Cause string `json:\"cause,omitempty\"`",
"executionSnapshot.Error string `json:\"error,omitempty\"`",
"executionSnapshot.ExecutionArn string `json:\"executionArn\"`",
@@ -35227,7 +35227,7 @@
"executionSnapshot.Status string `json:\"status\"`",
"executionSnapshot.StopDate *float64 `json:\"stopDate,omitempty\"`",
"executionSnapshot.TraceHeader string `json:\"traceHeader,omitempty\"`",
- "handlerSnapshot.Backend json.RawMessage `json:\"backend\"`",
+ "handlerSnapshot.Backend jsontext.Value `json:\"backend\"`",
"handlerSnapshot.Tags map[string]map[string]string `json:\"tags,omitempty\"`",
"integrationsSnapshot.ddbIntegration asl.DynamoDBIntegration",
"integrationsSnapshot.ebIntegration asl.EventBridgeIntegration",
@@ -35326,7 +35326,7 @@
"attachmentSetSnapshot.Set AttachmentSet `json:\"set\"`",
"backendSnapshot.Communications map[string][]Communication `json:\"communications\"`",
"backendSnapshot.NextDisplayID uint64 `json:\"nextDisplayId\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -35444,7 +35444,7 @@
"backendSnapshot.DecisionQueues map[string][]queuedDecisionTask `json:\"decisionQueues,omitempty\"`",
"backendSnapshot.ExecutionOrder []string `json:\"executionOrder\"`",
"backendSnapshot.History map[string][]HistoryEvent `json:\"history,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags,omitempty\"`",
"queuedDecisionTask.RunID string `json:\"runId\"`",
"queuedDecisionTask.ScheduledEventID int64 `json:\"scheduledEventId\"`",
@@ -35627,7 +35627,7 @@
"backendSnapshot.ClientTokenToJobID map[string]map[string]string `json:\"clientTokenToJobId,omitempty\"`",
"backendSnapshot.ExpenseClientTokenToJobID map[string]map[string]string `json:\"expenseClientTokenToJobId,omitempty\"`",
"backendSnapshot.LendingClientTokenToJobID map[string]map[string]string `json:\"lendingClientTokenToJobId,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"expenseJobSnapshot.Job ExpenseJob `json:\"job\"`",
"expenseJobSnapshot.Region string `json:\"region\"`",
"jobSnapshot.Job DocumentJob `json:\"job\"`",
@@ -35720,7 +35720,7 @@
"DBParameterGroup.Description string `json:\"description,omitempty\"`",
"DBParameterGroup.ID string `json:\"id\"`",
"DBParameterGroup.Name string `json:\"name\"`",
- "DBParameterGroup.Parameters json.RawMessage `json:\"parameters,omitempty\"`",
+ "DBParameterGroup.Parameters jsontext.Value `json:\"parameters,omitempty\"`",
"DBParameterGroup.Region string `json:\"region\"`",
"DBParameterGroup.Tags map[string]string `json:\"tags,omitempty\"`",
"LogDelivery.Bucket string `json:\"bucket\"`",
@@ -35746,7 +35746,7 @@
"Placement.SubnetIDs []string `json:\"subnetIds\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 1
},
@@ -35830,7 +35830,7 @@
"accountSettingsSnapshot.QueryCompute *queryComputeSnapshot `json:\"query_compute,omitempty\"`",
"accountSettingsSnapshot.QueryPricingModel string `json:\"query_pricing_model\"`",
"backendSnapshot.AccountSettings map[string]accountSettingsSnapshot `json:\"account_settings\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"queryComputeSnapshot.ActiveQueryTCU *int32 `json:\"active_query_tcu,omitempty\"`",
"queryComputeSnapshot.ComputeMode string `json:\"compute_mode,omitempty\"`",
"queryComputeSnapshot.NotificationConfiguration *accountSettingsNotificationConfigSnapshot `json:\"notification_configuration,omitempty\"`"
@@ -35930,7 +35930,7 @@
"Table.TableStatus string `json:\"table_status\"`",
"backendSnapshot.NextTaskID int `json:\"next_task_id\"`",
"backendSnapshot.Records map[string]map[string][]Record `json:\"records\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`"
],
"version": 1
@@ -36143,7 +36143,7 @@
"VocabularyFilter.VocabularyFilterName string `json:\"vocabularyFilterName\"`",
"VocabularyFilter.Words []string `json:\"words,omitempty\"`",
"backendSnapshot.ResourceTags map[string]map[string]string `json:\"resourceTags\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`"
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`"
],
"version": 2
},
@@ -36382,7 +36382,7 @@
"WorkflowStep.DeleteStepDetails *DeleteStepDetails `json:\"delete_step_details,omitempty\"`",
"WorkflowStep.TagStepDetails *TagStepDetails `json:\"tag_step_details,omitempty\"`",
"WorkflowStep.Type string `json:\"type\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.TagsStore map[string]map[string]string `json:\"tags_store\"`"
],
"version": 1
@@ -36451,7 +36451,7 @@
"TranslationJob.stopRequested bool",
"backendSnapshot.AccountID string `json:\"accountId\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`"
],
"version": 1
@@ -36521,7 +36521,7 @@
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ResourceTags map[string]map[string]string `json:\"resourceTags,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"policyStoreSchemaDTO.PolicyStoreID string `json:\"policyStoreId\"`",
"policyStoreSchemaDTO.Value *PolicyStoreSchema `json:\"value\"`"
],
@@ -36572,7 +36572,7 @@
"backendSnapshot.AuthPolicies map[string]string `json:\"authPolicies\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ResourcePolicies map[string]string `json:\"resourcePolicies\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"backendSnapshot.Targets map[string][]*storedTarget `json:\"targets\"`",
"storedALS.ARN string `json:\"arn\"`",
@@ -36827,7 +36827,7 @@
"backendSnapshot.OutstandingChangeToken string `json:\"outstandingChangeToken,omitempty\"`",
"backendSnapshot.PermissionPolicies map[string]string `json:\"permissionPolicies\"`",
"backendSnapshot.RuleGroupRules map[string][]ActivatedRule `json:\"ruleGroupRules\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`"
],
"version": 1
@@ -36873,43 +36873,43 @@
"RegexPatternSet.Tags map[string]string `json:\"tags,omitempty\"`",
"RuleGroup.ARN string `json:\"arn,omitempty\"`",
"RuleGroup.Capacity int64 `json:\"capacity\"`",
- "RuleGroup.CustomResponseBodies json.RawMessage `json:\"customResponseBodies,omitempty\"`",
+ "RuleGroup.CustomResponseBodies jsontext.Value `json:\"customResponseBodies,omitempty\"`",
"RuleGroup.Description string `json:\"description\"`",
"RuleGroup.ID string `json:\"id\"`",
"RuleGroup.LockToken string `json:\"lockToken\"`",
- "RuleGroup.MonetizationConfig json.RawMessage `json:\"monetizationConfig,omitempty\"`",
+ "RuleGroup.MonetizationConfig jsontext.Value `json:\"monetizationConfig,omitempty\"`",
"RuleGroup.Name string `json:\"name\"`",
"RuleGroup.Rules []map[string]any `json:\"rules,omitempty\"`",
"RuleGroup.Scope string `json:\"scope\"`",
"RuleGroup.Tags map[string]string `json:\"tags,omitempty\"`",
"RuleGroup.VisibilityConfig string `json:\"visibilityConfig\"`",
"WebACL.ARN string `json:\"arn,omitempty\"`",
- "WebACL.ApplicationConfig json.RawMessage `json:\"applicationConfig,omitempty\"`",
- "WebACL.AssociationConfig json.RawMessage `json:\"associationConfig,omitempty\"`",
- "WebACL.CaptchaConfig json.RawMessage `json:\"captchaConfig,omitempty\"`",
- "WebACL.ChallengeConfig json.RawMessage `json:\"challengeConfig,omitempty\"`",
- "WebACL.CustomResponseBodies json.RawMessage `json:\"customResponseBodies,omitempty\"`",
- "WebACL.DataProtectionConfig json.RawMessage `json:\"dataProtectionConfig,omitempty\"`",
- "WebACL.DefaultAction json.RawMessage `json:\"defaultAction,omitempty\"`",
+ "WebACL.ApplicationConfig jsontext.Value `json:\"applicationConfig,omitempty\"`",
+ "WebACL.AssociationConfig jsontext.Value `json:\"associationConfig,omitempty\"`",
+ "WebACL.CaptchaConfig jsontext.Value `json:\"captchaConfig,omitempty\"`",
+ "WebACL.ChallengeConfig jsontext.Value `json:\"challengeConfig,omitempty\"`",
+ "WebACL.CustomResponseBodies jsontext.Value `json:\"customResponseBodies,omitempty\"`",
+ "WebACL.DataProtectionConfig jsontext.Value `json:\"dataProtectionConfig,omitempty\"`",
+ "WebACL.DefaultAction jsontext.Value `json:\"defaultAction,omitempty\"`",
"WebACL.Description string `json:\"description\"`",
"WebACL.ID string `json:\"id\"`",
"WebACL.LockToken string `json:\"lockToken\"`",
- "WebACL.MonetizationConfig json.RawMessage `json:\"monetizationConfig,omitempty\"`",
+ "WebACL.MonetizationConfig jsontext.Value `json:\"monetizationConfig,omitempty\"`",
"WebACL.Name string `json:\"name\"`",
- "WebACL.OnSourceDDoSProtectionConfig json.RawMessage `json:\"onSourceDDoSProtectionConfig,omitempty\"`",
+ "WebACL.OnSourceDDoSProtectionConfig jsontext.Value `json:\"onSourceDDoSProtectionConfig,omitempty\"`",
"WebACL.Rules []map[string]any `json:\"rules,omitempty\"`",
"WebACL.Scope string `json:\"scope\"`",
"WebACL.Tags map[string]string `json:\"tags,omitempty\"`",
"WebACL.TokenDomains []string `json:\"tokenDomains,omitempty\"`",
- "WebACL.VisibilityConfig json.RawMessage `json:\"visibilityConfig,omitempty\"`",
+ "WebACL.VisibilityConfig jsontext.Value `json:\"visibilityConfig,omitempty\"`",
"apiKeySnapshot.Key APIKey `json:\"key\"`",
"apiKeySnapshot.Region string `json:\"region\"`",
"backendSnapshot.AccountID string `json:\"accountID\"`",
"backendSnapshot.Associations map[string]map[string]string `json:\"associations,omitempty\"`",
- "backendSnapshot.LoggingConfigs map[string]map[string]json.RawMessage `json:\"loggingConfigs,omitempty\"`",
+ "backendSnapshot.LoggingConfigs map[string]map[string]jsontext.Value `json:\"loggingConfigs,omitempty\"`",
"backendSnapshot.PermissionPolicies map[string]map[string]string `json:\"permissionPolicies,omitempty\"`",
"backendSnapshot.Region string `json:\"region\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"managedRuleSetSnapshot.Region string `json:\"region\"`",
"managedRuleSetSnapshot.RuleSet ManagedRuleSet `json:\"ruleSet\"`"
],
@@ -37110,7 +37110,7 @@
"backendSnapshot.OrgsByAlias map[string]string `json:\"orgsByAlias\"`",
"backendSnapshot.Region string `json:\"region\"`",
"backendSnapshot.ResourcesByEmail map[string]map[string]string `json:\"resourcesByEmail\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string][]Tag `json:\"tags\"`",
"backendSnapshot.UsersByEmail map[string]map[string]string `json:\"usersByEmail\"`",
"issuedImpersonationToken.ExpiresAt time.Time",
@@ -37162,7 +37162,7 @@
"backendSnapshot.ClientProperties map[string]storedClientProps `json:\"clientProperties,omitempty\"`",
"backendSnapshot.DirectoryIpGroups map[string]map[string]struct{} `json:\"directoryIpGroups\"`",
"backendSnapshot.ImagePermissions map[string]map[string]bool `json:\"imagePermissions,omitempty\"`",
- "backendSnapshot.Tables map[string]json.RawMessage `json:\"tables\"`",
+ "backendSnapshot.Tables map[string]jsontext.Value `json:\"tables\"`",
"backendSnapshot.Tags map[string]map[string]string `json:\"tags\"`",
"connAliasPermission.AccountID string `json:\"accountId\"`",
"connAliasPermission.AllowAssociation bool `json:\"allowAssociation\"`",
diff --git a/pkgs/pods/merge.go b/pkgs/pods/merge.go
index 6cb37536c5..31417784a9 100644
--- a/pkgs/pods/merge.go
+++ b/pkgs/pods/merge.go
@@ -2,8 +2,11 @@ package pods
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type scopeKey struct{ account, region string }
@@ -107,11 +110,10 @@ func mergeValue(cur, inc any, scopes map[scopeKey]bool) any {
}
func decodeJSON(raw []byte) (any, error) {
- dec := json.NewDecoder(bytes.NewReader(raw))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(raw))
var v any
- if err := dec.Decode(&v); err != nil {
+ if err := json.UnmarshalDecode(dec, &v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, fmt.Errorf("%w: %w", ErrCorrupt, err)
}
@@ -140,7 +142,7 @@ func MergeScoped(current, incoming []byte) ([]byte, bool, error) {
return nil, false, err
}
- merged, err := json.Marshal(mergeValue(cur, inc, scopes))
+ merged, err := json.Marshal(mergeValue(cur, inc, scopes), jsonx.V1())
if err != nil {
return nil, false, fmt.Errorf("marshal merged snapshot: %w", err)
}
diff --git a/pkgs/pods/pods.go b/pkgs/pods/pods.go
index 9fdcf4e60b..aed1579b57 100644
--- a/pkgs/pods/pods.go
+++ b/pkgs/pods/pods.go
@@ -4,7 +4,7 @@ import (
"archive/zip"
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -14,6 +14,7 @@ import (
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -156,7 +157,7 @@ func (s *Service) Save(ctx context.Context, name, message string, snaps map[stri
m.Size = int64(len(archive))
- meta, err := json.Marshal(m)
+ meta, err := json.Marshal(m, jsonx.V1())
if err != nil {
return Manifest{}, fmt.Errorf("marshal manifest: %w", err)
}
@@ -179,7 +180,7 @@ func buildArchive(m *Manifest, snaps map[string][]byte) ([]byte, error) {
zw := zip.NewWriter(&buf)
- meta, err := json.Marshal(m)
+ meta, err := json.Marshal(m, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("marshal manifest: %w", err)
}
@@ -262,7 +263,7 @@ func (s *Service) readManifest(ctx context.Context, name string, v int) (Manifes
}
var m Manifest
- if err = json.Unmarshal(raw, &m); err != nil {
+ if err = json.Unmarshal(raw, &m, jsonx.V1()); err != nil {
return Manifest{}, fmt.Errorf("%w: manifest: %w", ErrCorrupt, err)
}
diff --git a/pkgs/regionpeers/regionpeers.go b/pkgs/regionpeers/regionpeers.go
index 24bb98a5fd..aede357921 100644
--- a/pkgs/regionpeers/regionpeers.go
+++ b/pkgs/regionpeers/regionpeers.go
@@ -3,11 +3,14 @@
package regionpeers
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"regexp"
"slices"
"sync"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -113,7 +116,7 @@ func (s *Set[T]) Snapshot(base []byte, snap func(*T) []byte) []byte {
}
s.mu.Lock()
- regions := make(map[string]json.RawMessage, len(s.peers))
+ regions := make(map[string]jsontext.Value, len(s.peers))
for r, p := range s.peers {
if data := snap(p); data != nil {
@@ -126,19 +129,19 @@ func (s *Set[T]) Snapshot(base []byte, snap func(*T) []byte) []byte {
return base
}
- doc := make(map[string]json.RawMessage)
- if err := json.Unmarshal(base, &doc); err != nil {
+ doc := make(map[string]jsontext.Value)
+ if err := json.Unmarshal(base, &doc, jsonx.V1()); err != nil {
return base
}
- enc, err := json.Marshal(regions)
+ enc, err := json.Marshal(regions, jsonx.V1())
if err != nil {
return base
}
doc[snapshotKey] = enc
- out, err := json.Marshal(doc)
+ out, err := json.Marshal(doc, jsonx.V1())
if err != nil {
return base
}
@@ -158,10 +161,10 @@ func (s *Set[T]) Restore(data []byte, restore func(*T, []byte) error, closeFn fu
}
var doc struct {
- Regions map[string]json.RawMessage `json:"regions"`
+ Regions map[string]jsontext.Value `json:"regions"`
}
- if err := json.Unmarshal(data, &doc); err != nil {
+ if err := json.Unmarshal(data, &doc, jsonx.V1()); err != nil {
return err
}
diff --git a/pkgs/regionpeers/regionpeers_test.go b/pkgs/regionpeers/regionpeers_test.go
index a367068151..42b3567184 100644
--- a/pkgs/regionpeers/regionpeers_test.go
+++ b/pkgs/regionpeers/regionpeers_test.go
@@ -1,13 +1,14 @@
package regionpeers_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
)
@@ -57,7 +58,7 @@ func TestSet_SnapshotRestore(t *testing.T) {
t.Parallel()
snap := func(p *fake) []byte { return []byte(`"` + p.state + `"`) }
- restore := func(p *fake, d []byte) error { return json.Unmarshal(d, &p.state) }
+ restore := func(p *fake, d []byte) error { return json.Unmarshal(d, &p.state, jsonx.V1()) }
closeFn := func(*fake) {}
tests := []struct {
diff --git a/pkgs/registryauth/dockerconfig.go b/pkgs/registryauth/dockerconfig.go
index 75d94f0d50..60c491cdcc 100644
--- a/pkgs/registryauth/dockerconfig.go
+++ b/pkgs/registryauth/dockerconfig.go
@@ -4,13 +4,15 @@ import (
"bytes"
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const helperTimeout = 15 * time.Second
@@ -63,7 +65,7 @@ func loadDockerConfig() *dockerConfig {
}
var cfg dockerConfig
- if json.Unmarshal(data, &cfg) != nil {
+ if json.Unmarshal(data, &cfg, jsonx.V1()) != nil {
return nil
}
@@ -157,7 +159,7 @@ func runHelper(ctx context.Context, name, host string) (authConfig, bool) {
}
var rep helperReply
- if json.Unmarshal(out.Bytes(), &rep) != nil || rep.Secret == "" {
+ if json.Unmarshal(out.Bytes(), &rep, jsonx.V1()) != nil || rep.Secret == "" {
return authConfig{}, false
}
diff --git a/pkgs/registryauth/resolver.go b/pkgs/registryauth/resolver.go
index d2922193e7..9ef72126c4 100644
--- a/pkgs/registryauth/resolver.go
+++ b/pkgs/registryauth/resolver.go
@@ -3,10 +3,11 @@ package registryauth
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"sync"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -49,7 +50,7 @@ func (r *Resolver) RegisterEmulated(host, user, pass string) {
}
func encodeAuth(ac authConfig) string {
- raw, err := json.Marshal(ac) //nolint:gosec // credentials are the payload of the X-Registry-Auth header
+ raw, err := json.Marshal(ac, jsonx.V1())
if err != nil {
return ""
}
diff --git a/pkgs/registryauth/resolver_test.go b/pkgs/registryauth/resolver_test.go
index 49ea6e39ba..dd5125eb2b 100644
--- a/pkgs/registryauth/resolver_test.go
+++ b/pkgs/registryauth/resolver_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"log/slog"
"os"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/registryauth"
)
@@ -50,7 +51,7 @@ func decode(t *testing.T, enc string) map[string]string {
require.NoError(t, err)
m := map[string]string{}
- require.NoError(t, json.Unmarshal(raw, &m))
+ require.NoError(t, json.Unmarshal(raw, &m, jsonx.V1()))
return m
}
diff --git a/pkgs/restroute/restroute_test.go b/pkgs/restroute/restroute_test.go
index 64ebc486a2..a7d0bb18e4 100644
--- a/pkgs/restroute/restroute_test.go
+++ b/pkgs/restroute/restroute_test.go
@@ -2,7 +2,7 @@ package restroute_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/restroute"
)
@@ -215,7 +216,7 @@ func TestServerDispatchAndErrors(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Header().Get("X-Amzn-Errortype"))
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
if tt.wantCode != "" {
assert.NotEmpty(t, body["message"])
@@ -242,7 +243,7 @@ func TestErrorMessageDropsRepeatedCode(t *testing.T) {
)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "cell is busy", body["message"])
assert.Equal(t, "R", body["reason"])
}
diff --git a/pkgs/restroute/serve.go b/pkgs/restroute/serve.go
index 90530ecdde..910693fd37 100644
--- a/pkgs/restroute/serve.go
+++ b/pkgs/restroute/serve.go
@@ -2,7 +2,7 @@ package restroute
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Error is an AWS REST-JSON exception: HTTP status, error code, message and optional extra body members.
@@ -61,7 +62,7 @@ func (r *Request) Decode(v any) error {
return nil
}
- if err := json.Unmarshal(r.Body, v); err != nil {
+ if err := json.Unmarshal(r.Body, v, jsonx.V1()); err != nil {
return Validation("request body is not valid JSON for this operation")
}
diff --git a/pkgs/service/cbor.go b/pkgs/service/cbor.go
index 1726e3ec43..91e29e1b0f 100644
--- a/pkgs/service/cbor.go
+++ b/pkgs/service/cbor.go
@@ -2,7 +2,7 @@ package service
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"math"
"net/http"
@@ -11,6 +11,7 @@ import (
"github.com/aws/smithy-go/encoding/cbor"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ContentTypeCBOR is the AWS CBOR wire protocol content type used by DynamoDB, Kinesis, and Timestream.
@@ -39,7 +40,7 @@ func CBORToJSON(data []byte) ([]byte, error) {
generic := cborToInterface(val)
- out, err := json.Marshal(generic)
+ out, err := json.Marshal(generic, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("json marshal: %w", err)
}
@@ -58,7 +59,7 @@ func CBORToJSON(data []byte) ([]byte, error) {
// produces a CBOR list of byte strings when "BS" is in binaryKeys.
func JSONToCBOR(data []byte, binaryKeys map[string]bool) ([]byte, error) {
var generic any
- if err := json.Unmarshal(data, &generic); err != nil {
+ if err := json.Unmarshal(data, &generic, jsonx.V1()); err != nil {
return nil, fmt.Errorf("json unmarshal: %w", err)
}
diff --git a/pkgs/service/cbor_test.go b/pkgs/service/cbor_test.go
index 603e4bd699..6f9f9e9cee 100644
--- a/pkgs/service/cbor_test.go
+++ b/pkgs/service/cbor_test.go
@@ -2,7 +2,7 @@ package service_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/aws/smithy-go/encoding/cbor"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -109,7 +110,7 @@ func TestCBORToJSON(t *testing.T) {
}
var got map[string]any
- if err := json.Unmarshal(jsonBytes, &got); err != nil {
+ if err := json.Unmarshal(jsonBytes, &got, jsonx.V1()); err != nil {
t.Fatalf("unmarshal result: %v", err)
}
@@ -290,15 +291,15 @@ func TestCBORRoundTrip(t *testing.T) {
}
var orig, got map[string]any
- if e := json.Unmarshal([]byte(tc.args.jsonInput), &orig); e != nil {
+ if e := json.Unmarshal([]byte(tc.args.jsonInput), &orig, jsonx.V1()); e != nil {
t.Fatalf("unmarshal orig: %v", e)
}
- if e := json.Unmarshal(jsonOut, &got); e != nil {
+ if e := json.Unmarshal(jsonOut, &got, jsonx.V1()); e != nil {
t.Fatalf("unmarshal got: %v", e)
}
- origJSON, _ := json.Marshal(orig)
- gotJSON, _ := json.Marshal(got)
+ origJSON, _ := json.Marshal(orig, jsonx.V1())
+ gotJSON, _ := json.Marshal(got, jsonx.V1())
if string(origJSON) != string(gotJSON) {
t.Errorf("round-trip mismatch:\n orig: %s\n got: %s", origJSON, gotJSON)
@@ -359,7 +360,7 @@ func TestCBORBinaryRoundTrip(t *testing.T) {
}
var jsonMap map[string]any
- if err = json.Unmarshal(jsonBytes, &jsonMap); err != nil {
+ if err = json.Unmarshal(jsonBytes, &jsonMap, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
diff --git a/pkgs/service/cloudtrail_capture.go b/pkgs/service/cloudtrail_capture.go
index adaacc7b3d..19973e0beb 100644
--- a/pkgs/service/cloudtrail_capture.go
+++ b/pkgs/service/cloudtrail_capture.go
@@ -2,7 +2,7 @@ package service
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"net/http"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// minCredentialParts is the minimum number of slash-delimited fields in an AWS
@@ -275,7 +276,7 @@ func extractErrorInfo(status int, body []byte) (string, string) {
func extractJSONErrorInfo(body []byte) (string, string) {
var env jsonErrorEnvelope
- if err := json.Unmarshal(body, &env); err != nil {
+ if err := json.Unmarshal(body, &env, jsonx.V1()); err != nil {
return "", ""
}
diff --git a/pkgs/service/json_error_message.go b/pkgs/service/json_error_message.go
index 72351eedfb..335affa963 100644
--- a/pkgs/service/json_error_message.go
+++ b/pkgs/service/json_error_message.go
@@ -2,7 +2,8 @@ package service
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"hash/crc32"
"io"
"net/http"
@@ -10,6 +11,8 @@ import (
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// JSONErrorMessageMiddleware mirrors a lone "message" or "Message" key in JSON
@@ -92,8 +95,8 @@ func (w *jsonErrorMirrorWriter) flush() {
}
func mirrorMessageKey(body []byte) []byte {
- var obj map[string]json.RawMessage
- if err := json.Unmarshal(body, &obj); err != nil {
+ var obj map[string]jsontext.Value
+ if err := json.Unmarshal(body, &obj, jsonx.V1()); err != nil {
return body
}
@@ -115,7 +118,7 @@ func mirrorMessageKey(body []byte) []byte {
obj["message"] = upper
}
- out, err := json.Marshal(obj)
+ out, err := json.Marshal(obj, jsonx.V1())
if err != nil {
return body
}
diff --git a/pkgs/service/jsondisp.go b/pkgs/service/jsondisp.go
index 6dbb8916d1..e6b85b885a 100644
--- a/pkgs/service/jsondisp.go
+++ b/pkgs/service/jsondisp.go
@@ -2,7 +2,7 @@ package service
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// JSONErrorResponse is the standard JSON error envelope for AWS JSON-protocol services.
@@ -34,7 +35,7 @@ func (r JSONErrorResponse) MarshalJSON() ([]byte, error) {
Type string `json:"__type"`
Message string `json:"message"`
MessageCap string `json:"Message"`
- }{r.Type, r.Message, r.Message})
+ }{r.Type, r.Message, r.Message}, jsonx.V1())
}
// DispatchFunc is the signature for service-specific action dispatch functions.
@@ -57,7 +58,7 @@ func HandleJSON[In, Out any](
var input In
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -146,7 +147,7 @@ func HandleTarget(
func writeDispatchError(c *echo.Context, contentType string, status int, errType, message string) error {
c.Response().Header().Set("Content-Type", contentType)
- payload, err := json.Marshal(JSONErrorResponse{Type: errType, Message: message})
+ payload, err := json.Marshal(JSONErrorResponse{Type: errType, Message: message}, jsonx.V1())
if err != nil {
return err
}
diff --git a/pkgs/service/jsondisp_golden_test.go b/pkgs/service/jsondisp_golden_test.go
new file mode 100644
index 0000000000..6623e92abb
--- /dev/null
+++ b/pkgs/service/jsondisp_golden_test.go
@@ -0,0 +1,80 @@
+package service_test
+
+import (
+ "context"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/service"
+)
+
+type goldenSendInput struct {
+ QueueURL string `json:"QueueUrl"`
+ MessageBody string `json:"MessageBody"`
+ DelaySeconds *int32 `json:"DelaySeconds,omitempty"`
+ Attributes map[string]string `json:"MessageAttributes"`
+ Ignored string `json:"-"`
+}
+
+func TestJSONErrorResponseGolden(t *testing.T) {
+ t.Parallel()
+
+ resp := service.JSONErrorResponse{Type: "com.amazonaws.sqs#QueueDoesNotExist", Message: "queue & y"}
+ b, err := resp.MarshalJSON()
+ require.NoError(t, err)
+
+ esc := strings.NewReplacer("<", "\\u003c", ">", "\\u003e", "&", "\\u0026").Replace("queue & y")
+ want := `{"__type":"com.amazonaws.sqs#QueueDoesNotExist","message":"` + esc + `","Message":"` + esc + `"}`
+
+ assert.Equal(t, want, string(b))
+}
+
+func TestHandleJSONGoldenDecode(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ body string
+ want goldenSendInput
+ wantErr string
+ }{
+ {
+ name: "basic",
+ body: `{"QueueUrl":"u","MessageBody":"b","DelaySeconds":5,"MessageAttributes":{"a":"b"}}`,
+ want: goldenSendInput{
+ QueueURL: "u", MessageBody: "b", DelaySeconds: new(int32(5)),
+ Attributes: map[string]string{"a": "b"},
+ },
+ },
+ {name: "case insensitive and unknown", body: `{"queueurl":"u","MESSAGEBODY":"b","extra":1}`,
+ want: goldenSendInput{QueueURL: "u", MessageBody: "b"}},
+ {name: "null members", body: `{"QueueUrl":null,"MessageAttributes":null}`, want: goldenSendInput{}},
+ {name: "empty body", body: ``, want: goldenSendInput{}},
+ {
+ name: "type mismatch",
+ body: `{"QueueUrl":5}`,
+ wantErr: "json: cannot unmarshal number into Go struct field goldenSendInput.QueueUrl of type string",
+ },
+ {name: "truncated", body: `{"QueueUrl":`, wantErr: "unexpected end of JSON input"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ got, err := service.HandleJSON(context.Background(), []byte(tt.body),
+ func(_ context.Context, in *goldenSendInput) (*goldenSendInput, error) { return in, nil })
+ if tt.wantErr != "" {
+ require.EqualError(t, err, tt.wantErr)
+
+ return
+ }
+
+ require.NoError(t, err)
+ assert.Equal(t, &tt.want, got)
+ })
+ }
+}
diff --git a/pkgs/service/regions.go b/pkgs/service/regions.go
index a131bb1d8d..dcbe94d3cf 100644
--- a/pkgs/service/regions.go
+++ b/pkgs/service/regions.go
@@ -1,12 +1,13 @@
package service
import (
- "encoding/json"
+ "encoding/json/v2"
"sort"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
)
@@ -94,7 +95,7 @@ func SnapshotKnownRegions() []byte {
return nil
}
- data, err := json.Marshal(regions)
+ data, err := json.Marshal(regions, jsonx.V1())
if err != nil {
return nil
}
@@ -107,7 +108,7 @@ func SnapshotKnownRegions() []byte {
// backfill, not a correctness-critical load.
func RestoreKnownRegions(data []byte) {
var regions []string
- if err := json.Unmarshal(data, ®ions); err != nil {
+ if err := json.Unmarshal(data, ®ions, jsonx.V1()); err != nil {
return
}
diff --git a/pkgs/service/restdispatch.go b/pkgs/service/restdispatch.go
index 97fa5dcbc7..0ef6d606f3 100644
--- a/pkgs/service/restdispatch.go
+++ b/pkgs/service/restdispatch.go
@@ -2,12 +2,13 @@ package service
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -95,7 +96,7 @@ func (r RESTRouter) handleREST(c *echo.Context) error {
return c.JSON(statusCode, struct{}{})
}
- data, jsonErr := json.Marshal(result)
+ data, jsonErr := json.Marshal(result, jsonx.V1())
if jsonErr != nil {
return c.JSON(http.StatusInternalServerError, r.InternalErrorBody())
}
diff --git a/pkgs/service/rpcv2cbor_bridge.go b/pkgs/service/rpcv2cbor_bridge.go
index 614b300ccc..03688baf14 100644
--- a/pkgs/service/rpcv2cbor_bridge.go
+++ b/pkgs/service/rpcv2cbor_bridge.go
@@ -3,7 +3,8 @@ package service
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"strconv"
@@ -13,6 +14,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -122,11 +124,10 @@ func (b RPCv2CBORBridge) jsonToRPCv2CBOR(body []byte) ([]byte, error) {
return cbor.Encode(cbor.Map{}), nil
}
- dec := json.NewDecoder(bytes.NewReader(body))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(body))
var v any
- if err := dec.Decode(&v); err != nil {
+ if err := json.UnmarshalDecode(dec, &v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, err
}
@@ -159,14 +160,14 @@ func (b RPCv2CBORBridge) anyToCBOR(v any, key string) cbor.Value {
return cbor.String(t)
case bool:
return cbor.Bool(t)
- case json.Number:
+ case jsonx.Number:
return jsonNumberToCBOR(t, b.TimeKeys[key])
default:
return &cbor.Nil{}
}
}
-func jsonNumberToCBOR(n json.Number, timestamp bool) cbor.Value {
+func jsonNumberToCBOR(n jsonx.Number, timestamp bool) cbor.Value {
if !timestamp && !strings.ContainsAny(string(n), ".eE") {
if u, err := strconv.ParseUint(string(n), 10, 64); err == nil {
return cbor.Uint(u)
diff --git a/pkgs/store/core.go b/pkgs/store/core.go
index 614b3a6151..0b2da7df83 100644
--- a/pkgs/store/core.go
+++ b/pkgs/store/core.go
@@ -2,11 +2,14 @@ package store
import (
"cmp"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"reflect"
"slices"
"unsafe"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// tableCore is the non-generic engine behind every Table[V]. Values are held
@@ -171,7 +174,7 @@ func (t *tableCore) insertFresh(v unsafe.Pointer) {
// slice is built with reflect so no per-V code (and no *Table[V] interface
// conversion, which would keep every Table method alive in the binary) is
// needed.
-func (t *tableCore) snapshotJSON() (json.RawMessage, error) {
+func (t *tableCore) snapshotJSON() (jsontext.Value, error) {
items := t.snapshot()
sliceType := reflect.SliceOf(reflect.PointerTo(t.elem))
s := reflect.Zero(sliceType) // nil marshals as null, as the typed original did
@@ -184,7 +187,7 @@ func (t *tableCore) snapshotJSON() (json.RawMessage, error) {
s.Index(i).Set(reflect.NewAt(t.elem, p))
}
- data, err := json.Marshal(s.Interface())
+ data, err := json.Marshal(s.Interface(), jsonx.V1())
if err != nil {
return nil, fmt.Errorf("store: marshal table snapshot: %w", err)
}
@@ -193,10 +196,10 @@ func (t *tableCore) snapshotJSON() (json.RawMessage, error) {
}
// restoreJSON loads data produced by snapshotJSON.
-func (t *tableCore) restoreJSON(data json.RawMessage) error {
+func (t *tableCore) restoreJSON(data jsontext.Value) error {
sp := reflect.New(reflect.SliceOf(reflect.PointerTo(t.elem)))
- if err := json.Unmarshal(data, sp.Interface()); err != nil {
+ if err := json.Unmarshal(data, sp.Interface(), jsonx.V1()); err != nil {
return fmt.Errorf("store: unmarshal table snapshot: %w", err)
}
@@ -215,8 +218,8 @@ func (t *tableCore) restoreJSON(data json.RawMessage) error {
// tableSnapshotter is the type-erased contract a [Registry] holds per table.
type tableSnapshotter interface {
reset()
- snapshotJSON() (json.RawMessage, error)
- restoreJSON(data json.RawMessage) error
+ snapshotJSON() (jsontext.Value, error)
+ restoreJSON(data jsontext.Value) error
}
// indexCore is the non-generic engine behind every Index[V].
diff --git a/pkgs/store/registry.go b/pkgs/store/registry.go
index e2f7e1a18e..1f18bdbb1b 100644
--- a/pkgs/store/registry.go
+++ b/pkgs/store/registry.go
@@ -1,7 +1,7 @@
package store
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"sync"
@@ -78,9 +78,9 @@ func (r *Registry) ResetAll() {
// deterministic (see [Table.Snapshot]); marshaling a Go map with string keys
// additionally sorts those keys, so the overall result — and any JSON built
// from it — is byte-for-byte stable across calls for the same backend state.
-func (r *Registry) SnapshotAll() (map[string]json.RawMessage, error) {
+func (r *Registry) SnapshotAll() (map[string]jsontext.Value, error) {
tables := r.all()
- out := make(map[string]json.RawMessage, len(tables))
+ out := make(map[string]jsontext.Value, len(tables))
for name, t := range tables {
data, err := t.snapshotJSON()
@@ -99,7 +99,7 @@ func (r *Registry) SnapshotAll() (map[string]json.RawMessage, error) {
// from data is reset to empty rather than left untouched, so RestoreAll always
// produces the same backend state a fresh [NewRegistry] plus SnapshotAll's
// input would — never a stale mix of old and new state.
-func (r *Registry) RestoreAll(data map[string]json.RawMessage) error {
+func (r *Registry) RestoreAll(data map[string]jsontext.Value) error {
for name, t := range r.all() {
raw, ok := data[name]
if !ok {
diff --git a/pkgs/store/store_test.go b/pkgs/store/store_test.go
index a5649cd8d1..8cd719c504 100644
--- a/pkgs/store/store_test.go
+++ b/pkgs/store/store_test.go
@@ -1,7 +1,8 @@
package store_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"sync"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/store"
)
@@ -336,11 +338,11 @@ func Test_TableSnapshotJSONRoundTrip(t *testing.T) {
src := newWidgetTable(&widget{ID: "a", Group: "g", Count: 1})
- data, err := json.Marshal(src.Snapshot())
+ data, err := json.Marshal(src.Snapshot(), jsonx.V1())
require.NoError(t, err)
var items []*widget
- require.NoError(t, json.Unmarshal(data, &items))
+ require.NoError(t, json.Unmarshal(data, &items, jsonx.V1()))
dst := store.New(widgetKey)
dst.Restore(items)
@@ -590,7 +592,7 @@ func Test_RegistryRestoreAllResetsTablesMissingFromData(t *testing.T) {
widgets.Put(&widget{ID: "stale"})
// data intentionally omits "widgets" entirely.
- require.NoError(t, reg.RestoreAll(map[string]json.RawMessage{}))
+ require.NoError(t, reg.RestoreAll(map[string]jsontext.Value{}))
assert.Equal(t, 0, widgets.Len())
}
diff --git a/pkgs/tags/tags.go b/pkgs/tags/tags.go
index ef4020664e..2faffd45f2 100644
--- a/pkgs/tags/tags.go
+++ b/pkgs/tags/tags.go
@@ -5,9 +5,10 @@
package tags
import (
- "encoding/json"
+ "encoding/json/v2"
"sort"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/safemap"
)
@@ -153,7 +154,7 @@ func MapToKV(m map[string]string) []KV {
// MarshalJSON implements [json.Marshaler].
// Tags serialises as a plain JSON object (e.g. {"env":"prod","team":"platform"}).
func (t *Tags) MarshalJSON() ([]byte, error) {
- return json.Marshal(t.Clone()) //nolint:wrapcheck // thin delegation
+ return json.Marshal(t.Clone(), jsonx.V1()) //nolint:wrapcheck // thin delegation
}
// UnmarshalJSON implements [json.Unmarshaler].
@@ -164,7 +165,7 @@ func (t *Tags) MarshalJSON() ([]byte, error) {
// so the result matches the JSON payload exactly.
func (t *Tags) UnmarshalJSON(data []byte) error {
var m map[string]string
- if err := json.Unmarshal(data, &m); err != nil {
+ if err := json.Unmarshal(data, &m, jsonx.V1()); err != nil {
return err //nolint:wrapcheck // thin delegation
}
diff --git a/pkgs/tags/tags_test.go b/pkgs/tags/tags_test.go
index bb49535e3b..cb16f248d9 100644
--- a/pkgs/tags/tags_test.go
+++ b/pkgs/tags/tags_test.go
@@ -1,11 +1,12 @@
package tags_test
import (
- "encoding/json"
+ "encoding/json/v2"
"sort"
"sync"
"testing"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
"github.com/stretchr/testify/assert"
@@ -332,7 +333,7 @@ func TestTags_MarshalJSON(t *testing.T) {
tt.setup(tg)
}
- data, err := json.Marshal(tg)
+ data, err := json.Marshal(tg, jsonx.V1())
require.NoError(t, err)
assert.JSONEq(t, tt.want, string(data))
})
@@ -351,7 +352,7 @@ func TestTags_MarshalJSON_InStruct(t *testing.T) {
tg.Set("team", "platform")
r := resource{Name: "bucket", Tags: tg}
- data, err := json.Marshal(r)
+ data, err := json.Marshal(r, jsonx.V1())
require.NoError(t, err)
assert.JSONEq(t, `{"name":"bucket","tags":{"team":"platform"}}`, string(data))
}
@@ -379,7 +380,7 @@ func TestTags_UnmarshalJSON(t *testing.T) {
t.Parallel()
tg := tags.New("test.unmarshal." + tt.name)
- err := json.Unmarshal([]byte(tt.input), tg)
+ err := json.Unmarshal([]byte(tt.input), tg, jsonx.V1())
if tt.wantErr {
require.Error(t, err)
@@ -402,7 +403,7 @@ func TestTags_UnmarshalJSON_InStruct(t *testing.T) {
}
r := resource{Tags: tags.New("test.unmarshal.struct")}
- err := json.Unmarshal([]byte(`{"name":"bucket","tags":{"env":"staging"}}`), &r)
+ err := json.Unmarshal([]byte(`{"name":"bucket","tags":{"env":"staging"}}`), &r, jsonx.V1())
require.NoError(t, err)
assert.Equal(t, "bucket", r.Name)
assert.Equal(t, map[string]string{"env": "staging"}, r.Tags.Clone())
@@ -415,11 +416,11 @@ func TestTags_RoundTrip(t *testing.T) {
tg.Set("env", "prod")
tg.Set("team", "platform")
- data, err := json.Marshal(tg)
+ data, err := json.Marshal(tg, jsonx.V1())
require.NoError(t, err)
tg2 := tags.New("test.roundtrip2")
- require.NoError(t, json.Unmarshal(data, tg2))
+ require.NoError(t, json.Unmarshal(data, tg2, jsonx.V1()))
assert.Equal(t, tg.Clone(), tg2.Clone())
}
@@ -431,7 +432,7 @@ func TestTags_UnmarshalJSON_ClearsStaleKeys(t *testing.T) {
require.True(t, tg.HasTag("old-key"))
// Second unmarshal with a different payload must not leave "old-key" behind.
- require.NoError(t, json.Unmarshal([]byte(`{"new-key":"new-value"}`), tg))
+ require.NoError(t, json.Unmarshal([]byte(`{"new-key":"new-value"}`), tg, jsonx.V1()))
assert.False(t, tg.HasTag("old-key"), "stale key must be removed on unmarshal")
v, ok := tg.Get("new-key")
diff --git a/pkgs/wfinterp/flow_test.go b/pkgs/wfinterp/flow_test.go
index 12d635a658..a57f6ea78d 100644
--- a/pkgs/wfinterp/flow_test.go
+++ b/pkgs/wfinterp/flow_test.go
@@ -402,7 +402,7 @@ main:
require.NoError(t, err)
ctx, cancel := context.WithCancel(t.Context())
- started := make(chan struct{})
+ started := make(chan struct{}, 1)
host := &wfinterp.Host{
Clock: &blockingClock{},
Progress: func(f []wfinterp.Frame) {
diff --git a/pkgs/wfinterp/parsejson_test.go b/pkgs/wfinterp/parsejson_test.go
new file mode 100644
index 0000000000..946b9adacd
--- /dev/null
+++ b/pkgs/wfinterp/parsejson_test.go
@@ -0,0 +1,41 @@
+package wfinterp_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/wfinterp"
+)
+
+func TestParseJSONTrailingData(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ in string
+ wantErr bool
+ }{
+ {name: "single", in: `{"a":1}`},
+ {name: "trailing_space", in: "{\"a\":1} \n"},
+ {name: "second_value", in: `{"a":1} {"b":2}`, wantErr: true},
+ {name: "garbage", in: `{"a":1} x`, wantErr: true},
+ {name: "invalid", in: `{"a":`, wantErr: true},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ _, err := wfinterp.ParseJSON([]byte(tt.in))
+ if tt.wantErr {
+ require.Error(t, err)
+
+ return
+ }
+
+ assert.NoError(t, err)
+ })
+ }
+}
diff --git a/pkgs/wfinterp/value.go b/pkgs/wfinterp/value.go
index fca2b3317c..bfc661cff0 100644
--- a/pkgs/wfinterp/value.go
+++ b/pkgs/wfinterp/value.go
@@ -5,13 +5,16 @@ package wfinterp
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"math"
"sort"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errTrailingJSON = errors.New("trailing data")
@@ -165,15 +168,14 @@ func sortedKeys(m map[string]any) []string {
// ParseJSON decodes JSON into workflow values: integers become int64, other numbers float64.
func ParseJSON(raw []byte) (any, error) {
- dec := json.NewDecoder(bytes.NewReader(raw))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(raw))
var v any
- if err := dec.Decode(&v); err != nil {
+ if err := json.UnmarshalDecode(dec, &v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, fmt.Errorf("invalid JSON: %w", err)
}
- if dec.More() {
+ if rest := bytes.TrimLeft(raw[dec.InputOffset():], " \t\r\n"); len(rest) > 0 && rest[0] != ']' && rest[0] != '}' {
return nil, fmt.Errorf("invalid JSON: %w", errTrailingJSON)
}
@@ -182,7 +184,7 @@ func ParseJSON(raw []byte) (any, error) {
func normalizeJSON(v any) any {
switch x := v.(type) {
- case json.Number:
+ case jsonx.Number:
if i, err := x.Int64(); err == nil && !strings.ContainsAny(string(x), ".eE") {
return i
}
@@ -209,7 +211,7 @@ func normalizeJSON(v any) any {
// ToJSON encodes a workflow value as JSON; bytes become base64 strings.
func ToJSON(v any) ([]byte, error) {
- raw, err := json.Marshal(jsonSafe(v))
+ raw, err := json.Marshal(jsonSafe(v), jsonx.V1())
if err != nil {
return nil, fmt.Errorf("encode JSON: %w", err)
}
diff --git a/services/accessanalyzer/access_previews.go b/services/accessanalyzer/access_previews.go
index 14fda3f418..3994e3e724 100644
--- a/services/accessanalyzer/access_previews.go
+++ b/services/accessanalyzer/access_previews.go
@@ -1,7 +1,7 @@
package accessanalyzer
import (
- "encoding/json"
+ "encoding/json/jsontext"
"maps"
"sort"
"time"
@@ -13,7 +13,7 @@ import (
// required and must contain exactly one element (api_op_CreateAccessPreview.go:39-43).
func (b *InMemoryBackend) CreateAccessPreview(
analyzerArn string,
- configurations map[string]json.RawMessage,
+ configurations map[string]jsontext.Value,
) (*AccessPreview, error) {
if len(configurations) != 1 {
return nil, ErrValidation
diff --git a/services/accessanalyzer/analyzer_validation.go b/services/accessanalyzer/analyzer_validation.go
index 9ba2831846..c483d383a0 100644
--- a/services/accessanalyzer/analyzer_validation.go
+++ b/services/accessanalyzer/analyzer_validation.go
@@ -1,11 +1,14 @@
package accessanalyzer
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"regexp"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -28,7 +31,7 @@ func isValidAnalyzerType(t AnalyzerType) bool {
// validateCreateAnalyzer enforces the CreateAnalyzer name pattern (1-255,
// [A-Za-z][A-Za-z0-9_.-]*), the Type enum, and that the configuration union
// member matches the analyzer type.
-func validateCreateAnalyzer(name string, t AnalyzerType, config json.RawMessage) error {
+func validateCreateAnalyzer(name string, t AnalyzerType, config jsontext.Value) error {
if len(name) > maxAnalyzerNameLen || !analyzerNameRe.MatchString(name) {
return fmt.Errorf("%w: analyzerName must match [A-Za-z][A-Za-z0-9_.-]* (1-255 characters)", ErrValidation)
}
@@ -36,7 +39,7 @@ func validateCreateAnalyzer(name string, t AnalyzerType, config json.RawMessage)
return validateAnalyzerTypeAndConfig(t, config)
}
-func validateAnalyzerTypeAndConfig(t AnalyzerType, config json.RawMessage) error {
+func validateAnalyzerTypeAndConfig(t AnalyzerType, config jsontext.Value) error {
if !isValidAnalyzerType(t) {
return fmt.Errorf("%w: unsupported analyzer type %q", ErrValidation, t)
}
@@ -45,8 +48,8 @@ func validateAnalyzerTypeAndConfig(t AnalyzerType, config json.RawMessage) error
return nil
}
- var members map[string]json.RawMessage
- if err := json.Unmarshal(config, &members); err != nil {
+ var members map[string]jsontext.Value
+ if err := json.Unmarshal(config, &members, jsonx.V1()); err != nil {
return fmt.Errorf("%w: malformed configuration", ErrValidation)
}
@@ -65,12 +68,12 @@ func validateAnalyzerTypeAndConfig(t AnalyzerType, config json.RawMessage) error
return nil
}
-func validateUnusedAccessAge(raw json.RawMessage) error {
+func validateUnusedAccessAge(raw jsontext.Value) error {
var cfg struct {
UnusedAccessAge *int `json:"unusedAccessAge"`
}
- if err := json.Unmarshal(raw, &cfg); err != nil {
+ if err := json.Unmarshal(raw, &cfg, jsonx.V1()); err != nil {
return fmt.Errorf("%w: malformed unusedAccess configuration", ErrValidation)
}
diff --git a/services/accessanalyzer/analyzers.go b/services/accessanalyzer/analyzers.go
index ddd6e88374..ff40b8b8b8 100644
--- a/services/accessanalyzer/analyzers.go
+++ b/services/accessanalyzer/analyzers.go
@@ -1,7 +1,7 @@
package accessanalyzer
import (
- "encoding/json"
+ "encoding/json/jsontext"
"slices"
"sort"
"time"
@@ -15,7 +15,7 @@ import (
// UpdateAnalyzer all accept the AnalyzerConfiguration union as an optional
// parameter this way so existing call sites (which never set it) do not
// need to change.
-func firstConfiguration(cs []json.RawMessage) json.RawMessage {
+func firstConfiguration(cs []jsontext.Value) jsontext.Value {
if len(cs) == 0 {
return nil
}
@@ -34,7 +34,7 @@ func (b *InMemoryBackend) CreateAnalyzer(
name string,
analyzerType AnalyzerType,
tags map[string]string,
- configuration ...json.RawMessage,
+ configuration ...jsontext.Value,
) (*Analyzer, error) {
if name == "" {
return nil, ErrValidation
@@ -154,7 +154,7 @@ func (b *InMemoryBackend) DeleteAnalyzer(name string) error {
// AnalyzerConfiguration union body (see Analyzer.Configuration).
func (b *InMemoryBackend) CreateServiceLinkedAnalyzer(
analyzerType AnalyzerType,
- configuration ...json.RawMessage,
+ configuration ...jsontext.Value,
) (*Analyzer, error) {
name := "_AccessAnalyzerForInternalUse-" + uuid.NewString()[:8]
@@ -166,7 +166,7 @@ func (b *InMemoryBackend) CreateServiceLinkedAnalyzer(
// AnalyzerConfiguration; otherwise the existing configuration is left
// untouched and simply echoed back, matching UpdateAnalyzerOutput's
// contract of always returning the analyzer's current configuration.
-func (b *InMemoryBackend) UpdateAnalyzer(name string, configuration ...json.RawMessage) (*Analyzer, error) {
+func (b *InMemoryBackend) UpdateAnalyzer(name string, configuration ...jsontext.Value) (*Analyzer, error) {
b.mu.Lock("UpdateAnalyzer")
defer b.mu.Unlock()
diff --git a/services/accessanalyzer/analyzers_test.go b/services/accessanalyzer/analyzers_test.go
index 978f0c7d70..04c29d49f5 100644
--- a/services/accessanalyzer/analyzers_test.go
+++ b/services/accessanalyzer/analyzers_test.go
@@ -1,7 +1,7 @@
package accessanalyzer_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"testing"
"github.com/stretchr/testify/assert"
@@ -101,8 +101,8 @@ func TestDeleteAnalyzer_CascadesGhostRows(t *testing.T) {
_, err = b.AddAnalyzedResource(a.Arn, "arn:aws:s3:::analyzed-bucket", "AWS::S3::Bucket", false)
require.NoError(t, err)
- preview, err := b.CreateAccessPreview(a.Arn, map[string]json.RawMessage{
- "arn:aws:s3:::analyzed-bucket": json.RawMessage(`{"s3Bucket":{"bucketPolicy":"{}"}}`),
+ preview, err := b.CreateAccessPreview(a.Arn, map[string]jsontext.Value{
+ "arn:aws:s3:::analyzed-bucket": jsontext.Value(`{"s3Bucket":{"bucketPolicy":"{}"}}`),
})
require.NoError(t, err)
diff --git a/services/accessanalyzer/handler.go b/services/accessanalyzer/handler.go
index dece6fc389..0d1b151376 100644
--- a/services/accessanalyzer/handler.go
+++ b/services/accessanalyzer/handler.go
@@ -2,7 +2,7 @@ package accessanalyzer
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -232,7 +233,7 @@ func (h *Handler) handleREST(c *echo.Context) error {
return c.JSON(statusCode, struct{}{})
}
- data, err := json.Marshal(result)
+ data, err := json.Marshal(result, jsonx.V1())
if err != nil {
return c.JSON(http.StatusInternalServerError,
errorBody("InternalFailure", "failed to serialize response"))
diff --git a/services/accessanalyzer/handler_access_previews.go b/services/accessanalyzer/handler_access_previews.go
index fd5d819293..53fc5c39f3 100644
--- a/services/accessanalyzer/handler_access_previews.go
+++ b/services/accessanalyzer/handler_access_previews.go
@@ -1,9 +1,12 @@
package accessanalyzer
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -54,11 +57,11 @@ func (h *Handler) dispatchAccessPreviewOps(
// AccessPreview.Configurations godoc).
func (h *Handler) handleCreateAccessPreview(body []byte) (any, int, error) {
var req struct {
- Configurations map[string]json.RawMessage `json:"configurations"`
- AnalyzerArn string `json:"analyzerArn"`
+ Configurations map[string]jsontext.Value `json:"configurations"`
+ AnalyzerArn string `json:"analyzerArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
@@ -135,7 +138,7 @@ func (h *Handler) handleListAccessPreviewFindings(path string, body []byte) (any
MaxResults int `json:"maxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
diff --git a/services/accessanalyzer/handler_access_previews_test.go b/services/accessanalyzer/handler_access_previews_test.go
index 7726238344..31a8ce279b 100644
--- a/services/accessanalyzer/handler_access_previews_test.go
+++ b/services/accessanalyzer/handler_access_previews_test.go
@@ -1,13 +1,14 @@
package accessanalyzer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/accessanalyzer"
)
@@ -43,7 +44,7 @@ func TestAccessPreviewLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
previewID := created["id"]
require.NotEmpty(t, previewID)
@@ -51,7 +52,7 @@ func TestAccessPreviewLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &got, jsonx.V1()))
ap := got["accessPreview"].(map[string]any)
assert.Equal(t, previewID, ap["id"])
assert.Equal(t, "COMPLETED", ap["status"])
@@ -107,7 +108,7 @@ func TestAccessPreviewLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
previews, ok := resp["accessPreviews"].([]any)
require.True(t, ok)
require.Len(t, previews, 1)
@@ -134,7 +135,7 @@ func TestAccessPreviewLifecycle(t *testing.T) {
"configurations": singleS3BucketConfig("arn:aws:s3:::preview-findings-bucket"),
})
var created map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
previewID := created["id"]
rec2 := doRequest(t, h, http.MethodPost, "/access-preview/"+previewID, map[string]any{
@@ -143,7 +144,7 @@ func TestAccessPreviewLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
findings := resp["findings"].([]any)
require.Len(t, findings, 1)
@@ -170,7 +171,7 @@ func TestAccessPreviewLifecycle(t *testing.T) {
"configurations": singleS3BucketConfig("arn:aws:s3:::preview-findings-no-arn-bucket"),
})
var created map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
previewID := created["id"]
// ListAccessPreviewFindingsInput requires analyzerArn.
diff --git a/services/accessanalyzer/handler_analyzed_resources.go b/services/accessanalyzer/handler_analyzed_resources.go
index 50740fbf2a..66827a3a03 100644
--- a/services/accessanalyzer/handler_analyzed_resources.go
+++ b/services/accessanalyzer/handler_analyzed_resources.go
@@ -1,9 +1,11 @@
package accessanalyzer
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -69,7 +71,7 @@ func (h *Handler) handleListAnalyzedResources(body []byte) (any, int, error) {
MaxResults int `json:"maxResults"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
resources, nextToken, err := h.Backend.ListAnalyzedResources(
req.AnalyzerArn, req.ResourceType, req.MaxResults, req.NextToken,
@@ -106,7 +108,7 @@ func (h *Handler) handleStartResourceScan(body []byte) (int, error) {
ResourceArn string `json:"resourceArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return 0, ErrValidation
}
diff --git a/services/accessanalyzer/handler_analyzed_resources_test.go b/services/accessanalyzer/handler_analyzed_resources_test.go
index 29e97ee765..ec90b6d558 100644
--- a/services/accessanalyzer/handler_analyzed_resources_test.go
+++ b/services/accessanalyzer/handler_analyzed_resources_test.go
@@ -1,13 +1,14 @@
package accessanalyzer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/accessanalyzer"
)
@@ -34,7 +35,7 @@ func TestAnalyzedResourceLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
resource := resp["resource"].(map[string]any)
assert.Equal(t, resourceArn, resource["resourceArn"])
// resourceOwnerAccount is a required AnalyzedResource member.
@@ -70,7 +71,7 @@ func TestAnalyzedResourceLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
resources := resp["analyzedResources"].([]any)
assert.Len(t, resources, 2)
diff --git a/services/accessanalyzer/handler_analyzers.go b/services/accessanalyzer/handler_analyzers.go
index 0e4f65fea6..1b8aba9033 100644
--- a/services/accessanalyzer/handler_analyzers.go
+++ b/services/accessanalyzer/handler_analyzers.go
@@ -1,10 +1,13 @@
package accessanalyzer
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -63,13 +66,13 @@ func (h *Handler) dispatchAnalyzerOps(op, path, query string, body []byte) (any,
func (h *Handler) handleCreateAnalyzer(body []byte) (any, int, error) {
var req struct {
Tags map[string]string `json:"tags"`
- Configuration json.RawMessage `json:"configuration"`
+ Configuration jsontext.Value `json:"configuration"`
AnalyzerName string `json:"analyzerName"`
Type string `json:"type"`
ArchiveRules []inlineArchiveRule `json:"archiveRules"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
@@ -186,10 +189,10 @@ func (h *Handler) handleUpdateAnalyzer(path string, body []byte) (any, int, erro
name := extractAnalyzerName(path)
var req struct {
- Configuration json.RawMessage `json:"configuration"`
+ Configuration jsontext.Value `json:"configuration"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
@@ -208,12 +211,12 @@ func (h *Handler) handleUpdateAnalyzer(path string, body []byte) (any, int, erro
func (h *Handler) handleCreateServiceLinkedAnalyzer(body []byte) (any, int, error) {
var req struct {
- Configuration json.RawMessage `json:"configuration"`
+ Configuration jsontext.Value `json:"configuration"`
Type string `json:"type"`
ArchiveRules []inlineArchiveRule `json:"archiveRules"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
diff --git a/services/accessanalyzer/handler_analyzers_test.go b/services/accessanalyzer/handler_analyzers_test.go
index 8ceb0167b5..8cfdffd9f8 100644
--- a/services/accessanalyzer/handler_analyzers_test.go
+++ b/services/accessanalyzer/handler_analyzers_test.go
@@ -1,7 +1,7 @@
package accessanalyzer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/accessanalyzer"
)
@@ -30,7 +31,7 @@ func TestServiceLinkedAnalyzerLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.True(t, strings.HasPrefix(resp["arn"], "arn:aws:access-analyzer:"))
},
},
@@ -112,7 +113,7 @@ func TestUpdateAnalyzer(t *testing.T) {
// UpdateAnalyzerOutput carries only "configuration" -- unlike
// CreateAnalyzer/CreateServiceLinkedAnalyzer, it has no "arn" member.
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasArn := resp["arn"]
assert.False(t, hasArn, `UpdateAnalyzerOutput has no "arn" member`)
})
@@ -145,7 +146,7 @@ func TestAnalyzerConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
analyzer, ok := getResp["analyzer"].(map[string]any)
require.True(t, ok)
@@ -160,7 +161,7 @@ func TestAnalyzerConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
analyzers, ok := listResp["analyzers"].([]any)
require.True(t, ok)
require.Len(t, analyzers, 1)
@@ -185,7 +186,7 @@ func TestAnalyzerConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cfg, ok := resp["configuration"].(map[string]any)
require.True(t, ok)
unused, ok := cfg["unusedAccess"].(map[string]any)
diff --git a/services/accessanalyzer/handler_archive_rules.go b/services/accessanalyzer/handler_archive_rules.go
index bac1b102b0..ca00d58268 100644
--- a/services/accessanalyzer/handler_archive_rules.go
+++ b/services/accessanalyzer/handler_archive_rules.go
@@ -1,11 +1,13 @@
package accessanalyzer
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -78,7 +80,7 @@ func (h *Handler) handleCreateArchiveRule(path string, body []byte) (int, error)
RuleName string `json:"ruleName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return 0, ErrValidation
}
@@ -154,7 +156,7 @@ func (h *Handler) handleUpdateArchiveRule(path string, body []byte) (int, error)
Filter map[string]FilterCriterion `json:"filter"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return 0, ErrValidation
}
@@ -171,7 +173,7 @@ func (h *Handler) handleApplyArchiveRule(body []byte) (int, error) {
RuleName string `json:"ruleName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return 0, ErrValidation
}
diff --git a/services/accessanalyzer/handler_findings.go b/services/accessanalyzer/handler_findings.go
index 79d73d814d..c27f68df71 100644
--- a/services/accessanalyzer/handler_findings.go
+++ b/services/accessanalyzer/handler_findings.go
@@ -1,10 +1,12 @@
package accessanalyzer
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -108,7 +110,7 @@ func (h *Handler) handleListFindings(body []byte) (any, int, error) {
MaxResults int `json:"maxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
@@ -151,7 +153,7 @@ func (h *Handler) handleUpdateFindings(body []byte) (int, error) {
IDs []string `json:"ids"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return 0, ErrValidation
}
@@ -212,7 +214,7 @@ func (h *Handler) handleListFindingsV2(body []byte) (any, int, error) {
MaxResults int `json:"maxResults"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
findings, nextToken, err := h.Backend.ListFindingsV2(
req.AnalyzerArn, req.Status, req.Filter, req.Sort, req.MaxResults, req.NextToken,
@@ -273,7 +275,7 @@ func (h *Handler) handleGetFindingsStatistics(body []byte) (any, int, error) {
AnalyzerArn string `json:"analyzerArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
diff --git a/services/accessanalyzer/handler_findings_test.go b/services/accessanalyzer/handler_findings_test.go
index 228bd4a7a2..f6acf3eb38 100644
--- a/services/accessanalyzer/handler_findings_test.go
+++ b/services/accessanalyzer/handler_findings_test.go
@@ -1,7 +1,7 @@
package accessanalyzer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/accessanalyzer"
)
@@ -59,7 +60,7 @@ func TestGetFinding(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
finding, ok := resp["finding"].(map[string]any)
require.True(t, ok)
@@ -105,7 +106,7 @@ func TestListFindings(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
findings, ok := resp["findings"].([]any)
require.True(t, ok)
@@ -223,7 +224,7 @@ func TestFindingV2Lifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, findingID, resp["id"])
// findingType and the ExternalAccessDetails union member of
// findingDetails are required GetFindingV2Output members.
@@ -253,7 +254,7 @@ func TestFindingV2Lifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
findings := resp["findings"].([]any)
assert.Len(t, findings, 2)
first, ok := findings[0].(map[string]any)
@@ -331,7 +332,7 @@ func TestGetFindingsStatistics(t *testing.T) {
if tt.wantStatus == http.StatusOK && tt.wantActive > 0 {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stats := resp["findingsStatistics"].([]any)
require.Len(t, stats, 1)
@@ -368,7 +369,7 @@ func TestFindingRecommendationLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["recommendationType"])
},
},
@@ -436,7 +437,7 @@ func TestGetFindingRecommendation_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "UnusedPermissionRecommendation", resp["recommendationType"])
assert.Equal(t, "arn:aws:s3:::bucket", resp["resourceArn"])
diff --git a/services/accessanalyzer/handler_generated_policies.go b/services/accessanalyzer/handler_generated_policies.go
index 451fa61629..0f5cae1b2d 100644
--- a/services/accessanalyzer/handler_generated_policies.go
+++ b/services/accessanalyzer/handler_generated_policies.go
@@ -1,11 +1,13 @@
package accessanalyzer
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strconv"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// errPolicyGenerationJobNotModeled translates ErrPolicyGenerationNotFound for
@@ -82,7 +84,7 @@ func (h *Handler) handleStartPolicyGeneration(body []byte) (any, int, error) {
} `json:"policyGenerationDetails"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
diff --git a/services/accessanalyzer/handler_generated_policies_test.go b/services/accessanalyzer/handler_generated_policies_test.go
index 040d669870..99cfc5a4e8 100644
--- a/services/accessanalyzer/handler_generated_policies_test.go
+++ b/services/accessanalyzer/handler_generated_policies_test.go
@@ -1,7 +1,7 @@
package accessanalyzer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/accessanalyzer"
)
@@ -36,7 +37,7 @@ func TestPolicyGenerationLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["jobId"])
},
},
@@ -55,7 +56,7 @@ func TestPolicyGenerationLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["policyGenerations"])
},
},
@@ -102,7 +103,7 @@ func TestGetGeneratedPolicy(t *testing.T) {
"policyGenerationDetails": map[string]any{"principalArn": "arn:aws:iam::000000000000:role/R"},
})
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
jobID = resp["jobId"]
}
@@ -117,7 +118,7 @@ func TestGetGeneratedPolicy(t *testing.T) {
// principalArn member -- that value only appears under
// generatedPolicyResult.properties.principalArn for this operation.
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
jobDetails, ok := resp["jobDetails"].(map[string]any)
require.True(t, ok)
_, hasPrincipalArn := jobDetails["principalArn"]
@@ -163,7 +164,7 @@ func TestCancelPolicyGeneration(t *testing.T) {
"policyGenerationDetails": map[string]any{"principalArn": "arn:aws:iam::000000000000:role/R"},
})
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
jobID = resp["jobId"]
}
@@ -198,13 +199,13 @@ func TestStartPolicyGeneration_CloudTrailDetails(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
getRec := doRequest(t, h, http.MethodGet, "/policy/generation/"+startResp["jobId"], nil)
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
result, ok := resp["generatedPolicyResult"].(map[string]any)
require.True(t, ok)
@@ -249,7 +250,7 @@ func TestListPolicyGenerations_MaxResultsAndNextToken(t *testing.T) {
require.Equal(t, http.StatusOK, firstRec.Code)
var firstResp map[string]any
- require.NoError(t, json.Unmarshal(firstRec.Body.Bytes(), &firstResp))
+ require.NoError(t, json.Unmarshal(firstRec.Body.Bytes(), &firstResp, jsonx.V1()))
firstPage, ok := firstResp["policyGenerations"].([]any)
require.True(t, ok)
@@ -262,7 +263,7 @@ func TestListPolicyGenerations_MaxResultsAndNextToken(t *testing.T) {
require.Equal(t, http.StatusOK, secondRec.Code)
var secondResp map[string]any
- require.NoError(t, json.Unmarshal(secondRec.Body.Bytes(), &secondResp))
+ require.NoError(t, json.Unmarshal(secondRec.Body.Bytes(), &secondResp, jsonx.V1()))
secondPage, ok := secondResp["policyGenerations"].([]any)
require.True(t, ok)
diff --git a/services/accessanalyzer/handler_policy_validation.go b/services/accessanalyzer/handler_policy_validation.go
index b04510809b..a5a96bdda2 100644
--- a/services/accessanalyzer/handler_policy_validation.go
+++ b/services/accessanalyzer/handler_policy_validation.go
@@ -1,9 +1,11 @@
package accessanalyzer
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -57,7 +59,7 @@ func (h *Handler) handleCheckAccessNotGranted(body []byte) (any, int, error) {
Access []AccessSpec `json:"access"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
@@ -86,7 +88,7 @@ func (h *Handler) handleCheckNoNewAccess(body []byte) (any, int, error) {
PolicyType string `json:"policyType"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
@@ -114,7 +116,7 @@ func (h *Handler) handleCheckNoPublicAccess(body []byte) (any, int, error) {
ResourceType string `json:"resourceType"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
@@ -143,7 +145,7 @@ func (h *Handler) handleValidatePolicy(body []byte, query string) (any, int, err
ResourceType string `json:"validatePolicyResourceType"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
diff --git a/services/accessanalyzer/handler_policy_validation_test.go b/services/accessanalyzer/handler_policy_validation_test.go
index f92c6e5790..7ece7c3b07 100644
--- a/services/accessanalyzer/handler_policy_validation_test.go
+++ b/services/accessanalyzer/handler_policy_validation_test.go
@@ -1,12 +1,14 @@
package accessanalyzer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestCheckPolicyOps verifies Check* operations return PASS.
@@ -55,7 +57,7 @@ func TestCheckPolicyOps(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "PASS", resp["result"])
})
}
@@ -165,7 +167,7 @@ func TestCheckPolicyOps_MalformedPolicyDocument(t *testing.T) {
assert.Equal(t, http.StatusUnprocessableEntity, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "UnprocessableEntityException", resp["__type"])
})
}
@@ -183,7 +185,7 @@ func TestValidatePolicy(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["findings"])
}
@@ -204,7 +206,7 @@ func TestValidatePolicy_FindingDetailsPopulated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
findings, ok := resp["findings"].([]any)
require.True(t, ok)
require.NotEmpty(t, findings)
diff --git a/services/accessanalyzer/handler_tags.go b/services/accessanalyzer/handler_tags.go
index f987e957bb..c40ae3e40a 100644
--- a/services/accessanalyzer/handler_tags.go
+++ b/services/accessanalyzer/handler_tags.go
@@ -1,10 +1,12 @@
package accessanalyzer
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -36,7 +38,7 @@ func (h *Handler) handleTagResource(path string, body []byte) (any, int, error)
Tags map[string]string `json:"tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
diff --git a/services/accessanalyzer/handler_test.go b/services/accessanalyzer/handler_test.go
index e90700ea3d..b9027d8be4 100644
--- a/services/accessanalyzer/handler_test.go
+++ b/services/accessanalyzer/handler_test.go
@@ -2,7 +2,7 @@ package accessanalyzer_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/accessanalyzer"
)
@@ -33,7 +34,7 @@ func doRequest(t *testing.T, h *accessanalyzer.Handler, method, path string, bod
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
diff --git a/services/accessanalyzer/interfaces.go b/services/accessanalyzer/interfaces.go
index b901307c4a..de86ab5985 100644
--- a/services/accessanalyzer/interfaces.go
+++ b/services/accessanalyzer/interfaces.go
@@ -2,7 +2,7 @@ package accessanalyzer
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
)
// StorageBackend defines the interface for Access Analyzer backend implementations.
@@ -11,13 +11,13 @@ type StorageBackend interface {
// Analyzer operations. configuration is the optional raw
// AnalyzerConfiguration union wire body -- see Analyzer.Configuration.
CreateAnalyzer(
- name string, analyzerType AnalyzerType, tags map[string]string, configuration ...json.RawMessage,
+ name string, analyzerType AnalyzerType, tags map[string]string, configuration ...jsontext.Value,
) (*Analyzer, error)
GetAnalyzer(name string) (*Analyzer, error)
ListAnalyzers(analyzerType string) ([]*Analyzer, error)
DeleteAnalyzer(name string) error
- UpdateAnalyzer(name string, configuration ...json.RawMessage) (*Analyzer, error)
- CreateServiceLinkedAnalyzer(analyzerType AnalyzerType, configuration ...json.RawMessage) (*Analyzer, error)
+ UpdateAnalyzer(name string, configuration ...jsontext.Value) (*Analyzer, error)
+ CreateServiceLinkedAnalyzer(analyzerType AnalyzerType, configuration ...jsontext.Value) (*Analyzer, error)
// Archive rule operations
CreateArchiveRule(analyzerName, ruleName string, filter map[string]FilterCriterion) (*ArchiveRule, error)
@@ -82,7 +82,7 @@ type StorageBackend interface {
ListPolicyGenerations(principalArn string, maxResults int, nextToken string) ([]*PolicyGeneration, string, error)
// Access previews
- CreateAccessPreview(analyzerArn string, configurations map[string]json.RawMessage) (*AccessPreview, error)
+ CreateAccessPreview(analyzerArn string, configurations map[string]jsontext.Value) (*AccessPreview, error)
GetAccessPreview(accessPreviewID string) (*AccessPreview, error)
ListAccessPreviews(analyzerArn string) ([]*AccessPreview, error)
ListAccessPreviewFindings(
diff --git a/services/accessanalyzer/models.go b/services/accessanalyzer/models.go
index 79b937f5be..32470f04da 100644
--- a/services/accessanalyzer/models.go
+++ b/services/accessanalyzer/models.go
@@ -1,7 +1,7 @@
package accessanalyzer
import (
- "encoding/json"
+ "encoding/json/jsontext"
"maps"
"time"
)
@@ -72,7 +72,7 @@ type Analyzer struct {
Name string `json:"name"`
Type AnalyzerType `json:"type"`
Status AnalyzerStatus `json:"status"`
- Configuration json.RawMessage `json:"configuration,omitempty"`
+ Configuration jsontext.Value `json:"configuration,omitempty"`
}
// ArchiveRule represents an archive rule for an analyzer.
@@ -169,7 +169,7 @@ const (
// echoed back opaquely as submitted instead of decoded into the full union.
type AccessPreview struct {
CreatedAt time.Time
- Configurations map[string]json.RawMessage
+ Configurations map[string]jsontext.Value
ID string
AnalyzerArn string
Status AccessPreviewStatus
@@ -230,7 +230,7 @@ func cloneFilter(f map[string]FilterCriterion) map[string]FilterCriterion {
func copyAnalyzer(a *Analyzer) *Analyzer {
cp := *a
cp.Tags = cloneTags(a.Tags)
- cp.Configuration = append(json.RawMessage(nil), a.Configuration...)
+ cp.Configuration = append(jsontext.Value(nil), a.Configuration...)
return &cp
}
diff --git a/services/accessanalyzer/persistence.go b/services/accessanalyzer/persistence.go
index 7b3d95b0de..355a474a75 100644
--- a/services/accessanalyzer/persistence.go
+++ b/services/accessanalyzer/persistence.go
@@ -2,7 +2,7 @@ package accessanalyzer
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"time"
@@ -96,7 +96,7 @@ func buildPersistenceDTORegistry() (*store.Registry, *store.Table[archiveRuleSna
// identity of its own, so it is persisted here directly, matching its
// pre-refactor shape exactly.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
AccountID string `json:"accountID"`
Region string `json:"region"`
@@ -194,7 +194,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// store_setup.go's registerAllTables doc) from tables via the ephemeral DTO
// registry, factored out of Restore to keep Restore's own cognitive
// complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreArchiveRules(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreArchiveRules(tables map[string]jsontext.Value) error {
dtoReg, archiveRuleDTOs := buildPersistenceDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
return fmt.Errorf("accessanalyzer: restore snapshot DTO tables: %w", err)
diff --git a/services/accessanalyzer/persistence_test.go b/services/accessanalyzer/persistence_test.go
index 4a31cd1d16..6e0add3b36 100644
--- a/services/accessanalyzer/persistence_test.go
+++ b/services/accessanalyzer/persistence_test.go
@@ -1,7 +1,7 @@
package accessanalyzer_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"testing"
"time"
@@ -88,7 +88,7 @@ func TestInMemoryBackend_SnapshotRestore_FullState(t *testing.T) {
analyzer, err := original.CreateAnalyzer(
"analyzer-1", accessanalyzer.AnalyzerTypeAccount, map[string]string{"env": "test"},
- json.RawMessage(`{"unusedAccess":{"unusedAccessAge":90}}`),
+ jsontext.Value(`{"unusedAccess":{"unusedAccessAge":90}}`),
)
require.NoError(t, err)
@@ -119,8 +119,8 @@ func TestInMemoryBackend_SnapshotRestore_FullState(t *testing.T) {
)
require.NoError(t, err)
- ap, err := original.CreateAccessPreview(analyzer.Arn, map[string]json.RawMessage{
- "arn:aws:s3:::bucket-2": json.RawMessage(`{"s3Bucket":{"bucketPolicy":"{}"}}`),
+ ap, err := original.CreateAccessPreview(analyzer.Arn, map[string]jsontext.Value{
+ "arn:aws:s3:::bucket-2": jsontext.Value(`{"s3Bucket":{"bucketPolicy":"{}"}}`),
})
require.NoError(t, err)
diff --git a/services/accessanalyzer/policy_analysis.go b/services/accessanalyzer/policy_analysis.go
index c09b552aee..63dfb228db 100644
--- a/services/accessanalyzer/policy_analysis.go
+++ b/services/accessanalyzer/policy_analysis.go
@@ -1,11 +1,14 @@
package accessanalyzer
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"path/filepath"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -52,14 +55,14 @@ type iamStringOrSlice []string
func (s *iamStringOrSlice) UnmarshalJSON(data []byte) error {
var str string
- if err := json.Unmarshal(data, &str); err == nil {
+ if err := json.Unmarshal(data, &str, jsonx.V1()); err == nil {
*s = []string{str}
return nil
}
var slice []string
- if err := json.Unmarshal(data, &slice); err != nil {
+ if err := json.Unmarshal(data, &slice, jsonx.V1()); err != nil {
return err
}
@@ -78,27 +81,27 @@ type iamPrincipal struct { //nolint:govet // field order chosen for readability
func (p *iamPrincipal) UnmarshalJSON(data []byte) error {
var str string
- if err := json.Unmarshal(data, &str); err == nil {
+ if err := json.Unmarshal(data, &str, jsonx.V1()); err == nil {
p.IsWildcard = str == "*"
return nil
}
- var m map[string]json.RawMessage
- if err := json.Unmarshal(data, &m); err != nil {
+ var m map[string]jsontext.Value
+ if err := json.Unmarshal(data, &m, jsonx.V1()); err != nil {
return err
}
if v, ok := m["AWS"]; ok {
- _ = json.Unmarshal(v, &p.AWS)
+ _ = json.Unmarshal(v, &p.AWS, jsonx.V1())
}
if v, ok := m["Service"]; ok {
- _ = json.Unmarshal(v, &p.Service)
+ _ = json.Unmarshal(v, &p.Service, jsonx.V1())
}
if v, ok := m["Federated"]; ok {
- _ = json.Unmarshal(v, &p.Federated)
+ _ = json.Unmarshal(v, &p.Federated, jsonx.V1())
}
return nil
@@ -116,7 +119,7 @@ func parsePolicy(doc string) (iamPolicy, error) {
}
var p iamPolicy
- if err := json.Unmarshal([]byte(doc), &p); err != nil {
+ if err := json.Unmarshal([]byte(doc), &p, jsonx.V1()); err != nil {
return iamPolicy{}, ErrMalformedPolicy
}
@@ -439,7 +442,7 @@ func stmtFieldLoc(stmtPath []any, field string) []map[string]any {
}
// validateVersion checks the Version field of a policy document.
-func validateVersion(raw map[string]json.RawMessage, p iamPolicy) []ValidatePolicyFinding {
+func validateVersion(raw map[string]jsontext.Value, p iamPolicy) []ValidatePolicyFinding {
var findings []ValidatePolicyFinding
if _, ok := raw["Version"]; !ok {
@@ -546,8 +549,8 @@ func validateStatement(i int, stmt iamStatement, policyType string) []ValidatePo
// ValidatePolicy checks a policy document for structural and semantic errors.
func ValidatePolicy(policyDoc, policyType string) []ValidatePolicyFinding {
- var raw map[string]json.RawMessage
- if err := json.Unmarshal([]byte(policyDoc), &raw); err != nil {
+ var raw map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(policyDoc), &raw, jsonx.V1()); err != nil {
findings := []ValidatePolicyFinding{errFinding("INVALID_POLICY_SYNTAX", rootLoc())}
attachSpans(policyDoc, findings)
@@ -582,7 +585,7 @@ func ValidatePolicy(policyDoc, policyType string) []ValidatePolicyFinding {
// the finding) falls back one step at a time toward the document root,
// which always resolves -- so span is never dropped.
func attachSpans(doc string, findings []ValidatePolicyFinding) {
- root := json.RawMessage(doc)
+ root := jsontext.Value(doc)
for i := range findings {
locs := findings[i].Locations
@@ -595,7 +598,7 @@ func attachSpans(doc string, findings []ValidatePolicyFinding) {
// attachSpan resolves loc's "span" in place. loc is a map (a reference
// type), so mutating it here mutates the same map attachSpans's caller
// holds -- no index expression needs to survive past this call.
-func attachSpan(doc string, root json.RawMessage, loc map[string]any) {
+func attachSpan(doc string, root jsontext.Value, loc map[string]any) {
path, _ := loc[keyPath].([]any)
for {
@@ -619,7 +622,7 @@ func attachSpan(doc string, root json.RawMessage, loc map[string]any) {
// map[string]any{keyValue: field} / map[string]any{keyIndex: i} shapes
// rootLoc/fieldLoc/stmtLoc/stmtFieldLoc build (never JSON-decoded, so the
// map values are concrete Go string/int, not float64/etc).
-func resolveRawAt(node json.RawMessage, nodeStart int, path []any) (json.RawMessage, int, bool) {
+func resolveRawAt(node jsontext.Value, nodeStart int, path []any) (jsontext.Value, int, bool) {
if len(path) == 0 {
return node, nodeStart, true
}
@@ -641,9 +644,9 @@ func resolveRawAt(node json.RawMessage, nodeStart int, path []any) (json.RawMess
// resolveKeyStep resolves a map[string]any{keyValue: key} path step: the
// PathElementMemberValue shape rootLoc/fieldLoc/stmtFieldLoc build for "the
// value at this object key".
-func resolveKeyStep(node json.RawMessage, nodeStart int, key string, rest []any) (json.RawMessage, int, bool) {
- var m map[string]json.RawMessage
- if err := json.Unmarshal(node, &m); err != nil {
+func resolveKeyStep(node jsontext.Value, nodeStart int, key string, rest []any) (jsontext.Value, int, bool) {
+ var m map[string]jsontext.Value
+ if err := json.Unmarshal(node, &m, jsonx.V1()); err != nil {
return nil, 0, false
}
@@ -666,9 +669,9 @@ func resolveKeyStep(node json.RawMessage, nodeStart int, key string, rest []any)
// against the whole node) so an earlier element byte-identical to arr[idx]
// can't steal the match -- each element narrows the search window past
// itself.
-func resolveIndexStep(node json.RawMessage, nodeStart, idx int, rest []any) (json.RawMessage, int, bool) {
- var arr []json.RawMessage
- if err := json.Unmarshal(node, &arr); err != nil {
+func resolveIndexStep(node jsontext.Value, nodeStart, idx int, rest []any) (jsontext.Value, int, bool) {
+ var arr []jsontext.Value
+ if err := json.Unmarshal(node, &arr, jsonx.V1()); err != nil {
return nil, 0, false
}
diff --git a/services/accessanalyzer/policy_analysis_test.go b/services/accessanalyzer/policy_analysis_test.go
index 23800ce87d..1ea550a0c7 100644
--- a/services/accessanalyzer/policy_analysis_test.go
+++ b/services/accessanalyzer/policy_analysis_test.go
@@ -1,13 +1,14 @@
package accessanalyzer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/accessanalyzer"
)
@@ -24,7 +25,7 @@ func policyAllow(action, resource string) string {
},
}
- b, _ := json.Marshal(p)
+ b, _ := json.Marshal(p, jsonx.V1())
return string(b)
}
@@ -48,7 +49,7 @@ func policyPublic() string {
},
}
- b, _ := json.Marshal(p)
+ b, _ := json.Marshal(p, jsonx.V1())
return string(b)
}
@@ -541,7 +542,7 @@ func TestCheckAccessNotGrantedHTTP(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantResult, resp["result"])
})
}
@@ -588,7 +589,7 @@ func TestCheckNoNewAccessHTTP(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantResult, resp["result"])
})
}
@@ -633,7 +634,7 @@ func TestCheckNoPublicAccessHTTP(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantResult, resp["result"])
assert.NotNil(t, resp["reasons"])
})
@@ -689,7 +690,7 @@ func TestValidatePolicyHTTP(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["findings"])
findings := resp["findings"].([]any)
diff --git a/services/account/handler.go b/services/account/handler.go
index 0fdd91ed74..2bc93ec586 100644
--- a/services/account/handler.go
+++ b/services/account/handler.go
@@ -1,7 +1,7 @@
package account
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -11,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -219,7 +220,7 @@ func decodeJSON(body []byte, v any) error {
return nil
}
- return json.Unmarshal(body, v)
+ return json.Unmarshal(body, v, jsonx.V1())
}
func (h *Handler) handleGetContactInformation(c *echo.Context, body []byte) error {
diff --git a/services/account/handler_account_info_test.go b/services/account/handler_account_info_test.go
index cd3e46d239..f522bb7fc5 100644
--- a/services/account/handler_account_info_test.go
+++ b/services/account/handler_account_info_test.go
@@ -1,13 +1,15 @@
package account_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/account"
)
@@ -21,14 +23,14 @@ func TestHandler_GetAccountInformation(t *testing.T) {
respBody := rec.Body.Bytes()
var out account.Info
- require.NoError(t, json.Unmarshal(respBody, &out))
+ require.NoError(t, json.Unmarshal(respBody, &out, jsonx.V1()))
assert.NotEmpty(t, out.AccountID)
assert.NotEmpty(t, out.AccountCreatedDate)
assert.Equal(t, account.StateActive, out.AccountState)
// Response is a flat object, not wrapped like most other operations.
var raw map[string]any
- require.NoError(t, json.Unmarshal(respBody, &raw))
+ require.NoError(t, json.Unmarshal(respBody, &raw, jsonx.V1()))
assert.NotContains(t, raw, "Account")
}
@@ -63,7 +65,7 @@ func TestHandler_PutAccountName(t *testing.T) {
require.Equal(t, http.StatusOK, infoRec.Code)
var out account.Info
- require.NoError(t, json.NewDecoder(infoRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(infoRec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.accountName, out.AccountName)
}
})
@@ -78,7 +80,7 @@ func TestHandler_PrimaryEmail_GetDefault(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out["PrimaryEmail"])
}
@@ -114,7 +116,7 @@ func TestHandler_PrimaryEmail_UpdateFlow(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.NewDecoder(startRec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec.Body), &startOut, jsonx.V1()))
assert.Equal(t, "PENDING", startOut["Status"])
acceptRec := doRequest(t, h, "/acceptPrimaryEmailUpdate", map[string]any{
@@ -125,14 +127,14 @@ func TestHandler_PrimaryEmail_UpdateFlow(t *testing.T) {
require.Equal(t, http.StatusOK, acceptRec.Code)
var acceptOut map[string]any
- require.NoError(t, json.NewDecoder(acceptRec.Body).Decode(&acceptOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(acceptRec.Body), &acceptOut, jsonx.V1()))
assert.Equal(t, "ACCEPTED", acceptOut["Status"])
getRec := doRequest(t, h, "/getPrimaryEmail", map[string]any{"AccountId": "000000000000"})
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&getOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &getOut, jsonx.V1()))
assert.Equal(t, tc.newEmail, getOut["PrimaryEmail"])
})
}
@@ -206,7 +208,7 @@ func TestHandler_PrimaryEmail_StartConflictsOnCurrentEmail(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&getOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &getOut, jsonx.V1()))
currentEmail, ok := getOut["PrimaryEmail"].(string)
require.True(t, ok)
@@ -217,7 +219,7 @@ func TestHandler_PrimaryEmail_StartConflictsOnCurrentEmail(t *testing.T) {
require.Equal(t, http.StatusConflict, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, "ConflictException", out["__type"])
assert.Equal(t, "ConflictException", rec.Header().Get("X-Amzn-Errortype"))
}
@@ -262,7 +264,7 @@ func TestHandler_GetPrimaryEmailUpdateStatus(t *testing.T) {
}
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantBody, out["Status"])
assert.NotZero(t, out["UpdatedAt"])
})
@@ -277,7 +279,7 @@ func TestHandler_GetGovCloudAccountInformation_NotLinked(t *testing.T) {
assert.Equal(t, http.StatusNotFound, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", out["__type"])
assert.Equal(t, "ResourceNotFoundException", rec.Header().Get("X-Amzn-Errortype"))
}
diff --git a/services/account/handler_contacts_test.go b/services/account/handler_contacts_test.go
index 40445f54f7..886d5a6f9c 100644
--- a/services/account/handler_contacts_test.go
+++ b/services/account/handler_contacts_test.go
@@ -1,13 +1,16 @@
package account_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_ContactInformation_PutGet(t *testing.T) {
@@ -42,7 +45,7 @@ func TestHandler_ContactInformation_PutGet(t *testing.T) {
var out struct {
ContactInformation map[string]any `json:"ContactInformation"`
}
- require.NoError(t, json.NewDecoder(getRec2.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec2.Body), &out, jsonx.V1()))
// Every field set on Put round-trips through Get exactly.
for k, v := range full {
@@ -137,7 +140,7 @@ func TestHandler_AlternateContact_PutGetDelete(t *testing.T) {
var out struct {
AlternateContact map[string]any `json:"AlternateContact"`
}
- require.NoError(t, json.NewDecoder(getRec2.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec2.Body), &out, jsonx.V1()))
assert.Equal(t, "Test Contact", out.AlternateContact["Name"])
assert.Equal(t, tt.contactType, out.AlternateContact["AlternateContactType"])
diff --git a/services/account/handler_regions_test.go b/services/account/handler_regions_test.go
index 5dfc1f9e5c..689669af96 100644
--- a/services/account/handler_regions_test.go
+++ b/services/account/handler_regions_test.go
@@ -1,7 +1,8 @@
package account_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/account"
)
@@ -44,7 +46,7 @@ func TestHandler_ListRegions(t *testing.T) {
var out struct {
Regions []account.Region `json:"Regions"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.Regions, tt.wantCount)
})
}
@@ -60,7 +62,7 @@ func TestHandler_ListRegions_RegionFieldsPresent(t *testing.T) {
var out struct {
Regions []map[string]any `json:"Regions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.Regions)
for _, r := range out.Regions {
@@ -79,7 +81,7 @@ func TestHandler_ListRegions_Alphabetical(t *testing.T) {
var out struct {
Regions []account.Region `json:"Regions"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotEmpty(t, out.Regions)
for i := 1; i < len(out.Regions); i++ {
@@ -129,7 +131,7 @@ func TestHandler_ListRegions_Pagination(t *testing.T) {
NextToken string `json:"NextToken"`
Regions []map[string]any `json:"Regions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.LessOrEqual(t, len(resp.Regions), tc.maxResults)
for _, r := range resp.Regions {
@@ -231,7 +233,7 @@ func TestHandler_GetRegionOptStatus(t *testing.T) {
if tc.wantStatus != "" {
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tc.regionName, out["RegionName"])
assert.Equal(t, tc.wantStatus, out["RegionOptStatus"])
}
@@ -263,7 +265,7 @@ func TestHandler_EnableDisableRegion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
return out["RegionOptStatus"].(string)
}
diff --git a/services/account/handler_test.go b/services/account/handler_test.go
index 397bccd3e7..89c0f8df87 100644
--- a/services/account/handler_test.go
+++ b/services/account/handler_test.go
@@ -1,7 +1,8 @@
package account_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/internal/awstest"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/account"
)
@@ -247,6 +249,6 @@ func TestHandler_ErrorEnvelope(t *testing.T) {
assert.Equal(t, "ResourceNotFoundException", rec.Header().Get("X-Amzn-Errortype"))
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", out["__type"])
}
diff --git a/services/account/persistence.go b/services/account/persistence.go
index 199c24f9a8..7ee64bd71b 100644
--- a/services/account/persistence.go
+++ b/services/account/persistence.go
@@ -2,7 +2,7 @@ package account
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -58,21 +58,21 @@ const accountSnapshotVersion = 2
// key on) and Regions is a plain (non-map) slice, so both stay raw; the
// rest are scalars.
type backendSnapshot struct {
- AccountCreatedDate time.Time `json:"accountCreatedDate"`
- Tables map[string]json.RawMessage `json:"tables"`
- ContactInfo *ContactInformation `json:"contactInfo,omitempty"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- AccountName string `json:"accountName"`
- PrimaryEmail string `json:"primaryEmail"`
- PendingEmail string `json:"pendingEmail"`
- PendingOTP string `json:"pendingOTP"`
- PhoneVerifyNumber string `json:"phoneVerifyNumber,omitempty"`
- PhoneVerifyOTP string `json:"phoneVerifyOTP,omitempty"`
- PrimaryEmailUpdateStatus PrimaryEmailUpdateStatus `json:"primaryEmailUpdateStatus,omitempty"`
- PrimaryEmailUpdateAt time.Time `json:"primaryEmailUpdateAt"`
- Regions []*Region `json:"regions"`
- Version int `json:"version"`
+ AccountCreatedDate time.Time `json:"accountCreatedDate"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ ContactInfo *ContactInformation `json:"contactInfo,omitempty"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ AccountName string `json:"accountName"`
+ PrimaryEmail string `json:"primaryEmail"`
+ PendingEmail string `json:"pendingEmail"`
+ PendingOTP string `json:"pendingOTP"`
+ PhoneVerifyNumber string `json:"phoneVerifyNumber,omitempty"`
+ PhoneVerifyOTP string `json:"phoneVerifyOTP,omitempty"`
+ PrimaryEmailUpdateStatus PrimaryEmailUpdateStatus `json:"primaryEmailUpdateStatus,omitempty"`
+ PrimaryEmailUpdateAt time.Time `json:"primaryEmailUpdateAt"`
+ Regions []*Region `json:"regions"`
+ Version int `json:"version"`
}
// Snapshot serializes the backend state to JSON. It implements
diff --git a/services/account/validation_test.go b/services/account/validation_test.go
index d64640f719..a00e5fdda5 100644
--- a/services/account/validation_test.go
+++ b/services/account/validation_test.go
@@ -1,7 +1,7 @@
package account_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/account"
)
@@ -165,7 +166,7 @@ func TestHandler_Validation(t *testing.T) {
Type string `json:"__type"`
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantCode, out.Type)
assert.Contains(t, out.Message, tt.wantMsg)
assert.False(t, strings.HasPrefix(out.Message, tt.wantCode), "message repeats the code: %q", out.Message)
diff --git a/services/acm/acme_domain_validation_status_test.go b/services/acm/acme_domain_validation_status_test.go
index badf2e9663..008b279bb1 100644
--- a/services/acm/acme_domain_validation_status_test.go
+++ b/services/acm/acme_domain_validation_status_test.go
@@ -1,7 +1,7 @@
package acm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestAcmeDomainValidation_SettlesToValid(t *testing.T) {
@@ -39,7 +41,7 @@ func TestAcmeDomainValidation_SettlesToValid(t *testing.T) {
"PrevalidationOptions": map[string]any{
"DnsPrevalidation": map[string]any{"HostedZoneId": "Z123456"},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := postACMJSON(t, h, "CreateAcmeDomainValidation", string(body))
@@ -48,7 +50,7 @@ func TestAcmeDomainValidation_SettlesToValid(t *testing.T) {
var out struct {
Arn string `json:"AcmeDomainValidationArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
time.Sleep(tt.wait)
@@ -58,13 +60,13 @@ func TestAcmeDomainValidation_SettlesToValid(t *testing.T) {
"PrevalidationOptions": map[string]any{
"DnsPrevalidation": map[string]any{"HostedZoneId": "Z999999"},
},
- })
+ }, jsonx.V1())
require.NoError(t, mErr)
require.Equal(t, http.StatusOK,
postACMJSON(t, h, "UpdateAcmeDomainValidation", string(upd)).Code)
}
- descBody, err := json.Marshal(map[string]string{"AcmeDomainValidationArn": out.Arn})
+ descBody, err := json.Marshal(map[string]string{"AcmeDomainValidationArn": out.Arn}, jsonx.V1())
require.NoError(t, err)
desc := postACMJSON(t, h, "DescribeAcmeDomainValidation", string(descBody))
@@ -75,7 +77,7 @@ func TestAcmeDomainValidation_SettlesToValid(t *testing.T) {
Status string `json:"Status"`
} `json:"AcmeDomainValidation"`
}
- require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, tt.wantStatus, got.V.Status)
})
})
diff --git a/services/acm/certificate_detail_no_fabricated_keyid_test.go b/services/acm/certificate_detail_no_fabricated_keyid_test.go
index 4a7367fb43..82b87f85c5 100644
--- a/services/acm/certificate_detail_no_fabricated_keyid_test.go
+++ b/services/acm/certificate_detail_no_fabricated_keyid_test.go
@@ -1,11 +1,14 @@
package acm_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestACMHandler_DescribeCertificate_NoFabricatedKeyId locks in the removal
@@ -30,19 +33,19 @@ func TestACMHandler_DescribeCertificate_NoFabricatedKeyId(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
- descBody, err := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ descBody, err := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
require.NoError(t, err)
descRec := postACMJSON(t, h, "DescribeCertificate", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw, jsonx.V1()))
- var certRaw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(raw["Certificate"], &certRaw))
+ var certRaw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(raw["Certificate"], &certRaw, jsonx.V1()))
_, hasKeyID := certRaw["KeyId"]
require.False(t, hasKeyID, "DescribeCertificate must not emit a fabricated KeyId member on Certificate")
diff --git a/services/acm/certificate_validation_test.go b/services/acm/certificate_validation_test.go
index 2817885695..570abcf89e 100644
--- a/services/acm/certificate_validation_test.go
+++ b/services/acm/certificate_validation_test.go
@@ -1,12 +1,14 @@
package acm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestACMHandler_MalformedArn_ReturnsInvalidArnException verifies that a
@@ -102,7 +104,7 @@ func TestACMHandler_RequestCertificate_TooManySANs_ReturnsLimitExceeded(t *testi
body, err := json.Marshal(map[string]any{
"DomainName": "toomany.example.com",
"SubjectAlternativeNames": sans,
- })
+ }, jsonx.V1())
require.NoError(t, err)
h := newACMHandler()
@@ -124,7 +126,7 @@ func TestACMHandler_TooManyTags_ReturnsTooManyTagsException(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
tags := make([]map[string]string, 0, 51)
for i := range 51 {
@@ -134,7 +136,7 @@ func TestACMHandler_TooManyTags_ReturnsTooManyTagsException(t *testing.T) {
body, err := json.Marshal(map[string]any{
"CertificateArn": reqOut.CertificateArn,
"Tags": tags,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := postACMJSON(t, h, "AddTagsToCertificate", string(body))
@@ -155,12 +157,12 @@ func TestACMHandler_ReservedTagPrefix_ReturnsInvalidTagException(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
body, err := json.Marshal(map[string]any{
"CertificateArn": reqOut.CertificateArn,
"Tags": []map[string]string{{"Key": "aws:reserved", "Value": "v"}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := postACMJSON(t, h, "AddTagsToCertificate", string(body))
@@ -187,9 +189,9 @@ func TestACMHandler_RequestCertificate_DomainValidationOptions_Applied(t *testin
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
- descBody, err := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ descBody, err := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
require.NoError(t, err)
descRec := postACMJSON(t, h, "DescribeCertificate", string(descBody))
diff --git a/services/acm/handler.go b/services/acm/handler.go
index 24f82f24ac..17aadabceb 100644
--- a/services/acm/handler.go
+++ b/services/acm/handler.go
@@ -2,7 +2,8 @@ package acm
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"regexp"
@@ -12,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -182,8 +184,8 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
if err != nil {
return ""
}
- var m map[string]json.RawMessage
- if unmarshalErr := json.Unmarshal(body, &m); unmarshalErr != nil {
+ var m map[string]jsontext.Value
+ if unmarshalErr := json.Unmarshal(body, &m, jsonx.V1()); unmarshalErr != nil {
return ""
}
@@ -194,7 +196,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var arn string
- if unmarshalErr := json.Unmarshal(raw, &arn); unmarshalErr == nil && arn != "" {
+ if unmarshalErr := json.Unmarshal(raw, &arn, jsonx.V1()); unmarshalErr == nil && arn != "" {
return arn
}
}
@@ -227,7 +229,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// handleError writes a standardized error response back to the client.
diff --git a/services/acm/handler_account.go b/services/acm/handler_account.go
index 0f955ba393..84ac957a24 100644
--- a/services/acm/handler_account.go
+++ b/services/acm/handler_account.go
@@ -2,7 +2,9 @@ package acm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type expiryEventsConfiguration struct {
@@ -32,7 +34,7 @@ func (h *Handler) jsonGetAccountConfiguration(ctx context.Context, _ []byte) (an
func (h *Handler) jsonPutAccountConfiguration(ctx context.Context, body []byte) (any, error) {
var input putAccountConfigurationInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
diff --git a/services/acm/handler_acme_accounts.go b/services/acm/handler_acme_accounts.go
index c11d7ef1d3..86194a6410 100644
--- a/services/acm/handler_acme_accounts.go
+++ b/services/acm/handler_acme_accounts.go
@@ -2,7 +2,9 @@ package acm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type acmeAccountWire struct {
@@ -36,7 +38,7 @@ type describeAcmeAccountOutput struct {
func (h *Handler) jsonDescribeAcmeAccount(ctx context.Context, body []byte) (any, error) {
var input describeAcmeAccountInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -63,7 +65,7 @@ type listAcmeAccountsOutput struct {
func (h *Handler) jsonListAcmeAccounts(ctx context.Context, body []byte) (any, error) {
var input listAcmeAccountsInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -89,7 +91,7 @@ type revokeAcmeAccountOutput struct{}
func (h *Handler) jsonRevokeAcmeAccount(ctx context.Context, body []byte) (any, error) {
var input revokeAcmeAccountInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
diff --git a/services/acm/handler_acme_domain_validations.go b/services/acm/handler_acme_domain_validations.go
index ba9be4f718..95a6bf3747 100644
--- a/services/acm/handler_acme_domain_validations.go
+++ b/services/acm/handler_acme_domain_validations.go
@@ -2,9 +2,10 @@ package acm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -107,7 +108,7 @@ type createAcmeDomainValidationOutput struct {
func (h *Handler) jsonCreateAcmeDomainValidation(ctx context.Context, body []byte) (any, error) {
var input createAcmeDomainValidationInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -146,7 +147,7 @@ type describeAcmeDomainValidationOutput struct {
func (h *Handler) jsonDescribeAcmeDomainValidation(ctx context.Context, body []byte) (any, error) {
var input describeAcmeDomainValidationInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -176,7 +177,7 @@ type deleteAcmeDomainValidationOutput struct{}
func (h *Handler) jsonDeleteAcmeDomainValidation(ctx context.Context, body []byte) (any, error) {
var input deleteAcmeDomainValidationInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -204,9 +205,10 @@ type listAcmeDomainValidationsOutput struct {
AcmeDomainValidations []acmeDomainValidationWire `json:"AcmeDomainValidations"`
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) jsonListAcmeDomainValidations(ctx context.Context, body []byte) (any, error) {
var input listAcmeDomainValidationsInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -240,7 +242,7 @@ type updateAcmeDomainValidationOutput struct{}
func (h *Handler) jsonUpdateAcmeDomainValidation(ctx context.Context, body []byte) (any, error) {
var input updateAcmeDomainValidationInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
diff --git a/services/acm/handler_acme_eab.go b/services/acm/handler_acme_eab.go
index d7e527d11c..79fe1c47d4 100644
--- a/services/acm/handler_acme_eab.go
+++ b/services/acm/handler_acme_eab.go
@@ -2,9 +2,10 @@ package acm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -64,7 +65,7 @@ type createAcmeEABOutput struct {
func (h *Handler) jsonCreateAcmeExternalAccountBinding(ctx context.Context, body []byte) (any, error) {
var input createAcmeEABInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -110,7 +111,7 @@ type describeAcmeEABOutput struct {
func (h *Handler) jsonDescribeAcmeExternalAccountBinding(ctx context.Context, body []byte) (any, error) {
var input describeAcmeEABInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -140,7 +141,7 @@ type deleteAcmeEABOutput struct{}
func (h *Handler) jsonDeleteAcmeExternalAccountBinding(ctx context.Context, body []byte) (any, error) {
var input deleteAcmeEABInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -168,9 +169,10 @@ type listAcmeEABsOutput struct {
ExternalAccountBindings []acmeEABWire `json:"ExternalAccountBindings"`
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) jsonListAcmeExternalAccountBindings(ctx context.Context, body []byte) (any, error) {
var input listAcmeEABsInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -208,7 +210,7 @@ type getAcmeEABCredentialsOutput struct {
func (h *Handler) jsonGetAcmeExternalAccountBindingCredentials(ctx context.Context, body []byte) (any, error) {
var input getAcmeEABCredentialsInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -236,7 +238,7 @@ type revokeAcmeEABOutput struct{}
func (h *Handler) jsonRevokeAcmeExternalAccountBinding(ctx context.Context, body []byte) (any, error) {
var input revokeAcmeEABInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
diff --git a/services/acm/handler_acme_endpoints.go b/services/acm/handler_acme_endpoints.go
index a1d47d8233..015f62e8b1 100644
--- a/services/acm/handler_acme_endpoints.go
+++ b/services/acm/handler_acme_endpoints.go
@@ -2,9 +2,10 @@ package acm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -65,7 +66,7 @@ type createAcmeEndpointOutput struct {
func (h *Handler) jsonCreateAcmeEndpoint(ctx context.Context, body []byte) (any, error) {
var input createAcmeEndpointInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -111,7 +112,7 @@ type describeAcmeEndpointOutput struct {
func (h *Handler) jsonDescribeAcmeEndpoint(ctx context.Context, body []byte) (any, error) {
var input describeAcmeEndpointInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -141,7 +142,7 @@ type deleteAcmeEndpointOutput struct{}
func (h *Handler) jsonDeleteAcmeEndpoint(ctx context.Context, body []byte) (any, error) {
var input deleteAcmeEndpointInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -170,7 +171,7 @@ type listAcmeEndpointsOutput struct {
func (h *Handler) jsonListAcmeEndpoints(ctx context.Context, body []byte) (any, error) {
var input listAcmeEndpointsInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -198,7 +199,7 @@ type updateAcmeEndpointOutput struct{}
func (h *Handler) jsonUpdateAcmeEndpoint(ctx context.Context, body []byte) (any, error) {
var input updateAcmeEndpointInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
diff --git a/services/acm/handler_certificate_lifecycle_test.go b/services/acm/handler_certificate_lifecycle_test.go
index a3a67fee04..79a183e88c 100644
--- a/services/acm/handler_certificate_lifecycle_test.go
+++ b/services/acm/handler_certificate_lifecycle_test.go
@@ -2,7 +2,7 @@ package acm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/acm"
)
@@ -29,11 +30,11 @@ func TestACMHandler_DNSValidationWorkflow(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
require.NotEmpty(t, reqOut.CertificateArn)
// Describe should show PENDING_VALIDATION with CNAME records
- descBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ descBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
descRec := postACMJSON(t, h, "DescribeCertificate", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
@@ -48,7 +49,7 @@ func TestACMHandler_DNSValidationWorkflow(t *testing.T) {
} `json:"DomainValidationOptions"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
require.Equal(t, "PENDING_VALIDATION", descOut.Certificate.Status)
require.NotEmpty(t, descOut.Certificate.DomainValidationOptions)
assert.NotNil(t, descOut.Certificate.DomainValidationOptions[0].ResourceRecord)
@@ -64,7 +65,7 @@ func TestACMHandler_DNSValidationWorkflow(t *testing.T) {
Status string `json:"Status"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "ISSUED", out.Certificate.Status, "cert should transition to ISSUED")
})
}
@@ -89,7 +90,7 @@ func TestACMHandler_ResendValidationEmail(t *testing.T) {
var out struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.CertificateArn
},
@@ -98,7 +99,7 @@ func TestACMHandler_ResendValidationEmail(t *testing.T) {
"CertificateArn": certARN,
"Domain": "email-resend.example.com",
"ValidationDomain": "email-resend.example.com",
- })
+ }, jsonx.V1())
return string(b)
},
@@ -114,7 +115,7 @@ func TestACMHandler_ResendValidationEmail(t *testing.T) {
"CertificateArn": certARN,
"Domain": "example.com",
"ValidationDomain": "example.com",
- })
+ }, jsonx.V1())
return string(b)
},
@@ -131,7 +132,7 @@ func TestACMHandler_ResendValidationEmail(t *testing.T) {
var out struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.CertificateArn
},
@@ -140,7 +141,7 @@ func TestACMHandler_ResendValidationEmail(t *testing.T) {
"CertificateArn": certARN,
"Domain": "issued.example.com",
"ValidationDomain": "issued.example.com",
- })
+ }, jsonx.V1())
return string(b)
},
@@ -157,7 +158,7 @@ func TestACMHandler_ResendValidationEmail(t *testing.T) {
var out struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.CertificateArn
},
@@ -166,7 +167,7 @@ func TestACMHandler_ResendValidationEmail(t *testing.T) {
"CertificateArn": certARN,
"Domain": "dns.example.com",
"ValidationDomain": "dns.example.com",
- })
+ }, jsonx.V1())
return string(b)
},
@@ -222,7 +223,7 @@ func TestACMHandler_RevokeCertificate(t *testing.T) {
var out struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.CertificateArn
},
@@ -230,7 +231,7 @@ func TestACMHandler_RevokeCertificate(t *testing.T) {
b, _ := json.Marshal(map[string]string{
"CertificateArn": certARN,
"RevocationReason": "UNSPECIFIED",
- })
+ }, jsonx.V1())
return string(b)
},
@@ -245,7 +246,7 @@ func TestACMHandler_RevokeCertificate(t *testing.T) {
var out struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.CertificateArn
},
@@ -253,7 +254,7 @@ func TestACMHandler_RevokeCertificate(t *testing.T) {
b, _ := json.Marshal(map[string]string{
"CertificateArn": certARN,
"RevocationReason": "KEY_COMPROMISE",
- })
+ }, jsonx.V1())
return string(b)
},
@@ -286,12 +287,12 @@ func TestACMHandler_RevokeCertificate(t *testing.T) {
var out struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// Revoke once
b, _ := json.Marshal(map[string]string{
"CertificateArn": out.CertificateArn,
"RevocationReason": "UNSPECIFIED",
- })
+ }, jsonx.V1())
postACMJSON(t, h, "RevokeCertificate", string(b))
return out.CertificateArn
@@ -300,7 +301,7 @@ func TestACMHandler_RevokeCertificate(t *testing.T) {
b, _ := json.Marshal(map[string]string{
"CertificateArn": certARN,
"RevocationReason": "UNSPECIFIED",
- })
+ }, jsonx.V1())
return string(b)
},
@@ -349,18 +350,18 @@ func TestACMHandler_RevokeCertificate_DescribeShowsRevoked(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
// Revoke it
revokeBody, _ := json.Marshal(map[string]string{
"CertificateArn": reqOut.CertificateArn,
"RevocationReason": "KEY_COMPROMISE",
- })
+ }, jsonx.V1())
revokeRec := postACMJSON(t, h, "RevokeCertificate", string(revokeBody))
require.Equal(t, http.StatusOK, revokeRec.Code)
// Describe should show REVOKED with RevocationReason
- descBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ descBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
descRec := postACMJSON(t, h, "DescribeCertificate", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
@@ -371,7 +372,7 @@ func TestACMHandler_RevokeCertificate_DescribeShowsRevoked(t *testing.T) {
RevocationReason string `json:"RevocationReason"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, "REVOKED", descOut.Certificate.Status)
assert.Equal(t, "KEY_COMPROMISE", descOut.Certificate.RevocationReason)
assert.NotNil(t, descOut.Certificate.RevokedAt)
@@ -396,7 +397,7 @@ func TestACMHandler_UpdateCertificateOptions(t *testing.T) {
var out struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.CertificateArn
},
@@ -406,7 +407,7 @@ func TestACMHandler_UpdateCertificateOptions(t *testing.T) {
"Options": map[string]string{
"CertificateTransparencyLoggingPreference": "ENABLED",
},
- })
+ }, jsonx.V1())
return string(b)
},
@@ -421,7 +422,7 @@ func TestACMHandler_UpdateCertificateOptions(t *testing.T) {
var out struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.CertificateArn
},
@@ -431,7 +432,7 @@ func TestACMHandler_UpdateCertificateOptions(t *testing.T) {
"Options": map[string]string{
"CertificateTransparencyLoggingPreference": "DISABLED",
},
- })
+ }, jsonx.V1())
return string(b)
},
@@ -447,7 +448,7 @@ func TestACMHandler_UpdateCertificateOptions(t *testing.T) {
"Options": map[string]string{
"CertificateTransparencyLoggingPreference": "ENABLED",
},
- })
+ }, jsonx.V1())
return string(b)
},
@@ -506,7 +507,7 @@ func TestACMHandler_UpdateCertificateOptions_DescribeShowsOptions(t *testing.T)
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
// Update options
optsBody, _ := json.Marshal(map[string]any{
@@ -514,12 +515,12 @@ func TestACMHandler_UpdateCertificateOptions_DescribeShowsOptions(t *testing.T)
"Options": map[string]string{
"CertificateTransparencyLoggingPreference": "DISABLED",
},
- })
+ }, jsonx.V1())
optsRec := postACMJSON(t, h, "UpdateCertificateOptions", string(optsBody))
require.Equal(t, http.StatusOK, optsRec.Code)
// Describe should reflect options
- descBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ descBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
descRec := postACMJSON(t, h, "DescribeCertificate", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
@@ -530,7 +531,7 @@ func TestACMHandler_UpdateCertificateOptions_DescribeShowsOptions(t *testing.T)
} `json:"Options"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
require.NotNil(t, descOut.Certificate.Options)
assert.Equal(t, "DISABLED", descOut.Certificate.Options.CertificateTransparencyLoggingPreference)
}
@@ -559,9 +560,9 @@ func TestACMHandler_RevokeCertificate_PendingValidationRejected(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
- body, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ body, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
descRec := postACMJSON(t, h, "DescribeCertificate", string(body))
require.Equal(t, http.StatusOK, descRec.Code)
@@ -570,14 +571,14 @@ func TestACMHandler_RevokeCertificate_PendingValidationRejected(t *testing.T) {
Status string `json:"Status"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
require.Equal(t, "PENDING_VALIDATION", descOut.Certificate.Status)
// Try to revoke PENDING cert
revokeBody, _ := json.Marshal(map[string]string{
"CertificateArn": reqOut.CertificateArn,
"RevocationReason": "UNSPECIFIED",
- })
+ }, jsonx.V1())
revokeRec := postACMJSON(t, h, "RevokeCertificate", string(revokeBody))
assert.Equal(t, http.StatusBadRequest, revokeRec.Code)
assert.Contains(t, revokeRec.Body.String(), "ConflictException")
@@ -708,7 +709,7 @@ func TestACMHandler_StatusLifecycle_DescribeReflectsNewStatus(t *testing.T) {
certARN := tt.setupCert(t, b)
tt.transition(t, b, certARN)
- descBody, _ := json.Marshal(map[string]string{"CertificateArn": certARN})
+ descBody, _ := json.Marshal(map[string]string{"CertificateArn": certARN}, jsonx.V1())
descRec := postACMJSON(t, h, "DescribeCertificate", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
@@ -718,7 +719,7 @@ func TestACMHandler_StatusLifecycle_DescribeReflectsNewStatus(t *testing.T) {
FailureReason string `json:"FailureReason,omitempty"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, tt.wantStatus, descOut.Certificate.Status)
})
}
diff --git a/services/acm/handler_certificate_status_errors_test.go b/services/acm/handler_certificate_status_errors_test.go
index cce0dcd514..63e0ad1735 100644
--- a/services/acm/handler_certificate_status_errors_test.go
+++ b/services/acm/handler_certificate_status_errors_test.go
@@ -2,7 +2,7 @@ package acm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/acm"
)
@@ -49,7 +50,7 @@ func TestACMHandler_ExportCertificate_AmazonIssued(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
return reqOut.CertificateArn
},
@@ -85,7 +86,7 @@ func TestACMHandler_ExportCertificate_AmazonIssued(t *testing.T) {
body, _ := json.Marshal(map[string]string{
"CertificateArn": certARN,
"Passphrase": "dGVzdA==",
- })
+ }, jsonx.V1())
rec := postACMJSON(t, h, "ExportCertificate", string(body))
if tt.wantErrType == "" {
@@ -96,7 +97,7 @@ func TestACMHandler_ExportCertificate_AmazonIssued(t *testing.T) {
CertificateChain string `json:"CertificateChain"`
PrivateKey string `json:"PrivateKey"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out.Certificate, "BEGIN CERTIFICATE")
assert.NotEmpty(t, out.PrivateKey)
@@ -112,7 +113,7 @@ func TestACMHandler_ExportCertificate_AmazonIssued(t *testing.T) {
Exported bool `json:"Exported"`
} `json:"CertificateSummaryList"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
var found bool
for _, s := range listOut.CertificateSummaryList {
@@ -132,7 +133,7 @@ func TestACMHandler_ExportCertificate_AmazonIssued(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, errResp.Type)
})
}
@@ -148,7 +149,7 @@ func requestAndAwaitIssued(t *testing.T, h *acm.Handler, domainName, exportOptio
reqBody["Options"] = map[string]string{"Export": exportOption}
}
- reqJSON, err := json.Marshal(reqBody)
+ reqJSON, err := json.Marshal(reqBody, jsonx.V1())
require.NoError(t, err)
reqRec := postACMJSON(t, h, "RequestCertificate", string(reqJSON))
@@ -157,7 +158,7 @@ func requestAndAwaitIssued(t *testing.T, h *acm.Handler, domainName, exportOptio
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
descRec := postACMJSON(t, h, "DescribeCertificate", `{"CertificateArn":"`+reqOut.CertificateArn+`"}`)
@@ -166,7 +167,7 @@ func requestAndAwaitIssued(t *testing.T, h *acm.Handler, domainName, exportOptio
Status string `json:"Status"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
require.Equal(t, "ISSUED", out.Certificate.Status)
return reqOut.CertificateArn
@@ -204,14 +205,14 @@ func TestACMHandler_RenewCertificate_Imported_Returns_RequestInProgressException
require.NoError(t, err)
h := acm.NewHandler(b)
- body, _ := json.Marshal(map[string]string{"CertificateArn": imported.ARN})
+ body, _ := json.Marshal(map[string]string{"CertificateArn": imported.ARN}, jsonx.V1())
rec := postACMJSON(t, h, "RenewCertificate", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "RequestInProgressException", errResp.Type,
"RenewCertificate on IMPORTED cert must return RequestInProgressException, not RequestError")
})
@@ -298,14 +299,14 @@ func TestACMHandler_GetCertificate_NonIssuedStates_Returns_RequestInProgressExce
certARN := tt.setup(t, b)
h := acm.NewHandler(b)
- body, _ := json.Marshal(map[string]string{"CertificateArn": certARN})
+ body, _ := json.Marshal(map[string]string{"CertificateArn": certARN}, jsonx.V1())
rec := postACMJSON(t, h, "GetCertificate", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(
t,
"RequestInProgressException",
@@ -348,16 +349,16 @@ func TestACMHandler_RevokeCertificate_PendingValidation_Returns_ConflictExceptio
body, _ := json.Marshal(map[string]string{
"DomainName": "revoke-pending.example.com",
"ValidationMethod": method,
- })
+ }, jsonx.V1())
reqRec := postACMJSON(t, h, "RequestCertificate", string(body))
require.Equal(t, http.StatusOK, reqRec.Code)
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
- descBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ descBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
descRec := postACMJSON(t, h, "DescribeCertificate", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
@@ -366,20 +367,20 @@ func TestACMHandler_RevokeCertificate_PendingValidation_Returns_ConflictExceptio
Status string `json:"Status"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
require.Equal(t, "PENDING_VALIDATION", descOut.Certificate.Status)
revokeBody, _ := json.Marshal(map[string]string{
"CertificateArn": reqOut.CertificateArn,
"RevocationReason": "UNSPECIFIED",
- })
+ }, jsonx.V1())
rec := postACMJSON(t, h, "RevokeCertificate", string(revokeBody))
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(
t,
"ConflictException",
@@ -459,14 +460,14 @@ func TestACMHandler_UpdateCertificateOptions_NonIssued_Returns_InvalidStateExcep
"Options": map[string]string{
"CertificateTransparencyLoggingPreference": "ENABLED",
},
- })
+ }, jsonx.V1())
rec := postACMJSON(t, h, "UpdateCertificateOptions", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(
t,
"InvalidStateException",
@@ -499,7 +500,7 @@ func TestACMHandler_GetCertificate_Issued_Succeeds(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
// No ValidationMethod is set, so the cert issues synchronously.
descRec := postACMJSON(t, h, "DescribeCertificate",
@@ -509,17 +510,17 @@ func TestACMHandler_GetCertificate_Issued_Succeeds(t *testing.T) {
Status string `json:"Status"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
require.Equal(t, "ISSUED", descOut.Certificate.Status)
- body, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ body, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
rec := postACMJSON(t, h, "GetCertificate", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
var out struct {
Certificate string `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out.Certificate, "BEGIN CERTIFICATE")
})
}
diff --git a/services/acm/handler_certificates.go b/services/acm/handler_certificates.go
index f25f4054a2..f61894decf 100644
--- a/services/acm/handler_certificates.go
+++ b/services/acm/handler_certificates.go
@@ -3,9 +3,11 @@ package acm
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type requestCertificateInput struct {
@@ -257,7 +259,7 @@ type updateCertificateOptionsOutput struct{}
func (h *Handler) jsonRequestCertificate(ctx context.Context, body []byte) (any, error) {
var input requestCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrRequestCertInvalidParameter
}
certType := ""
@@ -400,7 +402,7 @@ func buildRenewalSummaryDetail(rs *RenewalSummary) *renewalSummaryDetail {
func (h *Handler) jsonDescribeCertificate(ctx context.Context, body []byte) (any, error) {
var input describeCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
if err := validateCertArn(input.CertificateArn); err != nil {
@@ -459,7 +461,7 @@ func (h *Handler) jsonDescribeCertificate(ctx context.Context, body []byte) (any
// jsonListCertificates handles the ListCertificates operation.
func (h *Handler) jsonListCertificates(ctx context.Context, body []byte) (any, error) {
var input listCertificatesInput
- _ = json.Unmarshal(body, &input)
+ _ = json.Unmarshal(body, &input, jsonx.V1())
if input.MaxItems < 0 || input.MaxItems > maxListItems {
return nil, fmt.Errorf("%w: MaxItems must be between 1 and %d", ErrInvalidArgs, maxListItems)
@@ -639,7 +641,7 @@ type listCertificateDomainValidationsOutput struct {
// jsonListCertificateDomainValidations handles the ListCertificateDomainValidations operation.
func (h *Handler) jsonListCertificateDomainValidations(ctx context.Context, body []byte) (any, error) {
var input listCertificateDomainValidationsInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -668,7 +670,7 @@ func (h *Handler) jsonListCertificateDomainValidations(ctx context.Context, body
func (h *Handler) jsonDeleteCertificate(ctx context.Context, body []byte) (any, error) {
var input deleteCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
if err := validateCertArn(input.CertificateArn); err != nil {
@@ -684,7 +686,7 @@ func (h *Handler) jsonDeleteCertificate(ctx context.Context, body []byte) (any,
func (h *Handler) jsonImportCertificate(ctx context.Context, body []byte) (any, error) {
var input importCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
if err := validateCertArn(input.CertificateArn); err != nil {
@@ -719,7 +721,7 @@ func (h *Handler) jsonImportCertificate(ctx context.Context, body []byte) (any,
func (h *Handler) jsonRenewCertificate(ctx context.Context, body []byte) (any, error) {
var input renewCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
if err := validateCertArn(input.CertificateArn); err != nil {
@@ -734,7 +736,7 @@ func (h *Handler) jsonRenewCertificate(ctx context.Context, body []byte) (any, e
func (h *Handler) jsonExportCertificate(ctx context.Context, body []byte) (any, error) {
var input exportCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -769,7 +771,7 @@ func (h *Handler) jsonExportCertificate(ctx context.Context, body []byte) (any,
func (h *Handler) jsonGetCertificate(ctx context.Context, body []byte) (any, error) {
var input getCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
if err := validateCertArn(input.CertificateArn); err != nil {
@@ -788,7 +790,7 @@ func (h *Handler) jsonGetCertificate(ctx context.Context, body []byte) (any, err
func (h *Handler) jsonResendValidationEmail(ctx context.Context, body []byte) (any, error) {
var input resendValidationEmailInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -806,7 +808,7 @@ func (h *Handler) jsonResendValidationEmail(ctx context.Context, body []byte) (a
func (h *Handler) jsonRevokeCertificate(ctx context.Context, body []byte) (any, error) {
var input revokeCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -823,7 +825,7 @@ func (h *Handler) jsonRevokeCertificate(ctx context.Context, body []byte) (any,
func (h *Handler) jsonUpdateCertificateOptions(ctx context.Context, body []byte) (any, error) {
var input updateCertificateOptionsInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
diff --git a/services/acm/handler_certificates_list_test.go b/services/acm/handler_certificates_list_test.go
index f95d69acdb..fe6d322a0a 100644
--- a/services/acm/handler_certificates_list_test.go
+++ b/services/acm/handler_certificates_list_test.go
@@ -3,7 +3,7 @@ package acm_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/acm"
)
@@ -46,7 +47,7 @@ func TestACMHandler_ListCertificates_StatusFilter(t *testing.T) {
Status string `json:"Status"`
} `json:"CertificateSummaryList"`
}
- require.NoError(t, json.Unmarshal(filterRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(filterRec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.CertificateSummaryList, 1, "only the immediately-issued cert should match")
for _, s := range out.CertificateSummaryList {
@@ -75,7 +76,7 @@ func TestACMHandler_ListCertificates_EnrichedSummary(t *testing.T) {
DomainName string `json:"DomainName"`
} `json:"CertificateSummaryList"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.CertificateSummaryList)
summary := out.CertificateSummaryList[0]
@@ -91,7 +92,7 @@ func TestACMHandler_ListCertificates_SortByCreatedAt(t *testing.T) {
// Create three certs in sequence
for _, domain := range []string{"sort-a.example.com", "sort-b.example.com", "sort-c.example.com"} {
- body, _ := json.Marshal(map[string]string{"DomainName": domain})
+ body, _ := json.Marshal(map[string]string{"DomainName": domain}, jsonx.V1())
rec := postACMJSON(t, h, "RequestCertificate", string(body))
require.Equal(t, http.StatusOK, rec.Code)
}
@@ -106,7 +107,7 @@ func TestACMHandler_ListCertificates_SortByCreatedAt(t *testing.T) {
DomainName string `json:"DomainName"`
} `json:"CertificateSummaryList"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.CertificateSummaryList, 3)
// Descending means newest first: sort-c, sort-b, sort-a
@@ -133,7 +134,7 @@ func TestACMHandler_ListCertificates_KeyTypesFilter(t *testing.T) {
DomainName string `json:"DomainName"`
} `json:"CertificateSummaryList"`
}
- require.NoError(t, json.Unmarshal(matchRec.Body.Bytes(), &matchOut))
+ require.NoError(t, json.Unmarshal(matchRec.Body.Bytes(), &matchOut, jsonx.V1()))
require.NotEmpty(t, matchOut.CertificateSummaryList, "EC_prime256v1 filter should return results")
// Filter that does not match
@@ -144,7 +145,7 @@ func TestACMHandler_ListCertificates_KeyTypesFilter(t *testing.T) {
var noMatchOut struct {
CertificateSummaryList []struct{} `json:"CertificateSummaryList"`
}
- require.NoError(t, json.Unmarshal(noMatchRec.Body.Bytes(), &noMatchOut))
+ require.NoError(t, json.Unmarshal(noMatchRec.Body.Bytes(), &noMatchOut, jsonx.V1()))
assert.Empty(t, noMatchOut.CertificateSummaryList, "RSA_2048 filter should return no results")
}
@@ -167,7 +168,7 @@ func TestACMHandler_ListCertificates_SubjectAlternativeNameSummaries(t *testing.
SubjectAlternativeNameSummaries []string `json:"SubjectAlternativeNameSummaries"`
} `json:"CertificateSummaryList"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.CertificateSummaryList)
assert.Contains(t, out.CertificateSummaryList[0].SubjectAlternativeNameSummaries, "www.san.example.com")
assert.Contains(t, out.CertificateSummaryList[0].SubjectAlternativeNameSummaries, "api.san.example.com")
@@ -231,7 +232,7 @@ func TestACMHandler_ListCertificates_IncludesFilters(t *testing.T) {
var out struct {
CertificateSummaryList []struct{} `json:"CertificateSummaryList"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
if tt.wantNonEmpty {
assert.NotEmpty(t, out.CertificateSummaryList)
} else {
@@ -357,7 +358,7 @@ func TestACMHandler_ListCertificates_StatusFilter_AllStatuses(t *testing.T) {
filterBody, _ := json.Marshal(map[string][]string{
"CertificateStatuses": {tt.filterStatus},
- })
+ }, jsonx.V1())
rec := postACMJSON(t, h, "ListCertificates", string(filterBody))
require.Equal(t, http.StatusOK, rec.Code)
@@ -366,7 +367,7 @@ func TestACMHandler_ListCertificates_StatusFilter_AllStatuses(t *testing.T) {
Status string `json:"Status"`
} `json:"CertificateSummaryList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.CertificateSummaryList, tt.wantCount)
if tt.wantCount > 0 {
@@ -398,7 +399,7 @@ func TestACMHandler_ListCertificates_SubjectAlternativeNameSummaries_IncludesPri
SubjectAlternativeNameSummaries []string `json:"SubjectAlternativeNameSummaries"`
} `json:"CertificateSummaryList"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.NotEmpty(t, listOut.CertificateSummaryList)
summaries := listOut.CertificateSummaryList[0].SubjectAlternativeNameSummaries
@@ -480,13 +481,13 @@ func TestACMHandler_SearchCertificates(t *testing.T) {
var out struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out, jsonx.V1()))
body, _ := json.Marshal(map[string]any{
"FilterStatement": map[string]any{
"Filter": map[string]any{"CertificateArn": out.CertificateArn},
},
- })
+ }, jsonx.V1())
rec := postACMJSON(t, h, "SearchCertificates", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "search-arn-1.example.com")
@@ -566,7 +567,7 @@ func TestACMHandler_SearchCertificates(t *testing.T) {
} `json:"X509Attributes"`
} `json:"Results"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Results, 1)
assert.Equal(t, "commonname.example.com", out.Results[0].X509Attributes.Subject.CommonName)
},
@@ -587,7 +588,7 @@ func TestACMHandler_SearchCertificates(t *testing.T) {
importBody, marshalErr := json.Marshal(map[string]any{
"Certificate": base64.StdEncoding.EncodeToString([]byte(certPEM)),
"PrivateKey": base64.StdEncoding.EncodeToString([]byte(keyPEM)),
- })
+ }, jsonx.V1())
require.NoError(t, marshalErr)
importRec := postACMJSON(t, h, "ImportCertificate", string(importBody))
@@ -604,7 +605,7 @@ func TestACMHandler_SearchCertificates(t *testing.T) {
CertificateArn string `json:"CertificateArn"`
} `json:"Results"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Results, 1)
},
},
@@ -631,7 +632,7 @@ func TestACMHandler_SearchCertificates(t *testing.T) {
} `json:"CertificateMetadata"`
} `json:"Results"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Results, 2)
assert.GreaterOrEqual(t,
out.Results[0].CertificateMetadata.AcmCertificateMetadata.CreatedAt,
diff --git a/services/acm/handler_certificates_test.go b/services/acm/handler_certificates_test.go
index 68f843c970..3bf4b8aab8 100644
--- a/services/acm/handler_certificates_test.go
+++ b/services/acm/handler_certificates_test.go
@@ -3,7 +3,8 @@ package acm_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/acm"
)
@@ -42,7 +44,7 @@ func TestACMHandler_ImportCertificate(t *testing.T) {
in, _ := json.Marshal(map[string]string{
"Certificate": b64(certPEM),
"PrivateKey": b64(keyPEM),
- })
+ }, jsonx.V1())
return string(in)
}(),
@@ -57,7 +59,7 @@ func TestACMHandler_ImportCertificate(t *testing.T) {
{
name: "missing_key",
body: func() string {
- in, _ := json.Marshal(map[string]string{"Certificate": b64(certPEM)})
+ in, _ := json.Marshal(map[string]string{"Certificate": b64(certPEM)}, jsonx.V1())
return string(in)
}(),
@@ -147,7 +149,7 @@ func TestACMHandler_ExportCertificate(t *testing.T) {
body, _ := json.Marshal(map[string]string{
"CertificateArn": tt.certARN,
"Passphrase": "dGVzdA==",
- })
+ }, jsonx.V1())
rec := postACMJSON(t, h, "ExportCertificate", string(body))
assert.Equal(t, tt.wantCode, rec.Code)
for _, s := range tt.wantContains {
@@ -183,9 +185,9 @@ func TestACMHandler_GetCertificate(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
- body, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ body, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
rec := postACMJSON(t, h, "GetCertificate", string(body))
assert.Equal(t, tt.wantCode, rec.Code)
for _, s := range tt.wantContains {
@@ -208,9 +210,9 @@ func TestACMHandler_DescribeCertificate_RealistFields(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
- descBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ descBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
descRec := postACMJSON(t, h, "DescribeCertificate", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
@@ -224,7 +226,7 @@ func TestACMHandler_DescribeCertificate_RealistFields(t *testing.T) {
SignatureAlgorithm string `json:"SignatureAlgorithm"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.NotEmpty(t, descOut.Certificate.Serial, "Serial should be set")
assert.Contains(t, descOut.Certificate.Subject, "realism.example.com", "Subject should contain domain")
assert.Contains(t, descOut.Certificate.Issuer, "realism.example.com", "Issuer should contain domain (self-signed)")
@@ -247,16 +249,16 @@ func TestACMHandler_ImportCertificate_RealistFields(t *testing.T) {
body, _ := json.Marshal(map[string]string{
"Certificate": b64(src.CertificateBody),
"PrivateKey": b64(src.PrivateKey),
- })
+ }, jsonx.V1())
importRec := postACMJSON(t, h, "ImportCertificate", string(body))
require.Equal(t, http.StatusOK, importRec.Code)
var importOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importOut))
+ require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importOut, jsonx.V1()))
- descBody, _ := json.Marshal(map[string]string{"CertificateArn": importOut.CertificateArn})
+ descBody, _ := json.Marshal(map[string]string{"CertificateArn": importOut.CertificateArn}, jsonx.V1())
descRec := postACMJSON(t, h, "DescribeCertificate", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
@@ -266,7 +268,7 @@ func TestACMHandler_ImportCertificate_RealistFields(t *testing.T) {
Type string `json:"Type"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, "IMPORTED", descOut.Certificate.Type)
assert.NotNil(t, descOut.Certificate.ImportedAt, "ImportedAt should be set for imported cert")
}
@@ -288,14 +290,14 @@ func TestACMHandler_ImportCertificate_ReImport(t *testing.T) {
body1, _ := json.Marshal(map[string]string{
"Certificate": b64(src1.CertificateBody),
"PrivateKey": b64(src1.PrivateKey),
- })
+ }, jsonx.V1())
importRec := postACMJSON(t, h, "ImportCertificate", string(body1))
require.Equal(t, http.StatusOK, importRec.Code)
var importOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importOut))
+ require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importOut, jsonx.V1()))
originalARN := importOut.CertificateArn
// Re-import using the same ARN with new cert material
@@ -303,7 +305,7 @@ func TestACMHandler_ImportCertificate_ReImport(t *testing.T) {
"CertificateArn": originalARN,
"Certificate": b64(src2.CertificateBody),
"PrivateKey": b64(src2.PrivateKey),
- })
+ }, jsonx.V1())
reImportRec := postACMJSON(t, h, "ImportCertificate", string(body2))
require.Equal(t, http.StatusOK, reImportRec.Code)
@@ -311,7 +313,7 @@ func TestACMHandler_ImportCertificate_ReImport(t *testing.T) {
var reImportOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reImportRec.Body.Bytes(), &reImportOut))
+ require.NoError(t, json.Unmarshal(reImportRec.Body.Bytes(), &reImportOut, jsonx.V1()))
assert.Equal(t, originalARN, reImportOut.CertificateArn, "re-import should return same ARN")
}
@@ -330,7 +332,7 @@ func TestACMHandler_RequestCertificate_IdempotencyToken(t *testing.T) {
var out1 struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
// Second call with same token should return same ARN
rec2 := postACMJSON(t, h, "RequestCertificate", body)
@@ -339,7 +341,7 @@ func TestACMHandler_RequestCertificate_IdempotencyToken(t *testing.T) {
var out2 struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
assert.Equal(t, out1.CertificateArn, out2.CertificateArn,
"repeated RequestCertificate with same IdempotencyToken must return same ARN")
@@ -376,7 +378,7 @@ func TestACMHandler_RequestCertificate_DomainValidation(t *testing.T) {
t.Parallel()
h := newACMHandler()
- body, _ := json.Marshal(map[string]string{"DomainName": tt.domain})
+ body, _ := json.Marshal(map[string]string{"DomainName": tt.domain}, jsonx.V1())
rec := postACMJSON(t, h, "RequestCertificate", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
})
@@ -395,9 +397,9 @@ func TestACMHandler_DescribeCertificate_KeyUsageAndInUseBy(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
- descBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ descBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
descRec := postACMJSON(t, h, "DescribeCertificate", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
@@ -408,7 +410,7 @@ func TestACMHandler_DescribeCertificate_KeyUsageAndInUseBy(t *testing.T) {
InUseBy []string `json:"InUseBy"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
require.NotEmpty(t, descOut.Certificate.KeyUsage, "KeyUsage should be set")
assert.Equal(t, "DIGITAL_SIGNATURE", descOut.Certificate.KeyUsage[0]["Name"])
@@ -430,9 +432,9 @@ func TestACMHandler_RequestCertificate_EMAIL_ValidationEmails(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
- descBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ descBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
descRec := postACMJSON(t, h, "DescribeCertificate", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
@@ -444,7 +446,7 @@ func TestACMHandler_RequestCertificate_EMAIL_ValidationEmails(t *testing.T) {
} `json:"DomainValidationOptions"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
require.NotEmpty(t, descOut.Certificate.DomainValidationOptions)
dvo := descOut.Certificate.DomainValidationOptions[0]
@@ -483,7 +485,7 @@ func TestACMHandler_ExportCertificate_PassphraseRequired(t *testing.T) {
{
name: "missing_passphrase_rejected",
body: func() string {
- b2, _ := json.Marshal(map[string]string{"CertificateArn": importedCert.ARN})
+ b2, _ := json.Marshal(map[string]string{"CertificateArn": importedCert.ARN}, jsonx.V1())
return string(b2)
}(),
@@ -496,7 +498,7 @@ func TestACMHandler_ExportCertificate_PassphraseRequired(t *testing.T) {
b2, _ := json.Marshal(map[string]string{
"CertificateArn": importedCert.ARN,
"Passphrase": "dGVzdC1wYXNz", // base64("test-pass")
- })
+ }, jsonx.V1())
return string(b2)
}(),
@@ -509,7 +511,7 @@ func TestACMHandler_ExportCertificate_PassphraseRequired(t *testing.T) {
b2, _ := json.Marshal(map[string]string{
"CertificateArn": importedCert.ARN,
"Passphrase": "not-valid-base64!!!",
- })
+ }, jsonx.V1())
return string(b2)
}(),
@@ -548,7 +550,7 @@ func TestACMHandler_ExportCertificate_CertificateChainAlwaysPresent(t *testing.T
exportBody, _ := json.Marshal(map[string]string{
"CertificateArn": imported.ARN,
"Passphrase": "dGVzdA==", // base64("test")
- })
+ }, jsonx.V1())
rec := postACMJSON(t, h, "ExportCertificate", string(exportBody))
require.Equal(t, http.StatusOK, rec.Code)
@@ -557,7 +559,7 @@ func TestACMHandler_ExportCertificate_CertificateChainAlwaysPresent(t *testing.T
Certificate string `json:"Certificate"`
PrivateKey string `json:"PrivateKey"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.CertificateChain, "CertificateChain should always be present on export")
assert.NotEmpty(t, out.Certificate)
assert.NotEmpty(t, out.PrivateKey)
@@ -565,7 +567,7 @@ func TestACMHandler_ExportCertificate_CertificateChainAlwaysPresent(t *testing.T
func mustMarshal(t *testing.T, v any) string {
t.Helper()
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
return string(b)
@@ -623,7 +625,7 @@ func TestACMHandler_SubjectAlternativeNames_IncludesPrimaryDomain(t *testing.T)
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
descBody := mustMarshal(t, map[string]string{"CertificateArn": reqOut.CertificateArn})
descRec := postACMJSON(t, h, "DescribeCertificate", descBody)
@@ -634,7 +636,7 @@ func TestACMHandler_SubjectAlternativeNames_IncludesPrimaryDomain(t *testing.T)
SubjectAlternativeNames []string `json:"SubjectAlternativeNames"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
sans := descOut.Certificate.SubjectAlternativeNames
require.NotEmpty(t, sans, "SubjectAlternativeNames must not be empty")
@@ -676,21 +678,21 @@ func TestACMHandler_DescribeCertificate_InUseByIsAlwaysArray(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
descBody := mustMarshal(t, map[string]string{"CertificateArn": reqOut.CertificateArn})
descRec := postACMJSON(t, h, "DescribeCertificate", descBody)
require.Equal(t, http.StatusOK, descRec.Code)
// Use raw JSON to verify InUseBy is an array, not null or missing.
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw, jsonx.V1()))
certRaw, ok := raw["Certificate"]
require.True(t, ok, "response must have Certificate field")
- var certMap map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(certRaw, &certMap))
+ var certMap map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(certRaw, &certMap, jsonx.V1()))
inUseByRaw, present := certMap["InUseBy"]
require.True(t, present,
@@ -716,7 +718,7 @@ func TestACMHandler_Serial_ColonSeparatedHexFormat(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
descBody := mustMarshal(t, map[string]string{"CertificateArn": reqOut.CertificateArn})
descRec := postACMJSON(t, h, "DescribeCertificate", descBody)
@@ -727,7 +729,7 @@ func TestACMHandler_Serial_ColonSeparatedHexFormat(t *testing.T) {
Serial string `json:"Serial"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
serial := descOut.Certificate.Serial
require.NotEmpty(t, serial, "Serial must be set for an issued certificate")
@@ -769,7 +771,7 @@ func TestACMHandler_ImportedCertificate_SerialColonFormat(t *testing.T) {
var importOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importOut))
+ require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importOut, jsonx.V1()))
descBody := mustMarshal(t, map[string]string{"CertificateArn": importOut.CertificateArn})
descRec := postACMJSON(t, h, "DescribeCertificate", descBody)
@@ -780,7 +782,7 @@ func TestACMHandler_ImportedCertificate_SerialColonFormat(t *testing.T) {
Serial string `json:"Serial"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
serial := descOut.Certificate.Serial
require.NotEmpty(t, serial, "imported cert Serial must be set")
diff --git a/services/acm/handler_request_certificate_invalid_parameter_test.go b/services/acm/handler_request_certificate_invalid_parameter_test.go
index 66d90c7a26..a77ec39916 100644
--- a/services/acm/handler_request_certificate_invalid_parameter_test.go
+++ b/services/acm/handler_request_certificate_invalid_parameter_test.go
@@ -1,13 +1,15 @@
package acm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestACMHandler_RequestCertificate_InvalidParameterException verifies that RequestCertificate's
@@ -40,7 +42,7 @@ func TestACMHandler_RequestCertificate_InvalidParameterException(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp.Type)
})
}
@@ -65,7 +67,7 @@ func TestACMHandler_RequestCertificate_IdempotencyMismatch_InvalidParameterExcep
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(second.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(second.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp.Type)
}
@@ -83,7 +85,7 @@ func TestACMHandler_CreateAcmeDomainValidation_InvalidDomainName_StillValidation
"AcmeEndpointArn": epARN,
"DomainName": "foo..bad.com",
"PrevalidationOptions": map[string]any{"DnsPrevalidation": map[string]any{}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := postACMJSON(t, h, "CreateAcmeDomainValidation", string(body))
@@ -92,7 +94,7 @@ func TestACMHandler_CreateAcmeDomainValidation_InvalidDomainName_StillValidation
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ValidationException", errResp.Type)
}
diff --git a/services/acm/handler_resource_tags.go b/services/acm/handler_resource_tags.go
index 5b5ed84748..a243436e71 100644
--- a/services/acm/handler_resource_tags.go
+++ b/services/acm/handler_resource_tags.go
@@ -2,9 +2,10 @@ package acm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -66,7 +67,7 @@ type listTagsForResourceOutput struct {
func (h *Handler) jsonListTagsForResource(ctx context.Context, body []byte) (any, error) {
var input listTagsForResourceInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -86,7 +87,7 @@ type tagResourceOutput struct{}
func (h *Handler) jsonTagResource(ctx context.Context, body []byte) (any, error) {
var input tagResourceInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -119,7 +120,7 @@ type untagResourceOutput struct{}
func (h *Handler) jsonUntagResource(ctx context.Context, body []byte) (any, error) {
var input untagResourceInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
diff --git a/services/acm/handler_search_certificates.go b/services/acm/handler_search_certificates.go
index 0c5967d71c..a184b848c9 100644
--- a/services/acm/handler_search_certificates.go
+++ b/services/acm/handler_search_certificates.go
@@ -2,8 +2,10 @@ package acm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type dnsNameFilterWire struct {
@@ -291,7 +293,7 @@ type searchCertificatesOutput struct {
func (h *Handler) jsonSearchCertificates(ctx context.Context, body []byte) (any, error) {
var input searchCertificatesInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
diff --git a/services/acm/handler_tags.go b/services/acm/handler_tags.go
index 837431c39e..9c9e0daa49 100644
--- a/services/acm/handler_tags.go
+++ b/services/acm/handler_tags.go
@@ -2,10 +2,11 @@ package acm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -146,7 +147,7 @@ func (h *Handler) getTags(resourceID string) []map[string]string {
func (h *Handler) jsonListTagsForCertificate(ctx context.Context, body []byte) (any, error) {
var input listTagsForCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -163,7 +164,7 @@ func (h *Handler) jsonListTagsForCertificate(ctx context.Context, body []byte) (
func (h *Handler) jsonAddTagsToCertificate(ctx context.Context, body []byte) (any, error) {
var input addTagsToCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
@@ -188,7 +189,7 @@ func (h *Handler) jsonAddTagsToCertificate(ctx context.Context, body []byte) (an
func (h *Handler) jsonRemoveTagsFromCertificate(ctx context.Context, body []byte) (any, error) {
var input removeTagsFromCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidParameter
}
diff --git a/services/acm/handler_tags_test.go b/services/acm/handler_tags_test.go
index b58a87004e..51bd2dc160 100644
--- a/services/acm/handler_tags_test.go
+++ b/services/acm/handler_tags_test.go
@@ -1,7 +1,7 @@
package acm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/acm"
)
@@ -66,17 +67,17 @@ func TestACMHandler_RequestCertificate_Tags(t *testing.T) {
var out struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// Verify tags are stored
- listBody, _ := json.Marshal(map[string]string{"CertificateArn": out.CertificateArn})
+ listBody, _ := json.Marshal(map[string]string{"CertificateArn": out.CertificateArn}, jsonx.V1())
listRec := postACMJSON(t, h, "ListTagsForCertificate", string(listBody))
require.Equal(t, http.StatusOK, listRec.Code)
var tagsOut struct {
Tags []map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsOut, jsonx.V1()))
tagMap := make(map[string]string)
for _, t2 := range tagsOut.Tags {
@@ -113,18 +114,18 @@ func TestACMHandler_GenericResourceTags_RouteByArnType(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
// Tag via the certificate-specific op...
addBody, _ := json.Marshal(map[string]any{
"CertificateArn": reqOut.CertificateArn,
"Tags": []map[string]string{{"Key": "via", "Value": "AddTagsToCertificate"}},
- })
+ }, jsonx.V1())
addRec := postACMJSON(t, h, "AddTagsToCertificate", string(addBody))
require.Equal(t, http.StatusOK, addRec.Code)
// ...and read it back via the generic op.
- listBody, _ := json.Marshal(map[string]string{"ResourceArn": reqOut.CertificateArn})
+ listBody, _ := json.Marshal(map[string]string{"ResourceArn": reqOut.CertificateArn}, jsonx.V1())
listRec := postACMJSON(t, h, "ListTagsForResource", string(listBody))
require.Equal(t, http.StatusOK, listRec.Code)
assert.Contains(t, listRec.Body.String(), "AddTagsToCertificate")
@@ -133,12 +134,12 @@ func TestACMHandler_GenericResourceTags_RouteByArnType(t *testing.T) {
tagBody, _ := json.Marshal(map[string]any{
"ResourceArn": reqOut.CertificateArn,
"Tags": []map[string]string{{"Key": "via", "Value": "TagResource"}},
- })
+ }, jsonx.V1())
tagRec := postACMJSON(t, h, "TagResource", string(tagBody))
require.Equal(t, http.StatusOK, tagRec.Code, tagRec.Body.String())
// ...and read it back via the certificate-specific op.
- certListBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ certListBody, _ := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
certListRec := postACMJSON(t, h, "ListTagsForCertificate", string(certListBody))
require.Equal(t, http.StatusOK, certListRec.Code)
assert.Contains(t, certListRec.Body.String(), "TagResource")
@@ -154,16 +155,19 @@ func TestACMHandler_GenericResourceTags_RouteByArnType(t *testing.T) {
tagBody, _ := json.Marshal(map[string]any{
"ResourceArn": epARN,
"Tags": []map[string]string{{"Key": "env", "Value": "prod"}},
- })
+ }, jsonx.V1())
tagRec := postACMJSON(t, h, "TagResource", string(tagBody))
require.Equal(t, http.StatusOK, tagRec.Code, tagRec.Body.String())
- listBody, _ := json.Marshal(map[string]string{"ResourceArn": epARN})
+ listBody, _ := json.Marshal(map[string]string{"ResourceArn": epARN}, jsonx.V1())
listRec := postACMJSON(t, h, "ListTagsForResource", string(listBody))
require.Equal(t, http.StatusOK, listRec.Code)
assert.Contains(t, listRec.Body.String(), "prod")
- untagBody, _ := json.Marshal(map[string]any{"ResourceArn": epARN, "TagKeys": []string{"env"}})
+ untagBody, _ := json.Marshal(
+ map[string]any{"ResourceArn": epARN, "TagKeys": []string{"env"}},
+ jsonx.V1(),
+ )
untagRec := postACMJSON(t, h, "UntagResource", string(untagBody))
require.Equal(t, http.StatusOK, untagRec.Code)
@@ -250,7 +254,7 @@ func TestACMHandler_NewStyleTagValidation_UsesValidationAndServiceQuota(t *testi
"AuthorizationBehavior": "PRE_APPROVED",
"CertificateAuthority": map[string]any{"PublicCertificateAuthority": map[string]any{}},
"Tags": tooManyTags(),
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := postACMJSON(t, h, "CreateAcmeEndpoint", string(body))
@@ -268,7 +272,7 @@ func TestACMHandler_NewStyleTagValidation_UsesValidationAndServiceQuota(t *testi
body, err := json.Marshal(map[string]any{
"ResourceArn": epARN,
"Tags": []map[string]string{{"Key": "aws:reserved", "Value": "v"}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := postACMJSON(t, h, "TagResource", string(body))
@@ -276,7 +280,7 @@ func TestACMHandler_NewStyleTagValidation_UsesValidationAndServiceQuota(t *testi
assert.Contains(t, rec.Body.String(), "ValidationException")
assert.NotContains(t, rec.Body.String(), "InvalidTagException")
- listBody, _ := json.Marshal(map[string]string{"ResourceArn": epARN})
+ listBody, _ := json.Marshal(map[string]string{"ResourceArn": epARN}, jsonx.V1())
listRec := postACMJSON(t, h, "ListTagsForResource", string(listBody))
require.Equal(t, http.StatusOK, listRec.Code)
assert.JSONEq(t, `{"Tags":[]}`, listRec.Body.String(), "rejected tag must not be stored")
@@ -289,7 +293,7 @@ func TestACMHandler_NewStyleTagValidation_UsesValidationAndServiceQuota(t *testi
epARN := createTestAcmeEndpoint(t, h)
- body, err := json.Marshal(map[string]any{"ResourceArn": epARN, "Tags": tooManyTags()})
+ body, err := json.Marshal(map[string]any{"ResourceArn": epARN, "Tags": tooManyTags()}, jsonx.V1())
require.NoError(t, err)
rec := postACMJSON(t, h, "TagResource", string(body))
@@ -336,7 +340,7 @@ func TestACMHandler_LegacyTagOps_KeyOrValueTooLong_InvalidTagException(t *testin
body, err := json.Marshal(map[string]any{
"DomainName": "toolong.example.com",
"Tags": tt.tags,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := postACMJSON(t, h, "RequestCertificate", string(body))
diff --git a/services/acm/handler_test.go b/services/acm/handler_test.go
index d33ca07179..07f0e83625 100644
--- a/services/acm/handler_test.go
+++ b/services/acm/handler_test.go
@@ -1,7 +1,7 @@
package acm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/acm"
)
@@ -205,10 +206,13 @@ func TestACMHandler(t *testing.T) {
CertificateArn string `json:"CertificateArn"`
} `json:"CertificateSummaryList"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.NotEmpty(t, listResp.CertificateSummaryList)
b, _ := json.Marshal(
- map[string]string{"CertificateArn": listResp.CertificateSummaryList[0].CertificateArn},
+ map[string]string{
+ "CertificateArn": listResp.CertificateSummaryList[0].CertificateArn,
+ },
+ jsonx.V1(),
)
body = string(b)
}
@@ -235,7 +239,7 @@ func createTestAcmeEndpoint(t *testing.T, h *acm.Handler) string {
var out struct {
AcmeEndpointArn string `json:"AcmeEndpointArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.AcmeEndpointArn)
return out.AcmeEndpointArn
@@ -291,7 +295,7 @@ func TestACMHandler_AcmeEndpoints(t *testing.T) {
t.Helper()
epARN := createTestAcmeEndpoint(t, h)
- body, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN})
+ body, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN}, jsonx.V1())
rec := postACMJSON(t, h, "DescribeAcmeEndpoint", string(body))
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
assert.Contains(t, rec.Body.String(), `"Status":"ACTIVE"`)
@@ -338,11 +342,14 @@ func TestACMHandler_AcmeEndpoints(t *testing.T) {
t.Helper()
epARN := createTestAcmeEndpoint(t, h)
- updBody, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN, "Contact": "REQUIRED"})
+ updBody, _ := json.Marshal(
+ map[string]string{"AcmeEndpointArn": epARN, "Contact": "REQUIRED"},
+ jsonx.V1(),
+ )
updRec := postACMJSON(t, h, "UpdateAcmeEndpoint", string(updBody))
require.Equal(t, http.StatusOK, updRec.Code, updRec.Body.String())
- descBody, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN})
+ descBody, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN}, jsonx.V1())
descRec := postACMJSON(t, h, "DescribeAcmeEndpoint", string(descBody))
assert.Contains(t, descRec.Body.String(), `"Contact":"REQUIRED"`)
},
@@ -353,7 +360,7 @@ func TestACMHandler_AcmeEndpoints(t *testing.T) {
t.Helper()
epARN := createTestAcmeEndpoint(t, h)
- delBody, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN})
+ delBody, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN}, jsonx.V1())
delRec := postACMJSON(t, h, "DeleteAcmeEndpoint", string(delBody))
require.Equal(t, http.StatusOK, delRec.Code)
@@ -377,7 +384,7 @@ func TestACMHandler_AcmeEndpoints(t *testing.T) {
assert.Contains(t, delRec.Body.String(), "ValidationException")
assert.NotContains(t, delRec.Body.String(), "ResourceNotFoundException")
- descBody, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN})
+ descBody, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN}, jsonx.V1())
descRec := postACMJSON(t, h, "DescribeAcmeEndpoint", string(descBody))
assert.Equal(t, http.StatusOK, descRec.Code, "unrelated endpoint must be untouched")
},
@@ -395,7 +402,7 @@ func TestACMHandler_AcmeEndpoints(t *testing.T) {
eabBody, _ := json.Marshal(map[string]string{
"AcmeEndpointArn": epARN,
"RoleArn": "arn:aws:iam::000000000000:role/acme-role",
- })
+ }, jsonx.V1())
eabRec := postACMJSON(t, h, "CreateAcmeExternalAccountBinding", string(eabBody))
require.Equal(t, http.StatusOK, eabRec.Code, eabRec.Body.String())
@@ -404,7 +411,7 @@ func TestACMHandler_AcmeEndpoints(t *testing.T) {
AcmeExternalAccountBindingArn string `json:"AcmeExternalAccountBindingArn"`
} `json:"ExternalAccountBinding"`
}
- require.NoError(t, json.Unmarshal(eabRec.Body.Bytes(), &eabOut))
+ require.NoError(t, json.Unmarshal(eabRec.Body.Bytes(), &eabOut, jsonx.V1()))
eabARN := eabOut.ExternalAccountBinding.AcmeExternalAccountBindingArn
require.NotEmpty(t, eabARN)
@@ -412,26 +419,26 @@ func TestACMHandler_AcmeEndpoints(t *testing.T) {
"AcmeEndpointArn": epARN,
"DomainName": "cascade.example.com",
"PrevalidationOptions": map[string]any{"DnsPrevalidation": map[string]any{}},
- })
+ }, jsonx.V1())
dvRec := postACMJSON(t, h, "CreateAcmeDomainValidation", string(dvBody))
require.Equal(t, http.StatusOK, dvRec.Code, dvRec.Body.String())
var dvOut struct {
AcmeDomainValidationArn string `json:"AcmeDomainValidationArn"`
}
- require.NoError(t, json.Unmarshal(dvRec.Body.Bytes(), &dvOut))
+ require.NoError(t, json.Unmarshal(dvRec.Body.Bytes(), &dvOut, jsonx.V1()))
require.NotEmpty(t, dvOut.AcmeDomainValidationArn)
- delBody, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN})
+ delBody, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN}, jsonx.V1())
delRec := postACMJSON(t, h, "DeleteAcmeEndpoint", string(delBody))
require.Equal(t, http.StatusOK, delRec.Code)
- descEABBody, _ := json.Marshal(map[string]string{"AcmeExternalAccountBindingArn": eabARN})
+ descEABBody, _ := json.Marshal(map[string]string{"AcmeExternalAccountBindingArn": eabARN}, jsonx.V1())
descEABRec := postACMJSON(t, h, "DescribeAcmeExternalAccountBinding", string(descEABBody))
assert.Equal(t, http.StatusBadRequest, descEABRec.Code, "EAB must be cascade-deleted with its endpoint")
descDVBody, _ := json.Marshal(
- map[string]string{"AcmeDomainValidationArn": dvOut.AcmeDomainValidationArn},
+ map[string]string{"AcmeDomainValidationArn": dvOut.AcmeDomainValidationArn}, jsonx.V1(),
)
descDVRec := postACMJSON(t, h, "DescribeAcmeDomainValidation", string(descDVBody))
assert.Equal(t, http.StatusBadRequest, descDVRec.Code,
@@ -473,7 +480,7 @@ func TestACMHandler_AcmeExternalAccountBindings(t *testing.T) {
"AcmeEndpointArn": epARN,
"RoleArn": "arn:aws:iam::000000000000:role/acme-role",
"Expiration": map[string]any{"Type": "DAYS", "Value": 7},
- })
+ }, jsonx.V1())
rec := postACMJSON(t, h, "CreateAcmeExternalAccountBinding", string(body))
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
assert.Contains(t, rec.Body.String(), "acme-external-account-binding/")
@@ -486,7 +493,10 @@ func TestACMHandler_AcmeExternalAccountBindings(t *testing.T) {
t.Helper()
epARN := createTestAcmeEndpoint(t, h)
- body, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN, "RoleArn": "not-a-role-arn"})
+ body, _ := json.Marshal(
+ map[string]string{"AcmeEndpointArn": epARN, "RoleArn": "not-a-role-arn"},
+ jsonx.V1(),
+ )
rec := postACMJSON(t, h, "CreateAcmeExternalAccountBinding", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
assert.Contains(t, rec.Body.String(), "ValidationException")
@@ -500,7 +510,7 @@ func TestACMHandler_AcmeExternalAccountBindings(t *testing.T) {
body, _ := json.Marshal(map[string]string{
"AcmeEndpointArn": "arn:aws:acm:us-east-1:000000000000:acme-endpoint/nope",
"RoleArn": "arn:aws:iam::000000000000:role/acme-role",
- })
+ }, jsonx.V1())
rec := postACMJSON(t, h, "CreateAcmeExternalAccountBinding", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
assert.Contains(t, rec.Body.String(), "ResourceNotFoundException")
@@ -515,7 +525,7 @@ func TestACMHandler_AcmeExternalAccountBindings(t *testing.T) {
createBody, _ := json.Marshal(map[string]string{
"AcmeEndpointArn": epARN,
"RoleArn": "arn:aws:iam::000000000000:role/acme-role",
- })
+ }, jsonx.V1())
createRec := postACMJSON(t, h, "CreateAcmeExternalAccountBinding", string(createBody))
require.Equal(t, http.StatusOK, createRec.Code)
@@ -524,10 +534,10 @@ func TestACMHandler_AcmeExternalAccountBindings(t *testing.T) {
AcmeExternalAccountBindingArn string `json:"AcmeExternalAccountBindingArn"`
} `json:"ExternalAccountBinding"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
eabARN := createOut.ExternalAccountBinding.AcmeExternalAccountBindingArn
- credBody, _ := json.Marshal(map[string]string{"AcmeExternalAccountBindingArn": eabARN})
+ credBody, _ := json.Marshal(map[string]string{"AcmeExternalAccountBindingArn": eabARN}, jsonx.V1())
credRec := postACMJSON(t, h, "GetAcmeExternalAccountBindingCredentials", string(credBody))
require.Equal(t, http.StatusOK, credRec.Code)
@@ -535,7 +545,7 @@ func TestACMHandler_AcmeExternalAccountBindings(t *testing.T) {
KeyID string `json:"KeyId"`
MacKey string `json:"MacKey"`
}
- require.NoError(t, json.Unmarshal(credRec.Body.Bytes(), &credOut))
+ require.NoError(t, json.Unmarshal(credRec.Body.Bytes(), &credOut, jsonx.V1()))
assert.NotEmpty(t, credOut.KeyID)
assert.NotEmpty(t, credOut.MacKey)
@@ -578,11 +588,11 @@ func TestACMHandler_AcmeExternalAccountBindings(t *testing.T) {
createBody, _ := json.Marshal(map[string]string{
"AcmeEndpointArn": epARN,
"RoleArn": "arn:aws:iam::000000000000:role/acme-role",
- })
+ }, jsonx.V1())
createRec := postACMJSON(t, h, "CreateAcmeExternalAccountBinding", string(createBody))
require.Equal(t, http.StatusOK, createRec.Code)
- listBody, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN})
+ listBody, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN}, jsonx.V1())
listRec := postACMJSON(t, h, "ListAcmeExternalAccountBindings", string(listBody))
require.Equal(t, http.StatusOK, listRec.Code)
assert.Contains(t, listRec.Body.String(), "acme-external-account-binding/")
@@ -597,7 +607,7 @@ func TestACMHandler_AcmeExternalAccountBindings(t *testing.T) {
createBody, _ := json.Marshal(map[string]string{
"AcmeEndpointArn": epARN,
"RoleArn": "arn:aws:iam::000000000000:role/acme-role",
- })
+ }, jsonx.V1())
createRec := postACMJSON(t, h, "CreateAcmeExternalAccountBinding", string(createBody))
require.Equal(t, http.StatusOK, createRec.Code)
@@ -606,11 +616,11 @@ func TestACMHandler_AcmeExternalAccountBindings(t *testing.T) {
AcmeExternalAccountBindingArn string `json:"AcmeExternalAccountBindingArn"`
} `json:"ExternalAccountBinding"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
delBody, _ := json.Marshal(map[string]string{
"AcmeExternalAccountBindingArn": createOut.ExternalAccountBinding.AcmeExternalAccountBindingArn,
- })
+ }, jsonx.V1())
delRec := postACMJSON(t, h, "DeleteAcmeExternalAccountBinding", string(delBody))
assert.Equal(t, http.StatusOK, delRec.Code)
},
@@ -627,7 +637,7 @@ func TestACMHandler_AcmeExternalAccountBindings(t *testing.T) {
createBody, _ := json.Marshal(map[string]string{
"AcmeEndpointArn": epARN,
"RoleArn": "arn:aws:iam::000000000000:role/acme-role",
- })
+ }, jsonx.V1())
createRec := postACMJSON(t, h, "CreateAcmeExternalAccountBinding", string(createBody))
require.Equal(t, http.StatusOK, createRec.Code)
@@ -636,7 +646,7 @@ func TestACMHandler_AcmeExternalAccountBindings(t *testing.T) {
AcmeExternalAccountBindingArn string `json:"AcmeExternalAccountBindingArn"`
} `json:"ExternalAccountBinding"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
eabARN := createOut.ExternalAccountBinding.AcmeExternalAccountBindingArn
delBody := `{"AcmeExternalAccountBindingArn":"` + epARN +
@@ -646,7 +656,7 @@ func TestACMHandler_AcmeExternalAccountBindings(t *testing.T) {
assert.Contains(t, delRec.Body.String(), "ValidationException")
assert.NotContains(t, delRec.Body.String(), "ResourceNotFoundException")
- descBody, _ := json.Marshal(map[string]string{"AcmeExternalAccountBindingArn": eabARN})
+ descBody, _ := json.Marshal(map[string]string{"AcmeExternalAccountBindingArn": eabARN}, jsonx.V1())
descRec := postACMJSON(t, h, "DescribeAcmeExternalAccountBinding", string(descBody))
assert.Equal(t, http.StatusOK, descRec.Code, "unrelated EAB must be untouched")
},
@@ -688,17 +698,17 @@ func TestACMHandler_AcmeDomainValidations(t *testing.T) {
"PrevalidationOptions": map[string]any{
"DnsPrevalidation": map[string]any{"HostedZoneId": "Z123456"},
},
- })
+ }, jsonx.V1())
createRec := postACMJSON(t, h, "CreateAcmeDomainValidation", string(body))
require.Equal(t, http.StatusOK, createRec.Code, createRec.Body.String())
var createOut struct {
AcmeDomainValidationArn string `json:"AcmeDomainValidationArn"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
descBody, _ := json.Marshal(
- map[string]string{"AcmeDomainValidationArn": createOut.AcmeDomainValidationArn},
+ map[string]string{"AcmeDomainValidationArn": createOut.AcmeDomainValidationArn}, jsonx.V1(),
)
descRec := postACMJSON(t, h, "DescribeAcmeDomainValidation", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
@@ -714,7 +724,10 @@ func TestACMHandler_AcmeDomainValidations(t *testing.T) {
t.Helper()
epARN := createTestAcmeEndpoint(t, h)
- body, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN, "DomainName": "dv2.example.com"})
+ body, _ := json.Marshal(
+ map[string]string{"AcmeEndpointArn": epARN, "DomainName": "dv2.example.com"},
+ jsonx.V1(),
+ )
rec := postACMJSON(t, h, "CreateAcmeDomainValidation", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
assert.Contains(t, rec.Body.String(), "ValidationException")
@@ -730,11 +743,11 @@ func TestACMHandler_AcmeDomainValidations(t *testing.T) {
"AcmeEndpointArn": epARN,
"DomainName": "dv3.example.com",
"PrevalidationOptions": map[string]any{"DnsPrevalidation": map[string]any{}},
- })
+ }, jsonx.V1())
createRec := postACMJSON(t, h, "CreateAcmeDomainValidation", string(body))
require.Equal(t, http.StatusOK, createRec.Code)
- listBody, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN})
+ listBody, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN}, jsonx.V1())
listRec := postACMJSON(t, h, "ListAcmeDomainValidations", string(listBody))
require.Equal(t, http.StatusOK, listRec.Code)
assert.Contains(t, listRec.Body.String(), "dv3.example.com")
@@ -750,26 +763,26 @@ func TestACMHandler_AcmeDomainValidations(t *testing.T) {
"AcmeEndpointArn": epARN,
"DomainName": "dv4.example.com",
"PrevalidationOptions": map[string]any{"DnsPrevalidation": map[string]any{}},
- })
+ }, jsonx.V1())
createRec := postACMJSON(t, h, "CreateAcmeDomainValidation", string(body))
require.Equal(t, http.StatusOK, createRec.Code)
var createOut struct {
AcmeDomainValidationArn string `json:"AcmeDomainValidationArn"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
updBody, _ := json.Marshal(map[string]any{
"AcmeDomainValidationArn": createOut.AcmeDomainValidationArn,
"PrevalidationOptions": map[string]any{
"DnsPrevalidation": map[string]any{"HostedZoneId": "Znew"},
},
- })
+ }, jsonx.V1())
updRec := postACMJSON(t, h, "UpdateAcmeDomainValidation", string(updBody))
require.Equal(t, http.StatusOK, updRec.Code, updRec.Body.String())
descBody, _ := json.Marshal(
- map[string]string{"AcmeDomainValidationArn": createOut.AcmeDomainValidationArn},
+ map[string]string{"AcmeDomainValidationArn": createOut.AcmeDomainValidationArn}, jsonx.V1(),
)
descRec := postACMJSON(t, h, "DescribeAcmeDomainValidation", string(descBody))
assert.Contains(t, descRec.Body.String(), `"HostedZoneId":"Znew"`)
@@ -785,17 +798,17 @@ func TestACMHandler_AcmeDomainValidations(t *testing.T) {
"AcmeEndpointArn": epARN,
"DomainName": "dv5.example.com",
"PrevalidationOptions": map[string]any{"DnsPrevalidation": map[string]any{}},
- })
+ }, jsonx.V1())
createRec := postACMJSON(t, h, "CreateAcmeDomainValidation", string(body))
require.Equal(t, http.StatusOK, createRec.Code)
var createOut struct {
AcmeDomainValidationArn string `json:"AcmeDomainValidationArn"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
delBody, _ := json.Marshal(
- map[string]string{"AcmeDomainValidationArn": createOut.AcmeDomainValidationArn},
+ map[string]string{"AcmeDomainValidationArn": createOut.AcmeDomainValidationArn}, jsonx.V1(),
)
delRec := postACMJSON(t, h, "DeleteAcmeDomainValidation", string(delBody))
assert.Equal(t, http.StatusOK, delRec.Code)
@@ -814,14 +827,14 @@ func TestACMHandler_AcmeDomainValidations(t *testing.T) {
"AcmeEndpointArn": epARN,
"DomainName": "dv6.example.com",
"PrevalidationOptions": map[string]any{"DnsPrevalidation": map[string]any{}},
- })
+ }, jsonx.V1())
createRec := postACMJSON(t, h, "CreateAcmeDomainValidation", string(body))
require.Equal(t, http.StatusOK, createRec.Code)
var createOut struct {
AcmeDomainValidationArn string `json:"AcmeDomainValidationArn"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
delBody := `{"AcmeDomainValidationArn":"` + epARN + `/acme-domain-validation/nope"}`
delRec := postACMJSON(t, h, "DeleteAcmeDomainValidation", delBody)
@@ -830,7 +843,7 @@ func TestACMHandler_AcmeDomainValidations(t *testing.T) {
assert.NotContains(t, delRec.Body.String(), "ResourceNotFoundException")
descBody, _ := json.Marshal(
- map[string]string{"AcmeDomainValidationArn": createOut.AcmeDomainValidationArn},
+ map[string]string{"AcmeDomainValidationArn": createOut.AcmeDomainValidationArn}, jsonx.V1(),
)
descRec := postACMJSON(t, h, "DescribeAcmeDomainValidation", string(descBody))
assert.Equal(t, http.StatusOK, descRec.Code, "unrelated domain validation must be untouched")
@@ -869,7 +882,7 @@ func TestACMHandler_AcmeAccounts_HonestlyEmpty(t *testing.T) {
body, _ := json.Marshal(map[string]string{
"AcmeEndpointArn": epARN, "AccountUrl": "https://example/acct/1",
- })
+ }, jsonx.V1())
rec := postACMJSON(t, h, "DescribeAcmeAccount", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
assert.Contains(t, rec.Body.String(), "ResourceNotFoundException")
@@ -880,7 +893,7 @@ func TestACMHandler_AcmeAccounts_HonestlyEmpty(t *testing.T) {
run: func(t *testing.T, h *acm.Handler, epARN string) {
t.Helper()
- body, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN})
+ body, _ := json.Marshal(map[string]string{"AcmeEndpointArn": epARN}, jsonx.V1())
rec := postACMJSON(t, h, "ListAcmeAccounts", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), `"AcmeAccounts":[]`)
@@ -893,7 +906,7 @@ func TestACMHandler_AcmeAccounts_HonestlyEmpty(t *testing.T) {
body, _ := json.Marshal(map[string]string{
"AcmeEndpointArn": epARN, "AccountUrl": "https://example/acct/1",
- })
+ }, jsonx.V1())
rec := postACMJSON(t, h, "RevokeAcmeAccount", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
assert.Contains(t, rec.Body.String(), "ResourceNotFoundException")
diff --git a/services/acm/persistence.go b/services/acm/persistence.go
index 7befa5450b..c892e21063 100644
--- a/services/acm/persistence.go
+++ b/services/acm/persistence.go
@@ -2,11 +2,13 @@ package acm
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -60,7 +62,7 @@ func buildPersistenceDTORegistry() (*store.Registry, *store.Table[regionalDTO[Ce
// directly, as before. Timers are runtime-only and were never persisted;
// Restore rebuilds them from certificate state (see below).
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
IdempotencyMap map[string]map[string]certIdempotencyEntry `json:"idempotencyMap,omitempty"`
AccountIdempotency map[string]map[string]accountIdempotencyEntry `json:"accountIdempotency,omitempty"`
AccountConfig map[string]AccountConfig `json:"accountConfig"`
@@ -246,7 +248,7 @@ func (b *InMemoryBackend) applyRestoredMaps(snap *backendSnapshot) {
// restoreCerts rebuilds the "dirty" certs store.Table from snap's DTO table,
// factored out of Restore to keep Restore's own cognitive complexity low.
// Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreCerts(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreCerts(tables map[string]jsontext.Value) error {
dtoReg, certDTOs := buildPersistenceDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
return fmt.Errorf("acm: restore snapshot cert table: %w", err)
@@ -279,7 +281,7 @@ func (h *Handler) Snapshot(ctx context.Context) []byte {
snap := handlerSnapshot{
Tags: tagsMap,
}
- if err := json.Unmarshal(h.Backend.Snapshot(ctx), &snap.Backend); err != nil {
+ if err := json.Unmarshal(h.Backend.Snapshot(ctx), &snap.Backend, jsonx.V1()); err != nil {
return nil
}
@@ -291,12 +293,12 @@ func (h *Handler) Snapshot(ctx context.Context) []byte {
func (h *Handler) Restore(ctx context.Context, data []byte) error {
var snap handlerSnapshot
- if err := json.Unmarshal(data, &snap); err != nil {
+ if err := json.Unmarshal(data, &snap, jsonx.V1()); err != nil {
// Fall back to backend-only snapshot for backward compatibility.
return h.Backend.Restore(ctx, data)
}
- backendData, err := json.Marshal(snap.Backend)
+ backendData, err := json.Marshal(snap.Backend, jsonx.V1())
if err != nil {
return err
}
diff --git a/services/acm/persistence_test.go b/services/acm/persistence_test.go
index a55eabd99f..7bb9816a2a 100644
--- a/services/acm/persistence_test.go
+++ b/services/acm/persistence_test.go
@@ -2,7 +2,7 @@ package acm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/acm"
)
@@ -271,10 +272,10 @@ func TestInMemoryBackend_RestoreVersionMismatch(t *testing.T) {
require.NoError(t, err)
var snapMap map[string]any
- require.NoError(t, json.Unmarshal(donor.Snapshot(t.Context()), &snapMap))
+ require.NoError(t, json.Unmarshal(donor.Snapshot(t.Context()), &snapMap, jsonx.V1()))
tt.mutate(snapMap)
- mutatedSnap, err := json.Marshal(snapMap)
+ mutatedSnap, err := json.Marshal(snapMap, jsonx.V1())
require.NoError(t, err)
// The restore target starts with its own, different state so we
@@ -337,12 +338,12 @@ func TestACMHandler_Persistence_Tags(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
addBody, err := json.Marshal(map[string]any{
"CertificateArn": reqOut.CertificateArn,
"Tags": []map[string]string{{"Key": "team", "Value": "platform"}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
addRec := postACMJSON(t, h, "AddTagsToCertificate", string(addBody))
@@ -354,7 +355,7 @@ func TestACMHandler_Persistence_Tags(t *testing.T) {
freshH := newACMHandler()
require.NoError(t, freshH.Restore(t.Context(), snap))
- listBody, err := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ listBody, err := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
require.NoError(t, err)
listRec := postACMJSON(t, freshH, "ListTagsForCertificate", string(listBody))
@@ -363,7 +364,7 @@ func TestACMHandler_Persistence_Tags(t *testing.T) {
var tagsOut struct {
Tags []map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsOut, jsonx.V1()))
tagMap := make(map[string]string, len(tagsOut.Tags))
for _, kv := range tagsOut.Tags {
diff --git a/services/acm/request_certificate_realism_test.go b/services/acm/request_certificate_realism_test.go
index 68a9693901..7c453196ce 100644
--- a/services/acm/request_certificate_realism_test.go
+++ b/services/acm/request_certificate_realism_test.go
@@ -1,13 +1,15 @@
package acm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestACMHandler_RequestCertificate_InputRealism(t *testing.T) {
@@ -66,7 +68,7 @@ func TestACMHandler_RequestCertificate_InputRealism(t *testing.T) {
Type string `json:"__type"`
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp.Type)
assert.NotContains(t, errResp.Message, "Exception")
})
@@ -83,7 +85,7 @@ func TestACMHandler_WildcardValidationRecordAndNotFound(t *testing.T) {
var created struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
rec = postACMJSON(t, h, "DescribeCertificate", `{"CertificateArn":"`+created.CertificateArn+`"}`)
require.Equal(t, http.StatusOK, rec.Code)
diff --git a/services/acm/wire_field_additions_test.go b/services/acm/wire_field_additions_test.go
index 8b8745cf98..dc92b8b760 100644
--- a/services/acm/wire_field_additions_test.go
+++ b/services/acm/wire_field_additions_test.go
@@ -1,7 +1,7 @@
package acm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/acm"
)
@@ -258,7 +259,7 @@ func TestACMHandler_ListCertificates_SummaryHasCreatedAtAndInUse(t *testing.T) {
InUse *bool `json:"InUse"`
} `json:"CertificateSummaryList"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.CertificateSummaryList)
summary := out.CertificateSummaryList[0]
@@ -296,7 +297,7 @@ func TestACMHandler_ListCertificates_SummaryKeyUsages(t *testing.T) {
ExtendedKeyUsages []string `json:"ExtendedKeyUsages"`
} `json:"CertificateSummaryList"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.CertificateSummaryList)
summary := out.CertificateSummaryList[0]
@@ -320,9 +321,9 @@ func TestACMHandler_RenewCertificate_RenewalSummaryHasUpdatedAt(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
- renewBody, err := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ renewBody, err := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
require.NoError(t, err)
renewRec := postACMJSON(t, h, "RenewCertificate", string(renewBody))
@@ -341,7 +342,7 @@ func TestACMHandler_RenewCertificate_RenewalSummaryHasUpdatedAt(t *testing.T) {
RenewalSummary renewalSummary `json:"RenewalSummary"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.NotEmpty(t, descOut.Certificate.RenewalSummary.RenewalStatus)
assert.Positive(t, descOut.Certificate.RenewalSummary.UpdatedAt,
"RenewalSummary.UpdatedAt must always be present on the wire")
@@ -362,9 +363,9 @@ func TestACMHandler_RequestCertificate_ExportOption_RoundTrips(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
- descBody, err := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ descBody, err := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
require.NoError(t, err)
descRec := postACMJSON(t, h, "DescribeCertificate", string(descBody))
@@ -377,7 +378,7 @@ func TestACMHandler_RequestCertificate_ExportOption_RoundTrips(t *testing.T) {
} `json:"Options"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, "ENABLED", descOut.Certificate.Options.Export)
}
@@ -429,7 +430,7 @@ func TestACMHandler_RequestCertificate_ManagedBy(t *testing.T) {
body, err := json.Marshal(map[string]string{
"DomainName": tt.domain,
"ManagedBy": tt.managedBy,
- })
+ }, jsonx.V1())
require.NoError(t, err)
reqRec := postACMJSON(t, h, "RequestCertificate", string(body))
@@ -442,9 +443,9 @@ func TestACMHandler_RequestCertificate_ManagedBy(t *testing.T) {
var reqOut struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut))
+ require.NoError(t, json.Unmarshal(reqRec.Body.Bytes(), &reqOut, jsonx.V1()))
- descBody, err := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn})
+ descBody, err := json.Marshal(map[string]string{"CertificateArn": reqOut.CertificateArn}, jsonx.V1())
require.NoError(t, err)
descRec := postACMJSON(t, h, "DescribeCertificate", string(descBody))
@@ -455,7 +456,7 @@ func TestACMHandler_RequestCertificate_ManagedBy(t *testing.T) {
ManagedBy string `json:"ManagedBy"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, tt.wantManagedBy, descOut.Certificate.ManagedBy)
listRec := postACMJSON(t, h, "ListCertificates", `{}`)
@@ -466,7 +467,7 @@ func TestACMHandler_RequestCertificate_ManagedBy(t *testing.T) {
ManagedBy string `json:"ManagedBy"`
} `json:"CertificateSummaryList"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.CertificateSummaryList, 1)
assert.Equal(t, tt.wantManagedBy, listOut.CertificateSummaryList[0].ManagedBy)
})
diff --git a/services/acmpca/handler.go b/services/acmpca/handler.go
index 17aef1f156..e51f0d611f 100644
--- a/services/acmpca/handler.go
+++ b/services/acmpca/handler.go
@@ -3,7 +3,8 @@ package acmpca
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -12,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -118,8 +120,8 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
return ""
}
- var m map[string]json.RawMessage
- if unmarshalErr := json.Unmarshal(body, &m); unmarshalErr != nil {
+ var m map[string]jsontext.Value
+ if unmarshalErr := json.Unmarshal(body, &m, jsonx.V1()); unmarshalErr != nil {
return ""
}
@@ -151,7 +153,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// handleError writes a standardized error response back to the client.
@@ -308,11 +310,11 @@ func decodeBase64Field(encoded, fieldName string, sentinel error) (string, error
}
// extractFirstResourceByKeys returns the first string resource found for the provided JSON field names.
-func extractFirstResourceByKeys(body map[string]json.RawMessage, keys ...string) string {
+func extractFirstResourceByKeys(body map[string]jsontext.Value, keys ...string) string {
for _, key := range keys {
if raw, ok := body[key]; ok {
var resourceID string
- if err := json.Unmarshal(raw, &resourceID); err == nil {
+ if err := json.Unmarshal(raw, &resourceID, jsonx.V1()); err == nil {
return resourceID
}
}
diff --git a/services/acmpca/handler_audit_reports.go b/services/acmpca/handler_audit_reports.go
index ea3a1f9696..aad7957c78 100644
--- a/services/acmpca/handler_audit_reports.go
+++ b/services/acmpca/handler_audit_reports.go
@@ -2,7 +2,9 @@ package acmpca
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createCertificateAuthorityAuditReportInput struct {
@@ -30,7 +32,7 @@ type describeCertificateAuthorityAuditReportOutput struct {
func (h *Handler) jsonCreateAuditReport(ctx context.Context, body []byte) (any, error) {
var input createCertificateAuthorityAuditReportInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArgs
}
@@ -52,7 +54,7 @@ func (h *Handler) jsonCreateAuditReport(ctx context.Context, body []byte) (any,
func (h *Handler) jsonDescribeAuditReport(ctx context.Context, body []byte) (any, error) {
var input describeCertificateAuthorityAuditReportInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArgs
}
diff --git a/services/acmpca/handler_ca_policy.go b/services/acmpca/handler_ca_policy.go
index b9497bcded..484b83aaa1 100644
--- a/services/acmpca/handler_ca_policy.go
+++ b/services/acmpca/handler_ca_policy.go
@@ -2,7 +2,9 @@ package acmpca
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type getPolicyInput struct {
@@ -28,7 +30,7 @@ type deletePolicyOutput struct{}
func (h *Handler) jsonGetPolicy(ctx context.Context, body []byte) (any, error) {
var input getPolicyInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
@@ -42,7 +44,7 @@ func (h *Handler) jsonGetPolicy(ctx context.Context, body []byte) (any, error) {
func (h *Handler) jsonPutPolicy(ctx context.Context, body []byte) (any, error) {
var input putPolicyInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
@@ -55,7 +57,7 @@ func (h *Handler) jsonPutPolicy(ctx context.Context, body []byte) (any, error) {
func (h *Handler) jsonDeletePolicy(ctx context.Context, body []byte) (any, error) {
var input deletePolicyInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
diff --git a/services/acmpca/handler_certificate_authorities.go b/services/acmpca/handler_certificate_authorities.go
index 3de412ebe4..43d13e2ace 100644
--- a/services/acmpca/handler_certificate_authorities.go
+++ b/services/acmpca/handler_certificate_authorities.go
@@ -2,8 +2,10 @@ package acmpca
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -211,14 +213,14 @@ func (in *updateCertificateAuthorityInput) UnmarshalJSON(data []byte) error {
type alias updateCertificateAuthorityInput
var a alias
- if err := json.Unmarshal(data, &a); err != nil {
+ if err := json.Unmarshal(data, &a, jsonx.V1()); err != nil {
return err
}
*in = updateCertificateAuthorityInput(a)
- var raw map[string]json.RawMessage
- if err := json.Unmarshal(data, &raw); err != nil {
+ var raw map[string]jsontext.Value
+ if err := json.Unmarshal(data, &raw, jsonx.V1()); err != nil {
return err
}
@@ -262,7 +264,7 @@ type restoreCertificateAuthorityOutput struct{}
func (h *Handler) jsonCreateCA(ctx context.Context, body []byte) (any, error) {
var input createCertificateAuthorityInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArgs
}
@@ -302,7 +304,7 @@ func (h *Handler) jsonCreateCA(ctx context.Context, body []byte) (any, error) {
func (h *Handler) jsonDescribeCA(ctx context.Context, body []byte) (any, error) {
var input describeCertificateAuthorityInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
@@ -316,7 +318,7 @@ func (h *Handler) jsonDescribeCA(ctx context.Context, body []byte) (any, error)
func (h *Handler) jsonListCAs(ctx context.Context, body []byte) (any, error) {
var input listCertificateAuthoritiesInput
- _ = json.Unmarshal(body, &input)
+ _ = json.Unmarshal(body, &input, jsonx.V1())
p, err := h.Backend.ListCertificateAuthorities(ctx, input.NextToken, input.MaxResults, input.ResourceOwner)
if err != nil {
@@ -337,7 +339,7 @@ func (h *Handler) jsonListCAs(ctx context.Context, body []byte) (any, error) {
func (h *Handler) jsonDeleteCA(ctx context.Context, body []byte) (any, error) {
var input deleteCertificateAuthorityInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
@@ -356,7 +358,7 @@ func (h *Handler) jsonDeleteCA(ctx context.Context, body []byte) (any, error) {
func (h *Handler) jsonUpdateCA(ctx context.Context, body []byte) (any, error) {
var input updateCertificateAuthorityInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
@@ -376,7 +378,7 @@ func (h *Handler) jsonUpdateCA(ctx context.Context, body []byte) (any, error) {
func (h *Handler) jsonGetCsr(ctx context.Context, body []byte) (any, error) {
var input getCertificateAuthorityCsrInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
@@ -390,7 +392,7 @@ func (h *Handler) jsonGetCsr(ctx context.Context, body []byte) (any, error) {
func (h *Handler) jsonImportCACert(ctx context.Context, body []byte) (any, error) {
var input importCertificateAuthorityCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
@@ -418,7 +420,7 @@ func (h *Handler) jsonImportCACert(ctx context.Context, body []byte) (any, error
func (h *Handler) jsonGetCACert(ctx context.Context, body []byte) (any, error) {
var input getCertificateAuthorityCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
@@ -432,7 +434,7 @@ func (h *Handler) jsonGetCACert(ctx context.Context, body []byte) (any, error) {
func (h *Handler) jsonRestoreCA(ctx context.Context, body []byte) (any, error) {
var input restoreCertificateAuthorityInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
diff --git a/services/acmpca/handler_certificate_authorities_test.go b/services/acmpca/handler_certificate_authorities_test.go
index 6e5bd34dbc..614c43001a 100644
--- a/services/acmpca/handler_certificate_authorities_test.go
+++ b/services/acmpca/handler_certificate_authorities_test.go
@@ -2,7 +2,7 @@ package acmpca_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/acmpca"
)
@@ -568,7 +569,7 @@ func TestACMPCA_DescribeCertificateAuthority_OwnerAccount(t *testing.T) {
var createOut struct {
CertificateAuthorityArn string `json:"CertificateAuthorityArn"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
descRec := doACMPCARequest(t, h, "DescribeCertificateAuthority", map[string]any{
"CertificateAuthorityArn": createOut.CertificateAuthorityArn,
@@ -580,7 +581,7 @@ func TestACMPCA_DescribeCertificateAuthority_OwnerAccount(t *testing.T) {
OwnerAccount string `json:"OwnerAccount"`
} `json:"CertificateAuthority"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, testAccountID, descOut.CertificateAuthority.OwnerAccount,
"OwnerAccount must match the backend account ID")
@@ -666,7 +667,7 @@ func TestACMPCA_DescribeCertificateAuthority_Serial_AfterActivation(t *testing.T
Status string `json:"Status"`
} `json:"CertificateAuthority"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
require.Equal(t, "ACTIVE", descOut.CertificateAuthority.Status)
assert.NotEmpty(t, descOut.CertificateAuthority.Serial,
diff --git a/services/acmpca/handler_certificates.go b/services/acmpca/handler_certificates.go
index e323d2b781..cb770c01a7 100644
--- a/services/acmpca/handler_certificates.go
+++ b/services/acmpca/handler_certificates.go
@@ -2,11 +2,13 @@ package acmpca
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -169,7 +171,7 @@ type revokeCertificateOutput struct{}
func (h *Handler) jsonIssueCert(ctx context.Context, body []byte) (any, error) {
var input issueCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArgs
}
@@ -486,7 +488,7 @@ func decodeGeneralName(gn generalNameWire) (APIPassthroughSAN, error) {
func (h *Handler) jsonGetCert(ctx context.Context, body []byte) (any, error) {
var input getCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
@@ -511,7 +513,7 @@ func (h *Handler) jsonGetCert(ctx context.Context, body []byte) (any, error) {
func (h *Handler) jsonRevokeCert(ctx context.Context, body []byte) (any, error) {
var input revokeCertificateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
diff --git a/services/acmpca/handler_certificates_test.go b/services/acmpca/handler_certificates_test.go
index 0938770db9..5cc64d4969 100644
--- a/services/acmpca/handler_certificates_test.go
+++ b/services/acmpca/handler_certificates_test.go
@@ -2,7 +2,7 @@ package acmpca_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/acmpca"
)
@@ -265,7 +266,7 @@ func TestACMPCA_IssueCertificate_EndDateValidity(t *testing.T) {
var out struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.CertificateArn,
"IssueCertificate with END_DATE must return a CertificateArn")
}
@@ -325,7 +326,7 @@ func TestACMPCA_IssueCertificate_ValidityTypeAliases(t *testing.T) {
var out struct {
CertificateArn string `json:"CertificateArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.CertificateArn,
"validity type %q must return CertificateArn", tt.validityType)
})
@@ -433,7 +434,7 @@ func TestACMPCA_GetCertificate_ChainIncludesCAChain(t *testing.T) {
Certificate string `json:"Certificate"`
CertificateChain string `json:"CertificateChain"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.Certificate, "Certificate must be present")
if tt.wantChain {
diff --git a/services/acmpca/handler_permissions.go b/services/acmpca/handler_permissions.go
index e4896ed35b..5065acee0d 100644
--- a/services/acmpca/handler_permissions.go
+++ b/services/acmpca/handler_permissions.go
@@ -2,7 +2,9 @@ package acmpca
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type listPermissionsInput struct {
@@ -44,7 +46,7 @@ type deletePermissionOutput struct{}
func (h *Handler) jsonListPermissions(ctx context.Context, body []byte) (any, error) {
var input listPermissionsInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
@@ -76,7 +78,7 @@ func (h *Handler) jsonListPermissions(ctx context.Context, body []byte) (any, er
func (h *Handler) jsonCreatePermission(ctx context.Context, body []byte) (any, error) {
var input createPermissionInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
@@ -95,7 +97,7 @@ func (h *Handler) jsonCreatePermission(ctx context.Context, body []byte) (any, e
func (h *Handler) jsonDeletePermission(ctx context.Context, body []byte) (any, error) {
var input deletePermissionInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
diff --git a/services/acmpca/handler_tags.go b/services/acmpca/handler_tags.go
index de14c7829d..6ee0a8a95b 100644
--- a/services/acmpca/handler_tags.go
+++ b/services/acmpca/handler_tags.go
@@ -2,11 +2,12 @@ package acmpca
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -126,7 +127,7 @@ func (h *Handler) GetTagsForTest(resourceID string) []map[string]string {
func (h *Handler) jsonTagCA(ctx context.Context, body []byte) (any, error) {
var input tagCertificateAuthorityInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
@@ -151,7 +152,7 @@ func (h *Handler) jsonTagCA(ctx context.Context, body []byte) (any, error) {
func (h *Handler) jsonUntagCA(ctx context.Context, body []byte) (any, error) {
var input untagCertificateAuthorityInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
@@ -171,7 +172,7 @@ func (h *Handler) jsonUntagCA(ctx context.Context, body []byte) (any, error) {
func (h *Handler) jsonListTags(ctx context.Context, body []byte) (any, error) {
var input listTagsInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, ErrInvalidArn
}
diff --git a/services/acmpca/handler_test.go b/services/acmpca/handler_test.go
index 0f909722a7..e45fe59cc0 100644
--- a/services/acmpca/handler_test.go
+++ b/services/acmpca/handler_test.go
@@ -5,7 +5,7 @@ import (
"context"
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/acmpca"
)
@@ -49,7 +50,7 @@ func doACMPCARequest(t *testing.T, h *acmpca.Handler, op string, body map[string
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -71,7 +72,7 @@ func parseACMPCAResponse(t *testing.T, rec *httptest.ResponseRecorder) map[strin
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
@@ -135,7 +136,7 @@ func TestACMPCAHandler_ExtractResource(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
diff --git a/services/acmpca/persistence.go b/services/acmpca/persistence.go
index 1932972c45..6e608385e7 100644
--- a/services/acmpca/persistence.go
+++ b/services/acmpca/persistence.go
@@ -4,7 +4,7 @@ import (
"context"
"crypto/ecdsa"
"crypto/x509"
- "encoding/json"
+ "encoding/json/jsontext"
"encoding/pem"
"errors"
"fmt"
@@ -108,7 +108,7 @@ func regionalDTOKeyFn[V any](d *regionalDTO[V]) string { return regionKey(d.Regi
// decoding a snapshot from an incompatible (older or newer) build of this
// backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Policies map[string]map[string]string `json:"policies"`
AccountID string `json:"accountID"`
Region string `json:"region"`
@@ -282,7 +282,7 @@ func unwrapRegionalDTOs[V any](dtos *store.Table[regionalDTO[V]], setRegion func
// restoreResourceTables rebuilds every store.Table on b from snap's
// per-table JSON, factored out of Restore to keep Restore's own cognitive
// complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
dtos := buildPersistenceDTORegistry()
if err := dtos.registry.RestoreAll(tables); err != nil {
diff --git a/services/amp/dataplane_status_http.go b/services/amp/dataplane_status_http.go
index 8fce8f7509..3749cff544 100644
--- a/services/amp/dataplane_status_http.go
+++ b/services/amp/dataplane_status_http.go
@@ -2,7 +2,7 @@ package amp
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
@@ -14,6 +14,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/amp/promql"
)
@@ -173,7 +174,7 @@ func (h *Handler) postAlerts(c *echo.Context, wsID string) error {
}
var in []postableAlert
- if jerr := json.Unmarshal(body, &in); jerr != nil || len(in) > maxPostedAlerts {
+ if jerr := json.Unmarshal(body, &in, jsonx.V1()); jerr != nil || len(in) > maxPostedAlerts {
return promError(c, http.StatusBadRequest, "bad_data", "body must be a JSON array of alerts")
}
diff --git a/services/amp/dataplane_test.go b/services/amp/dataplane_test.go
index 583ac4104c..b9d1e09350 100644
--- a/services/amp/dataplane_test.go
+++ b/services/amp/dataplane_test.go
@@ -1,7 +1,8 @@
package amp_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"math"
"net/http"
@@ -14,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/amp"
"github.com/blackbirdworks/gopherstack/services/amp/promql"
)
@@ -69,8 +71,8 @@ type promResult struct {
Status string `json:"status"`
ErrorType string `json:"errorType"`
Data struct {
- ResultType string `json:"resultType"`
- Result json.RawMessage `json:"result"`
+ ResultType string `json:"resultType"`
+ Result jsontext.Value `json:"result"`
} `json:"data"`
}
@@ -80,7 +82,7 @@ func (f *fixture) query(t *testing.T, ws, path string, vals url.Values) (httpRes
r := f.do(t, http.MethodGet, "/workspaces/"+ws+"/api/v1/"+path+"?"+vals.Encode(), nil, nil)
var pr promResult
- _ = json.Unmarshal(r.body, &pr)
+ _ = json.Unmarshal(r.body, &pr, jsonx.V1())
return r, pr
}
diff --git a/services/amp/persistence.go b/services/amp/persistence.go
index 4eb7e69389..b6084f54ad 100644
--- a/services/amp/persistence.go
+++ b/services/amp/persistence.go
@@ -2,7 +2,7 @@ package amp
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,10 +12,10 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state, samples included.
diff --git a/services/amp/rules_eval_test.go b/services/amp/rules_eval_test.go
index 677bdc0350..f87148d87a 100644
--- a/services/amp/rules_eval_test.go
+++ b/services/amp/rules_eval_test.go
@@ -2,7 +2,8 @@ package amp_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/url"
"strconv"
@@ -15,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/amp"
"github.com/blackbirdworks/gopherstack/services/amp/promql"
)
@@ -86,7 +88,7 @@ func (f *fixture) vector(t *testing.T, ws, q string) []vecSample {
require.Equal(t, http.StatusOK, r.status, string(r.body))
var out []vecSample
- require.NoError(t, json.Unmarshal(pr.Data.Result, &out))
+ require.NoError(t, json.Unmarshal(pr.Data.Result, &out, jsonx.V1()))
return out
}
@@ -98,11 +100,11 @@ func (f *fixture) apiData(t *testing.T, ws, path string, into any) {
require.Equal(t, http.StatusOK, r.status, string(r.body))
var env struct {
- Data json.RawMessage `json:"data"`
+ Data jsontext.Value `json:"data"`
}
- require.NoError(t, json.Unmarshal(r.body, &env))
- require.NoError(t, json.Unmarshal(env.Data, into))
+ require.NoError(t, json.Unmarshal(r.body, &env, jsonx.V1()))
+ require.NoError(t, json.Unmarshal(env.Data, into, jsonx.V1()))
}
func TestRecordingRule(t *testing.T) {
@@ -416,7 +418,7 @@ inhibit_rules:
func (f *fixture) postAlerts(t *testing.T, ws string, alerts []map[string]any) {
t.Helper()
- body, err := json.Marshal(alerts)
+ body, err := json.Marshal(alerts, jsonx.V1())
require.NoError(t, err)
path := "/workspaces/" + ws + "/alertmanager/api/v2/alerts"
diff --git a/services/amp/series_json.go b/services/amp/series_json.go
index 3e08c20655..e3bcc9f3c5 100644
--- a/services/amp/series_json.go
+++ b/services/amp/series_json.go
@@ -1,9 +1,10 @@
package amp
import (
- "encoding/json"
+ "encoding/json/v2"
"slices"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/amp/promql"
)
@@ -20,7 +21,7 @@ type seriesWithStale struct {
// MarshalJSON writes stale markers as timestamps because JSON cannot carry NaN; other series are unchanged.
func (r SeriesRecord) MarshalJSON() ([]byte, error) {
if !slices.ContainsFunc(r.Samples, func(s promql.Sample) bool { return promql.IsStale(s.V) }) {
- return json.Marshal(seriesPlain(r))
+ return json.Marshal(seriesPlain(r), jsonx.V1())
}
out := seriesWithStale{seriesPlain: seriesPlain(r)}
@@ -33,13 +34,13 @@ func (r SeriesRecord) MarshalJSON() ([]byte, error) {
}
}
- return json.Marshal(out)
+ return json.Marshal(out, jsonx.V1())
}
// UnmarshalJSON restores stale markers in time order.
func (r *SeriesRecord) UnmarshalJSON(data []byte) error {
var in seriesWithStale
- if err := json.Unmarshal(data, &in); err != nil {
+ if err := json.Unmarshal(data, &in, jsonx.V1()); err != nil {
return err
}
diff --git a/services/amp/validate.go b/services/amp/validate.go
index 46bac8264f..5880a02314 100644
--- a/services/amp/validate.go
+++ b/services/amp/validate.go
@@ -1,12 +1,13 @@
package amp
import (
- "encoding/json"
+ "encoding/json/v2"
"regexp"
"strings"
"gopkg.in/yaml.v3"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/restroute"
"github.com/blackbirdworks/gopherstack/services/amp/promql"
)
@@ -300,7 +301,7 @@ func validatePolicy(doc string) error {
Statement any `json:"Statement"`
}
- if err := json.Unmarshal([]byte(doc), &p); err != nil || p.Statement == nil {
+ if err := json.Unmarshal([]byte(doc), &p, jsonx.V1()); err != nil || p.Statement == nil {
return cannotParse("policyDocument must be a JSON policy with a Statement")
}
diff --git a/services/amplify/handler_apps.go b/services/amplify/handler_apps.go
index 5de9a74782..a3900c5ad2 100644
--- a/services/amplify/handler_apps.go
+++ b/services/amplify/handler_apps.go
@@ -2,12 +2,13 @@ package amplify
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
)
@@ -112,7 +113,7 @@ func (h *Handler) createApp(ctx context.Context, c *echo.Context) error {
var input createAppRequest
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return amplifyErrorJSON(c, http.StatusBadRequest, "invalid request body")
}
@@ -181,7 +182,7 @@ func (h *Handler) updateApp(ctx context.Context, c *echo.Context, appID string)
var input createAppRequest
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return amplifyErrorJSON(c, http.StatusBadRequest, "invalid request body")
}
diff --git a/services/amplify/handler_apps_test.go b/services/amplify/handler_apps_test.go
index 9dde2c51e8..2c869cda0d 100644
--- a/services/amplify/handler_apps_test.go
+++ b/services/amplify/handler_apps_test.go
@@ -1,13 +1,14 @@
package amplify_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/amplify"
)
@@ -50,7 +51,7 @@ func TestHandler_CreateApp(t *testing.T) {
if tt.wantName != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
app := resp["app"].(map[string]any)
assert.Equal(t, tt.wantName, app["name"])
}
@@ -143,7 +144,7 @@ func TestHandler_ListApps(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
apps := resp["apps"].([]any)
assert.Len(t, apps, tt.wantCount)
})
@@ -197,7 +198,7 @@ func TestHandler_ListAppsPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
apps := resp["apps"].([]any)
assert.Len(t, apps, tt.wantCount)
@@ -248,7 +249,7 @@ func TestHandler_DeleteApp(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
app, ok := resp["app"].(map[string]any)
require.True(t, ok, "expected an \"app\" key wrapping the deleted App, got body %s", rec.Body.String())
assert.Equal(t, appID, app["appId"])
@@ -313,7 +314,7 @@ func TestHandler_UpdateApp(t *testing.T) {
if tt.wantName != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
app := resp["app"].(map[string]any)
assert.Equal(t, tt.wantName, app["name"])
}
diff --git a/services/amplify/handler_artifacts.go b/services/amplify/handler_artifacts.go
index 71aa71e384..cf61aeb074 100644
--- a/services/amplify/handler_artifacts.go
+++ b/services/amplify/handler_artifacts.go
@@ -2,12 +2,13 @@ package amplify
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// generateAccessLogs handles POST /apps/{appId}/accesslogs.
@@ -27,7 +28,7 @@ func (h *Handler) generateAccessLogs(ctx context.Context, c *echo.Context, appID
EndTime string `json:"endTime"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return amplifyErrorJSON(c, http.StatusBadRequest, "invalid request body")
}
diff --git a/services/amplify/handler_artifacts_test.go b/services/amplify/handler_artifacts_test.go
index fddfdb0913..e4f165d0c3 100644
--- a/services/amplify/handler_artifacts_test.go
+++ b/services/amplify/handler_artifacts_test.go
@@ -1,12 +1,14 @@
package amplify_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_GenerateAccessLogs verifies POST /apps/{appId}/accesslogs returns a log URL.
@@ -32,7 +34,7 @@ func TestHandler_GenerateAccessLogs(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["logUrl"])
})
}
diff --git a/services/amplify/handler_branches.go b/services/amplify/handler_branches.go
index 1680b89d97..348403ed41 100644
--- a/services/amplify/handler_branches.go
+++ b/services/amplify/handler_branches.go
@@ -2,12 +2,13 @@ package amplify
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
)
@@ -110,7 +111,7 @@ func (h *Handler) createBranch(ctx context.Context, c *echo.Context, appID strin
var input createBranchRequest
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return amplifyErrorJSON(c, http.StatusBadRequest, "invalid request body")
}
@@ -184,7 +185,7 @@ func (h *Handler) updateBranch(ctx context.Context, c *echo.Context, appID, bran
var input createBranchRequest
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return amplifyErrorJSON(c, http.StatusBadRequest, "invalid request body")
}
diff --git a/services/amplify/handler_branches_test.go b/services/amplify/handler_branches_test.go
index bc7e33debf..4bf1388b54 100644
--- a/services/amplify/handler_branches_test.go
+++ b/services/amplify/handler_branches_test.go
@@ -1,13 +1,14 @@
package amplify_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/amplify"
)
@@ -223,7 +224,7 @@ func TestHandler_ListBranchesPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
branches := resp["branches"].([]any)
assert.Len(t, branches, tt.wantCount)
@@ -277,7 +278,7 @@ func TestHandler_DeleteBranch(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
branch, ok := resp["branch"].(map[string]any)
require.True(
t, ok, "expected a \"branch\" key wrapping the deleted Branch, got body %s", rec.Body.String(),
@@ -347,7 +348,7 @@ func TestHandler_UpdateBranch(t *testing.T) {
if tt.wantDesc != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
br := resp["branch"].(map[string]any)
assert.Equal(t, tt.wantDesc, br["description"])
}
diff --git a/services/amplify/handler_deployments.go b/services/amplify/handler_deployments.go
index 48671a8dd3..4b9bae020e 100644
--- a/services/amplify/handler_deployments.go
+++ b/services/amplify/handler_deployments.go
@@ -2,12 +2,13 @@ package amplify
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// createDeployment handles POST /apps/{appId}/branches/{branchName}/deployments.
@@ -26,7 +27,7 @@ func (h *Handler) createDeployment(ctx context.Context, c *echo.Context, appID,
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return amplifyErrorJSON(c, http.StatusBadRequest, "invalid request body")
}
}
@@ -60,7 +61,7 @@ func (h *Handler) startDeployment(ctx context.Context, c *echo.Context, appID, b
SourceURLType string `json:"sourceUrlType"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return amplifyErrorJSON(c, http.StatusBadRequest, "invalid request body")
}
diff --git a/services/amplify/handler_deployments_test.go b/services/amplify/handler_deployments_test.go
index 81263254bf..d06eb1872c 100644
--- a/services/amplify/handler_deployments_test.go
+++ b/services/amplify/handler_deployments_test.go
@@ -1,12 +1,14 @@
package amplify_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_DeploymentOps verifies CreateDeployment and StartDeployment via the HTTP handler.
@@ -34,7 +36,7 @@ func TestHandler_DeploymentOps(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
jobID := createResp["jobId"].(string)
assert.NotEmpty(t, jobID)
assert.NotNil(t, createResp["fileUploadUrls"])
@@ -45,7 +47,7 @@ func TestHandler_DeploymentOps(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
assert.NotNil(t, startResp["jobSummary"])
})
}
diff --git a/services/amplify/handler_domains.go b/services/amplify/handler_domains.go
index 9809103d60..984fdda9fb 100644
--- a/services/amplify/handler_domains.go
+++ b/services/amplify/handler_domains.go
@@ -2,12 +2,13 @@ package amplify
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// JSON response key used by the domain association handlers.
@@ -79,7 +80,7 @@ func (h *Handler) createDomainAssociation(ctx context.Context, c *echo.Context,
EnableAutoSubDomain bool `json:"enableAutoSubDomain"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return amplifyErrorJSON(c, http.StatusBadRequest, "invalid request body")
}
@@ -163,7 +164,7 @@ func (h *Handler) updateDomainAssociation(
AutoSubDomainCreationPatterns []string `json:"autoSubDomainCreationPatterns"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return amplifyErrorJSON(c, http.StatusBadRequest, "invalid request body")
}
diff --git a/services/amplify/handler_domains_test.go b/services/amplify/handler_domains_test.go
index b5954ad67f..33f90a1c85 100644
--- a/services/amplify/handler_domains_test.go
+++ b/services/amplify/handler_domains_test.go
@@ -1,12 +1,14 @@
package amplify_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_DomainAssociationCRUD verifies domain association lifecycle via the HTTP handler.
@@ -34,7 +36,7 @@ func TestHandler_DomainAssociationCRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
da := createResp["domainAssociation"].(map[string]any)
assert.Equal(t, "example.com", da["domainName"])
@@ -47,7 +49,7 @@ func TestHandler_DomainAssociationCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["domainAssociations"].([]any), 1)
// Update.
@@ -56,7 +58,7 @@ func TestHandler_DomainAssociationCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
updated := updateResp["domainAssociation"].(map[string]any)
assert.Equal(t, true, updated["enableAutoSubDomain"])
diff --git a/services/amplify/handler_environments.go b/services/amplify/handler_environments.go
index 02550e8546..3336085415 100644
--- a/services/amplify/handler_environments.go
+++ b/services/amplify/handler_environments.go
@@ -2,12 +2,13 @@ package amplify
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// JSON response key used by the backend environment handlers.
@@ -54,7 +55,7 @@ func (h *Handler) createBackendEnvironment(ctx context.Context, c *echo.Context,
DeploymentArtifacts string `json:"deploymentArtifacts"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return amplifyErrorJSON(c, http.StatusBadRequest, "invalid request body")
}
diff --git a/services/amplify/handler_environments_test.go b/services/amplify/handler_environments_test.go
index f48278b017..ddbc497f22 100644
--- a/services/amplify/handler_environments_test.go
+++ b/services/amplify/handler_environments_test.go
@@ -1,12 +1,14 @@
package amplify_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_BackendEnvironmentCRUD verifies backend environment lifecycle via the HTTP handler.
@@ -32,7 +34,7 @@ func TestHandler_BackendEnvironmentCRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
be := createResp["backendEnvironment"].(map[string]any)
assert.Equal(t, "prod", be["environmentName"])
@@ -45,7 +47,7 @@ func TestHandler_BackendEnvironmentCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["backendEnvironments"].([]any), 1)
// Delete.
diff --git a/services/amplify/handler_jobs.go b/services/amplify/handler_jobs.go
index 51f64b1ec1..21971ec33d 100644
--- a/services/amplify/handler_jobs.go
+++ b/services/amplify/handler_jobs.go
@@ -2,13 +2,14 @@ package amplify
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// JSON response key used by the job handlers (and by handler_deployments.go,
@@ -58,7 +59,7 @@ func (h *Handler) startJob(ctx context.Context, c *echo.Context, appID, branchNa
CommitTime float64 `json:"commitTime"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return amplifyErrorJSON(c, http.StatusBadRequest, "invalid request body")
}
diff --git a/services/amplify/handler_jobs_test.go b/services/amplify/handler_jobs_test.go
index 2052c0bf6a..73d8c7a247 100644
--- a/services/amplify/handler_jobs_test.go
+++ b/services/amplify/handler_jobs_test.go
@@ -1,7 +1,7 @@
package amplify_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/amplify"
)
@@ -38,7 +39,7 @@ func TestHandler_JobLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
summary := startResp["jobSummary"].(map[string]any)
jobID := summary["jobId"].(string)
assert.NotEmpty(t, jobID)
@@ -48,7 +49,7 @@ func TestHandler_JobLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["jobSummaries"].([]any), 1)
// Get job.
@@ -56,7 +57,7 @@ func TestHandler_JobLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
job := getResp["job"].(map[string]any)
assert.NotNil(t, job["summary"])
assert.NotNil(t, job["steps"])
@@ -109,7 +110,7 @@ func TestHandler_ListJobs_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["jobSummaries"].([]any), tt.wantCount)
if tt.wantNextToken {
@@ -149,7 +150,7 @@ func TestHandler_StartJob_ResponseShape(t *testing.T) {
} `json:"jobSummary"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload, jsonx.V1()))
assert.NotEmpty(t, payload.JobSummary.JobID)
assert.NotEmpty(t, payload.JobSummary.JobARN)
assert.Contains(t, payload.JobSummary.JobARN, "arn:aws:amplify:")
@@ -174,7 +175,7 @@ func TestHandler_GetJob_Steps(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["jobSummary"].(map[string]any)["jobId"].(string)
type step struct {
@@ -195,7 +196,7 @@ func TestHandler_GetJob_Steps(t *testing.T) {
Steps []step `json:"steps"`
} `json:"job"`
}
- require.NoError(t, json.Unmarshal(stepsRec.Body.Bytes(), &payload))
+ require.NoError(t, json.Unmarshal(stepsRec.Body.Bytes(), &payload, jsonx.V1()))
return payload.Job.Steps
}
diff --git a/services/amplify/handler_tags.go b/services/amplify/handler_tags.go
index c1a7c4fba5..466eb7f546 100644
--- a/services/amplify/handler_tags.go
+++ b/services/amplify/handler_tags.go
@@ -2,7 +2,7 @@ package amplify
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleTags dispatches GET/POST/DELETE /tags/{resourceArn}.
@@ -49,7 +50,7 @@ func (h *Handler) tagResource(ctx context.Context, c *echo.Context, resourceARN
Tags map[string]string `json:"tags"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return amplifyErrorJSON(c, http.StatusBadRequest, "invalid request body")
}
diff --git a/services/amplify/handler_test.go b/services/amplify/handler_test.go
index cc3b3510ca..ff5faa9539 100644
--- a/services/amplify/handler_test.go
+++ b/services/amplify/handler_test.go
@@ -2,7 +2,7 @@ package amplify_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/amplify"
)
@@ -34,7 +35,7 @@ func doRequest(
var buf *bytes.Buffer
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
buf = bytes.NewBuffer(b)
} else {
@@ -422,7 +423,7 @@ func TestHandler_ErrorResponseShape(t *testing.T) {
var payload map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload, jsonx.V1()))
assert.Equal(t, tt.wantType, payload["__type"])
assert.NotEmpty(t, payload["message"])
})
diff --git a/services/amplify/handler_webhooks.go b/services/amplify/handler_webhooks.go
index cce179fef6..3cbbf7557a 100644
--- a/services/amplify/handler_webhooks.go
+++ b/services/amplify/handler_webhooks.go
@@ -2,12 +2,13 @@ package amplify
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// JSON response key used by the webhook handlers.
@@ -51,7 +52,7 @@ func (h *Handler) createWebhook(ctx context.Context, c *echo.Context, appID stri
Description string `json:"description"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return amplifyErrorJSON(c, http.StatusBadRequest, "invalid request body")
}
@@ -106,7 +107,7 @@ func (h *Handler) updateWebhook(ctx context.Context, c *echo.Context, webhookID
BranchName string `json:"branchName"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return amplifyErrorJSON(c, http.StatusBadRequest, "invalid request body")
}
diff --git a/services/amplify/handler_webhooks_test.go b/services/amplify/handler_webhooks_test.go
index 738494a93a..33f0400f1c 100644
--- a/services/amplify/handler_webhooks_test.go
+++ b/services/amplify/handler_webhooks_test.go
@@ -1,12 +1,14 @@
package amplify_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_WebhookCRUD verifies create/get/list/update/delete webhook lifecycle.
@@ -34,7 +36,7 @@ func TestHandler_WebhookCRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
wh := createResp["webhook"].(map[string]any)
webhookID := wh["webhookId"].(string)
assert.NotEmpty(t, webhookID)
@@ -48,7 +50,7 @@ func TestHandler_WebhookCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["webhooks"].([]any), 1)
// Update.
@@ -57,7 +59,7 @@ func TestHandler_WebhookCRUD(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
updated := updateResp["webhook"].(map[string]any)
assert.Equal(t, "updated", updated["description"])
diff --git a/services/amplify/persistence.go b/services/amplify/persistence.go
index 9d717ba583..46b4e1cb0a 100644
--- a/services/amplify/persistence.go
+++ b/services/amplify/persistence.go
@@ -2,7 +2,7 @@ package amplify
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -34,10 +34,10 @@ const amplifySnapshotVersion = 1
// store.Table.Restore from the webhooks table's AppID field. So Tables is
// the entirety of the persisted state besides AccountID/Region.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountId"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountId"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON. It implements
diff --git a/services/amplify/sent_keys.go b/services/amplify/sent_keys.go
index 2a9f6c6eec..993e4d36b3 100644
--- a/services/amplify/sent_keys.go
+++ b/services/amplify/sent_keys.go
@@ -1,12 +1,18 @@
package amplify
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "encoding/json/jsontext"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
// sentKeys reports which top-level JSON members a request body carried, so an
// update can tell an explicit empty value or false from an omitted member.
func sentKeys(body []byte) map[string]bool {
- var raw map[string]json.RawMessage
- if err := json.Unmarshal(body, &raw); err != nil {
+ var raw map[string]jsontext.Value
+ if err := json.Unmarshal(body, &raw, jsonx.V1()); err != nil {
return nil
}
diff --git a/services/amplify/wire_shape_test.go b/services/amplify/wire_shape_test.go
index 8eb49b055d..506f0774a9 100644
--- a/services/amplify/wire_shape_test.go
+++ b/services/amplify/wire_shape_test.go
@@ -1,7 +1,7 @@
package amplify_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/amplify"
)
@@ -163,7 +164,7 @@ func TestGetBackendEnvironment_NoFabricatedAppID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
be, ok := resp["backendEnvironment"].(map[string]any)
require.True(t, ok)
@@ -205,7 +206,7 @@ func TestGetDomainAssociation_NoFabricatedAppID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
da, ok := resp["domainAssociation"].(map[string]any)
require.True(t, ok)
diff --git a/services/apigateway/api_keys_test.go b/services/apigateway/api_keys_test.go
index a6b6faa7b4..7b385bcd52 100644
--- a/services/apigateway/api_keys_test.go
+++ b/services/apigateway/api_keys_test.go
@@ -1,7 +1,8 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -23,7 +25,7 @@ func TestAPIGW_APIKeys(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300, "expected 2xx for CreateAPIKey")
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
keyID, _ := createResp["id"].(string)
require.NotEmpty(t, keyID)
@@ -91,7 +93,7 @@ func TestGetApiKey_ValueHiddenByDefault(t *testing.T) {
require.True(t, createRec.Code >= 200 && createRec.Code < 300)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createResp, jsonx.V1()))
keyID, _ := createResp["id"].(string)
require.NotEmpty(t, keyID)
@@ -99,7 +101,7 @@ func TestGetApiKey_ValueHiddenByDefault(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
if tt.wantValue {
assert.NotEmpty(t, resp["value"], "value must be present when includeValue=true")
@@ -154,7 +156,7 @@ func TestGetApiKeys_ValueHiddenByDefault(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
items, ok := resp["item"].([]any)
require.True(t, ok && len(items) > 0, "expected at least one key in response")
@@ -207,7 +209,7 @@ func TestPatchOps_UpdateApiKey(t *testing.T) {
require.True(t, createRec.Code >= 200 && createRec.Code < 300)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createResp, jsonx.V1()))
keyID, _ := createResp["id"].(string)
require.NotEmpty(t, keyID)
@@ -239,7 +241,7 @@ func TestApiKey_CustomerID(t *testing.T) {
require.True(t, createRec.Code >= 200 && createRec.Code < 300)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createResp, jsonx.V1()))
keyID, _ := createResp["id"].(string)
require.NotEmpty(t, keyID)
assert.Equal(t, "mp-cust-123", createResp["customerId"],
@@ -308,7 +310,7 @@ func TestUpdateApiKey(t *testing.T) {
createRec := postWithHandler(t, handler, e, "CreateApiKey", `{"name":"orig-key","enabled":true}`)
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
keyID := createResp["id"].(string)
lookupID := keyID
diff --git a/services/apigateway/authorizers_test.go b/services/apigateway/authorizers_test.go
index be539dd5b2..247eb73553 100644
--- a/services/apigateway/authorizers_test.go
+++ b/services/apigateway/authorizers_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -76,7 +77,7 @@ func TestTestInvokeAuthorizer(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "user-1", resp["principalId"])
}
})
diff --git a/services/apigateway/client_certificates_test.go b/services/apigateway/client_certificates_test.go
index a7bef08235..9be47848f0 100644
--- a/services/apigateway/client_certificates_test.go
+++ b/services/apigateway/client_certificates_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -26,7 +27,7 @@ func TestAPIGateway_UpdateClientCertificate_RESTRoute(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "updated desc", resp["description"])
}
@@ -81,7 +82,7 @@ func TestClientCertificates(t *testing.T) {
assert.Equal(t, tt.wantCreateCode, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
certID := createResp["clientCertificateId"].(string)
// Get
@@ -98,7 +99,7 @@ func TestClientCertificates(t *testing.T) {
listRec := postWithHandler(t, handler, e, "GetClientCertificates", `{}`)
assert.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
assert.GreaterOrEqual(t, len(listResp["item"].([]any)), tt.wantListMinLen)
}
diff --git a/services/apigateway/deployments_test.go b/services/apigateway/deployments_test.go
index 40c7e7f0ea..229139fe43 100644
--- a/services/apigateway/deployments_test.go
+++ b/services/apigateway/deployments_test.go
@@ -1,7 +1,8 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -29,7 +31,7 @@ func TestDeleteDeployment_StageProtection(t *testing.T) {
"create deployment: %d %s", rec.Code, rec.Body.String())
var depl map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &depl))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &depl, jsonx.V1()))
deplID, _ := depl["id"].(string)
require.NotEmpty(t, deplID)
@@ -58,7 +60,7 @@ func TestDeleteDeployment_Unreferenced(t *testing.T) {
"create deployment: %d %s", rec.Code, rec.Body.String())
var depl map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &depl))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &depl, jsonx.V1()))
deplID, _ := depl["id"].(string)
require.NotEmpty(t, deplID)
@@ -104,7 +106,7 @@ func TestCreateDeployment_StageDescription(t *testing.T) {
require.True(t, createAPIRec.Code >= 200 && createAPIRec.Code < 300)
var apiResp map[string]any
- require.NoError(t, json.NewDecoder(createAPIRec.Body).Decode(&apiResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createAPIRec.Body), &apiResp, jsonx.V1()))
apiID, _ := apiResp["id"].(string)
require.NotEmpty(t, apiID)
@@ -117,7 +119,7 @@ func TestCreateDeployment_StageDescription(t *testing.T) {
require.True(t, stageRec.Code >= 200 && stageRec.Code < 300)
var stage map[string]any
- require.NoError(t, json.NewDecoder(stageRec.Body).Decode(&stage))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(stageRec.Body), &stage, jsonx.V1()))
// JSON omitempty means empty description is absent (nil), not "".
got, _ := stage["description"].(string)
assert.Equal(t, tt.wantDescription, got)
@@ -163,13 +165,13 @@ func TestCreateDeployment_StageVariables(t *testing.T) {
require.True(t, createAPIRec.Code >= 200 && createAPIRec.Code < 300)
var apiResp map[string]any
- require.NoError(t, json.NewDecoder(createAPIRec.Body).Decode(&apiResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createAPIRec.Body), &apiResp, jsonx.V1()))
apiID, _ := apiResp["id"].(string)
require.NotEmpty(t, apiID)
var deplBody string
if tt.variables != nil {
- varsJSON, _ := json.Marshal(tt.variables)
+ varsJSON, _ := json.Marshal(tt.variables, jsonx.V1())
deplBody = `{"stageName":"prod","variables":` + string(varsJSON) + `}`
} else {
deplBody = `{"stageName":"prod"}`
@@ -181,7 +183,7 @@ func TestCreateDeployment_StageVariables(t *testing.T) {
require.True(t, stageRec.Code >= 200 && stageRec.Code < 300)
var stage map[string]any
- require.NoError(t, json.NewDecoder(stageRec.Body).Decode(&stage))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(stageRec.Body), &stage, jsonx.V1()))
vars, _ := stage["variables"].(map[string]any)
assert.Len(t, vars, tt.wantVarsLen)
@@ -235,7 +237,7 @@ func TestUpdateDeployment(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.description, resp["description"])
}
})
@@ -272,13 +274,13 @@ func TestHandler_GetAndDeleteDeployment(t *testing.T) {
createRec := postWithHandler(t, h, e, "CreateRestApi", `{"name":"api"}`)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
apiID := created["id"].(string)
deplRec := postWithHandler(t, h, e, "CreateDeployment",
fmt.Sprintf(`{"restApiId":%q,"stageName":"prod","description":""}`, apiID))
var depl map[string]any
- require.NoError(t, json.Unmarshal(deplRec.Body.Bytes(), &depl))
+ require.NoError(t, json.Unmarshal(deplRec.Body.Bytes(), &depl, jsonx.V1()))
deplID := depl["id"].(string)
if tt.action == "DeleteDeployment" {
diff --git a/services/apigateway/documentation_test.go b/services/apigateway/documentation_test.go
index 0eb635f8e1..defacc04a5 100644
--- a/services/apigateway/documentation_test.go
+++ b/services/apigateway/documentation_test.go
@@ -1,13 +1,15 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDocumentationPart_CRUD tests GetDocumentationPart, GetDocumentationParts, DeleteDocumentationPart.
@@ -61,7 +63,7 @@ func TestDocumentationPart_CRUD(t *testing.T) {
fmt.Sprintf(`{"restApiId":%q}`, apiID))
assert.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["item"].([]any), tt.listWantLen)
}
@@ -130,7 +132,7 @@ func TestDocumentationVersion_CRUD(t *testing.T) {
fmt.Sprintf(`{"restApiId":%q}`, apiID))
assert.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["item"].([]any), tt.listWantLen)
}
diff --git a/services/apigateway/domain_names_test.go b/services/apigateway/domain_names_test.go
index b67aeb170f..5446fb5cd6 100644
--- a/services/apigateway/domain_names_test.go
+++ b/services/apigateway/domain_names_test.go
@@ -1,7 +1,8 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -26,7 +28,7 @@ func TestAPIGW_DomainNames(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300, "expected 2xx")
var apiResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&apiResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &apiResp, jsonx.V1()))
apiID, _ := apiResp["id"].(string)
require.NotEmpty(t, apiID)
@@ -77,7 +79,7 @@ func TestAPIGateway_DomainNameAccessAssociation_RESTLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
arn, _ := created["domainNameAccessAssociationArn"].(string)
require.NotEmpty(t, arn, "create must return a real association ARN")
@@ -85,7 +87,7 @@ func TestAPIGateway_DomainNameAccessAssociation_RESTLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var list map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &list))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &list, jsonx.V1()))
items, _ := list["item"].([]any)
require.Len(t, items, 1, "list must reflect the created association, not a hardcoded empty list")
@@ -94,7 +96,7 @@ func TestAPIGateway_DomainNameAccessAssociation_RESTLifecycle(t *testing.T) {
listAfterDeleteRec := restCall(t, h, http.MethodGet, "/domainnameaccessassociations", "", "")
var listAfter map[string]any
- require.NoError(t, json.Unmarshal(listAfterDeleteRec.Body.Bytes(), &listAfter))
+ require.NoError(t, json.Unmarshal(listAfterDeleteRec.Body.Bytes(), &listAfter, jsonx.V1()))
itemsAfter, _ := listAfter["item"].([]any)
assert.Empty(t, itemsAfter)
@@ -234,7 +236,7 @@ func TestHandlerDomainName_SecurityPolicy_RoundTrip(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, "TLS_1_2", resp["securityPolicy"])
assert.NotEmpty(t, resp["regionalDomainName"])
assert.NotEmpty(t, resp["distributionDomainName"])
diff --git a/services/apigateway/error_envelope_test.go b/services/apigateway/error_envelope_test.go
index 6c3ef72fe2..7343a120e1 100644
--- a/services/apigateway/error_envelope_test.go
+++ b/services/apigateway/error_envelope_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/apigateway"
@@ -86,7 +87,7 @@ func TestErrorEnvelope_GetRestApiNotFoundDecodesToTypedError(t *testing.T) {
require.NoError(t, err)
var envelope map[string]any
- require.NoError(t, json.Unmarshal(raw, &envelope))
+ require.NoError(t, json.Unmarshal(raw, &envelope, jsonx.V1()))
require.Equal(t, "NotFoundException", envelope["__type"],
"raw body must carry __type key restjson.GetErrorInfo's fallback reads: %s", raw)
diff --git a/services/apigateway/export_test.go b/services/apigateway/export_test.go
index 0df8b60323..ef349f3e89 100644
--- a/services/apigateway/export_test.go
+++ b/services/apigateway/export_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -28,7 +29,7 @@ func TestAPIGateway_GetExport_RESTRoute(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var doc map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &doc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &doc, jsonx.V1()))
assert.Equal(t, "3.0.1", doc["openapi"])
}
@@ -74,7 +75,7 @@ func TestAPIGateway_GetExport_HeadersNotBody_RealClient(t *testing.T) {
require.NotEmpty(t, out.Body, "Body must round-trip as raw bytes, not a base64 JSON field")
var doc map[string]any
- require.NoError(t, json.Unmarshal(out.Body, &doc))
+ require.NoError(t, json.Unmarshal(out.Body, &doc, jsonx.V1()))
assert.Equal(t, "3.0.1", doc["openapi"])
}
diff --git a/services/apigateway/gateway_responses_test.go b/services/apigateway/gateway_responses_test.go
index 1a27a49987..1dbe7d18b2 100644
--- a/services/apigateway/gateway_responses_test.go
+++ b/services/apigateway/gateway_responses_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -33,7 +34,7 @@ func TestAPIGateway_UpdateGatewayResponse_RESTRoute(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "404", resp["statusCode"])
}
@@ -111,7 +112,7 @@ func TestGatewayResponses(t *testing.T) {
if tt.wantDefaultResp {
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, true, resp["defaultResponse"])
}
}
@@ -132,7 +133,7 @@ func TestGatewayResponses(t *testing.T) {
fmt.Sprintf(`{"restApiId":%q,"responseType":%q}`, apiID, tt.responseType))
assert.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
// After delete, should revert to default
assert.Equal(t, true, resp["defaultResponse"])
}
@@ -166,7 +167,7 @@ func TestDeleteRestAPI_ClearsGatewayResponses(t *testing.T) {
} `json:"gatewayResponses"`
} `json:"tables"`
}
- require.NoError(t, json.Unmarshal(b.Snapshot(t.Context()), &decoded))
+ require.NoError(t, json.Unmarshal(b.Snapshot(t.Context()), &decoded, jsonx.V1()))
for _, gr := range decoded.Tables.GatewayResponses {
assert.NotEqual(t, api.ID, gr.RestAPIID,
"a deleted REST API's gateway responses must not survive in the persisted snapshot")
diff --git a/services/apigateway/handler.go b/services/apigateway/handler.go
index 9fee815216..5ca824ccc5 100644
--- a/services/apigateway/handler.go
+++ b/services/apigateway/handler.go
@@ -3,7 +3,8 @@ package apigateway
import (
"context"
"crypto/rsa"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -23,6 +24,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/executeapi"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -446,7 +448,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var data map[string]any
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
@@ -515,7 +517,7 @@ func (h *Handler) Handler() echo.HandlerFunc {
func writeJSONProtocolDispatchError(c *echo.Context, status int, errType, message string) error {
c.Response().Header().Set(headerContentType, "application/x-amz-json-1.1")
- payload, err := json.Marshal(ErrorResponse{Type: errType, Message: message})
+ payload, err := json.Marshal(ErrorResponse{Type: errType, Message: message}, jsonx.V1())
if err != nil {
return err
}
@@ -715,7 +717,7 @@ func detectImportRESTAPI(
Body: body,
FailOnWarnings: query.Get("failonwarnings") == litTrue,
}
- encoded, err := json.Marshal(in)
+ encoded, err := json.Marshal(in, jsonx.V1())
if err != nil {
return "", nil, false
}
@@ -728,7 +730,7 @@ func detectImportRESTAPI(
Body: body,
Format: query.Get("format"),
}
- encoded, err := json.Marshal(in)
+ encoded, err := json.Marshal(in, jsonx.V1())
if err != nil {
return "", nil, false
}
@@ -741,7 +743,7 @@ func detectImportRESTAPI(
FailOnWarnings: query.Get("failonwarnings") == litTrue,
Body: body,
}
- encoded, err := json.Marshal(in)
+ encoded, err := json.Marshal(in, jsonx.V1())
if err != nil {
return "", nil, false
}
@@ -759,28 +761,28 @@ func detectImportRESTAPI(
// must be injected as a bare JSON number, not a quoted string, or a real
// client's Limit always 500s on json.Unmarshal.
func injectJSONFieldAPIGW(body []byte, key, value string) []byte {
- var m map[string]json.RawMessage
+ var m map[string]jsontext.Value
if len(body) > 0 {
- if err := json.Unmarshal(body, &m); err != nil {
- m = make(map[string]json.RawMessage)
+ if err := json.Unmarshal(body, &m, jsonx.V1()); err != nil {
+ m = make(map[string]jsontext.Value)
}
} else {
- m = make(map[string]json.RawMessage)
+ m = make(map[string]jsontext.Value)
}
if key == keyLimit {
if n, err := strconv.Atoi(value); err == nil {
- m[key] = json.RawMessage(strconv.Itoa(n))
- result, _ := json.Marshal(m)
+ m[key] = jsontext.Value(strconv.Itoa(n))
+ result, _ := json.Marshal(m, jsonx.V1())
return result
}
}
- quoted, _ := json.Marshal(value)
- m[key] = json.RawMessage(quoted)
+ quoted, _ := json.Marshal(value, jsonx.V1())
+ m[key] = jsontext.Value(quoted)
- result, _ := json.Marshal(m)
+ result, _ := json.Marshal(m, jsonx.V1())
return result
}
@@ -790,19 +792,19 @@ func injectJSONFieldAPIGW(body []byte, key, value string) []byte {
// (encoder.AddQuery in a loop) rather than a single scalar -- e.g. tagKeys
// (UntagResource, serializers.go:9284).
func injectJSONArrayFieldAPIGW(body []byte, key string, values []string) []byte {
- var m map[string]json.RawMessage
+ var m map[string]jsontext.Value
if len(body) > 0 {
- if err := json.Unmarshal(body, &m); err != nil {
- m = make(map[string]json.RawMessage)
+ if err := json.Unmarshal(body, &m, jsonx.V1()); err != nil {
+ m = make(map[string]jsontext.Value)
}
} else {
- m = make(map[string]json.RawMessage)
+ m = make(map[string]jsontext.Value)
}
- encoded, _ := json.Marshal(values)
+ encoded, _ := json.Marshal(values, jsonx.V1())
m[key] = encoded
- result, _ := json.Marshal(m)
+ result, _ := json.Marshal(m, jsonx.V1())
return result
}
@@ -984,7 +986,7 @@ func (h *Handler) dispatch(
return statusCode, nil, raw, nil
}
- encoded, err := json.Marshal(response)
+ encoded, err := json.Marshal(response, jsonx.V1())
if err != nil {
return 0, nil, nil, err
}
@@ -1047,7 +1049,7 @@ func (h *Handler) handleError(ctx context.Context, c *echo.Context, action strin
Message: strings.TrimPrefix(reqErr.Error(), errType+": "),
}
- payload, _ := json.Marshal(errResp)
+ payload, _ := json.Marshal(errResp, jsonx.V1())
return c.JSONBlob(statusCode, payload)
}
diff --git a/services/apigateway/handler_account.go b/services/apigateway/handler_account.go
index e637447b6f..c43d1867fb 100644
--- a/services/apigateway/handler_account.go
+++ b/services/apigateway/handler_account.go
@@ -1,8 +1,10 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// parseAPIGWAccountPath handles /account paths.
@@ -32,7 +34,7 @@ func (h *Handler) accountActions() map[string]actionFn {
},
opUpdateAccount: func(b []byte) (int, any, error) {
var input UpdateAccountInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.UpdateAccount(input)
diff --git a/services/apigateway/handler_api_keys.go b/services/apigateway/handler_api_keys.go
index eddb564e28..0eac95800d 100644
--- a/services/apigateway/handler_api_keys.go
+++ b/services/apigateway/handler_api_keys.go
@@ -1,10 +1,12 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type getAPIKeyInput struct {
@@ -56,7 +58,7 @@ func parseAPIGWAPIKeysPath(method string, segs []string, n int, query url.Values
func (h *Handler) getAPIKeyAction(b []byte) (int, any, error) {
var input getAPIKeyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
key, err := h.Backend.GetAPIKey(input.APIKeyID)
@@ -72,7 +74,7 @@ func (h *Handler) getAPIKeyAction(b []byte) (int, any, error) {
func (h *Handler) getAPIKeysAction(b []byte) (int, any, error) {
var input getAPIKeysPageInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
keys, position, err := h.fetchAPIKeys(input)
@@ -135,7 +137,7 @@ func filterAPIKeys(keys []APIKey, customerID, nameQuery string) []APIKey {
func (h *Handler) deleteAPIKeyAction(b []byte) (int, any, error) {
var input deleteAPIKeyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteAPIKey(input.APIKeyID); err != nil {
@@ -150,7 +152,7 @@ func (h *Handler) apiKeyActions() map[string]actionFn {
return map[string]actionFn{
opCreateAPIKey: func(b []byte) (int, any, error) {
var input CreateAPIKeyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -166,7 +168,7 @@ func (h *Handler) apiKeyActions() map[string]actionFn {
opDeleteAPIKey: h.deleteAPIKeyAction,
opUpdateAPIKey: func(b []byte) (int, any, error) {
var input updateAPIKeyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
key, err := h.Backend.UpdateAPIKey(input.APIKeyID, input.UpdateAPIKeyInput)
diff --git a/services/apigateway/handler_authorizers.go b/services/apigateway/handler_authorizers.go
index fcafe0c71b..3ed1a9ad02 100644
--- a/services/apigateway/handler_authorizers.go
+++ b/services/apigateway/handler_authorizers.go
@@ -1,8 +1,10 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createAuthorizerInput struct {
@@ -67,7 +69,7 @@ func (h *Handler) authorizerActions() map[string]actionFn {
func (h *Handler) createAuthorizerAction(b []byte) (int, any, error) {
var input createAuthorizerInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
ttl := defaultAuthorizerTTLSeconds
@@ -94,7 +96,7 @@ func (h *Handler) createAuthorizerAction(b []byte) (int, any, error) {
func (h *Handler) getAuthorizerAction(b []byte) (int, any, error) {
var input getAuthorizerInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
auth, err := h.Backend.GetAuthorizer(input.RestAPIID, input.AuthorizerID)
@@ -105,9 +107,10 @@ func (h *Handler) getAuthorizerAction(b []byte) (int, any, error) {
return http.StatusOK, auth, nil
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) getAuthorizersAction(b []byte) (int, any, error) {
var input getAuthorizersInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
auths, err := h.Backend.GetAuthorizers(input.RestAPIID)
@@ -127,7 +130,7 @@ func (h *Handler) getAuthorizersAction(b []byte) (int, any, error) {
func (h *Handler) updateAuthorizerAction(b []byte) (int, any, error) {
var input updateAuthorizerInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
auth, err := h.Backend.UpdateAuthorizer(input.RestAPIID, input.AuthorizerID, UpdateAuthorizerInput{
@@ -150,7 +153,7 @@ func (h *Handler) updateAuthorizerAction(b []byte) (int, any, error) {
func (h *Handler) deleteAuthorizerAction(b []byte) (int, any, error) {
var input deleteAuthorizerInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteAuthorizer(input.RestAPIID, input.AuthorizerID); err != nil {
@@ -162,7 +165,7 @@ func (h *Handler) deleteAuthorizerAction(b []byte) (int, any, error) {
func (h *Handler) testInvokeAuthorizerAction(b []byte) (int, any, error) {
var input TestInvokeAuthorizerInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
auth, err := h.Backend.GetAuthorizer(input.RestAPIID, input.AuthorizerID)
diff --git a/services/apigateway/handler_base_path_mappings.go b/services/apigateway/handler_base_path_mappings.go
index 7fd37a2132..ca4997c1a9 100644
--- a/services/apigateway/handler_base_path_mappings.go
+++ b/services/apigateway/handler_base_path_mappings.go
@@ -1,8 +1,10 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type getBasePathMappingInput struct {
@@ -50,7 +52,7 @@ func (h *Handler) basePathMappingActions() map[string]actionFn {
func (h *Handler) createBasePathMappingAction(b []byte) (int, any, error) {
var input CreateBasePathMappingInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -64,7 +66,7 @@ func (h *Handler) createBasePathMappingAction(b []byte) (int, any, error) {
func (h *Handler) getBasePathMappingAction(b []byte) (int, any, error) {
var input getBasePathMappingInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
bpm, err := h.Backend.GetBasePathMapping(input.DomainName, input.BasePath)
@@ -75,9 +77,10 @@ func (h *Handler) getBasePathMappingAction(b []byte) (int, any, error) {
return http.StatusOK, bpm, nil
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) getBasePathMappingsAction(b []byte) (int, any, error) {
var input getBasePathMappingsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
bpms, err := h.Backend.GetBasePathMappings(input.DomainName)
@@ -98,7 +101,7 @@ func (h *Handler) getBasePathMappingsAction(b []byte) (int, any, error) {
func (h *Handler) deleteBasePathMappingAction(b []byte) (int, any, error) {
var input deleteBasePathMappingInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteBasePathMapping(input.DomainName, input.BasePath); err != nil {
@@ -110,7 +113,7 @@ func (h *Handler) deleteBasePathMappingAction(b []byte) (int, any, error) {
func (h *Handler) updateBasePathMappingAction(b []byte) (int, any, error) {
var input UpdateBasePathMappingInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.UpdateBasePathMapping(input)
diff --git a/services/apigateway/handler_client_certificates.go b/services/apigateway/handler_client_certificates.go
index 13ad70dea6..6d9081e9ec 100644
--- a/services/apigateway/handler_client_certificates.go
+++ b/services/apigateway/handler_client_certificates.go
@@ -1,8 +1,10 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const opUpdateClientCertificate = "UpdateClientCertificate"
@@ -48,7 +50,7 @@ func (h *Handler) clientCertificateActions() map[string]actionFn {
func (h *Handler) generateClientCertificateAction(b []byte) (int, any, error) {
var input GenerateClientCertificateInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.GenerateClientCertificate(input)
@@ -63,7 +65,7 @@ func (h *Handler) getClientCertificateAction(b []byte) (int, any, error) {
var params struct {
ClientCertificateID string `json:"clientCertificateId"`
}
- if err := json.Unmarshal(b, ¶ms); err != nil {
+ if err := json.Unmarshal(b, ¶ms, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.GetClientCertificate(params.ClientCertificateID)
@@ -76,7 +78,7 @@ func (h *Handler) getClientCertificateAction(b []byte) (int, any, error) {
func (h *Handler) getClientCertificatesAction(b []byte) (int, any, error) {
var input getClientCertificatesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.GetClientCertificates()
@@ -99,7 +101,7 @@ func (h *Handler) deleteClientCertificateAction(b []byte) (int, any, error) {
var params struct {
ClientCertificateID string `json:"clientCertificateId"`
}
- if err := json.Unmarshal(b, ¶ms); err != nil {
+ if err := json.Unmarshal(b, ¶ms, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteClientCertificate(params.ClientCertificateID); err != nil {
@@ -111,7 +113,7 @@ func (h *Handler) deleteClientCertificateAction(b []byte) (int, any, error) {
func (h *Handler) updateClientCertificateAction(b []byte) (int, any, error) {
var input UpdateClientCertificateInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
diff --git a/services/apigateway/handler_deployments.go b/services/apigateway/handler_deployments.go
index aee02b0ec8..b124bf768f 100644
--- a/services/apigateway/handler_deployments.go
+++ b/services/apigateway/handler_deployments.go
@@ -1,9 +1,11 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createDeploymentInput struct {
@@ -53,7 +55,7 @@ func (h *Handler) deploymentActions() map[string]actionFn {
// createDeploymentAction handles CreateDeployment incl. inline stage update.
func (h *Handler) createDeploymentAction(b []byte) (int, any, error) {
var input createDeploymentInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
depl, err := h.Backend.CreateDeploymentWithCanary(
@@ -104,7 +106,7 @@ func (h *Handler) deploymentCRUDActions() map[string]actionFn {
func (h *Handler) getDeploymentAction(b []byte) (int, any, error) {
var input getDeploymentInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
depl, err := h.Backend.GetDeployment(input.RestAPIID, input.DeploymentID)
@@ -121,7 +123,7 @@ func (h *Handler) getDeploymentAction(b []byte) (int, any, error) {
func (h *Handler) getDeploymentsAction(b []byte) (int, any, error) {
var input getDeploymentsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
depls, err := h.Backend.GetDeployments(input.RestAPIID)
@@ -146,7 +148,7 @@ func (h *Handler) getDeploymentsAction(b []byte) (int, any, error) {
func (h *Handler) deleteDeploymentAction(b []byte) (int, any, error) {
var input deleteDeploymentInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteDeployment(input.RestAPIID, input.DeploymentID); err != nil {
@@ -163,7 +165,7 @@ func (h *Handler) deleteDeploymentAction(b []byte) (int, any, error) {
func (h *Handler) updateDeploymentAction(b []byte) (int, any, error) {
var input updateDeploymentHandlerInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
depl, err := h.Backend.UpdateDeployment(input.RestAPIID, input.DeploymentID, input.UpdateDeploymentInput)
diff --git a/services/apigateway/handler_documentation.go b/services/apigateway/handler_documentation.go
index dbadca69bf..61dec97d11 100644
--- a/services/apigateway/handler_documentation.go
+++ b/services/apigateway/handler_documentation.go
@@ -1,9 +1,11 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type getDocumentationPartInput struct {
@@ -120,7 +122,7 @@ func (h *Handler) documentationActions() map[string]actionFn {
func (h *Handler) createDocumentationPartAction(b []byte) (int, any, error) {
var input CreateDocumentationPartInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -134,7 +136,7 @@ func (h *Handler) createDocumentationPartAction(b []byte) (int, any, error) {
func (h *Handler) createDocumentationVersionAction(b []byte) (int, any, error) {
var input CreateDocumentationVersionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -148,7 +150,7 @@ func (h *Handler) createDocumentationVersionAction(b []byte) (int, any, error) {
func (h *Handler) getDocumentationPartAction(b []byte) (int, any, error) {
var input getDocumentationPartInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
p, err := h.Backend.GetDocumentationPart(input.RestAPIID, input.DocPartID)
@@ -161,7 +163,7 @@ func (h *Handler) getDocumentationPartAction(b []byte) (int, any, error) {
func (h *Handler) getDocumentationPartsAction(b []byte) (int, any, error) {
var input getDocumentationPartsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
ps, err := h.Backend.GetDocumentationParts(input.RestAPIID)
@@ -231,7 +233,7 @@ func filterDocumentationPartsByStatus(parts []DocumentationPart, status string)
func (h *Handler) updateDocumentationPartAction(b []byte) (int, any, error) {
var input UpdateDocumentationPartInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.UpdateDocumentationPart(input)
@@ -244,7 +246,7 @@ func (h *Handler) updateDocumentationPartAction(b []byte) (int, any, error) {
func (h *Handler) updateDocumentationVersionAction(b []byte) (int, any, error) {
var input UpdateDocumentationVersionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.UpdateDocumentationVersion(input)
@@ -257,7 +259,7 @@ func (h *Handler) updateDocumentationVersionAction(b []byte) (int, any, error) {
func (h *Handler) deleteDocumentationPartAction(b []byte) (int, any, error) {
var input deleteDocumentationPartInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteDocumentationPart(input.RestAPIID, input.DocPartID); err != nil {
@@ -269,7 +271,7 @@ func (h *Handler) deleteDocumentationPartAction(b []byte) (int, any, error) {
func (h *Handler) getDocumentationVersionAction(b []byte) (int, any, error) {
var input getDocumentationVersionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
v, err := h.Backend.GetDocumentationVersion(input.RestAPIID, input.DocVersion)
@@ -280,9 +282,10 @@ func (h *Handler) getDocumentationVersionAction(b []byte) (int, any, error) {
return http.StatusOK, v, nil
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) getDocumentationVersionsAction(b []byte) (int, any, error) {
var input getDocumentationVersionsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
vs, err := h.Backend.GetDocumentationVersions(input.RestAPIID)
@@ -303,7 +306,7 @@ func (h *Handler) getDocumentationVersionsAction(b []byte) (int, any, error) {
func (h *Handler) deleteDocumentationVersionAction(b []byte) (int, any, error) {
var input deleteDocumentationVersionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteDocumentationVersion(input.RestAPIID, input.DocVersion); err != nil {
diff --git a/services/apigateway/handler_domain_names.go b/services/apigateway/handler_domain_names.go
index 661762e685..b1b04aa243 100644
--- a/services/apigateway/handler_domain_names.go
+++ b/services/apigateway/handler_domain_names.go
@@ -1,9 +1,11 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -107,7 +109,7 @@ func (h *Handler) domainNameActions() map[string]actionFn {
func (h *Handler) createDomainNameAction(b []byte) (int, any, error) {
var input CreateDomainNameInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -121,7 +123,7 @@ func (h *Handler) createDomainNameAction(b []byte) (int, any, error) {
func (h *Handler) getDomainNameAction(b []byte) (int, any, error) {
var input getDomainNameInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
dn, err := h.Backend.GetDomainName(input.DomainName)
@@ -134,7 +136,7 @@ func (h *Handler) getDomainNameAction(b []byte) (int, any, error) {
func (h *Handler) getDomainNamesAction(b []byte) (int, any, error) {
var input getDomainNamesPageInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if input.ResourceOwner == resourceOwnerOther {
@@ -158,7 +160,7 @@ func (h *Handler) getDomainNamesAction(b []byte) (int, any, error) {
func (h *Handler) deleteDomainNameAction(b []byte) (int, any, error) {
var input deleteDomainNameInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteDomainName(input.DomainName); err != nil {
@@ -170,7 +172,7 @@ func (h *Handler) deleteDomainNameAction(b []byte) (int, any, error) {
func (h *Handler) updateDomainNameAction(b []byte) (int, any, error) {
var input UpdateDomainNameInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.UpdateDomainName(input)
@@ -187,7 +189,7 @@ func (h *Handler) domainNameAccessAssociationActions() map[string]actionFn {
return map[string]actionFn{
opCreateDomainNameAccessAssociation: func(b []byte) (int, any, error) {
var input CreateDomainNameAccessAssociationInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -204,7 +206,7 @@ func (h *Handler) domainNameAccessAssociationActions() map[string]actionFn {
Position string `json:"position,omitempty"`
Limit int `json:"limit,omitempty"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -224,7 +226,7 @@ func (h *Handler) domainNameAccessAssociationActions() map[string]actionFn {
var input struct {
Arn string `json:"domainNameAccessAssociationArn"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -239,7 +241,7 @@ func (h *Handler) domainNameAccessAssociationActions() map[string]actionFn {
Arn string `json:"domainNameAccessAssociationArn"`
DomainNameArn string `json:"domainNameArn"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -265,7 +267,7 @@ func (h *Handler) withDomainNameID(fn actionFn) actionFn {
DomainName string `json:"domainName"`
DomainNameID string `json:"domainNameId"`
}
- if err := json.Unmarshal(b, &ref); err != nil {
+ if err := json.Unmarshal(b, &ref, jsonx.V1()); err != nil {
return 0, nil, err
}
diff --git a/services/apigateway/handler_export.go b/services/apigateway/handler_export.go
index a381cd3600..daf1382945 100644
--- a/services/apigateway/handler_export.go
+++ b/services/apigateway/handler_export.go
@@ -1,12 +1,14 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
"gopkg.in/yaml.v3"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const opGetExport = "GetExport"
@@ -39,7 +41,7 @@ func (h *Handler) exportActions() map[string]actionFn {
return map[string]actionFn{
opGetExport: func(b []byte) (int, any, error) {
var input getExportInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -82,5 +84,5 @@ func encodeExport(export map[string]any, contentType string) ([]byte, error) {
return yaml.Marshal(export)
}
- return json.Marshal(export)
+ return json.Marshal(export, jsonx.V1())
}
diff --git a/services/apigateway/handler_fixtures_test.go b/services/apigateway/handler_fixtures_test.go
index b45dd31e63..c4f29b5c3f 100644
--- a/services/apigateway/handler_fixtures_test.go
+++ b/services/apigateway/handler_fixtures_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -27,7 +28,7 @@ func boostAPI(t *testing.T, handler *apigateway.Handler, e *echo.Echo) string {
rec := postWithHandler(t, handler, e, "CreateRestApi", `{"name":"boost-api"}`)
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["id"].(string)
}
@@ -38,7 +39,7 @@ func boostRootResource(t *testing.T, handler *apigateway.Handler, e *echo.Echo,
rec := postWithHandler(t, handler, e, "GetResources", fmt.Sprintf(`{"restApiId":%q}`, apiID))
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["item"].([]any)
require.NotEmpty(t, items)
@@ -52,7 +53,7 @@ func boostDeployment(t *testing.T, handler *apigateway.Handler, e *echo.Echo, ap
fmt.Sprintf(`{"restApiId":%q,"stageName":%q}`, apiID, stageName))
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["id"].(string)
}
@@ -72,7 +73,7 @@ func boostAuthorizer(t *testing.T, handler *apigateway.Handler, e *echo.Echo, ap
)
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["id"].(string)
}
@@ -84,7 +85,7 @@ func boostDocPart(t *testing.T, handler *apigateway.Handler, e *echo.Echo, apiID
fmt.Sprintf(`{"restApiId":%q,"location":{"type":"API"},"properties":"{}"}`, apiID))
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["id"].(string)
}
diff --git a/services/apigateway/handler_gateway_responses.go b/services/apigateway/handler_gateway_responses.go
index c82b2bc0cb..738b0ea781 100644
--- a/services/apigateway/handler_gateway_responses.go
+++ b/services/apigateway/handler_gateway_responses.go
@@ -1,9 +1,11 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"sort"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const opUpdateGatewayResponse = "UpdateGatewayResponse"
@@ -32,7 +34,7 @@ func (h *Handler) gatewayResponseActions() map[string]actionFn {
func (h *Handler) getGatewayResponseAction(b []byte) (int, any, error) {
var params getGatewayResponseInput
- if err := json.Unmarshal(b, ¶ms); err != nil {
+ if err := json.Unmarshal(b, ¶ms, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.GetGatewayResponse(params.RestAPIID, params.ResponseType)
@@ -45,7 +47,7 @@ func (h *Handler) getGatewayResponseAction(b []byte) (int, any, error) {
func (h *Handler) getGatewayResponsesAction(b []byte) (int, any, error) {
var params getGatewayResponsesInput
- if err := json.Unmarshal(b, ¶ms); err != nil {
+ if err := json.Unmarshal(b, ¶ms, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.GetGatewayResponses(params.RestAPIID)
@@ -67,7 +69,7 @@ func (h *Handler) getGatewayResponsesAction(b []byte) (int, any, error) {
func (h *Handler) putGatewayResponseAction(b []byte) (int, any, error) {
var input PutGatewayResponseInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.PutGatewayResponse(input)
@@ -80,7 +82,7 @@ func (h *Handler) putGatewayResponseAction(b []byte) (int, any, error) {
func (h *Handler) updateGatewayResponseAction(b []byte) (int, any, error) {
var input PutGatewayResponseInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -94,7 +96,7 @@ func (h *Handler) updateGatewayResponseAction(b []byte) (int, any, error) {
func (h *Handler) deleteGatewayResponseAction(b []byte) (int, any, error) {
var params getGatewayResponseInput
- if err := json.Unmarshal(b, ¶ms); err != nil {
+ if err := json.Unmarshal(b, ¶ms, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteGatewayResponse(params.RestAPIID, params.ResponseType); err != nil {
diff --git a/services/apigateway/handler_import_export.go b/services/apigateway/handler_import_export.go
index d1ae083393..9ed7d142c7 100644
--- a/services/apigateway/handler_import_export.go
+++ b/services/apigateway/handler_import_export.go
@@ -15,13 +15,14 @@ package apigateway
// payload, optionally with "restApiId"/"mode" as extra sibling fields.
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -59,7 +60,7 @@ type restAPISpecEnvelope struct {
// for the two payload shapes this handles.
func decodeRestAPISpecPayload(b []byte) ([]byte, restAPISpecEnvelope) {
var env restAPISpecEnvelope
- if err := json.Unmarshal(b, &env); err == nil && env.Body != "" {
+ if err := json.Unmarshal(b, &env, jsonx.V1()); err == nil && env.Body != "" {
if raw, decErr := base64.StdEncoding.DecodeString(env.Body); decErr == nil {
return raw, env
}
@@ -75,7 +76,7 @@ func decodeRestAPISpecPayload(b []byte) ([]byte, restAPISpecEnvelope) {
Format string `json:"format,omitempty"`
}
- _ = json.Unmarshal(b, &fallback)
+ _ = json.Unmarshal(b, &fallback, jsonx.V1())
return b, restAPISpecEnvelope{
RestAPIID: fallback.RestAPIID,
@@ -206,7 +207,7 @@ func (h *Handler) dispatchRestAPISpec(
env.Parameters[k] = v[0]
}
- envelope, err := json.Marshal(env)
+ envelope, err := json.Marshal(env, jsonx.V1())
if err != nil {
return writeJSONProtocolDispatchError(c, http.StatusInternalServerError,
"InternalFailure", "internal server error")
diff --git a/services/apigateway/handler_import_export_test.go b/services/apigateway/handler_import_export_test.go
index 72f3526c8f..cb1a6e8b46 100644
--- a/services/apigateway/handler_import_export_test.go
+++ b/services/apigateway/handler_import_export_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -48,7 +49,7 @@ func TestAPIGateway_ImportRestApi_DirectDispatch(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var api map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &api, jsonx.V1()))
assert.Equal(t, "Pet Store", api["name"])
assert.Equal(t, "A sample API", api["description"])
@@ -63,7 +64,7 @@ func TestAPIGateway_ImportRestApi_YAML(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var api map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &api, jsonx.V1()))
assert.Equal(t, "YAML API", api["name"])
assert.Equal(t, "from yaml", api["description"])
}
@@ -79,7 +80,7 @@ func TestAPIGateway_ImportRestApi_CreatesResourcesFromPaths(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var api map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &api, jsonx.V1()))
apiID, _ := api["id"].(string)
require.NotEmpty(t, apiID)
@@ -143,7 +144,7 @@ func TestAPIGateway_PutRestApi_DirectDispatch_Merge(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var api map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &api, jsonx.V1()))
assert.Equal(t, "merged", api["name"])
resources, _, err := backend.GetResources(created.ID, "", 0)
@@ -184,7 +185,7 @@ func TestAPIGateway_ImportRestApi_RESTRoute(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var api map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &api, jsonx.V1()))
assert.Equal(t, "Pet Store", api["name"])
// A plain POST /restapis (no mode=import) must still route to CreateRestApi
@@ -197,7 +198,7 @@ func TestAPIGateway_ImportRestApi_RESTRoute(t *testing.T) {
require.Equal(t, http.StatusCreated, rec2.Code)
var api2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &api2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &api2, jsonx.V1()))
assert.Equal(t, "plain-create", api2["name"])
}
@@ -228,7 +229,7 @@ func TestAPIGateway_PutRestApi_RESTRoute_Overwrite(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var api map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &api, jsonx.V1()))
assert.Equal(t, "Pet Store", api["name"])
resources, _, err := backend.GetResources(created.ID, "", 0)
@@ -300,7 +301,7 @@ func TestAPIGateway_ImportApiKeys_RESTRoute(t *testing.T) {
if tt.wantCount > 0 {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ids, _ := resp["ids"].([]any)
require.Len(t, ids, tt.wantCount)
}
@@ -329,7 +330,7 @@ func TestAPIGateway_ImportApiKeys_DuplicateWarns(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ids, _ := resp["ids"].([]any)
warnings, _ := resp["warnings"].([]any)
assert.Len(t, ids, 1, "only the non-duplicate row is imported")
@@ -364,7 +365,7 @@ func TestAPIGateway_ImportDocumentationParts_RESTRoute(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ids, _ := resp["ids"].([]any)
require.Len(t, ids, 2)
diff --git a/services/apigateway/handler_integrations.go b/services/apigateway/handler_integrations.go
index 9827335a17..d975d78fea 100644
--- a/services/apigateway/handler_integrations.go
+++ b/services/apigateway/handler_integrations.go
@@ -1,9 +1,11 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type putIntegrationInput struct {
@@ -103,7 +105,7 @@ func (h *Handler) integrationResponseActions() map[string]actionFn {
return map[string]actionFn{
opPutIntegrationResponse: func(b []byte) (int, any, error) {
var input putIntegrationResponseInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
ir, err := h.Backend.PutIntegrationResponse(
@@ -121,7 +123,7 @@ func (h *Handler) integrationResponseActions() map[string]actionFn {
},
opGetIntegrationResponse: func(b []byte) (int, any, error) {
var input getIntegrationResponseInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
ir, err := h.Backend.GetIntegrationResponse(
@@ -138,7 +140,7 @@ func (h *Handler) integrationResponseActions() map[string]actionFn {
},
opDeleteIntegrationResponse: func(b []byte) (int, any, error) {
var input deleteIntegrationResponseInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteIntegrationResponse(
@@ -154,7 +156,7 @@ func (h *Handler) integrationResponseActions() map[string]actionFn {
},
opUpdateIntegrationResponse: func(b []byte) (int, any, error) {
var input UpdateIntegrationResponseInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.UpdateIntegrationResponse(input)
@@ -171,7 +173,7 @@ func (h *Handler) integrationActions() map[string]actionFn {
return map[string]actionFn{
opPutIntegration: func(b []byte) (int, any, error) {
var input putIntegrationInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
switch input.Type {
@@ -197,7 +199,7 @@ func (h *Handler) integrationActions() map[string]actionFn {
},
opGetIntegration: func(b []byte) (int, any, error) {
var input getIntegrationInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
integ, err := h.Backend.GetIntegration(input.RestAPIID, input.ResourceID, input.HTTPMethod)
@@ -209,7 +211,7 @@ func (h *Handler) integrationActions() map[string]actionFn {
},
opDeleteIntegration: func(b []byte) (int, any, error) {
var input deleteIntegrationInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteIntegration(input.RestAPIID, input.ResourceID, input.HTTPMethod); err != nil {
@@ -220,7 +222,7 @@ func (h *Handler) integrationActions() map[string]actionFn {
},
opUpdateIntegration: func(b []byte) (int, any, error) {
var input UpdateIntegrationInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.UpdateIntegration(input)
diff --git a/services/apigateway/handler_methods.go b/services/apigateway/handler_methods.go
index 9e0304f5d8..424bd0037b 100644
--- a/services/apigateway/handler_methods.go
+++ b/services/apigateway/handler_methods.go
@@ -2,10 +2,12 @@ package apigateway
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type testInvokeMethodHandlerInput struct {
@@ -295,7 +297,7 @@ func (h *Handler) invokeLambdaTestMethod(
event.StageVariables = input.StageVariables
- payload, _ := json.Marshal(event)
+ payload, _ := json.Marshal(event, jsonx.V1())
lambdaFn := ExtractLambdaFunctionName(interpolateStageVars(integration.URI, input.StageVariables))
respBytes, _, invokeErr := h.lambda.InvokeFunction(context.Background(), lambdaFn, "RequestResponse", payload)
@@ -321,7 +323,7 @@ func lambdaTestOutput(respBytes []byte, invokeErr error) *TestInvokeMethodOutput
}
var lambdaResp LambdaProxyResponse
- if json.Unmarshal(respBytes, &lambdaResp) == nil {
+ if json.Unmarshal(respBytes, &lambdaResp, jsonx.V1()) == nil {
sc := lambdaResp.StatusCode
if sc == 0 {
sc = http.StatusOK
@@ -366,7 +368,7 @@ func (h *Handler) methodActions() map[string]actionFn {
func (h *Handler) putMethodAction(b []byte) (int, any, error) {
var input putMethodInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
switch input.AuthorizationType {
@@ -394,7 +396,7 @@ func (h *Handler) putMethodAction(b []byte) (int, any, error) {
func (h *Handler) getMethodAction(b []byte) (int, any, error) {
var input getMethodInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
m, err := h.Backend.GetMethod(input.RestAPIID, input.ResourceID, input.HTTPMethod)
@@ -407,7 +409,7 @@ func (h *Handler) getMethodAction(b []byte) (int, any, error) {
func (h *Handler) deleteMethodAction(b []byte) (int, any, error) {
var input deleteMethodInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteMethod(input.RestAPIID, input.ResourceID, input.HTTPMethod); err != nil {
@@ -419,7 +421,7 @@ func (h *Handler) deleteMethodAction(b []byte) (int, any, error) {
func (h *Handler) updateMethodAction(b []byte) (int, any, error) {
var input UpdateMethodInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.UpdateMethod(input)
@@ -432,7 +434,7 @@ func (h *Handler) updateMethodAction(b []byte) (int, any, error) {
func (h *Handler) testInvokeMethodAction(b []byte) (int, any, error) {
var input testInvokeMethodHandlerInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.testInvokeMethod(input.TestInvokeMethodInput)
@@ -448,7 +450,7 @@ func (h *Handler) methodResponseActions() map[string]actionFn {
return map[string]actionFn{
opPutMethodResponse: func(b []byte) (int, any, error) {
var input putMethodResponseInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
mr, err := h.Backend.PutMethodResponse(
@@ -466,7 +468,7 @@ func (h *Handler) methodResponseActions() map[string]actionFn {
},
opGetMethodResponse: func(b []byte) (int, any, error) {
var input getMethodResponseInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
mr, err := h.Backend.GetMethodResponse(
@@ -483,7 +485,7 @@ func (h *Handler) methodResponseActions() map[string]actionFn {
},
opDeleteMethodResponse: func(b []byte) (int, any, error) {
var input deleteMethodResponseInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteMethodResponse(
@@ -499,7 +501,7 @@ func (h *Handler) methodResponseActions() map[string]actionFn {
},
opUpdateMethodResponse: func(b []byte) (int, any, error) {
var input UpdateMethodResponseInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.UpdateMethodResponse(input)
diff --git a/services/apigateway/handler_request_validators.go b/services/apigateway/handler_request_validators.go
index fc161140c3..bd034fb8c3 100644
--- a/services/apigateway/handler_request_validators.go
+++ b/services/apigateway/handler_request_validators.go
@@ -1,8 +1,10 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createRequestValidatorInput struct {
@@ -47,7 +49,7 @@ func (h *Handler) requestValidatorActions() map[string]actionFn {
func (h *Handler) createRequestValidatorAction(b []byte) (int, any, error) {
var input createRequestValidatorInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
rv, err := h.Backend.CreateRequestValidator(input.RestAPIID, CreateRequestValidatorInput{
@@ -64,7 +66,7 @@ func (h *Handler) createRequestValidatorAction(b []byte) (int, any, error) {
func (h *Handler) getRequestValidatorAction(b []byte) (int, any, error) {
var input getRequestValidatorInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
rv, err := h.Backend.GetRequestValidator(input.RestAPIID, input.ValidatorID)
@@ -75,9 +77,10 @@ func (h *Handler) getRequestValidatorAction(b []byte) (int, any, error) {
return http.StatusOK, rv, nil
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) getRequestValidatorsAction(b []byte) (int, any, error) {
var input getRequestValidatorsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
rvs, err := h.Backend.GetRequestValidators(input.RestAPIID)
@@ -98,7 +101,7 @@ func (h *Handler) getRequestValidatorsAction(b []byte) (int, any, error) {
func (h *Handler) updateRequestValidatorAction(b []byte) (int, any, error) {
var input updateRequestValidatorInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
rv, err := h.Backend.UpdateRequestValidator(
@@ -115,7 +118,7 @@ func (h *Handler) updateRequestValidatorAction(b []byte) (int, any, error) {
func (h *Handler) deleteRequestValidatorAction(b []byte) (int, any, error) {
var input deleteRequestValidatorInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteRequestValidator(input.RestAPIID, input.ValidatorID); err != nil {
diff --git a/services/apigateway/handler_resources.go b/services/apigateway/handler_resources.go
index 460cc7f35c..b3f92333c7 100644
--- a/services/apigateway/handler_resources.go
+++ b/services/apigateway/handler_resources.go
@@ -1,8 +1,10 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type getResourcesInput struct {
@@ -47,7 +49,7 @@ func (h *Handler) resourceActions() map[string]actionFn {
func (h *Handler) getResourcesAction(b []byte) (int, any, error) {
var input getResourcesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
resources, position, err := h.Backend.GetResources(input.RestAPIID, input.Position, input.Limit)
@@ -64,7 +66,7 @@ func (h *Handler) getResourcesAction(b []byte) (int, any, error) {
func (h *Handler) getResourceAction(b []byte) (int, any, error) {
var input getResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
r, err := h.Backend.GetResource(input.RestAPIID, input.ResourceID)
@@ -77,7 +79,7 @@ func (h *Handler) getResourceAction(b []byte) (int, any, error) {
func (h *Handler) createResourceAction(b []byte) (int, any, error) {
var input createResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
r, err := h.Backend.CreateResource(input.RestAPIID, input.ParentID, input.PathPart)
@@ -90,7 +92,7 @@ func (h *Handler) createResourceAction(b []byte) (int, any, error) {
func (h *Handler) deleteResourceAction(b []byte) (int, any, error) {
var input deleteResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteResource(input.RestAPIID, input.ResourceID); err != nil {
@@ -102,7 +104,7 @@ func (h *Handler) deleteResourceAction(b []byte) (int, any, error) {
func (h *Handler) updateResourceAction(b []byte) (int, any, error) {
var input updateResourceHandlerInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
res, err := h.Backend.UpdateResource(input.RestAPIID, input.ResourceID, input.UpdateResourceInput)
diff --git a/services/apigateway/handler_rest_apis.go b/services/apigateway/handler_rest_apis.go
index 013f062980..9fcfdb2ecf 100644
--- a/services/apigateway/handler_rest_apis.go
+++ b/services/apigateway/handler_rest_apis.go
@@ -1,9 +1,10 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -39,7 +40,7 @@ func (h *Handler) restAPIActions() map[string]actionFn {
func (h *Handler) createRestAPIAction(b []byte) (int, any, error) {
var input createRestAPIInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if input.Tags != nil {
@@ -61,7 +62,7 @@ func (h *Handler) createRestAPIAction(b []byte) (int, any, error) {
func (h *Handler) deleteRestAPIAction(b []byte) (int, any, error) {
var input deleteRestAPIInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
// Deployment IDs are freshly random and never reused, so the trie cache
@@ -85,7 +86,7 @@ func (h *Handler) deleteRestAPIAction(b []byte) (int, any, error) {
func (h *Handler) getRestAPIAction(b []byte) (int, any, error) {
var input getRestAPIInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
api, err := h.Backend.GetRestAPI(input.RestAPIID)
@@ -98,7 +99,7 @@ func (h *Handler) getRestAPIAction(b []byte) (int, any, error) {
func (h *Handler) getRestAPIsAction(b []byte) (int, any, error) {
var input getRestApisInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
apis, position, err := h.Backend.GetRestAPIs(input.Limit, input.Position)
@@ -116,7 +117,7 @@ func (h *Handler) getRestAPIsAction(b []byte) (int, any, error) {
func (h *Handler) updateRestAPIAction(b []byte) (int, any, error) {
var input updateRestAPIHandlerInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
api, err := h.Backend.UpdateRestAPI(input.RestAPIID, input.UpdateRestAPIInput)
diff --git a/services/apigateway/handler_router_test.go b/services/apigateway/handler_router_test.go
index c7a4afd8d8..d4ba5ecc14 100644
--- a/services/apigateway/handler_router_test.go
+++ b/services/apigateway/handler_router_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"log/slog"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -31,7 +32,7 @@ func createParityAPI(t *testing.T, h *apigateway.Handler, name string) string {
require.Equal(t, http.StatusCreated, rec.Code, "create api: %s", rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
id, _ := out["id"].(string)
require.NotEmpty(t, id)
@@ -49,7 +50,7 @@ func getRootResourceID(t *testing.T, h *apigateway.Handler, apiID string) string
var out struct {
Items []map[string]any `json:"item"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
for _, r := range out.Items {
if r["path"] == "/" {
@@ -575,7 +576,7 @@ func TestHandler_APIGateway_FullWorkflow(t *testing.T) {
// parseJSON is a helper to decode JSON from a response recorder.
func parseJSON(rec *httptest.ResponseRecorder, v any) error {
- return json.Unmarshal(rec.Body.Bytes(), v)
+ return json.Unmarshal(rec.Body.Bytes(), v, jsonx.V1())
}
// errNoopNotImplemented is returned by noopBackend for methods that are not expected
@@ -1357,7 +1358,7 @@ func TestHandler_GetSupportedOperations(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var ops []string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops, jsonx.V1()))
assert.Contains(t, ops, "CreateRestApi")
})
}
diff --git a/services/apigateway/handler_schema_models.go b/services/apigateway/handler_schema_models.go
index 9dbce8598e..b653b09b29 100644
--- a/services/apigateway/handler_schema_models.go
+++ b/services/apigateway/handler_schema_models.go
@@ -1,8 +1,10 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type getModelInput struct {
@@ -47,7 +49,7 @@ func (h *Handler) schemaModelActions() map[string]actionFn {
func (h *Handler) createModelAction(b []byte) (int, any, error) {
var input CreateModelInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -61,7 +63,7 @@ func (h *Handler) createModelAction(b []byte) (int, any, error) {
func (h *Handler) getModelAction(b []byte) (int, any, error) {
var input getModelInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
m, err := h.Backend.GetModel(input.RestAPIID, input.ModelName, input.Flatten == litTrue)
@@ -72,9 +74,10 @@ func (h *Handler) getModelAction(b []byte) (int, any, error) {
return http.StatusOK, m, nil
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) getModelsAction(b []byte) (int, any, error) {
var input getModelsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
ms, err := h.Backend.GetModels(input.RestAPIID)
@@ -94,7 +97,7 @@ func (h *Handler) getModelsAction(b []byte) (int, any, error) {
func (h *Handler) deleteModelAction(b []byte) (int, any, error) {
var input deleteModelInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteModel(input.RestAPIID, input.ModelName); err != nil {
@@ -106,7 +109,7 @@ func (h *Handler) deleteModelAction(b []byte) (int, any, error) {
func (h *Handler) updateModelAction(b []byte) (int, any, error) {
var input updateModelInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
m, err := h.Backend.UpdateModel(input.RestAPIID, input.ModelName, input.UpdateModelInput)
@@ -122,7 +125,7 @@ func (h *Handler) getModelTemplateAction(b []byte) (int, any, error) {
RestAPIID string `json:"restApiId"`
ModelName string `json:"modelName"`
}
- if err := json.Unmarshal(b, ¶ms); err != nil {
+ if err := json.Unmarshal(b, ¶ms, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.GetModelTemplate(params.RestAPIID, params.ModelName)
diff --git a/services/apigateway/handler_sdk.go b/services/apigateway/handler_sdk.go
index 6b10462fbd..ef757aa424 100644
--- a/services/apigateway/handler_sdk.go
+++ b/services/apigateway/handler_sdk.go
@@ -1,8 +1,10 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -48,12 +50,12 @@ func (h *Handler) sdkActions() map[string]actionFn {
StageName string `json:"stageName"`
SdkType string `json:"sdkType"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
var raw map[string]any
- _ = json.Unmarshal(b, &raw)
+ _ = json.Unmarshal(b, &raw, jsonx.V1())
params := make(map[string]string)
@@ -78,7 +80,7 @@ func (h *Handler) sdkActions() map[string]actionFn {
var input struct {
ID string `json:"id"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -93,7 +95,7 @@ func (h *Handler) sdkActions() map[string]actionFn {
var input struct {
Limit int `json:"limit,omitempty"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
diff --git a/services/apigateway/handler_stages.go b/services/apigateway/handler_stages.go
index a74973de92..b72e7e810a 100644
--- a/services/apigateway/handler_stages.go
+++ b/services/apigateway/handler_stages.go
@@ -1,8 +1,10 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type getStagesInput struct {
@@ -46,7 +48,7 @@ func (h *Handler) stageActions() map[string]actionFn {
func (h *Handler) createStageAction(b []byte) (int, any, error) {
var input CreateStageInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -60,7 +62,7 @@ func (h *Handler) createStageAction(b []byte) (int, any, error) {
func (h *Handler) updateStageAction(b []byte) (int, any, error) {
var input updateStageHandlerInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
stage, err := h.Backend.UpdateStage(input.RestAPIID, input.StageName, input.UpdateStageInput)
@@ -77,7 +79,7 @@ func (h *Handler) updateStageAction(b []byte) (int, any, error) {
func (h *Handler) flushStageCacheAction(b []byte) (int, any, error) {
var input flushStageCacheInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if _, err := h.Backend.GetStage(input.RestAPIID, input.StageName); err != nil {
@@ -91,7 +93,7 @@ func (h *Handler) flushStageCacheAction(b []byte) (int, any, error) {
func (h *Handler) flushStageAuthorizersCacheAction(b []byte) (int, any, error) {
var input flushStageCacheInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
h.authCache.flush()
@@ -101,7 +103,7 @@ func (h *Handler) flushStageAuthorizersCacheAction(b []byte) (int, any, error) {
func (h *Handler) getStagesAction(b []byte) (int, any, error) {
var input getStagesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
stages, err := h.Backend.GetStages(input.RestAPIID)
@@ -123,7 +125,7 @@ func (h *Handler) getStagesAction(b []byte) (int, any, error) {
func (h *Handler) getStageAction(b []byte) (int, any, error) {
var input getStageInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
stage, err := h.Backend.GetStage(input.RestAPIID, input.StageName)
@@ -136,7 +138,7 @@ func (h *Handler) getStageAction(b []byte) (int, any, error) {
func (h *Handler) deleteStageAction(b []byte) (int, any, error) {
var input deleteStageInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteStage(input.RestAPIID, input.StageName); err != nil {
diff --git a/services/apigateway/handler_tags.go b/services/apigateway/handler_tags.go
index 58c12fbf38..53c4c78fbf 100644
--- a/services/apigateway/handler_tags.go
+++ b/services/apigateway/handler_tags.go
@@ -1,11 +1,12 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -51,7 +52,7 @@ func (h *Handler) tagActions() map[string]actionFn {
return map[string]actionFn{
opGetTags: func(b []byte) (int, any, error) {
var input getTagsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
t, err := h.Backend.GetResourceTags(input.ResourceARN)
@@ -63,7 +64,7 @@ func (h *Handler) tagActions() map[string]actionFn {
},
opTagResource: func(b []byte) (int, any, error) {
var input tagResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
for key := range input.Tags {
@@ -83,7 +84,7 @@ func (h *Handler) tagActions() map[string]actionFn {
},
opUntagResource: func(b []byte) (int, any, error) {
var input untagResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.UntagResource(input.ResourceARN, input.TagKeys); err != nil {
diff --git a/services/apigateway/handler_test.go b/services/apigateway/handler_test.go
index 2012383682..54e22b3d4c 100644
--- a/services/apigateway/handler_test.go
+++ b/services/apigateway/handler_test.go
@@ -2,7 +2,7 @@ package apigateway_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -129,7 +130,7 @@ func TestHandler_RestAPI(t *testing.T) {
assert.Equal(t, tt.wantCreateCode, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
assert.NotEmpty(t, created["id"])
assert.Equal(t, tt.primaryAPIName, created["name"])
@@ -144,7 +145,7 @@ func TestHandler_RestAPI(t *testing.T) {
assert.Equal(t, tt.wantGetCode, rec2.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, apiID, got["id"])
assert.Equal(t, tt.primaryAPIName, got["name"])
}
@@ -154,7 +155,7 @@ func TestHandler_RestAPI(t *testing.T) {
assert.Equal(t, http.StatusOK, rec3.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["item"].([]any), tt.wantListCount)
}
@@ -207,14 +208,14 @@ func TestHandler_Resources(t *testing.T) {
rec := postWithHandler(t, handler, e, "CreateRestApi", `{"name":"api"}`)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
apiID := created["id"].(string)
rec2 := postWithHandler(t, handler, e, "GetResources", fmt.Sprintf(`{"restApiId":%q}`, apiID))
assert.Equal(t, http.StatusOK, rec2.Code)
var res map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &res))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &res, jsonx.V1()))
items := res["item"].([]any)
if tt.wantResourceCount > 0 {
@@ -231,7 +232,7 @@ func TestHandler_Resources(t *testing.T) {
assert.Equal(t, http.StatusCreated, rec3.Code)
var child map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &child))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &child, jsonx.V1()))
if tt.wantChildPath != "" {
assert.Equal(t, tt.wantChildPath, child["path"])
@@ -284,12 +285,12 @@ func TestHandler_Method(t *testing.T) {
rec := postWithHandler(t, handler, e, "CreateRestApi", `{"name":"api"}`)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
apiID := created["id"].(string)
rec2 := postWithHandler(t, handler, e, "GetResources", fmt.Sprintf(`{"restApiId":%q}`, apiID))
var res map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &res))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &res, jsonx.V1()))
rootID := res["item"].([]any)[0].(map[string]any)["id"].(string)
rec3 := postWithHandler(t, handler, e, "PutMethod",
@@ -298,7 +299,7 @@ func TestHandler_Method(t *testing.T) {
assert.Equal(t, tt.wantPutCode, rec3.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &m, jsonx.V1()))
assert.Equal(t, tt.httpMethod, m["httpMethod"])
rec4 := postWithHandler(t, handler, e, "GetMethod",
@@ -347,12 +348,12 @@ func TestHandler_Integration(t *testing.T) {
rec := postWithHandler(t, handler, e, "CreateRestApi", `{"name":"api"}`)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
apiID := created["id"].(string)
rec2 := postWithHandler(t, handler, e, "GetResources", fmt.Sprintf(`{"restApiId":%q}`, apiID))
var res map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &res))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &res, jsonx.V1()))
rootID := res["item"].([]any)[0].(map[string]any)["id"].(string)
postWithHandler(t, handler, e, "PutMethod",
@@ -365,7 +366,7 @@ func TestHandler_Integration(t *testing.T) {
assert.Equal(t, tt.wantPutCode, rec3.Code)
var integ map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &integ))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &integ, jsonx.V1()))
assert.Equal(t, tt.integrationType, integ["type"])
rec4 := postWithHandler(t, handler, e, "GetIntegration",
@@ -410,7 +411,7 @@ func TestHandler_Deployment(t *testing.T) {
rec := postWithHandler(t, handler, e, "CreateRestApi", `{"name":"api"}`)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
apiID := created["id"].(string)
rec2 := postWithHandler(t, handler, e, "CreateDeployment",
@@ -419,14 +420,14 @@ func TestHandler_Deployment(t *testing.T) {
assert.Equal(t, tt.wantCreateCode, rec2.Code)
var depl map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &depl))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &depl, jsonx.V1()))
assert.NotEmpty(t, depl["id"])
rec3 := postWithHandler(t, handler, e, "GetDeployments", fmt.Sprintf(`{"restApiId":%q}`, apiID))
assert.Equal(t, http.StatusOK, rec3.Code)
var depls map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &depls))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &depls, jsonx.V1()))
assert.Len(t, depls["item"].([]any), tt.wantListCount)
})
}
@@ -463,7 +464,7 @@ func TestHandler_Stages(t *testing.T) {
rec := postWithHandler(t, handler, e, "CreateRestApi", `{"name":"api"}`)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
apiID := created["id"].(string)
postWithHandler(t, handler, e, "CreateDeployment",
@@ -473,7 +474,7 @@ func TestHandler_Stages(t *testing.T) {
assert.Equal(t, tt.wantListCode, rec2.Code)
var stages map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &stages))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &stages, jsonx.V1()))
assert.Len(t, stages["item"].([]any), tt.wantListCount)
rec3 := postWithHandler(t, handler, e, "GetStage",
@@ -540,7 +541,7 @@ func TestHandler_Errors(t *testing.T) {
if tt.wantType != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
}
})
@@ -716,7 +717,7 @@ func TestHandler_CreateAPIKey(t *testing.T) {
if tt.wantName != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantName, resp["name"])
// value must be non-empty (either provided or auto-generated)
assert.NotEmpty(t, resp["value"])
@@ -784,7 +785,7 @@ func TestHandler_CreateDocumentationPart(t *testing.T) {
require.Equal(t, http.StatusCreated, apiRec.Code)
var apiResp map[string]any
- require.NoError(t, json.Unmarshal(apiRec.Body.Bytes(), &apiResp))
+ require.NoError(t, json.Unmarshal(apiRec.Body.Bytes(), &apiResp, jsonx.V1()))
apiID, _ := apiResp["id"].(string)
require.NotEmpty(t, apiID)
@@ -833,7 +834,7 @@ func TestHandler_CreateDocumentationVersion(t *testing.T) {
require.Equal(t, http.StatusCreated, apiRec.Code)
var apiResp map[string]any
- require.NoError(t, json.Unmarshal(apiRec.Body.Bytes(), &apiResp))
+ require.NoError(t, json.Unmarshal(apiRec.Body.Bytes(), &apiResp, jsonx.V1()))
apiID, _ := apiResp["id"].(string)
require.NotEmpty(t, apiID)
@@ -979,7 +980,7 @@ func TestHandler_CreateModel(t *testing.T) {
require.Equal(t, http.StatusCreated, apiRec.Code)
var apiResp map[string]any
- require.NoError(t, json.Unmarshal(apiRec.Body.Bytes(), &apiResp))
+ require.NoError(t, json.Unmarshal(apiRec.Body.Bytes(), &apiResp, jsonx.V1()))
apiID, _ := apiResp["id"].(string)
require.NotEmpty(t, apiID)
@@ -1043,7 +1044,7 @@ func TestHandler_CreateStageStandalone(t *testing.T) {
require.Equal(t, http.StatusCreated, apiRec.Code)
var apiResp map[string]any
- require.NoError(t, json.Unmarshal(apiRec.Body.Bytes(), &apiResp))
+ require.NoError(t, json.Unmarshal(apiRec.Body.Bytes(), &apiResp, jsonx.V1()))
apiID, _ := apiResp["id"].(string)
require.NotEmpty(t, apiID)
@@ -1051,7 +1052,7 @@ func TestHandler_CreateStageStandalone(t *testing.T) {
require.Equal(t, http.StatusCreated, deplRec.Code)
var deplResp map[string]any
- require.NoError(t, json.Unmarshal(deplRec.Body.Bytes(), &deplResp))
+ require.NoError(t, json.Unmarshal(deplRec.Body.Bytes(), &deplResp, jsonx.V1()))
deplID, _ := deplResp["id"].(string)
require.NotEmpty(t, deplID)
@@ -1170,7 +1171,7 @@ func TestHandler_CreateUsagePlan(t *testing.T) {
if tt.checkResp != nil && rec.Code == http.StatusCreated {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tt.checkResp(t, resp)
}
})
@@ -1187,7 +1188,7 @@ func TestHandler_CreateUsagePlanKey(t *testing.T) {
require.Equal(t, http.StatusCreated, keyRec.Code)
var keyResp map[string]any
- require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &keyResp))
+ require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &keyResp, jsonx.V1()))
keyID, _ := keyResp["id"].(string)
require.NotEmpty(t, keyID)
@@ -1195,7 +1196,7 @@ func TestHandler_CreateUsagePlanKey(t *testing.T) {
require.Equal(t, http.StatusCreated, planRec.Code)
var planResp map[string]any
- require.NoError(t, json.Unmarshal(planRec.Body.Bytes(), &planResp))
+ require.NoError(t, json.Unmarshal(planRec.Body.Bytes(), &planResp, jsonx.V1()))
planID, _ := planResp["id"].(string)
require.NotEmpty(t, planID)
@@ -1204,7 +1205,7 @@ func TestHandler_CreateUsagePlanKey(t *testing.T) {
require.Equal(t, http.StatusCreated, dupKeyRec.Code)
var dupKeyResp map[string]any
- require.NoError(t, json.Unmarshal(dupKeyRec.Body.Bytes(), &dupKeyResp))
+ require.NoError(t, json.Unmarshal(dupKeyRec.Body.Bytes(), &dupKeyResp, jsonx.V1()))
dupKeyID, _ := dupKeyResp["id"].(string)
require.NotEmpty(t, dupKeyID)
@@ -1260,7 +1261,7 @@ func TestHandler_CreateUsagePlanKey(t *testing.T) {
if tt.wantType != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["type"])
}
})
diff --git a/services/apigateway/handler_usage.go b/services/apigateway/handler_usage.go
index 011b8e7ad8..8c01381843 100644
--- a/services/apigateway/handler_usage.go
+++ b/services/apigateway/handler_usage.go
@@ -1,8 +1,10 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const opUpdateUsage = "UpdateUsage"
@@ -13,7 +15,7 @@ func (h *Handler) usageActions() map[string]actionFn {
return map[string]actionFn{
opGetUsage: func(b []byte) (int, any, error) {
var input GetUsageInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.GetUsage(input)
@@ -25,7 +27,7 @@ func (h *Handler) usageActions() map[string]actionFn {
},
opUpdateUsage: func(b []byte) (int, any, error) {
var raw map[string]string
- if err := json.Unmarshal(b, &raw); err != nil {
+ if err := json.Unmarshal(b, &raw, jsonx.V1()); err != nil {
return 0, nil, err
}
diff --git a/services/apigateway/handler_usage_plans.go b/services/apigateway/handler_usage_plans.go
index 7c7726cd83..a1c090b864 100644
--- a/services/apigateway/handler_usage_plans.go
+++ b/services/apigateway/handler_usage_plans.go
@@ -1,8 +1,10 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type getUsagePlansPageInput struct {
@@ -139,7 +141,7 @@ func (h *Handler) usagePlanActions() map[string]actionFn {
func (h *Handler) createUsagePlanAction(b []byte) (int, any, error) {
var input CreateUsagePlanInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -153,7 +155,7 @@ func (h *Handler) createUsagePlanAction(b []byte) (int, any, error) {
func (h *Handler) createUsagePlanKeyAction(b []byte) (int, any, error) {
var input CreateUsagePlanKeyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -167,7 +169,7 @@ func (h *Handler) createUsagePlanKeyAction(b []byte) (int, any, error) {
func (h *Handler) getUsagePlanAction(b []byte) (int, any, error) {
var input getUsagePlanInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
p, err := h.Backend.GetUsagePlan(input.UsagePlanID)
@@ -180,7 +182,7 @@ func (h *Handler) getUsagePlanAction(b []byte) (int, any, error) {
func (h *Handler) getUsagePlansAction(b []byte) (int, any, error) {
var input getUsagePlansPageInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -215,7 +217,7 @@ func (h *Handler) getUsagePlansAction(b []byte) (int, any, error) {
func (h *Handler) updateUsagePlanAction(b []byte) (int, any, error) {
var input UpdateUsagePlanInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
out, err := h.Backend.UpdateUsagePlan(input)
@@ -228,7 +230,7 @@ func (h *Handler) updateUsagePlanAction(b []byte) (int, any, error) {
func (h *Handler) deleteUsagePlanAction(b []byte) (int, any, error) {
var input deleteUsagePlanInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteUsagePlan(input.UsagePlanID); err != nil {
@@ -240,7 +242,7 @@ func (h *Handler) deleteUsagePlanAction(b []byte) (int, any, error) {
func (h *Handler) getUsagePlanKeyAction(b []byte) (int, any, error) {
var input getUsagePlanKeyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
k, err := h.Backend.GetUsagePlanKey(input.UsagePlanID, input.KeyID)
@@ -253,7 +255,7 @@ func (h *Handler) getUsagePlanKeyAction(b []byte) (int, any, error) {
func (h *Handler) getUsagePlanKeysAction(b []byte) (int, any, error) {
var input getUsagePlanKeysInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
ks, err := h.Backend.GetUsagePlanKeys(input.UsagePlanID)
@@ -282,7 +284,7 @@ func (h *Handler) getUsagePlanKeysAction(b []byte) (int, any, error) {
func (h *Handler) deleteUsagePlanKeyAction(b []byte) (int, any, error) {
var input deleteUsagePlanKeyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
if err := h.Backend.DeleteUsagePlanKey(input.UsagePlanID, input.KeyID); err != nil {
diff --git a/services/apigateway/handler_vpc_links.go b/services/apigateway/handler_vpc_links.go
index 762127269b..24d7d62f7c 100644
--- a/services/apigateway/handler_vpc_links.go
+++ b/services/apigateway/handler_vpc_links.go
@@ -1,8 +1,10 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -64,7 +66,7 @@ func (h *Handler) vpcLinkActions() map[string]actionFn {
func (h *Handler) createVpcLinkAction(b []byte) (int, any, error) {
var input CreateVpcLinkInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -78,7 +80,7 @@ func (h *Handler) createVpcLinkAction(b []byte) (int, any, error) {
func (h *Handler) deleteVpcLinkAction(b []byte) (int, any, error) {
var input deleteVpcLinkInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -91,7 +93,7 @@ func (h *Handler) deleteVpcLinkAction(b []byte) (int, any, error) {
func (h *Handler) getVpcLinkAction(b []byte) (int, any, error) {
var input getVpcLinkInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
@@ -105,7 +107,7 @@ func (h *Handler) getVpcLinkAction(b []byte) (int, any, error) {
func (h *Handler) getVpcLinksAction(b []byte) (int, any, error) {
var input getVpcLinksInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
links, err := h.Backend.GetVpcLinks()
@@ -126,7 +128,7 @@ func (h *Handler) getVpcLinksAction(b []byte) (int, any, error) {
func (h *Handler) updateVpcLinkAction(b []byte) (int, any, error) {
var input UpdateVpcLinkInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return 0, nil, err
}
diff --git a/services/apigateway/import.go b/services/apigateway/import.go
index 75d44d1a54..815ea95009 100644
--- a/services/apigateway/import.go
+++ b/services/apigateway/import.go
@@ -10,7 +10,8 @@ package apigateway
import (
"bytes"
"encoding/csv"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strconv"
"strings"
@@ -19,6 +20,7 @@ import (
"gopkg.in/yaml.v3"
"github.com/blackbirdworks/gopherstack/pkgs/collections"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -43,15 +45,15 @@ type PutRestAPIInput struct {
// openAPIDoc is the subset of an OpenAPI/Swagger document we interpret.
type openAPIDoc struct {
- Info openAPIInfo `json:"info"`
- Paths map[string]map[string]json.RawMessage `json:"paths"`
- Definitions map[string]json.RawMessage `json:"definitions"`
- Components *openAPIComponents `json:"components"`
- SecurityDefinitions map[string]openAPISecurityScheme `json:"securityDefinitions"`
- Swagger string `json:"swagger"`
- OpenAPI string `json:"openapi"`
- APIKeySourceExt string `json:"x-amazon-apigateway-api-key-source"`
- BinaryMediaTypes []string `json:"x-amazon-apigateway-binary-media-types"`
+ Info openAPIInfo `json:"info"`
+ Paths map[string]map[string]jsontext.Value `json:"paths"`
+ Definitions map[string]jsontext.Value `json:"definitions"`
+ Components *openAPIComponents `json:"components"`
+ SecurityDefinitions map[string]openAPISecurityScheme `json:"securityDefinitions"`
+ Swagger string `json:"swagger"`
+ OpenAPI string `json:"openapi"`
+ APIKeySourceExt string `json:"x-amazon-apigateway-api-key-source"`
+ BinaryMediaTypes []string `json:"x-amazon-apigateway-binary-media-types"`
}
type openAPIInfo struct {
@@ -61,7 +63,7 @@ type openAPIInfo struct {
}
type openAPIComponents struct {
- Schemas map[string]json.RawMessage `json:"schemas"`
+ Schemas map[string]jsontext.Value `json:"schemas"`
SecuritySchemes map[string]openAPISecurityScheme `json:"securitySchemes"`
}
@@ -76,20 +78,20 @@ type openAPIOperation struct {
}
type openAPIParameter struct {
- Name string `json:"name"`
- In string `json:"in"`
- Schema json.RawMessage `json:"schema"`
- Required bool `json:"required"`
+ Name string `json:"name"`
+ In string `json:"in"`
+ Schema jsontext.Value `json:"schema"`
+ Required bool `json:"required"`
}
type openAPIResponse struct {
Content map[string]openAPIMediaTyp `json:"content"`
Description string `json:"description"`
- Schema json.RawMessage `json:"schema"`
+ Schema jsontext.Value `json:"schema"`
}
type openAPIMediaTyp struct {
- Schema json.RawMessage `json:"schema"`
+ Schema jsontext.Value `json:"schema"`
}
type openAPIRequestBody struct {
@@ -141,13 +143,13 @@ func parseOpenAPI(body []byte) (*openAPIDoc, error) {
)
}
- normalized, err := json.Marshal(raw)
+ normalized, err := json.Marshal(raw, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("%w: invalid OpenAPI document: %w", ErrInvalidParameter, err)
}
var doc openAPIDoc
- if jsonErr := json.Unmarshal(normalized, &doc); jsonErr != nil {
+ if jsonErr := json.Unmarshal(normalized, &doc, jsonx.V1()); jsonErr != nil {
return nil, fmt.Errorf("%w: invalid OpenAPI document: %w", ErrInvalidParameter, jsonErr)
}
if doc.Swagger == "" && doc.OpenAPI == "" {
@@ -244,7 +246,7 @@ func openAPIOperationWarnings(raw map[string]any, knownOperationExtensions map[s
}
// schemaDefinitions returns the named schemas regardless of spec version.
-func (d *openAPIDoc) schemaDefinitions() map[string]json.RawMessage {
+func (d *openAPIDoc) schemaDefinitions() map[string]jsontext.Value {
if len(d.Definitions) > 0 {
return d.Definitions
}
@@ -446,7 +448,7 @@ func importPaths(b *InMemoryBackend, api *RestAPI, doc *openAPIDoc) {
continue
}
var op openAPIOperation
- if err := json.Unmarshal(raw, &op); err != nil {
+ if err := json.Unmarshal(raw, &op, jsonx.V1()); err != nil {
continue
}
importMethod(res, httpMethod, &op, auths)
@@ -665,14 +667,14 @@ func applyImportedSecurity(method *Method, op *openAPIOperation) {
// schemaRefName extracts the model name from a JSON schema reference such as
// {"$ref":"#/definitions/Foo"} or {"$ref":"#/components/schemas/Foo"}.
-func schemaRefName(raw json.RawMessage) string {
+func schemaRefName(raw jsontext.Value) string {
if len(raw) == 0 {
return ""
}
var s struct {
Ref string `json:"$ref"`
}
- if err := json.Unmarshal(raw, &s); err != nil || s.Ref == "" {
+ if err := json.Unmarshal(raw, &s, jsonx.V1()); err != nil || s.Ref == "" {
return ""
}
idx := strings.LastIndex(s.Ref, "/")
@@ -811,7 +813,7 @@ func (b *InMemoryBackend) ImportAPIKeys(body []byte, format string, failOnWarnin
// "x-amazon-apigateway-documentation" documentationParts array.
type importDocumentationPartEntry struct {
Location DocumentationLocation `json:"location"`
- Properties json.RawMessage `json:"properties"`
+ Properties jsontext.Value `json:"properties"`
}
// importDocumentationPartsPayload is the expected JSON shape for
@@ -831,7 +833,7 @@ func (b *InMemoryBackend) ImportDocumentationParts(
restAPIID string, body []byte, mode string, failOnWarnings bool,
) ([]string, []string, error) {
var payload importDocumentationPartsPayload
- if err := json.Unmarshal(body, &payload); err != nil {
+ if err := json.Unmarshal(body, &payload, jsonx.V1()); err != nil {
return nil, nil, fmt.Errorf("%w: failed to parse documentation parts payload: %w", ErrInvalidParameter, err)
}
diff --git a/services/apigateway/import_test.go b/services/apigateway/import_test.go
index 316aa698be..9377b50113 100644
--- a/services/apigateway/import_test.go
+++ b/services/apigateway/import_test.go
@@ -1,13 +1,15 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -282,7 +284,7 @@ func TestImportRestAPI_RESTRouting(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, "PetStore", resp["name"])
apiID, _ := resp["id"].(string)
require.NotEmpty(t, apiID)
@@ -302,7 +304,7 @@ func TestImportRestAPI_RESTRouting(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, "plain", resp["name"])
})
diff --git a/services/apigateway/integrations_test.go b/services/apigateway/integrations_test.go
index ccfa55db32..81a815c872 100644
--- a/services/apigateway/integrations_test.go
+++ b/services/apigateway/integrations_test.go
@@ -1,7 +1,8 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -47,7 +49,7 @@ func TestPutIntegration_DefaultTimeout(t *testing.T) {
require.True(t, createRec.Code >= 200 && createRec.Code < 300)
var apiResp map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&apiResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &apiResp, jsonx.V1()))
apiID := apiResp["id"].(string)
require.NotEmpty(t, apiID)
@@ -55,7 +57,7 @@ func TestPutIntegration_DefaultTimeout(t *testing.T) {
require.True(t, resRec.Code >= 200 && resRec.Code < 300)
var resResp map[string]any
- require.NoError(t, json.NewDecoder(resRec.Body).Decode(&resResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resRec.Body), &resResp, jsonx.V1()))
items := resResp["item"].([]any)
rootID := items[0].(map[string]any)["id"].(string)
@@ -70,7 +72,7 @@ func TestPutIntegration_DefaultTimeout(t *testing.T) {
require.True(t, intRec.Code >= 200 && intRec.Code < 300)
var intResp map[string]any
- require.NoError(t, json.NewDecoder(intRec.Body).Decode(&intResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(intRec.Body), &intResp, jsonx.V1()))
got, ok := intResp["timeoutInMillis"].(float64)
require.True(t, ok, "timeoutInMillis must be present in PutIntegration response")
@@ -318,14 +320,14 @@ func TestHandlerIntegration_ConnectionType_RoundTrip(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300)
var apiResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&apiResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &apiResp, jsonx.V1()))
apiID := apiResp["id"].(string)
rec = restRequest(t, h, http.MethodGet, "/restapis/"+apiID+"/resources", "")
require.True(t, rec.Code >= 200 && rec.Code < 300)
var resResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resResp, jsonx.V1()))
items := resResp["item"].([]any)
rootID := items[0].(map[string]any)["id"].(string)
@@ -340,7 +342,7 @@ func TestHandlerIntegration_ConnectionType_RoundTrip(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300)
var integResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&integResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &integResp, jsonx.V1()))
assert.Equal(t, "INTERNET", integResp["connectionType"])
}
diff --git a/services/apigateway/invoke_stage_test.go b/services/apigateway/invoke_stage_test.go
index 5df23eb915..58ff716f74 100644
--- a/services/apigateway/invoke_stage_test.go
+++ b/services/apigateway/invoke_stage_test.go
@@ -1,13 +1,14 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -64,7 +65,7 @@ func TestInvokeStage(t *testing.T) {
var ev apigateway.LambdaProxyEvent
- require.NoError(t, json.Unmarshal(captured, &ev))
+ require.NoError(t, json.Unmarshal(captured, &ev, jsonx.V1()))
assert.Equal(t, "/items", ev.Resource)
assert.Equal(t, `{"a":1}`, ev.Body)
assert.Equal(t, "v", ev.QueryStringParameters["q"])
diff --git a/services/apigateway/methods_test.go b/services/apigateway/methods_test.go
index 6e9ce9e89f..8b3371e777 100644
--- a/services/apigateway/methods_test.go
+++ b/services/apigateway/methods_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -129,7 +130,7 @@ func TestPutMethod_RequestParametersRoundtrip(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code, "body: %s", rec.Body.String())
var method map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &method))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &method, jsonx.V1()))
params, _ := method["requestParameters"].(map[string]any)
require.NotNil(t, params, "requestParameters must be present in response")
@@ -234,7 +235,7 @@ func TestTestInvokeMethod(t *testing.T) {
if tt.wantStatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.EqualValues(t, http.StatusOK, resp["status"])
}
})
diff --git a/services/apigateway/models.go b/services/apigateway/models.go
index f379b7ecb2..4d72f22fe5 100644
--- a/services/apigateway/models.go
+++ b/services/apigateway/models.go
@@ -1,10 +1,11 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -32,12 +33,12 @@ type unixEpochTime struct {
}
func (t unixEpochTime) MarshalJSON() ([]byte, error) {
- return json.Marshal(t.Unix())
+ return json.Marshal(t.Unix(), jsonx.V1())
}
func (t *unixEpochTime) UnmarshalJSON(b []byte) error {
var epoch int64
- if err := json.Unmarshal(b, &epoch); err != nil {
+ if err := json.Unmarshal(b, &epoch, jsonx.V1()); err != nil {
return err
}
@@ -105,7 +106,7 @@ func wirePolicyString(plain string) *string {
return nil
}
- quoted, err := json.Marshal(plain)
+ quoted, err := json.Marshal(plain, jsonx.V1())
if err != nil || len(quoted) < 2 {
return nil
}
diff --git a/services/apigateway/multi_region_test.go b/services/apigateway/multi_region_test.go
index 6d509428cc..5f533a7793 100644
--- a/services/apigateway/multi_region_test.go
+++ b/services/apigateway/multi_region_test.go
@@ -2,7 +2,7 @@ package apigateway_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -50,7 +51,7 @@ func apiNames(t *testing.T, h *apigateway.Handler, region string) []string {
} `json:"item"`
}
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
names := make([]string, 0, len(out.Item))
for _, i := range out.Item {
diff --git a/services/apigateway/patch.go b/services/apigateway/patch.go
index ed2f5f59d0..10b384088b 100644
--- a/services/apigateway/patch.go
+++ b/services/apigateway/patch.go
@@ -1,12 +1,15 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// patch.go implements AWS API Gateway's PATCH-operation semantics beyond what
@@ -90,10 +93,10 @@ const (
// op used e.g. to promote a canary deployment,
// {"op":"copy","from":"/canarySettings/deploymentId","path":"/deploymentId"}.
type patchOp struct {
- Op string `json:"op"`
- Path string `json:"path"`
- From string `json:"from,omitempty"`
- Value json.RawMessage `json:"value"`
+ Op string `json:"op"`
+ Path string `json:"path"`
+ From string `json:"from,omitempty"`
+ Value jsontext.Value `json:"value"`
}
// parsePatchDocument detects and parses a PATCH request body: either a bare
@@ -101,7 +104,7 @@ type patchOp struct {
// wrapper (the real AWS wire shape), which may carry sibling fields alongside
// "patchOperations" (rest). ok is false when body is neither shape, in which
// case the caller should treat body as an already-flat field object.
-func parsePatchDocument(body []byte) ([]patchOp, map[string]json.RawMessage, bool) {
+func parsePatchDocument(body []byte) ([]patchOp, map[string]jsontext.Value, bool) {
if len(body) == 0 {
return nil, nil, false
}
@@ -109,7 +112,7 @@ func parsePatchDocument(body []byte) ([]patchOp, map[string]json.RawMessage, boo
switch body[0] {
case '[':
var arr []patchOp
- if err := json.Unmarshal(body, &arr); err != nil || len(arr) == 0 || arr[0].Op == "" {
+ if err := json.Unmarshal(body, &arr, jsonx.V1()); err != nil || len(arr) == 0 || arr[0].Op == "" {
return nil, nil, false
}
@@ -118,13 +121,13 @@ func parsePatchDocument(body []byte) ([]patchOp, map[string]json.RawMessage, boo
var wrapper struct {
PatchOperations []patchOp `json:"patchOperations"`
}
- if err := json.Unmarshal(body, &wrapper); err != nil || len(wrapper.PatchOperations) == 0 {
+ if err := json.Unmarshal(body, &wrapper, jsonx.V1()); err != nil || len(wrapper.PatchOperations) == 0 {
return nil, nil, false
}
- var siblings map[string]json.RawMessage
+ var siblings map[string]jsontext.Value
- _ = json.Unmarshal(body, &siblings)
+ _ = json.Unmarshal(body, &siblings, jsonx.V1())
delete(siblings, "patchOperations")
return wrapper.PatchOperations, siblings, true
@@ -180,7 +183,7 @@ var removableTopLevelScalar = map[string]map[string]bool{
// top level. Fields absent from patchFieldKind are assumed to be strings (the
// common case, and already correctly a JSON string on the wire) and passed
// through unchanged.
-func coerceTopLevelPatchValue(field string, raw json.RawMessage) json.RawMessage {
+func coerceTopLevelPatchValue(field string, raw jsontext.Value) jsontext.Value {
kind, known := patchFieldKind[field]
if !known {
return raw
@@ -194,13 +197,13 @@ func coerceTopLevelPatchValue(field string, raw json.RawMessage) json.RawMessage
switch kind {
case patchKindBool:
if b, parseErr := strconv.ParseBool(s); parseErr == nil {
- if encoded, marshalErr := json.Marshal(b); marshalErr == nil {
+ if encoded, marshalErr := json.Marshal(b, jsonx.V1()); marshalErr == nil {
return encoded
}
}
case patchKindInt:
if n, parseErr := strconv.Atoi(s); parseErr == nil {
- if encoded, marshalErr := json.Marshal(n); marshalErr == nil {
+ if encoded, marshalErr := json.Marshal(n, jsonx.V1()); marshalErr == nil {
return encoded
}
}
@@ -228,13 +231,13 @@ func patchIgnorableErr(err error) bool {
// numeric targets (see the package doc above), so every call site that needs
// the underlying scalar goes through this and then parses it with strconv.
// ok is false when Value is empty/absent or not a JSON string.
-func patchValueString(raw json.RawMessage) (string, bool) {
+func patchValueString(raw jsontext.Value) (string, bool) {
if len(raw) == 0 {
return "", false
}
var s string
- if err := json.Unmarshal(raw, &s); err != nil {
+ if err := json.Unmarshal(raw, &s, jsonx.V1()); err != nil {
return "", false
}
@@ -264,8 +267,8 @@ func patchPathSegments(path string) []string {
// setJSONValue marshals value and stores it under field in out. Errors are
// swallowed (defensive only): every call site below passes a well-formed
// built-in Go value that always marshals successfully.
-func setJSONValue(out map[string]json.RawMessage, field string, value any) {
- raw, err := json.Marshal(value)
+func setJSONValue(out map[string]jsontext.Value, field string, value any) {
+ raw, err := json.Marshal(value, jsonx.V1())
if err != nil {
return
}
@@ -284,7 +287,7 @@ func setJSONValue(out map[string]json.RawMessage, field string, value any) {
// discarding whatever an earlier op in the SAME request had already staged
// there. ok is false when field isn't staged yet or fails to decode, in
// which case the caller must fall back to reading current backend state.
-func stagedValue[T any](out map[string]json.RawMessage, field string) (T, bool) {
+func stagedValue[T any](out map[string]jsontext.Value, field string) (T, bool) {
var v T
raw, present := out[field]
@@ -292,7 +295,7 @@ func stagedValue[T any](out map[string]json.RawMessage, field string) (T, bool)
return v, false
}
- if err := json.Unmarshal(raw, &v); err != nil {
+ if err := json.Unmarshal(raw, &v, jsonx.V1()); err != nil {
return v, false
}
@@ -334,7 +337,7 @@ func (h *Handler) applyStructuredPatch(action string, pathParams map[string]stri
return body, nil
}
- out := make(map[string]json.RawMessage, len(rest)+len(ops))
+ out := make(map[string]jsontext.Value, len(rest)+len(ops))
maps.Copy(out, rest)
for _, op := range ops {
@@ -350,7 +353,7 @@ func (h *Handler) applyStructuredPatch(action string, pathParams map[string]stri
applyTopLevelPatchOp(action, op, out)
}
- raw, err := json.Marshal(out)
+ raw, err := json.Marshal(out, jsonx.V1())
if patchIgnorableErr(err) {
return body, nil
}
@@ -385,7 +388,7 @@ func unmodeledPatchPath(action, path string) error {
// "not provided" from "reset to zero value" — a much larger refactor across
// every resource's Update backend method than this pass's PATCH-semantics
// fix covers in full. Tracked as a follow-up (see PARITY.md gaps).
-func applyTopLevelPatchOp(action string, op patchOp, out map[string]json.RawMessage) {
+func applyTopLevelPatchOp(action string, op patchOp, out map[string]jsontext.Value) {
field := strings.TrimPrefix(op.Path, "/")
if field == "" || strings.Contains(field, "/") {
return
@@ -396,7 +399,7 @@ func applyTopLevelPatchOp(action string, op patchOp, out map[string]json.RawMess
out[field] = coerceTopLevelPatchValue(field, op.Value)
case patchOpRemove:
if removableTopLevelScalar[action][field] {
- out[field] = json.RawMessage(`""`)
+ out[field] = jsontext.Value(`""`)
}
}
}
@@ -408,7 +411,7 @@ func applyTopLevelPatchOp(action string, op patchOp, out map[string]json.RawMess
// is one this action must reject, or (false, nil) to fall through to the
// generic top-level fallback.
type resourcePatchResolver func(
- h *Handler, pathParams map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ h *Handler, pathParams map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) (bool, error)
// resourcePatchResolvers maps each action with map/struct/list/rejectable
@@ -440,7 +443,7 @@ var resourcePatchResolvers = map[string]resourcePatchResolver{
// single-field replace cannot express. See resourcePatchResolver's doc
// comment for the return-value contract.
func (h *Handler) applyResourcePatchOp(
- action string, pathParams map[string]string, op patchOp, out map[string]json.RawMessage,
+ action string, pathParams map[string]string, op patchOp, out map[string]jsontext.Value,
) (bool, error) {
segs := patchPathSegments(op.Path)
if len(segs) == 0 {
@@ -460,7 +463,7 @@ func (h *Handler) applyResourcePatchOp(
// stage-variable edits, single canarySettings/accessLogSettings sub-fields,
// and per-route method settings.
func (h *Handler) applyStagePatchOp(
- pathParams map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) (bool, error) {
if op.Op == patchOpCopy && op.Path == "/deploymentId" && op.From == "/canarySettings/deploymentId" {
stage, err := h.Backend.GetStage(pathParams[keyRestAPIID], pathParams[keyStageName])
@@ -499,7 +502,7 @@ func (h *Handler) applyStagePatchOp(
// with whatever an earlier op in the SAME request already staged into
// out["variables"] (see stagedValue's doc comment).
func (h *Handler) applyStageVariablePatch(
- pathParams map[string]string, op patchOp, rawName string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, rawName string, out map[string]jsontext.Value,
) bool {
if op.Op != patchOpAdd && op.Op != patchOpReplace && op.Op != patchOpRemove {
return false
@@ -534,7 +537,7 @@ func (h *Handler) applyStageVariablePatch(
// with the stage's existing canary settings and whatever an earlier op in the
// SAME request already staged into out["canarySettings"].
func (h *Handler) applyStageCanaryPatch(
- pathParams map[string]string, op patchOp, prop string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, prop string, out map[string]jsontext.Value,
) bool {
if op.Op != patchOpAdd && op.Op != patchOpReplace && op.Op != patchOpRemove {
return false
@@ -595,7 +598,7 @@ func applyStageCanaryProp(cur *CanarySettings, prop, val string, remove bool) bo
// contents are themselves a JSON-encoded {name: value} object.
if !remove {
var overrides map[string]string
- if json.Unmarshal([]byte(val), &overrides) == nil {
+ if json.Unmarshal([]byte(val), &overrides, jsonx.V1()) == nil {
cur.StageVariableOverrides = overrides
}
}
@@ -611,7 +614,7 @@ func applyStageCanaryProp(cur *CanarySettings, prop, val string, remove bool) bo
// existing access log settings and whatever an earlier op in the SAME
// request already staged into out["accessLogSettings"].
func (h *Handler) applyStageAccessLogPatch(
- pathParams map[string]string, op patchOp, prop string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, prop string, out map[string]jsontext.Value,
) bool {
if op.Op != patchOpAdd && op.Op != patchOpReplace && op.Op != patchOpRemove {
return false
@@ -737,7 +740,7 @@ func parseBoolLenient(v string) bool {
// (e.g. "/~1pets/GET/throttling/burstLimit" or "/*/*/logging/loglevel") —
// merged with the stage's existing per-route overrides.
func (h *Handler) applyStageMethodSettingPatch(
- pathParams map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) bool {
apply, known := stageMethodSettingProperty[strings.Join(segs[2:], "/")]
if !known || (op.Op != patchOpAdd && op.Op != patchOpReplace && op.Op != patchOpRemove) {
@@ -773,7 +776,7 @@ func (h *Handler) applyStageMethodSettingPatch(
// silently drop every other configured type) and whatever an earlier op in
// the SAME request already staged into out["binaryMediaTypes"].
func (h *Handler) applyRestAPIPatchOp(
- pathParams map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) (bool, error) {
if len(segs) != patchPathSegs2 || segs[0] != "binaryMediaTypes" {
return false, nil
@@ -823,7 +826,7 @@ const accountFeatureUsagePlans = "UsagePlans"
// membership. "/cloudwatchRoleArn" is a plain top-level string field and is
// handled by the generic fallback (applyTopLevelPatchOp).
func (h *Handler) applyAccountPatchOp(
- _ map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ _ map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) (bool, error) {
if len(segs) == 1 && segs[0] == "features" {
return h.applyAccountFeaturesPatch(op, out)
@@ -892,7 +895,7 @@ func applyAccountThrottleProp(cur *ThrottleSettings, prop, val string, remove bo
// removing accountFeatureUsagePlans, matching patch-operations.html's
// documented exception, and any op other than add/remove (replace is "Not
// supported").
-func (h *Handler) applyAccountFeaturesPatch(op patchOp, out map[string]json.RawMessage) (bool, error) {
+func (h *Handler) applyAccountFeaturesPatch(op patchOp, out map[string]jsontext.Value) (bool, error) {
if op.Op != patchOpAdd && op.Op != patchOpRemove {
return true, unsupportedPatchOp("/features", op.Op)
}
@@ -941,7 +944,7 @@ func (h *Handler) applyAccountFeaturesPatch(op patchOp, out map[string]json.RawM
// earlier op in this request has touched apiStages yet. ok is false only
// when the plan itself can't be resolved.
func (h *Handler) currentUsagePlanAPIStages(
- pathParams map[string]string, out map[string]json.RawMessage,
+ pathParams map[string]string, out map[string]jsontext.Value,
) ([]APIStageAssociation, bool) {
if staged, ok := stagedValue[[]APIStageAssociation](out, "apiStages"); ok {
return staged, true
@@ -962,7 +965,7 @@ func (h *Handler) currentUsagePlanAPIStages(
// applyUsagePlanThrottlePatch) — see the AWS "UpdateUsagePlan" patch-operations
// reference.
func (h *Handler) applyUsagePlanPatchOp(
- pathParams map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) (bool, error) {
if len(segs) == 1 && segs[0] == "apiStages" {
return h.applyUsagePlanAPIStageMembershipPatch(pathParams, op, out), nil
@@ -978,7 +981,7 @@ func (h *Handler) applyUsagePlanPatchOp(
// applyUsagePlanAPIStageMembershipPatch handles the whole-apiStage add/remove
// case ("/apiStages", Value "{restApiId}:{stage}").
func (h *Handler) applyUsagePlanAPIStageMembershipPatch(
- pathParams map[string]string, op patchOp, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, out map[string]jsontext.Value,
) bool {
if op.Op != patchOpAdd && op.Op != patchOpRemove {
return false
@@ -1038,7 +1041,7 @@ const usagePlanThrottlePathMinSegs = 5
// APIStageAssociation.Throttle elsewhere in this service (see usage.go's
// effectiveThrottle and CreateUsagePlan's test fixtures).
func (h *Handler) applyUsagePlanThrottlePatch(
- pathParams map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) bool {
if len(segs) < usagePlanThrottlePathMinSegs || len(segs) > usagePlanThrottlePathMinSegs+1 {
return false
@@ -1139,7 +1142,7 @@ func (h *Handler) mergeUsagePlanThrottleEntry(
// default response when none has been customized yet) and whatever an
// earlier op in the SAME request already staged into out[segs[0]].
func (h *Handler) applyGatewayResponsePatchOp(
- pathParams map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) (bool, error) {
if len(segs) != patchPathSegs2 || (segs[0] != fieldResponseParameters && segs[0] != "responseTemplates") {
return false, nil
@@ -1185,7 +1188,7 @@ func (h *Handler) applyGatewayResponsePatchOp(
// StageKeys (a wholesale replace would otherwise silently drop every other
// associated stage).
func (h *Handler) applyAPIKeyPatchOp(
- pathParams map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) (bool, error) {
if len(segs) != 1 || segs[0] != "stages" {
return false, nil
@@ -1236,7 +1239,7 @@ func (h *Handler) applyAPIKeyPatchOp(
// had no case for opUpdateDomainName at all, so every nested DomainName PATCH
// path silently no-opped via applyTopLevelPatchOp's path-contains-"/" guard.
func (h *Handler) applyDomainNamePatchOp(
- pathParams map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) (bool, error) {
if len(segs) != patchPathSegs2 {
return false, nil
@@ -1258,7 +1261,7 @@ func (h *Handler) applyDomainNamePatchOp(
// and whatever an earlier op in the SAME request already staged into
// out["endpointConfiguration"].
func (h *Handler) applyDomainNameEndpointConfigPatch(
- pathParams map[string]string, op patchOp, prop string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, prop string, out map[string]jsontext.Value,
) bool {
cur, ok := stagedValue[EndpointConfiguration](out, "endpointConfiguration")
if !ok {
@@ -1310,7 +1313,7 @@ func (h *Handler) applyDomainNameEndpointConfigPatch(
// with the domain name's existing mTLS configuration and whatever an earlier
// op in the SAME request already staged into out["mutualTlsAuthentication"].
func (h *Handler) applyDomainNameMTLSPatch(
- pathParams map[string]string, op patchOp, prop string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, prop string, out map[string]jsontext.Value,
) bool {
if op.Op != patchOpAdd && op.Op != patchOpReplace && op.Op != patchOpRemove {
return false
@@ -1354,7 +1357,7 @@ func (h *Handler) applyDomainNameMTLSPatch(
// patch-operations.html's UpdateResource table documents both paths as
// replace-only.
func (h *Handler) applyResourceEntityPatchOp(
- _ map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ _ map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) (bool, error) {
if len(segs) != 1 || segs[0] != "parentId" {
return false, nil
@@ -1398,7 +1401,7 @@ func (h *Handler) applyResourceEntityPatchOp(
// decision). "/restApiId" and "/stage" are spelled identically in every AWS
// source and already work via the generic fallback (applyTopLevelPatchOp).
func (h *Handler) applyBasePathMappingPatchOp(
- _ map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ _ map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) (bool, error) {
if len(segs) != 1 {
return false, nil
@@ -1431,7 +1434,7 @@ func (h *Handler) applyBasePathMappingPatchOp(
// json.Unmarshal then failed the WHOLE PATCH request with a 500 (same bug
// class as UpdateIntegration's /cacheKeyParameters).
func (h *Handler) applyAuthorizerPatchOp(
- pathParams map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) (bool, error) {
if len(segs) != 1 || segs[0] != "providerARNs" {
return false, nil
@@ -1484,7 +1487,7 @@ func (h *Handler) applyAuthorizerPatchOp(
// requestValidatorId) are plain replace-only fields left to the generic
// fallback.
func (h *Handler) applyMethodPatchOp(
- pathParams map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) (bool, error) {
if len(segs) == 1 && segs[0] == "authorizationScopes" {
return true, unmodeledPatchPath(opUpdateMethod, op.Path)
@@ -1510,7 +1513,7 @@ func (h *Handler) applyMethodPatchOp(
// silently drop every other parameter) and whatever an earlier op in the SAME
// request already staged into out["requestParameters"].
func (h *Handler) applyMethodRequestParameterPatch(
- pathParams map[string]string, op patchOp, rawName string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, rawName string, out map[string]jsontext.Value,
) (bool, error) {
if op.Op != patchOpAdd && op.Op != patchOpReplace && op.Op != patchOpRemove {
return true, unsupportedPatchOp("/requestParameters", op.Op)
@@ -1546,7 +1549,7 @@ func (h *Handler) applyMethodRequestParameterPatch(
// existing RequestModels and whatever an earlier op in the SAME request
// already staged into out["requestModels"].
func (h *Handler) applyMethodRequestModelPatch(
- pathParams map[string]string, op patchOp, rawContentType string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, rawContentType string, out map[string]jsontext.Value,
) (bool, error) {
if op.Op != patchOpAdd && op.Op != patchOpReplace && op.Op != patchOpRemove {
return true, unsupportedPatchOp("/requestModels", op.Op)
@@ -1587,7 +1590,7 @@ func (h *Handler) applyMethodRequestModelPatch(
// connectionId, connectionType, contentHandling, httpMethod, passthroughBehavior,
// uri, timeoutInMillis) are plain replace-only fields left to the generic fallback.
func (h *Handler) applyIntegrationPatchOp(
- pathParams map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) (bool, error) {
if len(segs) == 1 {
return h.applyIntegrationTopLevelPatchOp(pathParams, op, segs[0], out)
@@ -1613,7 +1616,7 @@ func (h *Handler) applyIntegrationPatchOp(
// PATCH paths that need explicit rejection or list-membership merging rather
// than the generic scalar fallback.
func (h *Handler) applyIntegrationTopLevelPatchOp(
- pathParams map[string]string, op patchOp, field string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, field string, out map[string]jsontext.Value,
) (bool, error) {
switch field {
case "type":
@@ -1636,7 +1639,7 @@ func (h *Handler) applyIntegrationTopLevelPatchOp(
// matching this file's other single-segment list-membership paths
// (UpdateApiKey's "/stages", UpdateRestApi's "/binaryMediaTypes").
func (h *Handler) applyIntegrationCacheKeyParametersPatch(
- pathParams map[string]string, op patchOp, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, out map[string]jsontext.Value,
) (bool, error) {
if op.Op != patchOpAdd && op.Op != patchOpReplace && op.Op != patchOpRemove {
return true, unsupportedPatchOp("/cacheKeyParameters", op.Op)
@@ -1681,7 +1684,7 @@ func (h *Handler) applyIntegrationCacheKeyParametersPatch(
// with the integration's existing map and whatever an earlier op in the SAME
// request already staged into out[field].
func (h *Handler) applyIntegrationMapPatch(
- pathParams map[string]string, op patchOp, field, rawKey string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, field, rawKey string, out map[string]jsontext.Value,
) (bool, error) {
if op.Op != patchOpAdd && op.Op != patchOpReplace && op.Op != patchOpRemove {
return true, unsupportedPatchOp("/"+field, op.Op)
@@ -1722,7 +1725,7 @@ func (h *Handler) applyIntegrationMapPatch(
// edits. "/contentHandling" and "/selectionPattern" are plain top-level
// replace-only fields left to the generic fallback.
func (h *Handler) applyIntegrationResponsePatchOp(
- pathParams map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) (bool, error) {
if len(segs) != patchPathSegs2 {
return false, nil
@@ -1743,7 +1746,7 @@ func (h *Handler) applyIntegrationResponsePatchOp(
// merging with the integration response's existing map and whatever an
// earlier op in the SAME request already staged into out[field].
func (h *Handler) applyIntegrationResponseMapPatch(
- pathParams map[string]string, op patchOp, field, rawKey string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, field, rawKey string, out map[string]jsontext.Value,
) (bool, error) {
if op.Op != patchOpAdd && op.Op != patchOpReplace && op.Op != patchOpRemove {
return true, unsupportedPatchOp("/"+field, op.Op)
@@ -1782,7 +1785,7 @@ func (h *Handler) applyIntegrationResponseMapPatch(
// applyMethodResponsePatchOp handles UpdateMethodResponse's per-key
// "/responseModels/{content-type}" and "/responseParameters/{name}" map edits.
func (h *Handler) applyMethodResponsePatchOp(
- pathParams map[string]string, op patchOp, segs []string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, segs []string, out map[string]jsontext.Value,
) (bool, error) {
if len(segs) != patchPathSegs2 {
return false, nil
@@ -1803,7 +1806,7 @@ func (h *Handler) applyMethodResponsePatchOp(
// method response's existing ResponseModels and whatever an earlier op in the
// SAME request already staged into out["responseModels"].
func (h *Handler) applyMethodResponseModelPatch(
- pathParams map[string]string, op patchOp, rawContentType string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, rawContentType string, out map[string]jsontext.Value,
) (bool, error) {
if op.Op != patchOpAdd && op.Op != patchOpReplace && op.Op != patchOpRemove {
return true, unsupportedPatchOp("/responseModels", op.Op)
@@ -1840,7 +1843,7 @@ func (h *Handler) applyMethodResponseModelPatch(
// method response's existing ResponseParameters and whatever an earlier op in
// the SAME request already staged into out["responseParameters"].
func (h *Handler) applyMethodResponseParameterPatch(
- pathParams map[string]string, op patchOp, rawName string, out map[string]json.RawMessage,
+ pathParams map[string]string, op patchOp, rawName string, out map[string]jsontext.Value,
) (bool, error) {
if op.Op != patchOpAdd && op.Op != patchOpReplace && op.Op != patchOpRemove {
return true, unsupportedPatchOp("/responseParameters", op.Op)
diff --git a/services/apigateway/persistence.go b/services/apigateway/persistence.go
index 638d739a53..bd9e89b343 100644
--- a/services/apigateway/persistence.go
+++ b/services/apigateway/persistence.go
@@ -2,7 +2,7 @@ package apigateway
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -414,7 +414,7 @@ var dirtyTableNames = struct {
// registerAllTables doc for the clean/dirty split), merged into one map so a
// single Tables blob round-trips the whole backend.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Account *Account `json:"account,omitempty"`
UsageOverrides map[string]map[string]int64 `json:"usageOverrides,omitempty"`
Version int `json:"version"`
diff --git a/services/apigateway/proxy.go b/services/apigateway/proxy.go
index bcce84fde1..bb7210f8ed 100644
--- a/services/apigateway/proxy.go
+++ b/services/apigateway/proxy.go
@@ -3,7 +3,8 @@ package apigateway
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -14,6 +15,7 @@ import (
"github.com/golang-jwt/jwt/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -410,7 +412,7 @@ func writeThrottleResponse(w http.ResponseWriter, errorType, message string) {
w.Header().Set(headerContentType, "application/json")
w.Header().Set("X-Amzn-Errortype", errorType)
w.WriteHeader(http.StatusTooManyRequests)
- _ = json.NewEncoder(w).Encode(map[string]string{jsonMessageKey: message})
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), map[string]string{jsonMessageKey: message})
}
// writeMissingAuthenticationTokenResponse writes AWS API Gateway's real response for a
@@ -423,7 +425,10 @@ func writeMissingAuthenticationTokenResponse(w http.ResponseWriter) {
w.Header().Set(headerContentType, "application/json")
w.Header().Set("X-Amzn-Errortype", "MissingAuthenticationTokenException")
w.WriteHeader(http.StatusForbidden)
- _ = json.NewEncoder(w).Encode(map[string]string{jsonMessageKey: "Missing Authentication Token"})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]string{jsonMessageKey: "Missing Authentication Token"},
+ )
}
// dispatchIntegration routes the request to the appropriate integration handler.
diff --git a/services/apigateway/proxy_authorizer.go b/services/apigateway/proxy_authorizer.go
index 0c96694f72..28470245db 100644
--- a/services/apigateway/proxy_authorizer.go
+++ b/services/apigateway/proxy_authorizer.go
@@ -3,7 +3,8 @@ package apigateway
import (
"container/list"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -16,6 +17,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -162,7 +164,7 @@ func authorizerTTL(auth *Authorizer) time.Duration {
func writeAuthorizerError(w http.ResponseWriter, status int, message string) {
w.Header().Set(headerContentType, "application/json")
w.WriteHeader(status)
- _ = json.NewEncoder(w).Encode(map[string]string{jsonMessageKey: message})
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), map[string]string{jsonMessageKey: message})
}
// runAuthorizer enforces the method's authorizer and returns true if the request
@@ -219,7 +221,7 @@ func (h *Handler) runAuthorizer(
event := h.buildAuthorizerEvent(ctx, r, auth, apiID, stageName)
event.MethodArn = methodArn
- payload, _ := json.Marshal(event)
+ payload, _ := json.Marshal(event, jsonx.V1())
funcName := ExtractLambdaFunctionName(auth.AuthorizerURI)
respBytes, _, invokeErr := h.lambda.InvokeFunction(ctx, funcName, "RequestResponse", payload)
@@ -232,7 +234,7 @@ func (h *Handler) runAuthorizer(
}
var authResp AuthorizerResponse
- if parseErr := json.Unmarshal(respBytes, &authResp); parseErr != nil {
+ if parseErr := json.Unmarshal(respBytes, &authResp, jsonx.V1()); parseErr != nil {
logger.Load(ctx).WarnContext(ctx, "APIGateway proxy: failed to parse authorizer response", "error", parseErr)
writeAuthorizerError(w, http.StatusUnauthorized, msgUnauthorized)
diff --git a/services/apigateway/proxy_authorizer_cache_internal_test.go b/services/apigateway/proxy_authorizer_cache_internal_test.go
index b793aa2be4..0b5deb1697 100644
--- a/services/apigateway/proxy_authorizer_cache_internal_test.go
+++ b/services/apigateway/proxy_authorizer_cache_internal_test.go
@@ -2,7 +2,7 @@ package apigateway
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"sync/atomic"
@@ -12,6 +12,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// policyInvoker allows only callers whose "auth" header (or token) is in allowed, for allowedARN.
@@ -25,7 +27,7 @@ func (p *policyInvoker) InvokeFunction(_ context.Context, _, _ string, payload [
p.calls.Add(1)
var ev AuthorizerEvent
- if err := json.Unmarshal(payload, &ev); err != nil {
+ if err := json.Unmarshal(payload, &ev, jsonx.V1()); err != nil {
return nil, http.StatusInternalServerError, err
}
@@ -49,7 +51,7 @@ func (p *policyInvoker) InvokeFunction(_ context.Context, _, _ string, payload [
PolicyDocument: &PolicyDocument{Statement: []PolicyStatement{
{Effect: effect, Action: "execute-api:Invoke", Resource: res},
}},
- })
+ }, jsonx.V1())
return body, http.StatusOK, nil
}
diff --git a/services/apigateway/proxy_authorizer_test.go b/services/apigateway/proxy_authorizer_test.go
index 0a90c96f6f..b47bd5fa19 100644
--- a/services/apigateway/proxy_authorizer_test.go
+++ b/services/apigateway/proxy_authorizer_test.go
@@ -2,7 +2,7 @@ package apigateway_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -53,13 +54,13 @@ func setupAuthorizerAPI(
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
apiID := createResp["id"].(string)
// Get root resource.
listRec := postWithHandler(t, h, e, "GetResources", `{"restApiId":"`+apiID+`"}`)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
rootID := listResp["item"].([]any)[0].(map[string]any)["id"].(string)
// Create /secure resource.
@@ -68,7 +69,7 @@ func setupAuthorizerAPI(
require.Equal(t, http.StatusCreated, childRec.Code)
var childResp map[string]any
- require.NoError(t, json.Unmarshal(childRec.Body.Bytes(), &childResp))
+ require.NoError(t, json.Unmarshal(childRec.Body.Bytes(), &childResp, jsonx.V1()))
childID := childResp["id"].(string)
// Create authorizer.
@@ -83,7 +84,7 @@ func setupAuthorizerAPI(
require.Equal(t, http.StatusCreated, authRec.Code)
var authResp map[string]any
- require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp))
+ require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp, jsonx.V1()))
authID := authResp["id"].(string)
// PutMethod with authorizerId.
@@ -280,7 +281,7 @@ func TestRunAuthorizer_MethodArn_ResourcePath(t *testing.T) {
// Verify the methodArn in the captured event uses the stripped resource path ("/secure"),
// not the full proxy path ("/restapis/.../prod/_user_request_/secure").
var event map[string]any
- require.NoError(t, json.Unmarshal(capturedPayload, &event))
+ require.NoError(t, json.Unmarshal(capturedPayload, &event, jsonx.V1()))
methodArn, _ := event["methodArn"].(string)
assert.Contains(t, methodArn, "/secure", "methodArn should contain the stripped path")
assert.NotContains(t, methodArn, "_user_request_", "methodArn must not contain internal proxy prefix")
@@ -856,12 +857,12 @@ func TestRunCognitoAuthorizer_ValidToken(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
apiID := createResp["id"].(string)
listRec := postWithHandler(t, h, e, "GetResources", `{"restApiId":"`+apiID+`"}`)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
rootID := listResp["item"].([]any)[0].(map[string]any)["id"].(string)
childRec := postWithHandler(t, h, e, "CreateResource",
@@ -869,7 +870,7 @@ func TestRunCognitoAuthorizer_ValidToken(t *testing.T) {
require.Equal(t, http.StatusCreated, childRec.Code)
var childResp map[string]any
- require.NoError(t, json.Unmarshal(childRec.Body.Bytes(), &childResp))
+ require.NoError(t, json.Unmarshal(childRec.Body.Bytes(), &childResp, jsonx.V1()))
childID := childResp["id"].(string)
authBody := `{
@@ -882,7 +883,7 @@ func TestRunCognitoAuthorizer_ValidToken(t *testing.T) {
require.Equal(t, http.StatusCreated, authRec.Code)
var authResp map[string]any
- require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp))
+ require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp, jsonx.V1()))
authID := authResp["id"].(string)
putMethodBody := `{"restApiId":"` + apiID + `","resourceId":"` + childID +
diff --git a/services/apigateway/proxy_integrations.go b/services/apigateway/proxy_integrations.go
index 9ccf56b2c7..6883f14023 100644
--- a/services/apigateway/proxy_integrations.go
+++ b/services/apigateway/proxy_integrations.go
@@ -4,7 +4,8 @@ import (
"container/list"
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"mime"
@@ -16,6 +17,7 @@ import (
"sync"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -43,7 +45,7 @@ func (h *Handler) handleAWSProxy(
return
}
- payload, _ := json.Marshal(event)
+ payload, _ := json.Marshal(event, jsonx.V1())
respBytes, _, invokeErr := h.lambda.InvokeFunction(
ctx,
@@ -315,7 +317,10 @@ func writeAWSIntegrationReadError(ctx context.Context, w http.ResponseWriter, re
w.Header().Set(headerContentType, contentTypeJSON)
w.Header().Set("X-Amzn-Errortype", "UnsupportedMediaTypeException")
w.WriteHeader(http.StatusUnsupportedMediaType)
- _ = json.NewEncoder(w).Encode(map[string]string{jsonMessageKey: "Unsupported Media Type"})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]string{jsonMessageKey: "Unsupported Media Type"},
+ )
return
}
@@ -983,7 +988,10 @@ func writeInternalServerError(w http.ResponseWriter, status int) {
w.Header().Set(headerContentType, "application/json")
w.Header().Set("X-Amzn-Errortype", "InternalServerErrorException")
w.WriteHeader(status)
- _ = json.NewEncoder(w).Encode(map[string]string{jsonMessageKey: "Internal server error"})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]string{jsonMessageKey: "Internal server error"},
+ )
}
var errMalformedProxyResponse = errors.New("malformed Lambda proxy response")
@@ -997,8 +1005,8 @@ const (
// way execute-api does: a JSON object with an integer statusCode, and
// string-valued headers and body.
func parseLambdaProxyResponse(raw []byte) (*LambdaProxyResponse, error) {
- var obj map[string]json.RawMessage
- if err := json.Unmarshal(raw, &obj); err != nil || obj == nil {
+ var obj map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &obj, jsonx.V1()); err != nil || obj == nil {
return nil, errMalformedProxyResponse
}
@@ -1009,14 +1017,14 @@ func parseLambdaProxyResponse(raw []byte) (*LambdaProxyResponse, error) {
return nil, errMalformedProxyResponse
}
- var n json.Number
- if err := json.Unmarshal(code, &n); err != nil {
+ var n jsonx.Number
+ if err := json.Unmarshal(code, &n, jsonx.V1()); err != nil {
var str string
- if json.Unmarshal(code, &str) != nil {
+ if json.Unmarshal(code, &str, jsonx.V1()) != nil {
return nil, errMalformedProxyResponse
}
- n = json.Number(str)
+ n = jsonx.Number(str)
}
status, err := strconv.Atoi(n.String())
@@ -1045,13 +1053,13 @@ func parseLambdaProxyResponse(raw []byte) (*LambdaProxyResponse, error) {
return &resp, nil
}
-func decodeProxyField(obj map[string]json.RawMessage, key string, dst any) error {
+func decodeProxyField(obj map[string]jsontext.Value, key string, dst any) error {
raw, ok := obj[key]
if !ok || string(raw) == jsonNull {
return nil
}
- if err := json.Unmarshal(raw, dst); err != nil {
+ if err := json.Unmarshal(raw, dst, jsonx.V1()); err != nil {
return errMalformedProxyResponse
}
diff --git a/services/apigateway/proxy_mapping_test.go b/services/apigateway/proxy_mapping_test.go
index 419b1564d6..ede2c06342 100644
--- a/services/apigateway/proxy_mapping_test.go
+++ b/services/apigateway/proxy_mapping_test.go
@@ -2,7 +2,7 @@ package apigateway_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -45,14 +46,14 @@ func setupMappingAPI(
rec := postWithHandler(t, h, e, "CreateRestApi", `{"name":"map-api"}`)
require.Equal(t, http.StatusCreated, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
apiID := created["id"].(string)
rec = postWithHandler(t, h, e, "GetResources", `{"restApiId":"`+apiID+`"}`)
var list map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &list))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &list, jsonx.V1()))
rootID := list["item"].([]any)[0].(map[string]any)["id"].(string)
rec = postWithHandler(t, h, e, "CreateResource",
@@ -61,7 +62,7 @@ func setupMappingAPI(
var child map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &child))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &child, jsonx.V1()))
resID := child["id"].(string)
base := `"restApiId":"` + apiID + `","resourceId":"` + resID + `","httpMethod":"POST"`
diff --git a/services/apigateway/proxy_test.go b/services/apigateway/proxy_test.go
index 86d2a91b89..c6bdf3755f 100644
--- a/services/apigateway/proxy_test.go
+++ b/services/apigateway/proxy_test.go
@@ -2,7 +2,7 @@ package apigateway_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -70,7 +71,7 @@ func setupProxyAPIViaHandler(
require.Equal(tb, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(tb, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(tb, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
apiID := createResp["id"].(string)
// Get root resource.
@@ -78,7 +79,7 @@ func setupProxyAPIViaHandler(
require.Equal(tb, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(tb, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(tb, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
rootID := listResp["item"].([]any)[0].(map[string]any)["id"].(string)
// Create child resource.
@@ -87,7 +88,7 @@ func setupProxyAPIViaHandler(
require.Equal(tb, http.StatusCreated, childRec.Code)
var childResp map[string]any
- require.NoError(tb, json.Unmarshal(childRec.Body.Bytes(), &childResp))
+ require.NoError(tb, json.Unmarshal(childRec.Body.Bytes(), &childResp, jsonx.V1()))
childID := childResp["id"].(string)
// PutMethod.
@@ -270,18 +271,18 @@ func setupVTLAPI(t *testing.T) (*apigateway.Handler, *echo.Echo, string) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
apiID := createResp["id"].(string)
listRec := postWithHandler(t, h, e, "GetResources", `{"restApiId":"`+apiID+`"}`)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
rootID := listResp["item"].([]any)[0].(map[string]any)["id"].(string)
childRec := postWithHandler(t, h, e, "CreateResource",
`{"restApiId":"`+apiID+`","parentId":"`+rootID+`","pathPart":"transform"}`)
var childResp map[string]any
- require.NoError(t, json.Unmarshal(childRec.Body.Bytes(), &childResp))
+ require.NoError(t, json.Unmarshal(childRec.Body.Bytes(), &childResp, jsonx.V1()))
childID := childResp["id"].(string)
postWithHandler(t, h, e, "PutMethod",
@@ -332,7 +333,7 @@ func TestHandleAWSIntegration(t *testing.T) {
checkCapture: func(t *testing.T, captured []byte) {
t.Helper()
var got map[string]any
- require.NoError(t, json.Unmarshal(captured, &got))
+ require.NoError(t, json.Unmarshal(captured, &got, jsonx.V1()))
assert.Equal(t, "alice", got["name"])
},
},
@@ -484,14 +485,14 @@ func setupProxyAPIWithResource(
createRec := postWithHandler(t, h, e, "CreateRestApi", `{"name":"path-api","description":"test"}`)
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
apiID := createResp["id"].(string)
// Get root resource ID by finding the resource with path "/".
listRec := postWithHandler(t, h, e, "GetResources", `{"restApiId":"`+apiID+`"}`)
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
items, _ := listResp["item"].([]any)
var parentID string
@@ -519,7 +520,7 @@ func setupProxyAPIWithResource(
`{"restApiId":"`+apiID+`","parentId":"`+parentID+`","pathPart":"`+part+`"}`)
require.Equal(t, http.StatusCreated, r.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(r.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(r.Body.Bytes(), &resp, jsonx.V1()))
parentID = resp["id"].(string)
}
}
@@ -635,7 +636,7 @@ func TestPathVariableMatching(t *testing.T) {
checkEvent: func(t *testing.T, payload []byte) {
t.Helper()
var event map[string]any
- require.NoError(t, json.Unmarshal(payload, &event))
+ require.NoError(t, json.Unmarshal(payload, &event, jsonx.V1()))
params, _ := event["pathParameters"].(map[string]any)
assert.Equal(t, "42", params["id"])
},
@@ -648,7 +649,7 @@ func TestPathVariableMatching(t *testing.T) {
checkEvent: func(t *testing.T, payload []byte) {
t.Helper()
var event map[string]any
- require.NoError(t, json.Unmarshal(payload, &event))
+ require.NoError(t, json.Unmarshal(payload, &event, jsonx.V1()))
params, _ := event["pathParameters"].(map[string]any)
assert.Equal(t, "/a/b/c", params["proxy"])
},
@@ -891,20 +892,20 @@ func TestHandleProxyRequest_RequiresDeployedStage(t *testing.T) {
createRec := postWithHandler(t, h, e, "CreateRestApi", `{"name":"gate-api"}`)
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
apiID := createResp["id"].(string)
listRec := postWithHandler(t, h, e, "GetResources", `{"restApiId":"`+apiID+`"}`)
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
rootID := listResp["item"].([]any)[0].(map[string]any)["id"].(string)
childRec := postWithHandler(t, h, e, "CreateResource",
`{"restApiId":"`+apiID+`","parentId":"`+rootID+`","pathPart":"items"}`)
require.Equal(t, http.StatusCreated, childRec.Code)
var childResp map[string]any
- require.NoError(t, json.Unmarshal(childRec.Body.Bytes(), &childResp))
+ require.NoError(t, json.Unmarshal(childRec.Body.Bytes(), &childResp, jsonx.V1()))
childID := childResp["id"].(string)
methodRec := postWithHandler(t, h, e, "PutMethod",
diff --git a/services/apigateway/proxy_validation.go b/services/apigateway/proxy_validation.go
index a1040aaaf9..900d019f8d 100644
--- a/services/apigateway/proxy_validation.go
+++ b/services/apigateway/proxy_validation.go
@@ -2,13 +2,15 @@ package apigateway
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/url"
"sort"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -72,7 +74,7 @@ func (h *Handler) validateRequestBody(
// Replace body so downstream handlers can still read it.
r.Body = io.NopCloser(strings.NewReader(string(bodyBytes)))
- if len(bodyBytes) == 0 || !json.Valid(bodyBytes) {
+ if len(bodyBytes) == 0 || !jsontext.Value(bodyBytes).IsValid(jsonx.V1()) {
writeValidationError(w, "Invalid request body")
return true
@@ -130,7 +132,7 @@ func writeValidationError(w http.ResponseWriter, message string) {
w.Header().Set(headerContentType, "application/json")
w.Header().Set("X-Amzn-Errortype", "BadRequestException")
w.WriteHeader(http.StatusBadRequest)
- _ = json.NewEncoder(w).Encode(map[string]string{jsonMessageKey: message})
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), map[string]string{jsonMessageKey: message})
}
// missingRequiredParameters returns the names of required request parameters (as
diff --git a/services/apigateway/proxy_validation_test.go b/services/apigateway/proxy_validation_test.go
index 7e548fb38a..c6427c69af 100644
--- a/services/apigateway/proxy_validation_test.go
+++ b/services/apigateway/proxy_validation_test.go
@@ -4,7 +4,7 @@ import (
"compress/gzip"
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -616,7 +617,7 @@ func TestProxy_StageVariables_InterpolatedInURI(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
// The invoker receives the payload; check the captured function name via the event.
var event map[string]any
- require.NoError(t, json.Unmarshal(capturedPayload, &event))
+ require.NoError(t, json.Unmarshal(capturedPayload, &event, jsonx.V1()))
// Response comes from captureInvoker which echoes the payload back, so status is 200.
// The important check is that the handler resolved the stage variable without panicking.
}
@@ -902,7 +903,7 @@ func TestProxy_AuthorizerCacheKey_IdentityValidationExpression(t *testing.T) {
authRec := postWithHandler(t, h, e, "CreateAuthorizer", authBody)
require.Equal(t, http.StatusCreated, authRec.Code)
var auth map[string]any
- require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &auth))
+ require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &auth, jsonx.V1()))
authID := auth["id"].(string)
_, _ = backend.PutMethod(apigateway.PutMethodInput{
diff --git a/services/apigateway/request_validators_test.go b/services/apigateway/request_validators_test.go
index 1eac7c110d..2f750b2761 100644
--- a/services/apigateway/request_validators_test.go
+++ b/services/apigateway/request_validators_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -46,7 +47,7 @@ func TestUpdateRequestValidator(t *testing.T) {
fmt.Sprintf(`{"restApiId":%q,"name":"orig-validator"}`, apiID))
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
validatorID := createResp["id"].(string)
lookupID := validatorID
diff --git a/services/apigateway/resources_test.go b/services/apigateway/resources_test.go
index 269a48cbe4..20e5cd6053 100644
--- a/services/apigateway/resources_test.go
+++ b/services/apigateway/resources_test.go
@@ -1,7 +1,8 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -46,7 +48,7 @@ func TestGetResources_NoPositionOnLastPage(t *testing.T) {
require.True(t, apiRec.Code >= 200 && apiRec.Code < 300)
var apiResp map[string]any
- require.NoError(t, json.NewDecoder(apiRec.Body).Decode(&apiResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(apiRec.Body), &apiResp, jsonx.V1()))
apiID := apiResp["id"].(string)
require.NotEmpty(t, apiID)
@@ -55,7 +57,7 @@ func TestGetResources_NoPositionOnLastPage(t *testing.T) {
require.True(t, resRec.Code >= 200 && resRec.Code < 300)
var resResp map[string]any
- require.NoError(t, json.NewDecoder(resRec.Body).Decode(&resResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resRec.Body), &resResp, jsonx.V1()))
items := resResp["item"].([]any)
rootID := items[0].(map[string]any)["id"].(string)
@@ -69,7 +71,7 @@ func TestGetResources_NoPositionOnLastPage(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
_, hasPosition := resp["position"]
assert.False(t, hasPosition,
@@ -162,7 +164,7 @@ func TestUpdateResource(t *testing.T) {
fmt.Sprintf(`{"restApiId":%q,"parentId":%q,"pathPart":"original"}`, apiID, rootID))
require.Equal(t, http.StatusCreated, createRec.Code)
var childResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &childResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &childResp, jsonx.V1()))
childID := childResp["id"].(string)
lookupID := childID
diff --git a/services/apigateway/rest_api_policy_wire_test.go b/services/apigateway/rest_api_policy_wire_test.go
index 421b35a722..d2b2ea92be 100644
--- a/services/apigateway/rest_api_policy_wire_test.go
+++ b/services/apigateway/rest_api_policy_wire_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -55,9 +56,9 @@ func TestRestAPIPolicy_WireReEscaping(t *testing.T) {
var created struct {
ID string `json:"id"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
- patchValue, err := json.Marshal(tt.plainPolicy)
+ patchValue, err := json.Marshal(tt.plainPolicy, jsonx.V1())
require.NoError(t, err)
patchBody := `{"patchOperations":[{"op":"replace","path":"/policy","value":` +
string(patchValue) + `}]}`
@@ -68,7 +69,7 @@ func TestRestAPIPolicy_WireReEscaping(t *testing.T) {
var resp struct {
Policy string `json:"policy"`
}
- require.NoError(t, json.Unmarshal(patchRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(patchRec.Body.Bytes(), &resp, jsonx.V1()))
// Mirror terraform-provider-aws's flattenAPIPolicy: wrap in
// literal quotes, then unquote -- must recover the plain policy.
diff --git a/services/apigateway/rest_apis_test.go b/services/apigateway/rest_apis_test.go
index 497a16a2a8..40e666f7c1 100644
--- a/services/apigateway/rest_apis_test.go
+++ b/services/apigateway/rest_apis_test.go
@@ -1,7 +1,8 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -54,7 +56,7 @@ func TestGetRestApis_NoPositionOnLastPage(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
_, hasPosition := resp["position"]
assert.False(t, hasPosition,
@@ -212,7 +214,7 @@ func TestUpdateRestAPI(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.newName, resp["name"])
}
})
@@ -365,7 +367,7 @@ func TestGetRestAPIs_RESTPath_WithLimit(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["item"])
})
}
diff --git a/services/apigateway/route_matcher_proxy_test.go b/services/apigateway/route_matcher_proxy_test.go
index a140686e48..5d305df476 100644
--- a/services/apigateway/route_matcher_proxy_test.go
+++ b/services/apigateway/route_matcher_proxy_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/apigateway"
"github.com/blackbirdworks/gopherstack/services/s3"
@@ -66,7 +67,7 @@ func jsonAction(t *testing.T, client *http.Client, baseURL, action, body string,
require.NoError(t, err)
if out != nil && len(respBody) > 0 {
- require.NoError(t, json.Unmarshal(respBody, out))
+ require.NoError(t, json.Unmarshal(respBody, out, jsonx.V1()))
}
return resp.StatusCode
diff --git a/services/apigateway/schema_models.go b/services/apigateway/schema_models.go
index fdd130f49b..993dcda0ff 100644
--- a/services/apigateway/schema_models.go
+++ b/services/apigateway/schema_models.go
@@ -1,10 +1,13 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"regexp"
"sort"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// CreateModel creates a data model for a REST API.
@@ -25,7 +28,7 @@ func (b *InMemoryBackend) CreateModel(input CreateModelInput) (*Model, error) {
return nil, fmt.Errorf("%w: Model name must be alphanumeric", ErrInvalidParameter)
}
- if input.Schema != "" && !json.Valid([]byte(input.Schema)) {
+ if input.Schema != "" && !jsontext.Value([]byte(input.Schema)).IsValid(jsonx.V1()) {
return nil, fmt.Errorf("%w: Invalid model specified: schema is not valid JSON", ErrInvalidParameter)
}
@@ -95,13 +98,13 @@ func (b *InMemoryBackend) GetModel(restAPIID, modelName string, flatten bool) (*
// JSON, or a $ref naming no model in this REST API, is left untouched.
func flattenModelSchema(b *InMemoryBackend, restAPIID, schema string, visiting map[string]bool) string {
var node any
- if err := json.Unmarshal([]byte(schema), &node); err != nil {
+ if err := json.Unmarshal([]byte(schema), &node, jsonx.V1()); err != nil {
return schema
}
resolved := resolveModelRefNode(b, restAPIID, node, visiting)
- out, err := json.Marshal(resolved)
+ out, err := json.Marshal(resolved, jsonx.V1())
if err != nil {
return schema
}
@@ -150,7 +153,7 @@ func resolveModelRefNode(b *InMemoryBackend, restAPIID string, node any, visitin
func resolveModelRef(b *InMemoryBackend, restAPIID, ref string, visiting map[string]bool) (any, bool) {
refJSON, err := json.Marshal(struct {
Ref string `json:"$ref"`
- }{Ref: ref})
+ }{Ref: ref}, jsonx.V1())
if err != nil {
return nil, false
}
@@ -172,7 +175,7 @@ func resolveModelRef(b *InMemoryBackend, restAPIID, ref string, visiting map[str
nested[name] = true
var decoded any
- if unmarshalErr := json.Unmarshal([]byte(m.Schema), &decoded); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal([]byte(m.Schema), &decoded, jsonx.V1()); unmarshalErr != nil {
return nil, false
}
diff --git a/services/apigateway/schema_models_test.go b/services/apigateway/schema_models_test.go
index 723371ee1a..924161cbf8 100644
--- a/services/apigateway/schema_models_test.go
+++ b/services/apigateway/schema_models_test.go
@@ -1,13 +1,16 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestAPIGW_Models covers GetModel, GetModels, CreateModel, DeleteModel.
@@ -21,7 +24,7 @@ func TestAPIGW_Models(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300, "expected 2xx")
var apiResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&apiResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &apiResp, jsonx.V1()))
apiID, _ := apiResp["id"].(string)
require.NotEmpty(t, apiID)
@@ -90,7 +93,7 @@ func TestGetModel_Flatten(t *testing.T) {
var got struct {
Schema string `json:"schema"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&got))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &got, jsonx.V1()))
assert.JSONEq(t, tt.wantSchema, got.Schema)
})
}
@@ -117,7 +120,7 @@ func TestGetModel_FlattenSelfCycle(t *testing.T) {
var got struct {
Schema string `json:"schema"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&got))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &got, jsonx.V1()))
assert.JSONEq(t, `{"$ref":"https://apigateway.amazonaws.com/restapis/{restApiId}/models/Node"}`, got.Schema)
}
diff --git a/services/apigateway/sdk.go b/services/apigateway/sdk.go
index 7b531c0fab..57fa5491d5 100644
--- a/services/apigateway/sdk.go
+++ b/services/apigateway/sdk.go
@@ -3,8 +3,11 @@ package apigateway
import (
"archive/zip"
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// SdkType describes a code-generation target supported by GetSdk. AWS
@@ -87,7 +90,7 @@ func (b *InMemoryBackend) GetSdk(
ctx := exportContext{b: b, restAPIID: restAPIID, apiName: api.Name, opts: ExportOptions{Integrations: true}}
spec := buildOAS30Export(ctx, stageName)
- specJSON, err := json.MarshalIndent(spec, "", " ")
+ specJSON, err := json.Marshal(spec, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
if err != nil {
return nil, fmt.Errorf("failed to marshal OpenAPI spec: %w", err)
}
diff --git a/services/apigateway/sdk_test.go b/services/apigateway/sdk_test.go
index 5109f261b5..f2422791b4 100644
--- a/services/apigateway/sdk_test.go
+++ b/services/apigateway/sdk_test.go
@@ -4,7 +4,7 @@ import (
"archive/zip"
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -28,7 +29,7 @@ func TestAPIGateway_GetSdkTypes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["item"].([]any)
require.NotEmpty(t, items)
@@ -52,7 +53,7 @@ func TestAPIGateway_GetSdkType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "java", resp["id"])
assert.Equal(t, "Java", resp["friendlyName"])
diff --git a/services/apigateway/stages_test.go b/services/apigateway/stages_test.go
index bbab176182..1cc70e1e04 100644
--- a/services/apigateway/stages_test.go
+++ b/services/apigateway/stages_test.go
@@ -1,7 +1,8 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -164,7 +166,7 @@ func TestHandlerStage_ClientCertificateId(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300)
var certResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&certResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &certResp, jsonx.V1()))
certID := certResp["clientCertificateId"].(string)
require.NotEmpty(t, certID)
@@ -172,7 +174,7 @@ func TestHandlerStage_ClientCertificateId(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300)
var apiResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&apiResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &apiResp, jsonx.V1()))
apiID := apiResp["id"].(string)
rec = restRequest(t, h, http.MethodPost, "/restapis/"+apiID+"/deployments",
@@ -184,7 +186,7 @@ func TestHandlerStage_ClientCertificateId(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300)
var stageResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&stageResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &stageResp, jsonx.V1()))
assert.Equal(t, certID, stageResp["clientCertificateId"])
}
diff --git a/services/apigateway/store_test.go b/services/apigateway/store_test.go
index 0bacbed38d..e280294d2b 100644
--- a/services/apigateway/store_test.go
+++ b/services/apigateway/store_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -112,7 +113,7 @@ func TestPaginatedListing(t *testing.T) {
rec2 := postWithHandler(t, handler, e, tt.action, `{}`)
assert.Equal(t, tt.wantCode, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["item"].([]any)
assert.GreaterOrEqual(t, len(items), tt.wantMinItems)
})
diff --git a/services/apigateway/tags_test.go b/services/apigateway/tags_test.go
index 417ad888b8..b206c57baa 100644
--- a/services/apigateway/tags_test.go
+++ b/services/apigateway/tags_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -45,7 +46,7 @@ func TestGetTags_DomainNameAccessAssociation(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tagsResp, jsonx.V1()))
gotTags, _ := tagsResp["tags"].(map[string]any)
assert.Empty(t, gotTags)
}
@@ -91,13 +92,13 @@ func TestResourceTags(t *testing.T) {
fmt.Sprintf(`{"name":"tagged-api","tags":%s}`, tt.initialTags))
require.Equal(t, http.StatusCreated, rec.Code)
var apiResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &apiResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &apiResp, jsonx.V1()))
apiID := apiResp["id"].(string)
arn := fmt.Sprintf("arn:aws:apigateway:us-east-1::/restapis/%s", apiID)
// TagResource
- tagsJSON, err := json.Marshal(tt.addTags)
+ tagsJSON, err := json.Marshal(tt.addTags, jsonx.V1())
require.NoError(t, err)
tagRec := postWithHandler(t, handler, e, "TagResource",
fmt.Sprintf(`{"resourceArn":%q,"tags":%s}`, arn, tagsJSON))
@@ -105,7 +106,7 @@ func TestResourceTags(t *testing.T) {
// UntagResource (if applicable)
if len(tt.removeTags) > 0 {
- keysJSON, err2 := json.Marshal(tt.removeTags)
+ keysJSON, err2 := json.Marshal(tt.removeTags, jsonx.V1())
require.NoError(t, err2)
untagRec := postWithHandler(t, handler, e, "UntagResource",
fmt.Sprintf(`{"resourceArn":%q,"tagKeys":%s}`, arn, keysJSON))
@@ -117,7 +118,7 @@ func TestResourceTags(t *testing.T) {
fmt.Sprintf(`{"resourceArn":%q}`, arn))
assert.Equal(t, http.StatusOK, getRec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tagsResp, jsonx.V1()))
tagsMap, _ := tagsResp["tags"].(map[string]any)
for k, v := range tt.wantTagsAfter {
assert.Equal(t, v, tagsMap[k])
diff --git a/services/apigateway/test_invoke.go b/services/apigateway/test_invoke.go
index c3f976490d..43722e72e4 100644
--- a/services/apigateway/test_invoke.go
+++ b/services/apigateway/test_invoke.go
@@ -2,7 +2,7 @@ package apigateway
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -11,6 +11,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const testInvokeStage = "test-invoke-stage"
@@ -100,7 +101,7 @@ func claimString(v any) string {
return s
}
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Sprint(v)
}
@@ -131,7 +132,7 @@ func (h *Handler) testInvokeLambdaAuthorizer(
methodArn := authorizerMethodArn(ctx, r, apiID, testInvokeStage, r.URL.Path)
event.MethodArn = methodArn
- payload, _ := json.Marshal(event)
+ payload, _ := json.Marshal(event, jsonx.V1())
funcName := ExtractLambdaFunctionName(auth.AuthorizerURI)
resp, failure := h.invokeAuthorizerFunction(ctx, funcName, payload)
@@ -139,7 +140,7 @@ func (h *Handler) testInvokeLambdaAuthorizer(
return &TestInvokeAuthorizerOutput{ClientStatus: http.StatusUnauthorized, Log: failure}, nil
}
- policy, _ := json.Marshal(resp.PolicyDocument)
+ policy, _ := json.Marshal(resp.PolicyDocument, jsonx.V1())
out := &TestInvokeAuthorizerOutput{
PrincipalID: resp.PrincipalID,
PolicyDocument: string(policy),
@@ -213,7 +214,7 @@ func (h *Handler) invokeAuthorizerFunction(
return resp, "Authorizer function invocation failed: " + invokeErr.Error()
}
- if err := json.Unmarshal(respBytes, &resp); err != nil {
+ if err := json.Unmarshal(respBytes, &resp, jsonx.V1()); err != nil {
return resp, "Authorizer function returned an unparseable response: " + err.Error()
}
diff --git a/services/apigateway/test_invoke_test.go b/services/apigateway/test_invoke_test.go
index 2cb66c1ab7..a5b51edc2f 100644
--- a/services/apigateway/test_invoke_test.go
+++ b/services/apigateway/test_invoke_test.go
@@ -2,7 +2,7 @@ package apigateway_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -188,7 +189,7 @@ func TestTestInvokeMethod_StageVariablesAndPathParameters(t *testing.T) {
assert.Equal(t, "real-fn", inv.fn, "stage variable substituted into the integration URI")
var event map[string]any
- require.NoError(t, json.Unmarshal(inv.payload, &event))
+ require.NoError(t, json.Unmarshal(inv.payload, &event, jsonx.V1()))
assert.Equal(t, map[string]any{"id": "42"}, event["pathParameters"])
assert.Equal(t, map[string]any{"fn": "real-fn"}, event["stageVariables"])
}
diff --git a/services/apigateway/usage_plans_test.go b/services/apigateway/usage_plans_test.go
index 93cb6febd4..d440e6e0cb 100644
--- a/services/apigateway/usage_plans_test.go
+++ b/services/apigateway/usage_plans_test.go
@@ -1,7 +1,8 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -27,7 +29,7 @@ func TestAPIGW_UsagePlans(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300, "expected 2xx")
var keyResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&keyResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &keyResp, jsonx.V1()))
keyID, _ := keyResp["id"].(string)
require.NotEmpty(t, keyID)
@@ -42,7 +44,7 @@ func TestAPIGW_UsagePlans(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300, "expected 2xx")
var planResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&planResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &planResp, jsonx.V1()))
planID, _ := planResp["id"].(string)
require.NotEmpty(t, planID)
@@ -262,7 +264,7 @@ func TestHandlerUsagePlan_ApiStages(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300)
var apiResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&apiResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &apiResp, jsonx.V1()))
apiID := apiResp["id"].(string)
rec = restRequest(t, h, http.MethodPost, "/restapis/"+apiID+"/deployments",
@@ -276,7 +278,7 @@ func TestHandlerUsagePlan_ApiStages(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300)
var planResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&planResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &planResp, jsonx.V1()))
apiStages, ok := planResp["apiStages"].([]any)
require.True(t, ok)
require.Len(t, apiStages, 1)
@@ -317,7 +319,7 @@ func TestUpdateUsagePlan(t *testing.T) {
createRec := postWithHandler(t, handler, e, "CreateUsagePlan", `{"name":"orig-plan"}`)
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
planID := createResp["id"].(string)
lookupID := planID
diff --git a/services/apigateway/usage_test.go b/services/apigateway/usage_test.go
index bf32857e27..a67cf25b6b 100644
--- a/services/apigateway/usage_test.go
+++ b/services/apigateway/usage_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -41,7 +42,7 @@ func TestUpdateUsage_ValidatesKeyExists(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300, "create plan: %s", rec.Body.String())
var plan map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &plan))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &plan, jsonx.V1()))
planID, _ := plan["id"].(string)
require.NotEmpty(t, planID)
@@ -81,7 +82,7 @@ func TestAPIGateway_UpdateUsage_RESTRoute(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
values, _ := resp["values"].(map[string]any)
require.Contains(t, values, key.ID, "real wire key is \"values\", not \"items\"")
@@ -174,7 +175,7 @@ func TestGetUsage(t *testing.T) {
planRec := postWithHandler(t, handler, e, "CreateUsagePlan", `{"name":"my-plan"}`)
require.Equal(t, http.StatusCreated, planRec.Code)
var planResp map[string]any
- require.NoError(t, json.Unmarshal(planRec.Body.Bytes(), &planResp))
+ require.NoError(t, json.Unmarshal(planRec.Body.Bytes(), &planResp, jsonx.V1()))
planID := planResp["id"].(string)
lookupID := planID
@@ -250,7 +251,7 @@ func TestDeleteUsagePlan_ClearsUsageOverrides(t *testing.T) {
var decoded struct {
UsageOverrides map[string]map[string]int64 `json:"usageOverrides"`
}
- require.NoError(t, json.Unmarshal(b.Snapshot(t.Context()), &decoded))
+ require.NoError(t, json.Unmarshal(b.Snapshot(t.Context()), &decoded, jsonx.V1()))
assert.NotContains(t, decoded.UsageOverrides, plan.ID,
"a deleted usage plan's override entry must not survive in the persisted snapshot")
}
@@ -278,7 +279,7 @@ func TestDeleteAPIKey_ClearsUsageOverrides(t *testing.T) {
var decoded struct {
UsageOverrides map[string]map[string]int64 `json:"usageOverrides"`
}
- require.NoError(t, json.Unmarshal(b.Snapshot(t.Context()), &decoded))
+ require.NoError(t, json.Unmarshal(b.Snapshot(t.Context()), &decoded, jsonx.V1()))
assert.NotContains(t, decoded.UsageOverrides[plan.ID], key.ID,
"a deleted API key's usage override must not survive in the persisted snapshot")
}
diff --git a/services/apigateway/validation.go b/services/apigateway/validation.go
index 1cf6b79cbb..0a95677c16 100644
--- a/services/apigateway/validation.go
+++ b/services/apigateway/validation.go
@@ -1,11 +1,13 @@
package apigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"math"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// errSchemaViolation is the sentinel returned when a request body fails JSON Schema
@@ -31,7 +33,7 @@ func validateJSONAgainstSchema(body []byte, schema string) error {
}
var doc any
- if err := json.Unmarshal(body, &doc); err != nil {
+ if err := json.Unmarshal(body, &doc, jsonx.V1()); err != nil {
return errSchemaViolation
}
@@ -44,7 +46,7 @@ func validateJSONAgainstSchema(body []byte, schema string) error {
// when the schema is not a parseable JSON object.
func parseSchemaObject(schema string) (map[string]any, bool) {
var root map[string]any
- if err := json.Unmarshal([]byte(schema), &root); err != nil {
+ if err := json.Unmarshal([]byte(schema), &root, jsonx.V1()); err != nil {
return nil, false
}
@@ -316,11 +318,11 @@ func toFloat(raw any) (float64, bool) {
// jsonValuesEqual compares two decoded JSON values for equality (used by enum checks).
func jsonValuesEqual(a, b any) bool {
- ab, err := json.Marshal(a)
+ ab, err := json.Marshal(a, jsonx.V1())
if err != nil {
return false
}
- bb, err := json.Marshal(b)
+ bb, err := json.Marshal(b, jsonx.V1())
if err != nil {
return false
}
diff --git a/services/apigateway/vpc_links_test.go b/services/apigateway/vpc_links_test.go
index eb9d2606cc..4e773dff51 100644
--- a/services/apigateway/vpc_links_test.go
+++ b/services/apigateway/vpc_links_test.go
@@ -1,7 +1,7 @@
package apigateway_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigateway"
)
@@ -29,7 +30,7 @@ func TestAPIGateway_VpcLink_RESTLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
id, _ := created["id"].(string)
require.NotEmpty(t, id)
assert.Equal(t, "my-link", created["name"])
@@ -41,7 +42,7 @@ func TestAPIGateway_VpcLink_RESTLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var list map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &list))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &list, jsonx.V1()))
items, _ := list["item"].([]any)
assert.Len(t, items, 1)
@@ -50,7 +51,7 @@ func TestAPIGateway_VpcLink_RESTLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, patchRec.Code)
var updated map[string]any
- require.NoError(t, json.Unmarshal(patchRec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(patchRec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "renamed-link", updated["name"])
deleteRec := restCall(t, h, http.MethodDelete, "/vpclinks/"+id, "", "")
@@ -117,7 +118,7 @@ func TestVpcLinks(t *testing.T) {
if tt.wantCreateCode == http.StatusCreated {
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
linkID = createResp["id"].(string)
// Get
@@ -133,7 +134,7 @@ func TestVpcLinks(t *testing.T) {
listRec := postWithHandler(t, handler, e, "GetVpcLinks", `{}`)
assert.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
assert.GreaterOrEqual(t, len(listResp["item"].([]any)), 1)
// Update
@@ -142,7 +143,7 @@ func TestVpcLinks(t *testing.T) {
fmt.Sprintf(`{"vpcLinkId":%q,"name":%q}`, linkID, tt.updateName))
assert.Equal(t, http.StatusOK, updateRec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Equal(t, tt.updateName, updateResp["name"])
}
diff --git a/services/apigateway/vtl_values.go b/services/apigateway/vtl_values.go
index 54ace57f14..dc04046e48 100644
--- a/services/apigateway/vtl_values.go
+++ b/services/apigateway/vtl_values.go
@@ -2,7 +2,8 @@ package apigateway
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -10,6 +11,8 @@ import (
"sort"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// vtlMap is an insertion-ordered map, matching the LinkedHashMap API Gateway
@@ -67,55 +70,59 @@ var errVTLJSON = errors.New("invalid JSON")
// parseOrderedJSON decodes JSON into vtl values (ordered maps, lists, int64/float64, string, bool, nil).
func parseOrderedJSON(s string) (any, error) {
- dec := json.NewDecoder(strings.NewReader(s))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(strings.NewReader(s), jsonx.V1())
v, err := decodeVTLValue(dec)
if err != nil {
return nil, err
}
- if _, err = dec.Token(); !errors.Is(err, io.EOF) {
+ if _, err = dec.ReadToken(); !errors.Is(err, io.EOF) {
return nil, errVTLJSON
}
return v, nil
}
-func decodeVTLValue(dec *json.Decoder) (any, error) {
- tok, err := dec.Token()
+func decodeVTLValue(dec *jsontext.Decoder) (any, error) {
+ tok, err := dec.ReadToken()
if err != nil {
return nil, err
}
- switch t := tok.(type) {
- case json.Delim:
- if t == '{' {
- return decodeVTLObject(dec)
- }
-
+ switch tok.Kind() {
+ case '{':
+ return decodeVTLObject(dec)
+ case '[':
return decodeVTLArray(dec)
- case json.Number:
- return numberFromJSON(t), nil
+ case '0':
+ return numberFromJSON(jsonx.Number(tok.String())), nil
+ case '"':
+ return tok.String(), nil
+ case 't', 'f':
+ return tok.Bool(), nil
default:
- return t, nil
+ var null any
+
+ return null, nil
}
}
-func decodeVTLObject(dec *json.Decoder) (any, error) {
+func decodeVTLObject(dec *jsontext.Decoder) (any, error) {
m := newVTLMap()
- for dec.More() {
- keyTok, err := dec.Token()
+ for dec.PeekKind() != '}' {
+ keyTok, err := dec.ReadToken()
if err != nil {
return nil, err
}
- key, ok := keyTok.(string)
- if !ok {
+ if keyTok.Kind() != '"' {
return nil, errVTLJSON
}
+ key := keyTok.String()
+
v, err := decodeVTLValue(dec)
if err != nil {
return nil, err
@@ -124,17 +131,17 @@ func decodeVTLObject(dec *json.Decoder) (any, error) {
m.put(key, v)
}
- if _, err := dec.Token(); err != nil {
+ if _, err := dec.ReadToken(); err != nil {
return nil, err
}
return m, nil
}
-func decodeVTLArray(dec *json.Decoder) (any, error) {
+func decodeVTLArray(dec *jsontext.Decoder) (any, error) {
l := &vtlList{items: []any{}}
- for dec.More() {
+ for dec.PeekKind() != ']' {
v, err := decodeVTLValue(dec)
if err != nil {
return nil, err
@@ -143,14 +150,14 @@ func decodeVTLArray(dec *json.Decoder) (any, error) {
l.items = append(l.items, v)
}
- if _, err := dec.Token(); err != nil {
+ if _, err := dec.ReadToken(); err != nil {
return nil, err
}
return l, nil
}
-func numberFromJSON(n json.Number) any {
+func numberFromJSON(n jsonx.Number) any {
if i, err := strconv.ParseInt(n.String(), 10, 64); err == nil {
return i
}
@@ -213,9 +220,8 @@ func writeVTLJSON(buf *bytes.Buffer, v any) {
func writeJSONString(buf *bytes.Buffer, s string) {
var tmp bytes.Buffer
- enc := json.NewEncoder(&tmp)
- enc.SetEscapeHTML(false)
- _ = enc.Encode(s)
+ enc := jsontext.NewEncoder(&tmp, jsonx.V1(), jsontext.EscapeForHTML(false))
+ _ = json.MarshalEncode(enc, s)
buf.Write(bytes.TrimRight(tmp.Bytes(), "\n"))
}
diff --git a/services/apigatewaymanagementapi/admin.go b/services/apigatewaymanagementapi/admin.go
index 470bc720b1..9c28498dd5 100644
--- a/services/apigatewaymanagementapi/admin.go
+++ b/services/apigatewaymanagementapi/admin.go
@@ -1,7 +1,8 @@
package apigatewaymanagementapi
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -11,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -244,10 +246,9 @@ func (h *Handler) connectionExists(connectionID string) bool {
}
func decodeJSON(r io.Reader, dst any) error {
- dec := json.NewDecoder(r)
- dec.DisallowUnknownFields()
+ dec := jsontext.NewDecoder(r)
- if err := dec.Decode(dst); err != nil {
+ if err := json.UnmarshalDecode(dec, dst, jsonx.V1(), json.RejectUnknownMembers(true)); err != nil {
return err
}
diff --git a/services/apigatewaymanagementapi/admin_test.go b/services/apigatewaymanagementapi/admin_test.go
index 28d243a496..f62d2b51a6 100644
--- a/services/apigatewaymanagementapi/admin_test.go
+++ b/services/apigatewaymanagementapi/admin_test.go
@@ -2,7 +2,7 @@ package apigatewaymanagementapi_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewaymanagementapi"
)
@@ -34,7 +35,7 @@ func TestAdmin_ListConnections(t *testing.T) {
Connections []apigatewaymanagementapi.Connection `json:"connections"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Connections, 2)
}
@@ -56,7 +57,7 @@ func TestAdmin_ListConnections_QueryFilter(t *testing.T) {
Connections []apigatewaymanagementapi.Connection `json:"connections"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Connections, 1)
assert.Equal(t, "beta-2", resp.Connections[0].ConnectionID)
}
@@ -70,7 +71,7 @@ func TestAdmin_SimulateConnection(t *testing.T) {
"connectionId": "sim-1",
"sourceIp": "1.2.3.4",
"userAgent": "ua",
- })
+ }, jsonx.V1())
rec := doRequest(t, h, http.MethodPost, "/_gopherstack/apigwmgmt/connections", body)
assert.Equal(t, http.StatusCreated, rec.Code)
@@ -88,7 +89,7 @@ func TestAdmin_SimulateDuplicate(t *testing.T) {
_, err := h.Backend.CreateConnection("dupe", "1.1.1.1", "ua", nil)
require.NoError(t, err)
- body, _ := json.Marshal(map[string]string{"connectionId": "dupe"})
+ body, _ := json.Marshal(map[string]string{"connectionId": "dupe"}, jsonx.V1())
rec := doRequest(t, h, http.MethodPost, "/_gopherstack/apigwmgmt/connections", body)
assert.Equal(t, http.StatusConflict, rec.Code)
}
@@ -98,7 +99,7 @@ func TestAdmin_SimulateEmptyConnectionID(t *testing.T) {
h := newTestHandler(t)
- body, _ := json.Marshal(map[string]string{"connectionId": ""})
+ body, _ := json.Marshal(map[string]string{"connectionId": ""}, jsonx.V1())
rec := doRequest(t, h, http.MethodPost, "/_gopherstack/apigwmgmt/connections", body)
assert.Equal(t, http.StatusBadRequest, rec.Code)
}
@@ -123,7 +124,7 @@ func TestAdmin_GetMessages(t *testing.T) {
} `json:"messages"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Messages, 2)
assert.Equal(t, "hello", resp.Messages[0].Data)
assert.Equal(t, 5, resp.Messages[0].Bytes)
@@ -170,7 +171,7 @@ func TestAdmin_GetTimeline(t *testing.T) {
Events []apigatewaymanagementapi.LifecycleEvent `json:"events"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Events, 3)
assert.Equal(t, apigatewaymanagementapi.EventConnected, resp.Events[0].Type)
assert.Equal(t, apigatewaymanagementapi.EventMessage, resp.Events[1].Type)
@@ -208,7 +209,7 @@ func TestAdmin_Broadcast(t *testing.T) {
require.NoError(t, err)
}
- body, _ := json.Marshal(map[string]string{"data": "broadcast-payload"})
+ body, _ := json.Marshal(map[string]string{"data": "broadcast-payload"}, jsonx.V1())
rec := doRequest(t, h, http.MethodPost, "/_gopherstack/apigwmgmt/broadcast", body)
assert.Equal(t, http.StatusOK, rec.Code)
@@ -217,7 +218,7 @@ func TestAdmin_Broadcast(t *testing.T) {
Delivered int `json:"delivered"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, 3, resp.Delivered)
for _, id := range []string{"a", "b", "c"} {
@@ -236,7 +237,7 @@ func TestAdmin_BroadcastTooLarge(t *testing.T) {
require.NoError(t, err)
big := bytes.Repeat([]byte("x"), 130*1024)
- body, _ := json.Marshal(map[string]string{"data": string(big)})
+ body, _ := json.Marshal(map[string]string{"data": string(big)}, jsonx.V1())
rec := doRequest(t, h, http.MethodPost, "/_gopherstack/apigwmgmt/broadcast", body)
assert.Equal(t, http.StatusRequestEntityTooLarge, rec.Code)
@@ -255,7 +256,7 @@ func TestAdmin_Stats(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var stats apigatewaymanagementapi.Stats
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stats))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stats, jsonx.V1()))
assert.Equal(t, 1, stats.ActiveConnections)
assert.Equal(t, int64(1), stats.TotalConnections)
assert.Equal(t, int64(1), stats.TotalMessages)
diff --git a/services/apigatewaymanagementapi/error_messages_test.go b/services/apigatewaymanagementapi/error_messages_test.go
index 2ac98e8888..fb2beda9d0 100644
--- a/services/apigatewaymanagementapi/error_messages_test.go
+++ b/services/apigatewaymanagementapi/error_messages_test.go
@@ -1,7 +1,7 @@
package apigatewaymanagementapi_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewaymanagementapi"
)
@@ -53,7 +54,7 @@ func TestModeledErrorBodies(t *testing.T) {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, tt.wantMsg, body["message"])
assert.NotContains(t, body, "connectionId")
})
diff --git a/services/apigatewaymanagementapi/handler_test.go b/services/apigatewaymanagementapi/handler_test.go
index 2b1fec35ff..1001144ea5 100644
--- a/services/apigatewaymanagementapi/handler_test.go
+++ b/services/apigatewaymanagementapi/handler_test.go
@@ -2,7 +2,8 @@ package apigatewaymanagementapi_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"log/slog"
"net/http"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/services/apigatewaymanagementapi"
)
@@ -325,8 +327,8 @@ func TestHandler_GetConnection(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
if tt.wantStatus == http.StatusOK {
- var body map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ var body map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Contains(t, body, "connectedAt")
assert.Contains(t, body, "identity")
assert.Contains(t, body, "lastActiveAt")
@@ -362,8 +364,8 @@ func TestHandler_GetConnection_NoConnectionIDField(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/@connections/"+tt.connectionID, nil)
require.Equal(t, http.StatusOK, rec.Code)
- var body map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ var body map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.NotContains(t, body, "connectionId",
"GetConnectionOutput has no ConnectionId member on the real API")
})
@@ -398,15 +400,15 @@ func TestHandler_GetConnection_IdentityShape(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/@connections/conn-identity", nil)
require.Equal(t, http.StatusOK, rec.Code)
- var body map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ var body map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
// AWS shape: identity must be a nested object.
identityRaw, ok := body["identity"]
require.True(t, ok, "response must include 'identity' field")
var identity map[string]string
- require.NoError(t, json.Unmarshal(identityRaw, &identity))
+ require.NoError(t, json.Unmarshal(identityRaw, &identity, jsonx.V1()))
assert.Equal(t, tt.sourceIP, identity["sourceIp"], "identity.sourceIp must match")
assert.Equal(t, tt.userAgent, identity["userAgent"], "identity.userAgent must match")
@@ -440,7 +442,7 @@ func TestHandler_GetConnection_TimestampFields(t *testing.T) {
ConnectedAt string `json:"connectedAt"`
LastActiveAt string `json:"lastActiveAt"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.NotEmpty(t, body.ConnectedAt, "connectedAt must be non-empty")
assert.NotEmpty(t, body.LastActiveAt, "lastActiveAt must be non-empty")
}
@@ -533,7 +535,7 @@ func TestHandler_GoneExceptionWireShape(t *testing.T) {
assert.Equal(t, "GoneException", rec.Header().Get("X-Amzn-Errortype"))
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "GoneException", body["__type"])
assert.NotEqual(t, "GoneException", body["message"],
"message must be a human-readable string, not the error type")
@@ -563,7 +565,7 @@ func TestHandler_PostToConnection_LimitExceeded(t *testing.T) {
assert.Equal(t, "LimitExceededException", rec.Header().Get("X-Amzn-Errortype"))
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "LimitExceededException", body["__type"])
assert.NotEmpty(t, body["message"])
@@ -588,7 +590,7 @@ func TestHandler_PostToConnection_PayloadTooLarge_WireShape(t *testing.T) {
assert.Equal(t, "PayloadTooLargeException", rec.Header().Get("X-Amzn-Errortype"))
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "PayloadTooLargeException", body["__type"])
assert.NotEmpty(t, body["message"])
}
diff --git a/services/apigatewaymanagementapi/ringbuffer_test.go b/services/apigatewaymanagementapi/ringbuffer_test.go
index 60c2514745..b9958eb638 100644
--- a/services/apigatewaymanagementapi/ringbuffer_test.go
+++ b/services/apigatewaymanagementapi/ringbuffer_test.go
@@ -1,13 +1,14 @@
package apigatewaymanagementapi_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewaymanagementapi"
)
@@ -211,7 +212,7 @@ func TestBackend_SnapshotPreservesEverything(t *testing.T) {
// Verify well-formed JSON
var raw map[string]any
- require.NoError(t, json.Unmarshal(data, &raw))
+ require.NoError(t, json.Unmarshal(data, &raw, jsonx.V1()))
fresh := apigatewaymanagementapi.NewInMemoryBackend()
require.NoError(t, fresh.Restore(t.Context(), data))
diff --git a/services/apigatewayv2/authorizer_cache_internal_test.go b/services/apigatewayv2/authorizer_cache_internal_test.go
index 53d5f2390d..17330fbde6 100644
--- a/services/apigatewayv2/authorizer_cache_internal_test.go
+++ b/services/apigatewayv2/authorizer_cache_internal_test.go
@@ -3,7 +3,7 @@ package apigatewayv2
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strconv"
@@ -15,6 +15,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// doInternalRequest performs an HTTP request against h and returns the
@@ -27,7 +29,7 @@ func doInternalRequest(t *testing.T, h *Handler, method, path string, body any)
var bodyReader *bytes.Reader
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
bodyReader = bytes.NewReader(b)
} else {
diff --git a/services/apigatewayv2/authorizer_context.go b/services/apigatewayv2/authorizer_context.go
index c2bbed876d..86cec940fe 100644
--- a/services/apigatewayv2/authorizer_context.go
+++ b/services/apigatewayv2/authorizer_context.go
@@ -1,13 +1,15 @@
package apigatewayv2
import (
- "encoding/json"
+ "encoding/json/v2"
"slices"
"strconv"
"strings"
"github.com/golang-jwt/jwt/v5"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const authorizerCtxKey = "apigwv2.authorizer"
@@ -51,7 +53,7 @@ func stringifyClaim(v any) string {
return "[" + strings.Join(parts, " ") + "]"
default:
- b, _ := json.Marshal(t)
+ b, _ := json.Marshal(t, jsonx.V1())
return string(b)
}
@@ -97,7 +99,7 @@ func lambdaAuthorizerContext(respBytes []byte) map[string]any {
var resp struct {
Context map[string]any `json:"context"`
}
- if err := json.Unmarshal(respBytes, &resp); err != nil || len(resp.Context) == 0 {
+ if err := json.Unmarshal(respBytes, &resp, jsonx.V1()); err != nil || len(resp.Context) == 0 {
return nil
}
diff --git a/services/apigatewayv2/authorizers.go b/services/apigatewayv2/authorizers.go
index 3bec274b22..39395e14ae 100644
--- a/services/apigatewayv2/authorizers.go
+++ b/services/apigatewayv2/authorizers.go
@@ -1,7 +1,7 @@
package apigatewayv2
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"sort"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -211,7 +212,7 @@ func (f *flexibleStrings) UnmarshalJSON(data []byte) error {
if strings.HasPrefix(trimmed, "[") {
var arr []string
- if err := json.Unmarshal(data, &arr); err != nil {
+ if err := json.Unmarshal(data, &arr, jsonx.V1()); err != nil {
return err
}
@@ -221,7 +222,7 @@ func (f *flexibleStrings) UnmarshalJSON(data []byte) error {
}
var s string
- if err := json.Unmarshal(data, &s); err != nil {
+ if err := json.Unmarshal(data, &s, jsonx.V1()); err != nil {
return err
}
@@ -375,7 +376,7 @@ func buildAuthorizerPayload(
},
}
- return json.Marshal(event)
+ return json.Marshal(event, jsonx.V1())
}
event := requestAuthorizerEventV2{
@@ -403,7 +404,7 @@ func buildAuthorizerPayload(
},
}
- return json.Marshal(event)
+ return json.Marshal(event, jsonx.V1())
}
// authorizerUsesV1Payload reports whether the authorizer expects the 1.0 event
@@ -418,7 +419,7 @@ func evaluateAuthorizerResponse(respBytes []byte, auth *Authorizer, routeArn str
// Simple response: only valid with payload 2.0 + EnableSimpleResponses.
if auth.EnableSimpleResponses && !authorizerUsesV1Payload(auth) {
var simple simpleAuthorizerResponse
- if err := json.Unmarshal(respBytes, &simple); err == nil {
+ if err := json.Unmarshal(respBytes, &simple, jsonx.V1()); err == nil {
return simple.IsAuthorized, false
}
@@ -427,7 +428,7 @@ func evaluateAuthorizerResponse(respBytes []byte, auth *Authorizer, routeArn str
// IAM policy response.
var policy iamPolicyResponse
- if err := json.Unmarshal(respBytes, &policy); err != nil {
+ if err := json.Unmarshal(respBytes, &policy, jsonx.V1()); err != nil {
return false, false
}
diff --git a/services/apigatewayv2/custom_domain_test.go b/services/apigatewayv2/custom_domain_test.go
index 888908c79c..a9ab330f1a 100644
--- a/services/apigatewayv2/custom_domain_test.go
+++ b/services/apigatewayv2/custom_domain_test.go
@@ -2,7 +2,7 @@ package apigatewayv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/s3"
)
@@ -32,11 +33,11 @@ func TestCustomDomainRouting(t *testing.T) {
} `json:"requestContext"`
RawPath string `json:"rawPath"`
}
- _ = json.Unmarshal(payload, &ev)
+ _ = json.Unmarshal(payload, &ev, jsonx.V1())
b, _ := json.Marshal(map[string]any{
"statusCode": 200,
"body": name + "|" + ev.RequestContext.Stage + "|" + ev.RawPath,
- })
+ }, jsonx.V1())
return b, 200, nil
},
diff --git a/services/apigatewayv2/custom_id_test.go b/services/apigatewayv2/custom_id_test.go
index 53b1711682..5bc7bf6d7e 100644
--- a/services/apigatewayv2/custom_id_test.go
+++ b/services/apigatewayv2/custom_id_test.go
@@ -1,13 +1,14 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -54,7 +55,7 @@ func TestCreateAPICustomIDTag(t *testing.T) {
}
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
assert.Equal(t, tt.wantID, api.APIID)
assert.NotContains(t, api.Tags, "_custom_id_")
assert.NotContains(t, api.Tags, "gopherstack:id")
diff --git a/services/apigatewayv2/handler.go b/services/apigatewayv2/handler.go
index 24d6f15c5f..874ebcde7a 100644
--- a/services/apigatewayv2/handler.go
+++ b/services/apigatewayv2/handler.go
@@ -3,7 +3,8 @@ package apigatewayv2
import (
"context"
"crypto/rsa"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"slices"
@@ -19,6 +20,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/cwmetric"
"github.com/blackbirdworks/gopherstack/pkgs/executeapi"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
@@ -643,7 +645,7 @@ func handleCreateMulti[I, O any](
log := logger.Load(c.Request().Context())
var input I
- if err := json.NewDecoder(c.Request().Body).Decode(&input); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &input, jsonx.V1()); err != nil {
return writeErr(c, http.StatusBadRequest, msgInvalidBody)
}
@@ -682,7 +684,7 @@ func handleUpdate[I, O any](
log := logger.Load(c.Request().Context())
var input I
- if err := json.NewDecoder(c.Request().Body).Decode(&input); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &input, jsonx.V1()); err != nil {
return writeErr(c, http.StatusBadRequest, msgInvalidBody)
}
@@ -785,7 +787,7 @@ func handleCreateNoParent[I, O any](
log := logger.Load(c.Request().Context())
var input I
- if err := json.NewDecoder(c.Request().Body).Decode(&input); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &input, jsonx.V1()); err != nil {
return writeErr(c, http.StatusBadRequest, msgInvalidBody)
}
diff --git a/services/apigatewayv2/handler_api_mappings_test.go b/services/apigatewayv2/handler_api_mappings_test.go
index 63223f459a..ed27246ff4 100644
--- a/services/apigatewayv2/handler_api_mappings_test.go
+++ b/services/apigatewayv2/handler_api_mappings_test.go
@@ -1,7 +1,7 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"net/http"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -119,7 +120,7 @@ func TestHandler_CreateApiMapping(t *testing.T) {
if tt.wantAPIIDSet {
var mapping apigatewayv2.APIMapping
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &mapping))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &mapping, jsonx.V1()))
assert.Equal(t, apiID, mapping.APIID)
assert.NotEmpty(t, mapping.APIMappingID)
}
@@ -174,7 +175,7 @@ func TestHandler_GetAPIMappings(t *testing.T) {
var out struct {
Items []apigatewayv2.APIMapping `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Items, tt.mappingsCnt)
}
})
@@ -230,7 +231,7 @@ func TestHandler_GetAPIMapping(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var mapping apigatewayv2.APIMapping
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &mapping))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &mapping, jsonx.V1()))
domainName := "example.com"
mappingID := mapping.APIMappingID
@@ -299,7 +300,7 @@ func TestHandler_DeleteAPIMapping(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var mapping apigatewayv2.APIMapping
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &mapping))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &mapping, jsonx.V1()))
domainName := "example.com"
mappingID := mapping.APIMappingID
@@ -340,7 +341,7 @@ func TestHandler_UpdateAPIMapping(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rr.Code)
var m apigatewayv2.APIMapping
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &m, jsonx.V1()))
return "api.example.com", m.APIMappingID
},
diff --git a/services/apigatewayv2/handler_api_models_test.go b/services/apigatewayv2/handler_api_models_test.go
index 5fe44c52a2..8e2dd18c52 100644
--- a/services/apigatewayv2/handler_api_models_test.go
+++ b/services/apigatewayv2/handler_api_models_test.go
@@ -1,7 +1,7 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -73,7 +74,7 @@ func TestHandler_CreateModel(t *testing.T) {
if tt.wantName != "" {
var model apigatewayv2.Model
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &model))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &model, jsonx.V1()))
assert.Equal(t, tt.wantName, model.Name)
assert.NotEmpty(t, model.ModelID)
}
@@ -121,7 +122,7 @@ func TestHandler_GetModelTemplate(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var model apigatewayv2.Model
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &model))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &model, jsonx.V1()))
rec := doRequest(t, h, http.MethodGet,
fmt.Sprintf("/v2/apis/%s/models/%s/template", apiID, model.ModelID), nil)
@@ -130,7 +131,7 @@ func TestHandler_GetModelTemplate(t *testing.T) {
var out struct {
Value string `json:"value"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantValue, out.Value)
})
}
@@ -224,7 +225,7 @@ func TestHandler_GetModels(t *testing.T) {
var out struct {
Items []apigatewayv2.Model `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Items, tt.modelCnt)
}
})
@@ -265,7 +266,7 @@ func TestHandler_GetModel(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var model apigatewayv2.Model
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &model))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &model, jsonx.V1()))
modelID := model.ModelID
if tt.useWrongID {
@@ -277,7 +278,7 @@ func TestHandler_GetModel(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var got apigatewayv2.Model
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "MyModel", got.Name)
}
})
@@ -318,7 +319,7 @@ func TestHandler_DeleteModel(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var model apigatewayv2.Model
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &model))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &model, jsonx.V1()))
modelID := model.ModelID
if tt.useWrongID {
@@ -350,7 +351,7 @@ func TestHandler_UpdateModel(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rr.Code)
var m apigatewayv2.Model
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &m, jsonx.V1()))
return apiID, m.ModelID
},
@@ -390,7 +391,7 @@ func TestHandler_UpdateModel(t *testing.T) {
if tt.wantName != "" {
var m apigatewayv2.Model
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &m, jsonx.V1()))
assert.Equal(t, tt.wantName, m.Name)
}
})
@@ -432,7 +433,7 @@ func TestGetModels_Pagination(t *testing.T) {
NextToken string `json:"nextToken"`
Items []map[string]any `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
require.LessOrEqual(t, len(resp.Items), 2)
for _, m := range resp.Items {
diff --git a/services/apigatewayv2/handler_apis.go b/services/apigatewayv2/handler_apis.go
index 40c08e7fd9..d27ee0af22 100644
--- a/services/apigatewayv2/handler_apis.go
+++ b/services/apigatewayv2/handler_apis.go
@@ -1,7 +1,8 @@
package apigatewayv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -13,6 +14,7 @@ import (
"github.com/labstack/echo/v5"
"gopkg.in/yaml.v3"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
@@ -103,7 +105,7 @@ func (h *Handler) handleCreateAPI(c *echo.Context) error {
log := logger.Load(c.Request().Context())
var input CreateAPIInput
- if err := json.NewDecoder(c.Request().Body).Decode(&input); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &input, jsonx.V1()); err != nil {
return writeErr(c, http.StatusBadRequest, msgInvalidBody)
}
@@ -196,7 +198,7 @@ func (h *Handler) handleUpdateAPI(c *echo.Context, apiID string) error {
log := logger.Load(c.Request().Context())
var input UpdateAPIInput
- if err := json.NewDecoder(c.Request().Body).Decode(&input); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &input, jsonx.V1()); err != nil {
return writeErr(c, http.StatusBadRequest, msgInvalidBody)
}
@@ -364,7 +366,7 @@ func parseOpenAPISpec(body string) (*openAPISpec, error) {
return &spec, nil
}
- if err := json.Unmarshal([]byte(body), &spec); err != nil {
+ if err := json.Unmarshal([]byte(body), &spec, jsonx.V1()); err != nil {
return nil, err
}
@@ -466,7 +468,7 @@ func (h *Handler) handleImportAPI(c *echo.Context) error {
var input struct {
Body string `json:"body"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&input); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &input, jsonx.V1()); err != nil {
return writeErr(c, http.StatusBadRequest, msgInvalidBody)
}
@@ -510,7 +512,7 @@ func (h *Handler) handleReimportAPI(c *echo.Context, apiID string) error {
var input struct {
Body string `json:"body"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&input); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &input, jsonx.V1()); err != nil {
return writeErr(c, http.StatusBadRequest, msgInvalidBody)
}
@@ -677,7 +679,7 @@ func (h *Handler) handleExportAPI(c *echo.Context, apiID, specification string)
return c.Blob(http.StatusOK, "application/x-yaml", blob)
}
- blob, mErr := json.Marshal(spec)
+ blob, mErr := json.Marshal(spec, jsonx.V1())
if mErr != nil {
return writeErr(c, http.StatusInternalServerError, mErr.Error())
}
diff --git a/services/apigatewayv2/handler_apis_test.go b/services/apigatewayv2/handler_apis_test.go
index c9644fef71..fd1aa6e5ac 100644
--- a/services/apigatewayv2/handler_apis_test.go
+++ b/services/apigatewayv2/handler_apis_test.go
@@ -1,7 +1,7 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -59,7 +60,7 @@ func TestHandler_CreateAPI(t *testing.T) {
if tt.wantName != "" {
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
assert.Equal(t, tt.wantName, api.Name)
assert.NotEmpty(t, api.APIID)
}
@@ -106,7 +107,7 @@ func TestHandler_GetAPI(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
assert.Equal(t, apiID, api.APIID)
}
})
@@ -151,7 +152,7 @@ func TestHandler_GetAPIs(t *testing.T) {
}
var resp listResp
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Items, tt.apiCount)
})
}
@@ -238,7 +239,7 @@ func TestHandler_UpdateAPI(t *testing.T) {
if tt.wantName != "" {
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
assert.Equal(t, tt.wantName, api.Name)
}
})
@@ -301,7 +302,7 @@ func TestExportAPI_ReturnsRawSpec(t *testing.T) {
require.Equal(t, http.StatusOK, rr.Code)
var spec map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &spec))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &spec, jsonx.V1()))
// Raw OpenAPI doc — not the old {"body":...,"specification":...} wrapper.
assert.Equal(t, "3.0.1", spec["openapi"])
@@ -355,7 +356,7 @@ func TestExportAPI_OutputType(t *testing.T) {
var asJSON map[string]any
require.Error(
t,
- json.Unmarshal(rr.Body.Bytes(), &asJSON),
+ json.Unmarshal(rr.Body.Bytes(), &asJSON, jsonx.V1()),
"YAML output must not also be valid strict JSON object syntax expected by this assertion",
)
assert.Contains(t, rr.Body.String(), "openapi:")
@@ -364,7 +365,7 @@ func TestExportAPI_OutputType(t *testing.T) {
}
var spec map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &spec))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &spec, jsonx.V1()))
assert.Equal(t, "3.0.1", spec["openapi"])
})
}
@@ -400,7 +401,7 @@ func TestExportAPI_JWTSecurityScheme(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var auth apigatewayv2.Authorizer
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &auth))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &auth, jsonx.V1()))
rr = doRequest(t, h, http.MethodPost, "/v2/apis/"+apiID+"/routes", map[string]any{
"routeKey": "GET /jwt",
@@ -413,7 +414,7 @@ func TestExportAPI_JWTSecurityScheme(t *testing.T) {
require.Equal(t, http.StatusOK, rr.Code)
var spec map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &spec))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &spec, jsonx.V1()))
components, ok := spec["components"].(map[string]any)
require.True(t, ok, "expected components block")
@@ -494,7 +495,7 @@ func TestGetAPIs_Pagination(t *testing.T) {
NextToken string `json:"nextToken"`
Items []apigatewayv2.API `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
require.LessOrEqual(t, len(resp.Items), tc.maxResults, "page must not exceed maxResults")
for _, api := range resp.Items {
@@ -540,7 +541,7 @@ func TestGetAPIs_NoMaxResults_ReturnsAll(t *testing.T) {
NextToken string `json:"nextToken"`
Items []apigatewayv2.API `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Items, 5)
assert.Empty(t, resp.NextToken)
}
@@ -563,7 +564,7 @@ func TestGetAPIs_EmptyLastPage(t *testing.T) {
NextToken string `json:"nextToken"`
Items []apigatewayv2.API `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Items, 2)
assert.Empty(t, resp.NextToken, "no nextToken when all items fit")
}
@@ -583,7 +584,7 @@ func TestCreateAPI_HTTP_DefaultAPIKeySelectionExpression(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "$request.header.x-api-key", resp["apiKeySelectionExpression"])
}
@@ -602,7 +603,7 @@ func TestCreateAPI_WebSocket_DefaultAPIKeySelectionExpression(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "$context.authorizer.usageIdentifierKey", resp["apiKeySelectionExpression"])
}
@@ -623,7 +624,7 @@ func TestCreateAPI_ExplicitAPIKeySelectionExpressionPreserved(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, custom, resp["apiKeySelectionExpression"])
}
@@ -675,7 +676,7 @@ func TestImportAPI_Basepath_Prepend(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
rr = doRequest(t, h, http.MethodGet, "/v2/apis/"+api.APIID+"/routes", nil)
require.Equal(t, http.StatusOK, rr.Code)
@@ -683,7 +684,7 @@ func TestImportAPI_Basepath_Prepend(t *testing.T) {
var routes struct {
Items []apigatewayv2.Route `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &routes))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &routes, jsonx.V1()))
require.Len(t, routes.Items, 1)
assert.Equal(t, "GET /v1/items", routes.Items[0].RouteKey)
}
@@ -707,7 +708,7 @@ func TestImportAPI_Basepath_DefaultIgnoresSpecBasePath(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
rr = doRequest(t, h, http.MethodGet, "/v2/apis/"+api.APIID+"/routes", nil)
require.Equal(t, http.StatusOK, rr.Code)
@@ -715,7 +716,7 @@ func TestImportAPI_Basepath_DefaultIgnoresSpecBasePath(t *testing.T) {
var routes struct {
Items []apigatewayv2.Route `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &routes))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &routes, jsonx.V1()))
require.Len(t, routes.Items, 1)
assert.Equal(t, "GET /items", routes.Items[0].RouteKey)
}
@@ -791,7 +792,7 @@ func TestImportAPI_Basepath_RouteKeyTransforms(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
rr = doRequest(t, h, http.MethodGet, "/v2/apis/"+api.APIID+"/routes", nil)
require.Equal(t, http.StatusOK, rr.Code)
@@ -799,7 +800,7 @@ func TestImportAPI_Basepath_RouteKeyTransforms(t *testing.T) {
var routes struct {
Items []apigatewayv2.Route `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &routes))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &routes, jsonx.V1()))
require.Len(t, routes.Items, 1)
assert.Equal(t, tt.wantRouteKey, routes.Items[0].RouteKey)
})
@@ -846,7 +847,7 @@ func TestReimportAPI_Basepath_RouteKeyTransforms(t *testing.T) {
var routes struct {
Items []apigatewayv2.Route `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &routes))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &routes, jsonx.V1()))
require.Len(t, routes.Items, 1)
assert.Equal(t, tt.wantRouteKey, routes.Items[0].RouteKey)
})
@@ -899,7 +900,7 @@ func TestImportAPI_FailOnWarnings(t *testing.T) {
var apis struct {
Items []apigatewayv2.API `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &apis))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &apis, jsonx.V1()))
assert.Len(t, apis.Items, tt.wantAPIs)
})
}
diff --git a/services/apigatewayv2/handler_authorizers_test.go b/services/apigatewayv2/handler_authorizers_test.go
index 94a9d92240..a5197582d7 100644
--- a/services/apigatewayv2/handler_authorizers_test.go
+++ b/services/apigatewayv2/handler_authorizers_test.go
@@ -2,7 +2,7 @@ package apigatewayv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -48,7 +49,7 @@ func TestHandler_Authorizers(t *testing.T) {
require.Equal(t, tt.wantCreateCode, rr.Code)
var auth apigatewayv2.Authorizer
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &auth))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &auth, jsonx.V1()))
assert.Equal(t, tt.authorizerName, auth.Name)
// GetAuthorizers
@@ -245,7 +246,7 @@ func TestGetAuthorizers_Pagination(t *testing.T) {
NextToken string `json:"nextToken"`
Items []map[string]any `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
require.LessOrEqual(t, len(resp.Items), tc.maxResults)
for _, a := range resp.Items {
@@ -311,7 +312,7 @@ func setupRequestAuthAPI(
authCalls := &atomic.Int64{}
integrationCalls := &atomic.Int64{}
- respBytes, _ := json.Marshal(sc.authResponse)
+ respBytes, _ := json.Marshal(sc.authResponse, jsonx.V1())
h.SetLambdaInvoker(&mockLambdaInvoker{
fn: func(_ context.Context, name, _ string, _ []byte) ([]byte, int, error) {
@@ -324,7 +325,7 @@ func setupRequestAuthAPI(
// Integration backend function.
integrationCalls.Add(1)
- b, _ := json.Marshal(map[string]any{"statusCode": 200, "body": "ok"})
+ b, _ := json.Marshal(map[string]any{"statusCode": 200, "body": "ok"}, jsonx.V1())
return b, 200, nil
},
@@ -341,7 +342,7 @@ func setupRequestAuthAPI(
require.Equal(t, http.StatusCreated, rr.Code)
var integ apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ, jsonx.V1()))
// Authorizer.
identity := sc.identitySource
@@ -365,7 +366,7 @@ func setupRequestAuthAPI(
require.Equal(t, http.StatusCreated, rr.Code)
var auth apigatewayv2.Authorizer
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &auth))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &auth, jsonx.V1()))
authorizerID := auth.AuthorizerID
if sc.authorizerMissing {
@@ -619,7 +620,7 @@ func TestRouteAuth_AWSIAM(t *testing.T) {
fn: func(_ context.Context, _, _ string, _ []byte) ([]byte, int, error) {
integrationCalls.Add(1)
- b, _ := json.Marshal(map[string]any{"statusCode": 200, "body": "ok"})
+ b, _ := json.Marshal(map[string]any{"statusCode": 200, "body": "ok"}, jsonx.V1())
return b, 200, nil
},
@@ -635,7 +636,7 @@ func TestRouteAuth_AWSIAM(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var integ apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ, jsonx.V1()))
rr = doRequest(t, h, http.MethodPost, "/v2/apis/"+apiID+"/routes", map[string]any{
"routeKey": "GET /iam",
diff --git a/services/apigatewayv2/handler_deployments_test.go b/services/apigatewayv2/handler_deployments_test.go
index 1ff59a659c..a28a4016e5 100644
--- a/services/apigatewayv2/handler_deployments_test.go
+++ b/services/apigatewayv2/handler_deployments_test.go
@@ -1,7 +1,7 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -54,7 +55,7 @@ func TestHandler_CreateDeployment(t *testing.T) {
if tt.wantStatus == http.StatusCreated {
var deployment apigatewayv2.Deployment
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &deployment))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &deployment, jsonx.V1()))
assert.NotEmpty(t, deployment.DeploymentID)
assert.Equal(t, "DEPLOYED", deployment.DeploymentStatus)
}
@@ -95,7 +96,7 @@ func TestHandler_GetDeployment(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var deployment apigatewayv2.Deployment
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &deployment))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &deployment, jsonx.V1()))
deploymentID = deployment.DeploymentID
}
@@ -138,7 +139,7 @@ func TestHandler_DeleteDeployment(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var deployment apigatewayv2.Deployment
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &deployment))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &deployment, jsonx.V1()))
deploymentID = deployment.DeploymentID
}
@@ -213,7 +214,7 @@ func TestHandler_GetDeployments(t *testing.T) {
}
var resp listResp
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Items, tt.deployCount)
}
})
@@ -239,7 +240,7 @@ func TestHandler_UpdateDeployment(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rr.Code)
var dep apigatewayv2.Deployment
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &dep))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &dep, jsonx.V1()))
return apiID, dep.DeploymentID
},
@@ -279,7 +280,7 @@ func TestHandler_UpdateDeployment(t *testing.T) {
if tt.wantDesc != "" {
var dep apigatewayv2.Deployment
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &dep))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &dep, jsonx.V1()))
assert.Equal(t, tt.wantDesc, dep.Description)
}
})
@@ -304,7 +305,7 @@ func getStage(t *testing.T, h *apigatewayv2.Handler, apiID, stageName string) ap
require.Equal(t, http.StatusOK, rr.Code)
var s apigatewayv2.Stage
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &s))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &s, jsonx.V1()))
return s
}
@@ -320,7 +321,7 @@ func createIntegrationHelper(t *testing.T, h *apigatewayv2.Handler, apiID string
require.Equal(t, http.StatusCreated, rr.Code)
var integ apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ, jsonx.V1()))
return integ.IntegrationID
}
@@ -369,7 +370,7 @@ func TestAutoDeploy_RouteAndIntegrationChangesDeploy(t *testing.T) {
var out struct {
Items []apigatewayv2.Deployment `json:"items"`
}
- require.NoError(t, json.Unmarshal(drr.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(drr.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.Items)
var foundAuto bool
@@ -423,7 +424,7 @@ func TestGetDeployments_Pagination(t *testing.T) {
NextToken string `json:"nextToken"`
Items []map[string]any `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
require.LessOrEqual(t, len(resp.Items), 2)
for _, d := range resp.Items {
@@ -484,6 +485,6 @@ func TestCreateDeployment_ValidStageName201(t *testing.T) {
assert.Equal(t, http.StatusCreated, rr.Code)
var dep map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &dep))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &dep, jsonx.V1()))
assert.Equal(t, "DEPLOYED", dep["deploymentStatus"])
}
diff --git a/services/apigatewayv2/handler_domain_names_test.go b/services/apigatewayv2/handler_domain_names_test.go
index de26e60fda..799717c9ea 100644
--- a/services/apigatewayv2/handler_domain_names_test.go
+++ b/services/apigatewayv2/handler_domain_names_test.go
@@ -1,7 +1,8 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -59,7 +61,7 @@ func TestHandler_CreateDomainName(t *testing.T) {
if tt.wantDomain != "" {
var dn apigatewayv2.DomainName
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &dn))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &dn, jsonx.V1()))
assert.Equal(t, tt.wantDomain, dn.DomainNameValue)
}
})
@@ -138,7 +140,7 @@ func TestHandler_GetDomainNames(t *testing.T) {
var out struct {
Items []apigatewayv2.DomainName `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Items, tt.domainCnt)
}
})
@@ -181,7 +183,7 @@ func TestHandler_GetDomainName(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var dn apigatewayv2.DomainName
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &dn))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &dn, jsonx.V1()))
assert.Equal(t, "example.com", dn.DomainNameValue)
}
})
@@ -310,7 +312,7 @@ func TestHandler_CreateDomainName_WithConfigurations(t *testing.T) {
if tt.wantConfig {
var dn apigatewayv2.DomainName
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &dn))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &dn, jsonx.V1()))
require.Len(t, dn.DomainNameConfigurations, 1)
assert.Equal(t, "AVAILABLE", dn.DomainNameConfigurations[0].DomainNameStatus)
assert.Equal(t, "REGIONAL", dn.DomainNameConfigurations[0].EndpointType)
@@ -358,14 +360,14 @@ func TestDomainNameConfigurationsNullBug(t *testing.T) {
rr := doRequest(t, h, http.MethodPost, "/v2/domainnames", tt.body)
require.Equal(t, http.StatusCreated, rr.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &raw, jsonx.V1()))
_, hasKey := raw["domainNameConfigurations"]
assert.True(t, hasKey, "domainNameConfigurations key must always be present in DomainName response")
var dn apigatewayv2.DomainName
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &dn))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &dn, jsonx.V1()))
if tt.wantConfigsEmpty {
assert.Empty(t, dn.DomainNameConfigurations,
diff --git a/services/apigatewayv2/handler_export_api_sdk_test.go b/services/apigatewayv2/handler_export_api_sdk_test.go
index 932743ff66..bb51b58d42 100644
--- a/services/apigatewayv2/handler_export_api_sdk_test.go
+++ b/services/apigatewayv2/handler_export_api_sdk_test.go
@@ -1,7 +1,7 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -36,7 +37,7 @@ func TestExportApi_OutputType_RoundTrip(t *testing.T) {
require.NoError(t, err)
var asJSON map[string]any
- require.NoError(t, json.Unmarshal(jsonOut.Body, &asJSON), "OutputType=JSON must return valid JSON")
+ require.NoError(t, json.Unmarshal(jsonOut.Body, &asJSON, jsonx.V1()), "OutputType=JSON must return valid JSON")
assert.Equal(t, "3.0.1", asJSON["openapi"])
yamlOut, err := client.ExportApi(t.Context(), &apigatewayv2sdk.ExportApiInput{
@@ -47,7 +48,7 @@ func TestExportApi_OutputType_RoundTrip(t *testing.T) {
require.NoError(t, err)
require.Error(
- t, json.Unmarshal(yamlOut.Body, &asJSON),
+ t, json.Unmarshal(yamlOut.Body, &asJSON, jsonx.V1()),
"OutputType=YAML must not return the same JSON syntax as OutputType=JSON",
)
assert.Contains(t, string(yamlOut.Body), "openapi:")
diff --git a/services/apigatewayv2/handler_integration_responses_test.go b/services/apigatewayv2/handler_integration_responses_test.go
index 2e9987ff49..0f5aa058e9 100644
--- a/services/apigatewayv2/handler_integration_responses_test.go
+++ b/services/apigatewayv2/handler_integration_responses_test.go
@@ -1,7 +1,7 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -69,7 +70,7 @@ func TestHandler_CreateIntegrationResponse(t *testing.T) {
require.Equal(t, http.StatusCreated, integrationRR.Code)
var integ apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(integrationRR.Body.Bytes(), &integ))
+ require.NoError(t, json.Unmarshal(integrationRR.Body.Bytes(), &integ, jsonx.V1()))
integrationID = integ.IntegrationID
}
@@ -91,7 +92,7 @@ func TestHandler_CreateIntegrationResponse(t *testing.T) {
if tt.wantRespKey != "" {
var ir apigatewayv2.IntegrationResponse
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &ir))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &ir, jsonx.V1()))
assert.Equal(t, tt.wantRespKey, ir.IntegrationResponseKey)
assert.NotEmpty(t, ir.IntegrationResponseID)
}
@@ -125,7 +126,7 @@ func TestHandler_DuplicateIntegrationResponseKey(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var integration apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration, jsonx.V1()))
path := fmt.Sprintf("/v2/apis/%s/integrations/%s/integrationresponses", apiID, integration.IntegrationID)
@@ -195,7 +196,7 @@ func TestHandler_GetIntegrationResponses(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var integration apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration, jsonx.V1()))
integrationID = integration.IntegrationID
}
@@ -215,7 +216,7 @@ func TestHandler_GetIntegrationResponses(t *testing.T) {
var out struct {
Items []apigatewayv2.IntegrationResponse `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Items, tt.responseCnt)
}
})
@@ -255,7 +256,7 @@ func TestHandler_GetIntegrationResponse(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var integration apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration, jsonx.V1()))
path := fmt.Sprintf("/v2/apis/%s/integrations/%s/integrationresponses", apiID, integration.IntegrationID)
rr = doRequest(t, h, http.MethodPost, path, map[string]any{
@@ -264,7 +265,7 @@ func TestHandler_GetIntegrationResponse(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var ir apigatewayv2.IntegrationResponse
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &ir))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &ir, jsonx.V1()))
responseID := ir.IntegrationResponseID
if tt.useWrongID {
@@ -312,7 +313,7 @@ func TestHandler_DeleteIntegrationResponse(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var integration apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration, jsonx.V1()))
path := fmt.Sprintf("/v2/apis/%s/integrations/%s/integrationresponses", apiID, integration.IntegrationID)
rr = doRequest(t, h, http.MethodPost, path, map[string]any{
@@ -321,7 +322,7 @@ func TestHandler_DeleteIntegrationResponse(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var ir apigatewayv2.IntegrationResponse
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &ir))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &ir, jsonx.V1()))
responseID := ir.IntegrationResponseID
if tt.useWrongID {
@@ -354,14 +355,14 @@ func TestHandler_UpdateIntegrationResponse(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rr.Code)
var i apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &i))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &i, jsonx.V1()))
rr = doRequest(t, h, http.MethodPost,
fmt.Sprintf("/v2/apis/%s/integrations/%s/integrationresponses", apiID, i.IntegrationID),
map[string]any{"integrationResponseKey": "200"})
require.Equal(t, http.StatusCreated, rr.Code)
var ir apigatewayv2.IntegrationResponse
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &ir))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &ir, jsonx.V1()))
return apiID, i.IntegrationID, ir.IntegrationResponseID
},
diff --git a/services/apigatewayv2/handler_integrations_test.go b/services/apigatewayv2/handler_integrations_test.go
index 7a5e9ca429..5f1fcceda9 100644
--- a/services/apigatewayv2/handler_integrations_test.go
+++ b/services/apigatewayv2/handler_integrations_test.go
@@ -1,7 +1,7 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -54,7 +55,7 @@ func TestHandler_CreateIntegration(t *testing.T) {
if tt.wantStatus == http.StatusCreated {
var integration apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration, jsonx.V1()))
assert.Equal(t, tt.integrationType, integration.IntegrationType)
assert.NotEmpty(t, integration.IntegrationID)
}
@@ -123,7 +124,7 @@ func TestHandler_GetIntegrations(t *testing.T) {
}
var resp listResp
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Items, len(tt.integrations))
}
})
@@ -165,7 +166,7 @@ func TestHandler_GetIntegration(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var integration apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration, jsonx.V1()))
integrationID = integration.IntegrationID
}
@@ -210,7 +211,7 @@ func TestHandler_DeleteIntegration(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var integration apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration, jsonx.V1()))
integrationID = integration.IntegrationID
}
@@ -261,7 +262,7 @@ func TestHandler_UpdateIntegration(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var integration apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integration, jsonx.V1()))
integrationID = integration.IntegrationID
}
@@ -350,7 +351,7 @@ func TestHandler_CreateIntegration_InvalidType(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
rr = doRequest(t, h, http.MethodPost, fmt.Sprintf("/v2/apis/%s/integrations", api.APIID), tt.body)
assert.Equal(t, tt.wantStatus, rr.Code)
@@ -405,7 +406,7 @@ func TestGetIntegrations_Pagination(t *testing.T) {
NextToken string `json:"nextToken"`
Items []map[string]any `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
require.LessOrEqual(t, len(resp.Items), tc.maxResults)
for _, s := range resp.Items {
diff --git a/services/apigatewayv2/handler_portals.go b/services/apigatewayv2/handler_portals.go
index 9f90fa6f98..a7a250367d 100644
--- a/services/apigatewayv2/handler_portals.go
+++ b/services/apigatewayv2/handler_portals.go
@@ -1,7 +1,8 @@
package apigatewayv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -9,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -449,7 +451,12 @@ func (h *Handler) handleUpdatePortal(c *echo.Context, portalID string) error {
func (h *Handler) handlePublishPortal(c *echo.Context, portalID string) error {
var input PublishPortalInput
- if err := json.NewDecoder(c.Request().Body).Decode(&input); err != nil && !errors.Is(err, io.EOF) {
+ if err := json.UnmarshalDecode(
+ jsontext.NewDecoder(c.Request().Body),
+ &input,
+ jsonx.V1(),
+ ); err != nil &&
+ !errors.Is(err, io.EOF) {
return writeErr(c, http.StatusBadRequest, msgInvalidBody)
}
@@ -521,7 +528,7 @@ func (h *Handler) handleGetPortalProductSharingPolicy(c *echo.Context, portalPro
func (h *Handler) handlePutPortalProductSharingPolicy(c *echo.Context, portalProductID string) error {
var input PortalProductSharingPolicy
- if err := json.NewDecoder(c.Request().Body).Decode(&input); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &input, jsonx.V1()); err != nil {
return writeErr(c, http.StatusBadRequest, msgInvalidBody)
}
diff --git a/services/apigatewayv2/handler_portals_test.go b/services/apigatewayv2/handler_portals_test.go
index 397f1886db..e53bbcf98e 100644
--- a/services/apigatewayv2/handler_portals_test.go
+++ b/services/apigatewayv2/handler_portals_test.go
@@ -1,7 +1,7 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -123,7 +124,7 @@ func TestHandler_CreatePortal(t *testing.T) {
if tt.wantPortal {
var portal apigatewayv2.Portal
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &portal))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &portal, jsonx.V1()))
assert.NotEmpty(t, portal.PortalID)
assert.Empty(t, portal.PublishStatus)
require.NotNil(t, portal.LastModified)
@@ -184,7 +185,7 @@ func TestHandler_CreatePortalProduct(t *testing.T) {
if tt.wantName != "" {
var product apigatewayv2.PortalProduct
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &product))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &product, jsonx.V1()))
assert.Equal(t, tt.wantName, product.DisplayName)
assert.NotEmpty(t, product.PortalProductID)
}
@@ -234,7 +235,7 @@ func TestHandler_CreateProductPage(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var product apigatewayv2.PortalProduct
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &product))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &product, jsonx.V1()))
portalProductID = product.PortalProductID
}
@@ -252,7 +253,7 @@ func TestHandler_CreateProductPage(t *testing.T) {
if tt.wantPage {
var page apigatewayv2.ProductPage
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &page))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &page, jsonx.V1()))
assert.NotEmpty(t, page.ProductPageID)
}
})
@@ -333,7 +334,7 @@ func TestHandler_CreateProductRestEndpointPage(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var product apigatewayv2.PortalProduct
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &product))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &product, jsonx.V1()))
portalProductID = product.PortalProductID
}
@@ -351,7 +352,7 @@ func TestHandler_CreateProductRestEndpointPage(t *testing.T) {
if tt.wantPage {
var page apigatewayv2.ProductRestEndpointPage
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &page))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &page, jsonx.V1()))
assert.NotEmpty(t, page.ProductRestEndpointPageID)
require.NotNil(t, page.RestEndpointIdentifier)
require.NotNil(t, page.RestEndpointIdentifier.IdentifierParts)
@@ -399,7 +400,7 @@ func TestHandler_ListPortals(t *testing.T) {
var out struct {
Items []apigatewayv2.Portal `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Items, tt.portalCnt)
})
}
@@ -434,7 +435,7 @@ func TestHandler_GetPortal(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var portal apigatewayv2.Portal
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &portal))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &portal, jsonx.V1()))
portalID := portal.PortalID
if tt.useWrongID {
@@ -486,7 +487,7 @@ func TestHandler_ListPortalProducts(t *testing.T) {
var out struct {
Items []apigatewayv2.PortalProduct `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Items, tt.productCnt)
})
}
@@ -523,7 +524,7 @@ func TestHandler_GetPortalProduct(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var pp apigatewayv2.PortalProduct
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &pp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &pp, jsonx.V1()))
ppID := pp.PortalProductID
if tt.useWrongID {
@@ -578,7 +579,7 @@ func TestHandler_ListProductPages(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var pp apigatewayv2.PortalProduct
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &pp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &pp, jsonx.V1()))
ppID = pp.PortalProductID
}
@@ -596,7 +597,7 @@ func TestHandler_ListProductPages(t *testing.T) {
var out struct {
Items []apigatewayv2.ProductPage `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Items, tt.pageCnt)
}
})
@@ -645,7 +646,7 @@ func TestHandler_ListProductRestEndpointPages(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var pp apigatewayv2.PortalProduct
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &pp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &pp, jsonx.V1()))
ppID = pp.PortalProductID
}
@@ -671,7 +672,7 @@ func TestHandler_ListProductRestEndpointPages(t *testing.T) {
var out struct {
Items []apigatewayv2.ProductRestEndpointPage `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Items, tt.pageCnt)
}
})
@@ -683,7 +684,7 @@ func createPortal(t *testing.T, h *apigatewayv2.Handler) string {
rr := doRequest(t, h, http.MethodPost, "/v2/portals", validCreatePortalBody())
require.Equal(t, http.StatusCreated, rr.Code)
var p apigatewayv2.Portal
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &p))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &p, jsonx.V1()))
return p.PortalID
}
@@ -695,7 +696,7 @@ func createPortalProduct(t *testing.T, h *apigatewayv2.Handler) string {
})
require.Equal(t, http.StatusCreated, rr.Code)
var pp apigatewayv2.PortalProduct
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &pp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &pp, jsonx.V1()))
return pp.PortalProductID
}
@@ -879,7 +880,7 @@ func TestHandler_GetProductPage(t *testing.T) {
fmt.Sprintf("/v2/portalproducts/%s/productpages", ppID), map[string]any{})
require.Equal(t, http.StatusCreated, rr.Code)
var p apigatewayv2.ProductPage
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &p))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &p, jsonx.V1()))
return ppID, p.ProductPageID
},
@@ -932,7 +933,7 @@ func TestHandler_DeleteProductPage(t *testing.T) {
fmt.Sprintf("/v2/portalproducts/%s/productpages", ppID), map[string]any{})
require.Equal(t, http.StatusCreated, rr.Code)
var p apigatewayv2.ProductPage
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &p))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &p, jsonx.V1()))
return ppID, p.ProductPageID
},
@@ -986,7 +987,7 @@ func TestHandler_GetProductRestEndpointPage(t *testing.T) {
)
require.Equal(t, http.StatusCreated, rr.Code)
var p apigatewayv2.ProductRestEndpointPage
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &p))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &p, jsonx.V1()))
return ppID, p.ProductRestEndpointPageID
},
@@ -1040,7 +1041,7 @@ func TestHandler_DeleteProductRestEndpointPage(t *testing.T) {
)
require.Equal(t, http.StatusCreated, rr.Code)
var p apigatewayv2.ProductRestEndpointPage
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &p))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &p, jsonx.V1()))
return ppID, p.ProductRestEndpointPageID
},
@@ -1094,7 +1095,7 @@ func TestHandler_ListPortals_MaxResultsHonoured(t *testing.T) {
NextToken string `json:"nextToken"`
Items []apigatewayv2.Portal `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &page1, jsonx.V1()))
require.Len(t, page1.Items, 1, "maxResults=1 must limit the page to 1 item")
require.NotEmpty(t, page1.NextToken, "a partial page must return a nextToken")
@@ -1106,7 +1107,7 @@ func TestHandler_ListPortals_MaxResultsHonoured(t *testing.T) {
NextToken string `json:"nextToken"`
Items []apigatewayv2.Portal `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rr2.Body.Bytes(), &page2, jsonx.V1()))
require.Len(t, page2.Items, portalCount-1, "second page must return the remainder")
}
@@ -1130,7 +1131,7 @@ func TestHandler_ListPortalProducts_MaxResultsHonoured(t *testing.T) {
NextToken string `json:"nextToken"`
Items []apigatewayv2.PortalProduct `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &page1, jsonx.V1()))
require.Len(t, page1.Items, 1, "maxResults=1 must limit the page to 1 item")
require.NotEmpty(t, page1.NextToken, "a partial page must return a nextToken")
@@ -1142,6 +1143,6 @@ func TestHandler_ListPortalProducts_MaxResultsHonoured(t *testing.T) {
NextToken string `json:"nextToken"`
Items []apigatewayv2.PortalProduct `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rr2.Body.Bytes(), &page2, jsonx.V1()))
require.Len(t, page2.Items, productCount-1, "second page must return the remainder")
}
diff --git a/services/apigatewayv2/handler_route_responses_test.go b/services/apigatewayv2/handler_route_responses_test.go
index e99cd4209e..3c276fb51a 100644
--- a/services/apigatewayv2/handler_route_responses_test.go
+++ b/services/apigatewayv2/handler_route_responses_test.go
@@ -1,7 +1,7 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -69,7 +70,7 @@ func TestHandler_CreateRouteResponse(t *testing.T) {
require.Equal(t, http.StatusCreated, routeRR.Code)
var route apigatewayv2.Route
- require.NoError(t, json.Unmarshal(routeRR.Body.Bytes(), &route))
+ require.NoError(t, json.Unmarshal(routeRR.Body.Bytes(), &route, jsonx.V1()))
routeID = route.RouteID
}
@@ -91,7 +92,7 @@ func TestHandler_CreateRouteResponse(t *testing.T) {
if tt.wantRespKey != "" {
var rresp apigatewayv2.RouteResponse
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &rresp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &rresp, jsonx.V1()))
assert.Equal(t, tt.wantRespKey, rresp.RouteResponseKey)
assert.NotEmpty(t, rresp.RouteResponseID)
}
@@ -125,7 +126,7 @@ func TestHandler_DuplicateRouteResponseKey(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var route apigatewayv2.Route
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route, jsonx.V1()))
path := fmt.Sprintf("/v2/apis/%s/routes/%s/routeresponses", apiID, route.RouteID)
@@ -187,7 +188,7 @@ func TestHandler_GetRouteResponses(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var route apigatewayv2.Route
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route, jsonx.V1()))
routeID = route.RouteID
}
@@ -199,7 +200,7 @@ func TestHandler_GetRouteResponses(t *testing.T) {
var out struct {
Items []apigatewayv2.RouteResponse `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Items, tt.responseCnt)
}
})
@@ -238,7 +239,7 @@ func TestHandler_GetRouteResponse(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var route apigatewayv2.Route
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route, jsonx.V1()))
path := fmt.Sprintf("/v2/apis/%s/routes/%s/routeresponses", apiID, route.RouteID)
rr = doRequest(t, h, http.MethodPost, path, map[string]any{
@@ -247,7 +248,7 @@ func TestHandler_GetRouteResponse(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var rr2 apigatewayv2.RouteResponse
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &rr2))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &rr2, jsonx.V1()))
responseID := rr2.RouteResponseID
if tt.useWrongID {
@@ -293,7 +294,7 @@ func TestHandler_DeleteRouteResponse(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var route apigatewayv2.Route
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route, jsonx.V1()))
path := fmt.Sprintf("/v2/apis/%s/routes/%s/routeresponses", apiID, route.RouteID)
rr = doRequest(t, h, http.MethodPost, path, map[string]any{
@@ -302,7 +303,7 @@ func TestHandler_DeleteRouteResponse(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var rr2 apigatewayv2.RouteResponse
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &rr2))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &rr2, jsonx.V1()))
responseID := rr2.RouteResponseID
if tt.useWrongID {
@@ -334,14 +335,14 @@ func TestHandler_UpdateRouteResponse(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rr.Code)
var r apigatewayv2.Route
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &r))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &r, jsonx.V1()))
rr = doRequest(t, h, http.MethodPost,
fmt.Sprintf("/v2/apis/%s/routes/%s/routeresponses", apiID, r.RouteID),
map[string]any{"routeResponseKey": "$default"})
require.Equal(t, http.StatusCreated, rr.Code)
var rr2 apigatewayv2.RouteResponse
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &rr2))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &rr2, jsonx.V1()))
return apiID, r.RouteID, rr2.RouteResponseID
},
diff --git a/services/apigatewayv2/handler_routes_test.go b/services/apigatewayv2/handler_routes_test.go
index 6d535e431e..88edf4b4fc 100644
--- a/services/apigatewayv2/handler_routes_test.go
+++ b/services/apigatewayv2/handler_routes_test.go
@@ -1,7 +1,7 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -54,7 +55,7 @@ func TestHandler_CreateRoute(t *testing.T) {
if tt.wantStatus == http.StatusCreated {
var route apigatewayv2.Route
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route, jsonx.V1()))
assert.Equal(t, tt.routeKey, route.RouteKey)
assert.NotEmpty(t, route.RouteID)
}
@@ -117,7 +118,7 @@ func TestHandler_GetRoutes(t *testing.T) {
}
var resp listResp
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Items, len(tt.routes))
}
})
@@ -159,7 +160,7 @@ func TestHandler_GetRoute(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var route apigatewayv2.Route
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route, jsonx.V1()))
routeID = route.RouteID
}
@@ -204,7 +205,7 @@ func TestHandler_DeleteRoute(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var route apigatewayv2.Route
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route, jsonx.V1()))
routeID = route.RouteID
}
@@ -249,7 +250,7 @@ func TestHandler_UpdateRoute(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var route apigatewayv2.Route
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &route, jsonx.V1()))
routeID = route.RouteID
}
@@ -355,7 +356,7 @@ func TestUpdateRouteClearsAuthorizerIDOnNone(t *testing.T) {
require.Equal(t, http.StatusCreated, authRR.Code)
var authResult map[string]any
- require.NoError(t, json.Unmarshal(authRR.Body.Bytes(), &authResult))
+ require.NoError(t, json.Unmarshal(authRR.Body.Bytes(), &authResult, jsonx.V1()))
authID := authResult["authorizerId"].(string)
// Create a route with JWT authorization.
@@ -367,7 +368,7 @@ func TestUpdateRouteClearsAuthorizerIDOnNone(t *testing.T) {
require.Equal(t, http.StatusCreated, routeRR.Code)
var routeResult map[string]any
- require.NoError(t, json.Unmarshal(routeRR.Body.Bytes(), &routeResult))
+ require.NoError(t, json.Unmarshal(routeRR.Body.Bytes(), &routeResult, jsonx.V1()))
routeID := routeResult["routeId"].(string)
// Patch the route with the test body.
@@ -381,7 +382,7 @@ func TestUpdateRouteClearsAuthorizerIDOnNone(t *testing.T) {
require.Equal(t, http.StatusOK, patchRR.Code)
var updated map[string]any
- require.NoError(t, json.Unmarshal(patchRR.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(patchRR.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, tt.wantAuthType, updated["authorizationType"])
@@ -417,7 +418,7 @@ func TestUpdateRouteClearsAuthorizerIDRoundtrip(t *testing.T) {
require.Equal(t, http.StatusCreated, authRR.Code)
var authResult map[string]any
- require.NoError(t, json.Unmarshal(authRR.Body.Bytes(), &authResult))
+ require.NoError(t, json.Unmarshal(authRR.Body.Bytes(), &authResult, jsonx.V1()))
authID := authResult["authorizerId"].(string)
// Create route with JWT auth.
@@ -429,7 +430,7 @@ func TestUpdateRouteClearsAuthorizerIDRoundtrip(t *testing.T) {
require.Equal(t, http.StatusCreated, routeRR.Code)
var routeResult map[string]any
- require.NoError(t, json.Unmarshal(routeRR.Body.Bytes(), &routeResult))
+ require.NoError(t, json.Unmarshal(routeRR.Body.Bytes(), &routeResult, jsonx.V1()))
routeID := routeResult["routeId"].(string)
// Verify initial state: JWT auth with authorizer ID set.
@@ -446,7 +447,7 @@ func TestUpdateRouteClearsAuthorizerIDRoundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, getRR.Code)
var getResult map[string]any
- require.NoError(t, json.Unmarshal(getRR.Body.Bytes(), &getResult))
+ require.NoError(t, json.Unmarshal(getRR.Body.Bytes(), &getResult, jsonx.V1()))
assert.Equal(t, "NONE", getResult["authorizationType"])
assert.Empty(t, getResult["authorizerId"], "authorizerId should be empty after UpdateRoute to NONE")
@@ -498,7 +499,7 @@ func TestGetRoutes_Pagination(t *testing.T) {
NextToken string `json:"nextToken"`
Items []apigatewayv2.Route `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
require.LessOrEqual(t, len(resp.Items), tc.maxResults)
for _, r := range resp.Items {
@@ -540,7 +541,7 @@ func TestCreateRoute_APIKeyRequiredPresentAndFalse(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
v, hasKey := resp["apiKeyRequired"]
assert.True(t, hasKey, "apiKeyRequired should be present in response")
@@ -562,7 +563,7 @@ func TestCreateRoute_AuthorizationScopesIsEmptyArray(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
raw, hasKey := resp["authorizationScopes"]
assert.True(t, hasKey, "authorizationScopes should be present in response")
@@ -586,7 +587,7 @@ func TestGetRoute_AuthorizationScopesIsEmptyArray(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &created, jsonx.V1()))
routeID, _ := created["routeId"].(string)
require.NotEmpty(t, routeID)
@@ -595,7 +596,7 @@ func TestGetRoute_AuthorizationScopesIsEmptyArray(t *testing.T) {
require.Equal(t, http.StatusOK, rr2.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(rr2.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rr2.Body.Bytes(), &got, jsonx.V1()))
raw, hasKey := got["authorizationScopes"]
assert.True(t, hasKey, "authorizationScopes should be present in GetRoute response")
diff --git a/services/apigatewayv2/handler_stages_test.go b/services/apigatewayv2/handler_stages_test.go
index 9759e2bbce..dac4678ee6 100644
--- a/services/apigatewayv2/handler_stages_test.go
+++ b/services/apigatewayv2/handler_stages_test.go
@@ -1,7 +1,7 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -54,7 +55,7 @@ func TestHandler_CreateStage(t *testing.T) {
if tt.wantStatus == http.StatusCreated {
var stage apigatewayv2.Stage
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &stage))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &stage, jsonx.V1()))
assert.Equal(t, tt.stageName, stage.StageName)
}
})
@@ -116,7 +117,7 @@ func TestHandler_GetStages(t *testing.T) {
}
var resp listResp
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Items, len(tt.stages))
}
})
@@ -306,7 +307,7 @@ func TestGetStages_Pagination(t *testing.T) {
NextToken string `json:"nextToken"`
Items []map[string]any `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
require.LessOrEqual(t, len(resp.Items), tc.maxResults)
for _, s := range resp.Items {
diff --git a/services/apigatewayv2/handler_tags.go b/services/apigatewayv2/handler_tags.go
index cb84fee7c4..8e2ee0a080 100644
--- a/services/apigatewayv2/handler_tags.go
+++ b/services/apigatewayv2/handler_tags.go
@@ -1,7 +1,8 @@
package apigatewayv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -9,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -75,7 +77,7 @@ func (h *Handler) handleTagResource(c *echo.Context, resourceARN string) error {
log := logger.Load(c.Request().Context())
var input tagResourceInput
- if err := json.NewDecoder(c.Request().Body).Decode(&input); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &input, jsonx.V1()); err != nil {
return writeErr(c, http.StatusBadRequest, msgInvalidBody)
}
diff --git a/services/apigatewayv2/handler_tags_test.go b/services/apigatewayv2/handler_tags_test.go
index d8ba9fd64f..6e44eea8d3 100644
--- a/services/apigatewayv2/handler_tags_test.go
+++ b/services/apigatewayv2/handler_tags_test.go
@@ -1,7 +1,7 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_Tags(t *testing.T) {
@@ -75,7 +77,7 @@ func TestHandler_Tags(t *testing.T) {
var out struct {
Tags map[string]string `json:"tags"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantTags, out.Tags)
}
})
diff --git a/services/apigatewayv2/handler_test.go b/services/apigatewayv2/handler_test.go
index eb896410a1..ec86211a08 100644
--- a/services/apigatewayv2/handler_test.go
+++ b/services/apigatewayv2/handler_test.go
@@ -3,7 +3,7 @@ package apigatewayv2_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -29,7 +30,7 @@ func doRequest(t *testing.T, h *apigatewayv2.Handler, method, path string, body
var bodyReader *bytes.Reader
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
bodyReader = bytes.NewReader(b)
} else {
@@ -77,7 +78,7 @@ func createAPI(t *testing.T, h *apigatewayv2.Handler, name string) string {
require.Equal(t, http.StatusCreated, rr.Code)
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
return api.APIID
}
diff --git a/services/apigatewayv2/handler_vpc_links_test.go b/services/apigatewayv2/handler_vpc_links_test.go
index 43e9895a18..41cd5fffa5 100644
--- a/services/apigatewayv2/handler_vpc_links_test.go
+++ b/services/apigatewayv2/handler_vpc_links_test.go
@@ -1,13 +1,15 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -46,14 +48,14 @@ func TestVpcLinkSecurityGroupIDsNullBug(t *testing.T) {
rr := doRequest(t, h, http.MethodPost, "/v2/vpclinks", tt.body)
require.Equal(t, http.StatusCreated, rr.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &raw, jsonx.V1()))
_, ok := raw["securityGroupIds"]
assert.True(t, ok, "securityGroupIds key must always be present in VpcLink response")
var link apigatewayv2.VpcLink
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &link))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &link, jsonx.V1()))
assert.Equal(t, tt.wantSecGroups, link.SecurityGroupIDs)
})
}
@@ -71,8 +73,8 @@ func TestVpcLinkSubnetIDsNullBug(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rr.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &raw, jsonx.V1()))
_, ok := raw["subnetIds"]
assert.True(t, ok, "subnetIds key must always be present in VpcLink response")
@@ -92,13 +94,13 @@ func TestVpcLinkGetReturnsArrayFields(t *testing.T) {
require.Equal(t, http.StatusCreated, createRR.Code)
var created apigatewayv2.VpcLink
- require.NoError(t, json.Unmarshal(createRR.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRR.Body.Bytes(), &created, jsonx.V1()))
getRR := doRequest(t, h, http.MethodGet, "/v2/vpclinks/"+created.VpcLinkID, nil)
require.Equal(t, http.StatusOK, getRR.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(getRR.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(getRR.Body.Bytes(), &raw, jsonx.V1()))
_, hasSec := raw["securityGroupIds"]
assert.True(t, hasSec, "securityGroupIds key must be present in GetVpcLink response")
diff --git a/services/apigatewayv2/http_proxy.go b/services/apigatewayv2/http_proxy.go
index bcf614317b..5a81bbbae4 100644
--- a/services/apigatewayv2/http_proxy.go
+++ b/services/apigatewayv2/http_proxy.go
@@ -2,7 +2,7 @@ package apigatewayv2
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -19,6 +19,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -373,7 +374,7 @@ func buildV1Payload(
},
}
- p, _ := json.Marshal(event)
+ p, _ := json.Marshal(event, jsonx.V1())
return p
}
@@ -451,7 +452,7 @@ func buildV2Payload(
},
}
- p, _ := json.Marshal(event)
+ p, _ := json.Marshal(event, jsonx.V1())
return p
}
diff --git a/services/apigatewayv2/http_proxy_flows_test.go b/services/apigatewayv2/http_proxy_flows_test.go
index c29cee4d90..05d211089c 100644
--- a/services/apigatewayv2/http_proxy_flows_test.go
+++ b/services/apigatewayv2/http_proxy_flows_test.go
@@ -2,7 +2,7 @@ package apigatewayv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"maps"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -39,7 +40,7 @@ func newFlowAPI(t *testing.T, cors map[string]any) *flowAPI {
require.Equal(t, http.StatusCreated, rr.Code)
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
return &flowAPI{h: h, apiID: api.APIID}
}
@@ -51,7 +52,7 @@ func (f *flowAPI) integration(t *testing.T, body map[string]any) string {
require.Equal(t, http.StatusCreated, rr.Code, rr.Body.String())
var integ apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ, jsonx.V1()))
return integ.IntegrationID
}
@@ -95,7 +96,7 @@ func (r *recordedEvents) last(t *testing.T) map[string]any {
func recordingInvoker(rec *recordedEvents, response string) *mockLambdaInvoker {
return &mockLambdaInvoker{fn: func(_ context.Context, _, _ string, payload []byte) ([]byte, int, error) {
var ev map[string]any
- if err := json.Unmarshal(payload, &ev); err == nil {
+ if err := json.Unmarshal(payload, &ev, jsonx.V1()); err == nil {
rec.mu.Lock()
rec.events = append(rec.events, ev)
rec.mu.Unlock()
@@ -359,7 +360,7 @@ func TestHTTPAPIProxy_JWTAuthorizerCognitoFlow(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var authz apigatewayv2.Authorizer
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &authz))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &authz, jsonx.V1()))
extra := map[string]any{"authorizationType": "JWT", "authorizerId": authz.AuthorizerID}
if tt.scopes != nil {
@@ -418,7 +419,7 @@ func TestHTTPAPIProxy_LambdaAuthorizerContext(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var authz apigatewayv2.Authorizer
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &authz))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &authz, jsonx.V1()))
f.lambdaRoute(t, "GET /secure", "2.0",
map[string]any{"authorizationType": "CUSTOM", "authorizerId": authz.AuthorizerID})
diff --git a/services/apigatewayv2/http_proxy_test.go b/services/apigatewayv2/http_proxy_test.go
index b4dd6947d7..3f32f6f887 100644
--- a/services/apigatewayv2/http_proxy_test.go
+++ b/services/apigatewayv2/http_proxy_test.go
@@ -2,7 +2,7 @@ package apigatewayv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -36,7 +37,7 @@ func (m *mockLambdaInvoker) InvokeFunction(
}
resp := map[string]any{"statusCode": 200, "body": "ok"}
- b, _ := json.Marshal(resp)
+ b, _ := json.Marshal(resp, jsonx.V1())
return b, 200, nil
}
@@ -119,7 +120,7 @@ func buildHTTPAPIWithLambda(
require.Equal(t, http.StatusCreated, rr.Code)
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
theAPIID := api.APIID
@@ -132,7 +133,7 @@ func buildHTTPAPIWithLambda(
require.Equal(t, http.StatusCreated, rr.Code)
var integ apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ, jsonx.V1()))
theIntegrationID := integ.IntegrationID
@@ -222,12 +223,12 @@ func TestHTTPAPIProxy_RouteMatching(t *testing.T) {
lambda := &mockLambdaInvoker{
fn: func(_ context.Context, _, _ string, payload []byte) ([]byte, int, error) {
var ev map[string]any
- if err := json.Unmarshal(payload, &ev); err == nil {
+ if err := json.Unmarshal(payload, &ev, jsonx.V1()); err == nil {
capturedRouteKey, _ = ev["routeKey"].(string)
}
resp := map[string]any{"statusCode": 200, "body": "routed"}
- b, _ := json.Marshal(resp)
+ b, _ := json.Marshal(resp, jsonx.V1())
return b, 200, nil
},
@@ -247,7 +248,7 @@ func TestHTTPAPIProxy_RouteMatching(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var integ apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ, jsonx.V1()))
rr = doRequest(t, h, http.MethodPost, "/v2/apis/"+apiID+"/routes", map[string]any{
"routeKey": rk,
@@ -337,10 +338,10 @@ func TestHTTPAPIProxy_PayloadFormat(t *testing.T) {
var capturedPayload map[string]any
lambda := &mockLambdaInvoker{
fn: func(_ context.Context, _, _ string, payload []byte) ([]byte, int, error) {
- _ = json.Unmarshal(payload, &capturedPayload)
+ _ = json.Unmarshal(payload, &capturedPayload, jsonx.V1())
resp := map[string]any{"statusCode": 200, "body": "ok"}
- b, _ := json.Marshal(resp)
+ b, _ := json.Marshal(resp, jsonx.V1())
return b, 200, nil
},
@@ -354,7 +355,7 @@ func TestHTTPAPIProxy_PayloadFormat(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
rr = doRequest(t, h, http.MethodPost, "/v2/apis/"+api.APIID+"/integrations", map[string]any{
"integrationType": "AWS_PROXY",
@@ -364,7 +365,7 @@ func TestHTTPAPIProxy_PayloadFormat(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var integ apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ, jsonx.V1()))
rr = doRequest(t, h, http.MethodPost, "/v2/apis/"+api.APIID+"/routes", map[string]any{
"routeKey": "GET /test",
@@ -414,7 +415,7 @@ func TestHTTPAPIProxy_CORSPreflight(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
// Create a route (even though OPTIONS won't use it).
rr = doRequest(t, h, http.MethodPost, "/v2/apis/"+api.APIID+"/integrations", map[string]any{
@@ -424,7 +425,7 @@ func TestHTTPAPIProxy_CORSPreflight(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var integ apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ, jsonx.V1()))
doRequest(t, h, http.MethodPost, "/v2/apis/"+api.APIID+"/routes", map[string]any{
"routeKey": "GET /items",
@@ -459,7 +460,7 @@ func TestHTTPAPIProxy_JWTAuthorizer(t *testing.T) {
fn: func(_ context.Context, _, _ string, _ []byte) ([]byte, int, error) {
integrationCalls.Add(1)
- b, _ := json.Marshal(map[string]any{"statusCode": 200, "body": "ok"})
+ b, _ := json.Marshal(map[string]any{"statusCode": 200, "body": "ok"}, jsonx.V1())
return b, 200, nil
},
@@ -472,7 +473,7 @@ func TestHTTPAPIProxy_JWTAuthorizer(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
// Create JWT authorizer.
rr = doRequest(t, h, http.MethodPost, "/v2/apis/"+api.APIID+"/authorizers", map[string]any{
@@ -487,7 +488,7 @@ func TestHTTPAPIProxy_JWTAuthorizer(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var authorizer apigatewayv2.Authorizer
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &authorizer))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &authorizer, jsonx.V1()))
// Create integration + route with JWT auth.
rr = doRequest(t, h, http.MethodPost, "/v2/apis/"+api.APIID+"/integrations", map[string]any{
@@ -497,7 +498,7 @@ func TestHTTPAPIProxy_JWTAuthorizer(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var integ apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ, jsonx.V1()))
rr = doRequest(t, h, http.MethodPost, "/v2/apis/"+api.APIID+"/routes", map[string]any{
"routeKey": "GET /secure",
@@ -535,7 +536,7 @@ func TestHTTPAPIProxy_JWTAuthorizerMissing_DoesNotInvokeIntegration(t *testing.T
fn: func(_ context.Context, _, _ string, _ []byte) ([]byte, int, error) {
integrationCalls.Add(1)
- b, _ := json.Marshal(map[string]any{"statusCode": 200, "body": "ok"})
+ b, _ := json.Marshal(map[string]any{"statusCode": 200, "body": "ok"}, jsonx.V1())
return b, 200, nil
},
@@ -547,7 +548,7 @@ func TestHTTPAPIProxy_JWTAuthorizerMissing_DoesNotInvokeIntegration(t *testing.T
require.Equal(t, http.StatusCreated, rr.Code)
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
rr = doRequest(t, h, http.MethodPost, "/v2/apis/"+api.APIID+"/integrations", map[string]any{
"integrationType": "AWS_PROXY",
@@ -556,7 +557,7 @@ func TestHTTPAPIProxy_JWTAuthorizerMissing_DoesNotInvokeIntegration(t *testing.T
require.Equal(t, http.StatusCreated, rr.Code)
var integ apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ, jsonx.V1()))
// authorizerId names an authorizer that was never created.
rr = doRequest(t, h, http.MethodPost, "/v2/apis/"+api.APIID+"/routes", map[string]any{
@@ -612,7 +613,7 @@ func TestHTTPAPIProxy_JWTAuthorizer_ValidCognitoToken(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var api apigatewayv2.API
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &api, jsonx.V1()))
rr = doRequest(t, h, http.MethodPost, "/v2/apis/"+api.APIID+"/authorizers", map[string]any{
"name": "cognito-jwt",
@@ -626,7 +627,7 @@ func TestHTTPAPIProxy_JWTAuthorizer_ValidCognitoToken(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var authorizer apigatewayv2.Authorizer
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &authorizer))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &authorizer, jsonx.V1()))
rr = doRequest(t, h, http.MethodPost, "/v2/apis/"+api.APIID+"/integrations", map[string]any{
"integrationType": "AWS_PROXY",
@@ -635,7 +636,7 @@ func TestHTTPAPIProxy_JWTAuthorizer_ValidCognitoToken(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var integ apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ, jsonx.V1()))
rr = doRequest(t, h, http.MethodPost, "/v2/apis/"+api.APIID+"/routes", map[string]any{
"routeKey": "GET /secure",
@@ -707,7 +708,7 @@ func TestHTTPAPIProxy_LambdaResponse(t *testing.T) {
lambda := &mockLambdaInvoker{
fn: func(_ context.Context, _, _ string, _ []byte) ([]byte, int, error) {
- b, _ := json.Marshal(tt.lambdaBody)
+ b, _ := json.Marshal(tt.lambdaBody, jsonx.V1())
return b, 200, nil
},
@@ -744,13 +745,13 @@ func TestHTTPAPIProxy_PathParameters(t *testing.T) {
h.SetLambdaInvoker(&mockLambdaInvoker{
fn: func(_ context.Context, _, _ string, payload []byte) ([]byte, int, error) {
var ev map[string]any
- if err := json.Unmarshal(payload, &ev); err == nil {
+ if err := json.Unmarshal(payload, &ev, jsonx.V1()); err == nil {
if pp, ok := ev["pathParameters"].(map[string]any); ok {
capturedPathParams = pp
}
}
- b, _ := json.Marshal(map[string]any{"statusCode": 200, "body": "ok"})
+ b, _ := json.Marshal(map[string]any{"statusCode": 200, "body": "ok"}, jsonx.V1())
return b, 200, nil
},
@@ -795,7 +796,7 @@ func TestHTTPAPIProxy_DeploymentSnapshot(t *testing.T) {
body = "from-b"
}
- b, _ := json.Marshal(map[string]any{"statusCode": 200, "body": body})
+ b, _ := json.Marshal(map[string]any{"statusCode": 200, "body": body}, jsonx.V1())
return b, 200, nil
},
@@ -812,7 +813,7 @@ func TestHTTPAPIProxy_DeploymentSnapshot(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var integ apigatewayv2.Integration
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &integ, jsonx.V1()))
rr = doRequest(t, h, http.MethodPost, "/v2/apis/"+apiID+"/routes", map[string]any{
"routeKey": "GET /a",
diff --git a/services/apigatewayv2/http_proxy_throttle_test.go b/services/apigatewayv2/http_proxy_throttle_test.go
index ec9b1a26dd..4ec7a6a633 100644
--- a/services/apigatewayv2/http_proxy_throttle_test.go
+++ b/services/apigatewayv2/http_proxy_throttle_test.go
@@ -2,7 +2,7 @@ package apigatewayv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"sync/atomic"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -38,7 +39,7 @@ func TestHTTPAPIProxy_RouteThrottle_TooManyRequests(t *testing.T) {
fn: func(_ context.Context, _, _ string, _ []byte) ([]byte, int, error) {
integrationCalls.Add(1)
- b, _ := json.Marshal(map[string]any{"statusCode": 200, "body": "ok"})
+ b, _ := json.Marshal(map[string]any{"statusCode": 200, "body": "ok"}, jsonx.V1())
return b, 200, nil
},
diff --git a/services/apigatewayv2/lambda_response.go b/services/apigatewayv2/lambda_response.go
index 6d560285f1..f7a547293d 100644
--- a/services/apigatewayv2/lambda_response.go
+++ b/services/apigatewayv2/lambda_response.go
@@ -3,11 +3,14 @@ package apigatewayv2
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -22,16 +25,16 @@ const (
func writeHTTPAPILambdaResponse(c *echo.Context, respBytes []byte, payloadVersion string) error {
trimmed := bytes.TrimSpace(respBytes)
- var envelope map[string]json.RawMessage
+ var envelope map[string]jsontext.Value
- isObject := json.Unmarshal(trimmed, &envelope) == nil && envelope != nil
+ isObject := json.Unmarshal(trimmed, &envelope, jsonx.V1()) == nil && envelope != nil
if isObject && isFunctionErrorEnvelope(envelope) {
return writeJSONMessage(c, http.StatusInternalServerError, msgInternalError)
}
_, hasStatus := envelope["statusCode"]
if !isObject || !hasStatus {
- if payloadVersion == payloadFormatV1 || len(trimmed) == 0 || !json.Valid(trimmed) {
+ if payloadVersion == payloadFormatV1 || len(trimmed) == 0 || !jsontext.Value(trimmed).IsValid(jsonx.V1()) {
return writeJSONMessage(c, http.StatusInternalServerError, msgInternalError)
}
@@ -43,14 +46,14 @@ func writeHTTPAPILambdaResponse(c *echo.Context, respBytes []byte, payloadVersio
}
var lambdaResp httpAPILambdaResponse
- if err := json.Unmarshal(trimmed, &lambdaResp); err != nil {
+ if err := json.Unmarshal(trimmed, &lambdaResp, jsonx.V1()); err != nil {
return writeJSONMessage(c, http.StatusInternalServerError, msgInternalError)
}
return writeLambdaEnvelope(c, &lambdaResp)
}
-func isFunctionErrorEnvelope(envelope map[string]json.RawMessage) bool {
+func isFunctionErrorEnvelope(envelope map[string]jsontext.Value) bool {
_, hasMsg := envelope["errorMessage"]
_, hasType := envelope["errorType"]
_, hasStatus := envelope["statusCode"]
@@ -111,7 +114,7 @@ func writeJSONMessage(c *echo.Context, status int, body string) error {
// httpAPIMessage renders the `{"message":"..."}` body execute-api returns for gateway errors.
func httpAPIMessage(msg string) string {
- b, _ := json.Marshal(map[string]string{jsonKeyMessage: msg})
+ b, _ := json.Marshal(map[string]string{jsonKeyMessage: msg}, jsonx.V1())
return strings.TrimSpace(string(b))
}
diff --git a/services/apigatewayv2/models.go b/services/apigatewayv2/models.go
index 9bfc20e50c..45f18033b7 100644
--- a/services/apigatewayv2/models.go
+++ b/services/apigatewayv2/models.go
@@ -1,8 +1,10 @@
package apigatewayv2
import (
- "encoding/json"
+ "encoding/json/v2"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// isoTime wraps [time.Time] and marshals to/from a JSON string in RFC3339 format,
@@ -12,12 +14,12 @@ type isoTime struct {
}
func (t isoTime) MarshalJSON() ([]byte, error) {
- return json.Marshal(t.UTC().Format(time.RFC3339))
+ return json.Marshal(t.UTC().Format(time.RFC3339), jsonx.V1())
}
func (t *isoTime) UnmarshalJSON(b []byte) error {
var s string
- if err := json.Unmarshal(b, &s); err != nil {
+ if err := json.Unmarshal(b, &s, jsonx.V1()); err != nil {
return err
}
diff --git a/services/apigatewayv2/multi_region_test.go b/services/apigatewayv2/multi_region_test.go
index c6ed1738dc..87abad8c80 100644
--- a/services/apigatewayv2/multi_region_test.go
+++ b/services/apigatewayv2/multi_region_test.go
@@ -2,7 +2,7 @@ package apigatewayv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -37,7 +38,7 @@ func regionCall(t *testing.T, h *apigatewayv2.Handler, region, method, path, bod
require.Less(t, rec.Code, http.StatusMultipleChoices, rec.Body.String())
out := map[string]any{}
- if rec.Body.Len() > 0 && json.Unmarshal(rec.Body.Bytes(), &out) != nil {
+ if rec.Body.Len() > 0 && json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()) != nil {
out["raw"] = rec.Body.String()
}
diff --git a/services/apigatewayv2/persistence.go b/services/apigatewayv2/persistence.go
index 80c00f5490..24a5ec5aca 100644
--- a/services/apigatewayv2/persistence.go
+++ b/services/apigatewayv2/persistence.go
@@ -2,7 +2,7 @@ package apigatewayv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -619,9 +619,9 @@ var dirtyTableNames = struct {
// registerAllTables doc for the clean/dirty split), merged into one map so a
// single Tables blob round-trips the whole backend.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- PortalProductSharingPolicies map[string]string `json:"portalProductSharingPolicies,omitempty"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ PortalProductSharingPolicies map[string]string `json:"portalProductSharingPolicies,omitempty"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/apigatewayv2/rse.go b/services/apigatewayv2/rse.go
index 56b1fdbecb..1a9c190373 100644
--- a/services/apigatewayv2/rse.go
+++ b/services/apigatewayv2/rse.go
@@ -2,11 +2,13 @@ package apigatewayv2
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// EvalRouteSelectionExpression evaluates an AWS API Gateway v2 route-selection
@@ -95,7 +97,7 @@ func parseJSONBody(r *http.Request) map[string]any {
}
var obj map[string]any
- if jsonErr := json.Unmarshal(raw, &obj); jsonErr != nil {
+ if jsonErr := json.Unmarshal(raw, &obj, jsonx.V1()); jsonErr != nil {
return nil
}
diff --git a/services/apigatewayv2/wire_field_fixes_respsweep_test.go b/services/apigatewayv2/wire_field_fixes_respsweep_test.go
index 5b4ad9a947..b14aceede6 100644
--- a/services/apigatewayv2/wire_field_fixes_respsweep_test.go
+++ b/services/apigatewayv2/wire_field_fixes_respsweep_test.go
@@ -1,13 +1,14 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -74,7 +75,7 @@ func testPortalIncludedPortalProductArnsPresent(t *testing.T, h *apigatewayv2.Ha
require.Equal(t, http.StatusCreated, rr.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &created, jsonx.V1()))
arns, ok := created["includedPortalProductArns"]
assert.True(t, ok, "includedPortalProductArns key must be present on Create")
assert.Equal(t, []any{}, arns)
@@ -85,7 +86,7 @@ func testPortalIncludedPortalProductArnsPresent(t *testing.T, h *apigatewayv2.Ha
var listed struct {
Items []map[string]any `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &listed))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &listed, jsonx.V1()))
require.Len(t, listed.Items, 1)
arns, ok = listed.Items[0]["includedPortalProductArns"]
@@ -100,7 +101,7 @@ func testPortalProductDescriptionPresent(t *testing.T, h *apigatewayv2.Handler)
require.Equal(t, http.StatusCreated, rr.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &created, jsonx.V1()))
desc, ok := created["description"]
assert.True(t, ok, "description key must be present on Create")
assert.Empty(t, desc)
@@ -111,7 +112,7 @@ func testPortalProductDescriptionPresent(t *testing.T, h *apigatewayv2.Handler)
var listed struct {
Items []map[string]any `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &listed))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &listed, jsonx.V1()))
require.Len(t, listed.Items, 1)
desc, ok = listed.Items[0]["description"]
@@ -126,7 +127,7 @@ func testProductPageArnAndTitlePresent(t *testing.T, h *apigatewayv2.Handler) {
require.Equal(t, http.StatusCreated, rr.Code)
var product map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &product))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &product, jsonx.V1()))
portalProductID, _ := product["portalProductId"].(string)
require.NotEmpty(t, portalProductID)
@@ -137,7 +138,7 @@ func testProductPageArnAndTitlePresent(t *testing.T, h *apigatewayv2.Handler) {
require.Equal(t, http.StatusCreated, rr.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &created, jsonx.V1()))
assert.NotEmpty(t, created["productPageArn"])
assert.Equal(t, "My Page", created["pageTitle"])
@@ -147,7 +148,7 @@ func testProductPageArnAndTitlePresent(t *testing.T, h *apigatewayv2.Handler) {
var listed struct {
Items []map[string]any `json:"items"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &listed))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &listed, jsonx.V1()))
require.Len(t, listed.Items, 1)
assert.NotEmpty(t, listed.Items[0]["productPageArn"])
assert.Equal(t, "My Page", listed.Items[0]["pageTitle"])
diff --git a/services/apigatewayv2/wire_field_fixes_test.go b/services/apigatewayv2/wire_field_fixes_test.go
index 4a91cb8575..3b2e674e22 100644
--- a/services/apigatewayv2/wire_field_fixes_test.go
+++ b/services/apigatewayv2/wire_field_fixes_test.go
@@ -1,7 +1,7 @@
package apigatewayv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -550,7 +551,7 @@ func TestCreateProductRestEndpointPage_DisplayContent(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var product apigatewayv2.PortalProduct
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &product))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &product, jsonx.V1()))
body := map[string]any{
"restEndpointIdentifier": map[string]any{
@@ -571,7 +572,7 @@ func TestCreateProductRestEndpointPage_DisplayContent(t *testing.T) {
require.Equal(t, http.StatusCreated, rr.Code)
var created apigatewayv2.ProductRestEndpointPage
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &created, jsonx.V1()))
assert.Equal(t, wantEndpoint, created.DisplayContent["endpoint"])
assert.Equal(t, "custom docs", created.DisplayContent["body"])
assert.Equal(t, wantEndpoint, created.Endpoint)
@@ -584,7 +585,7 @@ func TestCreateProductRestEndpointPage_DisplayContent(t *testing.T) {
require.Equal(t, http.StatusOK, rr.Code)
var got apigatewayv2.ProductRestEndpointPage
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, wantEndpoint, got.DisplayContent["endpoint"])
assert.Equal(t, "custom docs", got.DisplayContent["body"])
assert.Equal(t, wantEndpoint, got.Endpoint)
diff --git a/services/apigatewayv2/ws_proxy.go b/services/apigatewayv2/ws_proxy.go
index c3d1455490..485e9318ff 100644
--- a/services/apigatewayv2/ws_proxy.go
+++ b/services/apigatewayv2/ws_proxy.go
@@ -3,7 +3,7 @@ package apigatewayv2
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -19,6 +19,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -164,7 +165,7 @@ func (s *wsSession) sendForbidden() {
jsonKeyMessage: wsForbiddenMessage,
"connectionId": s.connID,
"requestId": uuid.NewString(),
- })
+ }, jsonx.V1())
s.send(b)
}
@@ -223,7 +224,7 @@ func (s *wsSession) invoke(
return nil, fmt.Errorf("%w: %s", ErrInvalidLambdaArn, integration.IntegrationURI)
}
- payload, marshalErr := json.Marshal(event)
+ payload, marshalErr := json.Marshal(event, jsonx.V1())
if marshalErr != nil {
return nil, fmt.Errorf("failed to marshal WebSocket event: %w", marshalErr)
}
@@ -364,7 +365,7 @@ func proxyResponseStatus(out []byte) int {
var resp struct {
StatusCode *int `json:"statusCode"`
}
- if json.Unmarshal(out, &resp) != nil || resp.StatusCode == nil {
+ if json.Unmarshal(out, &resp, jsonx.V1()) != nil || resp.StatusCode == nil {
return 0
}
@@ -441,7 +442,7 @@ func (h *Handler) enforceWSRequestAuthorizer(c *echo.Context, s *wsSession, auth
"multiValueQueryStringParameters": s.query,
"stageVariables": map[string]string{},
evKeyRequestContext: s.requestContext(routeKeyConnect, wsEventConnect),
- })
+ }, jsonx.V1())
if err != nil {
return errRouteAuthConfigInvalid
}
@@ -458,7 +459,7 @@ func (h *Handler) enforceWSRequestAuthorizer(c *echo.Context, s *wsSession, auth
var policy iamPolicyResponse
allow, explicit := false, false
- if json.Unmarshal(out, &policy) == nil {
+ if json.Unmarshal(out, &policy, jsonx.V1()) == nil {
allow, explicit = evaluateIAMPolicy(policy.PolicyDocument.Statement, methodArn)
}
@@ -497,7 +498,7 @@ func evalRouteSelection(expr string, body []byte) string {
expr = strings.ReplaceAll(expr, `\$`, escaped)
var doc any
- _ = json.Unmarshal(body, &doc)
+ _ = json.Unmarshal(body, &doc, jsonx.V1())
failed := false
out := wsBodyVarRE.ReplaceAllStringFunc(expr, func(m string) string {
diff --git a/services/apigatewayv2/ws_realclient_test.go b/services/apigatewayv2/ws_realclient_test.go
index 9f8cf81ee3..d1b9013f1a 100644
--- a/services/apigatewayv2/ws_realclient_test.go
+++ b/services/apigatewayv2/ws_realclient_test.go
@@ -2,7 +2,7 @@ package apigatewayv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/apigatewaymanagementapi"
"github.com/blackbirdworks/gopherstack/services/apigatewayv2"
@@ -40,7 +41,7 @@ type wsLambda struct {
func (l *wsLambda) InvokeFunction(_ context.Context, fn, _ string, payload []byte) ([]byte, int, error) {
var ev map[string]any
- _ = json.Unmarshal(payload, &ev)
+ _ = json.Unmarshal(payload, &ev, jsonx.V1())
l.calls <- wsLambdaCall{fn: fn, event: ev}
if h := l.handlers[fn]; h != nil {
@@ -459,7 +460,7 @@ func TestWebSocket_NoMatchingRouteSendsForbidden(t *testing.T) {
require.NoError(t, conn.WriteMessage(websocket.TextMessage, []byte(`{"action":"x"}`)))
var frame map[string]string
- require.NoError(t, json.Unmarshal([]byte(readText(t, conn)), &frame))
+ require.NoError(t, json.Unmarshal([]byte(readText(t, conn)), &frame, jsonx.V1()))
assert.Equal(t, "Forbidden", frame["message"])
assert.NotEmpty(t, frame["connectionId"])
}
diff --git a/services/appconfig/deletion_protection_test.go b/services/appconfig/deletion_protection_test.go
index 4f61628b16..9c0389ffed 100644
--- a/services/appconfig/deletion_protection_test.go
+++ b/services/appconfig/deletion_protection_test.go
@@ -1,7 +1,7 @@
package appconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/appconfig"
"github.com/blackbirdworks/gopherstack/services/appconfigdata"
@@ -58,20 +59,20 @@ func newDeletionProtectionFixture(t *testing.T) *deletionProtectionFixture {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
rec = doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/environments", []byte(`{"name":"dp-env"}`))
require.Equal(t, http.StatusCreated, rec.Code)
var env appconfig.Environment
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env, jsonx.V1()))
rec = doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/configurationprofiles",
[]byte(`{"name":"dp-profile","locationUri":"hosted","type":"AWS.Freeform"}`))
require.Equal(t, http.StatusCreated, rec.Code)
var profile appconfig.ConfigurationProfile
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile, jsonx.V1()))
return &deletionProtectionFixture{h: h, acd: acd, appID: app.ID, envID: env.ID, profileID: profile.ID}
}
diff --git a/services/appconfig/errsweep_wire_shape_test.go b/services/appconfig/errsweep_wire_shape_test.go
index adea34d7dc..4a2d4e01ee 100644
--- a/services/appconfig/errsweep_wire_shape_test.go
+++ b/services/appconfig/errsweep_wire_shape_test.go
@@ -1,7 +1,7 @@
package appconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/appconfig"
@@ -55,6 +56,6 @@ func TestHandler_UnmatchedRouteCarriesTypedErrorCode(t *testing.T) {
require.NoError(t, err)
var decoded map[string]any
- require.NoError(t, json.Unmarshal(raw, &decoded))
+ require.NoError(t, json.Unmarshal(raw, &decoded, jsonx.V1()))
require.NotEmpty(t, decoded["message"])
}
diff --git a/services/appconfig/extension_actions.go b/services/appconfig/extension_actions.go
index dee040d9ba..102a32ee18 100644
--- a/services/appconfig/extension_actions.go
+++ b/services/appconfig/extension_actions.go
@@ -5,7 +5,8 @@ import (
"crypto/rand"
"encoding/base64"
"encoding/hex"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -13,6 +14,7 @@ import (
"strconv"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
lambdabackend "github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -185,7 +187,7 @@ func (b *InMemoryBackend) runPreActions(
ev["Parameters"] = params
ev["Content"] = base64.StdEncoding.EncodeToString(content)
- payload, err := json.Marshal(ev)
+ payload, err := json.Marshal(ev, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("%w: encoding extension event: %w", ErrBadRequest, err)
}
@@ -209,12 +211,12 @@ func applyActionResponse(uri string, raw, content []byte) ([]byte, error) {
return content, nil
}
- if !json.Valid(raw) {
+ if !jsontext.Value(raw).IsValid(jsonx.V1()) {
return content, nil
}
var resp actionResponse
- if err := json.Unmarshal(raw, &resp); err != nil {
+ if err := json.Unmarshal(raw, &resp, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: extension action %s returned an unreadable response: %w", ErrBadRequest, uri, err)
}
diff --git a/services/appconfig/extension_actions_test.go b/services/appconfig/extension_actions_test.go
index c246ad0033..5e350f3b29 100644
--- a/services/appconfig/extension_actions_test.go
+++ b/services/appconfig/extension_actions_test.go
@@ -3,7 +3,7 @@ package appconfig_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"sync"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appconfig"
)
@@ -50,7 +51,7 @@ func (r *recordingInvoker) lastEvent(t *testing.T) map[string]any {
require.NotEmpty(t, r.payloads)
var ev map[string]any
- require.NoError(t, json.Unmarshal(r.payloads[len(r.payloads)-1], &ev))
+ require.NoError(t, json.Unmarshal(r.payloads[len(r.payloads)-1], &ev, jsonx.V1()))
return ev
}
diff --git a/services/appconfig/extension_events.go b/services/appconfig/extension_events.go
index f8e69e7ba6..45a1db3f6a 100644
--- a/services/appconfig/extension_events.go
+++ b/services/appconfig/extension_events.go
@@ -2,7 +2,8 @@ package appconfig
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"regexp"
@@ -11,6 +12,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
ebbackend "github.com/blackbirdworks/gopherstack/services/eventbridge"
@@ -276,7 +278,7 @@ func (b *InMemoryBackend) deliverAction(act preAction, event map[string]any) ([]
return nil, nil
}
- payload, err := json.Marshal(ev)
+ payload, err := json.Marshal(ev, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("%w: encoding extension event: %w", ErrBadRequest, err)
}
@@ -303,7 +305,7 @@ func stringifyDeploymentNumber(ev map[string]any) map[string]any {
}
func (b *InMemoryBackend) deliverSNS(act preAction, ev map[string]any) error {
- msg, err := json.Marshal(stringifyDeploymentNumber(ev))
+ msg, err := json.Marshal(stringifyDeploymentNumber(ev), jsonx.V1())
if err != nil {
return err
}
@@ -315,7 +317,7 @@ func (b *InMemoryBackend) deliverSNS(act preAction, ev map[string]any) error {
}
func (b *InMemoryBackend) deliverSQS(act preAction, ev map[string]any) error {
- body, err := json.Marshal(stringifyDeploymentNumber(ev))
+ body, err := json.Marshal(stringifyDeploymentNumber(ev), jsonx.V1())
if err != nil {
return err
}
@@ -324,7 +326,7 @@ func (b *InMemoryBackend) deliverSQS(act preAction, ev map[string]any) error {
}
func (b *InMemoryBackend) deliverEvents(act preAction, ev map[string]any) error {
- detail, err := json.Marshal(ev)
+ detail, err := json.Marshal(ev, jsonx.V1())
if err != nil {
return err
}
@@ -404,12 +406,12 @@ func (b *InMemoryBackend) tickRollbackReason(job tickJob) string {
return fmt.Sprintf("Extension action %s failed: %v", act.uri, err)
}
- if len(raw) == 0 || !json.Valid(raw) {
+ if len(raw) == 0 || !jsontext.Value(raw).IsValid(jsonx.V1()) {
continue
}
var resp actionResponse
- if json.Unmarshal(raw, &resp) != nil {
+ if json.Unmarshal(raw, &resp, jsonx.V1()) != nil {
continue
}
diff --git a/services/appconfig/extension_events_test.go b/services/appconfig/extension_events_test.go
index 6086b0c6cc..c1aa7bf588 100644
--- a/services/appconfig/extension_events_test.go
+++ b/services/appconfig/extension_events_test.go
@@ -2,7 +2,7 @@ package appconfig_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"slices"
"sync"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appconfig"
)
@@ -104,7 +105,7 @@ type lifecycleInvoker struct {
func (l *lifecycleInvoker) Invoke(_ context.Context, _ string, payload []byte) ([]byte, error) {
var ev map[string]any
- if err := json.Unmarshal(payload, &ev); err != nil {
+ if err := json.Unmarshal(payload, &ev, jsonx.V1()); err != nil {
return nil, err
}
@@ -270,7 +271,7 @@ func TestExtensionActions_OnDeploymentTargets(t *testing.T) {
assert.Equal(t, "OnDeploymentComplete", snsMsgs[1].attrs["MessageType"])
var m map[string]any
- require.NoError(t, json.Unmarshal([]byte(snsMsgs[0].message), &m))
+ require.NoError(t, json.Unmarshal([]byte(snsMsgs[0].message), &m, jsonx.V1()))
assert.Equal(t, "1", m["DeploymentNumber"])
assert.Equal(t, "1", m["ConfigurationVersion"])
assert.Equal(t, "my deploy", m["Description"])
@@ -282,7 +283,7 @@ func TestExtensionActions_OnDeploymentTargets(t *testing.T) {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal([]byte(sqsMsgs[1]), &m))
+ require.NoError(t, json.Unmarshal([]byte(sqsMsgs[1]), &m, jsonx.V1()))
assert.Equal(t, "OnDeploymentComplete", m["Type"])
assert.Equal(t, "1", m["DeploymentNumber"])
}},
@@ -297,7 +298,7 @@ func TestExtensionActions_OnDeploymentTargets(t *testing.T) {
assert.Contains(t, busMsgs[0].resources[0], ":extensionassociation/")
var m map[string]any
- require.NoError(t, json.Unmarshal([]byte(busMsgs[0].detail), &m))
+ require.NoError(t, json.Unmarshal([]byte(busMsgs[0].detail), &m, jsonx.V1()))
assert.InDelta(t, 1, m["DeploymentNumber"], 0)
}},
}
diff --git a/services/appconfig/extension_sibling_test.go b/services/appconfig/extension_sibling_test.go
index b4e068bd7d..92a8de639b 100644
--- a/services/appconfig/extension_sibling_test.go
+++ b/services/appconfig/extension_sibling_test.go
@@ -1,12 +1,13 @@
package appconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
sqsbackend "github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -61,7 +62,7 @@ func TestExtensionActions_SiblingSQSDelivery(t *testing.T) {
}
var m map[string]any
- require.NoError(t, json.Unmarshal([]byte(out.Messages[0].Body), &m))
+ require.NoError(t, json.Unmarshal([]byte(out.Messages[0].Body), &m, jsonx.V1()))
return m["Type"] == "OnDeploymentComplete"
}, eventuallyWait, eventuallyTick)
diff --git a/services/appconfig/feature_flags.go b/services/appconfig/feature_flags.go
index 49bd0dbcdf..9b78e86cd3 100644
--- a/services/appconfig/feature_flags.go
+++ b/services/appconfig/feature_flags.go
@@ -1,6 +1,12 @@
package appconfig
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "encoding/json/jsontext"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
// featureFlagDocument mirrors the parts of the real AWS.AppConfig.FeatureFlags
// JSON schema this backend needs in order to validate CreateExperimentDefinition's
@@ -19,7 +25,7 @@ import "encoding/json"
// feature flags), so it is documented and field-diffed against AWS's public
// user-guide schema page directly rather than SDK-generated code.
type featureFlagDocument struct {
- Flags map[string]json.RawMessage `json:"flags"`
+ Flags map[string]jsontext.Value `json:"flags"`
}
// parseFeatureFlagKeys attempts to parse content as a real
@@ -39,7 +45,7 @@ func parseFeatureFlagKeys(content []byte) (map[string]struct{}, bool) {
}
var doc featureFlagDocument
- if err := json.Unmarshal(content, &doc); err != nil || doc.Flags == nil {
+ if err := json.Unmarshal(content, &doc, jsonx.V1()); err != nil || doc.Flags == nil {
return nil, false
}
diff --git a/services/appconfig/handler_account_settings_test.go b/services/appconfig/handler_account_settings_test.go
index 532d856f18..efeb4fc6a6 100644
--- a/services/appconfig/handler_account_settings_test.go
+++ b/services/appconfig/handler_account_settings_test.go
@@ -1,7 +1,7 @@
package appconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appconfig"
)
@@ -47,7 +48,7 @@ func TestHandler_GetAccountSettings(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var settings appconfig.AccountSettings
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &settings))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &settings, jsonx.V1()))
}
// TestHandler_UpdateAccountSettings_DeletionProtectionFieldsSurviveIndependentUpdates
@@ -73,7 +74,7 @@ func TestHandler_UpdateAccountSettings_DeletionProtectionFieldsSurviveIndependen
require.Equal(t, http.StatusOK, rec.Code)
var settings appconfig.AccountSettings
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &settings))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &settings, jsonx.V1()))
require.NotNil(t, settings.DeletionProtection)
require.NotNil(t, settings.DeletionProtection.Enabled)
assert.False(t, *settings.DeletionProtection.Enabled, "B's own field must apply")
@@ -84,7 +85,7 @@ func TestHandler_UpdateAccountSettings_DeletionProtectionFieldsSurviveIndependen
// GetAccountSettings must reflect the same merged state.
rec = doRequest(t, h, http.MethodGet, "/settings", nil)
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &settings))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &settings, jsonx.V1()))
require.NotNil(t, settings.DeletionProtection.ProtectionPeriodInMinutes)
assert.EqualValues(t, 120, *settings.DeletionProtection.ProtectionPeriodInMinutes)
}
@@ -100,7 +101,7 @@ func TestHandler_UpdateAccountSettings(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var settings appconfig.AccountSettings
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &settings))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &settings, jsonx.V1()))
require.NotNil(t, settings.DeletionProtection)
require.NotNil(t, settings.DeletionProtection.Enabled)
assert.True(t, *settings.DeletionProtection.Enabled)
@@ -109,7 +110,7 @@ func TestHandler_UpdateAccountSettings(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/settings", nil)
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &settings))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &settings, jsonx.V1()))
require.NotNil(t, settings.DeletionProtection)
assert.True(t, *settings.DeletionProtection.Enabled)
}
diff --git a/services/appconfig/handler_applications_test.go b/services/appconfig/handler_applications_test.go
index 9f0ce0c5df..c4db0720e1 100644
--- a/services/appconfig/handler_applications_test.go
+++ b/services/appconfig/handler_applications_test.go
@@ -1,7 +1,7 @@
package appconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/services/appconfig"
)
@@ -51,7 +52,7 @@ func TestHandler_Application_CRUD(t *testing.T) {
if tt.wantName != "" {
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
assert.Equal(t, tt.wantName, app.Name)
}
})
@@ -76,7 +77,7 @@ func TestHandler_DeleteApplication(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
// Delete it.
rec = doRequest(t, h, http.MethodDelete, "/applications/"+app.ID, nil)
@@ -96,7 +97,7 @@ func TestHandler_UpdateApplication(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
rec = doRequest(
t,
@@ -108,7 +109,7 @@ func TestHandler_UpdateApplication(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var updated appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "updated", updated.Name)
assert.Equal(t, "new desc", updated.Description)
}
@@ -128,7 +129,7 @@ func TestHandler_UpdateApplication_OmittedDescriptionPreserved(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
// Update only Name; Description is omitted from the request body.
rec = doRequest(t, h, http.MethodPatch, "/applications/"+app.ID,
@@ -136,7 +137,7 @@ func TestHandler_UpdateApplication_OmittedDescriptionPreserved(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var updated appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "renamed", updated.Name)
assert.Equal(t, "keep-me", updated.Description,
"omitted Description must not clobber the existing value")
@@ -182,7 +183,7 @@ func TestHandler_ListApplicationsPagination(t *testing.T) {
NextToken string `json:"NextToken"`
Items []any `json:"Items"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Items, 4)
assert.Empty(t, resp.NextToken)
})
@@ -200,7 +201,7 @@ func TestHandler_ListApplicationsPagination(t *testing.T) {
NextToken string `json:"NextToken"`
Items []any `json:"Items"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Items, 2)
assert.NotEmpty(t, resp.NextToken)
})
@@ -219,7 +220,7 @@ func TestHandler_ListApplicationsPagination(t *testing.T) {
NextToken string `json:"NextToken"`
Items []any `json:"Items"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &first))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &first, jsonx.V1()))
require.NotEmpty(t, first.NextToken)
rec2 := doRequest(
@@ -234,7 +235,7 @@ func TestHandler_ListApplicationsPagination(t *testing.T) {
NextToken string `json:"NextToken"`
Items []any `json:"Items"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Items, 2)
assert.Empty(t, resp.NextToken)
})
@@ -260,7 +261,7 @@ func TestHandler_ListApplicationsPagination(t *testing.T) {
NextToken string `json:"NextToken"`
Items []any `json:"Items"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp.Items)
assert.Empty(t, resp.NextToken)
})
@@ -297,14 +298,14 @@ func TestHandler_CreateApplication_TagsAppliedInline(t *testing.T) {
body, err := json.Marshal(map[string]any{
"Name": "tagged-app-" + tt.name,
"Tags": tt.tags,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/applications", body)
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
resourceArn := "arn:aws:appconfig:us-east-1:123456789012:application/" + app.ID
tagsRec := doRequest(t, h, http.MethodGet, "/tags/"+resourceArn, nil)
@@ -313,7 +314,7 @@ func TestHandler_CreateApplication_TagsAppliedInline(t *testing.T) {
var tagsResp struct {
Tags map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp, jsonx.V1()))
if len(tt.tags) == 0 {
assert.Empty(t, tagsResp.Tags)
diff --git a/services/appconfig/handler_configuration_profiles_test.go b/services/appconfig/handler_configuration_profiles_test.go
index acd049a8b5..0b580a8601 100644
--- a/services/appconfig/handler_configuration_profiles_test.go
+++ b/services/appconfig/handler_configuration_profiles_test.go
@@ -1,7 +1,7 @@
package appconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appconfig"
)
@@ -23,7 +24,7 @@ func TestHandler_ConfigurationProfile_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
// Create profile.
profileBody := []byte(`{"name":"my-config","locationUri":"hosted","type":"AWS.Freeform"}`)
@@ -37,7 +38,7 @@ func TestHandler_ConfigurationProfile_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var profile appconfig.ConfigurationProfile
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile, jsonx.V1()))
assert.Equal(t, "my-config", profile.Name)
// Get profile.
@@ -70,7 +71,7 @@ func TestHandler_ListConfigurationProfiles_HTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
// Create two profiles.
for _, name := range []string{"prof-1", "prof-2"} {
@@ -99,7 +100,7 @@ func TestHandler_UpdateConfigurationProfile_HTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
profileBody := []byte(`{"name":"old-name","locationUri":"hosted","type":"AWS.Freeform"}`)
rec = doRequest(
@@ -112,7 +113,7 @@ func TestHandler_UpdateConfigurationProfile_HTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var profile appconfig.ConfigurationProfile
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile, jsonx.V1()))
// Update.
rec = doRequest(
@@ -125,7 +126,7 @@ func TestHandler_UpdateConfigurationProfile_HTTP(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var updated appconfig.ConfigurationProfile
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "new-name", updated.Name)
}
@@ -143,7 +144,7 @@ func TestHandler_UpdateConfigurationProfile_RetrievalRoleArnAndValidators(t *tes
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
profileBody := []byte(
`{"Name":"old-name","Description":"keep-me","LocationUri":"hosted","Type":"AWS.Freeform"}`,
@@ -152,7 +153,7 @@ func TestHandler_UpdateConfigurationProfile_RetrievalRoleArnAndValidators(t *tes
require.Equal(t, http.StatusCreated, rec.Code)
var profile appconfig.ConfigurationProfile
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile, jsonx.V1()))
rec = doRequest(t, h, http.MethodPatch,
"/applications/"+app.ID+"/configurationprofiles/"+profile.ID,
@@ -161,7 +162,7 @@ func TestHandler_UpdateConfigurationProfile_RetrievalRoleArnAndValidators(t *tes
assert.Equal(t, http.StatusOK, rec.Code)
var updated appconfig.ConfigurationProfile
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "arn:aws:iam::123456789012:role/retrieval", updated.RetrievalRoleArn,
"RetrievalRoleArn must be applied, not silently dropped")
require.Len(t, updated.Validators, 1, "Validators must be applied, not silently dropped")
@@ -245,21 +246,21 @@ func TestHandler_CreateConfigurationProfile_TagsAppliedInline(t *testing.T) {
require.Equal(t, http.StatusCreated, appRec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
body, err := json.Marshal(map[string]any{
"Name": "tagged-prof-" + tt.name,
"LocationUri": "hosted",
"Type": "AWS.Freeform",
"Tags": tt.tags,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/configurationprofiles", body)
require.Equal(t, http.StatusCreated, rec.Code)
var profile appconfig.ConfigurationProfile
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile, jsonx.V1()))
resourceArn := "arn:aws:appconfig:us-east-1:123456789012:application/" +
app.ID + "/configurationprofile/" + profile.ID
@@ -269,7 +270,7 @@ func TestHandler_CreateConfigurationProfile_TagsAppliedInline(t *testing.T) {
var tagsResp struct {
Tags map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp, jsonx.V1()))
if len(tt.tags) == 0 {
assert.Empty(t, tagsResp.Tags)
@@ -334,7 +335,7 @@ func seedExperimentDefinitionHTTP(t *testing.T, h *appconfig.Handler) (string, s
var app struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
envRec := doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/environments",
[]byte(`{"Name":"exp-http-env"}`))
@@ -343,7 +344,7 @@ func seedExperimentDefinitionHTTP(t *testing.T, h *appconfig.Handler) (string, s
var env struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env, jsonx.V1()))
profRec := doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/configurationprofiles",
[]byte(`{"Name":"exp-http-profile","LocationUri":"hosted","Type":"AWS.AppConfig.FeatureFlags"}`))
@@ -352,7 +353,7 @@ func seedExperimentDefinitionHTTP(t *testing.T, h *appconfig.Handler) (string, s
var prof struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof))
+ require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof, jsonx.V1()))
return app.ID, env.ID, prof.ID
}
@@ -382,7 +383,7 @@ func TestHandler_ExperimentDefinition_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created appconfig.ExperimentDefinition
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
assert.NotEmpty(t, created.ID)
assert.Equal(t, appID, created.ApplicationID)
assert.Equal(t, envID, created.EnvironmentID)
@@ -396,7 +397,7 @@ func TestHandler_ExperimentDefinition_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var got appconfig.ExperimentDefinition
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, created.ID, got.ID)
listRec := doRequest(t, h, http.MethodGet, "/experimentdefinitions?application_identifier="+appID, nil)
@@ -405,7 +406,7 @@ func TestHandler_ExperimentDefinition_CRUD(t *testing.T) {
var listResp struct {
Items []appconfig.ExperimentDefinition `json:"Items"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Items, 1)
assert.Equal(t, created.ID, listResp.Items[0].ID)
@@ -414,7 +415,7 @@ func TestHandler_ExperimentDefinition_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updated appconfig.ExperimentDefinition
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "country == 'US'", updated.AudienceRule)
deleteRec := doRequest(t, h, http.MethodDelete, "/applications/"+appID+"/experimentdefinitions/"+created.ID, nil)
@@ -424,7 +425,7 @@ func TestHandler_ExperimentDefinition_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, afterDeleteRec.Code, "default delete_type archives rather than destroying")
var afterDelete appconfig.ExperimentDefinition
- require.NoError(t, json.Unmarshal(afterDeleteRec.Body.Bytes(), &afterDelete))
+ require.NoError(t, json.Unmarshal(afterDeleteRec.Body.Bytes(), &afterDelete, jsonx.V1()))
assert.Equal(t, "ARCHIVED", afterDelete.Status)
// FlagKey is validated against the profile's actual feature flag
@@ -551,14 +552,14 @@ func TestHandler_DeleteConfigurationProfile_DeletionProtectionCheck(t *testing.T
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
rec = doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/configurationprofiles",
[]byte(`{"name":"dpc-profile","locationUri":"hosted","type":"AWS.Freeform"}`))
require.Equal(t, http.StatusCreated, rec.Code)
var profile appconfig.ConfigurationProfile
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile, jsonx.V1()))
path := "/applications/" + app.ID + "/configurationprofiles/" + profile.ID
diff --git a/services/appconfig/handler_configuration_test.go b/services/appconfig/handler_configuration_test.go
index 9bc3377ceb..7eb2ade566 100644
--- a/services/appconfig/handler_configuration_test.go
+++ b/services/appconfig/handler_configuration_test.go
@@ -2,7 +2,7 @@ package appconfig_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/services/appconfig"
)
@@ -26,7 +27,7 @@ func TestHandler_GetConfiguration(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
rec = doRequest(
t,
@@ -38,14 +39,14 @@ func TestHandler_GetConfiguration(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var env appconfig.Environment
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env, jsonx.V1()))
rec = doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/configurationprofiles",
[]byte(`{"Name":"conf-profile","LocationUri":"hosted","Type":"AWS.Freeform"}`))
require.Equal(t, http.StatusCreated, rec.Code)
var profile appconfig.ConfigurationProfile
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile, jsonx.V1()))
tests := []struct {
name string
@@ -94,7 +95,7 @@ func TestHandler_GetConfiguration_WithContent(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
rec = doRequest(
t,
@@ -106,14 +107,14 @@ func TestHandler_GetConfiguration_WithContent(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var env appconfig.Environment
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env, jsonx.V1()))
rec = doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/configurationprofiles",
[]byte(`{"Name":"content-profile","LocationUri":"hosted","Type":"AWS.Freeform"}`))
require.Equal(t, http.StatusCreated, rec.Code)
var profile appconfig.ConfigurationProfile
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile, jsonx.V1()))
// Create a hosted configuration version.
e := echo.New()
@@ -143,7 +144,7 @@ func TestHandler_GetConfiguration_WithContent(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var strat appconfig.DeploymentStrategy
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &strat))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &strat, jsonx.V1()))
depBody := []byte(
`{"ConfigurationProfileId":"` + profile.ID +
@@ -171,14 +172,14 @@ func TestHandler_ValidateConfiguration(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
rec = doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/configurationprofiles",
[]byte(`{"Name":"validate-profile","LocationUri":"hosted","Type":"AWS.Freeform"}`))
require.Equal(t, http.StatusCreated, rec.Code)
var profile appconfig.ConfigurationProfile
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile, jsonx.V1()))
validatorBase := "/applications/" + app.ID + "/configurationprofiles/" + profile.ID + "/validators"
diff --git a/services/appconfig/handler_deployment_strategies_test.go b/services/appconfig/handler_deployment_strategies_test.go
index b35c1d47b1..42c505b07f 100644
--- a/services/appconfig/handler_deployment_strategies_test.go
+++ b/services/appconfig/handler_deployment_strategies_test.go
@@ -1,7 +1,7 @@
package appconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appconfig"
)
@@ -24,7 +25,7 @@ func TestHandler_DeploymentStrategy_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var strategy appconfig.DeploymentStrategy
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &strategy))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &strategy, jsonx.V1()))
assert.Equal(t, "my-strategy", strategy.Name)
// Get.
@@ -88,14 +89,14 @@ func TestHandler_UpdateDeploymentStrategy_OmittedDescriptionPreserved(t *testing
require.Equal(t, http.StatusCreated, rec.Code)
var strat appconfig.DeploymentStrategy
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &strat))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &strat, jsonx.V1()))
rec = doRequest(t, h, http.MethodPatch, "/deploymentstrategies/"+strat.ID,
[]byte(`{"GrowthFactor":50}`))
assert.Equal(t, http.StatusOK, rec.Code)
var updated appconfig.DeploymentStrategy
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.InDelta(t, float32(50), updated.GrowthFactor, 0.001)
assert.Equal(t, "keep-me", updated.Description, "omitted Description must be preserved")
}
@@ -137,14 +138,14 @@ func TestHandler_CreateDeploymentStrategy_TagsAppliedInline(t *testing.T) {
"GrowthType": "LINEAR",
"ReplicateTo": "NONE",
"Tags": tt.tags,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/deploymentstrategies", body)
require.Equal(t, http.StatusCreated, rec.Code)
var strategy appconfig.DeploymentStrategy
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &strategy))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &strategy, jsonx.V1()))
resourceArn := "arn:aws:appconfig:us-east-1:123456789012:deploymentstrategy/" + strategy.ID
tagsRec := doRequest(t, h, http.MethodGet, "/tags/"+resourceArn, nil)
@@ -153,7 +154,7 @@ func TestHandler_CreateDeploymentStrategy_TagsAppliedInline(t *testing.T) {
var tagsResp struct {
Tags map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp, jsonx.V1()))
if len(tt.tags) == 0 {
assert.Empty(t, tagsResp.Tags)
diff --git a/services/appconfig/handler_deployments_test.go b/services/appconfig/handler_deployments_test.go
index 8933058c53..0457f67a0c 100644
--- a/services/appconfig/handler_deployments_test.go
+++ b/services/appconfig/handler_deployments_test.go
@@ -2,7 +2,7 @@ package appconfig_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/services/appconfig"
)
@@ -138,7 +139,7 @@ func seedExperimentRunHTTP(t *testing.T, h *appconfig.Handler) (string, appconfi
var app struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
envRec := doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/environments",
[]byte(`{"Name":"run-http-env"}`))
@@ -147,7 +148,7 @@ func seedExperimentRunHTTP(t *testing.T, h *appconfig.Handler) (string, appconfi
var env struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env, jsonx.V1()))
profRec := doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/configurationprofiles",
[]byte(`{"Name":"run-http-profile","LocationUri":"hosted","Type":"AWS.AppConfig.FeatureFlags"}`))
@@ -156,7 +157,7 @@ func seedExperimentRunHTTP(t *testing.T, h *appconfig.Handler) (string, appconfi
var prof struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof))
+ require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof, jsonx.V1()))
defBody := `{
"Name": "run-http-def",
@@ -171,7 +172,7 @@ func seedExperimentRunHTTP(t *testing.T, h *appconfig.Handler) (string, appconfi
require.Equal(t, http.StatusCreated, defRec.Code)
var def appconfig.ExperimentDefinition
- require.NoError(t, json.Unmarshal(defRec.Body.Bytes(), &def))
+ require.NoError(t, json.Unmarshal(defRec.Body.Bytes(), &def, jsonx.V1()))
return app.ID, def
}
@@ -194,7 +195,7 @@ func TestHandler_ExperimentRun_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, startRec.Code)
var run appconfig.ExperimentRun
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &run))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &run, jsonx.V1()))
assert.Equal(t, int32(1), run.Run)
assert.Equal(t, "RUNNING", run.Status)
assert.InDelta(t, float32(10), run.ExposurePercentage, 0.001)
@@ -208,14 +209,14 @@ func TestHandler_ExperimentRun_Lifecycle(t *testing.T) {
var listResp struct {
Items []appconfig.ExperimentRun `json:"Items"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Items, 1)
updateRec := doRequest(t, h, http.MethodPatch, base+"/1/update", []byte(`{"ExposurePercentage":25}`))
require.Equal(t, http.StatusOK, updateRec.Code)
var updated appconfig.ExperimentRun
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updated, jsonx.V1()))
assert.InDelta(t, float32(25), updated.ExposurePercentage, 0.001)
stopRec := doRequest(t, h, http.MethodPatch, base+"/1/stop",
@@ -223,7 +224,7 @@ func TestHandler_ExperimentRun_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, stopRec.Code)
var stopped appconfig.ExperimentRun
- require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopped))
+ require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopped, jsonx.V1()))
assert.Equal(t, "DONE", stopped.Status)
require.NotNil(t, stopped.Result)
assert.Equal(t, "treatment won", stopped.Result.ExecutiveSummary)
@@ -234,7 +235,7 @@ func TestHandler_ExperimentRun_Lifecycle(t *testing.T) {
var eventsResp struct {
Items []appconfig.ExperimentRunEvent `json:"Items"`
}
- require.NoError(t, json.Unmarshal(eventsRec.Body.Bytes(), &eventsResp))
+ require.NoError(t, json.Unmarshal(eventsRec.Body.Bytes(), &eventsResp, jsonx.V1()))
require.Len(t, eventsResp.Items, 3, "RUN_STARTED, EXPOSURE_UPDATED, RUN_STOPPED")
assert.Equal(t, "RUN_STOPPED", eventsResp.Items[0].EventType, "most-recent-first ordering")
}
@@ -295,7 +296,7 @@ func TestHandler_Deployment_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
// Create env.
rec = doRequest(
@@ -308,7 +309,7 @@ func TestHandler_Deployment_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var env appconfig.Environment
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env, jsonx.V1()))
// Create configuration profile (required by StartDeployment validation).
profBody := []byte(`{"name":"my-profile","locationUri":"hosted"}`)
@@ -322,7 +323,7 @@ func TestHandler_Deployment_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var prof appconfig.ConfigurationProfile
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &prof))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &prof, jsonx.V1()))
// Create a hosted configuration version (required for StartDeployment
// to validate ConfigurationVersion against, for a "hosted" profile).
@@ -343,7 +344,7 @@ func TestHandler_Deployment_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var strat appconfig.DeploymentStrategy
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &strat))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &strat, jsonx.V1()))
// Start deployment.
depBodyStr := `{"configurationProfileId":"` + prof.ID +
@@ -358,7 +359,7 @@ func TestHandler_Deployment_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var dep appconfig.Deployment
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dep))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dep, jsonx.V1()))
assert.Equal(t, int32(1), dep.DeploymentNumber)
assert.Equal(t, "DEPLOYING", dep.State, "a non-zero-duration strategy must not complete synchronously")
assert.NotEmpty(t, dep.EventLog, "StartDeployment must record a DEPLOYMENT_STARTED event")
@@ -377,7 +378,7 @@ func TestHandler_Deployment_Lifecycle(t *testing.T) {
)
require.Equal(t, http.StatusOK, rec.Code)
var final appconfig.Deployment
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &final))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &final, jsonx.V1()))
assert.Equal(t, "COMPLETE", final.State)
assert.InDelta(t, float32(100.0), final.PercentageComplete, 0.001)
@@ -412,13 +413,13 @@ func TestHandler_Deployment_Lifecycle(t *testing.T) {
"Allow-Revert", "true", nil,
)
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dep))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dep, jsonx.V1()))
assert.Equal(t, "REVERTED", dep.State, "StopDeploymentOutput itself must reflect the new state")
rec = doRequest(t, h, http.MethodGet,
"/applications/"+app.ID+"/environments/"+env.ID+"/deployments/1", nil)
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dep))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dep, jsonx.V1()))
assert.Equal(t, "REVERTED", dep.State)
})
}
@@ -493,7 +494,7 @@ func TestHandler_ListDeployments_HTTP(t *testing.T) {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appOut))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appOut, jsonx.V1()))
envRec := doRequest(
t, h, http.MethodPost,
@@ -506,7 +507,7 @@ func TestHandler_ListDeployments_HTTP(t *testing.T) {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &envOut))
+ require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &envOut, jsonx.V1()))
listRec := doRequest(
t, h, http.MethodGet,
@@ -531,7 +532,7 @@ func TestHandler_DeploymentFieldsPresent(t *testing.T) {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
envRec := doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/environments",
[]byte(`{"Name":"dep-env"}`))
@@ -541,7 +542,7 @@ func TestHandler_DeploymentFieldsPresent(t *testing.T) {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env, jsonx.V1()))
profRec := doRequest(t, h, http.MethodPost,
"/applications/"+app.ID+"/configurationprofiles",
@@ -552,7 +553,7 @@ func TestHandler_DeploymentFieldsPresent(t *testing.T) {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof))
+ require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof, jsonx.V1()))
hcvRec := doRequest(t, h, http.MethodPost,
"/applications/"+app.ID+"/configurationprofiles/"+prof.ID+"/hostedconfigurationversions",
@@ -567,13 +568,13 @@ func TestHandler_DeploymentFieldsPresent(t *testing.T) {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(stratRec.Body.Bytes(), &strat))
+ require.NoError(t, json.Unmarshal(stratRec.Body.Bytes(), &strat, jsonx.V1()))
depBody, _ := json.Marshal(map[string]string{
"ConfigurationProfileId": prof.ID,
"DeploymentStrategyId": strat.ID,
"ConfigurationVersion": "1",
- })
+ }, jsonx.V1())
depRec := doRequest(t, h, http.MethodPost,
"/applications/"+app.ID+"/environments/"+env.ID+"/deployments", depBody)
require.Equal(t, http.StatusCreated, depRec.Code)
@@ -587,7 +588,7 @@ func TestHandler_DeploymentFieldsPresent(t *testing.T) {
PercentageComplete float32 `json:"PercentageComplete"`
DeploymentNumber int32 `json:"DeploymentNumber"`
}
- require.NoError(t, json.Unmarshal(depRec.Body.Bytes(), &dep))
+ require.NoError(t, json.Unmarshal(depRec.Body.Bytes(), &dep, jsonx.V1()))
assert.Equal(t, app.ID, dep.ApplicationID)
assert.Equal(t, env.ID, dep.EnvironmentID)
@@ -612,7 +613,7 @@ func TestHandler_DeploymentNumberIncrements(t *testing.T) {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
envRec := doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/environments",
[]byte(`{"Name":"seq-env"}`))
@@ -622,7 +623,7 @@ func TestHandler_DeploymentNumberIncrements(t *testing.T) {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env, jsonx.V1()))
profRec := doRequest(t, h, http.MethodPost,
"/applications/"+app.ID+"/configurationprofiles",
@@ -633,7 +634,7 @@ func TestHandler_DeploymentNumberIncrements(t *testing.T) {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof))
+ require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof, jsonx.V1()))
hcvRec := doRequest(t, h, http.MethodPost,
"/applications/"+app.ID+"/configurationprofiles/"+prof.ID+"/hostedconfigurationversions",
@@ -648,13 +649,13 @@ func TestHandler_DeploymentNumberIncrements(t *testing.T) {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(stratRec.Body.Bytes(), &strat))
+ require.NoError(t, json.Unmarshal(stratRec.Body.Bytes(), &strat, jsonx.V1()))
depBody, _ := json.Marshal(map[string]string{
"ConfigurationProfileId": prof.ID,
"DeploymentStrategyId": strat.ID,
"ConfigurationVersion": "1",
- })
+ }, jsonx.V1())
for wantNum := int32(1); wantNum <= 3; wantNum++ {
rec := doRequest(t, h, http.MethodPost,
@@ -664,7 +665,7 @@ func TestHandler_DeploymentNumberIncrements(t *testing.T) {
var dep struct {
DeploymentNumber int32 `json:"DeploymentNumber"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dep))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dep, jsonx.V1()))
assert.Equal(t, wantNum, dep.DeploymentNumber,
"deployment must have sequential DeploymentNumber")
}
diff --git a/services/appconfig/handler_environments_test.go b/services/appconfig/handler_environments_test.go
index dfbde232ac..65ec83ed68 100644
--- a/services/appconfig/handler_environments_test.go
+++ b/services/appconfig/handler_environments_test.go
@@ -1,7 +1,7 @@
package appconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appconfig"
)
@@ -22,7 +23,7 @@ func TestHandler_Environment_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
// Create env.
rec = doRequest(
@@ -35,7 +36,7 @@ func TestHandler_Environment_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var env appconfig.Environment
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env, jsonx.V1()))
assert.Equal(t, "production", env.Name)
assert.Equal(t, app.ID, env.ApplicationID)
@@ -62,7 +63,7 @@ func TestHandler_UpdateEnvironment_HTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
rec = doRequest(
t,
@@ -74,7 +75,7 @@ func TestHandler_UpdateEnvironment_HTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var env appconfig.Environment
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env, jsonx.V1()))
// Update env.
rec = doRequest(
@@ -87,7 +88,7 @@ func TestHandler_UpdateEnvironment_HTTP(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var updated appconfig.Environment
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "production", updated.Name)
}
@@ -104,14 +105,14 @@ func TestHandler_UpdateEnvironment_OmittedFieldsPreserved(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
rec = doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/environments",
[]byte(`{"Name":"staging","Description":"keep-me","Monitors":[{"AlarmArn":"arn:aws:cloudwatch:alarm1"}]}`))
require.Equal(t, http.StatusCreated, rec.Code)
var env appconfig.Environment
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env, jsonx.V1()))
require.Len(t, env.Monitors, 1)
// Update only Name; Description and Monitors are omitted.
@@ -120,7 +121,7 @@ func TestHandler_UpdateEnvironment_OmittedFieldsPreserved(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var updated appconfig.Environment
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "production", updated.Name)
assert.Equal(t, "keep-me", updated.Description, "omitted Description must be preserved")
require.Len(t, updated.Monitors, 1, "omitted Monitors must be preserved")
@@ -132,7 +133,7 @@ func TestHandler_UpdateEnvironment_OmittedFieldsPreserved(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var cleared appconfig.Environment
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cleared))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cleared, jsonx.V1()))
assert.Empty(t, cleared.Monitors, "an explicit empty Monitors list must replace the existing one")
}
@@ -209,19 +210,19 @@ func TestHandler_CreateEnvironment_TagsAppliedInline(t *testing.T) {
require.Equal(t, http.StatusCreated, appRec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
body, err := json.Marshal(map[string]any{
"Name": "tagged-env-" + tt.name,
"Tags": tt.tags,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/environments", body)
require.Equal(t, http.StatusCreated, rec.Code)
var env appconfig.Environment
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env, jsonx.V1()))
resourceArn := "arn:aws:appconfig:us-east-1:123456789012:application/" +
app.ID + "/environment/" + env.ID
@@ -231,7 +232,7 @@ func TestHandler_CreateEnvironment_TagsAppliedInline(t *testing.T) {
var tagsResp struct {
Tags map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp, jsonx.V1()))
if len(tt.tags) == 0 {
assert.Empty(t, tagsResp.Tags)
@@ -255,7 +256,7 @@ func TestHandler_EnvironmentStateIsReadyForDeployment(t *testing.T) {
var app struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
tests := []struct {
name string
@@ -285,7 +286,7 @@ func TestHandler_EnvironmentStateIsReadyForDeployment(t *testing.T) {
var env struct {
State string `json:"State"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env, jsonx.V1()))
assert.Equal(t, tt.want, env.State,
"environment State must be READY_FOR_DEPLOYMENT (SCREAMING_SNAKE_CASE)")
})
@@ -305,7 +306,7 @@ func TestHandler_EnvironmentStateRoundTrip(t *testing.T) {
var app struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
envRec := doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/environments",
[]byte(`{"Name":"staging"}`))
@@ -315,7 +316,7 @@ func TestHandler_EnvironmentStateRoundTrip(t *testing.T) {
ID string `json:"Id"`
State string `json:"State"`
}
- require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env, jsonx.V1()))
assert.Equal(t, "READY_FOR_DEPLOYMENT", env.State)
require.NotEmpty(t, env.ID)
@@ -327,7 +328,7 @@ func TestHandler_EnvironmentStateRoundTrip(t *testing.T) {
var getEnv struct {
State string `json:"State"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getEnv))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getEnv, jsonx.V1()))
assert.Equal(t, "READY_FOR_DEPLOYMENT", getEnv.State, "GetEnvironment must also return READY_FOR_DEPLOYMENT")
// ListEnvironments must also return correct state.
@@ -339,7 +340,7 @@ func TestHandler_EnvironmentStateRoundTrip(t *testing.T) {
State string `json:"State"`
} `json:"Items"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Items, 1)
assert.Equal(t, "READY_FOR_DEPLOYMENT", listResp.Items[0].State,
"ListEnvironments must also return READY_FOR_DEPLOYMENT")
@@ -381,14 +382,14 @@ func TestHandler_DeleteEnvironment_DeletionProtectionCheck(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
rec = doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/environments",
[]byte(`{"name":"dpc-env"}`))
require.Equal(t, http.StatusCreated, rec.Code)
var env appconfig.Environment
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &env, jsonx.V1()))
path := "/applications/" + app.ID + "/environments/" + env.ID
diff --git a/services/appconfig/handler_extensions_test.go b/services/appconfig/handler_extensions_test.go
index 99c10de476..40e9e7ecf5 100644
--- a/services/appconfig/handler_extensions_test.go
+++ b/services/appconfig/handler_extensions_test.go
@@ -1,7 +1,7 @@
package appconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appconfig"
)
@@ -86,7 +87,7 @@ func TestHandler_Extension_CRUD(t *testing.T) {
if tt.wantField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantValue, resp[tt.wantField])
}
})
@@ -103,7 +104,7 @@ func TestHandler_Extension_GetDeleteByID(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var ext appconfig.Extension
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ext))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ext, jsonx.V1()))
assert.NotEmpty(t, ext.ID)
assert.Equal(t, "ext-to-delete", ext.Name)
@@ -163,7 +164,7 @@ func TestHandler_Extension_ListPaginated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["Items"].([]any)
require.True(t, ok)
assert.Len(t, items, 2)
@@ -180,14 +181,14 @@ func TestHandler_ExtensionAssociation_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var ext appconfig.Extension
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ext))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ext, jsonx.V1()))
// Create an application to use as resource.
rec = doRequest(t, h, http.MethodPost, "/applications", []byte(`{"name":"assoc-app"}`))
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
// List associations empty.
rec = doRequest(t, h, http.MethodGet, "/extensionassociations", nil)
@@ -202,7 +203,7 @@ func TestHandler_ExtensionAssociation_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var assoc appconfig.ExtensionAssociation
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &assoc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &assoc, jsonx.V1()))
assert.NotEmpty(t, assoc.ID)
assert.Equal(t, ext.Arn, assoc.ExtensionArn)
@@ -215,7 +216,7 @@ func TestHandler_ExtensionAssociation_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
items, ok := listResp["Items"].([]any)
require.True(t, ok)
assert.Len(t, items, 1)
@@ -285,7 +286,7 @@ func TestHandler_UpdateExtension(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var ext appconfig.Extension
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ext))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ext, jsonx.V1()))
assert.Equal(t, int32(1), ext.VersionNumber)
// Update extension.
@@ -294,7 +295,7 @@ func TestHandler_UpdateExtension(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var updated appconfig.Extension
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "updated", updated.Description)
assert.Equal(t, int32(2), updated.VersionNumber)
}
@@ -313,7 +314,7 @@ func TestHandler_UpdateExtension_OmittedDescriptionPreserved(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var ext appconfig.Extension
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ext))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ext, jsonx.V1()))
// Update Actions only; Description is omitted.
rec = doRequest(t, h, http.MethodPatch, "/extensions/"+ext.ID,
@@ -321,7 +322,7 @@ func TestHandler_UpdateExtension_OmittedDescriptionPreserved(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var updated appconfig.Extension
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "keep-me", updated.Description, "omitted Description must not be cleared")
}
@@ -344,7 +345,7 @@ func TestHandler_UpdateExtensionAssociation(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var ext appconfig.Extension
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ext))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ext, jsonx.V1()))
resourceID := "arn:aws:appconfig:us-east-1:123456789012:application/abc"
assocBody := []byte(
@@ -354,7 +355,7 @@ func TestHandler_UpdateExtensionAssociation(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var assoc appconfig.ExtensionAssociation
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &assoc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &assoc, jsonx.V1()))
// Update the association parameters.
rec = doRequest(t, h, http.MethodPatch, "/extensionassociations/"+assoc.ID,
@@ -362,7 +363,7 @@ func TestHandler_UpdateExtensionAssociation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var updated appconfig.ExtensionAssociation
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "value", updated.Parameters["key"])
}
@@ -474,7 +475,7 @@ func TestHandler_Extension_VersionQueryParams(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var v1 appconfig.Extension
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &v1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &v1, jsonx.V1()))
rec = doRequest(t, h, http.MethodPatch, "/extensions/"+v1.ID,
[]byte(`{"Description":"v2"}`))
@@ -485,7 +486,7 @@ func TestHandler_Extension_VersionQueryParams(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var gotV1 appconfig.Extension
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gotV1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gotV1, jsonx.V1()))
assert.Equal(t, "v1", gotV1.Description)
assert.Equal(t, int32(1), gotV1.VersionNumber)
@@ -494,7 +495,7 @@ func TestHandler_Extension_VersionQueryParams(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var gotLatest appconfig.Extension
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gotLatest))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gotLatest, jsonx.V1()))
assert.Equal(t, int32(2), gotLatest.VersionNumber)
// Delete version 1 explicitly via the "version" query param; version 2
@@ -525,7 +526,7 @@ func TestHandler_DeleteExtension_ConflictWhenAssociated(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var ext appconfig.Extension
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ext))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ext, jsonx.V1()))
assocBody := []byte(
`{"ExtensionIdentifier":"` + ext.ID +
@@ -555,7 +556,7 @@ func TestHandler_ListExtensions_NameFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["Items"].([]any)
require.True(t, ok)
assert.Len(t, items, 1)
@@ -564,7 +565,7 @@ func TestHandler_ListExtensions_NameFilter(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/extensions?name=nonexistent", nil)
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok = resp["Items"].([]any)
require.True(t, ok)
assert.Empty(t, items)
@@ -601,14 +602,14 @@ func TestHandler_CreateExtension_TagsAppliedInline(t *testing.T) {
body, err := json.Marshal(map[string]any{
"Name": "tagged-ext-" + tt.name,
"Tags": tt.tags,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/extensions", body)
require.Equal(t, http.StatusCreated, rec.Code)
var ext appconfig.Extension
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ext))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ext, jsonx.V1()))
require.NotEmpty(t, ext.Arn)
tagsRec := doRequest(t, h, http.MethodGet, "/tags/"+ext.Arn, nil)
@@ -617,7 +618,7 @@ func TestHandler_CreateExtension_TagsAppliedInline(t *testing.T) {
var tagsResp struct {
Tags map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp, jsonx.V1()))
if len(tt.tags) == 0 {
assert.Empty(t, tagsResp.Tags)
@@ -662,21 +663,21 @@ func TestHandler_CreateExtensionAssociation_TagsAppliedInline(t *testing.T) {
require.Equal(t, http.StatusCreated, extRec.Code)
var ext appconfig.Extension
- require.NoError(t, json.Unmarshal(extRec.Body.Bytes(), &ext))
+ require.NoError(t, json.Unmarshal(extRec.Body.Bytes(), &ext, jsonx.V1()))
resourceID := "arn:aws:appconfig:us-east-1:123456789012:application/tagged-assoc-" + tt.name
body, err := json.Marshal(map[string]any{
"ExtensionIdentifier": ext.ID,
"ResourceIdentifier": resourceID,
"Tags": tt.tags,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/extensionassociations", body)
require.Equal(t, http.StatusCreated, rec.Code)
var assoc appconfig.ExtensionAssociation
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &assoc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &assoc, jsonx.V1()))
require.NotEmpty(t, assoc.Arn)
tagsRec := doRequest(t, h, http.MethodGet, "/tags/"+assoc.Arn, nil)
@@ -685,7 +686,7 @@ func TestHandler_CreateExtensionAssociation_TagsAppliedInline(t *testing.T) {
var tagsResp struct {
Tags map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp, jsonx.V1()))
if len(tt.tags) == 0 {
assert.Empty(t, tagsResp.Tags)
diff --git a/services/appconfig/handler_hosted_configuration_versions_test.go b/services/appconfig/handler_hosted_configuration_versions_test.go
index b5387357e0..caead87390 100644
--- a/services/appconfig/handler_hosted_configuration_versions_test.go
+++ b/services/appconfig/handler_hosted_configuration_versions_test.go
@@ -2,7 +2,7 @@ package appconfig_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/services/appconfig"
)
@@ -27,7 +28,7 @@ func TestHandler_HostedConfigurationVersion_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
profileBody := []byte(`{"name":"hcv-profile","locationUri":"hosted","type":"AWS.Freeform"}`)
rec = doRequest(
@@ -40,7 +41,7 @@ func TestHandler_HostedConfigurationVersion_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var profile appconfig.ConfigurationProfile
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile, jsonx.V1()))
// Create hosted configuration version with raw body.
e := echo.New()
@@ -146,7 +147,7 @@ func TestHandler_HostedConfigVersion_HTTP_ListEmpty(t *testing.T) {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appOut))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appOut, jsonx.V1()))
profRec := doRequest(
t, h, http.MethodPost,
@@ -159,7 +160,7 @@ func TestHandler_HostedConfigVersion_HTTP_ListEmpty(t *testing.T) {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &profOut))
+ require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &profOut, jsonx.V1()))
listRec := doRequest(
t,
@@ -193,7 +194,7 @@ func TestHandler_HostedConfigVersionResponseHeaders(t *testing.T) {
var app struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
profRec := doRequest(t, h, http.MethodPost,
"/applications/"+app.ID+"/configurationprofiles",
@@ -203,7 +204,7 @@ func TestHandler_HostedConfigVersionResponseHeaders(t *testing.T) {
var prof struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof))
+ require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof, jsonx.V1()))
base := "/applications/" + app.ID + "/configurationprofiles/" + prof.ID +
"/hostedconfigurationversions"
@@ -254,7 +255,7 @@ func TestHandler_CreateHostedConfigurationVersion_LatestVersionNumberConflict(t
var app struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
profRec := doRequest(t, h, http.MethodPost,
"/applications/"+app.ID+"/configurationprofiles",
@@ -264,7 +265,7 @@ func TestHandler_CreateHostedConfigurationVersion_LatestVersionNumberConflict(t
var prof struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof))
+ require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof, jsonx.V1()))
base := "/applications/" + app.ID + "/configurationprofiles/" + prof.ID + "/hostedconfigurationversions"
diff --git a/services/appconfig/handler_list_summary_test.go b/services/appconfig/handler_list_summary_test.go
index cc39e49353..bef7ac6677 100644
--- a/services/appconfig/handler_list_summary_test.go
+++ b/services/appconfig/handler_list_summary_test.go
@@ -1,7 +1,7 @@
package appconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appconfig"
)
@@ -154,7 +155,7 @@ func listSingleItem(t *testing.T, h *appconfig.Handler, path string) map[string]
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["Items"].([]any)
require.True(t, ok, "response missing Items list, got keys: %v", mapKeys(resp))
@@ -175,7 +176,7 @@ func setupConfigurationProfileListLeak(t *testing.T, h *appconfig.Handler) map[s
var app struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
profBody := []byte(`{
"Name": "cp-list-leak-profile",
@@ -200,7 +201,7 @@ func setupDeploymentListLeak(t *testing.T, h *appconfig.Handler) map[string]any
var app struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
envRec := doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/environments",
[]byte(`{"Name":"dep-list-leak-env"}`))
@@ -209,7 +210,7 @@ func setupDeploymentListLeak(t *testing.T, h *appconfig.Handler) map[string]any
var env struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env, jsonx.V1()))
profRec := doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/configurationprofiles",
[]byte(`{"Name":"dep-list-leak-profile","LocationUri":"hosted"}`))
@@ -218,7 +219,7 @@ func setupDeploymentListLeak(t *testing.T, h *appconfig.Handler) map[string]any
var prof struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof))
+ require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof, jsonx.V1()))
hcvRec := doRequest(t, h, http.MethodPost,
"/applications/"+app.ID+"/configurationprofiles/"+prof.ID+"/hostedconfigurationversions",
@@ -234,7 +235,7 @@ func setupDeploymentListLeak(t *testing.T, h *appconfig.Handler) map[string]any
var strat struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(stratRec.Body.Bytes(), &strat))
+ require.NoError(t, json.Unmarshal(stratRec.Body.Bytes(), &strat, jsonx.V1()))
depBody := []byte(`{
"ConfigurationProfileId": "` + prof.ID + `",
@@ -249,7 +250,7 @@ func setupDeploymentListLeak(t *testing.T, h *appconfig.Handler) map[string]any
var dep struct {
State string `json:"State"`
}
- require.NoError(t, json.Unmarshal(depRec.Body.Bytes(), &dep))
+ require.NoError(t, json.Unmarshal(depRec.Body.Bytes(), &dep, jsonx.V1()))
require.Equal(t, "COMPLETE", dep.State, "a zero-duration strategy must complete synchronously")
return listSingleItem(t, h, "/applications/"+app.ID+"/environments/"+env.ID+"/deployments")
@@ -315,7 +316,7 @@ func setupExtensionAssociationListLeak(t *testing.T, h *appconfig.Handler) map[s
var ext struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(extRec.Body.Bytes(), &ext))
+ require.NoError(t, json.Unmarshal(extRec.Body.Bytes(), &ext, jsonx.V1()))
appRec := doRequest(t, h, http.MethodPost, "/applications", []byte(`{"Name":"ext-assoc-list-leak-app"}`))
require.Equal(t, http.StatusCreated, appRec.Code)
@@ -323,7 +324,7 @@ func setupExtensionAssociationListLeak(t *testing.T, h *appconfig.Handler) map[s
var app struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
resourceID := "arn:aws:appconfig:us-east-1:123456789012:application/" + app.ID
assocBody := []byte(`{
@@ -346,7 +347,7 @@ func setupHostedConfigurationVersionListLeak(t *testing.T, h *appconfig.Handler)
var app struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
profRec := doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/configurationprofiles",
[]byte(`{"Name":"hcv-list-leak-profile","LocationUri":"hosted"}`))
@@ -355,7 +356,7 @@ func setupHostedConfigurationVersionListLeak(t *testing.T, h *appconfig.Handler)
var prof struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof))
+ require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof, jsonx.V1()))
hcvRec := doRequestWithHeader(t, h, http.MethodPost,
"/applications/"+app.ID+"/configurationprofiles/"+prof.ID+"/hostedconfigurationversions",
@@ -386,7 +387,7 @@ func setupEnvironmentListLeak(t *testing.T, h *appconfig.Handler) map[string]any
var app struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
envRec := doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/environments",
[]byte(`{"Name":"env-list-leak-env"}`))
@@ -422,7 +423,7 @@ func TestHandler_GetOps_NoCreatedAtUpdatedAt(t *testing.T) {
require.Equal(t, http.StatusCreated, appRec.Code)
var createdApp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &createdApp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &createdApp, jsonx.V1()))
assert.NotContains(t, createdApp, "CreatedAt")
assert.NotContains(t, createdApp, "UpdatedAt")
@@ -433,7 +434,7 @@ func TestHandler_GetOps_NoCreatedAtUpdatedAt(t *testing.T) {
require.Equal(t, http.StatusOK, getAppRec.Code)
var gotApp map[string]any
- require.NoError(t, json.Unmarshal(getAppRec.Body.Bytes(), &gotApp))
+ require.NoError(t, json.Unmarshal(getAppRec.Body.Bytes(), &gotApp, jsonx.V1()))
assert.NotContains(t, gotApp, "CreatedAt")
assert.NotContains(t, gotApp, "UpdatedAt")
@@ -443,7 +444,7 @@ func TestHandler_GetOps_NoCreatedAtUpdatedAt(t *testing.T) {
require.Equal(t, http.StatusCreated, stratRec.Code)
var createdStrat map[string]any
- require.NoError(t, json.Unmarshal(stratRec.Body.Bytes(), &createdStrat))
+ require.NoError(t, json.Unmarshal(stratRec.Body.Bytes(), &createdStrat, jsonx.V1()))
assert.NotContains(t, createdStrat, "CreatedAt")
assert.NotContains(t, createdStrat, "UpdatedAt")
}
diff --git a/services/appconfig/handler_tags_test.go b/services/appconfig/handler_tags_test.go
index 885aca1866..b611e42e1e 100644
--- a/services/appconfig/handler_tags_test.go
+++ b/services/appconfig/handler_tags_test.go
@@ -1,13 +1,14 @@
package appconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appconfig"
)
@@ -21,7 +22,7 @@ func TestHandler_TagResource(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &app, jsonx.V1()))
resourceArn := "arn:aws:appconfig:us-east-1:123456789012:application/" + app.ID
@@ -52,7 +53,7 @@ func TestHandler_TagResource_VerifyTags(t *testing.T) {
require.Equal(t, http.StatusCreated, created.Code)
var app appconfig.Application
- require.NoError(t, json.Unmarshal(created.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(created.Body.Bytes(), &app, jsonx.V1()))
resourceArn := "arn:aws:appconfig:us-east-1:123456789012:application/" + app.ID
@@ -66,7 +67,7 @@ func TestHandler_TagResource_VerifyTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "prod", resp["Tags"]["env"])
assert.Equal(t, "1.0", resp["Tags"]["version"])
@@ -78,7 +79,7 @@ func TestHandler_TagResource_VerifyTags(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/tags/"+resourceArn, nil)
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["Tags"], 1)
assert.Equal(t, "1.0", resp["Tags"]["version"])
}
diff --git a/services/appconfig/handler_test.go b/services/appconfig/handler_test.go
index 6b859a5b23..b09c2a92af 100644
--- a/services/appconfig/handler_test.go
+++ b/services/appconfig/handler_test.go
@@ -2,7 +2,7 @@ package appconfig_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/services/appconfig"
)
@@ -394,7 +395,7 @@ func TestHandler_ResourceIDsPresentAcrossResources(t *testing.T) {
var app struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
require.NotEmpty(t, app.ID, "Application must have a non-empty Id")
// Create environment.
@@ -406,7 +407,7 @@ func TestHandler_ResourceIDsPresentAcrossResources(t *testing.T) {
ID string `json:"Id"`
ApplicationID string `json:"ApplicationId"`
}
- require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env, jsonx.V1()))
require.NotEmpty(t, env.ID, "Environment must have a non-empty Id")
assert.Equal(t, app.ID, env.ApplicationID,
"Environment.ApplicationId must match the parent application")
@@ -422,7 +423,7 @@ func TestHandler_ResourceIDsPresentAcrossResources(t *testing.T) {
ApplicationID string `json:"ApplicationId"`
LocationURI string `json:"LocationUri"`
}
- require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof))
+ require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof, jsonx.V1()))
require.NotEmpty(t, prof.ID, "ConfigurationProfile must have a non-empty Id")
assert.Equal(t, app.ID, prof.ApplicationID)
assert.Equal(t, "hosted", prof.LocationURI)
@@ -436,7 +437,7 @@ func TestHandler_ResourceIDsPresentAcrossResources(t *testing.T) {
ID string `json:"Id"`
Name string `json:"Name"`
}
- require.NoError(t, json.Unmarshal(stratRec.Body.Bytes(), &strat))
+ require.NoError(t, json.Unmarshal(stratRec.Body.Bytes(), &strat, jsonx.V1()))
require.NotEmpty(t, strat.ID, "DeploymentStrategy must have a non-empty Id")
assert.Equal(t, "id-strat", strat.Name)
}
@@ -456,7 +457,7 @@ func TestHandler_ListResponsesUseItemsKey(t *testing.T) {
var app struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &app, jsonx.V1()))
envRec := doRequest(t, h, http.MethodPost, "/applications/"+app.ID+"/environments",
[]byte(`{"Name":"items-env"}`))
@@ -470,7 +471,7 @@ func TestHandler_ListResponsesUseItemsKey(t *testing.T) {
var prof struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof))
+ require.NoError(t, json.Unmarshal(profRec.Body.Bytes(), &prof, jsonx.V1()))
stratRec := doRequest(t, h, http.MethodPost, "/deploymentstrategies",
[]byte(`{"Name":"items-strat","DeploymentDurationInMinutes":0,"GrowthFactor":100,"ReplicateTo":"NONE"}`))
@@ -479,7 +480,7 @@ func TestHandler_ListResponsesUseItemsKey(t *testing.T) {
var env struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env))
+ require.NoError(t, json.Unmarshal(envRec.Body.Bytes(), &env, jsonx.V1()))
listPaths := []struct {
name string
@@ -506,7 +507,7 @@ func TestHandler_ListResponsesUseItemsKey(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "path: %s", tt.path)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp["Items"]
assert.True(t, ok, "%s: response must have 'Items' key, got keys: %v",
tt.name, mapKeys(resp))
diff --git a/services/appconfig/persistence.go b/services/appconfig/persistence.go
index 488fdddf54..d6d19609bd 100644
--- a/services/appconfig/persistence.go
+++ b/services/appconfig/persistence.go
@@ -2,7 +2,7 @@ package appconfig
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -54,7 +54,7 @@ const appconfigSnapshotVersion = 1
// deploymentTimers is deliberately NOT part of this snapshot -- see its doc
// comment on InMemoryBackend.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
TagsByArn map[string]map[string]string `json:"tagsByArn"`
VersionCounters map[string]map[string]int32 `json:"versionCounters"`
DeploymentCounters map[string]map[string]int32 `json:"deploymentCounters"`
diff --git a/services/appconfigdata/configuration.go b/services/appconfigdata/configuration.go
index b711d04445..4d40980005 100644
--- a/services/appconfigdata/configuration.go
+++ b/services/appconfigdata/configuration.go
@@ -1,9 +1,11 @@
package appconfigdata
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// SetConfiguration stores or updates configuration content for a profile,
@@ -33,7 +35,7 @@ func (b *InMemoryBackend) setConfiguration(app, env, profile, content, contentTy
if isJSONContentType(contentType) {
var v any
- if err := json.Unmarshal([]byte(content), &v); err != nil {
+ if err := json.Unmarshal([]byte(content), &v, jsonx.V1()); err != nil {
return ErrContentTypeMismatch
}
}
diff --git a/services/appconfigdata/configuration_test.go b/services/appconfigdata/configuration_test.go
index 2d1cfafed8..1f33af6fd2 100644
--- a/services/appconfigdata/configuration_test.go
+++ b/services/appconfigdata/configuration_test.go
@@ -1,7 +1,7 @@
package appconfigdata_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appconfigdata"
)
@@ -361,14 +362,14 @@ func TestHandler_RetryAfterHeader(t *testing.T) {
"EnvironmentIdentifier": "env",
"ConfigurationProfileIdentifier": "p",
"RequiredMinimumPollIntervalInSeconds": tt.pollInterval,
- })
+ }, jsonx.V1())
require.NoError(t, err)
sessionRec := doRequest(t, h, http.MethodPost, "/configurationsessions", sessionBody)
require.Equal(t, http.StatusCreated, sessionRec.Code)
var sessionResp map[string]string
- require.NoError(t, json.Unmarshal(sessionRec.Body.Bytes(), &sessionResp))
+ require.NoError(t, json.Unmarshal(sessionRec.Body.Bytes(), &sessionResp, jsonx.V1()))
tok := sessionResp["InitialConfigurationToken"]
// First poll — gets content.
@@ -635,13 +636,13 @@ func TestHandler_PollRateLimitEnforced(t *testing.T) {
"EnvironmentIdentifier": "env",
"ConfigurationProfileIdentifier": "p",
"RequiredMinimumPollIntervalInSeconds": tt.pollInterval,
- })
+ }, jsonx.V1())
rec := doRequest(t, h, http.MethodPost, "/configurationsessions", sessionBody)
require.Equal(t, http.StatusCreated, rec.Code)
var sessionResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &sessionResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &sessionResp, jsonx.V1()))
tok := sessionResp["InitialConfigurationToken"]
// First poll — succeeds.
@@ -657,7 +658,7 @@ func TestHandler_PollRateLimitEnforced(t *testing.T) {
assert.Equal(t, tt.wantRetry, rec2.Header().Get("Retry-After"))
var body map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "BadRequestException", body["__type"])
assert.Equal(t, "InvalidParameters", body["Reason"])
diff --git a/services/appconfigdata/errors_test.go b/services/appconfigdata/errors_test.go
index 75fc391fdb..b0a085d30c 100644
--- a/services/appconfigdata/errors_test.go
+++ b/services/appconfigdata/errors_test.go
@@ -1,13 +1,14 @@
package appconfigdata_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appconfigdata"
)
@@ -134,7 +135,7 @@ func TestHandler_BadRequestException_Details(t *testing.T) {
assert.Equal(t, "BadRequestException", rec.Header().Get("X-Amzn-Errortype"))
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "BadRequestException", body["__type"])
assert.Equal(t, "InvalidParameters", body["Reason"])
@@ -158,14 +159,14 @@ func TestHandler_BadRequestException_Details(t *testing.T) {
"EnvironmentIdentifier": "e",
"ConfigurationProfileIdentifier": "p",
"RequiredMinimumPollIntervalInSeconds": 60,
- })
+ }, jsonx.V1())
require.NoError(t, err)
sessionRec := doRequest(t, h2, http.MethodPost, "/configurationsessions", sessionBody)
require.Equal(t, http.StatusCreated, sessionRec.Code)
var sessionResp map[string]string
- require.NoError(t, json.Unmarshal(sessionRec.Body.Bytes(), &sessionResp))
+ require.NoError(t, json.Unmarshal(sessionRec.Body.Bytes(), &sessionResp, jsonx.V1()))
tok := sessionResp["InitialConfigurationToken"]
// First poll succeeds.
@@ -180,7 +181,7 @@ func TestHandler_BadRequestException_Details(t *testing.T) {
assert.Equal(t, "BadRequestException", rec2.Header().Get("X-Amzn-Errortype"))
var body map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "BadRequestException", body["__type"])
assert.Equal(t, "InvalidParameters", body["Reason"])
@@ -216,7 +217,7 @@ func TestHandler_ResourceNotFoundException_Structure(t *testing.T) {
assert.Equal(t, "ResourceNotFoundException", rec.Header().Get("X-Amzn-Errortype"))
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
assert.Equal(t, "Deployment", resp["ResourceType"])
@@ -245,7 +246,7 @@ func TestHandler_ResourceNotFoundException_Structure(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "BadRequestException", resp["__type"])
})
}
@@ -377,7 +378,7 @@ func TestHandler_ErrorResponseShape(t *testing.T) {
"X-Amzn-ErrorType header must match exception type")
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, tt.wantErrorType, body["__type"],
"response body __type field must match exception type")
assert.NotEmpty(t, body["message"], "error body must have a message field")
diff --git a/services/appconfigdata/handler.go b/services/appconfigdata/handler.go
index a12548cc2e..149f71ca57 100644
--- a/services/appconfigdata/handler.go
+++ b/services/appconfigdata/handler.go
@@ -2,7 +2,8 @@ package appconfigdata
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -15,6 +16,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -191,7 +193,7 @@ func (h *Handler) handleStartConfigurationSession(c *echo.Context) error {
log := logger.Load(c.Request().Context())
var req startSessionRequest
- if err := json.NewDecoder(c.Request().Body).Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &req, jsonx.V1()); err != nil {
log.Error("appconfigdata: failed to decode StartConfigurationSession request", "error", err)
return writeAWSError(c, http.StatusBadRequest, exceptionBadRequest, "invalid request body")
diff --git a/services/appconfigdata/handler_sdk_route_table_test.go b/services/appconfigdata/handler_sdk_route_table_test.go
index cf6fc091b4..8b25b1c79a 100644
--- a/services/appconfigdata/handler_sdk_route_table_test.go
+++ b/services/appconfigdata/handler_sdk_route_table_test.go
@@ -2,7 +2,7 @@ package appconfigdata_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http/httptest"
"strings"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// sdkRouteCases is the authoritative method+path for every real AppConfigData
@@ -95,7 +97,7 @@ func TestExtractOperation_SDKRouteTable(t *testing.T) {
var decoded struct {
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &decoded))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &decoded, jsonx.V1()))
assert.NotEqual(t, "not found", decoded.Message,
"method=%s path=%s op=%s: dispatched to the unmatched-route default", tc.method, tc.path, tc.op)
}
diff --git a/services/appconfigdata/handler_test.go b/services/appconfigdata/handler_test.go
index 7038514d52..e27bd7519c 100644
--- a/services/appconfigdata/handler_test.go
+++ b/services/appconfigdata/handler_test.go
@@ -2,7 +2,7 @@ package appconfigdata_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/services/appconfigdata"
)
@@ -20,7 +21,7 @@ import (
func nowUTC() time.Time { return time.Now().UTC() }
func mustMarshalJSON(v any) []byte {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
panic(err)
}
@@ -81,7 +82,7 @@ func startSession(t *testing.T, h *appconfigdata.Handler, app, env, profile stri
require.Equal(t, http.StatusCreated, rec.Code, "startSession failed: %s", rec.Body.String())
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
token := resp["InitialConfigurationToken"]
require.NotEmpty(t, token)
@@ -93,7 +94,7 @@ func decodeErrorBody(t *testing.T, body string) (string, string) {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &m), "error body must be valid JSON")
+ require.NoError(t, json.Unmarshal([]byte(body), &m, jsonx.V1()), "error body must be valid JSON")
errType, _ := m["__type"].(string)
errMsg, _ := m["message"].(string)
diff --git a/services/appconfigdata/persistence.go b/services/appconfigdata/persistence.go
index 08a3c3a1a4..b8d4b362b1 100644
--- a/services/appconfigdata/persistence.go
+++ b/services/appconfigdata/persistence.go
@@ -2,7 +2,7 @@ package appconfigdata
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -44,8 +44,8 @@ const appconfigdataSnapshotVersion = 1
// material to disk was judged not worth the added attack surface for a
// session-token integrity check.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serializes the backend state to JSON. It implements
diff --git a/services/appconfigdata/sessions_test.go b/services/appconfigdata/sessions_test.go
index 4f8d00acaa..f85adb8e97 100644
--- a/services/appconfigdata/sessions_test.go
+++ b/services/appconfigdata/sessions_test.go
@@ -1,7 +1,7 @@
package appconfigdata_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appconfigdata"
)
@@ -122,7 +123,7 @@ func TestHandler_StartConfigurationSession(t *testing.T) {
if tt.wantToken {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["InitialConfigurationToken"])
}
})
@@ -169,7 +170,7 @@ func TestHandler_PollIntervalValidation(t *testing.T) {
"EnvironmentIdentifier": "env",
"ConfigurationProfileIdentifier": "p",
"RequiredMinimumPollIntervalInSeconds": tt.interval,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/configurationsessions", bodyJSON)
@@ -221,14 +222,14 @@ func TestHandler_PollIntervalHonored(t *testing.T) {
"EnvironmentIdentifier": "env",
"ConfigurationProfileIdentifier": "profile",
"RequiredMinimumPollIntervalInSeconds": tt.interval,
- })
+ }, jsonx.V1())
require.NoError(t, err)
sessionRec := doRequest(t, h, http.MethodPost, "/configurationsessions", sessionBody)
require.Equal(t, http.StatusCreated, sessionRec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(sessionRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(sessionRec.Body.Bytes(), &resp, jsonx.V1()))
token := resp["InitialConfigurationToken"]
cfgRec := doRequest(t, h, http.MethodGet, "/configuration?configuration_token="+token, nil)
@@ -257,7 +258,7 @@ func TestHandler_StartSession_IdentifierLength(t *testing.T) {
"ApplicationIdentifier": longID,
"EnvironmentIdentifier": "env",
"ConfigurationProfileIdentifier": "p",
- })
+ }, jsonx.V1())
return b
}(),
@@ -269,7 +270,7 @@ func TestHandler_StartSession_IdentifierLength(t *testing.T) {
"ApplicationIdentifier": "app",
"EnvironmentIdentifier": longID,
"ConfigurationProfileIdentifier": "p",
- })
+ }, jsonx.V1())
return b
}(),
@@ -281,7 +282,7 @@ func TestHandler_StartSession_IdentifierLength(t *testing.T) {
"ApplicationIdentifier": "app",
"EnvironmentIdentifier": "env",
"ConfigurationProfileIdentifier": longID,
- })
+ }, jsonx.V1())
return b
}(),
@@ -329,7 +330,7 @@ func TestHandler_StartSession_MaxPollInterval(t *testing.T) {
"EnvironmentIdentifier": "env",
"ConfigurationProfileIdentifier": "p",
"RequiredMinimumPollIntervalInSeconds": tt.interval,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/configurationsessions", bodyJSON)
@@ -405,7 +406,7 @@ func TestHandler_StartSession_ExactMaxIdentifierLength(t *testing.T) {
"ApplicationIdentifier": validID,
"EnvironmentIdentifier": validID,
"ConfigurationProfileIdentifier": validID,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/configurationsessions", bodyJSON)
@@ -419,7 +420,7 @@ func TestHandler_StartSession_ExactMaxIdentifierLength(t *testing.T) {
"ApplicationIdentifier": invalidID,
"EnvironmentIdentifier": "env",
"ConfigurationProfileIdentifier": "p",
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/configurationsessions", bodyJSON)
@@ -462,7 +463,7 @@ func TestHandler_StartSession_PollInterval_Boundary(t *testing.T) {
"EnvironmentIdentifier": "env",
"ConfigurationProfileIdentifier": "p",
"RequiredMinimumPollIntervalInSeconds": tt.interval,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/configurationsessions", bodyJSON)
diff --git a/services/appconfigdata/store.go b/services/appconfigdata/store.go
index ce3d2ed916..071c622d47 100644
--- a/services/appconfigdata/store.go
+++ b/services/appconfigdata/store.go
@@ -6,12 +6,13 @@ import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
"sync/atomic"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -64,8 +65,8 @@ func profileKey(app, env, profile string) string {
func normalizedContentHash(content, contentType string) string {
if isJSONContentType(contentType) {
var v any
- if err := json.Unmarshal([]byte(content), &v); err == nil {
- if normalized, marshalErr := json.Marshal(v); marshalErr == nil {
+ if err := json.Unmarshal([]byte(content), &v, jsonx.V1()); err == nil {
+ if normalized, marshalErr := json.Marshal(v, jsonx.V1()); marshalErr == nil {
sum := sha256.Sum256(normalized)
return hex.EncodeToString(sum[:])
diff --git a/services/appintegrations/persistence.go b/services/appintegrations/persistence.go
index c242097b1e..9457ecceb6 100644
--- a/services/appintegrations/persistence.go
+++ b/services/appintegrations/persistence.go
@@ -2,7 +2,7 @@ package appintegrations
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,10 +12,10 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/applicationautoscaling/errors_test.go b/services/applicationautoscaling/errors_test.go
index 5409bedd73..078c805037 100644
--- a/services/applicationautoscaling/errors_test.go
+++ b/services/applicationautoscaling/errors_test.go
@@ -9,7 +9,7 @@ package applicationautoscaling_test
// InvalidNextTokenException.
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/applicationautoscaling"
)
@@ -40,7 +41,7 @@ func TestHandler_PutScalingPolicy_RequiresRegisteredTarget(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errBody map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "ObjectNotFoundException", errBody["__type"])
}
@@ -62,7 +63,7 @@ func TestHandler_PutScheduledAction_RequiresRegisteredTarget(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errBody map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "ObjectNotFoundException", errBody["__type"])
}
@@ -84,7 +85,7 @@ func TestHandler_ObjectNotFoundException_Is400Not404(t *testing.T) {
require.NotEqual(t, http.StatusNotFound, rec.Code)
var errBody map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "ObjectNotFoundException", errBody["__type"])
}
@@ -218,7 +219,7 @@ func TestHandler_DescribeOps_InvalidNextToken(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errBody map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "InvalidNextTokenException", errBody["__type"])
})
}
diff --git a/services/applicationautoscaling/handler.go b/services/applicationautoscaling/handler.go
index dfe8db7295..fe95936868 100644
--- a/services/applicationautoscaling/handler.go
+++ b/services/applicationautoscaling/handler.go
@@ -2,7 +2,7 @@ package applicationautoscaling
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -177,13 +178,13 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// marshalError serialises a JSONErrorResponse into bytes.
// Marshaling a struct with only string fields cannot fail; error is intentionally ignored.
func marshalError(errType, message string) []byte {
- payload, _ := json.Marshal(service.JSONErrorResponse{Type: errType, Message: message})
+ payload, _ := json.Marshal(service.JSONErrorResponse{Type: errType, Message: message}, jsonx.V1())
return payload
}
@@ -206,7 +207,7 @@ func marshalResourceError(errType, message, resourceName string) []byte {
Type string `json:"__type"`
Message string `json:"message"`
ResourceName string `json:"ResourceName,omitempty"`
- }{Type: errType, Message: message, ResourceName: resourceName})
+ }{Type: errType, Message: message, ResourceName: resourceName}, jsonx.V1())
return payload
}
@@ -234,9 +235,6 @@ func clientMessage(err error) string {
}
func (h *Handler) writeAPIError(_ context.Context, c *echo.Context, _ string, err error, msg string) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
resourceName := ""
if rn, ok := errors.AsType[resourceNamer](err); ok {
resourceName = rn.ResourceName()
@@ -270,7 +268,7 @@ func (h *Handler) writeAPIError(_ context.Context, c *echo.Context, _ string, er
case errors.Is(err, ErrValidation):
return c.JSONBlob(http.StatusBadRequest, marshalError("ValidationException", msg))
case errors.Is(err, errInvalidRequest), errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
return c.JSONBlob(http.StatusBadRequest, marshalError("ValidationException", msg))
// Server faults (HTTP 500).
case errors.Is(err, ErrConcurrentUpdate):
diff --git a/services/applicationautoscaling/handler_forecast_test.go b/services/applicationautoscaling/handler_forecast_test.go
index 713da3de7b..5964d44712 100644
--- a/services/applicationautoscaling/handler_forecast_test.go
+++ b/services/applicationautoscaling/handler_forecast_test.go
@@ -1,12 +1,14 @@
package applicationautoscaling_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_GetPredictiveScalingForecast(t *testing.T) {
@@ -146,7 +148,7 @@ func TestHandler_GetPredictiveScalingForecast_HonestlyEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cf, ok := resp["CapacityForecast"].(map[string]any)
require.True(t, ok, "expected CapacityForecast in response")
diff --git a/services/applicationautoscaling/handler_scalable_targets_describe_test.go b/services/applicationautoscaling/handler_scalable_targets_describe_test.go
index 3c40ab560d..c6b9327ac2 100644
--- a/services/applicationautoscaling/handler_scalable_targets_describe_test.go
+++ b/services/applicationautoscaling/handler_scalable_targets_describe_test.go
@@ -1,13 +1,15 @@
package applicationautoscaling_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_DeregisterScalableTarget(t *testing.T) {
@@ -131,7 +133,7 @@ func TestHandler_DeregisterScalableTarget_CascadeDelete(t *testing.T) {
"ServiceNamespace": "ecs",
})
var targetsResp map[string]any
- require.NoError(t, json.Unmarshal(targetsRec.Body.Bytes(), &targetsResp))
+ require.NoError(t, json.Unmarshal(targetsRec.Body.Bytes(), &targetsResp, jsonx.V1()))
targets, _ := targetsResp["ScalableTargets"].([]any)
assert.Empty(t, targets, "scalable targets should be empty after deregister")
@@ -140,7 +142,7 @@ func TestHandler_DeregisterScalableTarget_CascadeDelete(t *testing.T) {
"ServiceNamespace": "ecs",
})
var policiesResp map[string]any
- require.NoError(t, json.Unmarshal(policiesRec.Body.Bytes(), &policiesResp))
+ require.NoError(t, json.Unmarshal(policiesRec.Body.Bytes(), &policiesResp, jsonx.V1()))
policies, _ := policiesResp["ScalingPolicies"].([]any)
assert.Empty(t, policies, "scaling policies should be cascade-deleted")
@@ -149,7 +151,7 @@ func TestHandler_DeregisterScalableTarget_CascadeDelete(t *testing.T) {
"ServiceNamespace": "ecs",
})
var actionsResp map[string]any
- require.NoError(t, json.Unmarshal(actionsRec.Body.Bytes(), &actionsResp))
+ require.NoError(t, json.Unmarshal(actionsRec.Body.Bytes(), &actionsResp, jsonx.V1()))
actions, _ := actionsResp["ScheduledActions"].([]any)
assert.Empty(t, actions, "scheduled actions should be cascade-deleted")
}
@@ -191,14 +193,14 @@ func TestDeregisterScalableTarget_Cascades(t *testing.T) {
"ServiceNamespace": ns,
})
var polOut map[string]any
- require.NoError(t, json.Unmarshal(polRec.Body.Bytes(), &polOut))
+ require.NoError(t, json.Unmarshal(polRec.Body.Bytes(), &polOut, jsonx.V1()))
assert.Empty(t, polOut["ScalingPolicies"], "policies must be deleted when target is deregistered")
actRec := doRequest(t, h, "DescribeScheduledActions", map[string]any{
"ServiceNamespace": ns,
})
var actOut map[string]any
- require.NoError(t, json.Unmarshal(actRec.Body.Bytes(), &actOut))
+ require.NoError(t, json.Unmarshal(actRec.Body.Bytes(), &actOut, jsonx.V1()))
assert.Empty(t, actOut["ScheduledActions"], "scheduled actions must be deleted when target is deregistered")
}
@@ -240,7 +242,7 @@ func TestHandler_DescribeScalableTargets(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
targets, ok := resp["ScalableTargets"].([]any)
require.True(t, ok)
assert.Len(t, targets, tt.wantCount)
@@ -264,7 +266,7 @@ func TestHandler_DescribeScalableTargets_HasTimestamps(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
targets := resp["ScalableTargets"].([]any)
require.Len(t, targets, 1)
@@ -287,7 +289,7 @@ func TestHandler_DescribeScalableTargets_OmitsTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var registerResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®isterResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®isterResp, jsonx.V1()))
doRequest(t, h, "TagResource", map[string]any{
"ResourceARN": registerResp["ScalableTargetARN"],
@@ -298,7 +300,7 @@ func TestHandler_DescribeScalableTargets_OmitsTags(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
targets := resp["ScalableTargets"].([]any)
require.Len(t, targets, 1)
@@ -332,7 +334,7 @@ func TestHandler_DescribeScalableTargets_ResourceIdsFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
targets, ok := resp["ScalableTargets"].([]any)
require.True(t, ok)
assert.Len(t, targets, 2, "expected exactly 2 targets when filtering by ResourceIds")
@@ -390,7 +392,7 @@ func TestHandler_DescribeScalableTargets_ScalableDimensionFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
targets, ok := resp["ScalableTargets"].([]any)
require.True(t, ok)
assert.Len(t, targets, tt.wantCount)
@@ -431,7 +433,7 @@ func TestHandler_DescribeScalableTargets_Pagination(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
targets := out["ScalableTargets"].([]any)
assert.Len(t, targets, tc.wantCount)
if tc.wantNext {
@@ -481,7 +483,7 @@ func TestHandler_MaxResults_DescribeScalableTargets(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
targets, ok := resp["ScalableTargets"].([]any)
require.True(t, ok)
assert.Len(t, targets, tt.wantLen)
@@ -512,7 +514,7 @@ func TestHandler_ApplyMaxResults_CapAt50(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
targets, _ := resp["ScalableTargets"].([]any)
assert.Len(t, targets, 50, "MaxResults=200 should be capped at 50")
}
diff --git a/services/applicationautoscaling/handler_scalable_targets_test.go b/services/applicationautoscaling/handler_scalable_targets_test.go
index 8296e49099..878b5b8c04 100644
--- a/services/applicationautoscaling/handler_scalable_targets_test.go
+++ b/services/applicationautoscaling/handler_scalable_targets_test.go
@@ -1,13 +1,15 @@
package applicationautoscaling_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_RegisterScalableTarget(t *testing.T) {
@@ -42,7 +44,7 @@ func TestHandler_RegisterScalableTarget(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, tt.wantKey)
assert.Contains(t, resp[tt.wantKey].(string), "arn:aws:application-autoscaling:")
})
@@ -65,7 +67,7 @@ func TestHandler_RegisterScalableTarget_Upsert(t *testing.T) {
rec1 := doRequest(t, h, "RegisterScalableTarget", body)
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]string
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
// Update (upsert) - should update, not error
body["MaxCapacity"] = int32(20)
@@ -76,7 +78,7 @@ func TestHandler_RegisterScalableTarget_Upsert(t *testing.T) {
descRec := doRequest(t, h, "DescribeScalableTargets", map[string]any{"ServiceNamespace": "ecs"})
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
targets := descResp["ScalableTargets"].([]any)
require.Len(t, targets, 1)
target := targets[0].(map[string]any)
@@ -171,7 +173,7 @@ func TestHandler_RegisterScalableTarget_WithTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var regResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp, jsonx.V1()))
targetARN, _ := regResp["ScalableTargetARN"].(string)
require.NotEmpty(t, targetARN)
@@ -180,7 +182,7 @@ func TestHandler_RegisterScalableTarget_WithTags(t *testing.T) {
require.Equal(t, http.StatusOK, tagRec.Code)
var tagResp map[string]any
- require.NoError(t, json.Unmarshal(tagRec.Body.Bytes(), &tagResp))
+ require.NoError(t, json.Unmarshal(tagRec.Body.Bytes(), &tagResp, jsonx.V1()))
tags, ok := tagResp["Tags"].(map[string]any)
require.True(t, ok, "expected Tags in response")
assert.Equal(t, "prod", tags["env"])
@@ -207,7 +209,7 @@ func TestHandler_RegisterScalableTarget_WithRoleARN(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
targets, ok := descResp["ScalableTargets"].([]any)
require.True(t, ok)
require.Len(t, targets, 1)
@@ -239,7 +241,7 @@ func TestHandler_RegisterScalableTarget_WithSuspendedState(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
targets, ok := resp["ScalableTargets"].([]any)
require.True(t, ok)
require.Len(t, targets, 1)
@@ -283,7 +285,7 @@ func TestHandler_RegisterScalableTarget_UpdateSuspendedState(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
targets, _ := resp["ScalableTargets"].([]any)
require.Len(t, targets, 1)
@@ -435,7 +437,7 @@ func TestRegisterDeregisterLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
targets := descOut["ScalableTargets"].([]any)
require.Len(t, targets, 1)
t0 := targets[0].(map[string]any)
@@ -454,7 +456,7 @@ func TestRegisterDeregisterLifecycle(t *testing.T) {
"ServiceNamespace": ns,
})
var afterOut map[string]any
- require.NoError(t, json.Unmarshal(afterRec.Body.Bytes(), &afterOut))
+ require.NoError(t, json.Unmarshal(afterRec.Body.Bytes(), &afterOut, jsonx.V1()))
assert.Empty(t, afterOut["ScalableTargets"])
}
@@ -493,7 +495,7 @@ func TestHandler_RegisterScalableTarget_UpdateOmittedCapacityPreserved(t *testin
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
targets, _ := resp["ScalableTargets"].([]any)
require.Len(t, targets, 1)
target := targets[0].(map[string]any)
diff --git a/services/applicationautoscaling/handler_scaling_policies_test.go b/services/applicationautoscaling/handler_scaling_policies_test.go
index e396562d3c..88fc0e3027 100644
--- a/services/applicationautoscaling/handler_scaling_policies_test.go
+++ b/services/applicationautoscaling/handler_scaling_policies_test.go
@@ -1,7 +1,7 @@
package applicationautoscaling_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"net/http"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_PutScalingPolicy(t *testing.T) {
@@ -26,7 +28,7 @@ func TestHandler_PutScalingPolicy(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policyARN, _ := resp["PolicyARN"].(string)
assert.Contains(t, policyARN, "arn:aws:autoscaling:")
assert.Contains(t, policyARN, "scalingPolicy:")
@@ -125,7 +127,7 @@ func TestHandler_PutScalingPolicy_DefaultPolicyType(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
policies, ok := resp["ScalingPolicies"].([]any)
require.True(t, ok)
require.Len(t, policies, 1)
@@ -161,7 +163,7 @@ func TestHandler_PutScalingPolicy_PreservesTypeOnUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policies, _ := resp["ScalingPolicies"].([]any)
require.Len(t, policies, 1)
policy := policies[0].(map[string]any)
@@ -284,7 +286,7 @@ func TestHandler_DescribeScalingPolicies(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policies, ok := resp["ScalingPolicies"].([]any)
require.True(t, ok)
assert.Len(t, policies, tt.wantCount)
@@ -348,7 +350,7 @@ func TestHandler_DescribeScalingPolicies_RicherFilters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policies, ok := resp["ScalingPolicies"].([]any)
require.True(t, ok)
assert.Len(t, policies, tt.wantCount)
@@ -380,7 +382,7 @@ func TestHandler_DescribeScalingPolicies_IncludesConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policies, _ := resp["ScalingPolicies"].([]any)
require.Len(t, policies, 1)
policy := policies[0].(map[string]any)
@@ -411,7 +413,7 @@ func TestHandler_MaxResults_DescribeScalingPolicies(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policies, ok := resp["ScalingPolicies"].([]any)
require.True(t, ok)
assert.Len(t, policies, 2)
@@ -442,7 +444,7 @@ func TestScalingPolicyCRUD(t *testing.T) {
})
require.Equal(t, http.StatusOK, putRec.Code)
var putOut map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut, jsonx.V1()))
policyARN := putOut["PolicyARN"].(string)
assert.NotEmpty(t, policyARN)
@@ -450,7 +452,7 @@ func TestScalingPolicyCRUD(t *testing.T) {
"ServiceNamespace": ns,
})
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
policies := descOut["ScalingPolicies"].([]any)
require.Len(t, policies, 1)
p0 := policies[0].(map[string]any)
@@ -471,7 +473,7 @@ func TestScalingPolicyCRUD(t *testing.T) {
"ServiceNamespace": ns,
})
var afterOut map[string]any
- require.NoError(t, json.Unmarshal(afterRec.Body.Bytes(), &afterOut))
+ require.NoError(t, json.Unmarshal(afterRec.Body.Bytes(), &afterOut, jsonx.V1()))
assert.Empty(t, afterOut["ScalingPolicies"])
}
@@ -538,7 +540,7 @@ func TestHandler_PutScalingPolicy_Alarms(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var putOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putOut, jsonx.V1()))
_, putHasAlarms := putOut["Alarms"]
assert.False(t, putHasAlarms, "PutScalingPolicy response Alarms should be omitted")
@@ -549,7 +551,7 @@ func TestHandler_PutScalingPolicy_Alarms(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
policies := descOut["ScalingPolicies"].([]any)
require.Len(t, policies, 1)
_, descHasAlarms := policies[0].(map[string]any)["Alarms"]
@@ -589,7 +591,7 @@ func TestHandler_PutScalingPolicy_PredictiveScalingConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
policies := descOut["ScalingPolicies"].([]any)
require.Len(t, policies, 1)
p0 := policies[0].(map[string]any)
diff --git a/services/applicationautoscaling/handler_scheduled_actions_test.go b/services/applicationautoscaling/handler_scheduled_actions_test.go
index af98f94cb8..03700a7e99 100644
--- a/services/applicationautoscaling/handler_scheduled_actions_test.go
+++ b/services/applicationautoscaling/handler_scheduled_actions_test.go
@@ -1,13 +1,15 @@
package applicationautoscaling_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_PutScheduledAction(t *testing.T) {
@@ -25,7 +27,7 @@ func TestHandler_PutScheduledAction(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["ScheduledActionARN"], "arn:aws:autoscaling:")
assert.Contains(t, resp["ScheduledActionARN"], "scheduledAction:")
}
@@ -54,7 +56,7 @@ func TestHandler_PutScheduledAction_Upsert(t *testing.T) {
descRec := doRequest(t, h, "DescribeScheduledActions", map[string]any{"ServiceNamespace": "ecs"})
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
actions := descResp["ScheduledActions"].([]any)
assert.Len(t, actions, 1)
}
@@ -141,7 +143,7 @@ func TestHandler_PutScheduledAction_WithScalableTargetAction(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
actions, ok := resp["ScheduledActions"].([]any)
require.True(t, ok)
require.Len(t, actions, 1)
@@ -176,7 +178,7 @@ func TestHandler_PutScheduledAction_StartEndTimeTimezone(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
actions, ok := resp["ScheduledActions"].([]any)
require.True(t, ok)
require.Len(t, actions, 1)
@@ -318,7 +320,7 @@ func TestHandler_DescribeScheduledActions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
actions, ok := resp["ScheduledActions"].([]any)
require.True(t, ok)
assert.Len(t, actions, tt.wantCount)
@@ -377,7 +379,7 @@ func TestHandler_DescribeScheduledActions_RicherFilters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
actions, ok := resp["ScheduledActions"].([]any)
require.True(t, ok)
assert.Len(t, actions, tt.wantCount)
@@ -407,7 +409,7 @@ func TestHandler_MaxResults_DescribeScheduledActions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
actions, ok := resp["ScheduledActions"].([]any)
require.True(t, ok)
assert.Len(t, actions, 2)
@@ -439,7 +441,7 @@ func TestScheduledActionCRUD(t *testing.T) {
})
require.Equal(t, http.StatusOK, putRec.Code)
var putOut map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut, jsonx.V1()))
actionARN := putOut["ScheduledActionARN"].(string)
assert.NotEmpty(t, actionARN)
@@ -447,7 +449,7 @@ func TestScheduledActionCRUD(t *testing.T) {
"ServiceNamespace": ns,
})
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
actions := descOut["ScheduledActions"].([]any)
require.Len(t, actions, 1)
a0 := actions[0].(map[string]any)
@@ -467,7 +469,7 @@ func TestScheduledActionCRUD(t *testing.T) {
"ServiceNamespace": ns,
})
var afterOut map[string]any
- require.NoError(t, json.Unmarshal(afterRec.Body.Bytes(), &afterOut))
+ require.NoError(t, json.Unmarshal(afterRec.Body.Bytes(), &afterOut, jsonx.V1()))
assert.Empty(t, afterOut["ScheduledActions"])
}
@@ -507,7 +509,7 @@ func TestHandler_PutScheduledAction_UpdateClearsOmittedStartEndTime(t *testing.T
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
actions, ok := resp["ScheduledActions"].([]any)
require.True(t, ok)
require.Len(t, actions, 1)
@@ -554,7 +556,7 @@ func TestHandler_PutScheduledAction_UpdateWithoutScheduleKeepsOld(t *testing.T)
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
actions, ok := resp["ScheduledActions"].([]any)
require.True(t, ok)
require.Len(t, actions, 1)
diff --git a/services/applicationautoscaling/handler_tags_test.go b/services/applicationautoscaling/handler_tags_test.go
index b54f97f773..5bf03e39d7 100644
--- a/services/applicationautoscaling/handler_tags_test.go
+++ b/services/applicationautoscaling/handler_tags_test.go
@@ -1,13 +1,15 @@
package applicationautoscaling_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_TagResource(t *testing.T) {
@@ -24,7 +26,7 @@ func TestHandler_TagResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
resourceARN := createResp["ScalableTargetARN"]
tagRec := doRequest(t, h, "TagResource", map[string]any{
@@ -84,7 +86,7 @@ func TestHandler_TagResource_ExceedsLimit(t *testing.T) {
require.Equal(t, http.StatusOK, regRec.Code)
var regResp map[string]any
- require.NoError(t, json.Unmarshal(regRec.Body.Bytes(), ®Resp))
+ require.NoError(t, json.Unmarshal(regRec.Body.Bytes(), ®Resp, jsonx.V1()))
targetARN, _ := regResp["ScalableTargetARN"].(string)
require.NotEmpty(t, targetARN)
@@ -110,7 +112,7 @@ func TestHandler_TagResource_ExceedsLimit(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, overRec.Code)
var errBody map[string]any
- require.NoError(t, json.Unmarshal(overRec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(overRec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "TooManyTagsException", errBody["__type"])
assert.Equal(t, targetARN, errBody["ResourceName"])
}
@@ -141,7 +143,7 @@ func TestHandler_RegisterScalableTarget_ExceedsTagLimit(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errBody map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "LimitExceededException", errBody["__type"])
}
@@ -187,7 +189,7 @@ func TestHandler_TagOps_NotFound_ResourceNotFoundException(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errBody map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errBody["__type"])
assert.Equal(t, missingARN, errBody["ResourceName"])
})
@@ -208,7 +210,7 @@ func TestHandler_ListTagsForResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
resourceARN := createResp["ScalableTargetARN"]
doRequest(t, h, "TagResource", map[string]any{
@@ -220,7 +222,7 @@ func TestHandler_ListTagsForResource(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags, ok := listResp["Tags"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "prod", tags["env"])
@@ -234,7 +236,7 @@ func TestHandler_ListTagsForResource_EmptyARN(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errBody map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errBody["__type"],
"ListTagsForResource's own deserializeOpError switch types only ResourceNotFoundException")
}
@@ -253,7 +255,7 @@ func TestHandler_UntagResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
resourceARN := createResp["ScalableTargetARN"]
doRequest(t, h, "TagResource", map[string]any{
@@ -269,7 +271,7 @@ func TestHandler_UntagResource(t *testing.T) {
listRec := doRequest(t, h, "ListTagsForResource", map[string]any{"ResourceARN": resourceARN})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags, ok := listResp["Tags"].(map[string]any)
require.True(t, ok)
_, hasEnv := tags["env"]
@@ -305,7 +307,7 @@ func TestHandler_TagResource_RoundTrip(t *testing.T) {
})
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
tags := listOut["Tags"].(map[string]any)
assert.Equal(t, "prod", tags["env"])
assert.Equal(t, "platform", tags["team"])
@@ -320,7 +322,7 @@ func TestHandler_TagResource_RoundTrip(t *testing.T) {
"ResourceARN": arn,
})
var afterOut map[string]any
- require.NoError(t, json.Unmarshal(afterRec.Body.Bytes(), &afterOut))
+ require.NoError(t, json.Unmarshal(afterRec.Body.Bytes(), &afterOut, jsonx.V1()))
afterTags := afterOut["Tags"].(map[string]any)
assert.NotContains(t, afterTags, "env")
assert.Equal(t, "platform", afterTags["team"])
diff --git a/services/applicationautoscaling/handler_test.go b/services/applicationautoscaling/handler_test.go
index d49fe37375..66659d7861 100644
--- a/services/applicationautoscaling/handler_test.go
+++ b/services/applicationautoscaling/handler_test.go
@@ -2,7 +2,7 @@ package applicationautoscaling_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"log/slog"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/applicationautoscaling"
)
@@ -35,7 +36,7 @@ func doRequest(t *testing.T, h *applicationautoscaling.Handler, action string, b
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -101,7 +102,7 @@ func seedTargetNS(
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["ScalableTargetARN"].(string)
}
@@ -433,7 +434,7 @@ func TestApplicationAutoScaling_Handler_Reset(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
targets, _ := out["ScalableTargets"].([]any)
assert.Len(t, targets, tt.wantAfter)
diff --git a/services/applicationautoscaling/multi_region_test.go b/services/applicationautoscaling/multi_region_test.go
index 7720c4b5a6..a3b62f4a86 100644
--- a/services/applicationautoscaling/multi_region_test.go
+++ b/services/applicationautoscaling/multi_region_test.go
@@ -2,7 +2,7 @@ package applicationautoscaling_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/applicationautoscaling"
)
@@ -41,7 +42,7 @@ func decode(t *testing.T, body string) map[string]any {
out := map[string]any{}
if body != "" {
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
}
return out
diff --git a/services/applicationautoscaling/persistence.go b/services/applicationautoscaling/persistence.go
index 0d0985e1f7..eab6e67a92 100644
--- a/services/applicationautoscaling/persistence.go
+++ b/services/applicationautoscaling/persistence.go
@@ -2,7 +2,7 @@ package applicationautoscaling
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -35,10 +35,10 @@ const applicationautoscalingSnapshotVersion = 1
// it too), so it remains ephemeral, reset to empty by both Reset and a
// version-mismatch Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/applicationautoscaling/persistence_test.go b/services/applicationautoscaling/persistence_test.go
index 94abafa699..7309609c79 100644
--- a/services/applicationautoscaling/persistence_test.go
+++ b/services/applicationautoscaling/persistence_test.go
@@ -1,13 +1,14 @@
package applicationautoscaling_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/applicationautoscaling"
)
@@ -368,7 +369,7 @@ func TestPersistence_SnapshotRestore(t *testing.T) {
rec := doRequest(t, h2, "DescribeScalableTargets", map[string]any{"ServiceNamespace": "ecs"})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
targets := resp["ScalableTargets"].([]any)
assert.Len(t, targets, 1)
}
diff --git a/services/applicationautoscaling/scaling_activities_test.go b/services/applicationautoscaling/scaling_activities_test.go
index 3bf19ffd8e..e675c1592d 100644
--- a/services/applicationautoscaling/scaling_activities_test.go
+++ b/services/applicationautoscaling/scaling_activities_test.go
@@ -1,13 +1,14 @@
package applicationautoscaling_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/applicationautoscaling"
)
@@ -126,7 +127,7 @@ func TestHandler_DescribeScalingActivities(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
activities, ok := resp["ScalingActivities"].([]any)
require.True(t, ok)
assert.Empty(t, activities)
@@ -154,7 +155,7 @@ func TestHandler_DescribeScalingActivities_AfterRegister(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
activities, ok := resp["ScalingActivities"].([]any)
require.True(t, ok)
require.Len(t, activities, 1)
@@ -188,7 +189,7 @@ func TestHandler_DescribeScalingActivities_WithInput(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
acts, ok := resp["ScalingActivities"].([]any)
require.True(t, ok, "ScalingActivities should be an array")
assert.Empty(t, acts, "expected empty array not null")
@@ -213,7 +214,7 @@ func TestHandler_DescribeScalingActivities_Pagination(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
page1 := out1["ScalingActivities"].([]any)
assert.Len(t, page1, 2)
nextToken, ok := out1["NextToken"].(string)
@@ -226,7 +227,7 @@ func TestHandler_DescribeScalingActivities_Pagination(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
page2 := out2["ScalingActivities"].([]any)
assert.Len(t, page2, 2)
}
@@ -243,7 +244,7 @@ func TestHandler_DescribeScalingActivities_TypedFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
activities := out["ScalingActivities"].([]any)
require.Len(t, activities, 1)
diff --git a/services/appmesh/handler.go b/services/appmesh/handler.go
index 160bf2c1aa..8853733455 100644
--- a/services/appmesh/handler.go
+++ b/services/appmesh/handler.go
@@ -2,7 +2,8 @@ package appmesh
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -13,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -211,12 +213,12 @@ func metaToWire(m ResourceMeta) map[string]any {
}
// specOrEmpty returns a JSON object, ensuring we never return null.
-func specOrEmpty(spec json.RawMessage) any {
+func specOrEmpty(spec jsontext.Value) any {
if len(spec) == 0 {
return map[string]any{}
}
var v any
- if err := json.Unmarshal(spec, &v); err != nil {
+ if err := json.Unmarshal(spec, &v, jsonx.V1()); err != nil {
return map[string]any{}
}
diff --git a/services/appmesh/handler_meshes.go b/services/appmesh/handler_meshes.go
index 5583569248..803cb0c54b 100644
--- a/services/appmesh/handler_meshes.go
+++ b/services/appmesh/handler_meshes.go
@@ -1,7 +1,7 @@
package appmesh
import (
- "encoding/json"
+ "encoding/json/jsontext"
"net/http"
"github.com/labstack/echo/v5"
@@ -71,10 +71,10 @@ func (h *Handler) handleMeshes(c *echo.Context, segs []string) error {
func (h *Handler) handleCreateMesh(c *echo.Context) error {
var body struct {
- MeshName string `json:"meshName"`
- ClientToken string `json:"clientToken"`
- Spec json.RawMessage `json:"spec"`
- Tags []tagInput `json:"tags"`
+ MeshName string `json:"meshName"`
+ ClientToken string `json:"clientToken"`
+ Spec jsontext.Value `json:"spec"`
+ Tags []tagInput `json:"tags"`
}
if err := c.Bind(&body); err != nil || !isValidResourceName(body.MeshName) {
return c.JSON(
@@ -104,8 +104,8 @@ func (h *Handler) handleDescribeMesh(c *echo.Context, meshName string) error {
func (h *Handler) handleUpdateMesh(c *echo.Context, meshName string) error {
var body struct {
- ClientToken string `json:"clientToken"`
- Spec json.RawMessage `json:"spec"`
+ ClientToken string `json:"clientToken"`
+ Spec jsontext.Value `json:"spec"`
}
if err := c.Bind(&body); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequestException", "invalid request body"))
diff --git a/services/appmesh/handler_test.go b/services/appmesh/handler_test.go
index 3141a2b0d2..3ff79ffc11 100644
--- a/services/appmesh/handler_test.go
+++ b/services/appmesh/handler_test.go
@@ -1,7 +1,7 @@
package appmesh_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appmesh"
)
@@ -26,7 +27,7 @@ func doRequest(t *testing.T, h *appmesh.Handler, method, path string, body any)
t.Helper()
var bodyStr string
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
bodyStr = string(b)
}
@@ -45,7 +46,7 @@ func doRequest(t *testing.T, h *appmesh.Handler, method, path string, body any)
func getBody(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
diff --git a/services/appmesh/handler_virtual_gateways.go b/services/appmesh/handler_virtual_gateways.go
index 5bc6c92aa6..ca9e2443f8 100644
--- a/services/appmesh/handler_virtual_gateways.go
+++ b/services/appmesh/handler_virtual_gateways.go
@@ -1,7 +1,7 @@
package appmesh
import (
- "encoding/json"
+ "encoding/json/jsontext"
"net/http"
"github.com/labstack/echo/v5"
@@ -47,10 +47,10 @@ func (h *Handler) handleVirtualGateways(c *echo.Context, segs []string, meshName
func (h *Handler) handleCreateVirtualGateway(c *echo.Context, meshName string) error {
var body struct {
- VirtualGatewayName string `json:"virtualGatewayName"`
- ClientToken string `json:"clientToken"`
- Spec json.RawMessage `json:"spec"`
- Tags []tagInput `json:"tags"`
+ VirtualGatewayName string `json:"virtualGatewayName"`
+ ClientToken string `json:"clientToken"`
+ Spec jsontext.Value `json:"spec"`
+ Tags []tagInput `json:"tags"`
}
if err := c.Bind(&body); err != nil || !isValidResourceName(body.VirtualGatewayName) {
return c.JSON(
@@ -77,8 +77,8 @@ func (h *Handler) handleDescribeVirtualGateway(c *echo.Context, meshName, name s
func (h *Handler) handleUpdateVirtualGateway(c *echo.Context, meshName, name string) error {
var body struct {
- ClientToken string `json:"clientToken"`
- Spec json.RawMessage `json:"spec"`
+ ClientToken string `json:"clientToken"`
+ Spec jsontext.Value `json:"spec"`
}
if err := c.Bind(&body); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequestException", "invalid request body"))
@@ -190,10 +190,10 @@ func (h *Handler) handleGatewayRoutes(c *echo.Context, segs []string, meshName s
func (h *Handler) handleCreateGatewayRoute(c *echo.Context, meshName, vgName string) error {
var body struct {
- GatewayRouteName string `json:"gatewayRouteName"`
- ClientToken string `json:"clientToken"`
- Spec json.RawMessage `json:"spec"`
- Tags []tagInput `json:"tags"`
+ GatewayRouteName string `json:"gatewayRouteName"`
+ ClientToken string `json:"clientToken"`
+ Spec jsontext.Value `json:"spec"`
+ Tags []tagInput `json:"tags"`
}
if err := c.Bind(&body); err != nil || !isValidResourceName(body.GatewayRouteName) {
return c.JSON(
@@ -220,8 +220,8 @@ func (h *Handler) handleDescribeGatewayRoute(c *echo.Context, meshName, vgName,
func (h *Handler) handleUpdateGatewayRoute(c *echo.Context, meshName, vgName, routeName string) error {
var body struct {
- ClientToken string `json:"clientToken"`
- Spec json.RawMessage `json:"spec"`
+ ClientToken string `json:"clientToken"`
+ Spec jsontext.Value `json:"spec"`
}
if err := c.Bind(&body); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequestException", "invalid request body"))
diff --git a/services/appmesh/handler_virtual_nodes.go b/services/appmesh/handler_virtual_nodes.go
index 4bc1dcce0e..0db9ddb14c 100644
--- a/services/appmesh/handler_virtual_nodes.go
+++ b/services/appmesh/handler_virtual_nodes.go
@@ -1,7 +1,7 @@
package appmesh
import (
- "encoding/json"
+ "encoding/json/jsontext"
"net/http"
"github.com/labstack/echo/v5"
@@ -47,10 +47,10 @@ func (h *Handler) handleVirtualNodes(c *echo.Context, segs []string, meshName st
func (h *Handler) handleCreateVirtualNode(c *echo.Context, meshName string) error {
var body struct {
- VirtualNodeName string `json:"virtualNodeName"`
- ClientToken string `json:"clientToken"`
- Spec json.RawMessage `json:"spec"`
- Tags []tagInput `json:"tags"`
+ VirtualNodeName string `json:"virtualNodeName"`
+ ClientToken string `json:"clientToken"`
+ Spec jsontext.Value `json:"spec"`
+ Tags []tagInput `json:"tags"`
}
if err := c.Bind(&body); err != nil || !isValidResourceName(body.VirtualNodeName) {
return c.JSON(
@@ -77,8 +77,8 @@ func (h *Handler) handleDescribeVirtualNode(c *echo.Context, meshName, name stri
func (h *Handler) handleUpdateVirtualNode(c *echo.Context, meshName, name string) error {
var body struct {
- ClientToken string `json:"clientToken"`
- Spec json.RawMessage `json:"spec"`
+ ClientToken string `json:"clientToken"`
+ Spec jsontext.Value `json:"spec"`
}
if err := c.Bind(&body); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequestException", "invalid request body"))
diff --git a/services/appmesh/handler_virtual_routers.go b/services/appmesh/handler_virtual_routers.go
index 140c21ddc8..7c288fa9c6 100644
--- a/services/appmesh/handler_virtual_routers.go
+++ b/services/appmesh/handler_virtual_routers.go
@@ -1,7 +1,7 @@
package appmesh
import (
- "encoding/json"
+ "encoding/json/jsontext"
"net/http"
"github.com/labstack/echo/v5"
@@ -47,10 +47,10 @@ func (h *Handler) handleVirtualRouters(c *echo.Context, segs []string, meshName
func (h *Handler) handleCreateVirtualRouter(c *echo.Context, meshName string) error {
var body struct {
- VirtualRouterName string `json:"virtualRouterName"`
- ClientToken string `json:"clientToken"`
- Spec json.RawMessage `json:"spec"`
- Tags []tagInput `json:"tags"`
+ VirtualRouterName string `json:"virtualRouterName"`
+ ClientToken string `json:"clientToken"`
+ Spec jsontext.Value `json:"spec"`
+ Tags []tagInput `json:"tags"`
}
if err := c.Bind(&body); err != nil || !isValidResourceName(body.VirtualRouterName) {
return c.JSON(
@@ -77,8 +77,8 @@ func (h *Handler) handleDescribeVirtualRouter(c *echo.Context, meshName, name st
func (h *Handler) handleUpdateVirtualRouter(c *echo.Context, meshName, name string) error {
var body struct {
- ClientToken string `json:"clientToken"`
- Spec json.RawMessage `json:"spec"`
+ ClientToken string `json:"clientToken"`
+ Spec jsontext.Value `json:"spec"`
}
if err := c.Bind(&body); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequestException", "invalid request body"))
@@ -190,10 +190,10 @@ func (h *Handler) handleRoutes(c *echo.Context, segs []string, meshName string)
func (h *Handler) handleCreateRoute(c *echo.Context, meshName, vrName string) error {
var body struct {
- RouteName string `json:"routeName"`
- ClientToken string `json:"clientToken"`
- Spec json.RawMessage `json:"spec"`
- Tags []tagInput `json:"tags"`
+ RouteName string `json:"routeName"`
+ ClientToken string `json:"clientToken"`
+ Spec jsontext.Value `json:"spec"`
+ Tags []tagInput `json:"tags"`
}
if err := c.Bind(&body); err != nil || !isValidResourceName(body.RouteName) {
return c.JSON(
@@ -220,8 +220,8 @@ func (h *Handler) handleDescribeRoute(c *echo.Context, meshName, vrName, routeNa
func (h *Handler) handleUpdateRoute(c *echo.Context, meshName, vrName, routeName string) error {
var body struct {
- ClientToken string `json:"clientToken"`
- Spec json.RawMessage `json:"spec"`
+ ClientToken string `json:"clientToken"`
+ Spec jsontext.Value `json:"spec"`
}
if err := c.Bind(&body); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequestException", "invalid request body"))
diff --git a/services/appmesh/handler_virtual_services.go b/services/appmesh/handler_virtual_services.go
index e6bee819a8..2cd89ae43c 100644
--- a/services/appmesh/handler_virtual_services.go
+++ b/services/appmesh/handler_virtual_services.go
@@ -1,7 +1,7 @@
package appmesh
import (
- "encoding/json"
+ "encoding/json/jsontext"
"net/http"
"github.com/labstack/echo/v5"
@@ -47,10 +47,10 @@ func (h *Handler) handleVirtualServices(c *echo.Context, segs []string, meshName
func (h *Handler) handleCreateVirtualService(c *echo.Context, meshName string) error {
var body struct {
- VirtualServiceName string `json:"virtualServiceName"`
- ClientToken string `json:"clientToken"`
- Spec json.RawMessage `json:"spec"`
- Tags []tagInput `json:"tags"`
+ VirtualServiceName string `json:"virtualServiceName"`
+ ClientToken string `json:"clientToken"`
+ Spec jsontext.Value `json:"spec"`
+ Tags []tagInput `json:"tags"`
}
if err := c.Bind(&body); err != nil || !isValidResourceName(body.VirtualServiceName) {
return c.JSON(
@@ -77,8 +77,8 @@ func (h *Handler) handleDescribeVirtualService(c *echo.Context, meshName, name s
func (h *Handler) handleUpdateVirtualService(c *echo.Context, meshName, name string) error {
var body struct {
- ClientToken string `json:"clientToken"`
- Spec json.RawMessage `json:"spec"`
+ ClientToken string `json:"clientToken"`
+ Spec jsontext.Value `json:"spec"`
}
if err := c.Bind(&body); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequestException", "invalid request body"))
diff --git a/services/appmesh/interfaces.go b/services/appmesh/interfaces.go
index 37e588e3d3..e061a17256 100644
--- a/services/appmesh/interfaces.go
+++ b/services/appmesh/interfaces.go
@@ -2,7 +2,7 @@ package appmesh
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -15,59 +15,59 @@ type StorageBackend interface {
Restore(ctx context.Context, data []byte) error
// Mesh operations
- CreateMesh(name string, spec json.RawMessage, tags map[string]string) (*Mesh, error)
+ CreateMesh(name string, spec jsontext.Value, tags map[string]string) (*Mesh, error)
DescribeMesh(name string) (*Mesh, error)
- UpdateMesh(name string, spec json.RawMessage) (*Mesh, error)
+ UpdateMesh(name string, spec jsontext.Value) (*Mesh, error)
DeleteMesh(name string) (*Mesh, error)
ListMeshes(maxResults int32, nextToken string) ([]*MeshSummary, string, error)
// VirtualNode operations
- CreateVirtualNode(meshName, name string, spec json.RawMessage, tags map[string]string) (*VirtualNode, error)
+ CreateVirtualNode(meshName, name string, spec jsontext.Value, tags map[string]string) (*VirtualNode, error)
DescribeVirtualNode(meshName, name string) (*VirtualNode, error)
- UpdateVirtualNode(meshName, name string, spec json.RawMessage) (*VirtualNode, error)
+ UpdateVirtualNode(meshName, name string, spec jsontext.Value) (*VirtualNode, error)
DeleteVirtualNode(meshName, name string) (*VirtualNode, error)
ListVirtualNodes(meshName string, maxResults int32, nextToken string) ([]*VirtualNodeSummary, string, error)
// VirtualRouter operations
- CreateVirtualRouter(meshName, name string, spec json.RawMessage, tags map[string]string) (*VirtualRouter, error)
+ CreateVirtualRouter(meshName, name string, spec jsontext.Value, tags map[string]string) (*VirtualRouter, error)
DescribeVirtualRouter(meshName, name string) (*VirtualRouter, error)
- UpdateVirtualRouter(meshName, name string, spec json.RawMessage) (*VirtualRouter, error)
+ UpdateVirtualRouter(meshName, name string, spec jsontext.Value) (*VirtualRouter, error)
DeleteVirtualRouter(meshName, name string) (*VirtualRouter, error)
ListVirtualRouters(meshName string, maxResults int32, nextToken string) ([]*VirtualRouterSummary, string, error)
// Route operations (nested under virtual router)
CreateRoute(
meshName, virtualRouterName, routeName string,
- spec json.RawMessage,
+ spec jsontext.Value,
tags map[string]string,
) (*Route, error)
DescribeRoute(meshName, virtualRouterName, routeName string) (*Route, error)
- UpdateRoute(meshName, virtualRouterName, routeName string, spec json.RawMessage) (*Route, error)
+ UpdateRoute(meshName, virtualRouterName, routeName string, spec jsontext.Value) (*Route, error)
DeleteRoute(meshName, virtualRouterName, routeName string) (*Route, error)
ListRoutes(meshName, virtualRouterName string, maxResults int32, nextToken string) ([]*RouteSummary, string, error)
// VirtualService operations
- CreateVirtualService(meshName, name string, spec json.RawMessage, tags map[string]string) (*VirtualService, error)
+ CreateVirtualService(meshName, name string, spec jsontext.Value, tags map[string]string) (*VirtualService, error)
DescribeVirtualService(meshName, name string) (*VirtualService, error)
- UpdateVirtualService(meshName, name string, spec json.RawMessage) (*VirtualService, error)
+ UpdateVirtualService(meshName, name string, spec jsontext.Value) (*VirtualService, error)
DeleteVirtualService(meshName, name string) (*VirtualService, error)
ListVirtualServices(meshName string, maxResults int32, nextToken string) ([]*VirtualServiceSummary, string, error)
// VirtualGateway operations
- CreateVirtualGateway(meshName, name string, spec json.RawMessage, tags map[string]string) (*VirtualGateway, error)
+ CreateVirtualGateway(meshName, name string, spec jsontext.Value, tags map[string]string) (*VirtualGateway, error)
DescribeVirtualGateway(meshName, name string) (*VirtualGateway, error)
- UpdateVirtualGateway(meshName, name string, spec json.RawMessage) (*VirtualGateway, error)
+ UpdateVirtualGateway(meshName, name string, spec jsontext.Value) (*VirtualGateway, error)
DeleteVirtualGateway(meshName, name string) (*VirtualGateway, error)
ListVirtualGateways(meshName string, maxResults int32, nextToken string) ([]*VirtualGatewaySummary, string, error)
// GatewayRoute operations (nested under virtual gateway)
CreateGatewayRoute(
meshName, virtualGatewayName, routeName string,
- spec json.RawMessage,
+ spec jsontext.Value,
tags map[string]string,
) (*GatewayRoute, error)
DescribeGatewayRoute(meshName, virtualGatewayName, routeName string) (*GatewayRoute, error)
- UpdateGatewayRoute(meshName, virtualGatewayName, routeName string, spec json.RawMessage) (*GatewayRoute, error)
+ UpdateGatewayRoute(meshName, virtualGatewayName, routeName string, spec jsontext.Value) (*GatewayRoute, error)
DeleteGatewayRoute(meshName, virtualGatewayName, routeName string) (*GatewayRoute, error)
ListGatewayRoutes(
meshName, virtualGatewayName string,
@@ -102,7 +102,7 @@ type Mesh struct {
Name string
Status string
Meta ResourceMeta
- Spec json.RawMessage
+ Spec jsontext.Value
}
// MeshSummary is a mesh entry in a list response.
@@ -122,7 +122,7 @@ type VirtualNode struct {
VirtualNodeName string
Status string
Meta ResourceMeta
- Spec json.RawMessage
+ Spec jsontext.Value
}
// VirtualNodeSummary is a virtual node entry in a list response.
@@ -143,7 +143,7 @@ type VirtualRouter struct {
VirtualRouterName string
Status string
Meta ResourceMeta
- Spec json.RawMessage
+ Spec jsontext.Value
}
// VirtualRouterSummary is a virtual router entry in a list response.
@@ -165,7 +165,7 @@ type Route struct {
RouteName string
Status string
Meta ResourceMeta
- Spec json.RawMessage
+ Spec jsontext.Value
}
// RouteSummary is a route entry in a list response.
@@ -187,7 +187,7 @@ type VirtualService struct {
VirtualServiceName string
Status string
Meta ResourceMeta
- Spec json.RawMessage
+ Spec jsontext.Value
}
// VirtualServiceSummary is a virtual service entry in a list response.
@@ -208,7 +208,7 @@ type VirtualGateway struct {
VirtualGatewayName string
Status string
Meta ResourceMeta
- Spec json.RawMessage
+ Spec jsontext.Value
}
// VirtualGatewaySummary is a virtual gateway entry in a list response.
@@ -230,7 +230,7 @@ type GatewayRoute struct {
GatewayRouteName string
Status string
Meta ResourceMeta
- Spec json.RawMessage
+ Spec jsontext.Value
}
// GatewayRouteSummary is a gateway route entry in a list response.
diff --git a/services/appmesh/meshes.go b/services/appmesh/meshes.go
index 19f93ffdf0..b54ac252c8 100644
--- a/services/appmesh/meshes.go
+++ b/services/appmesh/meshes.go
@@ -1,7 +1,7 @@
package appmesh
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -12,7 +12,7 @@ func (b *InMemoryBackend) meshARN(meshName string) string {
return arn.Build("appmesh", b.region, b.accountID, fmt.Sprintf("mesh/%s", meshName))
}
-func (b *InMemoryBackend) CreateMesh(name string, spec json.RawMessage, tags map[string]string) (*Mesh, error) {
+func (b *InMemoryBackend) CreateMesh(name string, spec jsontext.Value, tags map[string]string) (*Mesh, error) {
b.mu.Lock("CreateMesh")
defer b.mu.Unlock()
if err := validateMeshSpec(spec); err != nil {
@@ -47,7 +47,7 @@ func (b *InMemoryBackend) DescribeMesh(name string) (*Mesh, error) {
return m, nil
}
-func (b *InMemoryBackend) UpdateMesh(name string, spec json.RawMessage) (*Mesh, error) {
+func (b *InMemoryBackend) UpdateMesh(name string, spec jsontext.Value) (*Mesh, error) {
b.mu.Lock("UpdateMesh")
defer b.mu.Unlock()
if err := validateMeshSpec(spec); err != nil {
diff --git a/services/appmesh/meshes_test.go b/services/appmesh/meshes_test.go
index f016fb4d6e..0511bae77b 100644
--- a/services/appmesh/meshes_test.go
+++ b/services/appmesh/meshes_test.go
@@ -1,7 +1,7 @@
package appmesh_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"net/http"
"strings"
@@ -232,7 +232,7 @@ func TestBackend_SpecRoundTrip(t *testing.T) {
t.Parallel()
b := appmesh.NewInMemoryBackend("000000000000", "us-east-1")
- _, err := b.CreateMesh("m1", json.RawMessage(`{"key":"val"}`), nil)
+ _, err := b.CreateMesh("m1", jsontext.Value(`{"key":"val"}`), nil)
require.NoError(t, err)
m, err := b.DescribeMesh("m1")
diff --git a/services/appmesh/persistence.go b/services/appmesh/persistence.go
index c58e2f41c1..0c94fd83a9 100644
--- a/services/appmesh/persistence.go
+++ b/services/appmesh/persistence.go
@@ -2,7 +2,7 @@ package appmesh
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -36,7 +36,7 @@ const appmeshSnapshotVersion = 1
// an incompatible (older or newer) build of this backend as though it were
// the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
AccountID string `json:"accountID"`
Region string `json:"region"`
diff --git a/services/appmesh/spec_schema.go b/services/appmesh/spec_schema.go
index 3d3b285d15..bf98c5ce97 100644
--- a/services/appmesh/spec_schema.go
+++ b/services/appmesh/spec_schema.go
@@ -2,7 +2,8 @@ package appmesh
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -12,6 +13,7 @@ import (
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errSpecDSL = errors.New("appmesh spec dsl")
@@ -459,13 +461,13 @@ func mustSpecSchemas() *specSchemas {
return s
}
-func (s *specSchemas) validate(shape string, spec json.RawMessage) error {
+func (s *specSchemas) validate(shape string, spec jsontext.Value) error {
if len(bytes.TrimSpace(spec)) == 0 {
return nil
}
var v any
- if err := json.Unmarshal(spec, &v); err != nil {
+ if err := json.Unmarshal(spec, &v, jsonx.V1()); err != nil {
return awserr.Newf("spec: %s", awserr.ErrInvalidParameter, err)
}
diff --git a/services/appmesh/spec_schema_test.go b/services/appmesh/spec_schema_test.go
index 502ea0adf5..765caa7c6e 100644
--- a/services/appmesh/spec_schema_test.go
+++ b/services/appmesh/spec_schema_test.go
@@ -1,7 +1,7 @@
package appmesh_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"testing"
"github.com/stretchr/testify/assert"
@@ -105,10 +105,10 @@ func TestBackend_DeepSpecValidation(t *testing.T) {
require.NoError(t, err)
_, err = b.CreateVirtualRouter("m", "r", nil, nil)
require.NoError(t, err)
- _, err = b.CreateVirtualGateway("m", "g", json.RawMessage(`{"listeners":[`+httpPortMapping+`]}`), nil)
+ _, err = b.CreateVirtualGateway("m", "g", jsontext.Value(`{"listeners":[`+httpPortMapping+`]}`), nil)
require.NoError(t, err)
- spec := json.RawMessage(tt.spec)
+ spec := jsontext.Value(tt.spec)
switch tt.kind {
case "node":
diff --git a/services/appmesh/spec_validate.go b/services/appmesh/spec_validate.go
index 9ca254f0e1..64d1a41503 100644
--- a/services/appmesh/spec_validate.go
+++ b/services/appmesh/spec_validate.go
@@ -1,9 +1,11 @@
package appmesh
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Structural validation for the shallow spec shapes (MeshSpec, VirtualRouterSpec,
@@ -36,7 +38,7 @@ func isValidPortProtocol(p string) bool {
}
// validateMeshSpec structurally validates a MeshSpec body (types.MeshSpec).
-func validateMeshSpec(spec json.RawMessage) error {
+func validateMeshSpec(spec jsontext.Value) error {
if len(spec) == 0 {
return nil
}
@@ -48,7 +50,7 @@ func validateMeshSpec(spec json.RawMessage) error {
IPPreference string `json:"ipPreference"`
} `json:"serviceDiscovery"`
}
- if err := json.Unmarshal(spec, &body); err != nil {
+ if err := json.Unmarshal(spec, &body, jsonx.V1()); err != nil {
return awserr.Newf("spec: %s", awserr.ErrInvalidParameter, err)
}
if body.EgressFilter != nil && !isValidEgressFilterType(body.EgressFilter.Type) {
@@ -89,7 +91,7 @@ func validatePortMapping(pm *portMappingBody) error {
// validateVirtualRouterSpec structurally validates a VirtualRouterSpec body
// (types.VirtualRouterSpec).
-func validateVirtualRouterSpec(spec json.RawMessage) error {
+func validateVirtualRouterSpec(spec jsontext.Value) error {
if len(spec) == 0 {
return nil
}
@@ -98,7 +100,7 @@ func validateVirtualRouterSpec(spec json.RawMessage) error {
PortMapping *portMappingBody `json:"portMapping"`
} `json:"listeners"`
}
- if err := json.Unmarshal(spec, &body); err != nil {
+ if err := json.Unmarshal(spec, &body, jsonx.V1()); err != nil {
return awserr.Newf("spec: %s", awserr.ErrInvalidParameter, err)
}
for _, l := range body.Listeners {
@@ -122,14 +124,14 @@ type virtualServiceProviderBody struct {
// validateVirtualServiceSpec structurally validates a VirtualServiceSpec body
// (types.VirtualServiceSpec). provider is a smithy union: exactly one of
// virtualNode/virtualRouter may be set.
-func validateVirtualServiceSpec(spec json.RawMessage) error {
+func validateVirtualServiceSpec(spec jsontext.Value) error {
if len(spec) == 0 {
return nil
}
var body struct {
Provider *virtualServiceProviderBody `json:"provider"`
}
- if err := json.Unmarshal(spec, &body); err != nil {
+ if err := json.Unmarshal(spec, &body, jsonx.V1()); err != nil {
return awserr.Newf("spec: %s", awserr.ErrInvalidParameter, err)
}
if body.Provider == nil {
diff --git a/services/appmesh/spec_validate_test.go b/services/appmesh/spec_validate_test.go
index 5d5a43056d..909c27ea09 100644
--- a/services/appmesh/spec_validate_test.go
+++ b/services/appmesh/spec_validate_test.go
@@ -1,7 +1,7 @@
package appmesh_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"testing"
"github.com/stretchr/testify/assert"
@@ -37,9 +37,9 @@ func TestBackend_MeshSpecValidation(t *testing.T) {
t.Parallel()
b := appmesh.NewInMemoryBackend("000000000000", "us-east-1")
- var raw json.RawMessage
+ var raw jsontext.Value
if tt.spec != "" {
- raw = json.RawMessage(tt.spec)
+ raw = jsontext.Value(tt.spec)
}
_, err := b.CreateMesh("m1", raw, nil)
if tt.wantErr {
@@ -102,9 +102,9 @@ func TestBackend_VirtualRouterSpecValidation(t *testing.T) {
_, err := b.CreateMesh("m1", nil, nil)
require.NoError(t, err)
- var raw json.RawMessage
+ var raw jsontext.Value
if tt.spec != "" {
- raw = json.RawMessage(tt.spec)
+ raw = jsontext.Value(tt.spec)
}
_, err = b.CreateVirtualRouter("m1", "vr1", raw, nil)
if tt.wantErr {
@@ -168,9 +168,9 @@ func TestBackend_VirtualServiceSpecValidation(t *testing.T) {
_, err := b.CreateMesh("m1", nil, nil)
require.NoError(t, err)
- var raw json.RawMessage
+ var raw jsontext.Value
if tt.spec != "" {
- raw = json.RawMessage(tt.spec)
+ raw = jsontext.Value(tt.spec)
}
_, err = b.CreateVirtualService("m1", "vs1", raw, nil)
if tt.wantErr {
diff --git a/services/appmesh/store.go b/services/appmesh/store.go
index e0c1a2aa61..554cc46954 100644
--- a/services/appmesh/store.go
+++ b/services/appmesh/store.go
@@ -1,7 +1,7 @@
package appmesh
import (
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
"maps"
@@ -108,9 +108,9 @@ const statusActive = "ACTIVE"
const statusDeleted = "DELETED"
// normalizeSpec returns a non-nil JSON object if spec is nil or empty.
-func normalizeSpec(spec json.RawMessage) json.RawMessage {
+func normalizeSpec(spec jsontext.Value) jsontext.Value {
if len(spec) == 0 {
- return json.RawMessage(`{}`)
+ return jsontext.Value(`{}`)
}
return spec
diff --git a/services/appmesh/virtual_gateways.go b/services/appmesh/virtual_gateways.go
index e52765036a..7dc36f62c1 100644
--- a/services/appmesh/virtual_gateways.go
+++ b/services/appmesh/virtual_gateways.go
@@ -1,7 +1,7 @@
package appmesh
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"sort"
"time"
@@ -23,7 +23,7 @@ func (b *InMemoryBackend) gatewayRouteARN(meshName, vgName, routeName string) st
// ─── VirtualGateway ───
func (b *InMemoryBackend) CreateVirtualGateway(
- meshName, name string, spec json.RawMessage, tags map[string]string,
+ meshName, name string, spec jsontext.Value, tags map[string]string,
) (*VirtualGateway, error) {
b.mu.Lock("CreateVirtualGateway")
defer b.mu.Unlock()
@@ -67,7 +67,7 @@ func (b *InMemoryBackend) DescribeVirtualGateway(meshName, name string) (*Virtua
return vg, nil
}
-func (b *InMemoryBackend) UpdateVirtualGateway(meshName, name string, spec json.RawMessage) (*VirtualGateway, error) {
+func (b *InMemoryBackend) UpdateVirtualGateway(meshName, name string, spec jsontext.Value) (*VirtualGateway, error) {
if err := b.schemas.validate("virtualGatewaySpec", spec); err != nil {
return nil, err
}
@@ -145,7 +145,7 @@ func (b *InMemoryBackend) ListVirtualGateways(
// ─── GatewayRoute ───
func (b *InMemoryBackend) CreateGatewayRoute(
- meshName, virtualGatewayName, routeName string, spec json.RawMessage, tags map[string]string,
+ meshName, virtualGatewayName, routeName string, spec jsontext.Value, tags map[string]string,
) (*GatewayRoute, error) {
if err := b.schemas.validate("gatewayRouteSpec", spec); err != nil {
return nil, err
@@ -197,7 +197,7 @@ func (b *InMemoryBackend) DescribeGatewayRoute(meshName, virtualGatewayName, rou
}
func (b *InMemoryBackend) UpdateGatewayRoute(
- meshName, virtualGatewayName, routeName string, spec json.RawMessage,
+ meshName, virtualGatewayName, routeName string, spec jsontext.Value,
) (*GatewayRoute, error) {
if err := b.schemas.validate("gatewayRouteSpec", spec); err != nil {
return nil, err
diff --git a/services/appmesh/virtual_gateways_test.go b/services/appmesh/virtual_gateways_test.go
index 565f0ca038..d15c513717 100644
--- a/services/appmesh/virtual_gateways_test.go
+++ b/services/appmesh/virtual_gateways_test.go
@@ -1,7 +1,7 @@
package appmesh_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"net/http"
"testing"
@@ -232,7 +232,7 @@ func TestBackend_UpdateVirtualGateway(t *testing.T) {
tt.setup(b)
vg, err := b.UpdateVirtualGateway(
tt.meshName, tt.vgName,
- json.RawMessage(`{"listeners":[{"portMapping":{"port":8080,"protocol":"http"}}]}`),
+ jsontext.Value(`{"listeners":[{"portMapping":{"port":8080,"protocol":"http"}}]}`),
)
if tt.wantErr != nil {
require.Error(t, err)
@@ -306,7 +306,7 @@ func TestBackend_UpdateGatewayRoute(t *testing.T) {
tt.meshName,
tt.vgName,
tt.routeName,
- json.RawMessage(`{}`),
+ jsontext.Value(`{}`),
)
if tt.wantErr != nil {
require.Error(t, err)
diff --git a/services/appmesh/virtual_nodes.go b/services/appmesh/virtual_nodes.go
index 526d0a9e76..0e2c236dc2 100644
--- a/services/appmesh/virtual_nodes.go
+++ b/services/appmesh/virtual_nodes.go
@@ -1,12 +1,14 @@
package appmesh
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"sort"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (b *InMemoryBackend) virtualNodeARN(meshName, name string) string {
@@ -14,7 +16,7 @@ func (b *InMemoryBackend) virtualNodeARN(meshName, name string) string {
}
func (b *InMemoryBackend) CreateVirtualNode(
- meshName, name string, spec json.RawMessage, tags map[string]string,
+ meshName, name string, spec jsontext.Value, tags map[string]string,
) (*VirtualNode, error) {
if err := b.schemas.validate("virtualNodeSpec", spec); err != nil {
return nil, err
@@ -58,7 +60,7 @@ func (b *InMemoryBackend) DescribeVirtualNode(meshName, name string) (*VirtualNo
return vn, nil
}
-func (b *InMemoryBackend) UpdateVirtualNode(meshName, name string, spec json.RawMessage) (*VirtualNode, error) {
+func (b *InMemoryBackend) UpdateVirtualNode(meshName, name string, spec jsontext.Value) (*VirtualNode, error) {
if err := b.schemas.validate("virtualNodeSpec", spec); err != nil {
return nil, err
}
@@ -87,7 +89,7 @@ func (b *InMemoryBackend) virtualNodeIsServiceProvider(meshName, name string) bo
var body struct {
Provider *virtualServiceProviderBody `json:"provider"`
}
- if err := json.Unmarshal(vs.Spec, &body); err != nil {
+ if err := json.Unmarshal(vs.Spec, &body, jsonx.V1()); err != nil {
continue
}
if body.Provider != nil && body.Provider.VirtualNode != nil &&
diff --git a/services/appmesh/virtual_routers.go b/services/appmesh/virtual_routers.go
index 3ecf3e9e59..fc0364f56a 100644
--- a/services/appmesh/virtual_routers.go
+++ b/services/appmesh/virtual_routers.go
@@ -1,7 +1,7 @@
package appmesh
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"sort"
"time"
@@ -24,7 +24,7 @@ func (b *InMemoryBackend) routeARN(meshName, vrName, routeName string) string {
//nolint:dupl // create pattern (incl. spec validation call) is structurally identical across resource types
func (b *InMemoryBackend) CreateVirtualRouter(
- meshName, name string, spec json.RawMessage, tags map[string]string,
+ meshName, name string, spec jsontext.Value, tags map[string]string,
) (*VirtualRouter, error) {
b.mu.Lock("CreateVirtualRouter")
defer b.mu.Unlock()
@@ -68,7 +68,7 @@ func (b *InMemoryBackend) DescribeVirtualRouter(meshName, name string) (*Virtual
return vr, nil
}
-func (b *InMemoryBackend) UpdateVirtualRouter(meshName, name string, spec json.RawMessage) (*VirtualRouter, error) {
+func (b *InMemoryBackend) UpdateVirtualRouter(meshName, name string, spec jsontext.Value) (*VirtualRouter, error) {
b.mu.Lock("UpdateVirtualRouter")
defer b.mu.Unlock()
if err := validateVirtualRouterSpec(spec); err != nil {
@@ -146,7 +146,7 @@ func (b *InMemoryBackend) ListVirtualRouters(
// ─── Route ───
func (b *InMemoryBackend) CreateRoute(
- meshName, virtualRouterName, routeName string, spec json.RawMessage, tags map[string]string,
+ meshName, virtualRouterName, routeName string, spec jsontext.Value, tags map[string]string,
) (*Route, error) {
if err := b.schemas.validate("routeSpec", spec); err != nil {
return nil, err
@@ -199,7 +199,7 @@ func (b *InMemoryBackend) DescribeRoute(meshName, virtualRouterName, routeName s
func (b *InMemoryBackend) UpdateRoute(
meshName, virtualRouterName, routeName string,
- spec json.RawMessage,
+ spec jsontext.Value,
) (*Route, error) {
if err := b.schemas.validate("routeSpec", spec); err != nil {
return nil, err
diff --git a/services/appmesh/virtual_routers_test.go b/services/appmesh/virtual_routers_test.go
index 315941b6ff..2f14f1e1c8 100644
--- a/services/appmesh/virtual_routers_test.go
+++ b/services/appmesh/virtual_routers_test.go
@@ -1,7 +1,7 @@
package appmesh_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"net/http"
"testing"
@@ -291,7 +291,7 @@ func TestBackend_UpdateVirtualRouter(t *testing.T) {
t.Parallel()
b := appmesh.NewInMemoryBackend("000000000000", "us-east-1")
tt.setup(b)
- vr, err := b.UpdateVirtualRouter(tt.meshName, tt.vrName, json.RawMessage(`{}`))
+ vr, err := b.UpdateVirtualRouter(tt.meshName, tt.vrName, jsontext.Value(`{}`))
if tt.wantErr != nil {
require.Error(t, err)
assert.True(t, appmesh.ErrIs(err, tt.wantErr))
@@ -360,7 +360,7 @@ func TestBackend_UpdateRoute(t *testing.T) {
t.Parallel()
b := appmesh.NewInMemoryBackend("000000000000", "us-east-1")
tt.setup(b)
- r, err := b.UpdateRoute(tt.meshName, tt.vrName, tt.routeName, json.RawMessage(`{}`))
+ r, err := b.UpdateRoute(tt.meshName, tt.vrName, tt.routeName, jsontext.Value(`{}`))
if tt.wantErr != nil {
require.Error(t, err)
assert.True(t, appmesh.ErrIs(err, tt.wantErr))
diff --git a/services/appmesh/virtual_services.go b/services/appmesh/virtual_services.go
index 09fcdb6464..df8329e0cf 100644
--- a/services/appmesh/virtual_services.go
+++ b/services/appmesh/virtual_services.go
@@ -1,7 +1,7 @@
package appmesh
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"sort"
"time"
@@ -15,7 +15,7 @@ func (b *InMemoryBackend) virtualServiceARN(meshName, name string) string {
//nolint:dupl // create pattern (incl. spec validation call) is structurally identical across resource types
func (b *InMemoryBackend) CreateVirtualService(
- meshName, name string, spec json.RawMessage, tags map[string]string,
+ meshName, name string, spec jsontext.Value, tags map[string]string,
) (*VirtualService, error) {
b.mu.Lock("CreateVirtualService")
defer b.mu.Unlock()
@@ -59,7 +59,7 @@ func (b *InMemoryBackend) DescribeVirtualService(meshName, name string) (*Virtua
return vs, nil
}
-func (b *InMemoryBackend) UpdateVirtualService(meshName, name string, spec json.RawMessage) (*VirtualService, error) {
+func (b *InMemoryBackend) UpdateVirtualService(meshName, name string, spec jsontext.Value) (*VirtualService, error) {
b.mu.Lock("UpdateVirtualService")
defer b.mu.Unlock()
if err := validateVirtualServiceSpec(spec); err != nil {
diff --git a/services/appmesh/virtual_services_test.go b/services/appmesh/virtual_services_test.go
index 3e33e7e5a3..0cbce486fd 100644
--- a/services/appmesh/virtual_services_test.go
+++ b/services/appmesh/virtual_services_test.go
@@ -1,7 +1,7 @@
package appmesh_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"net/http"
"testing"
@@ -126,7 +126,7 @@ func TestBackend_UpdateVirtualService(t *testing.T) {
t.Parallel()
b := appmesh.NewInMemoryBackend("000000000000", "us-east-1")
tt.setup(b)
- vs, err := b.UpdateVirtualService(tt.meshName, tt.vsName, json.RawMessage(`{}`))
+ vs, err := b.UpdateVirtualService(tt.meshName, tt.vsName, jsontext.Value(`{}`))
if tt.wantErr != nil {
require.Error(t, err)
assert.True(t, appmesh.ErrIs(err, tt.wantErr))
diff --git a/services/apprunner/handler.go b/services/apprunner/handler.go
index 6a7ef3fc06..a2a4d03c47 100644
--- a/services/apprunner/handler.go
+++ b/services/apprunner/handler.go
@@ -2,7 +2,7 @@ package apprunner
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -192,13 +193,10 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, awserr.ErrNotFound):
return c.JSON(http.StatusBadRequest, map[string]string{
@@ -218,8 +216,8 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
case errors.Is(err, awserr.ErrInvalidParameter),
errors.Is(err, errInvalidRequest),
errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr),
- errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err),
+ jsonx.IsTypeError(err):
return c.JSON(http.StatusBadRequest, map[string]string{
keyType: invalidRequestType,
keyMessage: errMessage(err),
diff --git a/services/apprunner/handler_auto_scaling_configurations_test.go b/services/apprunner/handler_auto_scaling_configurations_test.go
index 354b147eb1..09ed22698a 100644
--- a/services/apprunner/handler_auto_scaling_configurations_test.go
+++ b/services/apprunner/handler_auto_scaling_configurations_test.go
@@ -1,12 +1,14 @@
package apprunner_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestAutoScalingConfigurationCRUD(t *testing.T) { //nolint:paralleltest // existing issue.
@@ -27,7 +29,7 @@ func TestAutoScalingConfigurationCRUD(t *testing.T) { //nolint:paralleltest // e
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
cfg := resp["AutoScalingConfiguration"].(map[string]any)
assert.Contains(t, cfg["AutoScalingConfigurationArn"], "autoscalingconfiguration/my-asg/1/")
assert.Equal(t, "ACTIVE", cfg["Status"])
@@ -67,7 +69,7 @@ func TestAutoScalingConfigurationDescribeDeleteList(t *testing.T) { //nolint:par
})
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
asgArn := createResp["AutoScalingConfiguration"].(map[string]any)["AutoScalingConfigurationArn"].(string)
tests := []struct {
@@ -85,7 +87,7 @@ func TestAutoScalingConfigurationDescribeDeleteList(t *testing.T) { //nolint:par
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
cfg := resp["AutoScalingConfiguration"].(map[string]any)
assert.Equal(t, "cfg1", cfg["AutoScalingConfigurationName"])
assert.InDelta(t, float64(50), cfg["MaxConcurrency"], 0.0001)
@@ -113,7 +115,7 @@ func TestAutoScalingConfigurationDescribeDeleteList(t *testing.T) { //nolint:par
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
list := resp["AutoScalingConfigurationSummaryList"].([]any)
// cfg1 plus the account's always-present DefaultConfiguration
// (see ensureDefaultAutoScalingConfiguration).
@@ -128,7 +130,7 @@ func TestAutoScalingConfigurationDescribeDeleteList(t *testing.T) { //nolint:par
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
cfg := resp["AutoScalingConfiguration"].(map[string]any)
assert.Equal(t, "cfg1", cfg["AutoScalingConfigurationName"])
},
@@ -152,13 +154,13 @@ func TestAutoScalingConfigurationRevisions(t *testing.T) { //nolint:paralleltest
rec := doRequest(t, h, "CreateAutoScalingConfiguration", map[string]any{"AutoScalingConfigurationName": "my-asg"})
require.Equal(t, http.StatusOK, rec.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r1, jsonx.V1()))
asgArn1 := r1["AutoScalingConfiguration"].(map[string]any)["AutoScalingConfigurationArn"].(string)
rec = doRequest(t, h, "CreateAutoScalingConfiguration", map[string]any{"AutoScalingConfigurationName": "my-asg"})
require.Equal(t, http.StatusOK, rec.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r2, jsonx.V1()))
rev2 := r2["AutoScalingConfiguration"].(map[string]any)["AutoScalingConfigurationRevision"].(float64)
assert.InDelta(t, float64(2), rev2, 0.0001)
@@ -169,21 +171,21 @@ func TestAutoScalingConfigurationRevisions(t *testing.T) { //nolint:paralleltest
rec = doRequest(t, h, "ListAutoScalingConfigurations", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
list := listResp["AutoScalingConfigurationSummaryList"].([]any)
// my-asg's latest revision plus DefaultConfiguration.
assert.Len(t, list, 2)
rec = doRequest(t, h, "ListAutoScalingConfigurations", map[string]any{"LatestOnly": true})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
list = listResp["AutoScalingConfigurationSummaryList"].([]any)
// my-asg's latest revision plus DefaultConfiguration.
assert.Len(t, list, 2)
rec = doRequest(t, h, "ListAutoScalingConfigurations", map[string]any{"LatestOnly": false})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
list = listResp["AutoScalingConfigurationSummaryList"].([]any)
// my-asg's 2 revisions plus the account's always-present
// DefaultConfiguration (see ensureDefaultAutoScalingConfiguration).
@@ -194,7 +196,7 @@ func TestAutoScalingConfigurationRevisions(t *testing.T) { //nolint:paralleltest
})
require.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
cfg := updateResp["AutoScalingConfiguration"].(map[string]any)
assert.Equal(t, true, cfg["IsDefault"])
}
@@ -205,7 +207,7 @@ func TestListServicesForAutoScalingConfiguration(t *testing.T) { //nolint:parall
rec := doRequest(t, h, "CreateAutoScalingConfiguration", map[string]any{"AutoScalingConfigurationName": "my-asg"})
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
asgArn := createResp["AutoScalingConfiguration"].(map[string]any)["AutoScalingConfigurationArn"].(string)
tests := []struct {
@@ -221,7 +223,7 @@ func TestListServicesForAutoScalingConfiguration(t *testing.T) { //nolint:parall
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
list := resp["ServiceArnList"].([]any)
assert.Empty(t, list)
},
@@ -264,7 +266,7 @@ func TestDeleteAutoScalingConfiguration_RejectsDefaultAndInUse(t *testing.T) { /
rec := doRequest(t, h, "ListAutoScalingConfigurations", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
list := listResp["AutoScalingConfigurationSummaryList"].([]any)
require.Len(t, list, 1, "only the seeded default configuration should exist yet")
defaultArn := list[0].(map[string]any)["AutoScalingConfigurationArn"].(string)
@@ -281,7 +283,7 @@ func TestDeleteAutoScalingConfiguration_RejectsDefaultAndInUse(t *testing.T) { /
)
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
asgArn := createResp["AutoScalingConfiguration"].(map[string]any)["AutoScalingConfigurationArn"].(string)
rec = doRequest(t, h, "CreateService", map[string]any{
@@ -293,7 +295,7 @@ func TestDeleteAutoScalingConfiguration_RejectsDefaultAndInUse(t *testing.T) { /
})
require.Equal(t, http.StatusOK, rec.Code)
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &svcResp, jsonx.V1()))
svcArn := svcResp["Service"].(map[string]any)["ServiceArn"].(string)
rec = doRequest(t, h, "DeleteAutoScalingConfiguration", map[string]any{"AutoScalingConfigurationArn": asgArn})
diff --git a/services/apprunner/handler_connections_test.go b/services/apprunner/handler_connections_test.go
index 8d19891c2c..b5520453b6 100644
--- a/services/apprunner/handler_connections_test.go
+++ b/services/apprunner/handler_connections_test.go
@@ -1,12 +1,14 @@
package apprunner_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestConnectionCRUD(t *testing.T) { //nolint:paralleltest // existing issue.
@@ -27,7 +29,7 @@ func TestConnectionCRUD(t *testing.T) { //nolint:paralleltest // existing issue.
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
conn := resp["Connection"].(map[string]any)
assert.Contains(t, conn["ConnectionArn"], "connection/my-conn/")
assert.Equal(t, "AVAILABLE", conn["Status"])
@@ -65,7 +67,7 @@ func TestConnectionDeleteList(t *testing.T) { //nolint:paralleltest // existing
rec := doRequest(t, h, "CreateConnection", map[string]any{"ConnectionName": "conn1", "ProviderType": "GITHUB"})
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
connArn := createResp["Connection"].(map[string]any)["ConnectionArn"].(string)
doRequest(t, h, "CreateConnection", map[string]any{"ConnectionName": "conn2", "ProviderType": "BITBUCKET"})
@@ -85,7 +87,7 @@ func TestConnectionDeleteList(t *testing.T) { //nolint:paralleltest // existing
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
list := resp["ConnectionSummaryList"].([]any)
assert.Len(t, list, 2)
},
@@ -98,7 +100,7 @@ func TestConnectionDeleteList(t *testing.T) { //nolint:paralleltest // existing
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
list := resp["ConnectionSummaryList"].([]any)
assert.Len(t, list, 1)
},
@@ -111,7 +113,7 @@ func TestConnectionDeleteList(t *testing.T) { //nolint:paralleltest // existing
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
conn := resp["Connection"].(map[string]any)
assert.Equal(t, "conn1", conn["ConnectionName"])
},
@@ -157,7 +159,7 @@ func TestDeleteConnection_RejectsWhenServiceUsesIt(t *testing.T) {
rec := doRequest(t, h, "CreateConnection", map[string]any{"ConnectionName": "gh-conn", "ProviderType": "GITHUB"})
require.Equal(t, http.StatusOK, rec.Code)
var connResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &connResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &connResp, jsonx.V1()))
connArn := connResp["Connection"].(map[string]any)["ConnectionArn"].(string)
rec = doRequest(t, h, "CreateService", map[string]any{
@@ -172,7 +174,7 @@ func TestDeleteConnection_RejectsWhenServiceUsesIt(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &svcResp, jsonx.V1()))
svcArn := svcResp["Service"].(map[string]any)["ServiceArn"].(string)
rec = doRequest(t, h, "DeleteConnection", map[string]any{"ConnectionArn": connArn})
diff --git a/services/apprunner/handler_custom_domains_test.go b/services/apprunner/handler_custom_domains_test.go
index 454337099e..bb00067f58 100644
--- a/services/apprunner/handler_custom_domains_test.go
+++ b/services/apprunner/handler_custom_domains_test.go
@@ -1,13 +1,15 @@
package apprunner_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCustomDomainAssociateDescribeDisassociate(t *testing.T) { //nolint:paralleltest // existing issue.
@@ -32,7 +34,7 @@ func TestCustomDomainAssociateDescribeDisassociate(t *testing.T) { //nolint:para
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
cd := resp["CustomDomain"].(map[string]any)
assert.Equal(t, "example.com", cd["DomainName"])
assert.Equal(t, "PENDING_CERTIFICATE_DNS_VALIDATION", cd["Status"])
@@ -107,7 +109,7 @@ func TestCustomDomainDescribeAndDisassociate(t *testing.T) { //nolint:parallelte
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
domains := resp["CustomDomains"].([]any)
assert.Len(t, domains, 2)
assert.NotEmpty(t, resp["ServiceArn"])
@@ -134,7 +136,7 @@ func TestCustomDomainDescribeAndDisassociate(t *testing.T) { //nolint:parallelte
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
cd := resp["CustomDomain"].(map[string]any)
assert.Equal(t, "example.com", cd["DomainName"])
},
@@ -180,7 +182,7 @@ func TestAssociateCustomDomainDNSTargetIsServiceURL(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dnsTarget, ok := resp["DNSTarget"].(string)
require.True(t, ok, "AssociateCustomDomain response must include DNSTarget string")
@@ -211,7 +213,7 @@ func TestDisassociateCustomDomainDNSTargetIsServiceURL(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dnsTarget, ok := resp["DNSTarget"].(string)
require.True(t, ok, "DisassociateCustomDomain response must include DNSTarget string")
diff --git a/services/apprunner/handler_observability_configurations_test.go b/services/apprunner/handler_observability_configurations_test.go
index 23efe7d65c..4341ee4a6d 100644
--- a/services/apprunner/handler_observability_configurations_test.go
+++ b/services/apprunner/handler_observability_configurations_test.go
@@ -1,12 +1,14 @@
package apprunner_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestObservabilityConfigurationCRUD(t *testing.T) { //nolint:paralleltest // existing issue.
@@ -30,7 +32,7 @@ func TestObservabilityConfigurationCRUD(t *testing.T) { //nolint:paralleltest //
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
cfg := resp["ObservabilityConfiguration"].(map[string]any)
assert.Contains(t, cfg["ObservabilityConfigurationArn"], "observabilityconfiguration/my-obs/1/")
assert.Equal(t, "ACTIVE", cfg["Status"])
@@ -65,7 +67,7 @@ func TestObservabilityConfigurationDescribeDeleteList(t *testing.T) { //nolint:p
})
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
obsArn := createResp["ObservabilityConfiguration"].(map[string]any)["ObservabilityConfigurationArn"].(string)
tests := []struct {
@@ -83,7 +85,7 @@ func TestObservabilityConfigurationDescribeDeleteList(t *testing.T) { //nolint:p
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
cfg := resp["ObservabilityConfiguration"].(map[string]any)
assert.Equal(t, "obs1", cfg["ObservabilityConfigurationName"])
},
@@ -110,7 +112,7 @@ func TestObservabilityConfigurationDescribeDeleteList(t *testing.T) { //nolint:p
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
list := resp["ObservabilityConfigurationSummaryList"].([]any)
assert.Len(t, list, 1)
},
@@ -123,7 +125,7 @@ func TestObservabilityConfigurationDescribeDeleteList(t *testing.T) { //nolint:p
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
cfg := resp["ObservabilityConfiguration"].(map[string]any)
assert.Equal(t, "obs1", cfg["ObservabilityConfigurationName"])
},
@@ -156,7 +158,7 @@ func TestDeleteObservabilityConfiguration_RejectsWhenServiceUsesIt(t *testing.T)
})
require.Equal(t, http.StatusOK, rec.Code)
var obsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &obsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &obsResp, jsonx.V1()))
obsArn := obsResp["ObservabilityConfiguration"].(map[string]any)["ObservabilityConfigurationArn"].(string)
rec = doRequest(t, h, "CreateService", map[string]any{
@@ -171,7 +173,7 @@ func TestDeleteObservabilityConfiguration_RejectsWhenServiceUsesIt(t *testing.T)
})
require.Equal(t, http.StatusOK, rec.Code)
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &svcResp, jsonx.V1()))
svcArn := svcResp["Service"].(map[string]any)["ServiceArn"].(string)
rec = doRequest(t, h, "DeleteObservabilityConfiguration", map[string]any{"ObservabilityConfigurationArn": obsArn})
@@ -201,26 +203,26 @@ func TestObservabilityConfigurationRevisionsLatestOnlyDefault(t *testing.T) { //
)
require.Equal(t, http.StatusOK, rec.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r2, jsonx.V1()))
rev2 := r2["ObservabilityConfiguration"].(map[string]any)["ObservabilityConfigurationRevision"].(float64)
assert.InDelta(t, float64(2), rev2, 0.0001)
rec = doRequest(t, h, "ListObservabilityConfigurations", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
list := listResp["ObservabilityConfigurationSummaryList"].([]any)
assert.Len(t, list, 1)
rec = doRequest(t, h, "ListObservabilityConfigurations", map[string]any{"LatestOnly": true})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
list = listResp["ObservabilityConfigurationSummaryList"].([]any)
assert.Len(t, list, 1)
rec = doRequest(t, h, "ListObservabilityConfigurations", map[string]any{"LatestOnly": false})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
list = listResp["ObservabilityConfigurationSummaryList"].([]any)
assert.Len(t, list, 2)
}
diff --git a/services/apprunner/handler_operations_test.go b/services/apprunner/handler_operations_test.go
index 423c035fff..360b9a45ac 100644
--- a/services/apprunner/handler_operations_test.go
+++ b/services/apprunner/handler_operations_test.go
@@ -1,12 +1,14 @@
package apprunner_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestListOperations(t *testing.T) {
@@ -22,7 +24,7 @@ func TestListOperations(t *testing.T) {
rec := doRequest(t, h, "ListOperations", map[string]any{"ServiceArn": svcArn})
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ops, ok := resp["OperationSummaryList"].([]any)
require.True(t, ok)
// CREATE + PAUSE + RESUME = 3 operations
diff --git a/services/apprunner/handler_services_test.go b/services/apprunner/handler_services_test.go
index 92eb4e3bca..cda5d87b7f 100644
--- a/services/apprunner/handler_services_test.go
+++ b/services/apprunner/handler_services_test.go
@@ -1,13 +1,14 @@
package apprunner_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apprunner"
)
@@ -35,7 +36,7 @@ func TestCreateService_ReturnsServiceArn(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
svc, ok := resp["Service"].(map[string]any)
require.True(t, ok)
assert.Contains(t, svc["ServiceArn"], "arn:aws:apprunner:us-east-1:000000000000:service/")
@@ -77,7 +78,7 @@ func TestServiceCRUD(t *testing.T) {
rec := doRequest(t, h, "DescribeService", map[string]any{"ServiceArn": svcArn})
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
svc := descResp["Service"].(map[string]any)
assert.Equal(t, "test-service", svc["ServiceName"])
assert.Equal(t, "RUNNING", svc["Status"])
@@ -86,7 +87,7 @@ func TestServiceCRUD(t *testing.T) {
rec = doRequest(t, h, "ListServices", map[string]any{})
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["ServiceSummaryList"], 1)
// Delete
@@ -109,7 +110,7 @@ func TestPauseResume(t *testing.T) {
rec := doRequest(t, h, "PauseService", map[string]any{"ServiceArn": svcArn})
assert.Equal(t, http.StatusOK, rec.Code)
var pauseResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pauseResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pauseResp, jsonx.V1()))
svc := pauseResp["Service"].(map[string]any)
assert.Equal(t, "PAUSED", svc["Status"])
@@ -117,7 +118,7 @@ func TestPauseResume(t *testing.T) {
rec = doRequest(t, h, "ResumeService", map[string]any{"ServiceArn": svcArn})
assert.Equal(t, http.StatusOK, rec.Code)
var resumeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resumeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resumeResp, jsonx.V1()))
svc = resumeResp["Service"].(map[string]any)
assert.Equal(t, "RUNNING", svc["Status"])
}
@@ -131,7 +132,7 @@ func TestStartDeployment(t *testing.T) {
rec := doRequest(t, h, "StartDeployment", map[string]any{"ServiceArn": svcArn})
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["OperationId"])
}
@@ -150,7 +151,7 @@ func TestUpdateService(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["OperationId"])
}
@@ -161,7 +162,7 @@ func TestListServices_Empty(t *testing.T) {
rec := doRequest(t, h, "ListServices", map[string]any{})
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
services, ok := resp["ServiceSummaryList"].([]any)
require.True(t, ok)
assert.Empty(t, services)
@@ -201,7 +202,7 @@ func TestPauseService_StateGuard(t *testing.T) { //nolint:paralleltest // existi
assert.Equal(t, tc.wantCode, rec.Code)
if tc.wantType != "" {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, tc.wantType, body["__type"])
}
})
@@ -242,7 +243,7 @@ func TestResumeService_StateGuard(t *testing.T) { //nolint:paralleltest // exist
assert.Equal(t, tc.wantCode, rec.Code)
if tc.wantType != "" {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, tc.wantType, body["__type"])
}
})
@@ -286,7 +287,7 @@ func TestUpdateService_StateGuard(t *testing.T) { //nolint:paralleltest // exist
assert.Equal(t, tc.wantCode, rec.Code)
if tc.wantType != "" {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, tc.wantType, body["__type"])
}
})
@@ -333,7 +334,7 @@ func TestStartDeployment_StateGuard(t *testing.T) { //nolint:paralleltest // exi
assert.Equal(t, tc.wantCode, rec.Code)
if tc.wantType != "" {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, tc.wantType, body["__type"])
}
})
@@ -363,14 +364,14 @@ func TestServiceOutputInstanceConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
svcArn := createResp["Service"].(map[string]any)["ServiceArn"].(string)
rec = doRequest(t, h, "DescribeService", map[string]any{"ServiceArn": svcArn})
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
svc := descResp["Service"].(map[string]any)
ic, ok := svc["InstanceConfiguration"].(map[string]any)
@@ -398,14 +399,14 @@ func TestServiceOutputSourceConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
svcArn := createResp["Service"].(map[string]any)["ServiceArn"].(string)
rec = doRequest(t, h, "DescribeService", map[string]any{"ServiceArn": svcArn})
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
svc := descResp["Service"].(map[string]any)
sc, ok := svc["SourceConfiguration"].(map[string]any)
@@ -436,14 +437,14 @@ func TestDefaultInstanceConfigurationPresent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
svcArn := createResp["Service"].(map[string]any)["ServiceArn"].(string)
rec = doRequest(t, h, "DescribeService", map[string]any{"ServiceArn": svcArn})
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
svc := descResp["Service"].(map[string]any)
ic, ok := svc["InstanceConfiguration"].(map[string]any)
@@ -474,7 +475,7 @@ func TestCreateService_InstanceRoleArn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ic := resp["Service"].(map[string]any)["InstanceConfiguration"].(map[string]any)
assert.Equal(t, "arn:aws:iam::000000000000:role/my-role", ic["InstanceRoleArn"])
}
@@ -499,7 +500,7 @@ func TestCreateService_EncryptionConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
enc, ok := resp["Service"].(map[string]any)["EncryptionConfiguration"].(map[string]any)
require.True(t, ok, "EncryptionConfiguration must be present when a KmsKey was provided")
assert.Equal(t, "arn:aws:kms:us-east-1:000000000000:key/abc", enc["KmsKey"])
@@ -515,7 +516,7 @@ func TestCreateService_EncryptionConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, present := resp["Service"].(map[string]any)["EncryptionConfiguration"]
assert.False(t, present, "EncryptionConfiguration must be absent when no KmsKey was provided")
})
@@ -537,7 +538,7 @@ func TestCreateService_AutoScalingConfigurationAssociation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var asgResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &asgResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &asgResp, jsonx.V1()))
asgArn := asgResp["AutoScalingConfiguration"].(map[string]any)["AutoScalingConfigurationArn"].(string)
rec = doRequest(t, h, "CreateService", map[string]any{
@@ -550,7 +551,7 @@ func TestCreateService_AutoScalingConfigurationAssociation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
svc := createResp["Service"].(map[string]any)
svcArn := svc["ServiceArn"].(string)
summary := svc["AutoScalingConfigurationSummary"].(map[string]any)
@@ -563,7 +564,7 @@ func TestCreateService_AutoScalingConfigurationAssociation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
arns := listResp["ServiceArnList"].([]any)
require.Len(t, arns, 1)
assert.Equal(t, svcArn, arns[0])
@@ -575,7 +576,7 @@ func TestCreateService_AutoScalingConfigurationAssociation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
cfg := descResp["AutoScalingConfiguration"].(map[string]any)
assert.Equal(t, false, cfg["HasAssociatedService"])
@@ -583,7 +584,7 @@ func TestCreateService_AutoScalingConfigurationAssociation(t *testing.T) {
"AutoScalingConfigurationArn": asgArn,
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Empty(t, listResp["ServiceArnList"])
}
@@ -600,7 +601,7 @@ func TestCreateService_DefaultAutoScalingConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summary := resp["Service"].(map[string]any)["AutoScalingConfigurationSummary"].(map[string]any)
assert.Equal(t, "DefaultConfiguration", summary["AutoScalingConfigurationName"])
assert.Equal(t, true, summary["IsDefault"])
@@ -625,7 +626,7 @@ func TestCreateService_UnknownAutoScalingConfigurationArn(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", body["__type"])
}
@@ -685,7 +686,7 @@ func TestCreateService_NetworkConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
nc := resp["Service"].(map[string]any)["NetworkConfiguration"].(map[string]any)
assert.Equal(t, "IPV4", nc["IpAddressType"])
assert.Equal(t, "DEFAULT", nc["EgressConfiguration"].(map[string]any)["EgressType"])
@@ -704,7 +705,7 @@ func TestCreateService_NetworkConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var vcResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &vcResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &vcResp, jsonx.V1()))
vcArn := vcResp["VpcConnector"].(map[string]any)["VpcConnectorArn"].(string)
rec = doRequest(t, h, "CreateService", map[string]any{
@@ -720,7 +721,7 @@ func TestCreateService_NetworkConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
nc := createResp["Service"].(map[string]any)["NetworkConfiguration"].(map[string]any)
assert.Equal(t, "VPC", nc["EgressConfiguration"].(map[string]any)["EgressType"])
assert.Equal(t, vcArn, nc["EgressConfiguration"].(map[string]any)["VpcConnectorArn"])
@@ -764,7 +765,7 @@ func TestCreateService_HealthCheckConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
hc := resp["Service"].(map[string]any)["HealthCheckConfiguration"].(map[string]any)
assert.Equal(t, "TCP", hc["Protocol"])
assert.Equal(t, "/", hc["Path"])
@@ -796,7 +797,7 @@ func TestCreateService_HealthCheckConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
hc := createResp["Service"].(map[string]any)["HealthCheckConfiguration"].(map[string]any)
assert.Equal(t, "HTTP", hc["Protocol"])
assert.Equal(t, "/healthz", hc["Path"])
@@ -822,7 +823,7 @@ func TestCreateService_ObservabilityConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var obsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &obsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &obsResp, jsonx.V1()))
obsArn := obsResp["ObservabilityConfiguration"].(map[string]any)["ObservabilityConfigurationArn"].(string)
rec = doRequest(t, h, "CreateService", map[string]any{
@@ -838,7 +839,7 @@ func TestCreateService_ObservabilityConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
oc := createResp["Service"].(map[string]any)["ObservabilityConfiguration"].(map[string]any)
assert.Equal(t, true, oc["ObservabilityEnabled"])
assert.Equal(t, obsArn, oc["ObservabilityConfigurationArn"])
@@ -884,7 +885,7 @@ func TestCreateService_CodeRepository(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var connResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &connResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &connResp, jsonx.V1()))
connArn := connResp["Connection"].(map[string]any)["ConnectionArn"].(string)
rec = doRequest(t, h, "CreateService", map[string]any{
@@ -908,7 +909,7 @@ func TestCreateService_CodeRepository(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
sc := createResp["Service"].(map[string]any)["SourceConfiguration"].(map[string]any)
cr := sc["CodeRepository"].(map[string]any)
assert.Equal(t, "https://github.com/example/repo", cr["RepositoryUrl"])
@@ -962,7 +963,7 @@ func TestUpdateService_CannotSwitchSourceType(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", body["__type"])
}
@@ -978,7 +979,7 @@ func TestListOperations_UpdatedAtPresent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ops := resp["OperationSummaryList"].([]any)
require.NotEmpty(t, ops)
@@ -1008,7 +1009,7 @@ func TestDeleteService_RejectsWhenActiveVpcIngressConnectionExists(t *testing.T)
})
require.Equal(t, http.StatusOK, rec.Code)
var vicResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &vicResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &vicResp, jsonx.V1()))
vicArn := vicResp["VpcIngressConnection"].(map[string]any)["VpcIngressConnectionArn"].(string)
rec = doRequest(t, h, "DeleteService", map[string]any{"ServiceArn": svcArn})
@@ -1017,7 +1018,7 @@ func TestDeleteService_RejectsWhenActiveVpcIngressConnectionExists(t *testing.T)
)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidStateException", body["__type"])
rec = doRequest(t, h, "DeleteVpcIngressConnection", map[string]any{"VpcIngressConnectionArn": vicArn})
diff --git a/services/apprunner/handler_tags_test.go b/services/apprunner/handler_tags_test.go
index 34ebfbdce8..05f82d5259 100644
--- a/services/apprunner/handler_tags_test.go
+++ b/services/apprunner/handler_tags_test.go
@@ -1,12 +1,14 @@
package apprunner_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestTags(t *testing.T) {
@@ -29,7 +31,7 @@ func TestTags(t *testing.T) {
rec = doRequest(t, h, "ListTagsForResource", map[string]any{"ResourceArn": svcArn})
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags, ok := listResp["Tags"].([]any)
require.True(t, ok)
assert.Len(t, tags, 2)
@@ -43,7 +45,7 @@ func TestTags(t *testing.T) {
// Verify untag
rec = doRequest(t, h, "ListTagsForResource", map[string]any{"ResourceArn": svcArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags = listResp["Tags"].([]any)
assert.Len(t, tags, 1)
}
diff --git a/services/apprunner/handler_test.go b/services/apprunner/handler_test.go
index 94e6e97b69..d1b1c2b789 100644
--- a/services/apprunner/handler_test.go
+++ b/services/apprunner/handler_test.go
@@ -2,7 +2,7 @@ package apprunner_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apprunner"
)
@@ -29,7 +30,7 @@ func doRequest(t *testing.T, h *apprunner.Handler, action string, body any) *htt
if body != nil {
var marshalErr error
- bodyBytes, marshalErr = json.Marshal(body)
+ bodyBytes, marshalErr = json.Marshal(body, jsonx.V1())
require.NoError(t, marshalErr)
}
@@ -60,7 +61,7 @@ func createTestService(t *testing.T, h *apprunner.Handler) string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
svc, ok := resp["Service"].(map[string]any)
require.True(t, ok)
diff --git a/services/apprunner/handler_vpc_connectors_test.go b/services/apprunner/handler_vpc_connectors_test.go
index cb57c7adba..a69461850a 100644
--- a/services/apprunner/handler_vpc_connectors_test.go
+++ b/services/apprunner/handler_vpc_connectors_test.go
@@ -1,12 +1,14 @@
package apprunner_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestVpcConnectorCRUD(t *testing.T) { //nolint:paralleltest // existing issue.
@@ -31,7 +33,7 @@ func TestVpcConnectorCRUD(t *testing.T) { //nolint:paralleltest // existing issu
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
vc := resp["VpcConnector"].(map[string]any)
assert.Contains(t, vc["VpcConnectorArn"], "vpcconnector/my-vpc/1/")
assert.Equal(t, "ACTIVE", vc["Status"])
@@ -74,7 +76,7 @@ func TestVpcConnectorDescribeDeleteList(t *testing.T) { //nolint:paralleltest //
})
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
vcArn := createResp["VpcConnector"].(map[string]any)["VpcConnectorArn"].(string)
tests := []struct {
@@ -92,7 +94,7 @@ func TestVpcConnectorDescribeDeleteList(t *testing.T) { //nolint:paralleltest //
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
vc := resp["VpcConnector"].(map[string]any)
assert.Equal(t, "vc1", vc["VpcConnectorName"])
},
@@ -119,7 +121,7 @@ func TestVpcConnectorDescribeDeleteList(t *testing.T) { //nolint:paralleltest //
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
list := resp["VpcConnectors"].([]any)
assert.Len(t, list, 1)
},
@@ -132,7 +134,7 @@ func TestVpcConnectorDescribeDeleteList(t *testing.T) { //nolint:paralleltest //
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
vc := resp["VpcConnector"].(map[string]any)
assert.Equal(t, "vc1", vc["VpcConnectorName"])
},
@@ -180,7 +182,7 @@ func TestDeleteVpcConnector_RejectsWhenServiceUsesIt(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var vcResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &vcResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &vcResp, jsonx.V1()))
vcArn := vcResp["VpcConnector"].(map[string]any)["VpcConnectorArn"].(string)
rec = doRequest(t, h, "CreateService", map[string]any{
@@ -194,7 +196,7 @@ func TestDeleteVpcConnector_RejectsWhenServiceUsesIt(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &svcResp, jsonx.V1()))
svcArn := svcResp["Service"].(map[string]any)["ServiceArn"].(string)
rec = doRequest(t, h, "DeleteVpcConnector", map[string]any{"VpcConnectorArn": vcArn})
diff --git a/services/apprunner/handler_vpc_ingress_connections_test.go b/services/apprunner/handler_vpc_ingress_connections_test.go
index f6d1a80aa2..3dd396a596 100644
--- a/services/apprunner/handler_vpc_ingress_connections_test.go
+++ b/services/apprunner/handler_vpc_ingress_connections_test.go
@@ -1,12 +1,14 @@
package apprunner_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestVpcIngressConnectionCRUD(t *testing.T) { //nolint:paralleltest // existing issue.
@@ -35,7 +37,7 @@ func TestVpcIngressConnectionCRUD(t *testing.T) { //nolint:paralleltest // exist
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
vic := resp["VpcIngressConnection"].(map[string]any)
assert.Contains(t, vic["VpcIngressConnectionArn"], "vpcingressconnection/my-vic/")
assert.Equal(t, "AVAILABLE", vic["Status"])
@@ -80,7 +82,7 @@ func TestVpcIngressConnectionDescribeDeleteListUpdate(t *testing.T) { //nolint:p
})
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
vicArn := createResp["VpcIngressConnection"].(map[string]any)["VpcIngressConnectionArn"].(string)
tests := []struct {
@@ -98,7 +100,7 @@ func TestVpcIngressConnectionDescribeDeleteListUpdate(t *testing.T) { //nolint:p
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
vic := resp["VpcIngressConnection"].(map[string]any)
assert.Equal(t, "vic1", vic["VpcIngressConnectionName"])
},
@@ -125,7 +127,7 @@ func TestVpcIngressConnectionDescribeDeleteListUpdate(t *testing.T) { //nolint:p
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
list := resp["VpcIngressConnectionSummaryList"].([]any)
assert.Len(t, list, 1)
},
@@ -138,7 +140,7 @@ func TestVpcIngressConnectionDescribeDeleteListUpdate(t *testing.T) { //nolint:p
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
list := resp["VpcIngressConnectionSummaryList"].([]any)
assert.Len(t, list, 1)
},
@@ -155,7 +157,7 @@ func TestVpcIngressConnectionDescribeDeleteListUpdate(t *testing.T) { //nolint:p
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
list := resp["VpcIngressConnectionSummaryList"].([]any)
assert.Len(t, list, 1)
},
@@ -168,7 +170,7 @@ func TestVpcIngressConnectionDescribeDeleteListUpdate(t *testing.T) { //nolint:p
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
list := resp["VpcIngressConnectionSummaryList"].([]any)
assert.Empty(t, list)
},
@@ -187,7 +189,7 @@ func TestVpcIngressConnectionDescribeDeleteListUpdate(t *testing.T) { //nolint:p
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
vic := resp["VpcIngressConnection"].(map[string]any)
ivc := vic["IngressVpcConfiguration"].(map[string]any)
assert.Equal(t, "vpc-new", ivc["VpcId"])
@@ -207,7 +209,7 @@ func TestVpcIngressConnectionDescribeDeleteListUpdate(t *testing.T) { //nolint:p
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
vic := resp["VpcIngressConnection"].(map[string]any)
assert.Equal(t, "vic1", vic["VpcIngressConnectionName"])
},
diff --git a/services/apprunner/multi_region_test.go b/services/apprunner/multi_region_test.go
index eab901d70a..fd82d8d173 100644
--- a/services/apprunner/multi_region_test.go
+++ b/services/apprunner/multi_region_test.go
@@ -2,7 +2,7 @@ package apprunner_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apprunner"
)
@@ -41,7 +42,7 @@ func decode(t *testing.T, body string) map[string]any {
out := map[string]any{}
if body != "" {
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
}
return out
diff --git a/services/apprunner/observability_configuration_summary_wire_test.go b/services/apprunner/observability_configuration_summary_wire_test.go
index 438c70b171..3676d98a5e 100644
--- a/services/apprunner/observability_configuration_summary_wire_test.go
+++ b/services/apprunner/observability_configuration_summary_wire_test.go
@@ -1,7 +1,7 @@
package apprunner_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apprunner"
)
@@ -36,7 +37,7 @@ func TestListObservabilityConfigurations_SummaryHasNoFabricatedFields(t *testing
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp["ObservabilityConfigurationSummaryList"].([]any)
require.True(t, ok)
diff --git a/services/apprunner/persistence.go b/services/apprunner/persistence.go
index 610a54ea40..cac2ba732e 100644
--- a/services/apprunner/persistence.go
+++ b/services/apprunner/persistence.go
@@ -2,7 +2,7 @@ package apprunner
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -37,7 +37,7 @@ const apprunnerSnapshotVersion = 1
// snapshot from an incompatible (older or newer) build of this backend as
// though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
CustomDomains map[string][]*storedCustomDomain `json:"customDomains"`
Tags map[string]map[string]string `json:"tags"`
Version int `json:"version"`
diff --git a/services/apprunner/vpc_ingress_service_arn_test.go b/services/apprunner/vpc_ingress_service_arn_test.go
index 15ea8eb15e..d806d16a06 100644
--- a/services/apprunner/vpc_ingress_service_arn_test.go
+++ b/services/apprunner/vpc_ingress_service_arn_test.go
@@ -1,12 +1,14 @@
package apprunner_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCreateVpcIngressConnection_ServiceMustExist(t *testing.T) {
@@ -42,7 +44,7 @@ func TestCreateVpcIngressConnection_ServiceMustExist(t *testing.T) {
if tt.wantType != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["__type"], tt.wantType)
}
})
diff --git a/services/apprunner/wire_output_required_r80d_test.go b/services/apprunner/wire_output_required_r80d_test.go
index f19589e0d3..a612ab6096 100644
--- a/services/apprunner/wire_output_required_r80d_test.go
+++ b/services/apprunner/wire_output_required_r80d_test.go
@@ -1,7 +1,7 @@
package apprunner_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/apprunner"
)
@@ -77,7 +78,7 @@ func Test_CodeRepository_SourceCodeVersion_Required(t *testing.T) {
ServiceArn string `json:"ServiceArn"`
} `json:"Service"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
rec = doRequest(t, h, "DescribeService", map[string]any{
"ServiceArn": createOut.Service.ServiceArn,
@@ -95,7 +96,7 @@ func Test_CodeRepository_SourceCodeVersion_Required(t *testing.T) {
} `json:"SourceConfiguration"`
} `json:"Service"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut, jsonx.V1()))
require.NotNil(t, descOut.Service.SourceConfiguration.CodeRepository.SourceCodeVersion)
assert.Equal(t, "main", descOut.Service.SourceConfiguration.CodeRepository.SourceCodeVersion.Value)
}
diff --git a/services/appstream/app_blocks_test.go b/services/appstream/app_blocks_test.go
index f28280c40c..75956703ec 100644
--- a/services/appstream/app_blocks_test.go
+++ b/services/appstream/app_blocks_test.go
@@ -1,13 +1,14 @@
package appstream_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appstream"
)
@@ -37,7 +38,7 @@ func TestAppStream_AppBlocks(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
ab := resp["AppBlock"].(map[string]any)
assert.Equal(t, "my-block", ab["Name"])
assert.Contains(t, ab["Arn"], ":app-block/my-block")
@@ -73,7 +74,7 @@ func TestAppStream_AppBlocks(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
blocks := resp["AppBlocks"].([]any)
assert.Len(t, blocks, 2)
},
@@ -136,7 +137,7 @@ func TestAppStream_AppBlockBuilders(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
bb := resp["AppBlockBuilder"].(map[string]any)
assert.Equal(t, "my-builder", bb["Name"])
assert.Equal(t, "STOPPED", bb["State"])
@@ -162,7 +163,7 @@ func TestAppStream_AppBlockBuilders(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
bb := resp["AppBlockBuilder"].(map[string]any)
assert.Equal(t, "RUNNING", bb["State"])
},
@@ -180,7 +181,7 @@ func TestAppStream_AppBlockBuilders(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
bb := resp["AppBlockBuilder"].(map[string]any)
assert.Equal(t, "STOPPED", bb["State"])
},
@@ -196,7 +197,7 @@ func TestAppStream_AppBlockBuilders(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
bb := resp["AppBlockBuilder"].(map[string]any)
assert.Equal(t, "STOPPED", bb["State"])
},
@@ -212,7 +213,7 @@ func TestAppStream_AppBlockBuilders(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
bb := resp["AppBlockBuilder"].(map[string]any)
assert.Equal(t, "updated", bb["Description"])
},
@@ -228,7 +229,7 @@ func TestAppStream_AppBlockBuilders(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["StreamingURL"])
assert.NotEmpty(t, resp["Expires"])
},
@@ -263,7 +264,7 @@ func TestAppStream_AppBlockBuilders(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
assocs := resp["AppBlockBuilderAppBlockAssociations"].([]any)
assert.Len(t, assocs, 1)
},
@@ -307,7 +308,7 @@ func TestAppStream_AppBlockARNFormat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ab := resp["AppBlock"].(map[string]any)
assert.Contains(t, ab["Arn"], "arn:aws:appstream:")
assert.Contains(t, ab["Arn"], "app-block/arn-appblock")
@@ -330,7 +331,7 @@ func TestAppStream_AppBlockBuilderStreamingURL(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["StreamingURL"])
assert.NotEmpty(t, resp["Expires"])
}
@@ -355,7 +356,7 @@ func TestAppStream_DeleteAppBlock_InUseByApplication(t *testing.T) {
require.Equal(t, http.StatusOK, abRec.Code)
var abResp map[string]any
- require.NoError(t, json.Unmarshal(abRec.Body.Bytes(), &abResp))
+ require.NoError(t, json.Unmarshal(abRec.Body.Bytes(), &abResp, jsonx.V1()))
ab := abResp["AppBlock"].(map[string]any)
appRec := doRequest(t, h, "CreateApplication", map[string]any{
diff --git a/services/appstream/applications_test.go b/services/appstream/applications_test.go
index 09f9a394e4..42b5d65afa 100644
--- a/services/appstream/applications_test.go
+++ b/services/appstream/applications_test.go
@@ -1,13 +1,14 @@
package appstream_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appstream"
)
@@ -40,7 +41,7 @@ func TestAppStream_Applications(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
app := resp["Application"].(map[string]any)
assert.Equal(t, "my-app", app["Name"])
assert.Contains(t, app["Arn"], ":application/my-app")
@@ -75,7 +76,7 @@ func TestAppStream_Applications(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
apps := resp["Applications"].([]any)
assert.Len(t, apps, 2)
},
@@ -91,7 +92,7 @@ func TestAppStream_Applications(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
app := resp["Application"].(map[string]any)
assert.Equal(t, "Updated App", app["DisplayName"])
},
@@ -113,7 +114,7 @@ func TestAppStream_Applications(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
usages, ok := resp["AppLicenseUsages"]
require.True(t, ok, "response must carry AppLicenseUsages")
assert.Empty(t, usages)
@@ -167,7 +168,7 @@ func TestAppStream_Applications(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
assocs := resp["ApplicationFleetAssociations"].([]any)
assert.Len(t, assocs, 1)
},
@@ -262,7 +263,7 @@ func TestAppStream_CreateApplication_RequiredMembersRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
app := createOut["Application"].(map[string]any)
icon, ok := app["IconS3Location"].(map[string]any)
@@ -275,7 +276,7 @@ func TestAppStream_CreateApplication_RequiredMembersRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
apps := descOut["Applications"].([]any)
require.Len(t, apps, 1)
got := apps[0].(map[string]any)
@@ -304,7 +305,7 @@ func TestAppStream_ApplicationARNFormat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
app := resp["Application"].(map[string]any)
assert.Contains(t, app["Arn"], "arn:aws:appstream:")
assert.Contains(t, app["Arn"], "application/arn-app")
diff --git a/services/appstream/directory_configs_test.go b/services/appstream/directory_configs_test.go
index 7d27954a04..81ae0fd6b7 100644
--- a/services/appstream/directory_configs_test.go
+++ b/services/appstream/directory_configs_test.go
@@ -1,13 +1,14 @@
package appstream_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appstream"
)
@@ -34,7 +35,7 @@ func TestAppStream_DirectoryConfigs(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
dc := resp["DirectoryConfig"].(map[string]any)
assert.Equal(t, "corp.example.com", dc["DirectoryName"])
},
@@ -51,7 +52,7 @@ func TestAppStream_DirectoryConfigs(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
dcs := resp["DirectoryConfigs"].([]any)
assert.Len(t, dcs, 1)
},
@@ -122,7 +123,7 @@ func TestAppStream_DirectoryConfigRoundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, recDesc.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &resp, jsonx.V1()))
configs := resp["DirectoryConfigs"].([]any)
require.Len(t, configs, 1)
dc := configs[0].(map[string]any)
@@ -154,7 +155,7 @@ func TestAppStream_DirectoryConfigServiceAccountCredentials(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
dc := createResp["DirectoryConfig"].(map[string]any)
cred := dc["ServiceAccountCredentials"].(map[string]any)
@@ -174,7 +175,7 @@ func TestAppStream_DirectoryConfigServiceAccountCredentials(t *testing.T) {
require.Equal(t, http.StatusOK, recUpd.Code)
var updResp map[string]any
- require.NoError(t, json.Unmarshal(recUpd.Body.Bytes(), &updResp))
+ require.NoError(t, json.Unmarshal(recUpd.Body.Bytes(), &updResp, jsonx.V1()))
updDC := updResp["DirectoryConfig"].(map[string]any)
updCred := updDC["ServiceAccountCredentials"].(map[string]any)
assert.Equal(t, "svc-account-2", updCred["AccountName"])
diff --git a/services/appstream/entitlements_test.go b/services/appstream/entitlements_test.go
index 8b88b2a9ae..4b884009fa 100644
--- a/services/appstream/entitlements_test.go
+++ b/services/appstream/entitlements_test.go
@@ -1,13 +1,14 @@
package appstream_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appstream"
)
@@ -38,7 +39,7 @@ func TestAppStream_Entitlements(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
ent := resp["Entitlement"].(map[string]any)
assert.Equal(t, "my-ent", ent["Name"])
assert.Equal(t, "ALL", ent["AppVisibility"])
@@ -63,7 +64,7 @@ func TestAppStream_Entitlements(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
ents := resp["Entitlements"].([]any)
assert.Len(t, ents, 1)
},
@@ -84,7 +85,7 @@ func TestAppStream_Entitlements(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
ent := resp["Entitlement"].(map[string]any)
assert.Equal(t, "updated", ent["Description"])
},
@@ -137,7 +138,7 @@ func TestAppStream_Entitlements(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
apps := resp["EntitledApplications"].([]any)
assert.Len(t, apps, 1)
},
@@ -206,7 +207,7 @@ func TestAppStream_EntitlementRoundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, recDesc.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &resp, jsonx.V1()))
entitlements := resp["Entitlements"].([]any)
require.Len(t, entitlements, 1)
ent := entitlements[0].(map[string]any)
diff --git a/services/appstream/fleets_test.go b/services/appstream/fleets_test.go
index ed9920fbf2..6a057d8649 100644
--- a/services/appstream/fleets_test.go
+++ b/services/appstream/fleets_test.go
@@ -1,13 +1,14 @@
package appstream_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appstream"
)
@@ -34,7 +35,7 @@ func TestAppStream_Fleets(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
fleet := resp["Fleet"].(map[string]any)
assert.Equal(t, "my-fleet", fleet["Name"])
assert.Equal(t, "STOPPED", fleet["State"])
@@ -64,7 +65,7 @@ func TestAppStream_Fleets(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
fleets := resp["Fleets"].([]any)
assert.Len(t, fleets, 1)
},
@@ -124,7 +125,7 @@ func TestAppStream_Fleets(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
fleet := resp["Fleet"].(map[string]any)
assert.Equal(t, "stream.compute.large", fleet["InstanceType"])
},
@@ -190,7 +191,7 @@ func TestAppStream_Associations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
names := resp["Names"].([]any)
assert.Len(t, names, 1)
assert.Equal(t, "fleet-2", names[0])
@@ -209,7 +210,7 @@ func TestAppStream_Associations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
names := resp["Names"].([]any)
assert.Len(t, names, 1)
assert.Equal(t, "stack-3", names[0])
@@ -377,7 +378,7 @@ func TestAppStream_FleetErrorCodes(t *testing.T) {
if tc.wantType != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantType, resp["__type"], "wrong __type in error response")
}
})
@@ -400,7 +401,7 @@ func TestAppStream_FleetComputeCapacityStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fleet := resp["Fleet"].(map[string]any)
ccs, ok := fleet["ComputeCapacityStatus"].(map[string]any)
@@ -426,7 +427,7 @@ func TestAppStream_FleetComputeCapacityStatus(t *testing.T) {
require.Equal(t, http.StatusOK, recD.Code)
var dr map[string]any
- require.NoError(t, json.Unmarshal(recD.Body.Bytes(), &dr))
+ require.NoError(t, json.Unmarshal(recD.Body.Bytes(), &dr, jsonx.V1()))
fleets := dr["Fleets"].([]any)
require.Len(t, fleets, 1)
f := fleets[0].(map[string]any)
@@ -449,7 +450,7 @@ func TestAppStream_FleetComputeCapacityStatusDefault(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fleet := resp["Fleet"].(map[string]any)
ccs, ok := fleet["ComputeCapacityStatus"].(map[string]any)
@@ -473,7 +474,7 @@ func TestAppStream_FleetImageNameRoundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fleet := resp["Fleet"].(map[string]any)
assert.Equal(t, "AppStream-WinServer2019-05-01-2023", fleet["ImageName"])
}
@@ -493,7 +494,7 @@ func TestAppStream_FleetImageArnRoundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fleet := resp["Fleet"].(map[string]any)
assert.Equal(t, imageArn, fleet["ImageArn"])
}
@@ -512,7 +513,7 @@ func TestAppStream_FleetIdleDisconnectTimeout(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fleet := resp["Fleet"].(map[string]any)
assert.EqualValues(t, 600, fleet["IdleDisconnectTimeoutInSeconds"])
}
@@ -531,7 +532,7 @@ func TestAppStream_FleetEnableDefaultInternetAccess(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fleet := resp["Fleet"].(map[string]any)
assert.Equal(t, true, fleet["EnableDefaultInternetAccess"])
}
@@ -554,7 +555,7 @@ func TestAppStream_FleetUpdateImageName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fleet := resp["Fleet"].(map[string]any)
assert.Equal(t, "new-image", fleet["ImageName"])
}
@@ -578,7 +579,7 @@ func TestAppStream_FleetUpdateComputeCapacity(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fleet := resp["Fleet"].(map[string]any)
ccs := fleet["ComputeCapacityStatus"].(map[string]any)
assert.EqualValues(t, 5, ccs["Desired"])
@@ -597,7 +598,7 @@ func TestAppStream_FleetARNFormat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fleet := resp["Fleet"].(map[string]any)
assert.Regexp(t, `^arn:aws:appstream:[a-z0-9-]+:\d+:fleet/arn-fleet$`, fleet["Arn"])
}
@@ -617,7 +618,7 @@ func TestAppStream_FleetDefaultsMatchAWS(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fleet := resp["Fleet"].(map[string]any)
assert.Equal(t, "ON_DEMAND", fleet["FleetType"])
assert.EqualValues(t, 57600, fleet["MaxUserDurationInSeconds"])
@@ -639,7 +640,7 @@ func TestAppStream_FleetStartedStateRunning(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fleet := resp["Fleets"].([]any)[0].(map[string]any)
assert.Equal(t, "RUNNING", fleet["State"])
}
@@ -669,6 +670,6 @@ func TestAppStream_DeleteFleet_ClearsAssociations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["Names"], "re-created fleet must not inherit the deleted fleet's stack associations")
}
diff --git a/services/appstream/handler.go b/services/appstream/handler.go
index feb4e93923..2109579589 100644
--- a/services/appstream/handler.go
+++ b/services/appstream/handler.go
@@ -2,7 +2,7 @@ package appstream
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -155,7 +156,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// errorCodeStatus maps a backend error to the AWS exception name and HTTP
@@ -657,7 +658,7 @@ type createStackInput struct {
func (h *Handler) opCreateStack(_ context.Context, body []byte) (any, error) {
var req createStackInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -698,10 +699,11 @@ type describeStacksInput struct {
Names []string `json:"Names"`
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) opDescribeStacks(_ context.Context, body []byte) (any, error) {
var req describeStacksInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
@@ -744,7 +746,7 @@ type updateStackInput struct {
func (h *Handler) opUpdateStack(_ context.Context, body []byte) (any, error) {
var req updateStackInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -781,7 +783,7 @@ type deleteStackInput struct {
func (h *Handler) opDeleteStack(_ context.Context, body []byte) (any, error) {
var req deleteStackInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -879,7 +881,7 @@ type createFleetInput struct {
func (h *Handler) opCreateFleet(_ context.Context, body []byte) (any, error) {
var req createFleetInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -925,10 +927,11 @@ type describeFleetsInput struct {
Names []string `json:"Names"`
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) opDescribeFleets(_ context.Context, body []byte) (any, error) {
var req describeFleetsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
@@ -980,7 +983,7 @@ type updateFleetInput struct {
func (h *Handler) opUpdateFleet(_ context.Context, body []byte) (any, error) {
var req updateFleetInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -1030,7 +1033,7 @@ type deleteFleetInput struct {
func (h *Handler) opDeleteFleet(_ context.Context, body []byte) (any, error) {
var req deleteFleetInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -1047,7 +1050,7 @@ type fleetNameInput struct {
func (h *Handler) opStartFleet(_ context.Context, body []byte) (any, error) {
var req fleetNameInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -1060,7 +1063,7 @@ func (h *Handler) opStartFleet(_ context.Context, body []byte) (any, error) {
func (h *Handler) opStopFleet(_ context.Context, body []byte) (any, error) {
var req fleetNameInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -1080,7 +1083,7 @@ type associateFleetInput struct {
func (h *Handler) opAssociateFleet(_ context.Context, body []byte) (any, error) {
var req associateFleetInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -1093,7 +1096,7 @@ func (h *Handler) opAssociateFleet(_ context.Context, body []byte) (any, error)
func (h *Handler) opDisassociateFleet(_ context.Context, body []byte) (any, error) {
var req associateFleetInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -1111,7 +1114,7 @@ type listAssociatedFleetsInput struct {
func (h *Handler) opListAssociatedFleets(_ context.Context, body []byte) (any, error) {
var req listAssociatedFleetsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -1135,7 +1138,7 @@ type listAssociatedStacksInput struct {
func (h *Handler) opListAssociatedStacks(_ context.Context, body []byte) (any, error) {
var req listAssociatedStacksInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -1161,7 +1164,7 @@ type tagResourceInput struct {
func (h *Handler) opTagResource(_ context.Context, body []byte) (any, error) {
var req tagResourceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -1180,7 +1183,7 @@ type untagResourceInput struct {
func (h *Handler) opUntagResource(_ context.Context, body []byte) (any, error) {
var req untagResourceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -1197,7 +1200,7 @@ type listTagsForResourceInput struct {
func (h *Handler) opListTagsForResource(_ context.Context, body []byte) (any, error) {
var req listTagsForResourceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
diff --git a/services/appstream/handler_appblock.go b/services/appstream/handler_appblock.go
index d813f591af..e64b2b5919 100644
--- a/services/appstream/handler_appblock.go
+++ b/services/appstream/handler_appblock.go
@@ -2,11 +2,12 @@ package appstream
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- AppBlock handlers ---
@@ -54,7 +55,7 @@ type createAppBlockInput struct {
func (h *Handler) opCreateAppBlock(_ context.Context, body []byte) (any, error) {
var req createAppBlockInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -79,7 +80,7 @@ type deleteAppBlockInput struct {
func (h *Handler) opDeleteAppBlock(_ context.Context, body []byte) (any, error) {
var req deleteAppBlockInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -95,10 +96,11 @@ type describeAppBlocksInput struct {
Arns []string `json:"Arns"`
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) opDescribeAppBlocks(_ context.Context, body []byte) (any, error) {
var req describeAppBlocksInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
@@ -144,7 +146,7 @@ type createAppBlockBuilderInput struct {
func (h *Handler) opCreateAppBlockBuilder(_ context.Context, body []byte) (any, error) {
var req createAppBlockBuilderInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -177,7 +179,7 @@ type deleteAppBlockBuilderInput struct {
func (h *Handler) opDeleteAppBlockBuilder(_ context.Context, body []byte) (any, error) {
var req deleteAppBlockBuilderInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -193,10 +195,11 @@ type describeAppBlockBuildersInput struct {
Names []string `json:"Names"`
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) opDescribeAppBlockBuilders(_ context.Context, body []byte) (any, error) {
var req describeAppBlockBuildersInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
@@ -225,7 +228,7 @@ type appBlockBuilderNameInput struct {
func (h *Handler) opStartAppBlockBuilder(_ context.Context, body []byte) (any, error) {
var req appBlockBuilderNameInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -243,7 +246,7 @@ func (h *Handler) opStartAppBlockBuilder(_ context.Context, body []byte) (any, e
func (h *Handler) opStopAppBlockBuilder(_ context.Context, body []byte) (any, error) {
var req appBlockBuilderNameInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -275,7 +278,7 @@ type updateAppBlockBuilderInput struct {
func (h *Handler) opUpdateAppBlockBuilder(_ context.Context, body []byte) (any, error) {
var req updateAppBlockBuilderInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -317,7 +320,7 @@ type createAppBlockBuilderStreamingURLInput struct {
func (h *Handler) opCreateAppBlockBuilderStreamingURL(_ context.Context, body []byte) (any, error) {
var req createAppBlockBuilderStreamingURLInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -341,7 +344,7 @@ type appBlockBuilderAppBlockInput struct {
func (h *Handler) opAssociateAppBlockBuilderAppBlock(_ context.Context, body []byte) (any, error) {
var req appBlockBuilderAppBlockInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -358,7 +361,7 @@ func (h *Handler) opAssociateAppBlockBuilderAppBlock(_ context.Context, body []b
func (h *Handler) opDisassociateAppBlockBuilderAppBlock(_ context.Context, body []byte) (any, error) {
var req appBlockBuilderAppBlockInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -378,7 +381,7 @@ type describeAppBlockBuilderAppBlockAssociationsInput struct {
func (h *Handler) opDescribeAppBlockBuilderAppBlockAssociations(_ context.Context, body []byte) (any, error) {
var req describeAppBlockBuilderAppBlockAssociationsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
diff --git a/services/appstream/handler_application.go b/services/appstream/handler_application.go
index cbccb591ec..c5fabec8c6 100644
--- a/services/appstream/handler_application.go
+++ b/services/appstream/handler_application.go
@@ -2,11 +2,12 @@ package appstream
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- Application handlers ---
@@ -46,7 +47,7 @@ type createApplicationInput struct {
func (h *Handler) opCreateApplication(_ context.Context, body []byte) (any, error) {
var req createApplicationInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -68,7 +69,7 @@ type deleteApplicationInput struct {
func (h *Handler) opDeleteApplication(_ context.Context, body []byte) (any, error) {
var req deleteApplicationInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -84,10 +85,11 @@ type describeApplicationsInput struct {
Arns []string `json:"Arns"`
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) opDescribeApplications(_ context.Context, body []byte) (any, error) {
var req describeApplicationsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
@@ -124,7 +126,7 @@ type updateApplicationInput struct {
func (h *Handler) opUpdateApplication(_ context.Context, body []byte) (any, error) {
var req updateApplicationInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -153,7 +155,7 @@ func (h *Handler) opUpdateApplication(_ context.Context, body []byte) (any, erro
func (h *Handler) opDescribeAppLicenseUsage(_ context.Context, body []byte) (any, error) {
var req pageReq
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
@@ -186,7 +188,7 @@ type applicationFleetInput struct {
func (h *Handler) opAssociateApplicationFleet(_ context.Context, body []byte) (any, error) {
var req applicationFleetInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -203,7 +205,7 @@ func (h *Handler) opAssociateApplicationFleet(_ context.Context, body []byte) (a
func (h *Handler) opDisassociateApplicationFleet(_ context.Context, body []byte) (any, error) {
var req applicationFleetInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -223,7 +225,7 @@ type describeApplicationFleetAssociationsInput struct {
func (h *Handler) opDescribeApplicationFleetAssociations(_ context.Context, body []byte) (any, error) {
var req describeApplicationFleetAssociationsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
@@ -273,7 +275,7 @@ func toEntitlementAttributes(attrs []entitlementAttributeJSON) []EntitlementAttr
func (h *Handler) opCreateEntitlement(_ context.Context, body []byte) (any, error) {
var req createEntitlementInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -295,7 +297,7 @@ type deleteEntitlementInput struct {
func (h *Handler) opDeleteEntitlement(_ context.Context, body []byte) (any, error) {
var req deleteEntitlementInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -315,7 +317,7 @@ type describeEntitlementsInput struct {
func (h *Handler) opDescribeEntitlements(_ context.Context, body []byte) (any, error) {
var req describeEntitlementsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
@@ -353,7 +355,7 @@ type updateEntitlementInput struct {
func (h *Handler) opUpdateEntitlement(_ context.Context, body []byte) (any, error) {
var req updateEntitlementInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -376,7 +378,7 @@ type associateApplicationToEntitlementInput struct {
func (h *Handler) opAssociateApplicationToEntitlement(_ context.Context, body []byte) (any, error) {
var req associateApplicationToEntitlementInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -397,7 +399,7 @@ type disassociateApplicationFromEntitlementInput struct {
func (h *Handler) opDisassociateApplicationFromEntitlement(_ context.Context, body []byte) (any, error) {
var req disassociateApplicationFromEntitlementInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -418,7 +420,7 @@ type listEntitledApplicationsInput struct {
func (h *Handler) opListEntitledApplications(_ context.Context, body []byte) (any, error) {
var req listEntitledApplicationsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -486,7 +488,7 @@ type createDirectoryConfigInput struct {
func (h *Handler) opCreateDirectoryConfig(_ context.Context, body []byte) (any, error) {
var req createDirectoryConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -507,7 +509,7 @@ type deleteDirectoryConfigInput struct {
func (h *Handler) opDeleteDirectoryConfig(_ context.Context, body []byte) (any, error) {
var req deleteDirectoryConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -523,10 +525,11 @@ type describeDirectoryConfigsInput struct {
DirectoryNames []string `json:"DirectoryNames"`
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) opDescribeDirectoryConfigs(_ context.Context, body []byte) (any, error) {
var req describeDirectoryConfigsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
@@ -563,7 +566,7 @@ type updateDirectoryConfigInput struct {
func (h *Handler) opUpdateDirectoryConfig(_ context.Context, body []byte) (any, error) {
var req updateDirectoryConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
diff --git a/services/appstream/handler_image.go b/services/appstream/handler_image.go
index 67548ac94a..ba3efa717d 100644
--- a/services/appstream/handler_image.go
+++ b/services/appstream/handler_image.go
@@ -2,11 +2,12 @@ package appstream
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- Image handlers ---
@@ -20,7 +21,7 @@ type copyImageInput struct {
func (h *Handler) opCopyImage(_ context.Context, body []byte) (any, error) {
var req copyImageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -46,7 +47,7 @@ type createImportedImageInput struct {
func (h *Handler) opCreateImportedImage(_ context.Context, body []byte) (any, error) {
var req createImportedImageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -84,7 +85,7 @@ type createUpdatedImageInput struct {
func (h *Handler) opCreateUpdatedImage(_ context.Context, body []byte) (any, error) {
var req createUpdatedImageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -112,7 +113,7 @@ type deleteImageInput struct {
func (h *Handler) opDeleteImage(_ context.Context, body []byte) (any, error) {
var req deleteImageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -134,7 +135,7 @@ type describeImagesInput struct {
func (h *Handler) opDescribeImages(_ context.Context, body []byte) (any, error) {
var req describeImagesInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
@@ -173,7 +174,7 @@ type imagePermissionsInput struct {
func (h *Handler) opUpdateImagePermissions(_ context.Context, body []byte) (any, error) {
var req imagePermissionsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -197,7 +198,7 @@ type deleteImagePermissionsInput struct {
func (h *Handler) opDeleteImagePermissions(_ context.Context, body []byte) (any, error) {
var req deleteImagePermissionsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -216,7 +217,7 @@ type describeImagePermissionsInput struct {
func (h *Handler) opDescribeImagePermissions(_ context.Context, body []byte) (any, error) {
var req describeImagePermissionsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -280,7 +281,7 @@ type createImageBuilderInput struct {
func (h *Handler) opCreateImageBuilder(_ context.Context, body []byte) (any, error) {
var req createImageBuilderInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -315,7 +316,7 @@ type deleteImageBuilderInput struct {
func (h *Handler) opDeleteImageBuilder(_ context.Context, body []byte) (any, error) {
var req deleteImageBuilderInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -332,10 +333,11 @@ type describeImageBuildersInput struct {
Names []string `json:"Names"`
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) opDescribeImageBuilders(_ context.Context, body []byte) (any, error) {
var req describeImageBuildersInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
@@ -368,7 +370,7 @@ type startImageBuilderInput struct {
// this handler invented one, which real SDK clients would never receive.
func (h *Handler) opStartImageBuilder(_ context.Context, body []byte) (any, error) {
var req startImageBuilderInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -392,7 +394,7 @@ type stopImageBuilderInput struct {
func (h *Handler) opStopImageBuilder(_ context.Context, body []byte) (any, error) {
var req stopImageBuilderInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -411,7 +413,7 @@ type createImageBuilderStreamingURLInput struct {
func (h *Handler) opCreateImageBuilderStreamingURL(_ context.Context, body []byte) (any, error) {
var req createImageBuilderStreamingURLInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -435,7 +437,7 @@ type associateSoftwareInput struct {
func (h *Handler) opAssociateSoftwareToImageBuilder(_ context.Context, body []byte) (any, error) {
var req associateSoftwareInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -448,7 +450,7 @@ func (h *Handler) opAssociateSoftwareToImageBuilder(_ context.Context, body []by
func (h *Handler) opDisassociateSoftwareFromImageBuilder(_ context.Context, body []byte) (any, error) {
var req associateSoftwareInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -466,7 +468,7 @@ type describeSoftwareAssociationsInput struct {
func (h *Handler) opDescribeSoftwareAssociations(_ context.Context, body []byte) (any, error) {
var req describeSoftwareAssociationsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -505,7 +507,7 @@ type startSoftwareDeploymentInput struct {
func (h *Handler) opStartSoftwareDeploymentToImageBuilder(_ context.Context, body []byte) (any, error) {
var req startSoftwareDeploymentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -533,7 +535,7 @@ type createExportImageTaskInput struct {
func (h *Handler) opCreateExportImageTask(_ context.Context, body []byte) (any, error) {
var req createExportImageTaskInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -553,7 +555,7 @@ type getExportImageTaskInput struct {
func (h *Handler) opGetExportImageTask(_ context.Context, body []byte) (any, error) {
var req getExportImageTaskInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -594,7 +596,7 @@ func stateFilterValues(filters []filterInput) []string {
func (h *Handler) opListExportImageTasks(_ context.Context, body []byte) (any, error) {
var req listExportImageTasksInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
diff --git a/services/appstream/handler_test.go b/services/appstream/handler_test.go
index 7b97b91695..a501bb4792 100644
--- a/services/appstream/handler_test.go
+++ b/services/appstream/handler_test.go
@@ -2,7 +2,7 @@ package appstream_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appstream"
)
@@ -62,7 +63,7 @@ func doRequest(t *testing.T, h *appstream.Handler, action string, body any) *htt
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -174,7 +175,7 @@ func TestAppStream_DescribeByARN(t *testing.T) {
require.Equal(t, http.StatusOK, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appArn := appResp["Application"].(map[string]any)["Arn"].(string)
require.NotEmpty(t, appArn)
@@ -185,7 +186,7 @@ func TestAppStream_DescribeByARN(t *testing.T) {
require.Equal(t, http.StatusOK, abRec.Code)
var abResp map[string]any
- require.NoError(t, json.Unmarshal(abRec.Body.Bytes(), &abResp))
+ require.NoError(t, json.Unmarshal(abRec.Body.Bytes(), &abResp, jsonx.V1()))
appBlockArn := abResp["AppBlock"].(map[string]any)["Arn"].(string)
require.NotEmpty(t, appBlockArn)
@@ -193,7 +194,7 @@ func TestAppStream_DescribeByARN(t *testing.T) {
require.Equal(t, http.StatusOK, imgRec.Code)
var imgResp map[string]any
- require.NoError(t, json.Unmarshal(imgRec.Body.Bytes(), &imgResp))
+ require.NoError(t, json.Unmarshal(imgRec.Body.Bytes(), &imgResp, jsonx.V1()))
imageArn := imgResp["Image"].(map[string]any)["Arn"].(string)
require.NotEmpty(t, imageArn)
@@ -204,7 +205,7 @@ func TestAppStream_DescribeByARN(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
apps := resp["Applications"].([]any)
require.Len(t, apps, 1)
assert.Equal(t, "arn-app", apps[0].(map[string]any)["Name"])
@@ -217,7 +218,7 @@ func TestAppStream_DescribeByARN(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
blocks := resp["AppBlocks"].([]any)
require.Len(t, blocks, 1)
assert.Equal(t, "arn-appblock", blocks[0].(map[string]any)["Name"])
@@ -230,7 +231,7 @@ func TestAppStream_DescribeByARN(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
images := resp["Images"].([]any)
require.Len(t, images, 1)
assert.Equal(t, "arn-image", images[0].(map[string]any)["Name"])
@@ -260,7 +261,7 @@ func TestAppStream_AssociationsAcceptARNIdentifiers(t *testing.T) {
require.Equal(t, http.StatusOK, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appArn := appResp["Application"].(map[string]any)["Arn"].(string)
require.NotEmpty(t, appArn)
@@ -275,7 +276,7 @@ func TestAppStream_AssociationsAcceptARNIdentifiers(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assocs := descResp["ApplicationFleetAssociations"].([]any)
require.Len(t, assocs, 1)
assert.Equal(t, "assoc-arn-fleet", assocs[0].(map[string]any)["FleetName"])
@@ -294,7 +295,7 @@ func TestAppStream_AssociationsAcceptARNIdentifiers(t *testing.T) {
require.Equal(t, http.StatusOK, abRec.Code)
var abResp map[string]any
- require.NoError(t, json.Unmarshal(abRec.Body.Bytes(), &abResp))
+ require.NoError(t, json.Unmarshal(abRec.Body.Bytes(), &abResp, jsonx.V1()))
appBlockArn := abResp["AppBlock"].(map[string]any)["Arn"].(string)
require.NotEmpty(t, appBlockArn)
@@ -309,7 +310,7 @@ func TestAppStream_AssociationsAcceptARNIdentifiers(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assocs := descResp["AppBlockBuilderAppBlockAssociations"].([]any)
require.Len(t, assocs, 1)
assert.Equal(t, appBlockArn, assocs[0].(map[string]any)["AppBlockArn"])
diff --git a/services/appstream/handler_user.go b/services/appstream/handler_user.go
index 2cd48732d8..6ea21d28c9 100644
--- a/services/appstream/handler_user.go
+++ b/services/appstream/handler_user.go
@@ -2,11 +2,12 @@ package appstream
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- User handlers ---
@@ -21,7 +22,7 @@ type createUserInput struct {
func (h *Handler) opCreateUser(_ context.Context, body []byte) (any, error) {
var req createUserInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -57,7 +58,7 @@ type deleteUserInput struct {
func (h *Handler) opDeleteUser(_ context.Context, body []byte) (any, error) {
var req deleteUserInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -76,7 +77,7 @@ type describeUsersInput struct {
func (h *Handler) opDescribeUsers(_ context.Context, body []byte) (any, error) {
var req describeUsersInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
@@ -111,7 +112,7 @@ type userAuthInput struct {
func (h *Handler) opDisableUser(_ context.Context, body []byte) (any, error) {
var req userAuthInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -124,7 +125,7 @@ func (h *Handler) opDisableUser(_ context.Context, body []byte) (any, error) {
func (h *Handler) opEnableUser(_ context.Context, body []byte) (any, error) {
var req userAuthInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -157,7 +158,7 @@ func (h *Handler) opBatchAssociateUserStack( //nolint:dupl // existing issue.
body []byte,
) (any, error) {
var req batchAssociateUserStackInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -197,7 +198,7 @@ func (h *Handler) opBatchDisassociateUserStack( //nolint:dupl // existing issue.
body []byte,
) (any, error) {
var req batchAssociateUserStackInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -242,7 +243,7 @@ type describeUserStackAssociationsInput struct {
func (h *Handler) opDescribeUserStackAssociations(_ context.Context, body []byte) (any, error) {
var req describeUserStackAssociationsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
@@ -286,7 +287,7 @@ type describeSessionsInput struct {
func (h *Handler) opDescribeSessions(_ context.Context, body []byte) (any, error) {
var req describeSessionsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
@@ -321,7 +322,7 @@ type sessionIDInput struct {
func (h *Handler) opDrainSessionInstance(_ context.Context, body []byte) (any, error) {
var req sessionIDInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -334,7 +335,7 @@ func (h *Handler) opDrainSessionInstance(_ context.Context, body []byte) (any, e
func (h *Handler) opExpireSession(_ context.Context, body []byte) (any, error) {
var req sessionIDInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -354,7 +355,7 @@ type createStreamingURLInput struct {
func (h *Handler) opCreateStreamingURL(_ context.Context, body []byte) (any, error) {
var req createStreamingURLInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -394,7 +395,7 @@ func (h *Handler) opDeleteUsageReportSubscription(_ context.Context, _ []byte) (
func (h *Handler) opDescribeUsageReportSubscriptions(_ context.Context, body []byte) (any, error) {
var req pageReq
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
}
@@ -450,7 +451,7 @@ type createThemeForStackInput struct {
func (h *Handler) opCreateThemeForStack(_ context.Context, body []byte) (any, error) {
var req createThemeForStackInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -473,7 +474,7 @@ func (h *Handler) opCreateThemeForStack(_ context.Context, body []byte) (any, er
func (h *Handler) opDeleteThemeForStack(_ context.Context, body []byte) (any, error) {
var req themeStackInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -486,7 +487,7 @@ func (h *Handler) opDeleteThemeForStack(_ context.Context, body []byte) (any, er
func (h *Handler) opDescribeThemeForStack(_ context.Context, body []byte) (any, error) {
var req themeStackInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
@@ -514,7 +515,7 @@ type updateThemeForStackInput struct {
func (h *Handler) opUpdateThemeForStack(_ context.Context, body []byte) (any, error) {
var req updateThemeForStackInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, awserr.New(errInvalidParameter, awserr.ErrInvalidParameter)
}
diff --git a/services/appstream/images_test.go b/services/appstream/images_test.go
index d3f9f6ac83..619d39db3f 100644
--- a/services/appstream/images_test.go
+++ b/services/appstream/images_test.go
@@ -1,13 +1,14 @@
package appstream_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appstream"
)
@@ -31,7 +32,7 @@ func TestAppStream_Images(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
img := resp["Image"].(map[string]any)
assert.Equal(t, "my-img", img["Name"])
assert.Equal(t, "AVAILABLE", img["State"])
@@ -49,7 +50,7 @@ func TestAppStream_Images(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
imgs := resp["Images"].([]any)
assert.Len(t, imgs, 2)
},
@@ -69,7 +70,7 @@ func TestAppStream_Images(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
assert.Equal(t, "dst-img", resp["DestinationImageName"])
},
},
@@ -125,7 +126,7 @@ func TestAppStream_Images(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
perms := resp["SharedImagePermissionsList"].([]any)
assert.Len(t, perms, 1)
},
@@ -185,7 +186,7 @@ func TestAppStream_ImageBuilders(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
ib := resp["ImageBuilder"].(map[string]any)
// Real CreateImageBuilder launches the build instance
// immediately -- terraform-provider-aws's resource waits
@@ -208,7 +209,7 @@ func TestAppStream_ImageBuilders(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
ib := resp["ImageBuilder"].(map[string]any)
assert.Equal(t, "RUNNING", ib["State"])
// Real StartImageBuilderOutput carries only ImageBuilder --
@@ -229,7 +230,7 @@ func TestAppStream_ImageBuilders(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
ib := resp["ImageBuilder"].(map[string]any)
assert.Equal(t, "STOPPED", ib["State"])
},
@@ -245,7 +246,7 @@ func TestAppStream_ImageBuilders(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
ib := resp["ImageBuilder"].(map[string]any)
assert.Equal(t, "STOPPED", ib["State"])
},
@@ -261,7 +262,7 @@ func TestAppStream_ImageBuilders(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["StreamingURL"])
assert.NotEmpty(t, resp["Expires"])
},
@@ -294,7 +295,7 @@ func TestAppStream_ImageBuilders(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
assert.Equal(t, "list-sw-ib", resp["AssociatedResource"])
assocs := resp["SoftwareAssociations"].([]any)
assert.Len(t, assocs, 1)
@@ -311,7 +312,7 @@ func TestAppStream_ImageBuilders(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
assert.Equal(t, "sw-assoc-image", resp["AssociatedResource"])
assocs := resp["SoftwareAssociations"].([]any)
assert.Empty(t, assocs)
@@ -397,7 +398,7 @@ func TestAppStream_ExportImageTasks(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
task := resp["ExportImageTask"].(map[string]any)
assert.NotEmpty(t, task["TaskId"])
assert.Equal(t, "exported-ami", task["AmiName"])
@@ -423,7 +424,7 @@ func TestAppStream_ExportImageTasks(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
tasks := resp["ExportImageTasks"].([]any)
assert.Len(t, tasks, 1)
},
@@ -459,7 +460,7 @@ func TestAppStream_GetExportImageTask(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
task := createResp["ExportImageTask"].(map[string]any)
taskID := task["TaskId"].(string)
@@ -467,7 +468,7 @@ func TestAppStream_GetExportImageTask(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp, jsonx.V1()))
gotTask := getResp["ExportImageTask"].(map[string]any)
assert.Equal(t, taskID, gotTask["TaskId"])
}
@@ -484,7 +485,7 @@ func TestAppStream_ImageBuilderARNFormat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
builder := resp["ImageBuilder"].(map[string]any)
assert.Regexp(t, `^arn:aws:appstream:[a-z0-9-]+:\d+:image-builder/arn-builder$`, builder["Arn"])
}
diff --git a/services/appstream/persistence.go b/services/appstream/persistence.go
index 8f789989b5..e3b24c5bcc 100644
--- a/services/appstream/persistence.go
+++ b/services/appstream/persistence.go
@@ -2,7 +2,7 @@ package appstream
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -38,7 +38,7 @@ const appstreamSnapshotVersion = 2
// subscription exists at a time), so it is also persisted directly rather
// than through a table.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Associations map[string]map[string]bool `json:"associations"`
Tags map[string]map[string]string `json:"tags"`
AppBlockBuilderAssoc map[string]map[string]bool `json:"appBlockBuilderAssoc"`
diff --git a/services/appstream/rpcv2cbor.go b/services/appstream/rpcv2cbor.go
index 3c544e88cd..d43edeb433 100644
--- a/services/appstream/rpcv2cbor.go
+++ b/services/appstream/rpcv2cbor.go
@@ -2,7 +2,7 @@ package appstream
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -12,6 +12,7 @@ import (
"github.com/aws/smithy-go/encoding/cbor"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -157,7 +158,7 @@ func cborBodyToJSON(body []byte) ([]byte, error) {
return nil, fmt.Errorf("%w: got %T", errExpectedCBORMap, v)
}
- return json.Marshal(cborToGo(m))
+ return json.Marshal(cborToGo(m), jsonx.V1())
}
// cborToGo converts a decoded CBOR value tree into the generic Go value
@@ -221,7 +222,7 @@ func awstimeSeconds(sec int64, nsec int) float64 {
// a CBOR value tree suitable for the wire.
func jsonToCBOR(body []byte) (cbor.Value, error) {
var v any
- if err := json.Unmarshal(body, &v); err != nil {
+ if err := json.Unmarshal(body, &v, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/appstream/sessions_test.go b/services/appstream/sessions_test.go
index a9615472d7..9f89e3c699 100644
--- a/services/appstream/sessions_test.go
+++ b/services/appstream/sessions_test.go
@@ -1,7 +1,7 @@
package appstream_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appstream"
)
@@ -40,7 +41,7 @@ func TestAppStream_Sessions(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["StreamingURL"])
assert.NotEmpty(t, resp["Expires"])
},
@@ -66,7 +67,7 @@ func TestAppStream_Sessions(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
sessions := resp["Sessions"].([]any)
assert.Len(t, sessions, 1)
},
@@ -112,7 +113,7 @@ func TestAppStream_SessionLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp, jsonx.V1()))
sessions := descResp["Sessions"].([]any)
require.Len(t, sessions, 1)
sessionID := sessions[0].(map[string]any)["Id"].(string)
@@ -142,7 +143,7 @@ func TestAppStream_DrainSessionInstance(t *testing.T) {
"StackName": "drain-stk", "FleetName": "drain-fleet",
})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
sessions := descResp["Sessions"].([]any)
require.Len(t, sessions, 1)
sessionID := sessions[0].(map[string]any)["Id"].(string)
@@ -178,7 +179,7 @@ func TestAppStream_SessionStreamingURL(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["StreamingURL"])
expires, ok := resp["Expires"].(float64)
@@ -220,7 +221,7 @@ func TestAppStream_DescribeSessionsFilterByStack(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sessions, ok := resp["Sessions"].([]any)
require.True(t, ok)
assert.Len(t, sessions, 2)
diff --git a/services/appstream/stacks_test.go b/services/appstream/stacks_test.go
index 58bcca5525..95a254fd71 100644
--- a/services/appstream/stacks_test.go
+++ b/services/appstream/stacks_test.go
@@ -1,13 +1,14 @@
package appstream_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appstream"
)
@@ -30,7 +31,7 @@ func TestAppStream_Stacks(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
stack, ok := resp["Stack"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "my-stack", stack["Name"])
@@ -58,7 +59,7 @@ func TestAppStream_Stacks(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
stacks, ok := resp["Stacks"].([]any)
require.True(t, ok)
assert.Len(t, stacks, 1)
@@ -76,7 +77,7 @@ func TestAppStream_Stacks(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
stacks := resp["Stacks"].([]any)
assert.Len(t, stacks, 1)
s := stacks[0].(map[string]any)
@@ -100,7 +101,7 @@ func TestAppStream_Stacks(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
stack := resp["Stack"].(map[string]any)
assert.Equal(t, "Updated", stack["DisplayName"])
},
@@ -224,7 +225,7 @@ func TestAppStream_StackErrorCodes(t *testing.T) {
if tc.wantType != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantType, resp["__type"], "wrong __type in error response")
}
})
@@ -241,7 +242,7 @@ func TestAppStream_StackARNFormat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stack := resp["Stack"].(map[string]any)
assert.Regexp(t, `^arn:aws:appstream:[a-z0-9-]+:\d+:stack/arn-stack$`, stack["Arn"])
}
diff --git a/services/appstream/tags_test.go b/services/appstream/tags_test.go
index 98ee5d1d7e..531a832c71 100644
--- a/services/appstream/tags_test.go
+++ b/services/appstream/tags_test.go
@@ -1,13 +1,14 @@
package appstream_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appstream"
)
@@ -29,7 +30,7 @@ func TestAppStream_Tags(t *testing.T) {
createStack(t, h, "tag-stack")
rec := doRequest(t, h, "DescribeStacks", map[string]any{"Names": []string{"tag-stack"}})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["Stacks"].([]any)[0].(map[string]any)["Arn"].(string)
},
@@ -45,7 +46,7 @@ func TestAppStream_Tags(t *testing.T) {
createStack(t, h, "listtag-stack")
rec := doRequest(t, h, "DescribeStacks", map[string]any{"Names": []string{"listtag-stack"}})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp["Stacks"].([]any)[0].(map[string]any)["Arn"].(string)
doRequest(
t,
@@ -63,7 +64,7 @@ func TestAppStream_Tags(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
tags, ok := resp["Tags"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "staging", tags["env"])
@@ -76,7 +77,7 @@ func TestAppStream_Tags(t *testing.T) {
createStack(t, h, "untag-stack")
rec := doRequest(t, h, "DescribeStacks", map[string]any{"Names": []string{"untag-stack"}})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp["Stacks"].([]any)[0].(map[string]any)["Arn"].(string)
doRequest(t, h, "TagResource", map[string]any{"ResourceArn": arn, "Tags": map[string]string{"k": "v"}})
@@ -124,14 +125,14 @@ func TestAppStream_TagLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
stackArn := createResp["Stack"].(map[string]any)["Arn"].(string)
recList := doRequest(t, h, "ListTagsForResource", map[string]any{"ResourceArn": stackArn})
require.Equal(t, http.StatusOK, recList.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listResp, jsonx.V1()))
tags := listResp["Tags"].(map[string]any)
assert.Equal(t, "test", tags["env"])
assert.Equal(t, "platform", tags["team"])
@@ -143,7 +144,7 @@ func TestAppStream_TagLifecycle(t *testing.T) {
recList2 := doRequest(t, h, "ListTagsForResource", map[string]any{"ResourceArn": stackArn})
var listResp2 map[string]any
- require.NoError(t, json.Unmarshal(recList2.Body.Bytes(), &listResp2))
+ require.NoError(t, json.Unmarshal(recList2.Body.Bytes(), &listResp2, jsonx.V1()))
tags2 := listResp2["Tags"].(map[string]any)
assert.NotContains(t, tags2, "env")
assert.Equal(t, "platform", tags2["team"])
diff --git a/services/appstream/themes_test.go b/services/appstream/themes_test.go
index 7761f8ab7e..a6e4d89084 100644
--- a/services/appstream/themes_test.go
+++ b/services/appstream/themes_test.go
@@ -1,13 +1,14 @@
package appstream_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appstream"
)
@@ -56,7 +57,7 @@ func TestAppStream_Themes(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
th := resp["Theme"].(map[string]any)
assert.Equal(t, "theme-stk", th["StackName"])
assert.Equal(t, "ENABLED", th["State"])
diff --git a/services/appstream/usage_report_subscriptions_test.go b/services/appstream/usage_report_subscriptions_test.go
index e39c850dc3..caebb1dc49 100644
--- a/services/appstream/usage_report_subscriptions_test.go
+++ b/services/appstream/usage_report_subscriptions_test.go
@@ -1,13 +1,14 @@
package appstream_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appstream"
)
@@ -31,7 +32,7 @@ func TestAppStream_UsageReports(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
assert.Equal(t, "DAILY", resp["Schedule"])
assert.NotEmpty(t, resp["S3BucketName"])
},
@@ -48,7 +49,7 @@ func TestAppStream_UsageReports(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
subs := resp["UsageReportSubscriptions"].([]any)
assert.Len(t, subs, 1)
},
@@ -103,7 +104,7 @@ func TestAppStream_UsageReportSubscriptionRoundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, recDesc.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &resp, jsonx.V1()))
subs := resp["UsageReportSubscriptions"].([]any)
require.Len(t, subs, 1)
sub := subs[0].(map[string]any)
diff --git a/services/appstream/users_test.go b/services/appstream/users_test.go
index ba12217f8a..e33409b676 100644
--- a/services/appstream/users_test.go
+++ b/services/appstream/users_test.go
@@ -1,13 +1,14 @@
package appstream_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appstream"
)
@@ -56,7 +57,7 @@ func TestAppStream_Users(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
users := resp["Users"].([]any)
assert.Len(t, users, 2)
},
@@ -136,7 +137,7 @@ func TestAppStream_UserARNPartition(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
users := resp["Users"].([]any)
require.Len(t, users, 1)
assert.Equal(
@@ -161,7 +162,7 @@ func TestAppStream_UserARNFormat(t *testing.T) {
require.Equal(t, http.StatusOK, recDesc.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &resp, jsonx.V1()))
users := resp["Users"].([]any)
require.Len(t, users, 1)
user := users[0].(map[string]any)
@@ -187,7 +188,7 @@ func TestAppStream_UserStatusEnabled(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
users := resp["Users"].([]any)
require.Len(t, users, 1)
user := users[0].(map[string]any)
@@ -226,7 +227,7 @@ func TestAppStream_UserStackAssociations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
errs := resp["errors"].([]any)
assert.Empty(t, errs)
},
@@ -247,7 +248,7 @@ func TestAppStream_UserStackAssociations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
errs := resp["errors"].([]any)
assert.Len(t, errs, 1)
},
@@ -274,7 +275,7 @@ func TestAppStream_UserStackAssociations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
assocs := resp["UserStackAssociations"].([]any)
assert.Len(t, assocs, 1)
},
@@ -344,7 +345,7 @@ func TestAppStream_BatchAssociateUserStackErrors(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// errors field may be empty or present depending on implementation
// but response must be 200 with valid JSON
assert.NotNil(t, resp)
@@ -374,7 +375,7 @@ func TestAppStream_BatchAssociateUserStack_StackNotFound(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
errs := resp["errors"].([]any)
require.Len(t, errs, 1)
assert.Equal(t, "STACK_NOT_FOUND", errs[0].(map[string]any)["ErrorCode"])
@@ -383,7 +384,7 @@ func TestAppStream_BatchAssociateUserStack_StackNotFound(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Empty(t, descResp["UserStackAssociations"])
}
@@ -416,7 +417,7 @@ func TestAppStream_DescribeUserStackAssociations(t *testing.T) {
require.Equal(t, http.StatusOK, recDesc.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &resp, jsonx.V1()))
assocs := resp["UserStackAssociations"].([]any)
assert.Len(t, assocs, 1)
assoc := assocs[0].(map[string]any)
@@ -446,7 +447,7 @@ func TestAppStream_BatchDisassociateUserStack_StackNotFound(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
errs := resp["errors"].([]any)
require.Len(t, errs, 1)
assert.Equal(t, "STACK_NOT_FOUND", errs[0].(map[string]any)["ErrorCode"])
diff --git a/services/appsync/api_keys_test.go b/services/appsync/api_keys_test.go
index 135b7bf81b..0a86dfe1be 100644
--- a/services/appsync/api_keys_test.go
+++ b/services/appsync/api_keys_test.go
@@ -1,7 +1,7 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -367,7 +368,7 @@ func TestListAPIKeys_Pagination(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var apiOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &apiOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &apiOut, jsonx.V1()))
apiID := apiOut["graphqlApi"].(map[string]any)["apiId"].(string)
for range 4 {
@@ -407,7 +408,7 @@ func TestListAPIKeys_Pagination(t *testing.T) {
NextToken string `json:"nextToken"`
APIKeys []map[string]any `json:"apiKeys"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.APIKeys, tt.wantLen)
if tt.wantNextToken {
assert.NotEmpty(t, out.NextToken)
diff --git a/services/appsync/auth.go b/services/appsync/auth.go
index 90d53f5551..1feba9f624 100644
--- a/services/appsync/auth.go
+++ b/services/appsync/auth.go
@@ -2,7 +2,7 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// errUnexpectedJWTSigningMethod mirrors the sentinel
@@ -255,7 +256,7 @@ func (b *InMemoryBackend) checkLambdaAuth(
},
}
- payload, marshalErr := json.Marshal(event)
+ payload, marshalErr := json.Marshal(event, jsonx.V1())
if marshalErr != nil {
return nil, false
}
@@ -267,7 +268,7 @@ func (b *InMemoryBackend) checkLambdaAuth(
}
var resp lambdaAuthorizerResponse
- if jsonErr := json.Unmarshal(result, &resp); jsonErr != nil {
+ if jsonErr := json.Unmarshal(result, &resp, jsonx.V1()); jsonErr != nil {
return nil, false
}
diff --git a/services/appsync/auth_test.go b/services/appsync/auth_test.go
index bba2be11d9..c608f2362c 100644
--- a/services/appsync/auth_test.go
+++ b/services/appsync/auth_test.go
@@ -4,7 +4,7 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -136,7 +137,7 @@ func TestHandleGraphQL_APIKeyAuth_HTTPShape(t *testing.T) {
require.Equal(t, http.StatusUnauthorized, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// Real AppSync's transport-level auth failure body is exactly
// {"message":"Unauthorized"} -- no "code"/"__type" field, unlike this
// package's control-plane errorResponse() convention.
@@ -182,7 +183,7 @@ func TestExecuteGraphQL_LambdaAuth(t *testing.T) {
} `json:"requestContext"`
AuthorizationToken string `json:"authorizationToken"`
}
- require.NoError(t, json.Unmarshal(mock.calls[0].payload, &event))
+ require.NoError(t, json.Unmarshal(mock.calls[0].payload, &event, jsonx.V1()))
assert.Equal(t, "secret-tok", event.AuthorizationToken)
assert.Equal(t, api.APIID, event.RequestContext.APIID)
assert.Contains(t, event.RequestContext.QueryString, "hello")
diff --git a/services/appsync/caching_test.go b/services/appsync/caching_test.go
index a722481b73..185981f779 100644
--- a/services/appsync/caching_test.go
+++ b/services/appsync/caching_test.go
@@ -1,7 +1,7 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -291,7 +292,7 @@ func TestAPICache_UnmarshalJSON_HealthMetricsConfig(t *testing.T) {
t.Parallel()
var got appsync.APICache
- err := json.Unmarshal([]byte(tt.body), &got)
+ err := json.Unmarshal([]byte(tt.body), &got, jsonx.V1())
if tt.wantErr {
require.Error(t, err)
diff --git a/services/appsync/data_sources_config_test.go b/services/appsync/data_sources_config_test.go
index 90ebeb1237..40bf9b72ba 100644
--- a/services/appsync/data_sources_config_test.go
+++ b/services/appsync/data_sources_config_test.go
@@ -1,7 +1,7 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -661,7 +662,7 @@ func TestListDataSources_Pagination(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var apiOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &apiOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &apiOut, jsonx.V1()))
apiID := apiOut["graphqlApi"].(map[string]any)["apiId"].(string)
for i := range 4 {
@@ -702,7 +703,7 @@ func TestListDataSources_Pagination(t *testing.T) {
NextToken string `json:"nextToken"`
DataSources []map[string]any `json:"dataSources"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.DataSources, tt.wantLen)
if tt.wantNextToken {
assert.NotEmpty(t, out.NextToken)
diff --git a/services/appsync/ddb_ops.go b/services/appsync/ddb_ops.go
index b37902df42..1d8c65fccd 100644
--- a/services/appsync/ddb_ops.go
+++ b/services/appsync/ddb_ops.go
@@ -3,11 +3,13 @@ package appsync
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
"reflect"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -234,10 +236,10 @@ func intOf(v any) int {
return int(n)
case int:
return n
- case json.Number:
- i, _ := n.Int64()
+ case jsonx.Number:
+ i, _ := n.Int()
- return int(i)
+ return i
default:
return 0
}
@@ -305,7 +307,7 @@ func ddbQueryScan(
}
func encodeNextToken(key map[string]any) string {
- b, _ := json.Marshal(key)
+ b, _ := json.Marshal(key, jsonx.V1())
return base64.RawURLEncoding.EncodeToString(b)
}
@@ -317,7 +319,7 @@ func decodeNextToken(tok string) (map[string]any, error) {
}
var key map[string]any
- if err = json.Unmarshal(raw, &key); err != nil || len(key) == 0 {
+ if err = json.Unmarshal(raw, &key, jsonx.V1()); err != nil || len(key) == 0 {
return nil, fmt.Errorf("%w: invalid nextToken", ErrValidation)
}
diff --git a/services/appsync/ddb_resolver_flow_test.go b/services/appsync/ddb_resolver_flow_test.go
index dfb0541b65..76aeaf6b36 100644
--- a/services/appsync/ddb_resolver_flow_test.go
+++ b/services/appsync/ddb_resolver_flow_test.go
@@ -2,13 +2,14 @@ package appsync_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -123,7 +124,7 @@ type Mutation { putTodo(id: ID!, title: String): Todo }`)
require.Len(t, out, 1)
for _, v := range out {
- got, mErr := json.Marshal(v)
+ got, mErr := json.Marshal(v, jsonx.V1())
require.NoError(t, mErr)
assert.JSONEq(t, tt.want, string(got))
}
@@ -182,7 +183,7 @@ type Query { todo: Todo todos: [Todo] }`)
out, err := b.ExecuteGraphQL(t.Context(), api.APIID, tt.query, "", nil, appsync.GraphQLAuth{APIKey: key.ID})
require.NoError(t, err)
- got, mErr := json.Marshal(out)
+ got, mErr := json.Marshal(out, jsonx.V1())
require.NoError(t, mErr)
assert.JSONEq(t, tt.want, string(got))
})
diff --git a/services/appsync/ddb_transact.go b/services/appsync/ddb_transact.go
index 3f209704b5..b07b1a70e6 100644
--- a/services/appsync/ddb_transact.go
+++ b/services/appsync/ddb_transact.go
@@ -3,10 +3,12 @@ package appsync
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -190,7 +192,7 @@ func ddbSync(ctx context.Context, ddb DynamoDBBackend, cfg *DynamoDBDataSourceCo
raw, err := base64.RawURLEncoding.DecodeString(tok)
var st syncToken
- if err != nil || json.Unmarshal(raw, &st) != nil || len(st.Key) == 0 {
+ if err != nil || json.Unmarshal(raw, &st, jsonx.V1()) != nil || len(st.Key) == 0 {
return nil, fmt.Errorf("%w: invalid nextToken", ErrValidation)
}
@@ -210,7 +212,7 @@ func ddbSync(ctx context.Context, ddb DynamoDBBackend, cfg *DynamoDBDataSourceCo
var next any
if len(page.LastKey) > 0 {
- b, _ := json.Marshal(syncToken{Key: page.LastKey, StartedAt: startedAt})
+ b, _ := json.Marshal(syncToken{Key: page.LastKey, StartedAt: startedAt}, jsonx.V1())
next = base64.RawURLEncoding.EncodeToString(b)
}
diff --git a/services/appsync/domain_names_test.go b/services/appsync/domain_names_test.go
index dafcd64472..9fec038a01 100644
--- a/services/appsync/domain_names_test.go
+++ b/services/appsync/domain_names_test.go
@@ -1,7 +1,7 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -352,7 +353,7 @@ func TestListDomainNames_Pagination(t *testing.T) {
NextToken string `json:"nextToken"`
DomainNameConfigs []map[string]any `json:"domainNameConfigs"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.DomainNameConfigs, tt.wantLen)
if tt.wantNextToken {
assert.NotEmpty(t, out.NextToken)
diff --git a/services/appsync/ds_external.go b/services/appsync/ds_external.go
index e352511b62..0719aafb70 100644
--- a/services/appsync/ds_external.go
+++ b/services/appsync/ds_external.go
@@ -2,13 +2,15 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -194,7 +196,7 @@ func eventBridgeEntry(m map[string]any, busARN string) (EventBridgeEntry, error)
e.Detail = d
case nil:
default:
- raw, err := json.Marshal(d)
+ raw, err := json.Marshal(d, jsonx.V1())
if err != nil {
return e, fmt.Errorf("%w: event detail is not JSON-serializable", ErrValidation)
}
@@ -275,7 +277,7 @@ func parseSearchBody(body []byte) any {
}
var v any
- if err := json.Unmarshal(body, &v); err != nil {
+ if err := json.Unmarshal(body, &v, jsonx.V1()); err != nil {
return string(body)
}
@@ -313,7 +315,7 @@ func openSearchRequest(endpoint string, m map[string]any) (OpenSearchRequest, er
case string:
req.Body = []byte(body)
default:
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
if err != nil {
return req, fmt.Errorf("%w: request body is not JSON-serializable", ErrValidation)
}
diff --git a/services/appsync/ds_external_test.go b/services/appsync/ds_external_test.go
index 00b24d8e50..b25e448896 100644
--- a/services/appsync/ds_external_test.go
+++ b/services/appsync/ds_external_test.go
@@ -2,7 +2,8 @@ package appsync_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -142,7 +144,7 @@ func TestEventBridgeDataSource_Request(t *testing.T) {
assert.JSONEq(t, `{"k":"v"}`, eb.entries[0].Detail)
assert.Equal(t, []string{"r1"}, eb.entries[0].Resources)
- raw, _ := json.Marshal(out["run"])
+ raw, _ := json.Marshal(out["run"], jsonx.V1())
assert.JSONEq(t, `{"Entries":[{"EventId":"ev-1"}],"FailedEntryCount":0}`, string(raw))
}
@@ -165,7 +167,7 @@ func TestRDSHelpers(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- ctxJSON, _ := json.Marshal(map[string]any{"result": result})
+ ctxJSON, _ := json.Marshal(map[string]any{"result": result}, jsonx.V1())
out, err := b.EvaluateMappingTemplate(tt.template, string(ctxJSON))
require.NoError(t, err)
assert.JSONEq(t, tt.want, out)
@@ -218,16 +220,16 @@ export function response(ctx) { return ctx.result; }`
t.Run(tt.kind, func(t *testing.T) {
t.Parallel()
- ctxJSON, _ := json.Marshal(map[string]any{"arguments": map[string]any{"kind": tt.kind}})
+ ctxJSON, _ := json.Marshal(map[string]any{"arguments": map[string]any{"kind": tt.kind}}, jsonx.V1())
out, err := b.EvaluateCode(code, string(ctxJSON), "request", "APPSYNC_JS")
require.NoError(t, err)
var got struct {
- Version string `json:"version"`
- Statements json.RawMessage `json:"statements"`
- VariableMap json.RawMessage `json:"variableMap"`
+ Version string `json:"version"`
+ Statements jsontext.Value `json:"statements"`
+ VariableMap jsontext.Value `json:"variableMap"`
}
- require.NoError(t, json.Unmarshal([]byte(out), &got))
+ require.NoError(t, json.Unmarshal([]byte(out), &got, jsonx.V1()))
assert.JSONEq(t, tt.wantStmts, string(got.Statements))
assert.JSONEq(t, tt.wantVars, string(got.VariableMap))
assert.Equal(t, "2018-05-29", got.Version)
diff --git a/services/appsync/ds_http.go b/services/appsync/ds_http.go
index d47732aafa..dd6b0113a5 100644
--- a/services/appsync/ds_http.go
+++ b/services/appsync/ds_http.go
@@ -5,7 +5,7 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -17,6 +17,7 @@ import (
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -147,7 +148,7 @@ func buildHTTPRequest(ctx context.Context, endpoint string, req map[string]any)
case string:
payload = strings.NewReader(b)
default:
- raw, mErr := json.Marshal(b)
+ raw, mErr := json.Marshal(b, jsonx.V1())
if mErr != nil {
return nil, fmt.Errorf("%w: request body is not JSON-serializable", ErrValidation)
}
diff --git a/services/appsync/ds_rds.go b/services/appsync/ds_rds.go
index 4a0d475284..e02fd9545a 100644
--- a/services/appsync/ds_rds.go
+++ b/services/appsync/ds_rds.go
@@ -2,8 +2,10 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// invokeRDSDataSource runs the mapped statements through the RDS Data API and
@@ -51,7 +53,7 @@ func invokeRDSDataSource(
results = append(results, out)
}
- raw, err := json.Marshal(map[string]any{"sqlStatementResults": results})
+ raw, err := json.Marshal(map[string]any{"sqlStatementResults": results}, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("rds result is not JSON-serializable: %w", err)
}
diff --git a/services/appsync/evaluation.go b/services/appsync/evaluation.go
index 1e53352f50..0587453825 100644
--- a/services/appsync/evaluation.go
+++ b/services/appsync/evaluation.go
@@ -1,9 +1,11 @@
package appsync
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// evalContext decodes an EvaluateMappingTemplate/EvaluateCode context JSON
@@ -12,7 +14,7 @@ func evalContext(contextJSON string) (map[string]any, error) {
ctx := map[string]any{}
if strings.TrimSpace(contextJSON) != "" {
- if err := json.Unmarshal([]byte(contextJSON), &ctx); err != nil {
+ if err := json.Unmarshal([]byte(contextJSON), &ctx, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid context JSON", ErrValidation)
}
}
@@ -116,7 +118,7 @@ func (b *InMemoryBackend) EvaluateCodeLogs(code, contextJSON, function, runtime
return "", out.Logs, err
}
- raw, err := json.Marshal(out.Value)
+ raw, err := json.Marshal(out.Value, jsonx.V1())
if err != nil {
return "", out.Logs, fmt.Errorf("%w: result is not JSON-serializable", ErrValidation)
}
diff --git a/services/appsync/evaluation_logs_test.go b/services/appsync/evaluation_logs_test.go
index 0179c20ce8..5c6348b63e 100644
--- a/services/appsync/evaluation_logs_test.go
+++ b/services/appsync/evaluation_logs_test.go
@@ -1,12 +1,14 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// AppSync developer guide, "Logging utilities": $util.log.info/error output is
@@ -49,7 +51,7 @@ func TestEvaluateMappingTemplate_Logs(t *testing.T) {
Result string `json:"evaluationResult"`
Logs []string `json:"logs"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantLogs, out.Logs)
assert.Equal(t, tt.wantErr, out.Error != nil)
})
diff --git a/services/appsync/export_test.go b/services/appsync/export_test.go
index 9948b998af..104750e588 100644
--- a/services/appsync/export_test.go
+++ b/services/appsync/export_test.go
@@ -2,7 +2,9 @@
// These exports exist solely for testing and must not be called from production code.
package appsync
-import "encoding/json"
+import (
+ "encoding/json/jsontext"
+)
// RenderVTL is the exported test hook for the VTL template renderer.
// It is only exposed for package-level tests and should not be used in production code.
@@ -22,13 +24,13 @@ func ToDynamoDBJSON(val any) string {
// the explicit apiKeys handling, version guard, and Tags-close housekeeping
// that the production persistence.go's Snapshot/Restore layer adds on top --
// without needing an unexported field accessor.
-func (b *InMemoryBackend) SnapshotRegistry() (map[string]json.RawMessage, error) {
+func (b *InMemoryBackend) SnapshotRegistry() (map[string]jsontext.Value, error) {
return b.registry.SnapshotAll()
}
// RestoreRegistry is the exported test hook for the backend's internal
// store.Registry.RestoreAll. See SnapshotRegistry.
-func (b *InMemoryBackend) RestoreRegistry(data map[string]json.RawMessage) error {
+func (b *InMemoryBackend) RestoreRegistry(data map[string]jsontext.Value) error {
return b.registry.RestoreAll(data)
}
diff --git a/services/appsync/functions_test.go b/services/appsync/functions_test.go
index ef896ef7b7..2169b7da32 100644
--- a/services/appsync/functions_test.go
+++ b/services/appsync/functions_test.go
@@ -1,7 +1,7 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -513,7 +514,7 @@ func TestListFunctions_Pagination(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var apiOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &apiOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &apiOut, jsonx.V1()))
apiID := apiOut["graphqlApi"].(map[string]any)["apiId"].(string)
rec = doRequest(t, h, http.MethodPost, fmt.Sprintf("/v1/apis/%s/datasources", apiID), map[string]any{
@@ -561,7 +562,7 @@ func TestListFunctions_Pagination(t *testing.T) {
NextToken string `json:"nextToken"`
Functions []map[string]any `json:"functions"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Functions, tt.wantLen)
if tt.wantNextToken {
assert.NotEmpty(t, out.NextToken)
diff --git a/services/appsync/graphql.go b/services/appsync/graphql.go
index 2754f28a40..4cf0f01344 100644
--- a/services/appsync/graphql.go
+++ b/services/appsync/graphql.go
@@ -3,7 +3,8 @@ package appsync
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"slices"
@@ -12,6 +13,8 @@ import (
"github.com/vektah/gqlparser/v2"
"github.com/vektah/gqlparser/v2/ast"
"github.com/vektah/gqlparser/v2/validator/rules"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -380,12 +383,12 @@ func resolveValue(val *ast.Value, variables map[string]any) any {
return val.Raw == "true"
case ast.IntValue:
var i float64
- _ = json.Unmarshal([]byte(val.Raw), &i)
+ _ = json.Unmarshal([]byte(val.Raw), &i, jsonx.V1())
return i
case ast.FloatValue:
var f float64
- _ = json.Unmarshal([]byte(val.Raw), &f)
+ _ = json.Unmarshal([]byte(val.Raw), &f, jsonx.V1())
return f
case ast.StringValue, ast.BlockValue, ast.EnumValue:
@@ -461,7 +464,7 @@ func functionMappingUnit(f *Function, source any) mappingUnit {
// object -- callers that require one check the decoded type themselves).
func unmarshalOrRaw(s string) any {
var v any
- if err := json.Unmarshal([]byte(s), &v); err == nil {
+ if err := json.Unmarshal([]byte(s), &v, jsonx.V1()); err == nil {
return v
}
@@ -634,7 +637,7 @@ func invokeLambdaDataSource(
var lambdaResult any
- if jsonErr := json.Unmarshal(result, &lambdaResult); jsonErr != nil {
+ if jsonErr := json.Unmarshal(result, &lambdaResult, jsonx.V1()); jsonErr != nil {
lambdaResult = string(result)
}
@@ -660,10 +663,10 @@ func buildLambdaEvent(mu mappingUnit, args map[string]any, prevResult any) ([]by
}
}
- return json.Marshal(value)
+ return json.Marshal(value, jsonx.V1())
}
- return json.Marshal(defaultLambdaEvent(mu, args))
+ return json.Marshal(defaultLambdaEvent(mu, args), jsonx.V1())
}
func defaultLambdaEvent(mu mappingUnit, args map[string]any) map[string]any {
@@ -747,10 +750,9 @@ func invokeNoneDataSource(mu mappingUnit, args map[string]any, prevResult any) (
// parseGraphQLRequest parses the GraphQL request body.
func parseGraphQLRequest(body []byte) (*graphqlRequest, error) {
var req graphqlRequest
- dec := json.NewDecoder(bytes.NewReader(body))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(body))
- if err := dec.Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(dec, &req, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, fmt.Errorf("invalid GraphQL request: %w", err)
}
diff --git a/services/appsync/graphql_apis_config_test.go b/services/appsync/graphql_apis_config_test.go
index 7effb0871b..fe3ab95b67 100644
--- a/services/appsync/graphql_apis_config_test.go
+++ b/services/appsync/graphql_apis_config_test.go
@@ -1,7 +1,7 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -659,7 +660,7 @@ func TestListGraphqlAPIs_Pagination(t *testing.T) {
NextToken string `json:"nextToken"`
GraphqlAPIs []map[string]any `json:"graphqlApis"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.GraphqlAPIs, tt.wantLen)
if tt.wantNextToken {
assert.NotEmpty(t, out.NextToken)
@@ -703,7 +704,7 @@ func TestListGraphqlAPIs_FullPagination(t *testing.T) {
NextToken string `json:"nextToken"`
GraphqlAPIs []map[string]any `json:"graphqlApis"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.LessOrEqual(t, len(out.GraphqlAPIs), 2)
for _, api := range out.GraphqlAPIs {
diff --git a/services/appsync/handler_api_keys.go b/services/appsync/handler_api_keys.go
index 8d1ee3dddc..24fa7f547b 100644
--- a/services/appsync/handler_api_keys.go
+++ b/services/appsync/handler_api_keys.go
@@ -2,13 +2,14 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleAPIKeys handles /v1/apis/{apiId}/apikeys[/{keyId}].
@@ -52,7 +53,7 @@ func (h *Handler) createAPIKey(ctx context.Context, c *echo.Context, apiID strin
Expires int64 `json:"expires"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -105,7 +106,7 @@ func (h *Handler) updateAPIKey(ctx context.Context, c *echo.Context, apiID, keyI
Expires int64 `json:"expires"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/appsync/handler_api_keys_test.go b/services/appsync/handler_api_keys_test.go
index 2dedfe77d7..aea0516b81 100644
--- a/services/appsync/handler_api_keys_test.go
+++ b/services/appsync/handler_api_keys_test.go
@@ -1,7 +1,8 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -56,7 +58,7 @@ func TestHandler_CreateApiKey(t *testing.T) {
if tt.wantKeyID {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
key, ok := resp["apiKey"].(map[string]any)
require.True(t, ok)
assert.NotEmpty(t, key["id"])
@@ -75,7 +77,7 @@ func TestHandler_CreateAPIKey_Da2Prefix(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
gqlAPI := resp["graphqlApi"].(map[string]any)
apiID := gqlAPI["apiId"].(string)
@@ -84,7 +86,7 @@ func TestHandler_CreateAPIKey_Da2Prefix(t *testing.T) {
require.Equal(t, http.StatusCreated, rec2.Code)
var keyResp map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&keyResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &keyResp, jsonx.V1()))
apiKey := keyResp["apiKey"].(map[string]any)
keyID := apiKey["id"].(string)
require.GreaterOrEqual(t, len(keyID), 4, "key ID must be at least 4 characters")
@@ -107,7 +109,7 @@ func TestHandler_ListApiKeys(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
keys := resp["apiKeys"].([]any)
assert.Len(t, keys, 2)
}
@@ -146,7 +148,7 @@ func TestHandler_UpdateApiKey(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
apiKey := resp["apiKey"].(map[string]any)
assert.Equal(t, "updated", apiKey["description"])
}
diff --git a/services/appsync/handler_caching.go b/services/appsync/handler_caching.go
index 848204b6d0..69be67d11f 100644
--- a/services/appsync/handler_caching.go
+++ b/services/appsync/handler_caching.go
@@ -2,12 +2,13 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleAPICaches handles /v1/apis/{apiId}/ApiCaches[/entries].
@@ -56,7 +57,7 @@ func (h *Handler) createAPICache(ctx context.Context, c *echo.Context, apiID str
}
var cache APICache
- if jsonErr := json.Unmarshal(body, &cache); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &cache, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -95,7 +96,7 @@ func (h *Handler) updateAPICache(ctx context.Context, c *echo.Context, apiID str
}
var cache APICache
- if jsonErr := json.Unmarshal(body, &cache); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &cache, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/appsync/handler_caching_test.go b/services/appsync/handler_caching_test.go
index de845829bc..b24c8548a2 100644
--- a/services/appsync/handler_caching_test.go
+++ b/services/appsync/handler_caching_test.go
@@ -1,13 +1,15 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -102,7 +104,7 @@ func TestHandler_CreateAPICache_Validation(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
gqlAPI := resp["graphqlApi"].(map[string]any)
apiID := gqlAPI["apiId"].(string)
@@ -130,7 +132,7 @@ func TestHandler_GetApiCache(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotNil(t, resp["apiCache"])
}
@@ -174,7 +176,7 @@ func TestHandler_UpdateApiCache(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
cache := resp["apiCache"].(map[string]any)
assert.Equal(t, "LARGE", cache["type"])
}
diff --git a/services/appsync/handler_channel_namespaces.go b/services/appsync/handler_channel_namespaces.go
index 13aaf86f9d..86182e4ab6 100644
--- a/services/appsync/handler_channel_namespaces.go
+++ b/services/appsync/handler_channel_namespaces.go
@@ -2,13 +2,14 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleChannelNamespacesCollection(ctx context.Context, c *echo.Context, apiID string) error {
@@ -50,7 +51,7 @@ func (h *Handler) createChannelNamespace(ctx context.Context, c *echo.Context, a
SubscribeAuthModes []AuthMode `json:"subscribeAuthModes"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -116,7 +117,7 @@ func (h *Handler) updateChannelNamespace(ctx context.Context, c *echo.Context, a
SubscribeAuthModes []AuthMode `json:"subscribeAuthModes"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/appsync/handler_channel_namespaces_test.go b/services/appsync/handler_channel_namespaces_test.go
index 2a4a1c8ade..83e98cef87 100644
--- a/services/appsync/handler_channel_namespaces_test.go
+++ b/services/appsync/handler_channel_namespaces_test.go
@@ -1,13 +1,15 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -64,7 +66,7 @@ func TestHandler_CreateChannelNamespace(t *testing.T) {
if tt.wantName != "" {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
ns, ok := resp["channelNamespace"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantName, ns["name"])
@@ -83,7 +85,7 @@ func TestHandler_ChannelNamespace_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec1.Code)
var apiResp map[string]any
- require.NoError(t, json.NewDecoder(rec1.Body).Decode(&apiResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec1.Body), &apiResp, jsonx.V1()))
apiID := apiResp["api"].(map[string]any)["apiId"].(string)
// Create namespace.
@@ -96,7 +98,7 @@ func TestHandler_ChannelNamespace_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var listResp map[string]any
- require.NoError(t, json.NewDecoder(rec3.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec3.Body), &listResp, jsonx.V1()))
items := listResp["channelNamespaces"].([]any)
assert.Len(t, items, 1)
@@ -105,7 +107,7 @@ func TestHandler_ChannelNamespace_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec4.Code)
var getResp map[string]any
- require.NoError(t, json.NewDecoder(rec4.Body).Decode(&getResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec4.Body), &getResp, jsonx.V1()))
ns := getResp["channelNamespace"].(map[string]any)
assert.Equal(t, "ns1", ns["name"])
diff --git a/services/appsync/handler_data_sources.go b/services/appsync/handler_data_sources.go
index d890c75824..6347308e2e 100644
--- a/services/appsync/handler_data_sources.go
+++ b/services/appsync/handler_data_sources.go
@@ -2,13 +2,14 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleDataSources handles /v1/apis/{apiId}/datasources[/{name}].
@@ -50,7 +51,7 @@ func (h *Handler) createDataSource(ctx context.Context, c *echo.Context, apiID s
}
var ds DataSource
- if jsonErr := json.Unmarshal(body, &ds); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &ds, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -109,7 +110,7 @@ func (h *Handler) updateDataSource(ctx context.Context, c *echo.Context, apiID,
}
var ds DataSource
- if jsonErr := json.Unmarshal(body, &ds); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &ds, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -180,7 +181,7 @@ func (h *Handler) startDataSourceIntrospection(ctx context.Context, c *echo.Cont
RDSDataAPIConfig *RDSDataAPIConfig `json:"rdsDataApiConfig"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/appsync/handler_data_sources_test.go b/services/appsync/handler_data_sources_test.go
index 0ff16444cc..11f27bad32 100644
--- a/services/appsync/handler_data_sources_test.go
+++ b/services/appsync/handler_data_sources_test.go
@@ -1,13 +1,15 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -143,7 +145,7 @@ func TestHandler_DataSourceIntrospection_StartThenGet(t *testing.T) {
IntrospectionID string `json:"introspectionId"`
IntrospectionStatus string `json:"introspectionStatus"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
assert.NotEmpty(t, startOut.IntrospectionID)
assert.Equal(t, "SUCCESS", startOut.IntrospectionStatus)
@@ -157,7 +159,7 @@ func TestHandler_DataSourceIntrospection_StartThenGet(t *testing.T) {
Models []any `json:"models"`
} `json:"introspectionResult"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, startOut.IntrospectionID, getOut.IntrospectionID)
assert.Equal(t, "SUCCESS", getOut.IntrospectionStatus)
assert.Empty(t, getOut.IntrospectionResult.Models)
@@ -202,7 +204,7 @@ func TestHandler_CreateAndGetDataSource(t *testing.T) {
if tt.wantName != "" {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
ds, ok := resp["dataSource"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantName, ds["name"])
@@ -233,7 +235,7 @@ func TestHandler_ListDataSources(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
dss, ok := resp["dataSources"].([]any)
require.True(t, ok)
assert.Len(t, dss, 1)
@@ -275,7 +277,7 @@ func TestHandler_UpdateDataSource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
ds := resp["dataSource"].(map[string]any)
assert.Equal(t, "updated", ds["description"])
diff --git a/services/appsync/handler_domain_names.go b/services/appsync/handler_domain_names.go
index fdf8cdbd7e..77a4e4a21d 100644
--- a/services/appsync/handler_domain_names.go
+++ b/services/appsync/handler_domain_names.go
@@ -2,13 +2,14 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleDomainNames handles /v1/domainnames[/{domainName}[/apiassociation]].
@@ -81,7 +82,7 @@ func (h *Handler) createDomainName(ctx context.Context, c *echo.Context) error {
Description string `json:"description"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -112,7 +113,7 @@ func (h *Handler) associateAPI(ctx context.Context, c *echo.Context, domainName
APIID string `json:"apiId"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -189,7 +190,7 @@ func (h *Handler) updateDomainName(ctx context.Context, c *echo.Context, domainN
CertificateARN string `json:"certificateArn"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/appsync/handler_domain_names_test.go b/services/appsync/handler_domain_names_test.go
index 8246a2a0ee..fcd7659d1c 100644
--- a/services/appsync/handler_domain_names_test.go
+++ b/services/appsync/handler_domain_names_test.go
@@ -1,13 +1,15 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -53,7 +55,7 @@ func TestHandler_CreateDomainName(t *testing.T) {
if tt.wantDomainName != "" {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
cfg, ok := resp["domainNameConfig"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantDomainName, cfg["domainName"])
@@ -147,7 +149,7 @@ func TestHandler_DomainNameCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var listResp map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &listResp, jsonx.V1()))
dns := listResp["domainNameConfigs"].([]any)
assert.Len(t, dns, 1)
@@ -198,7 +200,7 @@ func TestHandler_DisassociateAPI(t *testing.T) {
require.Equal(t, http.StatusCreated, rec1.Code)
var apiResp map[string]any
- require.NoError(t, json.NewDecoder(rec1.Body).Decode(&apiResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec1.Body), &apiResp, jsonx.V1()))
apiID := apiResp["graphqlApi"].(map[string]any)["apiId"].(string)
doRequest(t, h, http.MethodPost, "/v1/domainnames/api.example.com/apiassociation",
@@ -228,7 +230,7 @@ func TestHandler_UpdateDomainName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
dn := resp["domainNameConfig"].(map[string]any)
assert.Equal(t, "updated description", dn["description"])
}
diff --git a/services/appsync/handler_evaluation.go b/services/appsync/handler_evaluation.go
index 645759d8ec..919358e7cb 100644
--- a/services/appsync/handler_evaluation.go
+++ b/services/appsync/handler_evaluation.go
@@ -2,13 +2,14 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleDataplaneEvaluations handles /v1/dataplane-evaluations/{template|code}.
@@ -43,7 +44,7 @@ func (h *Handler) evaluateMappingTemplate(ctx context.Context, c *echo.Context)
Context string `json:"context"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -75,7 +76,7 @@ func (h *Handler) evaluateCode(ctx context.Context, c *echo.Context) error {
} `json:"runtime"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/appsync/handler_events.go b/services/appsync/handler_events.go
index 7b2b186f02..12e324b94d 100644
--- a/services/appsync/handler_events.go
+++ b/services/appsync/handler_events.go
@@ -2,13 +2,14 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleV2APIs handles /v2/apis[/{apiId}[/channelNamespaces[/{name}]]].
@@ -69,7 +70,7 @@ func (h *Handler) createAPI(ctx context.Context, c *echo.Context) error {
OwnerContact string `json:"ownerContact"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -138,7 +139,7 @@ func (h *Handler) updateAPI(ctx context.Context, c *echo.Context, apiID string)
OwnerContact string `json:"ownerContact"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/appsync/handler_events_test.go b/services/appsync/handler_events_test.go
index 909c7d0c20..d082ea7119 100644
--- a/services/appsync/handler_events_test.go
+++ b/services/appsync/handler_events_test.go
@@ -1,12 +1,15 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateApi(t *testing.T) {
@@ -44,7 +47,7 @@ func TestHandler_CreateApi(t *testing.T) {
if tt.wantName != "" {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
api, ok := resp["api"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantName, api["name"])
@@ -64,7 +67,7 @@ func TestHandler_EventAPI_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
apiID := createResp["api"].(map[string]any)["apiId"].(string)
// List APIs.
@@ -72,7 +75,7 @@ func TestHandler_EventAPI_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var listResp map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &listResp, jsonx.V1()))
items := listResp["apis"].([]any)
assert.Len(t, items, 1)
@@ -81,7 +84,7 @@ func TestHandler_EventAPI_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var getResp map[string]any
- require.NoError(t, json.NewDecoder(rec3.Body).Decode(&getResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec3.Body), &getResp, jsonx.V1()))
assert.Equal(t, "MyEventAPI", getResp["api"].(map[string]any)["name"])
// Delete API.
@@ -97,7 +100,7 @@ func TestHandler_EventAPI_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec6.Code)
var listResp2 map[string]any
- require.NoError(t, json.NewDecoder(rec6.Body).Decode(&listResp2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec6.Body), &listResp2, jsonx.V1()))
assert.Empty(t, listResp2["apis"])
}
@@ -129,7 +132,7 @@ func TestHandler_UpdateAPI(t *testing.T) {
require.Equal(t, http.StatusCreated, rec1.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec1.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec1.Body), &createResp, jsonx.V1()))
apiID := createResp["api"].(map[string]any)["apiId"].(string)
rec2 := doRequest(t, h, http.MethodPut, "/v2/apis/"+apiID,
@@ -137,7 +140,7 @@ func TestHandler_UpdateAPI(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &resp, jsonx.V1()))
api := resp["api"].(map[string]any)
assert.Equal(t, "UpdatedAPI", api["name"])
assert.Equal(t, "ops@example.com", api["ownerContact"])
diff --git a/services/appsync/handler_functions.go b/services/appsync/handler_functions.go
index 34674bbd09..e9833ee2c9 100644
--- a/services/appsync/handler_functions.go
+++ b/services/appsync/handler_functions.go
@@ -2,13 +2,14 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleFunctions handles /v1/apis/{apiId}/functions[/{functionId}[/resolvers]].
@@ -61,7 +62,7 @@ func (h *Handler) createFunction(ctx context.Context, c *echo.Context, apiID str
}
var f Function
- if jsonErr := json.Unmarshal(body, &f); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &f, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -124,7 +125,7 @@ func (h *Handler) updateFunction(ctx context.Context, c *echo.Context, apiID, fu
}
var f Function
- if jsonErr := json.Unmarshal(body, &f); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &f, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/appsync/handler_functions_test.go b/services/appsync/handler_functions_test.go
index 8cf15cf639..18ad8666cc 100644
--- a/services/appsync/handler_functions_test.go
+++ b/services/appsync/handler_functions_test.go
@@ -1,13 +1,15 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -78,7 +80,7 @@ func TestHandler_CreateFunction(t *testing.T) {
if tt.wantFuncName != "" {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
fn, ok := resp["functionConfiguration"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantFuncName, fn["name"])
@@ -102,7 +104,7 @@ func TestHandler_ListFunctions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
fns := resp["functions"].([]any)
assert.Len(t, fns, 1)
}
@@ -121,7 +123,7 @@ func TestHandler_GetFunction(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotNil(t, resp["functionConfiguration"])
}
@@ -158,7 +160,7 @@ func TestHandler_UpdateFunction(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
fnConfig := resp["functionConfiguration"].(map[string]any)
assert.Equal(t, "updated fn", fnConfig["description"])
}
diff --git a/services/appsync/handler_graphql_apis.go b/services/appsync/handler_graphql_apis.go
index 8b914b8b4c..7721458136 100644
--- a/services/appsync/handler_graphql_apis.go
+++ b/services/appsync/handler_graphql_apis.go
@@ -2,7 +2,7 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strconv"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// createGraphqlAPI handles POST /v1/apis.
@@ -39,7 +40,7 @@ func (h *Handler) createGraphqlAPI(ctx context.Context, c *echo.Context) error {
XrayEnabled bool `json:"xrayEnabled"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -202,7 +203,7 @@ func (h *Handler) updateGraphqlAPI(ctx context.Context, c *echo.Context, apiID s
ResolverCountLimit int32 `json:"resolverCountLimit"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -255,7 +256,7 @@ func (h *Handler) handleEnvironmentVariables(ctx context.Context, c *echo.Contex
EnvironmentVariables map[string]string `json:"environmentVariables"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/appsync/handler_graphql_apis_test.go b/services/appsync/handler_graphql_apis_test.go
index c4065cbf7e..a6f3a8c63a 100644
--- a/services/appsync/handler_graphql_apis_test.go
+++ b/services/appsync/handler_graphql_apis_test.go
@@ -1,7 +1,8 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -61,7 +63,7 @@ func TestHandler_CreateAndGetGraphqlAPI(t *testing.T) {
if tt.wantAPIName != "" {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
api, ok := resp["graphqlApi"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantAPIName, api["name"])
@@ -162,7 +164,7 @@ func TestHandler_GraphQLExecution(t *testing.T) {
if tt.wantKey != "" {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
if _, hasErrors := resp["errors"]; !hasErrors {
data, ok := resp["data"].(map[string]any)
@@ -208,7 +210,7 @@ func TestHandler_ListGraphqlAPIs(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
apis, ok := resp["graphqlApis"].([]any)
require.True(t, ok)
assert.Len(t, apis, tt.wantCount)
@@ -348,7 +350,7 @@ func TestHandler_UpdateGraphqlAPI(t *testing.T) {
if tt.wantName != "" && tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
gqlAPI := resp["graphqlApi"].(map[string]any)
assert.Equal(t, tt.wantName, gqlAPI["name"])
}
@@ -366,7 +368,7 @@ func TestHandler_CreateAndUpdateGraphqlAPI_OwnerContact(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createResp, jsonx.V1()))
createdAPI := createResp["graphqlApi"].(map[string]any)
assert.Equal(t, "team-a@example.com", createdAPI["ownerContact"])
apiID := createdAPI["apiId"].(string)
@@ -375,7 +377,7 @@ func TestHandler_CreateAndUpdateGraphqlAPI_OwnerContact(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&getResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &getResp, jsonx.V1()))
assert.Equal(t, "team-a@example.com", getResp["graphqlApi"].(map[string]any)["ownerContact"])
updateRec := doRequest(t, h, http.MethodPatch, "/v1/apis/"+apiID,
@@ -383,7 +385,7 @@ func TestHandler_CreateAndUpdateGraphqlAPI_OwnerContact(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateResp map[string]any
- require.NoError(t, json.NewDecoder(updateRec.Body).Decode(&updateResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updateRec.Body), &updateResp, jsonx.V1()))
assert.Equal(t, "team-b@example.com", updateResp["graphqlApi"].(map[string]any)["ownerContact"])
}
@@ -399,7 +401,7 @@ func TestHandler_EnvironmentVariables(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var getResp map[string]any
- require.NoError(t, json.NewDecoder(rec1.Body).Decode(&getResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec1.Body), &getResp, jsonx.V1()))
assert.Empty(t, getResp["environmentVariables"])
// Put env vars.
@@ -408,7 +410,7 @@ func TestHandler_EnvironmentVariables(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var putResp map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&putResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &putResp, jsonx.V1()))
envVars := putResp["environmentVariables"].(map[string]any)
assert.Equal(t, "value1", envVars["KEY1"])
assert.Equal(t, "value2", envVars["KEY2"])
@@ -418,7 +420,7 @@ func TestHandler_EnvironmentVariables(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var getResp2 map[string]any
- require.NoError(t, json.NewDecoder(rec3.Body).Decode(&getResp2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec3.Body), &getResp2, jsonx.V1()))
envVars2 := getResp2["environmentVariables"].(map[string]any)
assert.Equal(t, "value1", envVars2["KEY1"])
}
@@ -480,7 +482,7 @@ func TestHandler_CreateGraphqlAPI_XrayEnabled(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
apiObj := resp["graphqlApi"].(map[string]any)
assert.NotEmpty(t, apiObj["apiId"])
@@ -540,7 +542,7 @@ func TestHandler_UpdateGraphqlAPI_XrayEnabled(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
apiObj := resp["graphqlApi"].(map[string]any)
// xrayEnabled omits false in JSON (omitempty), so nil == false.
gotXray, _ := apiObj["xrayEnabled"].(bool)
@@ -560,7 +562,7 @@ func TestHandler_ListGraphqlAPIs_ApiTypeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
apis := resp["graphqlApis"].([]any)
assert.Len(t, apis, 1)
}
diff --git a/services/appsync/handler_resolvers.go b/services/appsync/handler_resolvers.go
index bdd749bae2..f3d60a21f4 100644
--- a/services/appsync/handler_resolvers.go
+++ b/services/appsync/handler_resolvers.go
@@ -2,13 +2,14 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleTypeResolvers handles /v1/apis/{apiId}/types/{typeName}/resolvers[/{fieldName}].
@@ -54,7 +55,7 @@ func (h *Handler) createResolver(ctx context.Context, c *echo.Context, apiID, ty
}
var r Resolver
- if jsonErr := json.Unmarshal(body, &r); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &r, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -113,7 +114,7 @@ func (h *Handler) updateResolver(ctx context.Context, c *echo.Context, apiID, ty
}
var r Resolver
- if jsonErr := json.Unmarshal(body, &r); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &r, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/appsync/handler_resolvers_test.go b/services/appsync/handler_resolvers_test.go
index 66982b80fe..f2767c4929 100644
--- a/services/appsync/handler_resolvers_test.go
+++ b/services/appsync/handler_resolvers_test.go
@@ -1,13 +1,15 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -57,7 +59,7 @@ func TestHandler_CreateAndGetResolver(t *testing.T) {
if tt.wantFieldName != "" {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
r, ok := resp["resolver"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantFieldName, r["fieldName"])
@@ -88,7 +90,7 @@ func TestHandler_ListResolvers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
res, ok := resp["resolvers"].([]any)
require.True(t, ok)
assert.Len(t, res, 1)
@@ -123,7 +125,7 @@ func TestHandler_UpdateResolver(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
resolver := resp["resolver"].(map[string]any)
assert.Equal(t, "$util.toJson($context.args)", resolver["requestMappingTemplate"])
}
@@ -149,7 +151,7 @@ func TestHandler_ListResolversByFunction(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
resolvers := resp["resolvers"].([]any)
assert.Len(t, resolvers, 1)
assert.Equal(t, "getItem", resolvers[0].(map[string]any)["fieldName"])
diff --git a/services/appsync/handler_routing_test.go b/services/appsync/handler_routing_test.go
index 3519460c42..94f8ba41ed 100644
--- a/services/appsync/handler_routing_test.go
+++ b/services/appsync/handler_routing_test.go
@@ -1,7 +1,8 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -924,7 +926,7 @@ func Test_TagResource_RealPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var listResp map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &listResp, jsonx.V1()))
tagMap, ok := listResp["tags"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "prod", tagMap["env"])
@@ -934,7 +936,7 @@ func Test_TagResource_RealPath(t *testing.T) {
require.Equal(t, http.StatusNoContent, rec3.Code)
rec4 := doRequest(t, h, http.MethodGet, tagPath, nil)
- require.NoError(t, json.NewDecoder(rec4.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec4.Body), &listResp, jsonx.V1()))
assert.Empty(t, listResp["tags"])
}
@@ -956,7 +958,7 @@ func Test_TagResource_RealPath_EventAPI(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &resp, jsonx.V1()))
tagMap, ok := resp["tags"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "core", tagMap["team"])
@@ -994,7 +996,7 @@ func Test_ApiCache_RealPaths(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
cache := resp["apiCache"].(map[string]any)
assert.Equal(t, "LARGE", cache["type"])
@@ -1043,7 +1045,7 @@ func Test_ListSourceApiAssociations_ByAPIID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
// The real AWS SDK's ListSourceApiAssociationsOutput wraps the list under
// "sourceApiAssociationSummaries", not "sourceApiAssociations" (the latter is only
// the URL path segment name).
@@ -1066,7 +1068,7 @@ func Test_ListApis_ResponseKey(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
apis, ok := resp["apis"].([]any)
require.True(t, ok, "response must wrap the list under \"apis\", got keys %v", resp)
assert.Len(t, apis, 1)
diff --git a/services/appsync/handler_schema.go b/services/appsync/handler_schema.go
index a6d2bbfaeb..bdbbc0c0e4 100644
--- a/services/appsync/handler_schema.go
+++ b/services/appsync/handler_schema.go
@@ -3,12 +3,13 @@ package appsync
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleSchemaCreation handles /v1/apis/{apiId}/schemacreation.
@@ -34,7 +35,7 @@ func (h *Handler) startSchemaCreation(ctx context.Context, c *echo.Context, apiI
Definition string `json:"definition"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/appsync/handler_schema_test.go b/services/appsync/handler_schema_test.go
index 683d4e9658..53d4e75fa6 100644
--- a/services/appsync/handler_schema_test.go
+++ b/services/appsync/handler_schema_test.go
@@ -2,13 +2,15 @@ package appsync_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -61,7 +63,7 @@ func TestHandler_StartSchemaCreation(t *testing.T) {
if tt.wantStatus2 != "" {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, tt.wantStatus2, resp["status"])
}
})
@@ -79,7 +81,7 @@ func TestHandler_GetSchemaCreationStatus(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, string(appsync.SchemaStatusActive), resp["status"])
}
@@ -149,10 +151,10 @@ func TestHandler_GetIntrospectionSchema_Format(t *testing.T) {
checkBody: func(t *testing.T, body string) {
t.Helper()
- require.True(t, json.Valid([]byte(body)))
+ require.True(t, jsontext.Value([]byte(body)).IsValid(jsonx.V1()))
var doc map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &doc))
+ require.NoError(t, json.Unmarshal([]byte(body), &doc, jsonx.V1()))
data, ok := doc["data"].(map[string]any)
require.True(t, ok)
@@ -207,7 +209,7 @@ func TestHandler_GetIntrospectionSchema_InvalidSchema_JSON(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, "GraphQLSchemaException", resp["code"])
}
diff --git a/services/appsync/handler_schema_types.go b/services/appsync/handler_schema_types.go
index e73944cfab..7ffa23678f 100644
--- a/services/appsync/handler_schema_types.go
+++ b/services/appsync/handler_schema_types.go
@@ -2,13 +2,14 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleTypes handles /v1/apis/{apiId}/types[/{typeName}[/resolvers[/{fieldName}]]].
@@ -68,7 +69,7 @@ func (h *Handler) createTypeHandler(ctx context.Context, c *echo.Context, apiID
Format string `json:"format"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -149,7 +150,7 @@ func (h *Handler) updateType(ctx context.Context, c *echo.Context, apiID, typeNa
Format string `json:"format"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/appsync/handler_schema_types_test.go b/services/appsync/handler_schema_types_test.go
index 50fec249b2..d8ad2e54a0 100644
--- a/services/appsync/handler_schema_types_test.go
+++ b/services/appsync/handler_schema_types_test.go
@@ -1,7 +1,8 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -139,7 +141,7 @@ func TestHandler_CreateType(t *testing.T) {
if tt.wantName != "" {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
typ, ok := resp["type"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantName, typ["name"])
@@ -158,7 +160,7 @@ func TestHandler_CreateType_DuplicateRejected(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
gqlAPI := resp["graphqlApi"].(map[string]any)
apiID := gqlAPI["apiId"].(string)
@@ -187,7 +189,7 @@ func TestHandler_ListTypes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
types := resp["types"].([]any)
assert.Len(t, types, 1)
}
@@ -206,7 +208,7 @@ func TestHandler_GetType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotNil(t, resp["type"])
}
@@ -243,7 +245,7 @@ func TestHandler_UpdateType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
typeDef := resp["type"].(map[string]any)
assert.Contains(t, typeDef["definition"], "name")
}
diff --git a/services/appsync/handler_source_api_associations.go b/services/appsync/handler_source_api_associations.go
index 893f64aeeb..9f99b9f0a9 100644
--- a/services/appsync/handler_source_api_associations.go
+++ b/services/appsync/handler_source_api_associations.go
@@ -2,13 +2,14 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleSourceAPIs handles /v1/sourceApis/{sourceApiIdentifier}/mergedApiAssociations[/{assocId}].
@@ -86,7 +87,7 @@ func (h *Handler) associateMergedGraphqlAPI(ctx context.Context, c *echo.Context
}
var input associateAPIInput
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -173,7 +174,7 @@ func (h *Handler) associateSourceGraphqlAPI(ctx context.Context, c *echo.Context
}
var input associateAPIInput
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -237,7 +238,7 @@ func (h *Handler) updateSourceAPIAssociation(
Description string `json:"description"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/appsync/handler_source_api_associations_test.go b/services/appsync/handler_source_api_associations_test.go
index 9d5a9a80f2..d1cb7ecf57 100644
--- a/services/appsync/handler_source_api_associations_test.go
+++ b/services/appsync/handler_source_api_associations_test.go
@@ -1,7 +1,8 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -95,7 +97,7 @@ func TestHandler_AssociateMergedGraphqlApi(t *testing.T) {
assert.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assoc, ok := resp["sourceApiAssociation"].(map[string]any)
require.True(t, ok)
assert.NotEmpty(t, assoc["associationId"])
@@ -138,7 +140,7 @@ func TestHandler_AssociateSourceGraphqlApi(t *testing.T) {
assert.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assoc, ok := resp["sourceApiAssociation"].(map[string]any)
require.True(t, ok)
assert.NotEmpty(t, assoc["associationId"])
@@ -178,7 +180,7 @@ func TestHandler_SourceAPIAssociations_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec1.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec1.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec1.Body), &createResp, jsonx.V1()))
assocID := createResp["sourceApiAssociation"].(map[string]any)["associationId"].(string)
// List source API associations.
@@ -186,7 +188,7 @@ func TestHandler_SourceAPIAssociations_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var listResp map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &listResp, jsonx.V1()))
assocs := listResp["sourceApiAssociationSummaries"].([]any)
assert.Len(t, assocs, 1)
@@ -195,7 +197,7 @@ func TestHandler_SourceAPIAssociations_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var getResp map[string]any
- require.NoError(t, json.NewDecoder(rec3.Body).Decode(&getResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec3.Body), &getResp, jsonx.V1()))
assoc := getResp["sourceApiAssociation"].(map[string]any)
assert.Equal(t, mrgAPI.APIID, assoc["mergedApiId"])
@@ -225,7 +227,7 @@ func TestHandler_DisassociateMergedGraphqlApi(t *testing.T) {
require.Equal(t, http.StatusCreated, rec1.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec1.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec1.Body), &createResp, jsonx.V1()))
assocID := createResp["sourceApiAssociation"].(map[string]any)["associationId"].(string)
// Disassociate.
@@ -246,7 +248,7 @@ func TestHandler_ListSourceApiAssociations_Empty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Empty(t, resp["sourceApiAssociationSummaries"])
}
@@ -318,7 +320,7 @@ func TestHandler_StartSchemaMerge(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, appsync.SourceAPIAssociationStatusMergeSuccess, resp["sourceApiAssociationStatus"])
})
}
diff --git a/services/appsync/handler_tags.go b/services/appsync/handler_tags.go
index 50f47ce91e..f3dae2c8f7 100644
--- a/services/appsync/handler_tags.go
+++ b/services/appsync/handler_tags.go
@@ -2,13 +2,14 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleTagsByARN handles /v1/tags/{resourceArn} — the real AWS SDK path for
@@ -91,7 +92,7 @@ func (h *Handler) tagResource(ctx context.Context, c *echo.Context, apiID string
Tags map[string]string `json:"tags"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/appsync/handler_tags_test.go b/services/appsync/handler_tags_test.go
index f77d361f6d..1ef66c8559 100644
--- a/services/appsync/handler_tags_test.go
+++ b/services/appsync/handler_tags_test.go
@@ -1,7 +1,8 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -30,7 +32,7 @@ func TestHandler_TagOperations(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &resp, jsonx.V1()))
tags := resp["tags"].(map[string]any)
assert.Equal(t, "prod", tags["env"])
assert.Equal(t, "platform", tags["team"])
@@ -54,7 +56,7 @@ func TestHandler_TagOperations(t *testing.T) {
require.Equal(t, http.StatusOK, rec4.Code)
var resp4 map[string]any
- require.NoError(t, json.NewDecoder(rec4.Body).Decode(&resp4))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec4.Body), &resp4, jsonx.V1()))
tags4 := resp4["tags"].(map[string]any)
assert.NotContains(t, tags4, "env")
assert.Equal(t, "platform", tags4["team"])
diff --git a/services/appsync/helpers_test.go b/services/appsync/helpers_test.go
index a2ff9a5565..55f3622442 100644
--- a/services/appsync/helpers_test.go
+++ b/services/appsync/helpers_test.go
@@ -7,7 +7,7 @@ package appsync_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -16,6 +16,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -36,7 +37,7 @@ func doRequest(t *testing.T, handler *appsync.Handler, method, path string, body
var buf *bytes.Buffer
if body != nil {
- b, _ := json.Marshal(body)
+ b, _ := json.Marshal(body, jsonx.V1())
buf = bytes.NewBuffer(b)
} else {
buf = bytes.NewBuffer(nil)
@@ -65,7 +66,7 @@ func doRequestWithHeaders(
var buf *bytes.Buffer
if body != nil {
- b, _ := json.Marshal(body)
+ b, _ := json.Marshal(body, jsonx.V1())
buf = bytes.NewBuffer(b)
} else {
buf = bytes.NewBuffer(nil)
@@ -94,7 +95,7 @@ func doV2Request(t *testing.T, handler *appsync.Handler, method, path string, bo
var buf *bytes.Buffer
if body != nil {
- b, _ := json.Marshal(body)
+ b, _ := json.Marshal(body, jsonx.V1())
buf = bytes.NewBuffer(b)
} else {
buf = bytes.NewBuffer(nil)
diff --git a/services/appsync/identity_test.go b/services/appsync/identity_test.go
index 3855759181..01a307d564 100644
--- a/services/appsync/identity_test.go
+++ b/services/appsync/identity_test.go
@@ -1,7 +1,7 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -199,7 +200,7 @@ func whoMap(t *testing.T, v any) map[string]any {
require.True(t, ok, "unexpected who: %#v", v)
var m map[string]any
- require.NoError(t, json.Unmarshal([]byte(s), &m))
+ require.NoError(t, json.Unmarshal([]byte(s), &m, jsonx.V1()))
return m
}
diff --git a/services/appsync/introspection.go b/services/appsync/introspection.go
index 9e1f0c46d5..6d9fdeb586 100644
--- a/services/appsync/introspection.go
+++ b/services/appsync/introspection.go
@@ -1,10 +1,12 @@
package appsync
import (
- "encoding/json"
+ "encoding/json/v2"
"slices"
"github.com/vektah/gqlparser/v2/ast"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// The types below mirror the GraphQL specification's introspection system
@@ -110,7 +112,7 @@ func marshalIntrospectionSchema(schema *ast.Schema, includeDirectives bool) ([]b
},
}
- return json.Marshal(doc)
+ return json.Marshal(doc, jsonx.V1())
}
func buildRootRef(def *ast.Definition) *rootTypeRef {
diff --git a/services/appsync/introspection_test.go b/services/appsync/introspection_test.go
index db8049167a..610d0e410d 100644
--- a/services/appsync/introspection_test.go
+++ b/services/appsync/introspection_test.go
@@ -1,12 +1,14 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -109,10 +111,10 @@ func fieldByName(fields []testField, name string) *testField {
func decodeIntrospection(t *testing.T, raw []byte) testIntrospectionResponse {
t.Helper()
- require.True(t, json.Valid(raw))
+ require.True(t, jsontext.Value(raw).IsValid(jsonx.V1()))
var doc testIntrospectionResponse
- require.NoError(t, json.Unmarshal(raw, &doc))
+ require.NoError(t, json.Unmarshal(raw, &doc, jsonx.V1()))
return doc
}
diff --git a/services/appsync/jseval_test.go b/services/appsync/jseval_test.go
index 50c791cb6a..6dc9fd0af3 100644
--- a/services/appsync/jseval_test.go
+++ b/services/appsync/jseval_test.go
@@ -1,11 +1,13 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// twoHandlerCode defines both request and response handlers, used to exercise
@@ -168,6 +170,6 @@ func TestInMemoryBackend_EvaluateCode_ResultIsValidJSON(t *testing.T) {
require.NoError(t, err)
var decoded map[string]any
- require.NoError(t, json.Unmarshal([]byte(out), &decoded))
+ require.NoError(t, json.Unmarshal([]byte(out), &decoded, jsonx.V1()))
assert.Equal(t, "Scan", decoded["operation"])
}
diff --git a/services/appsync/lambda_batch.go b/services/appsync/lambda_batch.go
index 0f5a66c18d..9269df0451 100644
--- a/services/appsync/lambda_batch.go
+++ b/services/appsync/lambda_batch.go
@@ -2,12 +2,14 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"strings"
"github.com/vektah/gqlparser/v2/ast"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -197,7 +199,7 @@ func (e *execEnv) invokeBatch(
payloads[i] = ent.payload
}
- body, err := json.Marshal(payloads)
+ body, err := json.Marshal(payloads, jsonx.V1())
if err != nil {
fail(err)
@@ -217,7 +219,7 @@ func (e *execEnv) invokeBatch(
if invType == lambdaInvokeEvent {
results = make([]any, len(chunk))
- } else if err = json.Unmarshal(raw, &results); err != nil || len(results) != len(chunk) {
+ } else if err = json.Unmarshal(raw, &results, jsonx.V1()); err != nil || len(results) != len(chunk) {
fail(fmt.Errorf("%w: BatchInvoke response must be a list of %d items", ErrValidation, len(chunk)))
return
diff --git a/services/appsync/lambda_batch_test.go b/services/appsync/lambda_batch_test.go
index 067bc6939c..6b22d07d82 100644
--- a/services/appsync/lambda_batch_test.go
+++ b/services/appsync/lambda_batch_test.go
@@ -2,7 +2,7 @@ package appsync_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sync"
"testing"
@@ -11,6 +11,8 @@ import (
appsynctypes "github.com/aws/aws-sdk-go-v2/service/appsync/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type batchInvoker struct {
@@ -27,7 +29,7 @@ func (b *batchInvoker) InvokeFunction(_ context.Context, name, _ string, payload
}
var items []any
- if err := json.Unmarshal(payload, &items); err != nil {
+ if err := json.Unmarshal(payload, &items, jsonx.V1()); err != nil {
return nil, 0, err
}
@@ -35,7 +37,7 @@ func (b *batchInvoker) InvokeFunction(_ context.Context, name, _ string, payload
b.sizes = append(b.sizes, len(items))
b.mu.Unlock()
- out, err := json.Marshal(b.respond(items))
+ out, err := json.Marshal(b.respond(items), jsonx.V1())
return out, 200, err
}
@@ -166,7 +168,7 @@ type Query { posts: [Post] }`)
Data map[string]any `json:"data"`
Errors []any `json:"errors"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &reply), rec.Body.String())
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &reply, jsonx.V1()), rec.Body.String())
assert.Equal(t, tt.wantSizes, inv.sizes)
assert.Len(t, reply.Errors, max(tt.wantErrs, boolInt(tt.wantData == nil)))
diff --git a/services/appsync/models.go b/services/appsync/models.go
index c71d1f43b9..1df7ccd832 100644
--- a/services/appsync/models.go
+++ b/services/appsync/models.go
@@ -2,11 +2,13 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/vektah/gqlparser/v2/ast"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -227,7 +229,7 @@ func (r Resolver) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
PipelineConfig *pipelineConfigWire `json:"pipelineConfig,omitempty"`
alias
- }{alias: alias(r), PipelineConfig: pc})
+ }{alias: alias(r), PipelineConfig: pc}, jsonx.V1())
}
// UnmarshalJSON is the inverse of [Resolver.MarshalJSON], needed so a
@@ -241,7 +243,7 @@ func (r *Resolver) UnmarshalJSON(data []byte) error {
PipelineConfig *pipelineConfigWire `json:"pipelineConfig,omitempty"`
}{alias: (*alias)(r)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -456,10 +458,10 @@ func (a *APICache) UnmarshalJSON(data []byte) error {
aux := struct {
*alias
- HealthMetricsConfig json.RawMessage `json:"healthMetricsConfig,omitempty"`
+ HealthMetricsConfig jsontext.Value `json:"healthMetricsConfig,omitempty"`
}{alias: (*alias)(a)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -468,12 +470,12 @@ func (a *APICache) UnmarshalJSON(data []byte) error {
}
// A real string (or null, a no-op unmarshal target) decodes here directly.
- if err := json.Unmarshal(aux.HealthMetricsConfig, &a.HealthMetricsConfig); err == nil {
+ if err := json.Unmarshal(aux.HealthMetricsConfig, &a.HealthMetricsConfig, jsonx.V1()); err == nil {
return nil
}
var legacy bool
- if err := json.Unmarshal(aux.HealthMetricsConfig, &legacy); err != nil {
+ if err := json.Unmarshal(aux.HealthMetricsConfig, &legacy, jsonx.V1()); err != nil {
return fmt.Errorf("APICache: invalid healthMetricsConfig %s: %w", aux.HealthMetricsConfig, err)
}
diff --git a/services/appsync/multi_region_test.go b/services/appsync/multi_region_test.go
index 59ab92a007..e4bd963cea 100644
--- a/services/appsync/multi_region_test.go
+++ b/services/appsync/multi_region_test.go
@@ -2,7 +2,8 @@ package appsync_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -14,6 +15,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -39,7 +41,7 @@ func regionDo(t *testing.T, h *appsync.Handler, region, method, path, body strin
out := map[string]any{}
if rec.Body.Len() > 0 {
- _ = json.Unmarshal(rec.Body.Bytes(), &out)
+ _ = json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1())
}
return rec.Code, out
@@ -158,9 +160,9 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(t.Context())
- var doc map[string]json.RawMessage
+ var doc map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(snap, &doc))
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
_, hasRegions := doc["regions"]
assert.Equal(t, tc.wantRegions, hasRegions)
diff --git a/services/appsync/nested_resolver_test.go b/services/appsync/nested_resolver_test.go
index 6d3bd2c5bf..dac026fe6f 100644
--- a/services/appsync/nested_resolver_test.go
+++ b/services/appsync/nested_resolver_test.go
@@ -1,12 +1,13 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -95,7 +96,7 @@ type Query { post: Post posts: [Post] }`)
)
require.NoError(t, err)
- got, mErr := json.Marshal(out)
+ got, mErr := json.Marshal(out, jsonx.V1())
require.NoError(t, mErr)
assert.JSONEq(t, tt.want, string(got))
})
diff --git a/services/appsync/null_propagation_test.go b/services/appsync/null_propagation_test.go
index 0d3aac9e37..ac898dac66 100644
--- a/services/appsync/null_propagation_test.go
+++ b/services/appsync/null_propagation_test.go
@@ -1,13 +1,14 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -105,7 +106,7 @@ func TestNullPropagation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var got gqlReply
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, tt.wantData, got.Data)
require.Len(t, got.Errors, 1, rec.Body.String())
assert.Equal(t, tt.wantMsg, got.Errors[0].Message)
diff --git a/services/appsync/persistence.go b/services/appsync/persistence.go
index 2eb26b3f7e..66b080411a 100644
--- a/services/appsync/persistence.go
+++ b/services/appsync/persistence.go
@@ -10,7 +10,7 @@ package appsync
// every "clean" table directly, same as here).
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -48,7 +48,7 @@ const appsyncSnapshotVersion = 2
// func(*APIKey) string that could reproduce the "#" composite
// key from the value alone). It is persisted explicitly here instead.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
APIKeys map[string]map[string]*APIKey `json:"apiKeys,omitempty"`
Version int `json:"version"`
}
diff --git a/services/appsync/realtime.go b/services/appsync/realtime.go
index 678b29009f..890d9f02f7 100644
--- a/services/appsync/realtime.go
+++ b/services/appsync/realtime.go
@@ -3,7 +3,8 @@ package appsync
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -16,6 +17,8 @@ import (
"github.com/vektah/gqlparser/v2"
"github.com/vektah/gqlparser/v2/ast"
"github.com/vektah/gqlparser/v2/validator/rules"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -59,9 +62,9 @@ type rtSub struct {
}
type rtMessage struct {
- ID string `json:"id,omitempty"`
- Type string `json:"type"`
- Payload json.RawMessage `json:"payload,omitempty"`
+ ID string `json:"id,omitempty"`
+ Type string `json:"type"`
+ Payload jsontext.Value `json:"payload,omitempty"`
}
type rtStartPayload struct {
@@ -95,7 +98,7 @@ func realtimeAuthFromQuery(r *http.Request) map[string]string {
}
}
- _ = json.Unmarshal(dec, &out)
+ _ = json.Unmarshal(dec, &out, jsonx.V1())
return out
}
@@ -153,14 +156,14 @@ func (b *InMemoryBackend) startSubscription(
ctx context.Context, apiID string, conn *rtConn, msg rtMessage, defaultAuth map[string]string, r *http.Request,
) {
var payload rtStartPayload
- if err := json.Unmarshal(msg.Payload, &payload); err != nil {
+ if err := json.Unmarshal(msg.Payload, &payload, jsonx.V1()); err != nil {
_ = conn.send(rtError(msg.ID, rtErrBadRequest, "invalid start payload"))
return
}
var req graphqlRequest
- if err := json.Unmarshal([]byte(payload.Data), &req); err != nil {
+ if err := json.Unmarshal([]byte(payload.Data), &req, jsonx.V1()); err != nil {
_ = conn.send(rtError(msg.ID, rtErrBadRequest, "payload.data must be a JSON string"))
return
diff --git a/services/appsync/realtime_test.go b/services/appsync/realtime_test.go
index b2d86d7496..8e757c3282 100644
--- a/services/appsync/realtime_test.go
+++ b/services/appsync/realtime_test.go
@@ -2,7 +2,7 @@ package appsync_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http/httptest"
"strings"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -74,7 +75,7 @@ func newRealtimeFixture(t *testing.T) *rtFixture {
func (f *rtFixture) dial(t *testing.T, apiKey string) *websocket.Conn {
t.Helper()
- hdr, err := json.Marshal(map[string]string{"x-api-key": apiKey})
+ hdr, err := json.Marshal(map[string]string{"x-api-key": apiKey}, jsonx.V1())
require.NoError(t, err)
d := websocket.Dialer{Subprotocols: []string{"graphql-ws"}}
@@ -99,7 +100,7 @@ func readMsg(t *testing.T, conn *websocket.Conn) map[string]any {
func startMsg(t *testing.T, id, query, apiKey string) map[string]any {
t.Helper()
- data, err := json.Marshal(map[string]any{"query": query})
+ data, err := json.Marshal(map[string]any{"query": query}, jsonx.V1())
require.NoError(t, err)
return map[string]any{
@@ -164,7 +165,7 @@ func TestRealtime_SubscriptionDelivery(t *testing.T) {
assert.Equal(t, "data", msg["type"])
assert.Equal(t, "sub1", msg["id"])
- payload, mErr := json.Marshal(msg["payload"])
+ payload, mErr := json.Marshal(msg["payload"], jsonx.V1())
require.NoError(t, mErr)
assert.JSONEq(t, `{"data":`+tt.want+`}`, string(payload))
diff --git a/services/appsync/resolver_cache.go b/services/appsync/resolver_cache.go
index ceb51e4fca..78664c5294 100644
--- a/services/appsync/resolver_cache.go
+++ b/services/appsync/resolver_cache.go
@@ -1,11 +1,13 @@
package appsync
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
"time"
"github.com/vektah/gqlparser/v2/ast"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -122,7 +124,7 @@ func (p cachePolicy) keyParts(ctxMap map[string]any) map[string]any {
}
func encodeKey(typeName, fieldName string, parts map[string]any) string {
- raw, _ := json.Marshal(parts)
+ raw, _ := json.Marshal(parts, jsonx.V1())
return typeName + "." + fieldName + "|" + string(raw)
}
@@ -246,7 +248,7 @@ func (b *InMemoryBackend) evictMatching(apiID, prefix string, want map[string]an
}
var have map[string]any
- if json.Unmarshal([]byte(rest), &have) != nil {
+ if json.Unmarshal([]byte(rest), &have, jsonx.V1()) != nil {
continue
}
diff --git a/services/appsync/resolver_cache_test.go b/services/appsync/resolver_cache_test.go
index 6714696bf4..95baac12c6 100644
--- a/services/appsync/resolver_cache_test.go
+++ b/services/appsync/resolver_cache_test.go
@@ -1,7 +1,7 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -92,10 +93,10 @@ func (f *cacheFixture) query(t *testing.T, q string) string {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out gqlReply
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Empty(t, out.Errors, rec.Body.String())
- raw, err := json.Marshal(out.Data)
+ raw, err := json.Marshal(out.Data, jsonx.V1())
require.NoError(t, err)
return string(raw)
diff --git a/services/appsync/resolver_runtime_e2e_test.go b/services/appsync/resolver_runtime_e2e_test.go
index ccb2e27ea5..6170f08461 100644
--- a/services/appsync/resolver_runtime_e2e_test.go
+++ b/services/appsync/resolver_runtime_e2e_test.go
@@ -1,7 +1,7 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"sync"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -216,7 +217,7 @@ func (e *runtimeEnv) exec(t *testing.T, query string) gqlReply {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out gqlReply
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out), rec.Body.String())
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()), rec.Body.String())
return out
}
diff --git a/services/appsync/resolvers_config_test.go b/services/appsync/resolvers_config_test.go
index 27e52e78dc..8d53918c36 100644
--- a/services/appsync/resolvers_config_test.go
+++ b/services/appsync/resolvers_config_test.go
@@ -1,7 +1,7 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -503,7 +504,7 @@ func TestListResolvers_Pagination(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var apiOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &apiOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &apiOut, jsonx.V1()))
apiID := apiOut["graphqlApi"].(map[string]any)["apiId"].(string)
rec = doRequest(t, h, http.MethodPost, fmt.Sprintf("/v1/apis/%s/datasources", apiID), map[string]any{
@@ -557,7 +558,7 @@ func TestListResolvers_Pagination(t *testing.T) {
NextToken string `json:"nextToken"`
Resolvers []map[string]any `json:"resolvers"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Resolvers, tt.wantLen)
if tt.wantNextToken {
assert.NotEmpty(t, out.NextToken)
diff --git a/services/appsync/schema_types_test.go b/services/appsync/schema_types_test.go
index f1593ecb8a..80b400b107 100644
--- a/services/appsync/schema_types_test.go
+++ b/services/appsync/schema_types_test.go
@@ -1,7 +1,7 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -266,7 +267,7 @@ func TestListTypes_Pagination(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var apiOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &apiOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &apiOut, jsonx.V1()))
apiID := apiOut["graphqlApi"].(map[string]any)["apiId"].(string)
for i := range 4 {
@@ -307,7 +308,7 @@ func TestListTypes_Pagination(t *testing.T) {
NextToken string `json:"nextToken"`
Types []map[string]any `json:"types"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Types, tt.wantLen)
if tt.wantNextToken {
assert.NotEmpty(t, out.NextToken)
diff --git a/services/appsync/subscription_filter.go b/services/appsync/subscription_filter.go
index 9f704f3b9b..7872181764 100644
--- a/services/appsync/subscription_filter.go
+++ b/services/appsync/subscription_filter.go
@@ -2,7 +2,7 @@ package appsync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"reflect"
@@ -10,6 +10,8 @@ import (
"strings"
"github.com/vektah/gqlparser/v2/ast"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -30,14 +32,14 @@ type subFilterSet [][]subFilter
// plainJSON converts a VTL/JS value into plain JSON-shaped Go values.
func plainJSON(v any) (any, error) {
- raw, err := json.Marshal(v)
+ raw, err := json.Marshal(v, jsonx.V1())
if err != nil {
return nil, err
}
var out any
- err = json.Unmarshal(raw, &out)
+ err = json.Unmarshal(raw, &out, jsonx.V1())
return out, err
}
diff --git a/services/appsync/subscription_filter_test.go b/services/appsync/subscription_filter_test.go
index 61ce89079d..5978c89311 100644
--- a/services/appsync/subscription_filter_test.go
+++ b/services/appsync/subscription_filter_test.go
@@ -1,12 +1,13 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -102,7 +103,7 @@ func TestSubscriptionFilter_AppliedToPublishedEvents(t *testing.T) {
msg := next()
require.Equal(t, "data", msg["type"], msg)
- raw, err := json.Marshal(msg["payload"])
+ raw, err := json.Marshal(msg["payload"], jsonx.V1())
require.NoError(t, err)
assert.JSONEq(t, `{"data":{"onPut":{"id":"2","title":"`+tt.want+`"}}}`, string(raw),
"the filtered-out mutation must not be delivered before the matching one")
@@ -207,7 +208,7 @@ export function response(ctx) {
f.put(t, "1", "sell")
f.put(t, "2", "buy")
- raw, err := json.Marshal(next()["payload"])
+ raw, err := json.Marshal(next()["payload"], jsonx.V1())
require.NoError(t, err)
assert.JSONEq(t, `{"data":{"onPut":{"id":"2","title":"buy"}}}`, string(raw))
}
diff --git a/services/appsync/util_ddb.go b/services/appsync/util_ddb.go
index 174712b7c1..4ea90d21cc 100644
--- a/services/appsync/util_ddb.go
+++ b/services/appsync/util_ddb.go
@@ -1,10 +1,12 @@
package appsync
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func ddbTyped(v any) any {
@@ -118,7 +120,7 @@ func utilDynamo() map[string]utilFn {
return nil, err
}
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
return string(b), err
}
diff --git a/services/appsync/util_esdsl.go b/services/appsync/util_esdsl.go
index 8dbe37573c..78ce3da062 100644
--- a/services/appsync/util_esdsl.go
+++ b/services/appsync/util_esdsl.go
@@ -1,8 +1,10 @@
package appsync
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -24,7 +26,7 @@ func toQueryDSL(_ *utilEnv, a []any) (any, error) {
return nil, err
}
- b, err := json.Marshal(q)
+ b, err := json.Marshal(q, jsonx.V1())
return string(b), err
}
diff --git a/services/appsync/util_extra.go b/services/appsync/util_extra.go
index b7ac8f7349..236956226e 100644
--- a/services/appsync/util_extra.go
+++ b/services/appsync/util_extra.go
@@ -2,7 +2,8 @@ package appsync
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"encoding/xml"
"errors"
"fmt"
@@ -12,6 +13,7 @@ import (
"golang.org/x/text/unicode/norm"
"github.com/blackbirdworks/gopherstack/pkgs/ctxval"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -88,7 +90,7 @@ func s3Object(a []any) (any, error) {
ref.Version = argStr(a, s3ObjectArgs)
}
- b, err := json.Marshal(map[string]any{"s3": ref})
+ b, err := json.Marshal(map[string]any{"s3": ref}, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("encode s3 object: %w", err)
}
@@ -102,7 +104,7 @@ func s3ObjectJSON(_ *utilEnv, a []any) (any, error) {
return nil, err
}
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
return string(b), err
}
@@ -112,7 +114,7 @@ func fromS3ObjectJSON(doc string) (any, error) {
S3 *s3Ref `json:"s3"`
}
- if err := json.Unmarshal([]byte(doc), &wrapper); err != nil || wrapper.S3 == nil {
+ if err := json.Unmarshal([]byte(doc), &wrapper, jsonx.V1()); err != nil || wrapper.S3 == nil {
return nil, fmt.Errorf("%w: not a DynamoDB S3 object", ErrValidation)
}
@@ -234,10 +236,9 @@ func xmlToJSONString(u *utilEnv, a []any) (any, error) {
var buf bytes.Buffer
- enc := json.NewEncoder(&buf)
- enc.SetEscapeHTML(false)
+ enc := jsontext.NewEncoder(&buf, jsonx.V1(), jsontext.EscapeForHTML(false))
- if err = enc.Encode(m); err != nil {
+ if err = json.MarshalEncode(enc, m); err != nil {
return nil, fmt.Errorf("encode xml: %w", err)
}
diff --git a/services/appsync/util_funcs.go b/services/appsync/util_funcs.go
index 4d69793682..30e0a48141 100644
--- a/services/appsync/util_funcs.go
+++ b/services/appsync/util_funcs.go
@@ -3,7 +3,7 @@ package appsync
import (
"crypto/rand"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"math/big"
@@ -14,6 +14,8 @@ import (
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// resolverError is an error raised from a mapping template or handler
@@ -168,13 +170,13 @@ func utilCore() map[string]utilFn {
return newKSUID(), nil
},
"toJson": func(_ *utilEnv, a []any) (any, error) {
- b, err := json.Marshal(argAt(a, 0))
+ b, err := json.Marshal(argAt(a, 0), jsonx.V1())
return string(b), err
},
"parseJson": func(_ *utilEnv, a []any) (any, error) {
var v any
- if err := json.Unmarshal([]byte(argStr(a, 0)), &v); err != nil {
+ if err := json.Unmarshal([]byte(argStr(a, 0)), &v, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON", ErrValidation)
}
@@ -405,7 +407,7 @@ func fmtNum(v any) string {
return strconv.Itoa(t)
case float64:
return strconv.FormatFloat(t, 'f', -1, 64)
- case json.Number:
+ case jsonx.Number:
return t.String()
}
diff --git a/services/appsync/util_rds.go b/services/appsync/util_rds.go
index cbbf19a5a3..b17c94b005 100644
--- a/services/appsync/util_rds.go
+++ b/services/appsync/util_rds.go
@@ -1,8 +1,10 @@
package appsync
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const rdsLongValue = "longValue"
@@ -21,7 +23,7 @@ func utilRDS() map[string]utilFn {
return nil, err
}
- raw, err := json.Marshal(objs)
+ raw, err := json.Marshal(objs, jsonx.V1())
return string(raw), err
},
@@ -52,7 +54,7 @@ func rdsResultRoot(v any) (map[string]any, error) {
return t, nil
case string:
var m map[string]any
- if err := json.Unmarshal([]byte(t), &m); err != nil {
+ if err := json.Unmarshal([]byte(t), &m, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid RDS result JSON", ErrValidation)
}
diff --git a/services/appsync/vtl.go b/services/appsync/vtl.go
index b7385fe9b1..38a4d9efbc 100644
--- a/services/appsync/vtl.go
+++ b/services/appsync/vtl.go
@@ -1,15 +1,17 @@
package appsync
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// renderVTL renders tmpl with the given arguments and result as the context.
func renderVTL(tmpl string, args map[string]any, result any) (string, error) {
if tmpl == "" {
if result != nil {
- b, err := json.Marshal(result)
+ b, err := json.Marshal(result, jsonx.V1())
return string(b), err
}
@@ -39,7 +41,7 @@ func returnedString(v any) string {
return s
}
- b, _ := json.Marshal(v)
+ b, _ := json.Marshal(v, jsonx.V1())
return string(b)
}
@@ -59,7 +61,7 @@ func vtlOutputValue(out vtlOutcome) any {
}
func toDynamoDBJSON(val any) string {
- b, _ := json.Marshal(ddbTyped(val))
+ b, _ := json.Marshal(ddbTyped(val), jsonx.V1())
return string(b)
}
diff --git a/services/appsync/vtl_bounds_test.go b/services/appsync/vtl_bounds_test.go
new file mode 100644
index 0000000000..acddcc4f71
--- /dev/null
+++ b/services/appsync/vtl_bounds_test.go
@@ -0,0 +1,37 @@
+package appsync_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/services/appsync"
+)
+
+func TestRenderVTL_ListIndexBounds(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ tmpl string
+ want string
+ }{
+ {name: "in_range", tmpl: `#set($l=["a","b"])$l.remove(1)|$l.size()`, want: "b|1"},
+ {
+ name: "beyond_int32",
+ tmpl: `#set($l=["a","b"])$l.remove(5000000000)|$l.size()`,
+ want: "$l.remove(5000000000)|2",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ got, err := appsync.RenderVTL(tt.tmpl, nil, nil)
+ require.NoError(t, err)
+ assert.Equal(t, tt.want, got)
+ })
+ }
+}
diff --git a/services/appsync/vtl_eval.go b/services/appsync/vtl_eval.go
index c44bc5044b..18ddf38015 100644
--- a/services/appsync/vtl_eval.go
+++ b/services/appsync/vtl_eval.go
@@ -1,12 +1,14 @@
package appsync
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"math"
"sort"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -23,7 +25,7 @@ func (l *vlist) MarshalJSON() ([]byte, error) {
return []byte("[]"), nil
}
- return json.Marshal(l.items)
+ return json.Marshal(l.items, jsonx.V1())
}
// vns is a utility namespace reference ($util, $util.time, ...).
@@ -763,7 +765,7 @@ func toNum(v any) (float64, bool, bool) {
return float64(t), true, true
case float64:
return t, false, true
- case json.Number:
+ case jsonx.Number:
if i, err := t.Int64(); err == nil {
return float64(i), true, true
}
@@ -788,6 +790,14 @@ func toInt(v any) (int, bool) {
return 0, false
}
+ return smallInt(f)
+}
+
+func smallInt(f float64) (int, bool) {
+ if f < math.MinInt32 || f > math.MaxInt32 {
+ return 0, false
+ }
+
return int(f), true
}
@@ -805,7 +815,7 @@ func vrender(v any) string {
return strconv.Itoa(t)
case float64:
return renderFloat(t)
- case json.Number:
+ case jsonx.Number:
return t.String()
case *vlist:
parts := make([]string, len(t.items))
@@ -867,7 +877,7 @@ func vnorm(v any) any {
}
return t
- case json.Number:
+ case jsonx.Number:
if i, err := t.Int64(); err == nil {
return i
}
diff --git a/services/appsync/vtl_methods.go b/services/appsync/vtl_methods.go
index 6f099213f2..ff4af63f11 100644
--- a/services/appsync/vtl_methods.go
+++ b/services/appsync/vtl_methods.go
@@ -263,8 +263,8 @@ func listIndexOf(l *vlist, v any) int {
func listRemove(l *vlist, v any) any {
if f, isInt, ok := toNum(v); ok && isInt {
- i := int(f)
- if i < 0 || i >= len(l.items) {
+ i, small := smallInt(f)
+ if !small || i < 0 || i >= len(l.items) {
return unknownMethod()
}
diff --git a/services/appsync/wire_field_fixes_test.go b/services/appsync/wire_field_fixes_test.go
index f77ce59de4..1010828c22 100644
--- a/services/appsync/wire_field_fixes_test.go
+++ b/services/appsync/wire_field_fixes_test.go
@@ -1,7 +1,8 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -126,7 +128,7 @@ func TestGraphqlApi_EnvironmentVariablesNotLeaked(t *testing.T) {
require.Equal(t, 200, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
graphqlAPI, ok := resp["graphqlApi"].(map[string]any)
require.True(t, ok)
@@ -138,7 +140,7 @@ func TestGraphqlApi_EnvironmentVariablesNotLeaked(t *testing.T) {
require.Equal(t, 200, envRec.Code)
var envResp map[string]any
- require.NoError(t, json.NewDecoder(envRec.Body).Decode(&envResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(envRec.Body), &envResp, jsonx.V1()))
envVars, ok := envResp["environmentVariables"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "super-secret-value", envVars["DB_PASSWORD"])
diff --git a/services/appsync/wire_shape.go b/services/appsync/wire_shape.go
index 331babb185..1074ad6c59 100644
--- a/services/appsync/wire_shape.go
+++ b/services/appsync/wire_shape.go
@@ -1,17 +1,21 @@
package appsync
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
// wireShape re-encodes v without the internal-only keys drop from its top-level
// object (or from each element when v is a slice).
func wireShape(v any, drop ...string) any {
- raw, err := json.Marshal(v)
+ raw, err := json.Marshal(v, jsonx.V1())
if err != nil {
return v
}
var decoded any
- if json.Unmarshal(raw, &decoded) != nil {
+ if json.Unmarshal(raw, &decoded, jsonx.V1()) != nil {
return v
}
diff --git a/services/appsync/wire_shape_test.go b/services/appsync/wire_shape_test.go
index 6117d2f026..906404383d 100644
--- a/services/appsync/wire_shape_test.go
+++ b/services/appsync/wire_shape_test.go
@@ -1,13 +1,14 @@
package appsync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/appsync"
)
@@ -62,7 +63,7 @@ func TestWire_NoInternalAPIID(t *testing.T) {
require.Equal(t, http.StatusOK, list.Code)
var out map[string][]map[string]any
- require.NoError(t, json.Unmarshal(list.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(list.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out[tt.listKey])
for _, item := range out[tt.listKey] {
diff --git a/services/arczonalshift/handler_ops.go b/services/arczonalshift/handler_ops.go
index 4cafe0a6e1..23283a1804 100644
--- a/services/arczonalshift/handler_ops.go
+++ b/services/arczonalshift/handler_ops.go
@@ -2,9 +2,11 @@ package arczonalshift
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/restroute"
)
@@ -121,9 +123,9 @@ func (h *Handler) updatePractice(_ context.Context, req *restroute.Request) (any
return nil, 0, err
}
- var present map[string]json.RawMessage
+ var present map[string]jsontext.Value
if len(req.Body) > 0 {
- _ = json.Unmarshal(req.Body, &present)
+ _ = json.Unmarshal(req.Body, &present, jsonx.V1())
}
set := map[string]bool{}
diff --git a/services/arczonalshift/managed_more.go b/services/arczonalshift/managed_more.go
index 189b10f9d9..c9c34717b7 100644
--- a/services/arczonalshift/managed_more.go
+++ b/services/arczonalshift/managed_more.go
@@ -1,12 +1,13 @@
package arczonalshift
import (
- "encoding/json"
+ "encoding/json/v2"
"slices"
"strings"
awsarn "github.com/aws/aws-sdk-go-v2/aws/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/restroute"
"github.com/blackbirdworks/gopherstack/pkgs/service"
asgbackend "github.com/blackbirdworks/gopherstack/services/autoscaling"
@@ -162,7 +163,7 @@ func clusterZonalShiftEnabled(c *eksbackend.Cluster) bool {
Enabled bool `json:"enabled"`
}
- return json.Unmarshal(raw, &cfg) == nil && cfg.Enabled
+ return json.Unmarshal(raw, &cfg, jsonx.V1()) == nil && cfg.Enabled
}
func (b *InMemoryBackend) managedCluster(arn string, p awsarn.ARN) (managedResource, error) {
diff --git a/services/arczonalshift/managed_resources_test.go b/services/arczonalshift/managed_resources_test.go
index 4c4027f9c3..0c13b9a4af 100644
--- a/services/arczonalshift/managed_resources_test.go
+++ b/services/arczonalshift/managed_resources_test.go
@@ -2,7 +2,7 @@ package arczonalshift_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -67,7 +67,7 @@ func (w *managedWorld) group(t *testing.T, name string, enabled bool) string {
return g.AutoScalingGroupARN
}
-func (w *managedWorld) cluster(t *testing.T, name string, zonalShift json.RawMessage) string {
+func (w *managedWorld) cluster(t *testing.T, name string, zonalShift jsontext.Value) string {
t.Helper()
vpc, err := w.ec2.CreateVpc("10.0.0.0/16", "")
@@ -84,7 +84,7 @@ func (w *managedWorld) cluster(t *testing.T, name string, zonalShift json.RawMes
opts := eksbk.ClusterOptionalConfig{}
if zonalShift != nil {
- opts.ConfigBlocks = map[string]json.RawMessage{"zonalShiftConfig": zonalShift}
+ opts.ConfigBlocks = map[string]jsontext.Value{"zonalShiftConfig": zonalShift}
}
c, err := w.eks.CreateCluster(name, "1.31", "arn:aws:iam::"+testAccount+":role/eks",
@@ -127,7 +127,7 @@ func TestManagedResourcesAutoScalingAndEKS(t *testing.T) {
create: func(t *testing.T, w *managedWorld) string {
t.Helper()
- return w.cluster(t, "k8s", json.RawMessage(`{"enabled":true}`))
+ return w.cluster(t, "k8s", jsontext.Value(`{"enabled":true}`))
},
},
{
@@ -135,7 +135,7 @@ func TestManagedResourcesAutoScalingAndEKS(t *testing.T) {
create: func(t *testing.T, w *managedWorld) string {
t.Helper()
- return w.cluster(t, "k8s", json.RawMessage(`{"enabled":false}`))
+ return w.cluster(t, "k8s", jsontext.Value(`{"enabled":false}`))
},
},
{
diff --git a/services/arczonalshift/misc_test.go b/services/arczonalshift/misc_test.go
index 067f3017e1..dccd0539d5 100644
--- a/services/arczonalshift/misc_test.go
+++ b/services/arczonalshift/misc_test.go
@@ -1,7 +1,7 @@
package arczonalshift_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/sdkcheck"
zs "github.com/blackbirdworks/gopherstack/services/arczonalshift"
)
@@ -158,7 +159,7 @@ func TestErrorBodyShape(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Header().Get("X-Amzn-Errortype"))
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.NotEmpty(t, body["message"])
assert.Equal(t, body["message"], body["Message"])
assert.False(t, strings.HasPrefix(body["message"].(string), tt.wantCode))
diff --git a/services/arczonalshift/persistence.go b/services/arczonalshift/persistence.go
index 4e237a5ddc..6251681699 100644
--- a/services/arczonalshift/persistence.go
+++ b/services/arczonalshift/persistence.go
@@ -2,7 +2,7 @@ package arczonalshift
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
@@ -13,8 +13,8 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state. It implements persistence.Persistable.
diff --git a/services/athena/audit_athena_test.go b/services/athena/audit_athena_test.go
index 4d1b1e16bf..c9fbfb1d91 100644
--- a/services/athena/audit_athena_test.go
+++ b/services/athena/audit_athena_test.go
@@ -1,13 +1,15 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestAuditAthena_ResultReuseConfiguration_StoredAndReturned verifies that
@@ -254,7 +256,7 @@ func TestAuditAthena_EnforceWorkGroupConfiguration_OverridesOutputLocation(t *te
wgCfgJSON, _ := json.Marshal(map[string]any{
"EnforceWorkGroupConfiguration": tc.enforceWGConfig,
"ResultConfiguration": map[string]any{"OutputLocation": tc.wgOutputLocation},
- })
+ }, jsonx.V1())
rec := a1Do(t, h, "CreateWorkGroup",
fmt.Sprintf(`{"Name":"enforce-wg","Configuration":%s}`, string(wgCfgJSON)))
require.Equal(t, http.StatusOK, rec.Code)
@@ -266,7 +268,7 @@ func TestAuditAthena_EnforceWorkGroupConfiguration_OverridesOutputLocation(t *te
"ResultConfiguration": map[string]any{
"OutputLocation": tc.perQueryLocation,
},
- })
+ }, jsonx.V1())
rec2 := a1Do(t, h, "StartQueryExecution", string(startBody))
require.Equal(t, http.StatusOK, rec2.Code)
@@ -400,7 +402,7 @@ func TestAuditAthena_BatchGetQueryExecution_Shape(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
- idsJSON, _ := json.Marshal(tc.ids)
+ idsJSON, _ := json.Marshal(tc.ids, jsonx.V1())
batchRec := a1Do(t, h, "BatchGetQueryExecution",
fmt.Sprintf(`{"QueryExecutionIds":%s}`, string(idsJSON)))
require.Equal(t, http.StatusOK, batchRec.Code)
@@ -471,7 +473,7 @@ func TestAuditAthena_WorkGroup_EnforceEncryption(t *testing.T) {
"EncryptionOption": "SSE_S3",
},
},
- })
+ }, jsonx.V1())
rec := a1Do(t, h, "CreateWorkGroup",
fmt.Sprintf(`{"Name":"enc-wg","Configuration":%s}`, string(wgCfg)))
diff --git a/services/athena/client_token_test.go b/services/athena/client_token_test.go
index 711a9dbc09..09ed31c8fb 100644
--- a/services/athena/client_token_test.go
+++ b/services/athena/client_token_test.go
@@ -1,12 +1,14 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_ClientRequestTokenReplay(t *testing.T) {
@@ -153,7 +155,7 @@ func TestHandler_StartSessionTags(t *testing.T) {
require.Equal(t, http.StatusOK, tagged.Code, tagged.Body.String())
var out tagList
- require.NoError(t, json.Unmarshal(tagged.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(tagged.Body.Bytes(), &out, jsonx.V1()))
got := map[string]string{}
for _, tg := range out.Tags {
@@ -182,7 +184,7 @@ func TestHandler_StartSessionCopyWorkGroupTags(t *testing.T) {
require.Equal(t, http.StatusOK, tagged.Code, tagged.Body.String())
var out tagList
- require.NoError(t, json.Unmarshal(tagged.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(tagged.Body.Bytes(), &out, jsonx.V1()))
got := map[string]string{}
for _, tg := range out.Tags {
diff --git a/services/athena/ddl_test.go b/services/athena/ddl_test.go
index 00f0bb7b0a..ab89b2c897 100644
--- a/services/athena/ddl_test.go
+++ b/services/athena/ddl_test.go
@@ -1,13 +1,14 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/athena"
)
@@ -278,7 +279,7 @@ func startQueryBody(query string) string {
"Catalog": testCatalog,
"Database": testDatabase,
},
- })
+ }, jsonx.V1())
return string(body)
}
@@ -306,7 +307,7 @@ func TestExecuteStatement_InsertThenSelect(t *testing.T) {
insExec := doRequest(t, h, "GetQueryExecution", `{"QueryExecutionId":"`+insID+`"}`)
var insResp map[string]any
- require.NoError(t, json.Unmarshal(insExec.Body.Bytes(), &insResp))
+ require.NoError(t, json.Unmarshal(insExec.Body.Bytes(), &insResp, jsonx.V1()))
status := insResp["QueryExecution"].(map[string]any)["Status"].(map[string]any)
assert.Equal(t, "SUCCEEDED", status["State"])
@@ -316,7 +317,7 @@ func TestExecuteStatement_InsertThenSelect(t *testing.T) {
require.Equal(t, http.StatusOK, results.Code)
var resResp map[string]any
- require.NoError(t, json.Unmarshal(results.Body.Bytes(), &resResp))
+ require.NoError(t, json.Unmarshal(results.Body.Bytes(), &resResp, jsonx.V1()))
rs := resResp["ResultSet"].(map[string]any)
rows := rs["Rows"].([]any)
diff --git a/services/athena/engine_complex.go b/services/athena/engine_complex.go
index e32bb1b14a..4f1663d821 100644
--- a/services/athena/engine_complex.go
+++ b/services/athena/engine_complex.go
@@ -3,7 +3,8 @@ package athena
import (
"bytes"
"database/sql/driver"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"sort"
@@ -11,6 +12,8 @@ import (
"strings"
sqlitedriver "modernc.org/sqlite"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// containerMark prefixes the JSON text of an ARRAY/MAP value so results render
@@ -54,11 +57,10 @@ func complexTypeOf(glueType string) (string, bool) {
}
func decodeComplex(s string) (any, bool) {
- dec := json.NewDecoder(strings.NewReader(strings.TrimPrefix(s, containerMark)))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(strings.NewReader(strings.TrimPrefix(s, containerMark)))
var out any
- if err := dec.Decode(&out); err != nil {
+ if err := json.UnmarshalDecode(dec, &out, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, false
}
@@ -79,7 +81,7 @@ func trinoString(v any) string {
return t
case bool:
return strconv.FormatBool(t)
- case json.Number:
+ case jsonx.Number:
return t.String()
case []any:
parts := make([]string, len(t))
diff --git a/services/athena/engine_funcs_agg.go b/services/athena/engine_funcs_agg.go
index 36849420a5..54c58c1e0b 100644
--- a/services/athena/engine_funcs_agg.go
+++ b/services/athena/engine_funcs_agg.go
@@ -2,13 +2,15 @@ package athena
import (
"database/sql/driver"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"math"
"sort"
"strings"
sqlitedriver "modernc.org/sqlite"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// aggState is one running aggregate; step ignores NULL inputs unless nullsToo.
@@ -152,7 +154,7 @@ func jsonElem(v driver.Value) any {
if s, ok := v.(string); ok {
var parsed any
if t := strings.TrimPrefix(s, containerMark); len(t) > 0 && (t[0] == '[' || t[0] == '{') &&
- json.Unmarshal([]byte(t), &parsed) == nil {
+ json.Unmarshal([]byte(t), &parsed, jsonx.V1()) == nil {
return parsed
}
}
diff --git a/services/athena/engine_funcs_json.go b/services/athena/engine_funcs_json.go
index f30a414645..1d81363033 100644
--- a/services/athena/engine_funcs_json.go
+++ b/services/athena/engine_funcs_json.go
@@ -2,13 +2,15 @@ package athena
import (
"database/sql/driver"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"math"
"slices"
"sort"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// arrays and maps live in SQLite as JSON text.
@@ -20,7 +22,7 @@ func parseJSONArg(v driver.Value) (any, bool) {
}
var out any
- if err := json.Unmarshal([]byte(strings.TrimPrefix(s, containerMark)), &out); err != nil {
+ if err := json.Unmarshal([]byte(strings.TrimPrefix(s, containerMark)), &out, jsonx.V1()); err != nil {
return nil, false
}
@@ -29,7 +31,7 @@ func parseJSONArg(v driver.Value) (any, bool) {
// jsonText encodes an ARRAY/MAP value; containerMark tags it so results render Trino-style.
func jsonText(v any) driver.Value {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return sqlNull()
}
@@ -39,7 +41,7 @@ func jsonText(v any) driver.Value {
// jsonPlain encodes a JSON-typed value, which renders as raw JSON text.
func jsonPlain(v any) driver.Value {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return sqlNull()
}
@@ -385,7 +387,7 @@ func udfArrayDistinct(_ *fnCtx, args []driver.Value) (driver.Value, error) {
out := make([]any, 0, len(a))
for _, e := range a {
- k, _ := json.Marshal(e)
+ k, _ := json.Marshal(e, jsonx.V1())
if !seen[string(k)] {
seen[string(k)] = true
out = append(out, e)
diff --git a/services/athena/engine_iceberg_meta.go b/services/athena/engine_iceberg_meta.go
index 8b03e9da91..1de0a75ce3 100644
--- a/services/athena/engine_iceberg_meta.go
+++ b/services/athena/engine_iceberg_meta.go
@@ -3,12 +3,13 @@ package athena
import (
"context"
"database/sql"
- "encoding/json"
+ "encoding/json/v2"
"sort"
"strings"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/iceberg"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// icebergMetaSuffix splits "tbl$snapshots" into the table and metadata table names.
@@ -32,7 +33,7 @@ const colSnapshotID = "snapshot_id"
func tsText(ms int64) string { return time.UnixMilli(ms).UTC().Format(tsMillisLayout) }
func mapJSON(m map[string]string) string {
- b, _ := json.Marshal(m)
+ b, _ := json.Marshal(m, jsonx.V1())
return string(b)
}
@@ -221,7 +222,7 @@ func (l *tableLoader) filesTable(ctx context.Context, snap iceberg.Snapshot, has
for _, f := range append(files.data, files.deletes...) {
df := f.entry.DataFile
- part, _ := json.Marshal(df.Partition)
+ part, _ := json.Marshal(df.Partition, jsonx.V1())
rows = append(rows, []any{
int64(df.Content), df.Path, df.Format, int64(f.spec), string(part), df.RecordCount, df.FileSize,
})
diff --git a/services/athena/engine_load.go b/services/athena/engine_load.go
index 6cf53bc662..799b94a4d6 100644
--- a/services/athena/engine_load.go
+++ b/services/athena/engine_load.go
@@ -7,7 +7,8 @@ import (
"context"
"database/sql"
"encoding/csv"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -17,6 +18,8 @@ import (
"github.com/aws/aws-sdk-go-v2/aws"
sdk_s3 "github.com/aws/aws-sdk-go-v2/service/s3"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type dataFormat int
@@ -454,12 +457,12 @@ func jsonRaw(v any) string {
return ""
case string:
return t
- case json.Number:
+ case jsonx.Number:
return t.String()
case bool:
return strconv.FormatBool(t)
default:
- b, _ := json.Marshal(t)
+ b, _ := json.Marshal(t, jsonx.V1())
return string(b)
}
@@ -475,12 +478,11 @@ func (l *tableLoader) decodeJSON(ctx context.Context, rd io.Reader, f tableForma
continue
}
- dec := json.NewDecoder(bytes.NewReader(line))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(line))
var obj map[string]any
- if err := dec.Decode(&obj); err != nil {
+ if err := json.UnmarshalDecode(dec, &obj, jsonx.V1(), jsonx.UseNumber()); err != nil {
if f.ignoreBad {
continue
}
diff --git a/services/athena/engine_parquet.go b/services/athena/engine_parquet.go
index 6b0c57108a..6c6ff513c7 100644
--- a/services/athena/engine_parquet.go
+++ b/services/athena/engine_parquet.go
@@ -3,13 +3,14 @@ package athena
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
"strconv"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/parquetio"
)
@@ -124,7 +125,7 @@ func parquetCell(v any) string {
case bool:
return strconv.FormatBool(t)
default:
- b, err := json.Marshal(t)
+ b, err := json.Marshal(t, jsonx.V1())
if err != nil {
return fmt.Sprint(t)
}
diff --git a/services/athena/engine_write_format.go b/services/athena/engine_write_format.go
index 61de56b966..78b83648ec 100644
--- a/services/athena/engine_write_format.go
+++ b/services/athena/engine_write_format.go
@@ -3,10 +3,12 @@ package athena
import (
"bytes"
"encoding/csv"
- "encoding/json"
+ "encoding/json/v2"
"math"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func encodeTextRows(res *sqlResult, rows []int, cols []outCol, delim string) []byte {
@@ -63,7 +65,7 @@ func encodeJSONLines(res *sqlResult, rows []int, cols []outCol) []byte {
buf.WriteByte(',')
}
- key, _ := json.Marshal(c.name)
+ key, _ := json.Marshal(c.name, jsonx.V1())
buf.Write(key)
buf.WriteByte(':')
@@ -99,7 +101,7 @@ func jsonLiteral(v string, null bool, typ string) string {
case kindString, kindText:
}
- b, _ := json.Marshal(strings.ToValidUTF8(v, "�"))
+ b, _ := json.Marshal(strings.ToValidUTF8(v, "�"), jsonx.V1())
return string(b)
}
diff --git a/services/athena/error_codes_test.go b/services/athena/error_codes_test.go
index 2df0489093..6106a6af88 100644
--- a/services/athena/error_codes_test.go
+++ b/services/athena/error_codes_test.go
@@ -1,12 +1,14 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_DistinctErrorCodes verifies that Athena operations surface the
@@ -110,7 +112,7 @@ func TestHandler_DistinctErrorCodes(t *testing.T) {
"Athena client errors are HTTP 400")
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"],
"error __type must be AWS-accurate for %s", tt.action)
diff --git a/services/athena/handler.go b/services/athena/handler.go
index 7d1742534f..01967675aa 100644
--- a/services/athena/handler.go
+++ b/services/athena/handler.go
@@ -2,7 +2,7 @@ package athena
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -17,6 +17,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -266,7 +267,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var env resourceNameEnvelope
- if uerr := json.Unmarshal(body, &env); uerr != nil {
+ if uerr := json.Unmarshal(body, &env, jsonx.V1()); uerr != nil {
return ""
}
@@ -336,7 +337,7 @@ func (h *Handler) doDispatch(_ context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(response)
+ return json.Marshal(response, jsonx.V1())
}
// handleError writes a standardized error response back to the client.
@@ -387,7 +388,7 @@ func (h *Handler) handleError(
Message: stripExceptionPrefix(reqErr.Error()),
}
- payload, _ := json.Marshal(errResp)
+ payload, _ := json.Marshal(errResp, jsonx.V1())
return c.JSONBlob(statusCode, payload)
}
diff --git a/services/athena/handler_calculations.go b/services/athena/handler_calculations.go
index 369c5ec406..ec285e8322 100644
--- a/services/athena/handler_calculations.go
+++ b/services/athena/handler_calculations.go
@@ -1,6 +1,10 @@
package athena
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
type startCalculationInput struct {
SessionID string `json:"SessionId"`
@@ -24,7 +28,7 @@ func (h *Handler) calcCoreOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"StartCalculationExecution": func(b []byte) (any, error) {
var input startCalculationInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -52,7 +56,7 @@ func (h *Handler) calcCoreOps() map[string]athenaActionFn {
},
"GetCalculationExecution": func(b []byte) (any, error) {
var input calculationIDInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -73,7 +77,7 @@ func (h *Handler) calcCoreOps() map[string]athenaActionFn {
},
"GetCalculationExecutionStatus": func(b []byte) (any, error) {
var input calculationIDInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -86,7 +90,7 @@ func (h *Handler) calcCoreOps() map[string]athenaActionFn {
},
"GetCalculationExecutionCode": func(b []byte) (any, error) {
var input calculationIDInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -104,7 +108,7 @@ func (h *Handler) calcControlOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"StopCalculationExecution": func(b []byte) (any, error) {
var input calculationIDInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -117,7 +121,7 @@ func (h *Handler) calcControlOps() map[string]athenaActionFn {
},
"ListCalculationExecutions": func(b []byte) (any, error) {
var input listCalculationsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/athena/handler_capacity_reservations.go b/services/athena/handler_capacity_reservations.go
index cc3b71622c..7eb649ce0f 100644
--- a/services/athena/handler_capacity_reservations.go
+++ b/services/athena/handler_capacity_reservations.go
@@ -1,6 +1,10 @@
package athena
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
type cancelCapacityReservationInput struct {
Name string `json:"Name"`
@@ -38,7 +42,7 @@ func (h *Handler) capacityReservationOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"CreateCapacityReservation": func(b []byte) (any, error) {
var input createCapacityReservationInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -48,7 +52,7 @@ func (h *Handler) capacityReservationOps() map[string]athenaActionFn {
},
"CancelCapacityReservation": func(b []byte) (any, error) {
var input cancelCapacityReservationInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -56,7 +60,7 @@ func (h *Handler) capacityReservationOps() map[string]athenaActionFn {
},
"DeleteCapacityReservation": func(b []byte) (any, error) {
var input deleteCapacityReservationInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -69,7 +73,7 @@ func (h *Handler) capacityExtraOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"GetCapacityReservation": func(b []byte) (any, error) {
var input capacityReservationNameInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -82,7 +86,7 @@ func (h *Handler) capacityExtraOps() map[string]athenaActionFn {
},
"ListCapacityReservations": func(b []byte) (any, error) {
var input listPageInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -106,7 +110,7 @@ func (h *Handler) capacityExtraOps() map[string]athenaActionFn {
},
"UpdateCapacityReservation": func(b []byte) (any, error) {
var input updateCapacityReservationInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -114,7 +118,7 @@ func (h *Handler) capacityExtraOps() map[string]athenaActionFn {
},
"PutCapacityAssignmentConfiguration": func(b []byte) (any, error) {
var input putCapacityAssignmentInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -124,7 +128,7 @@ func (h *Handler) capacityExtraOps() map[string]athenaActionFn {
},
"GetCapacityAssignmentConfiguration": func(b []byte) (any, error) {
var input getCapacityAssignmentInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/athena/handler_capacity_reservations_test.go b/services/athena/handler_capacity_reservations_test.go
index e7cf59bddf..72d2851bb5 100644
--- a/services/athena/handler_capacity_reservations_test.go
+++ b/services/athena/handler_capacity_reservations_test.go
@@ -1,13 +1,14 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/athena"
)
@@ -51,7 +52,7 @@ func TestHandler_CreateCapacityReservation(t *testing.T) {
if tt.wantErr {
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.NotEmpty(t, errResp["__type"])
}
})
@@ -239,7 +240,7 @@ func TestHandler_CapacityReservation_LastAllocationStruct(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cr := resp["CapacityReservation"].(map[string]any)
assert.Equal(t, "ACTIVE", cr["Status"])
@@ -263,7 +264,7 @@ func TestHandler_CapacityReservation_UpdateLastAllocation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cr := resp["CapacityReservation"].(map[string]any)
assert.InDelta(t, float64(30), cr["TargetDpus"], 0.001)
@@ -330,7 +331,7 @@ func TestHandler_GetCapacityReservation_NoInventedTagsField(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasTags := resp["CapacityReservation"]["Tags"]
assert.False(t, hasTags, "CapacityReservation response must not carry an invented Tags field")
@@ -339,7 +340,7 @@ func TestHandler_GetCapacityReservation_NoInventedTagsField(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var tagsResp map[string][]map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1()))
require.Len(t, tagsResp["Tags"], 1, "the tag set at creation must still be visible via ListTagsForResource")
assert.Equal(t, "owner", tagsResp["Tags"][0]["Key"])
}
diff --git a/services/athena/handler_data_catalogs.go b/services/athena/handler_data_catalogs.go
index cbae21ed03..7fd7e37500 100644
--- a/services/athena/handler_data_catalogs.go
+++ b/services/athena/handler_data_catalogs.go
@@ -1,6 +1,10 @@
package athena
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
// dataCatalogRespKey is the JSON response key CreateDataCatalog,
// GetDataCatalog, and DeleteDataCatalog all wrap their DataCatalog payload
@@ -40,7 +44,7 @@ func (h *Handler) dataCatalogOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"CreateDataCatalog": func(b []byte) (any, error) {
var input createDataCatalogInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -59,7 +63,7 @@ func (h *Handler) dataCatalogOps() map[string]athenaActionFn {
},
"GetDataCatalog": func(b []byte) (any, error) {
var input getDataCatalogInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -72,7 +76,7 @@ func (h *Handler) dataCatalogOps() map[string]athenaActionFn {
},
"ListDataCatalogs": func(b []byte) (any, error) {
var input listDataCatalogsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -90,7 +94,7 @@ func (h *Handler) dataCatalogOps() map[string]athenaActionFn {
},
"UpdateDataCatalog": func(b []byte) (any, error) {
var input updateDataCatalogInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -100,7 +104,7 @@ func (h *Handler) dataCatalogOps() map[string]athenaActionFn {
},
"DeleteDataCatalog": func(b []byte) (any, error) {
var input deleteDataCatalogInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/athena/handler_data_catalogs_test.go b/services/athena/handler_data_catalogs_test.go
index e92165fa74..1aa73a4f20 100644
--- a/services/athena/handler_data_catalogs_test.go
+++ b/services/athena/handler_data_catalogs_test.go
@@ -1,7 +1,7 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/athena"
)
@@ -86,7 +87,7 @@ func TestHandler_GetDataCatalog(t *testing.T) {
if tt.wantType != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dc, _ := resp["DataCatalog"].(map[string]any)
require.NotNil(t, dc)
assert.Equal(t, tt.wantType, dc["Type"])
@@ -110,7 +111,7 @@ func TestHandler_CreateDataCatalog_ReturnsDataCatalog(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dc, ok := resp["DataCatalog"].(map[string]any)
require.True(t, ok, "CreateDataCatalogOutput must carry a DataCatalog object")
assert.Equal(t, "created-cat", dc["Name"])
@@ -145,7 +146,7 @@ func TestHandler_ListDataCatalogs(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, _ := resp["DataCatalogsSummary"].([]any)
assert.GreaterOrEqual(t, len(list), 1)
})
@@ -239,7 +240,7 @@ func TestHandler_DeleteDataCatalog_ReturnsDataCatalog(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dc, ok := resp["DataCatalog"].(map[string]any)
require.True(t, ok, "DeleteDataCatalogOutput must carry the deleted DataCatalog object")
assert.Equal(t, "del-cat2", dc["Name"])
@@ -300,7 +301,7 @@ func TestHandler_DeleteDataCatalog_FederatedReportsDeleteComplete(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dc, ok := resp["DataCatalog"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "DELETE_COMPLETE", dc["Status"])
@@ -386,7 +387,7 @@ func TestHandler_DataCatalog_FederatedStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dc := resp["DataCatalog"].(map[string]any)
assert.Equal(t, tt.wantStatus, dc["Status"])
@@ -408,7 +409,7 @@ func TestHandler_DataCatalog_ListIncludesStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["DataCatalogsSummary"].([]any)
require.True(t, ok)
@@ -505,7 +506,7 @@ func TestListDataCatalogs_NextTokenOmittedWhenEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasNextToken := resp["NextToken"]
assert.False(t, hasNextToken,
@@ -627,7 +628,7 @@ func TestAwsDataCatalog_BuiltIn(t *testing.T) {
if tt.check != nil {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tt.check(t, resp)
}
})
diff --git a/services/athena/handler_databases.go b/services/athena/handler_databases.go
index af22e51b24..b1d85b9519 100644
--- a/services/athena/handler_databases.go
+++ b/services/athena/handler_databases.go
@@ -1,6 +1,10 @@
package athena
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
type getDatabaseInput struct {
CatalogName string `json:"CatalogName"`
@@ -31,7 +35,7 @@ func (h *Handler) databaseOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"GetDatabase": func(b []byte) (any, error) {
var input getDatabaseInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -44,7 +48,7 @@ func (h *Handler) databaseOps() map[string]athenaActionFn {
},
"ListDatabases": func(b []byte) (any, error) {
var input listDatabasesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -68,7 +72,7 @@ func (h *Handler) databaseOps() map[string]athenaActionFn {
},
"GetTableMetadata": func(b []byte) (any, error) {
var input getTableMetadataInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -81,7 +85,7 @@ func (h *Handler) databaseOps() map[string]athenaActionFn {
},
"ListTableMetadata": func(b []byte) (any, error) {
var input listTableMetadataInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/athena/handler_named_queries.go b/services/athena/handler_named_queries.go
index c458c88472..97d0c416e4 100644
--- a/services/athena/handler_named_queries.go
+++ b/services/athena/handler_named_queries.go
@@ -1,8 +1,10 @@
package athena
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createNamedQueryInput struct {
@@ -44,7 +46,7 @@ func (h *Handler) namedQueryOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"CreateNamedQuery": func(b []byte) (any, error) {
var input createNamedQueryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -65,7 +67,7 @@ func (h *Handler) namedQueryOps() map[string]athenaActionFn {
},
"GetNamedQuery": func(b []byte) (any, error) {
var input getNamedQueryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -78,7 +80,7 @@ func (h *Handler) namedQueryOps() map[string]athenaActionFn {
},
"ListNamedQueries": func(b []byte) (any, error) {
var input listNamedQueriesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -100,7 +102,7 @@ func (h *Handler) namedQueryOps() map[string]athenaActionFn {
},
"BatchGetNamedQuery": func(b []byte) (any, error) {
var input batchGetNamedQueryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -121,7 +123,7 @@ func (h *Handler) namedQueryOps() map[string]athenaActionFn {
},
"DeleteNamedQuery": func(b []byte) (any, error) {
var input deleteNamedQueryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -136,7 +138,7 @@ func (h *Handler) namedQueryUpdateOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"UpdateNamedQuery": func(b []byte) (any, error) {
var input updateNamedQueryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/athena/handler_named_queries_test.go b/services/athena/handler_named_queries_test.go
index dc103000fc..c712851e37 100644
--- a/services/athena/handler_named_queries_test.go
+++ b/services/athena/handler_named_queries_test.go
@@ -1,13 +1,14 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/athena"
)
@@ -39,7 +40,7 @@ func TestHandler_CreateNamedQuery(t *testing.T) {
if tt.wantID {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["NamedQueryId"])
}
})
@@ -80,7 +81,7 @@ func TestHandler_GetNamedQuery(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
queryID = created["NamedQueryId"]
}
@@ -89,7 +90,7 @@ func TestHandler_GetNamedQuery(t *testing.T) {
if tt.wantQuery {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
nq, _ := resp["NamedQuery"].(map[string]any)
require.NotNil(t, nq)
assert.Equal(t, "q", nq["Name"])
@@ -127,7 +128,7 @@ func TestHandler_ListNamedQueries(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string][]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["NamedQueryIds"], tt.wantCount)
})
}
@@ -157,7 +158,7 @@ func TestHandler_BatchGetNamedQuery(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
id := created["NamedQueryId"]
rec := doRequest(t, h, "BatchGetNamedQuery",
@@ -165,7 +166,7 @@ func TestHandler_BatchGetNamedQuery(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
found, _ := resp["NamedQueries"].([]any)
assert.Len(t, found, 1)
unprocessed, _ := resp["UnprocessedNamedQueryIds"].([]any)
@@ -203,7 +204,7 @@ func TestHandler_DeleteNamedQuery(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var cr map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
rec := doRequest(t, h, "DeleteNamedQuery", `{"NamedQueryId":"`+cr["NamedQueryId"]+`"}`)
assert.Equal(t, tt.wantStatus, rec.Code)
@@ -430,14 +431,14 @@ func TestBatchGetNamedQuery_UnprocessedShape(t *testing.T) {
}
}
- body, err := json.Marshal(map[string]any{"NamedQueryIds": tt.ids})
+ body, err := json.Marshal(map[string]any{"NamedQueryIds": tt.ids}, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, "BatchGetNamedQuery", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
found, _ := resp["NamedQueries"].([]any)
assert.Len(t, found, tt.wantFound)
@@ -492,7 +493,7 @@ func TestCreateNamedQuery_WorkgroupValidation(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Contains(t, errResp["__type"], "InvalidRequestException")
}
})
diff --git a/services/athena/handler_notebooks.go b/services/athena/handler_notebooks.go
index b709648519..e891982adf 100644
--- a/services/athena/handler_notebooks.go
+++ b/services/athena/handler_notebooks.go
@@ -1,8 +1,10 @@
package athena
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// createNotebookInput has no Tags: the real CreateNotebookInput carries only Name/WorkGroup/ClientRequestToken.
@@ -88,7 +90,7 @@ func (h *Handler) notebookOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"CreateNotebook": func(b []byte) (any, error) {
var input createNotebookInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -105,7 +107,7 @@ func (h *Handler) notebookOps() map[string]athenaActionFn {
},
"CreatePresignedNotebookUrl": func(b []byte) (any, error) {
var input createPresignedNotebookURLInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -129,7 +131,7 @@ func (h *Handler) notebookOps() map[string]athenaActionFn {
},
"DeleteNotebook": func(b []byte) (any, error) {
var input deleteNotebookInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -137,7 +139,7 @@ func (h *Handler) notebookOps() map[string]athenaActionFn {
},
"ExportNotebook": func(b []byte) (any, error) {
var input exportNotebookInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -158,7 +160,7 @@ func (h *Handler) notebookExtraOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"GetNotebookMetadata": func(b []byte) (any, error) {
var input notebookIDInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -171,7 +173,7 @@ func (h *Handler) notebookExtraOps() map[string]athenaActionFn {
},
"ListNotebookMetadata": func(b []byte) (any, error) {
var input listNotebookMetadataInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -195,7 +197,7 @@ func (h *Handler) notebookExtraOps() map[string]athenaActionFn {
},
"ImportNotebook": func(b []byte) (any, error) {
var input importNotebookInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -218,7 +220,7 @@ func (h *Handler) notebookExtraOps() map[string]athenaActionFn {
},
"UpdateNotebook": func(b []byte) (any, error) {
var input updateNotebookInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -226,7 +228,7 @@ func (h *Handler) notebookExtraOps() map[string]athenaActionFn {
},
"UpdateNotebookMetadata": func(b []byte) (any, error) {
var input updateNotebookMetadataInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/athena/handler_notebooks_test.go b/services/athena/handler_notebooks_test.go
index 2b46915723..dcec04c9ea 100644
--- a/services/athena/handler_notebooks_test.go
+++ b/services/athena/handler_notebooks_test.go
@@ -1,13 +1,14 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/athena"
)
@@ -38,7 +39,7 @@ func TestHandler_CreateNotebook(t *testing.T) {
if tt.wantID {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["NotebookId"])
}
})
@@ -74,7 +75,7 @@ func TestHandler_CreatePresignedNotebookUrl(t *testing.T) {
if tt.wantURL {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["NotebookUrl"], tt.sessionID)
assert.NotEmpty(t, resp["AuthToken"])
assert.Positive(t, resp["AuthTokenExpirationTime"])
@@ -98,7 +99,7 @@ func TestHandler_DeleteNotebook(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var cr map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
return cr["NotebookId"]
},
@@ -142,7 +143,7 @@ func TestHandler_ExportNotebook(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var cr map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
return cr["NotebookId"]
},
@@ -170,7 +171,7 @@ func TestHandler_ExportNotebook(t *testing.T) {
if tt.wantName != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
meta, _ := resp["NotebookMetadata"].(map[string]any)
require.NotNil(t, meta, "NotebookMetadata should be present")
assert.Equal(t, tt.wantName, meta["Name"])
diff --git a/services/athena/handler_prepared_statements.go b/services/athena/handler_prepared_statements.go
index 96ef54cb51..0f0487c292 100644
--- a/services/athena/handler_prepared_statements.go
+++ b/services/athena/handler_prepared_statements.go
@@ -1,8 +1,10 @@
package athena
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// batchGetPreparedStatementInput's StatementNames field is tagged
@@ -49,7 +51,7 @@ func (h *Handler) preparedStatementOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"CreatePreparedStatement": func(b []byte) (any, error) {
var input createPreparedStatementInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -59,7 +61,7 @@ func (h *Handler) preparedStatementOps() map[string]athenaActionFn {
},
"BatchGetPreparedStatement": func(b []byte) (any, error) {
var input batchGetPreparedStatementInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -83,7 +85,7 @@ func (h *Handler) preparedStatementOps() map[string]athenaActionFn {
},
"DeletePreparedStatement": func(b []byte) (any, error) {
var input deletePreparedStatementInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -94,7 +96,7 @@ func (h *Handler) preparedStatementOps() map[string]athenaActionFn {
},
"GetPreparedStatement": func(b []byte) (any, error) {
var input getPreparedStatementInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -107,7 +109,7 @@ func (h *Handler) preparedStatementOps() map[string]athenaActionFn {
},
"ListPreparedStatements": func(b []byte) (any, error) {
var input listPreparedStatementsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -129,7 +131,7 @@ func (h *Handler) preparedStatementOps() map[string]athenaActionFn {
},
"UpdatePreparedStatement": func(b []byte) (any, error) {
var input updatePreparedStatementInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/athena/handler_prepared_statements_test.go b/services/athena/handler_prepared_statements_test.go
index a6ba564dc1..2466f86f98 100644
--- a/services/athena/handler_prepared_statements_test.go
+++ b/services/athena/handler_prepared_statements_test.go
@@ -1,13 +1,14 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/athena"
)
@@ -52,7 +53,7 @@ func TestHandler_CreatePreparedStatement(t *testing.T) {
if tt.wantErr {
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.NotEmpty(t, errResp["__type"])
}
})
@@ -97,7 +98,7 @@ func TestHandler_BatchGetPreparedStatement(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
found, _ := resp["PreparedStatements"].([]any)
assert.Len(t, found, tt.wantFound)
unprocessed, _ := resp["UnprocessedPreparedStatementNames"].([]any)
@@ -225,7 +226,7 @@ func TestHandler_GetPreparedStatement(t *testing.T) {
if tt.wantName != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ps, _ := resp["PreparedStatement"].(map[string]any)
require.NotNil(t, ps, "PreparedStatement must be present")
assert.Equal(t, tt.wantName, ps["StatementName"])
@@ -277,7 +278,7 @@ func TestHandler_ListPreparedStatements(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string][]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["PreparedStatements"], tt.wantCount)
})
}
@@ -294,7 +295,7 @@ func TestHandler_ListPreparedStatements_ReturnsSummary(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string][]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp["PreparedStatements"], 1)
sum := resp["PreparedStatements"][0]
diff --git a/services/athena/handler_query_executions.go b/services/athena/handler_query_executions.go
index 0f7db22af4..335c2cbcff 100644
--- a/services/athena/handler_query_executions.go
+++ b/services/athena/handler_query_executions.go
@@ -1,8 +1,10 @@
package athena
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type startQueryExecutionInput struct {
@@ -46,7 +48,7 @@ func (h *Handler) queryExecutionOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"StartQueryExecution": func(b []byte) (any, error) {
var input startQueryExecutionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -76,7 +78,7 @@ func (h *Handler) queryExecutionOps() map[string]athenaActionFn {
},
"StopQueryExecution": func(b []byte) (any, error) {
var input stopQueryExecutionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -84,7 +86,7 @@ func (h *Handler) queryExecutionOps() map[string]athenaActionFn {
},
"GetQueryExecution": func(b []byte) (any, error) {
var input getQueryExecutionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -98,7 +100,7 @@ func (h *Handler) queryExecutionOps() map[string]athenaActionFn {
"ListQueryExecutions": h.handleListQueryExecutions,
"BatchGetQueryExecution": func(b []byte) (any, error) {
var input batchGetQueryExecutionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -120,7 +122,7 @@ func (h *Handler) queryExecutionOps() map[string]athenaActionFn {
"GetQueryResults": h.handleGetQueryResults,
"GetQueryRuntimeStatistics": func(b []byte) (any, error) {
var input getQueryRuntimeStatsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -142,7 +144,7 @@ const athenaMaxQueryResultsPageSize = 1000
// opaque-token pagination.
func (h *Handler) handleListQueryExecutions(b []byte) (any, error) {
var input listQueryExecutionsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -176,7 +178,7 @@ type getQueryResultsInput struct {
// Real rows are returned for SELECT queries against registered in-memory tables.
func (h *Handler) handleGetQueryResults(b []byte) (any, error) {
var input getQueryResultsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", ErrValidation, err)
}
diff --git a/services/athena/handler_query_executions_test.go b/services/athena/handler_query_executions_test.go
index 96c54cf59f..54eb8396cb 100644
--- a/services/athena/handler_query_executions_test.go
+++ b/services/athena/handler_query_executions_test.go
@@ -1,7 +1,7 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/athena"
)
@@ -41,7 +42,7 @@ func TestHandler_StartQueryExecution(t *testing.T) {
if tt.wantID {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["QueryExecutionId"])
}
})
@@ -81,7 +82,7 @@ func TestHandler_GetQueryExecution(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
execID = created["QueryExecutionId"]
}
@@ -90,7 +91,7 @@ func TestHandler_GetQueryExecution(t *testing.T) {
if tt.wantState != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
qe, ok := resp["QueryExecution"].(map[string]any)
require.True(t, ok)
status, ok := qe["Status"].(map[string]any)
@@ -118,7 +119,7 @@ func TestHandler_StopQueryExecution(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var cr map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &cr, jsonx.V1()))
execID := cr["QueryExecutionId"]
// Override state to RUNNING so it can be stopped.
@@ -142,7 +143,7 @@ func TestHandler_StopQueryExecution(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var cr map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &cr, jsonx.V1()))
return cr["QueryExecutionId"] // default state is SUCCEEDED
},
@@ -191,7 +192,7 @@ func TestHandler_ListQueryExecutions(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ids, _ := resp["QueryExecutionIds"].([]any)
assert.Len(t, ids, tt.wantCount)
})
@@ -221,7 +222,7 @@ func TestHandler_BatchGetQueryExecution(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var cr map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &cr, jsonx.V1()))
id := cr["QueryExecutionId"]
rec := doRequest(t, h, "BatchGetQueryExecution",
@@ -229,7 +230,7 @@ func TestHandler_BatchGetQueryExecution(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
found, _ := resp["QueryExecutions"].([]any)
assert.Len(t, found, 1)
unprocessed, _ := resp["UnprocessedQueryExecutionIds"].([]any)
@@ -272,7 +273,7 @@ func TestHandler_QueryExecution_EngineVersionAndStatementType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
qe := resp["QueryExecution"].(map[string]any)
assert.Equal(t, tt.wantStatementType, qe["StatementType"])
@@ -302,7 +303,7 @@ func TestHandler_QueryExecution_ExecutionParameters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
qe := resp["QueryExecution"].(map[string]any)
params, ok := qe["ExecutionParameters"].([]any)
@@ -334,7 +335,7 @@ func TestHandler_ResultConfiguration_AclAndOwner(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
qe := resp["QueryExecution"].(map[string]any)
rc := qe["ResultConfiguration"].(map[string]any)
@@ -522,7 +523,7 @@ func TestStartQueryExecution_EmptyQueryStringRejected(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Contains(t, errResp["__type"], "InvalidRequestException",
"empty QueryString must return InvalidRequestException")
}
@@ -560,7 +561,7 @@ func TestListQueryExecutions_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp listResp
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.LessOrEqual(t, len(resp.QueryExecutionIDs), 2, "page exceeds MaxResults")
for _, id := range resp.QueryExecutionIDs {
@@ -595,7 +596,7 @@ func TestListQueryExecutions_NextTokenOmittedOnLastPage(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasToken := resp["NextToken"]
assert.False(t, hasToken, "NextToken must be omitted on the last page")
}
@@ -760,14 +761,14 @@ func TestBatchGetQueryExecution_UnprocessedShape(t *testing.T) {
}
}
- body, err := json.Marshal(map[string]any{"QueryExecutionIds": tt.ids})
+ body, err := json.Marshal(map[string]any{"QueryExecutionIds": tt.ids}, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, "BatchGetQueryExecution", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
found, _ := resp["QueryExecutions"].([]any)
assert.Len(t, found, tt.wantFound)
@@ -822,7 +823,7 @@ func TestStartQueryExecution_WorkgroupValidation(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Contains(t, errResp["__type"], "InvalidRequestException")
}
})
diff --git a/services/athena/handler_sessions.go b/services/athena/handler_sessions.go
index 926628ff9d..39df596131 100644
--- a/services/athena/handler_sessions.go
+++ b/services/athena/handler_sessions.go
@@ -1,6 +1,10 @@
package athena
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
const (
keySessionID = "SessionId"
@@ -72,7 +76,7 @@ type getResourceDashboardInput struct {
func (h *Handler) handleStartSession(b []byte) (any, error) {
var input startSessionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -123,7 +127,7 @@ func (h *Handler) sessionCoreOps() map[string]athenaActionFn {
"StartSession": h.handleStartSession,
"GetSession": func(b []byte) (any, error) {
var input sessionIDInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -147,7 +151,7 @@ func (h *Handler) sessionCoreOps() map[string]athenaActionFn {
},
"GetSessionStatus": func(b []byte) (any, error) {
var input sessionIDInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -160,7 +164,7 @@ func (h *Handler) sessionCoreOps() map[string]athenaActionFn {
},
"GetSessionEndpoint": func(b []byte) (any, error) {
var input sessionIDInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -177,7 +181,7 @@ func (h *Handler) sessionCoreOps() map[string]athenaActionFn {
},
"TerminateSession": func(b []byte) (any, error) {
var input sessionIDInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -195,7 +199,7 @@ func (h *Handler) sessionListOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"ListSessions": func(b []byte) (any, error) {
var input listSessionsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -219,7 +223,7 @@ func (h *Handler) sessionListOps() map[string]athenaActionFn {
},
"ListNotebookSessions": func(b []byte) (any, error) {
var input listNotebookSessionsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -250,7 +254,7 @@ func (h *Handler) sessionInfoOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"ListExecutors": func(b []byte) (any, error) {
var input listExecutorsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -274,7 +278,7 @@ func (h *Handler) sessionInfoOps() map[string]athenaActionFn {
},
"ListEngineVersions": func(b []byte) (any, error) {
var input listPageInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -293,7 +297,7 @@ func (h *Handler) sessionInfoOps() map[string]athenaActionFn {
},
"ListApplicationDPUSizes": func(b []byte) (any, error) {
var input listPageInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -309,7 +313,7 @@ func (h *Handler) sessionInfoOps() map[string]athenaActionFn {
},
"GetResourceDashboard": func(b []byte) (any, error) {
var input getResourceDashboardInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/athena/handler_sessions_test.go b/services/athena/handler_sessions_test.go
index 4d684ae137..737d6e6cd4 100644
--- a/services/athena/handler_sessions_test.go
+++ b/services/athena/handler_sessions_test.go
@@ -1,7 +1,7 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/athena"
)
@@ -17,7 +18,7 @@ func jsonNested(t *testing.T, body []byte, keys ...string) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(body, &m))
+ require.NoError(t, json.Unmarshal(body, &m, jsonx.V1()))
cur := m
for _, k := range keys {
@@ -296,7 +297,7 @@ func TestHandler_ListSessions(t *testing.T) {
if tt.wantNonEmpty {
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
assert.NotEmpty(t, m["Sessions"])
}
})
@@ -409,7 +410,7 @@ func TestHandler_ListNotebookSessions(t *testing.T) {
var body struct {
NotebookSessionsList []map[string]any `json:"NotebookSessionsList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body.NotebookSessionsList, 1, "expected the started session to be linked to its notebook")
}
})
@@ -458,7 +459,7 @@ func TestHandler_ListEngineVersions_NoInventedAuthEngineVersionField(t *testing.
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string][]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotEmpty(t, resp["EngineVersions"])
for _, ev := range resp["EngineVersions"] {
@@ -744,7 +745,7 @@ func TestGetSessionEndpoint_UsesConfiguredRegion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
url, _ := resp["EndpointUrl"].(string)
assert.Contains(t, url, tt.wantPrefix,
diff --git a/services/athena/handler_tags.go b/services/athena/handler_tags.go
index 09e77c5e77..27c78fb264 100644
--- a/services/athena/handler_tags.go
+++ b/services/athena/handler_tags.go
@@ -1,6 +1,10 @@
package athena
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
type tagResourceInput struct {
ResourceARN string `json:"ResourceARN"`
@@ -22,7 +26,7 @@ func (h *Handler) tagOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"TagResource": func(b []byte) (any, error) {
var input tagResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -30,7 +34,7 @@ func (h *Handler) tagOps() map[string]athenaActionFn {
},
"UntagResource": func(b []byte) (any, error) {
var input untagResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -38,7 +42,7 @@ func (h *Handler) tagOps() map[string]athenaActionFn {
},
"ListTagsForResource": func(b []byte) (any, error) {
var input listTagsForResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/athena/handler_tags_test.go b/services/athena/handler_tags_test.go
index 50c7ae6180..dc35997bfc 100644
--- a/services/athena/handler_tags_test.go
+++ b/services/athena/handler_tags_test.go
@@ -1,13 +1,14 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/athena"
)
@@ -98,7 +99,7 @@ func TestHandler_ListTagsForResource(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string][]map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["Tags"], tt.wantCount)
assert.Equal(t, "team", resp["Tags"][0]["Key"])
assert.Equal(t, "platform", resp["Tags"][0]["Value"])
@@ -126,7 +127,7 @@ func TestHandler_ListTagsForResource_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
tags1, _ := page1["Tags"].([]any)
require.Len(t, tags1, 2)
nextToken, ok := page1["NextToken"].(string)
@@ -138,7 +139,7 @@ func TestHandler_ListTagsForResource_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
tags2, _ := page2["Tags"].([]any)
require.Len(t, tags2, 1)
_, hasNextToken := page2["NextToken"]
diff --git a/services/athena/handler_test.go b/services/athena/handler_test.go
index b695e90060..f7784b160e 100644
--- a/services/athena/handler_test.go
+++ b/services/athena/handler_test.go
@@ -1,7 +1,7 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/athena"
)
@@ -51,7 +52,7 @@ func jsonField(t *testing.T, body []byte, key string) string {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(body, &m))
+ require.NoError(t, json.Unmarshal(body, &m, jsonx.V1()))
v, ok := m[key]
require.True(t, ok, "key %q missing", key)
@@ -75,7 +76,7 @@ func athenaDoPass5(t *testing.T, h *athena.Handler, action, body string) *httpte
func athenaUnmarshalPass5(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
@@ -104,7 +105,7 @@ func a1Unmarshal(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
@@ -136,7 +137,7 @@ func TestHandler_UnknownOperation(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.NotEmpty(t, errResp["__type"])
})
}
diff --git a/services/athena/handler_work_groups.go b/services/athena/handler_work_groups.go
index 9835e00e5f..88eb4eddb7 100644
--- a/services/athena/handler_work_groups.go
+++ b/services/athena/handler_work_groups.go
@@ -1,6 +1,10 @@
package athena
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
type listWorkGroupsInput struct {
NextToken string `json:"NextToken"`
@@ -37,7 +41,7 @@ func (h *Handler) workGroupOps() map[string]athenaActionFn {
return map[string]athenaActionFn{
"CreateWorkGroup": func(b []byte) (any, error) {
var input createWorkGroupInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -51,7 +55,7 @@ func (h *Handler) workGroupOps() map[string]athenaActionFn {
},
"GetWorkGroup": func(b []byte) (any, error) {
var input getWorkGroupInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -64,7 +68,7 @@ func (h *Handler) workGroupOps() map[string]athenaActionFn {
},
"ListWorkGroups": func(b []byte) (any, error) {
var input listWorkGroupsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
list, nextToken, err := h.Backend.ListWorkGroups(input.NextToken, input.MaxResults)
@@ -80,7 +84,7 @@ func (h *Handler) workGroupOps() map[string]athenaActionFn {
},
"UpdateWorkGroup": func(b []byte) (any, error) {
var input updateWorkGroupInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -90,7 +94,7 @@ func (h *Handler) workGroupOps() map[string]athenaActionFn {
},
"DeleteWorkGroup": func(b []byte) (any, error) {
var input deleteWorkGroupInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/athena/handler_work_groups_test.go b/services/athena/handler_work_groups_test.go
index 89a98c38a5..990a069216 100644
--- a/services/athena/handler_work_groups_test.go
+++ b/services/athena/handler_work_groups_test.go
@@ -1,7 +1,7 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/athena"
)
@@ -65,7 +66,7 @@ func TestHandler_CreateWorkGroup(t *testing.T) {
if tt.wantErr {
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.NotEmpty(t, errResp["__type"])
}
})
@@ -106,7 +107,7 @@ func TestHandler_GetWorkGroup(t *testing.T) {
if tt.wantName != "" {
var resp map[string]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantName, resp["WorkGroup"]["Name"])
}
})
@@ -122,7 +123,7 @@ func TestHandler_ListWorkGroups(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string][]map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.GreaterOrEqual(t, len(resp["WorkGroups"]), 1)
found := false
@@ -267,7 +268,7 @@ func TestHandler_GetWorkGroup_NoInventedTagsField(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasTags := resp["WorkGroup"]["Tags"]
assert.False(t, hasTags, "WorkGroup response must not carry an invented Tags field")
@@ -276,7 +277,7 @@ func TestHandler_GetWorkGroup_NoInventedTagsField(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var tagsResp map[string][]map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1()))
require.Len(t, tagsResp["Tags"], 1, "the tag set at creation must still be visible via ListTagsForResource")
assert.Equal(t, "env", tagsResp["Tags"][0]["Key"])
}
@@ -329,7 +330,7 @@ func TestHandler_WorkGroupConfiguration_EnforceAndExtras(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wg := resp["WorkGroup"].(map[string]any)
cfg := wg["Configuration"].(map[string]any)
@@ -358,7 +359,7 @@ func TestHandler_WorkGroupSummary_IncludesFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string][]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
var found map[string]any
for _, wg := range resp["WorkGroups"] {
@@ -537,7 +538,7 @@ func TestWorkGroup_StateValidation(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Contains(t, errResp["__type"], "InvalidRequestException",
"invalid State must return InvalidRequestException")
}
@@ -563,7 +564,7 @@ func TestCreateWorkGroup_AlwaysEnabled_NoStateMember(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wg, ok := resp["WorkGroup"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "ENABLED", wg["State"], "CreateWorkGroupInput has no State member; must always be ENABLED")
@@ -821,7 +822,7 @@ func TestHandler_UpdateWorkGroup_PreservesUnmentionedConfiguration(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wg, ok := resp["WorkGroup"].(map[string]any)
require.True(t, ok)
cfg, ok := wg["Configuration"].(map[string]any)
diff --git a/services/athena/multi_region_test.go b/services/athena/multi_region_test.go
index 5181d71bf5..47a77fc6dd 100644
--- a/services/athena/multi_region_test.go
+++ b/services/athena/multi_region_test.go
@@ -1,7 +1,7 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"slices"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/athena"
)
@@ -40,7 +41,7 @@ func regionCall(t *testing.T, h *athena.Handler, region, op, body string) map[st
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
out := map[string]any{}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/athena/pagination_opaque_test.go b/services/athena/pagination_opaque_test.go
index e2ad72f24f..4a365fbc2b 100644
--- a/services/athena/pagination_opaque_test.go
+++ b/services/athena/pagination_opaque_test.go
@@ -1,13 +1,14 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/athena"
)
@@ -24,7 +25,7 @@ func listPage(t *testing.T, h *athena.Handler, maxResults int, token string) ([]
NextToken string `json:"NextToken"`
QueryExecutionIDs []string `json:"QueryExecutionIds"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp.QueryExecutionIDs, resp.NextToken
}
@@ -53,7 +54,7 @@ func itoa(n int) string {
}
func jsonString(s string) string {
- b, _ := json.Marshal(s)
+ b, _ := json.Marshal(s, jsonx.V1())
return string(b)
}
@@ -142,7 +143,7 @@ func TestPagination_ForgedTokenRejected(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", errResp["__type"])
})
}
diff --git a/services/athena/persistence.go b/services/athena/persistence.go
index 9ab6e55d55..396586a58c 100644
--- a/services/athena/persistence.go
+++ b/services/athena/persistence.go
@@ -29,7 +29,7 @@ package athena
// mirroring services/appsync's apiKeys field.
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -189,7 +189,7 @@ func restoreQueryResults(m map[string]*sqlResultSnapshot) map[string]*sqlResult
// - ResourceTags (resourceTags map[string]map[string]string): values are
// map[string]string, not *T; already JSON-friendly as-is.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
QueryResults map[string]*sqlResultSnapshot `json:"queryResults,omitempty"`
TableData map[string][]map[string]any `json:"tableData,omitempty"`
ResourceTags map[string]map[string]string `json:"resourceTags,omitempty"`
@@ -310,7 +310,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreDirtyTablesLocked restores the "dirty" tables (databases, tables)
// from tables via the ephemeral DTO registry, converting each DTO back to
// its live type. The caller MUST hold b.mu for writing.
-func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]jsontext.Value) error {
dtoReg, dbDTOs, tblDTOs := newDirtyTableRegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
diff --git a/services/athena/prepared_exec_test.go b/services/athena/prepared_exec_test.go
index 7ec335e2ab..1ed3459dd7 100644
--- a/services/athena/prepared_exec_test.go
+++ b/services/athena/prepared_exec_test.go
@@ -1,12 +1,13 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/athena"
)
@@ -125,7 +126,7 @@ func fetchResultRows(t *testing.T, h *athena.Handler, id string) [][]string {
rec := doRequest(t, h, "GetQueryResults", `{"QueryExecutionId":"`+id+`"}`)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rs, _ := resp["ResultSet"].(map[string]any)
require.NotNil(t, rs)
diff --git a/services/athena/query_results_test.go b/services/athena/query_results_test.go
index 03ffbad80c..f1656ce9a3 100644
--- a/services/athena/query_results_test.go
+++ b/services/athena/query_results_test.go
@@ -1,7 +1,7 @@
package athena_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/athena"
)
@@ -107,7 +108,7 @@ func TestGetQueryResults_SQLExecution(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rs, _ := resp["ResultSet"].(map[string]any)
require.NotNil(t, rs)
@@ -182,7 +183,7 @@ func TestGetQueryResults_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
nextToken, _ := resp1["NextToken"].(string)
assert.NotEmpty(t, nextToken, "page 1 must have NextToken")
@@ -199,7 +200,7 @@ func TestGetQueryResults_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Empty(t, resp2["NextToken"], "last page must have no NextToken")
rs2 := resp2["ResultSet"].(map[string]any)
@@ -235,7 +236,7 @@ func TestGetQueryResults_CatalogQualifiedTable(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rs := resp["ResultSet"].(map[string]any)
allRows := rs["Rows"].([]any)
@@ -311,7 +312,7 @@ func TestHandler_GetQueryResults(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rs, _ := resp["ResultSet"].(map[string]any)
require.NotNil(t, rs, "ResultSet should be present")
rows, _ := rs["Rows"].([]any)
@@ -448,7 +449,7 @@ func TestGetQueryResults_NonSucceededQueryRejected(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Contains(t, errResp["__type"], "InvalidRequestException",
"GetQueryResults on a non-SUCCEEDED query must return InvalidRequestException")
msg, _ := errResp["message"].(string)
diff --git a/services/athena/result_object.go b/services/athena/result_object.go
index 9d33002272..965cdd6e1f 100644
--- a/services/athena/result_object.go
+++ b/services/athena/result_object.go
@@ -3,12 +3,14 @@ package athena
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"strings"
"github.com/aws/aws-sdk-go-v2/aws"
sdk_s3 "github.com/aws/aws-sdk-go-v2/service/s3"
sdk_s3_types "github.com/aws/aws-sdk-go-v2/service/s3/types"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -80,7 +82,7 @@ func resultMetadata(result *sqlResult) []byte {
cols[i] = col{Name: c.name, Type: c.typ}
}
- out, _ := json.Marshal(map[string]any{"columns": cols})
+ out, _ := json.Marshal(map[string]any{"columns": cols}, jsonx.V1())
return out
}
diff --git a/services/autoscaling/persistence.go b/services/autoscaling/persistence.go
index 11c000ed3a..5e68d1c130 100644
--- a/services/autoscaling/persistence.go
+++ b/services/autoscaling/persistence.go
@@ -2,7 +2,7 @@ package autoscaling
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -35,7 +35,7 @@ const autoscalingSnapshotVersion = 1
// map[string][]T/[]*T fields -- see store_setup.go's registerAllTables doc
// for why those three are not store.Table-backed.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Activities map[string][]ScalingActivity `json:"activities"`
InstanceRefreshes map[string][]*InstanceRefresh `json:"instanceRefreshes"`
NotificationConfigs map[string][]*NotificationConfiguration `json:"notificationConfigs"`
diff --git a/services/awsconfig/accounts.go b/services/awsconfig/accounts.go
index 96c030130f..002786c91a 100644
--- a/services/awsconfig/accounts.go
+++ b/services/awsconfig/accounts.go
@@ -1,11 +1,13 @@
package awsconfig
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"regexp"
"slices"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
)
@@ -98,7 +100,7 @@ func (s *accountSet) snapshot(base []byte, snap func(*Handler) []byte) []byte {
}
s.mu.RLock("snapshot")
- accounts := make(map[string]json.RawMessage, len(s.peers))
+ accounts := make(map[string]jsontext.Value, len(s.peers))
for a, p := range s.peers {
if data := snap(p); data != nil {
@@ -111,19 +113,19 @@ func (s *accountSet) snapshot(base []byte, snap func(*Handler) []byte) []byte {
return base
}
- doc := make(map[string]json.RawMessage)
- if err := json.Unmarshal(base, &doc); err != nil {
+ doc := make(map[string]jsontext.Value)
+ if err := json.Unmarshal(base, &doc, jsonx.V1()); err != nil {
return base
}
- enc, err := json.Marshal(accounts)
+ enc, err := json.Marshal(accounts, jsonx.V1())
if err != nil {
return base
}
doc[accountsSnapshotKey] = enc
- out, err := json.Marshal(doc)
+ out, err := json.Marshal(doc, jsonx.V1())
if err != nil {
return base
}
@@ -142,10 +144,10 @@ func (s *accountSet) restore(data []byte, restore func(*Handler, []byte) error)
}
var doc struct {
- Accounts map[string]json.RawMessage `json:"accounts"`
+ Accounts map[string]jsontext.Value `json:"accounts"`
}
- if err := json.Unmarshal(data, &doc); err != nil {
+ if err := json.Unmarshal(data, &doc, jsonx.V1()); err != nil {
return err
}
diff --git a/services/awsconfig/conformance_pack_template.go b/services/awsconfig/conformance_pack_template.go
index df28b10f3d..80a1720823 100644
--- a/services/awsconfig/conformance_pack_template.go
+++ b/services/awsconfig/conformance_pack_template.go
@@ -1,10 +1,13 @@
package awsconfig
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"sort"
"gopkg.in/yaml.v3"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// resourceTypeConfigRule is the CloudFormation resource type a conformance
@@ -32,7 +35,7 @@ type conformancePackConfigRuleResourceProperties struct {
ConfigRuleName string `json:"ConfigRuleName,omitempty"`
Description string `json:"Description,omitempty"`
MaximumExecutionFrequency string `json:"MaximumExecutionFrequency,omitempty"`
- InputParameters json.RawMessage `json:"InputParameters,omitempty"`
+ InputParameters jsontext.Value `json:"InputParameters,omitempty"`
}
// conformancePackTemplate is the minimal top-level shape parsed out of a
@@ -57,24 +60,24 @@ func parseConformancePackConfigRules(
jsonBody := []byte(templateBody)
var doc map[string]any
- if err := json.Unmarshal(jsonBody, &doc); err != nil {
+ if err := json.Unmarshal(jsonBody, &doc, jsonx.V1()); err != nil {
converted, yamlErr := yamlToJSON(jsonBody)
if yamlErr != nil {
return nil
}
- if jsonErr := json.Unmarshal(converted, &doc); jsonErr != nil {
+ if jsonErr := json.Unmarshal(converted, &doc, jsonx.V1()); jsonErr != nil {
return nil
}
}
- resolved, err := json.Marshal(resolveTemplateRefs(doc, templateParamValues(doc, params)))
+ resolved, err := json.Marshal(resolveTemplateRefs(doc, templateParamValues(doc, params)), jsonx.V1())
if err != nil {
return nil
}
var tmpl conformancePackTemplate
- if err = json.Unmarshal(resolved, &tmpl); err != nil {
+ if err = json.Unmarshal(resolved, &tmpl, jsonx.V1()); err != nil {
return nil
}
@@ -193,7 +196,7 @@ func yamlToJSON(body []byte) ([]byte, error) {
return nil, err
}
- return json.Marshal(v)
+ return json.Marshal(v, jsonx.V1())
}
// rewriteRefTags turns the YAML short form "!Ref Name" into the long form
diff --git a/services/awsconfig/delivery_status.go b/services/awsconfig/delivery_status.go
index a5a802fe24..462152426b 100644
--- a/services/awsconfig/delivery_status.go
+++ b/services/awsconfig/delivery_status.go
@@ -4,7 +4,7 @@ import (
"bytes"
"compress/gzip"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
s3pkg "github.com/blackbirdworks/gopherstack/services/s3"
)
@@ -384,7 +385,7 @@ func buildConfigSnapshotBody(snapshotID string, items []*ResourceConfigItem) ([]
envelope.ConfigurationItems = append(envelope.ConfigurationItems, *item)
}
- encoded, err := json.Marshal(envelope)
+ encoded, err := json.Marshal(envelope, jsonx.V1())
if err != nil {
return nil, err
}
@@ -461,7 +462,7 @@ func buildSnapshotSNSMessage(snapshotID, bucket, key string, t time.Time) string
RecordVersion: configSnapshotNotificationRecordVersion,
}
- encoded, err := json.Marshal(msg)
+ encoded, err := json.Marshal(msg, jsonx.V1())
if err != nil {
return ""
}
diff --git a/services/awsconfig/evaluation.go b/services/awsconfig/evaluation.go
index bba427a0e3..37c72636cd 100644
--- a/services/awsconfig/evaluation.go
+++ b/services/awsconfig/evaluation.go
@@ -1,11 +1,13 @@
package awsconfig
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Compliance type constants used by the evaluation engine.
@@ -109,7 +111,7 @@ func parseConfiguration(configuration string) map[string]any {
}
var m map[string]any
- if err := json.Unmarshal([]byte(configuration), &m); err != nil {
+ if err := json.Unmarshal([]byte(configuration), &m, jsonx.V1()); err != nil {
return map[string]any{}
}
@@ -125,7 +127,7 @@ func parseInputParameters(input string) map[string]string {
}
var raw map[string]any
- if err := json.Unmarshal([]byte(input), &raw); err != nil {
+ if err := json.Unmarshal([]byte(input), &raw, jsonx.V1()); err != nil {
return out
}
@@ -134,7 +136,7 @@ func parseInputParameters(input string) map[string]string {
case string:
out[k] = tv
default:
- b, _ := json.Marshal(tv)
+ b, _ := json.Marshal(tv, jsonx.V1())
out[k] = string(b)
}
}
diff --git a/services/awsconfig/evaluation_test.go b/services/awsconfig/evaluation_test.go
index e45be7f172..7f68e1eea2 100644
--- a/services/awsconfig/evaluation_test.go
+++ b/services/awsconfig/evaluation_test.go
@@ -1,7 +1,7 @@
package awsconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/awsconfig"
)
@@ -399,7 +400,7 @@ func TestHandler_DescribeConfigRulesPagination(t *testing.T) {
NextToken string `json:"NextToken"`
ConfigRules []awsconfig.ConfigRule `json:"ConfigRules"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
require.Len(t, page1.ConfigRules, 100)
require.NotEmpty(t, page1.NextToken)
@@ -410,7 +411,7 @@ func TestHandler_DescribeConfigRulesPagination(t *testing.T) {
NextToken string `json:"NextToken"`
ConfigRules []awsconfig.ConfigRule `json:"ConfigRules"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
require.Len(t, page2.ConfigRules, 50)
assert.Empty(t, page2.NextToken)
@@ -446,7 +447,7 @@ func TestHandler_GetComplianceDetailsByConfigRule_RealResults(t *testing.T) {
var out struct {
EvaluationResults []awsconfig.DetailedEvaluationResult `json:"EvaluationResults"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.EvaluationResults, 2)
got := complianceByResource(out.EvaluationResults)
@@ -475,7 +476,7 @@ func TestHandler_StartResourceEvaluationAndSummary(t *testing.T) {
var started struct {
ResourceEvaluationID string `json:"ResourceEvaluationId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &started))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &started, jsonx.V1()))
require.NotEmpty(t, started.ResourceEvaluationID)
assert.NotEqual(t, "eval-stub", started.ResourceEvaluationID)
@@ -514,7 +515,7 @@ func TestHandler_GetResourceConfigHistoryPagination(t *testing.T) {
NextToken string `json:"nextToken"`
ConfigurationItems []awsconfig.ResourceConfigItem `json:"configurationItems"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.ConfigurationItems, 1)
assert.Equal(t, `{"v":2}`, out.ConfigurationItems[0].Configuration)
require.NotEmpty(t, out.NextToken)
@@ -531,7 +532,7 @@ func TestHandler_GetResourceConfigHistoryPagination(t *testing.T) {
NextToken string `json:"nextToken"`
ConfigurationItems []awsconfig.ResourceConfigItem `json:"configurationItems"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out2, jsonx.V1()))
require.Len(t, out2.ConfigurationItems, 1)
assert.Equal(t, `{"v":1}`, out2.ConfigurationItems[0].Configuration)
assert.Empty(t, out2.NextToken)
diff --git a/services/awsconfig/handler.go b/services/awsconfig/handler.go
index 5889902770..cb98e61f41 100644
--- a/services/awsconfig/handler.go
+++ b/services/awsconfig/handler.go
@@ -2,7 +2,8 @@ package awsconfig
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -14,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
@@ -202,8 +204,8 @@ type configNameBody struct {
// extractNamedField extracts a top-level string field by key from a JSON body.
func extractNamedField(body []byte, key string) string {
- var m map[string]json.RawMessage
- if err := json.Unmarshal(body, &m); err != nil {
+ var m map[string]jsontext.Value
+ if err := json.Unmarshal(body, &m, jsonx.V1()); err != nil {
return ""
}
@@ -213,7 +215,7 @@ func extractNamedField(body []byte, key string) string {
}
var s string
- if err := json.Unmarshal(raw, &s); err != nil {
+ if err := json.Unmarshal(raw, &s, jsonx.V1()); err != nil {
return ""
}
@@ -279,13 +281,13 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// marshalError serialises a typed AWS error response into bytes.
// Marshaling a struct with only string fields cannot fail; error is intentionally ignored.
func marshalError(errType, message string) []byte {
- payload, _ := json.Marshal(service.JSONErrorResponse{Type: errType, Message: message})
+ payload, _ := json.Marshal(service.JSONErrorResponse{Type: errType, Message: message}, jsonx.V1())
return payload
}
@@ -350,15 +352,12 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
}
}
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
// configservice@v1.68.4 has no single code that fits every operation here:
// ValidationException is modeled on only 38 of its ~102 operations, the rest
// use InvalidParameterValueException or model no bad-request-class error at
// all (e.g. DeleteConfigurationAggregator declares only
// NoSuchConfigurationAggregatorException). Left untyped rather than guessing.
- if errors.Is(err, errUnknownAction) || errors.As(err, &syntaxErr) || errors.As(err, &typeErr) {
+ if errors.Is(err, errUnknownAction) || jsonx.IsSyntaxError(err) || jsonx.IsTypeError(err) {
return c.JSON(http.StatusBadRequest, map[string]string{"message": err.Error()})
}
diff --git a/services/awsconfig/handler_config_rules_test.go b/services/awsconfig/handler_config_rules_test.go
index 61c03acbab..82ce0a4259 100644
--- a/services/awsconfig/handler_config_rules_test.go
+++ b/services/awsconfig/handler_config_rules_test.go
@@ -1,7 +1,8 @@
package awsconfig_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/awsconfig"
)
@@ -61,7 +63,7 @@ func TestDescribeConfigRulesAcceptsFilters(t *testing.T) {
ConfigRuleName string `json:"ConfigRuleName"`
} `json:"ConfigRules"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.ConfigRules, 1)
assert.Equal(t, "rule-detective", out.ConfigRules[0].ConfigRuleName)
}
@@ -125,7 +127,7 @@ func TestConfigRuleScopeRoundtrip(t *testing.T) {
} `json:"Scope"`
} `json:"ConfigRules"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.ConfigRules, 1)
require.NotNil(t, out.ConfigRules[0].Scope)
assert.Contains(t, out.ConfigRules[0].Scope.ComplianceResourceTypes, "AWS::S3::Bucket")
@@ -251,7 +253,7 @@ func TestAWSConfigHandler_DescribeConfigRulesAndCompliance(t *testing.T) {
}
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotNil(t, out[tt.wantField])
})
}
@@ -394,11 +396,11 @@ func TestAWSConfigHandler_DescribeConfigRules_BackedByStorage(t *testing.T) {
rec := doAWSConfigRequest(t, h, "DescribeConfigRules", tt.body)
assert.Equal(t, tt.wantCode, rec.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
var rules []any
- require.NoError(t, json.Unmarshal(out["ConfigRules"], &rules))
+ require.NoError(t, json.Unmarshal(out["ConfigRules"], &rules, jsonx.V1()))
assert.Len(t, rules, tt.wantCount)
})
}
diff --git a/services/awsconfig/handler_configuration_recorders.go b/services/awsconfig/handler_configuration_recorders.go
index 4b2e534e3d..1484972f48 100644
--- a/services/awsconfig/handler_configuration_recorders.go
+++ b/services/awsconfig/handler_configuration_recorders.go
@@ -2,11 +2,12 @@ package awsconfig
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -50,7 +51,7 @@ type extractConfigRecorderNameInput struct {
func extractConfigRecorderName(body []byte) string {
var req extractConfigRecorderNameInput
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
return ""
}
@@ -59,7 +60,7 @@ func extractConfigRecorderName(body []byte) string {
func extractTopLevelRecorderName(body []byte) string {
var req configurationRecorderNameInput
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
return ""
}
@@ -72,7 +73,7 @@ type extractFirstRecorderNameInput struct {
func extractFirstRecorderName(body []byte) string {
var req extractFirstRecorderNameInput
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
return ""
}
if len(req.ConfigurationRecorderNames) > 0 {
diff --git a/services/awsconfig/handler_configuration_recorders_test.go b/services/awsconfig/handler_configuration_recorders_test.go
index 333f288072..60326b84ba 100644
--- a/services/awsconfig/handler_configuration_recorders_test.go
+++ b/services/awsconfig/handler_configuration_recorders_test.go
@@ -1,13 +1,15 @@
package awsconfig_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/awsconfig"
)
@@ -84,7 +86,7 @@ func TestRecorderRecordingGroupRoundtrip(t *testing.T) {
} `json:"recordingGroup"`
} `json:"ConfigurationRecorders"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.ConfigurationRecorders, 1)
require.NotNil(t, out.ConfigurationRecorders[0].RecordingGroup)
assert.True(t, out.ConfigurationRecorders[0].RecordingGroup.AllSupported)
@@ -130,7 +132,7 @@ func TestListConfigurationRecordersSummaries(t *testing.T) {
RecordingScope string `json:"recordingScope"`
} `json:"ConfigurationRecorderSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.ConfigurationRecorderSummaries, 1)
assert.Equal(t, "default", out.ConfigurationRecorderSummaries[0].Name)
assert.Contains(t, out.ConfigurationRecorderSummaries[0].Arn, "arn:aws:config:")
@@ -330,7 +332,7 @@ func TestAWSConfigHandler_DescribeConfigurationRecorderStatus(t *testing.T) {
} `json:"ConfigurationRecordersStatus"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.ConfigurationRecordersStatus, tt.wantRecordingLen)
if tt.wantRecordingLen > 0 {
@@ -529,11 +531,11 @@ func TestAWSConfigHandler_DescribeConfigurationRecorders_NameFilter(t *testing.T
return
}
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
var recorders []any
- require.NoError(t, json.Unmarshal(out["ConfigurationRecorders"], &recorders))
+ require.NoError(t, json.Unmarshal(out["ConfigurationRecorders"], &recorders, jsonx.V1()))
assert.Len(t, recorders, tt.wantCount)
})
}
@@ -565,7 +567,7 @@ func TestAWSConfigHandler_ServiceLinkedConfigurationRecorder(t *testing.T) {
Arn string `json:"Arn"`
Name string `json:"Name"`
}
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut, jsonx.V1()))
assert.Equal(t, "AWSConfigurationRecorderForGuardduty", putOut.Name)
assert.NotEmpty(t, putOut.Arn)
@@ -578,7 +580,7 @@ func TestAWSConfigHandler_ServiceLinkedConfigurationRecorder(t *testing.T) {
Arn string `json:"Arn"`
Name string `json:"Name"`
}
- require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &delOut))
+ require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &delOut, jsonx.V1()))
assert.Equal(t, putOut.Name, delOut.Name)
// A second delete now 404s (NoSuchConfigurationRecorderException).
@@ -682,7 +684,7 @@ func TestAWSConfigHandler_ConnectorsRoundtrip(t *testing.T) {
var putOut struct {
Arn string `json:"Arn"`
}
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut, jsonx.V1()))
require.NotEmpty(t, putOut.Arn)
getRec := doAWSConfigRequest(t, h, "GetConnector", map[string]any{"Arn": putOut.Arn})
@@ -698,7 +700,7 @@ func TestAWSConfigHandler_ConnectorsRoundtrip(t *testing.T) {
} `json:"connectorConfiguration"`
} `json:"Connector"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, putOut.Arn, getOut.Connector.Arn)
assert.Equal(t, "client-1", getOut.Connector.ConnectorConfiguration.Azure.ClientIdentifier)
@@ -711,7 +713,7 @@ func TestAWSConfigHandler_ConnectorsRoundtrip(t *testing.T) {
Provider string `json:"provider"`
} `json:"ConnectorSummaries"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.ConnectorSummaries, 1)
assert.Equal(t, putOut.Arn, listOut.ConnectorSummaries[0].Arn)
assert.Equal(t, "AZURE", listOut.ConnectorSummaries[0].Provider)
@@ -735,7 +737,7 @@ func TestAWSConfigHandler_PutThirdPartyServiceLinkedConfigurationRecorder(t *tes
var connOut struct {
Arn string `json:"Arn"`
}
- require.NoError(t, json.Unmarshal(putConnRec.Body.Bytes(), &connOut))
+ require.NoError(t, json.Unmarshal(putConnRec.Body.Bytes(), &connOut, jsonx.V1()))
body := map[string]any{
"ServicePrincipal": "thirdparty.amazonaws.com",
@@ -753,7 +755,7 @@ func TestAWSConfigHandler_PutThirdPartyServiceLinkedConfigurationRecorder(t *tes
Arn string `json:"Arn"`
Name string `json:"Name"`
}
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut, jsonx.V1()))
assert.NotEmpty(t, putOut.Name)
assert.NotEmpty(t, putOut.Arn)
@@ -773,7 +775,7 @@ func TestAWSConfigHandler_PutThirdPartyServiceLinkedConfigurationRecorder(t *tes
var connOut2 struct {
Arn string `json:"Arn"`
}
- require.NoError(t, json.Unmarshal(putConnRec2.Body.Bytes(), &connOut2))
+ require.NoError(t, json.Unmarshal(putConnRec2.Body.Bytes(), &connOut2, jsonx.V1()))
conflictBody := map[string]any{
"ServicePrincipal": "thirdparty.amazonaws.com",
diff --git a/services/awsconfig/handler_conformance_packs_test.go b/services/awsconfig/handler_conformance_packs_test.go
index 66691da435..2027f96109 100644
--- a/services/awsconfig/handler_conformance_packs_test.go
+++ b/services/awsconfig/handler_conformance_packs_test.go
@@ -1,7 +1,7 @@
package awsconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/awsconfig"
)
@@ -66,7 +67,7 @@ func TestConformancePackARN(t *testing.T) {
DeliveryS3Bucket string `json:"DeliveryS3Bucket"`
} `json:"ConformancePackDetails"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.ConformancePackDetails, 1)
assert.Contains(t, out.ConformancePackDetails[0].ConformancePackArn, "arn:aws:config:")
assert.NotEmpty(t, out.ConformancePackDetails[0].ConformancePackID)
@@ -195,7 +196,7 @@ func TestAWSConfigHandler_PutConformancePack_TemplateBodyDeploysRules(t *testing
ComplianceType string `json:"ComplianceType"`
} `json:"ConformancePackRuleComplianceList"`
}
- require.NoError(t, json.Unmarshal(complianceRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(complianceRec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.ConformancePackRuleComplianceList, 1)
assert.Equal(t, "rule-a", out.ConformancePackRuleComplianceList[0].ConfigRuleName)
assert.Equal(t, "INSUFFICIENT_DATA", out.ConformancePackRuleComplianceList[0].ComplianceType)
@@ -233,7 +234,7 @@ func TestAWSConfigHandler_ListConformancePackComplianceScores(t *testing.T) {
Score string `json:"Score"`
} `json:"ConformancePackComplianceScores"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.ConformancePackComplianceScores, 1)
assert.Equal(t, "pack1", out.ConformancePackComplianceScores[0].ConformancePackName)
assert.Equal(t, "INSUFFICIENT_DATA", out.ConformancePackComplianceScores[0].Score)
diff --git a/services/awsconfig/handler_delivery_channels.go b/services/awsconfig/handler_delivery_channels.go
index 546375f06a..5a4037a5c7 100644
--- a/services/awsconfig/handler_delivery_channels.go
+++ b/services/awsconfig/handler_delivery_channels.go
@@ -2,8 +2,9 @@ package awsconfig
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -33,7 +34,7 @@ type extractDeliveryChannelNameInput struct {
func extractDeliveryChannelName(body []byte) string {
var req extractDeliveryChannelNameInput
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
return ""
}
@@ -46,7 +47,7 @@ type extractFirstDeliveryChannelNameInput struct {
func extractFirstDeliveryChannelName(body []byte) string {
var req extractFirstDeliveryChannelNameInput
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
return ""
}
if len(req.DeliveryChannelNames) > 0 {
diff --git a/services/awsconfig/handler_delivery_channels_test.go b/services/awsconfig/handler_delivery_channels_test.go
index a326843aa1..296889c987 100644
--- a/services/awsconfig/handler_delivery_channels_test.go
+++ b/services/awsconfig/handler_delivery_channels_test.go
@@ -1,13 +1,15 @@
package awsconfig_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/awsconfig"
)
@@ -252,11 +254,11 @@ func TestAWSConfigHandler_DescribeDeliveryChannels_NameFilter(t *testing.T) {
return
}
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
var channels []any
- require.NoError(t, json.Unmarshal(out["DeliveryChannels"], &channels))
+ require.NoError(t, json.Unmarshal(out["DeliveryChannels"], &channels, jsonx.V1()))
assert.Len(t, channels, tt.wantCount)
})
}
diff --git a/services/awsconfig/handler_remediation_test.go b/services/awsconfig/handler_remediation_test.go
index 3518b0fc36..a54f9381c9 100644
--- a/services/awsconfig/handler_remediation_test.go
+++ b/services/awsconfig/handler_remediation_test.go
@@ -1,13 +1,14 @@
package awsconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/awsconfig"
)
@@ -43,7 +44,7 @@ func TestAWSConfigHandler_StartAndDescribeRemediationExecution(t *testing.T) {
State string `json:"State"`
} `json:"RemediationExecutionStatuses"`
}
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.RemediationExecutionStatuses, 1)
assert.Equal(t, "SUCCEEDED", out.RemediationExecutionStatuses[0].State)
}
diff --git a/services/awsconfig/handler_stored_queries_test.go b/services/awsconfig/handler_stored_queries_test.go
index 28da729d77..6095781f5a 100644
--- a/services/awsconfig/handler_stored_queries_test.go
+++ b/services/awsconfig/handler_stored_queries_test.go
@@ -1,12 +1,14 @@
package awsconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestListStoredQueriesMetadata verifies ListStoredQueries returns StoredQueryMetadata.
@@ -27,7 +29,7 @@ func TestListStoredQueriesMetadata(t *testing.T) {
QueryName string `json:"QueryName"`
} `json:"StoredQueryMetadata"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.StoredQueryMetadata, 1)
assert.Equal(t, "my-query", out.StoredQueryMetadata[0].QueryName)
assert.Contains(t, out.StoredQueryMetadata[0].QueryArn, "arn:aws:config:")
diff --git a/services/awsconfig/handler_test.go b/services/awsconfig/handler_test.go
index 8aa9f3df51..33b44e967d 100644
--- a/services/awsconfig/handler_test.go
+++ b/services/awsconfig/handler_test.go
@@ -2,7 +2,7 @@ package awsconfig_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/awsconfig"
)
@@ -29,7 +30,7 @@ func doAWSConfigRequest(t *testing.T, h *awsconfig.Handler, action string, body
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
diff --git a/services/awsconfig/multi_region_test.go b/services/awsconfig/multi_region_test.go
index d8c40df6eb..5a57667fc9 100644
--- a/services/awsconfig/multi_region_test.go
+++ b/services/awsconfig/multi_region_test.go
@@ -2,7 +2,7 @@ package awsconfig_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/awsconfig"
)
@@ -41,7 +42,7 @@ func decode(t *testing.T, body string) map[string]any {
out := map[string]any{}
if body != "" {
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
}
return out
diff --git a/services/awsconfig/persistence.go b/services/awsconfig/persistence.go
index 003a287047..6100adc5f4 100644
--- a/services/awsconfig/persistence.go
+++ b/services/awsconfig/persistence.go
@@ -2,7 +2,7 @@ package awsconfig
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -126,8 +126,8 @@ func fromRemediationExecutionSnapshot(v *remediationExecutionSnapshot) *Remediat
// rather than closing it, per the mechanical-conversion (not parity-fix)
// scope of that rollout.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
@@ -222,7 +222,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// its "remediationExecutions" DTO entry in tables (gopherstack-ltj0d;
// b.remediationExecutions is not on b.registry, see store_setup.go's package
// doc and remediationExecutionSnapshot above).
-func (b *InMemoryBackend) restoreRemediationExecutionsFromDTO(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreRemediationExecutionsFromDTO(tables map[string]jsontext.Value) error {
remDTOReg := store.NewRegistry()
remDTOs := store.Register(remDTOReg, "remediationExecutions", store.New(remediationExecutionSnapshotKey))
diff --git a/services/awsconfig/select_query.go b/services/awsconfig/select_query.go
index 3efd7ef1df..8caf636af4 100644
--- a/services/awsconfig/select_query.go
+++ b/services/awsconfig/select_query.go
@@ -1,11 +1,13 @@
package awsconfig
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// select_query.go implements a minimal evaluator for the SQL-like query
@@ -170,7 +172,7 @@ func evaluateSelectQuery(items []*ResourceConfigItem, expression string) ([]stri
for _, item := range sorted {
var config map[string]any
- _ = json.Unmarshal([]byte(item.Configuration), &config)
+ _ = json.Unmarshal([]byte(item.Configuration), &config, jsonx.V1())
if config == nil {
config = map[string]any{}
@@ -207,7 +209,7 @@ func marshalSelectedFields(item *ResourceConfigItem, config map[string]any, fiel
}
}
- encoded, err := json.Marshal(row)
+ encoded, err := json.Marshal(row, jsonx.V1())
if err != nil {
return "", false
}
diff --git a/services/awsconfig/select_query_test.go b/services/awsconfig/select_query_test.go
index 12c9ed08db..c56fde5c28 100644
--- a/services/awsconfig/select_query_test.go
+++ b/services/awsconfig/select_query_test.go
@@ -1,12 +1,13 @@
package awsconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"sort"
"testing"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/awsconfig"
)
@@ -80,7 +81,7 @@ func TestSelectResourceConfig_SelectsRequestedFields(t *testing.T) {
}
var row map[string]any
- if unmarshalErr := json.Unmarshal([]byte(results[0]), &row); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal([]byte(results[0]), &row, jsonx.V1()); unmarshalErr != nil {
t.Fatalf("unmarshal row: %v", unmarshalErr)
}
@@ -116,7 +117,7 @@ func extractResourceIDs(t *testing.T, results []string) []string {
for _, r := range results {
var row map[string]any
- if err := json.Unmarshal([]byte(r), &row); err != nil {
+ if err := json.Unmarshal([]byte(r), &row, jsonx.V1()); err != nil {
t.Fatalf("unmarshal row: %v", err)
}
diff --git a/services/awsconfig/tags_test.go b/services/awsconfig/tags_test.go
index fdd7773579..e6aae1b14d 100644
--- a/services/awsconfig/tags_test.go
+++ b/services/awsconfig/tags_test.go
@@ -1,12 +1,14 @@
package awsconfig_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/awsconfig"
)
@@ -122,7 +124,7 @@ func TestCreationTags_Decoded(t *testing.T) {
var out struct {
Arn string `json:"Arn"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
return out.Arn
},
diff --git a/services/azureappconfig/http_test.go b/services/azureappconfig/http_test.go
index c5283516d3..1b1acd452b 100644
--- a/services/azureappconfig/http_test.go
+++ b/services/azureappconfig/http_test.go
@@ -1,7 +1,7 @@
package azureappconfig_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/url"
@@ -10,6 +10,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const av = "api-version=2023-11-01"
@@ -22,7 +24,7 @@ func readJSON(t *testing.T, resp *http.Response) map[string]any {
var out map[string]any
- require.NoError(t, json.Unmarshal(raw, &out), string(raw))
+ require.NoError(t, json.Unmarshal(raw, &out, jsonx.V1()), string(raw))
return out
}
diff --git a/services/azureappconfig/server.go b/services/azureappconfig/server.go
index f095c1eb74..2273f31114 100644
--- a/services/azureappconfig/server.go
+++ b/services/azureappconfig/server.go
@@ -2,7 +2,7 @@ package azureappconfig
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net"
"net/http"
@@ -11,6 +11,8 @@ import (
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var apiVersions = map[string]bool{ //nolint:gochecknoglobals // supported api-version set, read-only
@@ -156,7 +158,7 @@ func (h *Handler) fail(req *request, err error) error {
}
func mustJSON(v any) []byte {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return []byte("{}")
}
@@ -270,7 +272,7 @@ func decodeBody(body []byte, out any) error {
return nil
}
- if err := json.Unmarshal(body, out); err != nil {
+ if err := json.Unmarshal(body, out, jsonx.V1()); err != nil {
return ErrInvalidArgument
}
diff --git a/services/azurearm/aci_wire.go b/services/azurearm/aci_wire.go
index bc98abdeca..b172f1ed8a 100644
--- a/services/azurearm/aci_wire.go
+++ b/services/azurearm/aci_wire.go
@@ -3,11 +3,14 @@ package azurearm
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"regexp"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -67,7 +70,7 @@ type aciContainerPropsWire struct {
VolumeMounts []aciMountWire `json:"volumeMounts,omitempty"`
LivenessProbe *aciProbeWire `json:"livenessProbe,omitempty"`
ReadinessProbe *aciProbeWire `json:"readinessProbe,omitempty"`
- InstanceView json.RawMessage `json:"instanceView,omitempty"`
+ InstanceView jsontext.Value `json:"instanceView,omitempty"`
}
type aciContainerWire struct {
@@ -97,7 +100,7 @@ type aciAzureFileWire struct {
}
type aciVolumeWire struct {
- EmptyDir *json.RawMessage `json:"emptyDir,omitempty"`
+ EmptyDir *jsontext.Value `json:"emptyDir,omitempty"`
Secret map[string]string `json:"secret,omitempty"`
AzureFile *aciAzureFileWire `json:"azureFile,omitempty"`
GitRepo *aciGitRepoWire `json:"gitRepo,omitempty"`
@@ -114,7 +117,7 @@ type aciPropsWire struct {
Priority string `json:"priority,omitempty"`
ProvisioningState string `json:"provisioningState,omitempty"`
OSType string `json:"osType,omitempty"`
- InstanceView json.RawMessage `json:"instanceView,omitempty"`
+ InstanceView jsontext.Value `json:"instanceView,omitempty"`
Volumes []aciVolumeWire `json:"volumes,omitempty"`
InitContainers []aciInitWire `json:"initContainers,omitempty"`
SubnetIDs []aciSubnetWire `json:"subnetIds,omitempty"`
@@ -134,15 +137,14 @@ func aciBad(format string, args ...any) error {
func parseACIProps(raw any) (aciPropsWire, error) {
var p aciPropsWire
- buf, err := json.Marshal(raw)
+ buf, err := json.Marshal(raw, jsonx.V1())
if err != nil {
return p, aciBad("properties: %v", err)
}
- dec := json.NewDecoder(bytes.NewReader(buf))
- dec.DisallowUnknownFields()
+ dec := jsontext.NewDecoder(bytes.NewReader(buf))
- if err = dec.Decode(&p); err != nil {
+ if err = json.UnmarshalDecode(dec, &p, jsonx.V1(), json.RejectUnknownMembers(true)); err != nil {
if name, ok := strings.CutPrefix(err.Error(), "json: unknown field "); ok {
return p, aciUnsupported("property " + name)
}
diff --git a/services/azurearm/aci_wire_ext.go b/services/azurearm/aci_wire_ext.go
index 2eb689bb17..c8bcf00f09 100644
--- a/services/azurearm/aci_wire_ext.go
+++ b/services/azurearm/aci_wire_ext.go
@@ -1,7 +1,7 @@
package azurearm
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"net/netip"
"strings"
@@ -47,11 +47,11 @@ type aciProbeWire struct {
}
type aciInitPropsWire struct {
- Image string `json:"image"`
- Command []string `json:"command,omitempty"`
- EnvironmentVariables []aciEnvWire `json:"environmentVariables,omitempty"`
- VolumeMounts []aciMountWire `json:"volumeMounts,omitempty"`
- InstanceView json.RawMessage `json:"instanceView,omitempty"`
+ Image string `json:"image"`
+ Command []string `json:"command,omitempty"`
+ EnvironmentVariables []aciEnvWire `json:"environmentVariables,omitempty"`
+ VolumeMounts []aciMountWire `json:"volumeMounts,omitempty"`
+ InstanceView jsontext.Value `json:"instanceView,omitempty"`
}
type aciInitWire struct {
diff --git a/services/azurearm/aks_sdk_test.go b/services/azurearm/aks_sdk_test.go
index d3b7dd46e2..62880fd3e4 100644
--- a/services/azurearm/aks_sdk_test.go
+++ b/services/azurearm/aks_sdk_test.go
@@ -3,7 +3,7 @@ package azurearm_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/azurearm"
)
@@ -1089,7 +1090,7 @@ func TestAKSSubnetReferences(t *testing.T) {
func mustJSON(t *testing.T, v any) string {
t.Helper()
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
return string(b)
diff --git a/services/azurearm/apim_asyncop_test.go b/services/azurearm/apim_asyncop_test.go
index e6f5f544a9..13967299b2 100644
--- a/services/azurearm/apim_asyncop_test.go
+++ b/services/azurearm/apim_asyncop_test.go
@@ -1,7 +1,7 @@
package azurearm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"strings"
@@ -16,6 +16,8 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/apimanagement/armapimanagement/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const apimSvcBody = `{"location":"westus","sku":{"name":"Developer","capacity":1},` +
@@ -53,7 +55,7 @@ func statusDoc(t *testing.T, body string) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &m))
+ require.NoError(t, json.Unmarshal([]byte(body), &m, jsonx.V1()))
return m
}
diff --git a/services/azurearm/apim_gateway_test.go b/services/azurearm/apim_gateway_test.go
index c4e8c90cf2..668cecc2d0 100644
--- a/services/azurearm/apim_gateway_test.go
+++ b/services/azurearm/apim_gateway_test.go
@@ -2,7 +2,8 @@ package azurearm_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net"
"net/http"
@@ -15,6 +16,8 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/apimanagement/armapimanagement/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type echoReply struct {
@@ -33,7 +36,7 @@ func newEchoBackend(t *testing.T) *httptest.Server {
b, _ := io.ReadAll(r.Body)
w.Header().Set("X-Backend", "yes")
w.Header().Set("Content-Type", "application/json")
- _ = json.NewEncoder(w).Encode(echoReply{
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), echoReply{
Method: r.Method,
Path: r.URL.Path,
Query: r.URL.RawQuery,
@@ -101,7 +104,7 @@ func (e *gwEnv) call(t *testing.T, method, path, body string, extra map[string]s
func (e *gwEnv) policy(t *testing.T, scope, doc string, wantStatus int) string {
t.Helper()
- b, err := json.Marshal(map[string]any{"properties": map[string]any{"format": "xml", "value": doc}})
+ b, err := json.Marshal(map[string]any{"properties": map[string]any{"format": "xml", "value": doc}}, jsonx.V1())
require.NoError(t, err)
status, body, _ := e.raw(t, http.MethodPut, e.armPath(scope+"/policies/policy"), string(b), nil)
@@ -129,7 +132,7 @@ func decodeEcho(t *testing.T, body string) echoReply {
var r echoReply
- require.NoError(t, json.Unmarshal([]byte(body), &r), body)
+ require.NoError(t, json.Unmarshal([]byte(body), &r, jsonx.V1()), body)
return r
}
diff --git a/services/azurearm/apim_policy_test.go b/services/azurearm/apim_policy_test.go
index 7f63067614..c8ed9736ef 100644
--- a/services/azurearm/apim_policy_test.go
+++ b/services/azurearm/apim_policy_test.go
@@ -1,7 +1,7 @@
package azurearm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -11,6 +11,8 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/apimanagement/armapimanagement/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestAPIMPolicy_Validation(t *testing.T) {
@@ -142,7 +144,10 @@ func TestAPIMPolicy_Validation(t *testing.T) {
format = "xml"
}
- b, err := json.Marshal(map[string]any{"properties": map[string]any{"format": format, "value": tc.doc}})
+ b, err := json.Marshal(
+ map[string]any{"properties": map[string]any{"format": format, "value": tc.doc}},
+ jsonx.V1(),
+ )
require.NoError(t, err)
status, body, _ := s.raw(t, http.MethodPut, s.armPath("/apis/echo/policies/policy"), string(b), nil)
@@ -713,7 +718,7 @@ func TestAPIMGateway_PolicyInheritance(t *testing.T) {
e := newGWEnv(t)
svcDoc := policyDoc(`s`, "", "")
- b, err := json.Marshal(map[string]any{"properties": map[string]any{"format": "xml", "value": svcDoc}})
+ b, err := json.Marshal(map[string]any{"properties": map[string]any{"format": "xml", "value": svcDoc}}, jsonx.V1())
require.NoError(t, err)
status, body, _ := e.raw(t, http.MethodPut, e.armPath("/policies/policy"), string(b), nil)
diff --git a/services/azurearm/apim_sources_test.go b/services/azurearm/apim_sources_test.go
index 94ad5cfbe7..df8da8ad04 100644
--- a/services/azurearm/apim_sources_test.go
+++ b/services/azurearm/apim_sources_test.go
@@ -1,7 +1,7 @@
package azurearm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strconv"
@@ -12,6 +12,8 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/apimanagement/armapimanagement/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func serviceBody(sku string, capacity int) string {
@@ -26,7 +28,7 @@ func decodeProps(t *testing.T, body string) map[string]any {
Properties map[string]any `json:"properties"`
}
- require.NoError(t, json.Unmarshal([]byte(body), &out), body)
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()), body)
return out.Properties
}
@@ -122,7 +124,10 @@ func TestAPIMLimits_PolicySize(t *testing.T) {
s.putAPI(t, "echo", "echo", "")
doc := "" + strings.Repeat("", tc.bases) + ""
- b, err := json.Marshal(map[string]any{"properties": map[string]any{"format": "xml", "value": doc}})
+ b, err := json.Marshal(
+ map[string]any{"properties": map[string]any{"format": "xml", "value": doc}},
+ jsonx.V1(),
+ )
require.NoError(t, err)
status, body, _ := s.raw(t, http.MethodPut, s.armPath("/apis/echo/policies/policy"), string(b), nil)
@@ -581,7 +586,10 @@ func TestAPIMPolicy_ReferenceRules(t *testing.T) {
s.devService(t)
s.putAPI(t, "echo", "echo", "")
- b, err := json.Marshal(map[string]any{"properties": map[string]any{"format": "xml", "value": tc.doc}})
+ b, err := json.Marshal(
+ map[string]any{"properties": map[string]any{"format": "xml", "value": tc.doc}},
+ jsonx.V1(),
+ )
require.NoError(t, err)
status, body, _ := s.raw(t, http.MethodPut, s.armPath(tc.scope+"/policies/policy"), string(b), nil)
diff --git a/services/azurearm/directory_test.go b/services/azurearm/directory_test.go
index 5ac05623bb..225dee39c3 100644
--- a/services/azurearm/directory_test.go
+++ b/services/azurearm/directory_test.go
@@ -2,7 +2,7 @@ package azurearm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/azurearm"
)
@@ -108,7 +109,7 @@ func TestTokenEndpointClientAuthentication(t *testing.T) {
require.Equal(t, tt.status, rec.Code, rec.Body.String())
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
if tt.wantCode != 0 {
assert.Equal(t, "invalid_client", body["error"])
diff --git a/services/azurearm/export_test.go b/services/azurearm/export_test.go
index fa18fb2638..3f29d2430a 100644
--- a/services/azurearm/export_test.go
+++ b/services/azurearm/export_test.go
@@ -1,11 +1,13 @@
package azurearm
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// This file exposes internals needed by the blackbox (azurearm_test) test
@@ -52,7 +54,7 @@ func NewTestHandlerWithCosmos(dataPlane CosmosResources, settings Settings) *Han
// EventHubSnapshotJSON marshals the provider's ARM-side snapshot.
func EventHubSnapshotJSON(p *EventHubProvider) []byte {
- b, _ := json.Marshal(p.snapshot())
+ b, _ := json.Marshal(p.snapshot(), jsonx.V1())
return b
}
@@ -63,7 +65,7 @@ func EventHubRestoreJSON(t *testing.T, p *EventHubProvider, data []byte) {
var s eventHubSnapshot
- require.NoError(t, json.Unmarshal(data, &s))
+ require.NoError(t, json.Unmarshal(data, &s, jsonx.V1()))
p.restore(&s)
}
@@ -78,7 +80,7 @@ func NewTestHandlerWithEventGrid(dataPlane EventGrid, settings Settings) *Handle
// EventGridSnapshotJSON marshals the provider's ARM-side snapshot.
func EventGridSnapshotJSON(p *EventGridProvider) []byte {
- b, _ := json.Marshal(p.snapshot())
+ b, _ := json.Marshal(p.snapshot(), jsonx.V1())
return b
}
@@ -89,7 +91,7 @@ func EventGridRestoreJSON(t *testing.T, p *EventGridProvider, data []byte) {
var s eventGridSnapshot
- require.NoError(t, json.Unmarshal(data, &s))
+ require.NoError(t, json.Unmarshal(data, &s, jsonx.V1()))
p.restore(&s)
}
@@ -104,7 +106,7 @@ func NewTestHandlerWithAppConfig(dataPlane AppConfig, settings Settings) *Handle
// AppConfigSnapshotJSON marshals the provider's ARM-side snapshot.
func AppConfigSnapshotJSON(p *AppConfigProvider) []byte {
- b, _ := json.Marshal(p.snapshot())
+ b, _ := json.Marshal(p.snapshot(), jsonx.V1())
return b
}
@@ -115,7 +117,7 @@ func AppConfigRestoreJSON(t *testing.T, p *AppConfigProvider, data []byte) {
var s appConfigSnapshot
- require.NoError(t, json.Unmarshal(data, &s))
+ require.NoError(t, json.Unmarshal(data, &s, jsonx.V1()))
p.restore(&s)
}
@@ -144,7 +146,7 @@ func NewTestHandlerWithContainerRegistry(dataPlane ContainerRegistries, settings
// ContainerRegistrySnapshotJSON marshals the provider's ARM-side snapshot.
func ContainerRegistrySnapshotJSON(p *ContainerRegistryProvider) []byte {
- b, _ := json.Marshal(p.snapshot())
+ b, _ := json.Marshal(p.snapshot(), jsonx.V1())
return b
}
@@ -155,7 +157,7 @@ func ContainerRegistryRestoreJSON(t *testing.T, p *ContainerRegistryProvider, da
var s containerRegistrySnapshot
- require.NoError(t, json.Unmarshal(data, &s))
+ require.NoError(t, json.Unmarshal(data, &s, jsonx.V1()))
p.restore(&s)
}
@@ -172,7 +174,7 @@ func NewTestHandlerWithRedis(dataPlane Redis, now func() time.Time) (*Handler, *
// RedisSnapshotJSON marshals the provider's ARM-side snapshot.
func RedisSnapshotJSON(p *RedisProvider) []byte {
- b, _ := json.Marshal(p.snapshot())
+ b, _ := json.Marshal(p.snapshot(), jsonx.V1())
return b
}
@@ -183,7 +185,7 @@ func RedisRestoreJSON(t *testing.T, p *RedisProvider, data []byte) {
var s redisSnapshot
- require.NoError(t, json.Unmarshal(data, &s))
+ require.NoError(t, json.Unmarshal(data, &s, jsonx.V1()))
p.restore(&s)
}
@@ -199,7 +201,7 @@ func NewTestHandlerWithSQL(dataPlane SQL) (*Handler, *SQLProvider) {
// SQLSnapshotJSON marshals the provider's ARM-side snapshot.
func SQLSnapshotJSON(p *SQLProvider) []byte {
- b, _ := json.Marshal(p.snapshot())
+ b, _ := json.Marshal(p.snapshot(), jsonx.V1())
return b
}
@@ -210,6 +212,6 @@ func SQLRestoreJSON(t *testing.T, p *SQLProvider, data []byte) {
var s sqlSnapshot
- require.NoError(t, json.Unmarshal(data, &s))
+ require.NoError(t, json.Unmarshal(data, &s, jsonx.V1()))
p.restore(&s)
}
diff --git a/services/azurearm/flexdb_mysql_test.go b/services/azurearm/flexdb_mysql_test.go
index 53a43f1307..d515bb0920 100644
--- a/services/azurearm/flexdb_mysql_test.go
+++ b/services/azurearm/flexdb_mysql_test.go
@@ -2,7 +2,7 @@ package azurearm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -11,6 +11,8 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/mysql/armmysqlflexibleservers/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const myNS = "Microsoft.DBforMySQL"
@@ -87,7 +89,7 @@ func TestMySQLFlexServerCRUD(t *testing.T) {
status, raw := s.raw(t, http.MethodGet, flexServerPath(myNS, "my-crud"), "", flexAPIMy)
require.Equal(t, http.StatusOK, status)
- enc, err := json.Marshal(raw)
+ enc, err := json.Marshal(raw, jsonx.V1())
require.NoError(t, err)
assert.NotContains(t, string(enc), flexPassword)
assert.NotContains(t, string(enc), "administratorLoginPassword")
diff --git a/services/azurearm/flexdb_pg_test.go b/services/azurearm/flexdb_pg_test.go
index df044fc44a..2667172c40 100644
--- a/services/azurearm/flexdb_pg_test.go
+++ b/services/azurearm/flexdb_pg_test.go
@@ -2,7 +2,7 @@ package azurearm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -12,6 +12,8 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/postgresql/armpostgresqlflexibleservers/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const pgNS = "Microsoft.DBforPostgreSQL"
@@ -71,7 +73,7 @@ func TestPGFlexServerCRUD(t *testing.T) {
status, raw := s.raw(t, http.MethodGet, flexServerPath(pgNS, "pg-crud"), "", flexAPIPG)
require.Equal(t, http.StatusOK, status)
- enc, err := json.Marshal(raw)
+ enc, err := json.Marshal(raw, jsonx.V1())
require.NoError(t, err)
assert.NotContains(t, string(enc), flexPassword)
assert.NotContains(t, string(enc), "administratorLoginPassword")
diff --git a/services/azurearm/flexdb_stack_test.go b/services/azurearm/flexdb_stack_test.go
index 9c9baf19de..731dd95453 100644
--- a/services/azurearm/flexdb_stack_test.go
+++ b/services/azurearm/flexdb_stack_test.go
@@ -2,7 +2,7 @@ package azurearm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -18,6 +18,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/azurearm"
)
@@ -130,7 +131,7 @@ func (s *flexStack) raw(t *testing.T, method, path, body, apiVersion string) (in
var out map[string]any
if len(data) > 0 {
- _ = json.Unmarshal(data, &out)
+ _ = json.Unmarshal(data, &out, jsonx.V1())
}
return resp.StatusCode, out
diff --git a/services/azurearm/handler.go b/services/azurearm/handler.go
index c6e8a5854e..e74892e425 100644
--- a/services/azurearm/handler.go
+++ b/services/azurearm/handler.go
@@ -3,7 +3,8 @@ package azurearm
import (
"context"
"crypto/tls"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net"
@@ -18,6 +19,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/aadauth"
"github.com/blackbirdworks/gopherstack/pkgs/devtls"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -568,7 +570,7 @@ func decodeJSONBody(r *http.Request) (map[string]any, error) {
}
var body map[string]any
- if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(r.Body), &body, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", ErrInvalidRequestBody, err)
}
diff --git a/services/azurearm/handler_test.go b/services/azurearm/handler_test.go
index c7fe8c3862..07e5b3c490 100644
--- a/services/azurearm/handler_test.go
+++ b/services/azurearm/handler_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"crypto/tls"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"fmt"
"net"
@@ -23,6 +23,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/aadauth"
"github.com/blackbirdworks/gopherstack/pkgs/devtls"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azurearm"
)
@@ -74,7 +75,7 @@ func doRequest(t *testing.T, h *azurearm.Handler, method, path string, body []by
var decoded map[string]any
if rec.Body.Len() > 0 {
- _ = json.Unmarshal(rec.Body.Bytes(), &decoded)
+ _ = json.Unmarshal(rec.Body.Bytes(), &decoded, jsonx.V1())
}
return rec.Code, decoded
@@ -96,7 +97,7 @@ func TestHandler_MetadataEndpoints(t *testing.T) {
var doc map[string]any
- require.NoError(t, json.Unmarshal(body, &doc))
+ require.NoError(t, json.Unmarshal(body, &doc, jsonx.V1()))
assert.Equal(t, "gopherstack", doc["name"])
}
@@ -150,7 +151,7 @@ func TestHandler_TokenEndpoint(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "Bearer", resp["token_type"])
assert.NotEmpty(t, resp["access_token"])
})
@@ -512,7 +513,7 @@ func TestHandler_MetadataEndpoints_HonorsRequestHostPort(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var doc map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &doc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &doc, jsonx.V1()))
assert.Equal(t, "https://localhost:18006/", doc["resourceManagerEndpoint"])
}
diff --git a/services/azurearm/rp_aks.go b/services/azurearm/rp_aks.go
index 232f689c2f..22b772d06b 100644
--- a/services/azurearm/rp_aks.go
+++ b/services/azurearm/rp_aks.go
@@ -4,12 +4,13 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"sync"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
)
@@ -122,7 +123,7 @@ func aksResourceID(sub, rg string, types, names []string) ResourceID {
func aksEtag(t time.Time) string { return acaEtag(t) }
func aksJSON(v any) string {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return ""
}
diff --git a/services/azurearm/rp_aks_persist.go b/services/azurearm/rp_aks_persist.go
index 5fdf1bbc2d..f3c28ce45e 100644
--- a/services/azurearm/rp_aks_persist.go
+++ b/services/azurearm/rp_aks_persist.go
@@ -1,6 +1,10 @@
package azurearm
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
type containerServiceSnapshot struct {
Clusters map[string]*storedAKSCluster `json:"clusters"`
@@ -10,13 +14,13 @@ func (p *ContainerServiceProvider) snapshot() *containerServiceSnapshot {
p.mu.RLock("snapshot")
defer p.mu.RUnlock()
- buf, err := json.Marshal(&containerServiceSnapshot{Clusters: p.clusters})
+ buf, err := json.Marshal(&containerServiceSnapshot{Clusters: p.clusters}, jsonx.V1())
if err != nil {
return &containerServiceSnapshot{}
}
var out containerServiceSnapshot
- if err = json.Unmarshal(buf, &out); err != nil {
+ if err = json.Unmarshal(buf, &out, jsonx.V1()); err != nil {
return &containerServiceSnapshot{}
}
diff --git a/services/azurearm/rp_apim_exec.go b/services/azurearm/rp_apim_exec.go
index 80b9abf393..79b6282606 100644
--- a/services/azurearm/rp_apim_exec.go
+++ b/services/azurearm/rp_apim_exec.go
@@ -3,7 +3,7 @@ package azurearm
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +16,8 @@ import (
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// gwState carries one gateway request through the policy pipeline.
@@ -125,7 +127,7 @@ func (g *gwState) subAll(in []string) []string {
}
func (g *gwState) fail(status int, msg string) {
- body, _ := json.Marshal(map[string]any{apimCStatusCode: status, fieldMessage: msg})
+ body, _ := json.Marshal(map[string]any{apimCStatusCode: status, fieldMessage: msg}, jsonx.V1())
g.setResponse(status, http.Header{apimCContentType: {"application/json"}}, body)
g.short = true
}
@@ -373,7 +375,7 @@ func firstExample(examples map[string]any) ([]byte, bool) {
return []byte(s), true
}
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
return b, err == nil
}
diff --git a/services/azurearm/rp_apim_gateway.go b/services/azurearm/rp_apim_gateway.go
index 0b0854217b..78042df730 100644
--- a/services/azurearm/rp_apim_gateway.go
+++ b/services/azurearm/rp_apim_gateway.go
@@ -1,7 +1,8 @@
package azurearm
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net"
"net/http"
@@ -10,6 +11,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
)
@@ -160,7 +162,10 @@ func gatewayTarget(r *http.Request) (string, string, bool) {
func writeGW(w http.ResponseWriter, status int, msg string) {
w.Header().Set(apimCContentType, "application/json")
w.WriteHeader(status)
- _ = json.NewEncoder(w).Encode(map[string]any{apimCStatusCode: status, fieldMessage: msg})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]any{apimCStatusCode: status, fieldMessage: msg},
+ )
}
// ServeGateway answers a request addressed to a service's gateway (Host .azure-api.net, or the path prefix
diff --git a/services/azurearm/rp_apim_import.go b/services/azurearm/rp_apim_import.go
index 0009fc36df..53a9762ccc 100644
--- a/services/azurearm/rp_apim_import.go
+++ b/services/azurearm/rp_apim_import.go
@@ -1,11 +1,13 @@
package azurearm
import (
- "encoding/json"
+ "encoding/json/v2"
"regexp"
"sort"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -56,7 +58,7 @@ func parseAPISpec(format, value string) (*apimAPISpec, error) {
}
var doc map[string]any
- if err := json.Unmarshal([]byte(value), &doc); err != nil {
+ if err := json.Unmarshal([]byte(value), &doc, jsonx.V1()); err != nil {
return nil, apimInvalid("The API definition is not valid JSON (YAML is not supported): %v", err)
}
diff --git a/services/azurearm/rp_apim_persist.go b/services/azurearm/rp_apim_persist.go
index d8266bc774..9072b9d9eb 100644
--- a/services/azurearm/rp_apim_persist.go
+++ b/services/azurearm/rp_apim_persist.go
@@ -1,24 +1,30 @@
package azurearm
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "encoding/json/jsontext"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
// apimSnapshot is the persisted state of the API Management provider, subscription keys, secret named values and
// backend credentials included (as with the other azurearm credentials).
type apimSnapshot struct {
- Services json.RawMessage `json:"services,omitempty"`
- Operations json.RawMessage `json:"operations,omitempty"`
+ Services jsontext.Value `json:"services,omitempty"`
+ Operations jsontext.Value `json:"operations,omitempty"`
}
func (p *APIMProvider) snapshot() *apimSnapshot {
p.mu.RLock(apimCSnapshot)
defer p.mu.RUnlock()
- raw, err := json.Marshal(p.svcs)
+ raw, err := json.Marshal(p.svcs, jsonx.V1())
if err != nil {
return &apimSnapshot{}
}
- ops, err := json.Marshal(p.ops)
+ ops, err := json.Marshal(p.ops, jsonx.V1())
if err != nil {
return &apimSnapshot{Services: raw}
}
@@ -39,7 +45,7 @@ func (p *APIMProvider) restore(snap *apimSnapshot) {
}
var ops map[string]*apimOp
- if err := json.Unmarshal(snap.Operations, &ops); err == nil {
+ if err := json.Unmarshal(snap.Operations, &ops, jsonx.V1()); err == nil {
for id, o := range ops {
if o != nil {
p.ops[id] = o
@@ -48,7 +54,7 @@ func (p *APIMProvider) restore(snap *apimSnapshot) {
}
var svcs map[string]*apimService
- if err := json.Unmarshal(snap.Services, &svcs); err != nil {
+ if err := json.Unmarshal(snap.Services, &svcs, jsonx.V1()); err != nil {
return
}
diff --git a/services/azurearm/rp_authorization.go b/services/azurearm/rp_authorization.go
index 0d0daaf1be..532609235c 100644
--- a/services/azurearm/rp_authorization.go
+++ b/services/azurearm/rp_authorization.go
@@ -3,7 +3,7 @@ package azurearm
import (
"context"
_ "embed" // role and policy catalogs
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"regexp"
@@ -13,6 +13,7 @@ import (
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -187,14 +188,14 @@ func NewAuthorizationProvider(tenantID string, msi *ManagedIdentityProvider) *Au
p.resetMaps()
var roles []*builtinRole
- if err := json.Unmarshal(authRolesJSON, &roles); err == nil {
+ if err := json.Unmarshal(authRolesJSON, &roles, jsonx.V1()); err == nil {
for _, r := range roles {
p.builtinRoles[strings.ToLower(r.Name)] = r
}
}
var cat builtinPolicyCatalog
- if err := json.Unmarshal(authPoliciesJSON, &cat); err == nil {
+ if err := json.Unmarshal(authPoliciesJSON, &cat, jsonx.V1()); err == nil {
for i := range cat.Definitions {
p.builtinPols[strings.ToLower(cat.Definitions[i].Name)] = &cat.Definitions[i]
}
diff --git a/services/azurearm/rp_authorization_sdk_test.go b/services/azurearm/rp_authorization_sdk_test.go
index fb5658daf7..375756d0a5 100644
--- a/services/azurearm/rp_authorization_sdk_test.go
+++ b/services/azurearm/rp_authorization_sdk_test.go
@@ -2,7 +2,7 @@ package azurearm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/azurearm"
)
@@ -126,7 +127,7 @@ func (s *authStack) raw(t *testing.T, method, path, body string) (int, map[strin
require.NoError(t, err)
var out map[string]any
- _ = json.Unmarshal(data, &out)
+ _ = json.Unmarshal(data, &out, jsonx.V1())
return resp.StatusCode, out
}
diff --git a/services/azurearm/rp_compute_ext_test.go b/services/azurearm/rp_compute_ext_test.go
index 039f814761..5c814e69f0 100644
--- a/services/azurearm/rp_compute_ext_test.go
+++ b/services/azurearm/rp_compute_ext_test.go
@@ -1,13 +1,15 @@
package azurearm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v9"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const extSecret = "s3cr3t-protected-value"
@@ -169,7 +171,7 @@ func TestComputeSDK_ExtensionSecretsNotPersisted(t *testing.T) {
require.NoError(t, err)
snap := src.handler.Snapshot(t.Context())
- raw, err := json.Marshal(snap)
+ raw, err := json.Marshal(snap, jsonx.V1())
require.NoError(t, err)
assert.NotContains(t, string(raw), extSecret)
@@ -374,7 +376,7 @@ func TestComputeSDK_ManagedRunCommands(t *testing.T) {
assert.Equal(t, "Succeeded", *got.Properties.ProvisioningState)
assert.Equal(t, "Unknown", string(*got.Properties.InstanceView.ExecutionState))
- raw, err := json.Marshal(s.handler.Snapshot(t.Context()))
+ raw, err := json.Marshal(s.handler.Snapshot(t.Context()), jsonx.V1())
require.NoError(t, err)
assert.NotContains(t, string(raw), extSecret)
diff --git a/services/azurearm/rp_compute_sets_test.go b/services/azurearm/rp_compute_sets_test.go
index dcaddafe07..f1b392c5e9 100644
--- a/services/azurearm/rp_compute_sets_test.go
+++ b/services/azurearm/rp_compute_sets_test.go
@@ -1,7 +1,8 @@
package azurearm_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"strings"
@@ -10,6 +11,8 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v9"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestComputeSDK_AvailabilitySets(t *testing.T) {
@@ -462,7 +465,7 @@ func TestComputeSDK_ListPagingAndPatch(t *testing.T) {
}
var page listPage
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&page))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &page, jsonx.V1()))
assert.Len(t, page.Value, 2)
require.NotEmpty(t, page.NextLink)
@@ -470,7 +473,7 @@ func TestComputeSDK_ListPagingAndPatch(t *testing.T) {
resp = s.rawDo(t, http.MethodGet, next, "")
var last listPage
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&last))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &last, jsonx.V1()))
assert.Len(t, last.Value, 1)
assert.Empty(t, last.NextLink)
diff --git a/services/azurearm/rp_containerapps_app.go b/services/azurearm/rp_containerapps_app.go
index cfa66f75a4..4ab10ed62b 100644
--- a/services/azurearm/rp_containerapps_app.go
+++ b/services/azurearm/rp_containerapps_app.go
@@ -2,7 +2,7 @@ package azurearm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
@@ -10,6 +10,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -88,7 +89,7 @@ func acaTemplateKey(t map[string]any) string {
c := acaClone(t)
delete(c, fieldRevSuffix)
- b, _ := json.Marshal(c)
+ b, _ := json.Marshal(c, jsonx.V1())
return string(b)
}
diff --git a/services/azurearm/rp_containerapps_persist.go b/services/azurearm/rp_containerapps_persist.go
index 1d44920f27..7ee0979aa4 100644
--- a/services/azurearm/rp_containerapps_persist.go
+++ b/services/azurearm/rp_containerapps_persist.go
@@ -1,6 +1,10 @@
package azurearm
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
type containerAppsSnapshot struct {
Envs map[string]*storedACAEnv `json:"envs"`
@@ -23,13 +27,13 @@ func (p *ContainerAppsProvider) snapshot() *containerAppsSnapshot {
snap := &containerAppsSnapshot{Envs: p.envs, Apps: p.apps, Jobs: p.jobs}
- buf, err := json.Marshal(snap)
+ buf, err := json.Marshal(snap, jsonx.V1())
if err != nil {
return &containerAppsSnapshot{}
}
var out containerAppsSnapshot
- if err = json.Unmarshal(buf, &out); err != nil {
+ if err = json.Unmarshal(buf, &out, jsonx.V1()); err != nil {
return &containerAppsSnapshot{}
}
diff --git a/services/azurearm/rp_containerapps_revision.go b/services/azurearm/rp_containerapps_revision.go
index b830e53e7b..b1b5b8c814 100644
--- a/services/azurearm/rp_containerapps_revision.go
+++ b/services/azurearm/rp_containerapps_revision.go
@@ -3,7 +3,7 @@ package azurearm
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
"net/http"
@@ -91,7 +91,7 @@ func acaVolumeWires(t map[string]any, env *storedACAEnv, secrets map[string]stri
switch strings.ToLower(strOf(v, "storageType")) {
case "", "emptydir":
- raw := json.RawMessage("{}")
+ raw := jsontext.Value("{}")
w.EmptyDir = &raw
case "secret":
w.Secret = acaSecretVolume(acaList(v[fieldSecrets]), secrets)
diff --git a/services/azurearm/rp_containerapps_wire.go b/services/azurearm/rp_containerapps_wire.go
index 651889833a..31c0ee8273 100644
--- a/services/azurearm/rp_containerapps_wire.go
+++ b/services/azurearm/rp_containerapps_wire.go
@@ -1,13 +1,15 @@
package azurearm
import (
- "encoding/json"
+ "encoding/json/v2"
"math"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -111,13 +113,13 @@ func acaClone(m map[string]any) map[string]any {
return nil
}
- buf, err := json.Marshal(m)
+ buf, err := json.Marshal(m, jsonx.V1())
if err != nil {
return nil
}
var out map[string]any
- if err = json.Unmarshal(buf, &out); err != nil {
+ if err = json.Unmarshal(buf, &out, jsonx.V1()); err != nil {
return nil
}
diff --git a/services/azurearm/rp_cosmos_sql.go b/services/azurearm/rp_cosmos_sql.go
index 75b2c96684..d6f50f9251 100644
--- a/services/azurearm/rp_cosmos_sql.go
+++ b/services/azurearm/rp_cosmos_sql.go
@@ -1,12 +1,14 @@
package azurearm
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"sort"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type cosmosChildKind int
@@ -608,7 +610,7 @@ func rawJSON(v any) []byte {
return nil
}
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return nil
}
@@ -618,7 +620,7 @@ func rawJSON(v any) []byte {
func jsonOr(raw []byte, def map[string]any) map[string]any {
var m map[string]any
- if len(raw) > 0 && json.Unmarshal(raw, &m) == nil && m != nil {
+ if len(raw) > 0 && json.Unmarshal(raw, &m, jsonx.V1()) == nil && m != nil {
return m
}
diff --git a/services/azurearm/rp_cosmos_test.go b/services/azurearm/rp_cosmos_test.go
index e5d150eaf7..0e264e1c53 100644
--- a/services/azurearm/rp_cosmos_test.go
+++ b/services/azurearm/rp_cosmos_test.go
@@ -2,7 +2,7 @@ package azurearm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"sort"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azurearm"
)
@@ -234,7 +235,7 @@ func cosmosDo(t *testing.T, h *azurearm.Handler, method, path string, body any)
if body != nil {
var err error
- raw, err = json.Marshal(body)
+ raw, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -734,9 +735,9 @@ func TestCosmos_LegacyThroughputSnapshotMigrates(t *testing.T) {
putCosmosAccount(t, h, map[string]any{})
var env map[string]any
- require.NoError(t, json.Unmarshal(h.Snapshot(context.Background()), &env))
+ require.NoError(t, json.Unmarshal(h.Snapshot(context.Background()), &env, jsonx.V1()))
inject(t, env, map[string]any{"acct1/db1": map[string]any{"autoscaleMax": 4000}})
- raw, err := json.Marshal(env)
+ raw, err := json.Marshal(env, jsonx.V1())
require.NoError(t, err)
h2, fake2 := newCosmosHandler()
diff --git a/services/azurearm/rp_identity_sdk_test.go b/services/azurearm/rp_identity_sdk_test.go
index 0c4c2a28ef..94e2ecc94e 100644
--- a/services/azurearm/rp_identity_sdk_test.go
+++ b/services/azurearm/rp_identity_sdk_test.go
@@ -1,7 +1,7 @@
package azurearm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
"time"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azurearm"
)
@@ -387,7 +388,7 @@ func TestIdentitySDK_WebSiteLegacySnapshot(t *testing.T) {
require.NoError(t, err)
var snap map[string]any
- require.NoError(t, json.Unmarshal(s.handler.Snapshot(t.Context()), &snap))
+ require.NoError(t, json.Unmarshal(s.handler.Snapshot(t.Context()), &snap, jsonx.V1()))
sites, _ := snap["web"].(map[string]any)["sites"].(map[string]any)
require.Len(t, sites, 1)
@@ -398,7 +399,7 @@ func TestIdentitySDK_WebSiteLegacySnapshot(t *testing.T) {
m["identity"] = map[string]any{"type": "SystemAssigned"}
}
- legacy, err := json.Marshal(snap)
+ legacy, err := json.Marshal(snap, jsonx.V1())
require.NoError(t, err)
dst := newCmpStack(t)
diff --git a/services/azurearm/rp_keyvault_deleted_test.go b/services/azurearm/rp_keyvault_deleted_test.go
index 1ed3f290b6..c4ddb037a6 100644
--- a/services/azurearm/rp_keyvault_deleted_test.go
+++ b/services/azurearm/rp_keyvault_deleted_test.go
@@ -1,7 +1,7 @@
package azurearm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azurearm"
)
@@ -24,7 +25,7 @@ func newKVHandler() (*azurearm.Handler, *fakeKeyVaults) {
func putVault(t *testing.T, h *azurearm.Handler, name string, props map[string]any) int {
t.Helper()
- raw, err := json.Marshal(map[string]any{"location": "westus", "properties": props})
+ raw, err := json.Marshal(map[string]any{"location": "westus", "properties": props}, jsonx.V1())
require.NoError(t, err)
status, _ := doRequest(t, h, "PUT",
@@ -177,7 +178,7 @@ func TestKeyVault_CheckNameAvailability(t *testing.T) {
require.Less(t, putVault(t, h, "gone1", map[string]any{}), 300)
deleteVault(t, h, "gone1")
- raw, err := json.Marshal(map[string]any{"name": tt.check, "type": "Microsoft.KeyVault/vaults"})
+ raw, err := json.Marshal(map[string]any{"name": tt.check, "type": "Microsoft.KeyVault/vaults"}, jsonx.V1())
require.NoError(t, err)
st, body := doRequest(t, h, "POST",
diff --git a/services/azurearm/rp_monitor_test.go b/services/azurearm/rp_monitor_test.go
index aaae7e257b..b85a8f5e4f 100644
--- a/services/azurearm/rp_monitor_test.go
+++ b/services/azurearm/rp_monitor_test.go
@@ -2,7 +2,7 @@ package azurearm_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/azurearm"
)
@@ -97,7 +98,7 @@ func newMonitorHandler(t *testing.T, dp azurearm.MonitorLogs) *azurearm.Handler
func monitorDo(t *testing.T, h *azurearm.Handler, method, path string, body any) (int, map[string]any) {
t.Helper()
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(method, path, bytes.NewReader(raw))
@@ -106,7 +107,7 @@ func monitorDo(t *testing.T, h *azurearm.Handler, method, path string, body any)
newEchoServer(h).ServeHTTP(rec, req)
var out map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &out)
+ _ = json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1())
return rec.Code, out
}
diff --git a/services/azurearm/rp_network_pe_sdk_test.go b/services/azurearm/rp_network_pe_sdk_test.go
index f19c896062..dfe3d23222 100644
--- a/services/azurearm/rp_network_pe_sdk_test.go
+++ b/services/azurearm/rp_network_pe_sdk_test.go
@@ -1,7 +1,8 @@
package azurearm_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +10,8 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (s *netStack) rawJSON(t *testing.T, method, path, body string) (int, map[string]any) {
@@ -18,7 +21,7 @@ func (s *netStack) rawJSON(t *testing.T, method, path, body string) (int, map[st
var out map[string]any
- _ = json.NewDecoder(resp.Body).Decode(&out)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1())
return resp.StatusCode, out
}
diff --git a/services/azurearm/rp_network_sdk_test.go b/services/azurearm/rp_network_sdk_test.go
index 0268e3dee3..0880e598c3 100644
--- a/services/azurearm/rp_network_sdk_test.go
+++ b/services/azurearm/rp_network_sdk_test.go
@@ -2,7 +2,8 @@ package azurearm_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"maps"
"net/http"
@@ -22,6 +23,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/azurearm"
)
@@ -1126,5 +1128,5 @@ func TestNetworkSDK_Protocol(t *testing.T) {
}
func jsonDecode(resp *http.Response, v any) error {
- return json.NewDecoder(resp.Body).Decode(v)
+ return json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), v, jsonx.V1())
}
diff --git a/services/azurearm/rp_redis_test.go b/services/azurearm/rp_redis_test.go
index 3d5a954c10..fe5a9e2f2f 100644
--- a/services/azurearm/rp_redis_test.go
+++ b/services/azurearm/rp_redis_test.go
@@ -2,7 +2,7 @@ package azurearm_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"maps"
"net/http"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azurearm"
)
@@ -176,7 +177,7 @@ func (e *redisEnv) do(t *testing.T, method, path string, body any) (int, map[str
var rdr *bytes.Reader
if body != nil {
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
rdr = bytes.NewReader(raw)
@@ -191,7 +192,7 @@ func (e *redisEnv) do(t *testing.T, method, path string, body any) (int, map[str
var out map[string]any
if rec.Body.Len() > 0 {
- _ = json.Unmarshal(rec.Body.Bytes(), &out)
+ _ = json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1())
}
return rec.Code, out, rec.Header()
diff --git a/services/azurearm/rp_sql_test.go b/services/azurearm/rp_sql_test.go
index cb68687df8..0f9fc1023e 100644
--- a/services/azurearm/rp_sql_test.go
+++ b/services/azurearm/rp_sql_test.go
@@ -2,7 +2,7 @@ package azurearm_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"maps"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azurearm"
)
@@ -116,7 +117,7 @@ func newSQLEnv() *sqlEnv {
func (e *sqlEnv) do(t *testing.T, method, path string, body any) (int, map[string]any, http.Header) {
t.Helper()
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
if body == nil {
@@ -130,7 +131,7 @@ func (e *sqlEnv) do(t *testing.T, method, path string, body any) (int, map[strin
var out map[string]any
if rec.Body.Len() > 0 {
- _ = json.Unmarshal(rec.Body.Bytes(), &out)
+ _ = json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1())
}
return rec.Code, out, rec.Header()
diff --git a/services/azurecontainerregistry/acrapi_test.go b/services/azurecontainerregistry/acrapi_test.go
index cfa88f52f8..55ea379f1c 100644
--- a/services/azurecontainerregistry/acrapi_test.go
+++ b/services/azurecontainerregistry/acrapi_test.go
@@ -1,13 +1,14 @@
package azurecontainerregistry_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociregistry/ocitest"
)
@@ -179,7 +180,7 @@ func TestACRTagsAndManifests(t *testing.T) {
}
func patch(enabledKey string, value bool) []byte {
- raw, _ := json.Marshal(map[string]any{"value": map[string]any{enabledKey: value}})
+ raw, _ := json.Marshal(map[string]any{"value": map[string]any{enabledKey: value}}, jsonx.V1())
return raw
}
diff --git a/services/azurecontainerregistry/helpers_test.go b/services/azurecontainerregistry/helpers_test.go
index 62ba571299..514c4f85e6 100644
--- a/services/azurecontainerregistry/helpers_test.go
+++ b/services/azurecontainerregistry/helpers_test.go
@@ -2,13 +2,14 @@ package azurecontainerregistry_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociregistry/ocitest"
acr "github.com/blackbirdworks/gopherstack/services/azurecontainerregistry"
)
@@ -72,7 +73,7 @@ func decode(t *testing.T, body []byte) map[string]any {
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
return out
}
diff --git a/services/azurecontainerregistry/hooks.go b/services/azurecontainerregistry/hooks.go
index a41c04f1e6..a2955ea7ea 100644
--- a/services/azurecontainerregistry/hooks.go
+++ b/services/azurecontainerregistry/hooks.go
@@ -1,11 +1,12 @@
package azurecontainerregistry
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociregistry"
)
@@ -36,7 +37,7 @@ func loadChange(app http.Handler, r *http.Request, repo, ref string) (ManifestCh
}
var doc manifestDoc
- if json.Unmarshal(m.Body, &doc) == nil {
+ if json.Unmarshal(m.Body, &doc, jsonx.V1()) == nil {
c.ConfigType = doc.Config.MediaType
if doc.MediaType != "" {
diff --git a/services/azurecontainerregistry/serve.go b/services/azurecontainerregistry/serve.go
index 3ff0dae425..6c9f848730 100644
--- a/services/azurecontainerregistry/serve.go
+++ b/services/azurecontainerregistry/serve.go
@@ -2,13 +2,15 @@ package azurecontainerregistry
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/url"
"slices"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociregistry"
)
@@ -48,7 +50,7 @@ type errorEntry struct {
}
func writeJSON(w http.ResponseWriter, status int, v any) {
- raw, err := json.Marshal(v)
+ raw, err := json.Marshal(v, jsonx.V1())
if err != nil {
status, raw = http.StatusInternalServerError, []byte(`{"errors":[{"code":"UNKNOWN","message":"encode"}]}`)
}
@@ -303,7 +305,7 @@ func parseForm(w http.ResponseWriter, r *http.Request) (url.Values, bool) {
func decodeBody(w http.ResponseWriter, r *http.Request, out any) bool {
r.Body = http.MaxBytesReader(w, r.Body, maxFormBytes)
- if err := json.NewDecoder(r.Body).Decode(out); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(r.Body), out, jsonx.V1()); err != nil {
writeRegistryError(w, http.StatusBadRequest, "INVALID_REQUEST", "The request body is not valid JSON.")
return false
diff --git a/services/azurecontainerregistry/webhooks.go b/services/azurecontainerregistry/webhooks.go
index 804818e3d8..b46c47d12f 100644
--- a/services/azurecontainerregistry/webhooks.go
+++ b/services/azurecontainerregistry/webhooks.go
@@ -3,7 +3,7 @@ package azurecontainerregistry
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"maps"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -292,7 +293,7 @@ func (b *InMemoryBackend) deliver(reg string, w Webhook, content EventContent) {
}
func (b *InMemoryBackend) send(ctx context.Context, reg string, w Webhook, content EventContent) {
- body, err := json.Marshal(content)
+ body, err := json.Marshal(content, jsonx.V1())
if err != nil {
return
}
diff --git a/services/azurecontainerregistry/webhooks_test.go b/services/azurecontainerregistry/webhooks_test.go
index 080e0acff4..6e151c3b8a 100644
--- a/services/azurecontainerregistry/webhooks_test.go
+++ b/services/azurecontainerregistry/webhooks_test.go
@@ -1,7 +1,7 @@
package azurecontainerregistry_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociregistry/ocitest"
acr "github.com/blackbirdworks/gopherstack/services/azurecontainerregistry"
)
@@ -44,7 +45,7 @@ func newReceiver(t *testing.T) *receiver {
var d delivery
d.header = req.Header.Clone()
- _ = json.Unmarshal(raw, &d.content)
+ _ = json.Unmarshal(raw, &d.content, jsonx.V1())
r.mu.Lock()
r.got = append(r.got, d)
@@ -88,7 +89,7 @@ func pushHelmChart(t *testing.T, c ocitest.Client, repo, tag string) ocitest.Ima
"mediaType": "application/vnd.cncf.helm.chart.content.v1.tar+gzip", "size": len(layer),
"digest": layerDigest,
}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
put := c.Do(http.MethodPut, "/v2/"+repo+"/manifests/"+tag, body,
diff --git a/services/azureeventgrid/armscope_test.go b/services/azureeventgrid/armscope_test.go
index 72d244398e..74b05e8fd6 100644
--- a/services/azureeventgrid/armscope_test.go
+++ b/services/azureeventgrid/armscope_test.go
@@ -1,13 +1,14 @@
package azureeventgrid_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azureeventgrid"
)
@@ -138,7 +139,7 @@ func TestPublishResourceEvent(t *testing.T) {
for _, g := range got {
var ev map[string]any
- require.NoError(t, json.Unmarshal([]byte(g.text), &ev))
+ require.NoError(t, json.Unmarshal([]byte(g.text), &ev, jsonx.V1()))
assert.Equal(t, tt.wantTopics[g.queue], ev["topic"])
assert.Equal(t, tt.ev.ResourceURI, ev["subject"])
assert.Equal(t, tt.ev.EventType, ev["eventType"])
diff --git a/services/azureeventgrid/attributes.go b/services/azureeventgrid/attributes.go
index 3d06009b66..84e68a58eb 100644
--- a/services/azureeventgrid/attributes.go
+++ b/services/azureeventgrid/attributes.go
@@ -1,10 +1,12 @@
package azureeventgrid
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -36,13 +38,13 @@ func (d Destination) mappings() ([]attrMapping, error) {
return nil, nil
}
- data, err := json.Marshal(raw)
+ data, err := json.Marshal(raw, jsonx.V1())
if err != nil {
return nil, invalidSpec("deliveryAttributeMappings is not valid JSON")
}
var out []attrMapping
- if err = json.Unmarshal(data, &out); err != nil {
+ if err = json.Unmarshal(data, &out, jsonx.V1()); err != nil {
return nil, invalidSpec("deliveryAttributeMappings is malformed: %v", err)
}
@@ -133,7 +135,7 @@ func headerValue(v any) string {
return s
}
- raw, _ := json.Marshal(v)
+ raw, _ := json.Marshal(v, jsonx.V1())
return string(raw)
}
diff --git a/services/azureeventgrid/batching_test.go b/services/azureeventgrid/batching_test.go
index 78a6bd692d..cfa2d56c58 100644
--- a/services/azureeventgrid/batching_test.go
+++ b/services/azureeventgrid/batching_test.go
@@ -1,7 +1,7 @@
package azureeventgrid_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"maps"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azureeventgrid"
)
@@ -68,7 +69,7 @@ func TestWebhookBatching(t *testing.T) {
for i := range tt.events {
ev := egEvent("e"+string(rune('a'+i)), "/s")
ev.Fields["data"] = map[string]any{"pad": strings.Repeat("x", tt.padding)}
- raw, _ := json.Marshal(ev.Fields)
+ raw, _ := json.Marshal(ev.Fields, jsonx.V1())
ev.Raw = raw
events = append(events, ev)
}
@@ -85,7 +86,7 @@ func TestWebhookBatching(t *testing.T) {
for _, body := range h.bodies {
var arr []map[string]any
- require.NoError(t, json.Unmarshal(body, &arr))
+ require.NoError(t, json.Unmarshal(body, &arr, jsonx.V1()))
sizes = append(sizes, len(arr))
total += len(arr)
}
@@ -127,7 +128,7 @@ func TestWebhookBatchFailureRetriesWholeBatch(t *testing.T) {
var arr []map[string]any
- require.NoError(t, json.Unmarshal(h.bodies[1], &arr))
+ require.NoError(t, json.Unmarshal(h.bodies[1], &arr, jsonx.V1()))
assert.Len(t, arr, 2)
assert.Equal(t, "1", h.requests[1].Header.Get("Aeg-Delivery-Count"))
}
@@ -297,6 +298,6 @@ func TestCloudEventsBatchedDelivery(t *testing.T) {
var arr []map[string]any
- require.NoError(t, json.Unmarshal(body, &arr))
+ require.NoError(t, json.Unmarshal(body, &arr, jsonx.V1()))
assert.Len(t, arr, 2)
}
diff --git a/services/azureeventgrid/deadletter.go b/services/azureeventgrid/deadletter.go
index abbd794d90..f3b1f889d4 100644
--- a/services/azureeventgrid/deadletter.go
+++ b/services/azureeventgrid/deadletter.go
@@ -1,14 +1,16 @@
package azureeventgrid
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func decodeFields(raw []byte) map[string]any {
var m map[string]any
- _ = json.Unmarshal(raw, &m)
+ _ = json.Unmarshal(raw, &m, jsonx.V1())
return m
}
@@ -25,7 +27,7 @@ func deadLetterPayload(item deadItem) ([]byte, error) {
m["publishTime"] = item.d.Published.UTC().Format(time.RFC3339Nano)
m["lastDeliveryAttemptTime"] = item.d.LastAttempt.UTC().Format(time.RFC3339Nano)
- return json.Marshal(m)
+ return json.Marshal(m, jsonx.V1())
}
func blobName(item deadItem, at time.Time) string {
diff --git a/services/azureeventgrid/delivery_test.go b/services/azureeventgrid/delivery_test.go
index 8447c877f0..5937f49057 100644
--- a/services/azureeventgrid/delivery_test.go
+++ b/services/azureeventgrid/delivery_test.go
@@ -1,7 +1,8 @@
package azureeventgrid_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azureeventgrid"
)
@@ -43,9 +45,12 @@ func newHook(t *testing.T, code func(n int) int) *hook {
if !h.manual {
var ev []map[string]any
- _ = json.Unmarshal(body, &ev)
+ _ = json.Unmarshal(body, &ev, jsonx.V1())
code, _ := ev[0]["data"].(map[string]any)["validationCode"].(string)
- _ = json.NewEncoder(w).Encode(map[string]string{"validationResponse": code})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]string{"validationResponse": code},
+ )
}
return
@@ -83,7 +88,10 @@ func TestWebhookHandshake(t *testing.T) {
schema: azureeventgrid.SchemaEventGrid,
wantState: azureeventgrid.StateSucceeded,
respond: func(w http.ResponseWriter, _ *http.Request, code string) {
- _ = json.NewEncoder(w).Encode(map[string]string{"validationResponse": code})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]string{"validationResponse": code},
+ )
},
},
{
@@ -139,7 +147,7 @@ func TestWebhookHandshake(t *testing.T) {
seen.mu.Lock()
seen.headers, seen.method = r.Header.Clone(), r.Method
- _ = json.Unmarshal(body, &seen.event)
+ _ = json.Unmarshal(body, &seen.event, jsonx.V1())
seen.mu.Unlock()
code := ""
@@ -192,7 +200,7 @@ func TestManualValidationUnlocksDelivery(t *testing.T) {
var ev []map[string]any
- require.NoError(t, json.Unmarshal(h.shakeBody, &ev))
+ require.NoError(t, json.Unmarshal(h.shakeBody, &ev, jsonx.V1()))
validationURL, _ = ev[0]["data"].(map[string]any)["validationUrl"].(string)
h.mu.Unlock()
@@ -236,7 +244,7 @@ func TestWebhookDeliveryShape(t *testing.T) {
var got []map[string]any
- require.NoError(t, json.Unmarshal(h.bodies[0], &got))
+ require.NoError(t, json.Unmarshal(h.bodies[0], &got, jsonx.V1()))
require.Len(t, got, 1)
assert.Equal(t, "e1", got[0]["id"])
assert.Equal(t, topicRID, got[0]["topic"])
@@ -323,7 +331,7 @@ func TestRetryAndDeadLetter(t *testing.T) {
var out map[string]any
- require.NoError(t, json.Unmarshal(blobs[0].body, &out))
+ require.NoError(t, json.Unmarshal(blobs[0].body, &out, jsonx.V1()))
assert.Equal(t, tt.wantReason, out["deadLetterReason"])
assert.EqualValues(t, tt.wantCalls, out["deliveryAttempts"])
assert.Equal(t, "e1", out["id"])
@@ -400,7 +408,7 @@ func TestTargetDelivery(t *testing.T) {
var arr []map[string]any
- require.NoError(t, json.Unmarshal(got.body, &arr))
+ require.NoError(t, json.Unmarshal(got.body, &arr, jsonx.V1()))
assert.Equal(t, "e1", arr[0]["id"])
},
},
@@ -437,12 +445,12 @@ func TestTargetDelivery(t *testing.T) {
}
}
-func mustField(t *testing.T, raw []byte, key string) json.RawMessage {
+func mustField(t *testing.T, raw []byte, key string) jsontext.Value {
t.Helper()
- var m map[string]json.RawMessage
+ var m map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(raw, &m))
+ require.NoError(t, json.Unmarshal(raw, &m, jsonx.V1()))
return m[key]
}
diff --git a/services/azureeventgrid/domains_test.go b/services/azureeventgrid/domains_test.go
index 11814db2d6..cebcb04fa2 100644
--- a/services/azureeventgrid/domains_test.go
+++ b/services/azureeventgrid/domains_test.go
@@ -1,7 +1,7 @@
package azureeventgrid_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
eg "github.com/blackbirdworks/gopherstack/services/azureeventgrid"
)
@@ -123,7 +124,7 @@ func TestDomainPublishRouting(t *testing.T) {
for _, s := range e.rec.all() {
var got map[string]any
- require.NoError(t, json.Unmarshal([]byte(s.text), &got))
+ require.NoError(t, json.Unmarshal([]byte(s.text), &got, jsonx.V1()))
assert.True(t, strings.HasPrefix(got["topic"].(string), domainRID+"/topics/"), "%v", got["topic"])
assert.Equal(t, "1", got["metadataVersion"])
}
@@ -334,7 +335,7 @@ func TestDomainSubscriptionWebhookBatchingAndHeaders(t *testing.T) {
var events []eg.Event
for _, id := range []string{"a", "b", "c"} {
- raw, _ := json.Marshal(domainEvent("orders", id, "/s"))
+ raw, _ := json.Marshal(domainEvent("orders", id, "/s"), jsonx.V1())
parsed, perr := eg.ParseEvents(raw, eg.SchemaEventGrid)
require.NoError(t, perr)
@@ -350,7 +351,7 @@ func TestDomainSubscriptionWebhookBatchingAndHeaders(t *testing.T) {
var arr []map[string]any
- require.NoError(t, json.Unmarshal(h.bodies[0], &arr))
+ require.NoError(t, json.Unmarshal(h.bodies[0], &arr, jsonx.V1()))
assert.Len(t, arr, 3)
assert.Equal(t, "v", h.requests[0].Header.Get("X-Secret"))
assert.Equal(t, domainRID+"/topics/orders", arr[0]["topic"])
diff --git a/services/azureeventgrid/filter.go b/services/azureeventgrid/filter.go
index d45f80eaa9..35f6bb162b 100644
--- a/services/azureeventgrid/filter.go
+++ b/services/azureeventgrid/filter.go
@@ -1,8 +1,9 @@
package azureeventgrid
import (
- "encoding/json"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var attrAliases = map[string]string{ //nolint:gochecknoglobals // static alias table
@@ -162,7 +163,7 @@ func (a AdvancedFilter) matchScalar(v any) bool {
return a.matchString(x)
case float64:
return a.matchNumber(x)
- case json.Number:
+ case jsonx.Number:
f, err := x.Float64()
return err == nil && a.matchNumber(f)
@@ -230,7 +231,7 @@ func toFloat(v any) (float64, bool) {
return x, true
case int:
return float64(x), true
- case json.Number:
+ case jsonx.Number:
f, err := x.Float64()
return f, err == nil
diff --git a/services/azureeventgrid/filter_test.go b/services/azureeventgrid/filter_test.go
index 2d45601683..c61b552a41 100644
--- a/services/azureeventgrid/filter_test.go
+++ b/services/azureeventgrid/filter_test.go
@@ -1,12 +1,13 @@
package azureeventgrid_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
eg "github.com/blackbirdworks/gopherstack/services/azureeventgrid"
)
@@ -21,7 +22,7 @@ func decode(t *testing.T, raw string) map[string]any {
var m map[string]any
- require.NoError(t, json.Unmarshal([]byte(raw), &m))
+ require.NoError(t, json.Unmarshal([]byte(raw), &m, jsonx.V1()))
return m
}
diff --git a/services/azureeventgrid/helpers_test.go b/services/azureeventgrid/helpers_test.go
index d0ba346b29..dbc5d9dffd 100644
--- a/services/azureeventgrid/helpers_test.go
+++ b/services/azureeventgrid/helpers_test.go
@@ -1,7 +1,7 @@
package azureeventgrid_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"sync"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azureeventgrid"
)
@@ -105,7 +106,7 @@ func egEvent(id, subject string) azureeventgrid.Event {
raw, _ := json.Marshal(map[string]any{
"id": id, "subject": subject, "eventType": "App.Thing", "eventTime": "2026-10-20T12:00:00Z",
"dataVersion": "1", "data": map[string]any{"n": 1},
- })
+ }, jsonx.V1())
events, err := azureeventgrid.ParseEvents(raw, azureeventgrid.SchemaEventGrid)
if err != nil {
diff --git a/services/azureeventgrid/http_routes.go b/services/azureeventgrid/http_routes.go
index 12d2061d38..339d9e595c 100644
--- a/services/azureeventgrid/http_routes.go
+++ b/services/azureeventgrid/http_routes.go
@@ -1,7 +1,7 @@
package azureeventgrid
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"net"
@@ -11,6 +11,8 @@ import (
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -283,7 +285,12 @@ func (h *Handler) handlePull(c *echo.Context, rt route) error {
LockTokens []string `json:"lockTokens"`
}
- if err = json.Unmarshal(body, &in); err != nil || len(in.LockTokens) == 0 || len(in.LockTokens) > maxLockTokens {
+ if err = json.Unmarshal(
+ body,
+ &in,
+ jsonx.V1(),
+ ); err != nil || len(in.LockTokens) == 0 ||
+ len(in.LockTokens) > maxLockTokens {
return writeError(c, http.StatusBadRequest, outcomeBadRequest, "lockTokens must hold 1 to 100 tokens.")
}
diff --git a/services/azureeventgrid/model.go b/services/azureeventgrid/model.go
index 7a14e3086d..1f435665d2 100644
--- a/services/azureeventgrid/model.go
+++ b/services/azureeventgrid/model.go
@@ -1,7 +1,7 @@
package azureeventgrid
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -69,14 +69,14 @@ type Spec struct {
}
type delivery struct {
- Due time.Time `json:"due"`
- Published time.Time `json:"published"`
- LastAttempt time.Time `json:"lastAttempt"`
- ID string `json:"id"`
- LastOutcome string `json:"lastOutcome,omitempty"`
- Event json.RawMessage `json:"event"`
- Attempts int `json:"attempts"`
- InFlight bool `json:"-"`
+ Due time.Time `json:"due"`
+ Published time.Time `json:"published"`
+ LastAttempt time.Time `json:"lastAttempt"`
+ ID string `json:"id"`
+ LastOutcome string `json:"lastOutcome,omitempty"`
+ Event jsontext.Value `json:"event"`
+ Attempts int `json:"attempts"`
+ InFlight bool `json:"-"`
}
type subscription struct {
@@ -99,12 +99,12 @@ type topic struct {
}
type pullEvent struct {
- AvailableAt time.Time `json:"availableAt"`
- LockedUntil time.Time `json:"lockedUntil"`
- ExpiresAt time.Time `json:"expiresAt"`
- LockToken string `json:"lockToken,omitempty"`
- Event json.RawMessage `json:"event"`
- Count int `json:"count"`
+ AvailableAt time.Time `json:"availableAt"`
+ LockedUntil time.Time `json:"lockedUntil"`
+ ExpiresAt time.Time `json:"expiresAt"`
+ LockToken string `json:"lockToken,omitempty"`
+ Event jsontext.Value `json:"event"`
+ Count int `json:"count"`
}
type pullSub struct {
diff --git a/services/azureeventgrid/publish.go b/services/azureeventgrid/publish.go
index 808e5f755e..fb3000cfaa 100644
--- a/services/azureeventgrid/publish.go
+++ b/services/azureeventgrid/publish.go
@@ -2,9 +2,12 @@ package azureeventgrid
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -18,7 +21,7 @@ const (
type Event struct {
Fields map[string]any
ID string
- Raw json.RawMessage
+ Raw jsontext.Value
}
func invalidEvent(format string, args ...any) error {
@@ -33,14 +36,14 @@ func ParseEvents(body []byte, schema string) ([]Event, error) {
return nil, invalidEvent("the request body is empty")
}
- var raws []json.RawMessage
+ var raws []jsontext.Value
if trimmed[0] == '[' {
- if err := json.Unmarshal(trimmed, &raws); err != nil {
+ if err := json.Unmarshal(trimmed, &raws, jsonx.V1()); err != nil {
return nil, invalidEvent("the request body is not valid JSON")
}
} else {
- raws = []json.RawMessage{trimmed}
+ raws = []jsontext.Value{trimmed}
}
if len(raws) == 0 {
@@ -65,9 +68,9 @@ func ParseEvents(body []byte, schema string) ([]Event, error) {
return out, nil
}
-func parseEvent(raw json.RawMessage, schema string) (Event, error) {
+func parseEvent(raw jsontext.Value, schema string) (Event, error) {
var fields map[string]any
- if err := json.Unmarshal(raw, &fields); err != nil || fields == nil {
+ if err := json.Unmarshal(raw, &fields, jsonx.V1()); err != nil || fields == nil {
return Event{}, invalidEvent("the event is not a JSON object")
}
@@ -99,7 +102,7 @@ func (e *Event) stamp(topicRID, schema string) error {
e.Fields["topic"], e.Fields["metadataVersion"] = topicRID, "1"
}
- raw, err := json.Marshal(e.Fields)
+ raw, err := json.Marshal(e.Fields, jsonx.V1())
if err != nil {
return invalidEvent("the event cannot be encoded")
}
diff --git a/services/azureeventgrid/pull.go b/services/azureeventgrid/pull.go
index 94a130a9bd..c4107177d9 100644
--- a/services/azureeventgrid/pull.go
+++ b/services/azureeventgrid/pull.go
@@ -2,12 +2,14 @@ package azureeventgrid
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"time"
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/iso8601"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -46,7 +48,7 @@ type pullProps struct {
// Received is one event handed out by a receive call.
type Received struct {
LockToken string
- Event json.RawMessage
+ Event jsontext.Value
DeliveryCount int
}
@@ -84,19 +86,19 @@ func (s *pullSub) props() map[string]any {
},
}
- raw, _ := json.Marshal(p)
+ raw, _ := json.Marshal(p, jsonx.V1())
out := map[string]any{}
- _ = json.Unmarshal(raw, &out)
+ _ = json.Unmarshal(raw, &out, jsonx.V1())
out[propProvisioning] = StateSucceeded
return out
}
func parsePullProps(props map[string]any) (pullProps, error) {
- raw, _ := json.Marshal(props)
+ raw, _ := json.Marshal(props, jsonx.V1())
var p pullProps
- if err := json.Unmarshal(raw, &p); err != nil {
+ if err := json.Unmarshal(raw, &p, jsonx.V1()); err != nil {
return p, invalidSpec("properties are malformed: %v", err)
}
diff --git a/services/azureeventgrid/subscription.go b/services/azureeventgrid/subscription.go
index b67e24727e..ff79d3081f 100644
--- a/services/azureeventgrid/subscription.go
+++ b/services/azureeventgrid/subscription.go
@@ -2,12 +2,14 @@ package azureeventgrid
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"net/url"
"sort"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -16,13 +18,13 @@ const (
)
func parseSpec(props map[string]any, inputSchema string) (Spec, error) {
- raw, err := json.Marshal(props)
+ raw, err := json.Marshal(props, jsonx.V1())
if err != nil {
return Spec{}, invalidSpec("properties are not valid JSON")
}
var spec Spec
- if err = json.Unmarshal(raw, &spec); err != nil {
+ if err = json.Unmarshal(raw, &spec, jsonx.V1()); err != nil {
return Spec{}, invalidSpec("properties are malformed: %v", err)
}
@@ -70,9 +72,9 @@ func normalizeSpec(spec *Spec, inputSchema string) error {
// specProps renders the ARM properties of a subscription. Webhook secrets in the
// query string are withheld (see FullURL).
func specProps(spec Spec, state, topicRID string) map[string]any {
- raw, _ := json.Marshal(spec)
+ raw, _ := json.Marshal(spec, jsonx.V1())
out := map[string]any{}
- _ = json.Unmarshal(raw, &out)
+ _ = json.Unmarshal(raw, &out, jsonx.V1())
if dest, ok := out["destination"].(map[string]any); ok && spec.Destination.EndpointType == EndpointWebHook {
dest["properties"] = webhookProps(spec.Destination)
@@ -199,13 +201,13 @@ func mergeSpecPatch(cur Spec, patch map[string]any) (map[string]any, error) {
}
}
- raw, err := json.Marshal(cur)
+ raw, err := json.Marshal(cur, jsonx.V1())
if err != nil {
return nil, invalidSpec("current spec is not valid JSON")
}
merged := map[string]any{}
- if err = json.Unmarshal(raw, &merged); err != nil {
+ if err = json.Unmarshal(raw, &merged, jsonx.V1()); err != nil {
return nil, invalidSpec("current spec is not valid JSON")
}
diff --git a/services/azureeventgrid/webhook.go b/services/azureeventgrid/webhook.go
index 0073ddb324..a2e9be97ee 100644
--- a/services/azureeventgrid/webhook.go
+++ b/services/azureeventgrid/webhook.go
@@ -3,7 +3,7 @@ package azureeventgrid
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/url"
@@ -11,6 +11,8 @@ import (
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -96,7 +98,7 @@ func validationEvent(topicRID, code, validationURL string) ([]byte, error) {
"id": uuid.NewString(), "topic": topicRID, fieldSubject: "", fieldEventType: validationEventType,
fieldEventTime: time.Now().UTC().Format(time.RFC3339Nano), "metadataVersion": "1", fieldDataVersion: "2",
"data": map[string]any{"validationCode": code, "validationUrl": validationURL},
- }})
+ }}, jsonx.V1())
}
// postHandshake sends the SubscriptionValidationEvent; echoed reports whether the
@@ -138,7 +140,7 @@ func (b *InMemoryBackend) postHandshake(
}
data, _ := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes))
- _ = json.Unmarshal(data, &out)
+ _ = json.Unmarshal(data, &out, jsonx.V1())
return out.ValidationResponse == code, nil
}
diff --git a/services/azureeventhubs/http_routes.go b/services/azureeventhubs/http_routes.go
index c4aedbec97..8ffe0d8260 100644
--- a/services/azureeventhubs/http_routes.go
+++ b/services/azureeventhubs/http_routes.go
@@ -1,7 +1,7 @@
package azureeventhubs
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/amqp1"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -151,7 +152,7 @@ func (h *Handler) authorized(r *http.Request, right string) bool {
}
func writeError(c *echo.Context, status int, msg string) error {
- body, err := json.Marshal(map[string]string{"Code": http.StatusText(status), "Message": msg})
+ body, err := json.Marshal(map[string]string{"Code": http.StatusText(status), "Message": msg}, jsonx.V1())
if err != nil {
return c.String(http.StatusInternalServerError, "internal error")
}
@@ -219,7 +220,7 @@ func restEvents(r *http.Request, body []byte) ([]NewEvent, error) {
if bp := r.Header.Get("Brokerproperties"); bp != "" {
var props brokerProperties
- if err := json.Unmarshal([]byte(bp), &props); err != nil {
+ if err := json.Unmarshal([]byte(bp), &props, jsonx.V1()); err != nil {
return nil, err
}
@@ -233,9 +234,9 @@ func restEvents(r *http.Request, body []byte) ([]NewEvent, error) {
func batchEvents(body []byte) ([]NewEvent, error) {
var items []jsonEvent
- if err := json.Unmarshal(body, &items); err != nil {
+ if err := json.Unmarshal(body, &items, jsonx.V1()); err != nil {
var one jsonEvent
- if err2 := json.Unmarshal(body, &one); err2 != nil {
+ if err2 := json.Unmarshal(body, &one, jsonx.V1()); err2 != nil {
return nil, err
}
diff --git a/services/azurefile/ops_share.go b/services/azurefile/ops_share.go
index 014a657a86..032a236cc2 100644
--- a/services/azurefile/ops_share.go
+++ b/services/azurefile/ops_share.go
@@ -1,7 +1,7 @@
package azurefile
import (
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"math"
"net/http"
@@ -9,6 +9,7 @@ import (
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -751,7 +752,12 @@ func (h *Handler) createPermission(rc *reqCtx) (*resp, *apiError) {
}
var in permissionJSON
- if err := json.Unmarshal(data, &in); err != nil || in.Permission == "" || len(in.Permission) > maxSDDLLen {
+ if err := json.Unmarshal(
+ data,
+ &in,
+ jsonx.V1(),
+ ); err != nil || in.Permission == "" ||
+ len(in.Permission) > maxSDDLLen {
return nil, errInvalidHeader("x-ms-file-permission")
}
@@ -792,7 +798,7 @@ func (h *Handler) getPermission(rc *reqCtx) (*resp, *apiError) {
return nil, errInvalidHeader("x-ms-file-permission-key")
}
- body, err := json.Marshal(permissionJSON{Permission: sddl, Format: "Sddl"})
+ body, err := json.Marshal(permissionJSON{Permission: sddl, Format: "Sddl"}, jsonx.V1())
if err != nil {
return nil, newErr(http.StatusInternalServerError, "InternalError", "Failed to marshal response.")
}
diff --git a/services/azurefunctions/functions.go b/services/azurefunctions/functions.go
index 7ebac1bc80..6c64a3e601 100644
--- a/services/azurefunctions/functions.go
+++ b/services/azurefunctions/functions.go
@@ -2,7 +2,7 @@ package azurefunctions
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -13,6 +13,7 @@ import (
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -107,7 +108,7 @@ func (b *InMemoryBackend) adminFunctions(ctx context.Context, name string) ([]ad
}
var fns []adminFunction
- if err = json.Unmarshal(body, &fns); err != nil {
+ if err = json.Unmarshal(body, &fns, jsonx.V1()); err != nil {
return nil, fmt.Errorf("decode host functions: %w", err)
}
diff --git a/services/azurefunctions/keys.go b/services/azurefunctions/keys.go
index d7047a58a2..be8684c3f3 100644
--- a/services/azurefunctions/keys.go
+++ b/services/azurefunctions/keys.go
@@ -3,12 +3,14 @@ package azurefunctions
import (
"crypto/rand"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"os"
"path/filepath"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -105,7 +107,7 @@ func writeSecrets(dir string, st *appState) error {
Source: "runtime",
}
- raw, err := json.Marshal(host)
+ raw, err := json.Marshal(host, jsonx.V1())
if err != nil {
return fmt.Errorf("encode host secrets: %w", err)
}
@@ -117,7 +119,7 @@ func writeSecrets(dir string, st *appState) error {
want := map[string]bool{"host.json": true}
for fn, keys := range st.FunctionKeys {
- raw, err = json.Marshal(functionSecretsFile{Source: "runtime", Keys: sortedKeys(keys)})
+ raw, err = json.Marshal(functionSecretsFile{Source: "runtime", Keys: sortedKeys(keys)}, jsonx.V1())
if err != nil {
return fmt.Errorf("encode function secrets: %w", err)
}
diff --git a/services/azurefunctions/keys_test.go b/services/azurefunctions/keys_test.go
index ae12992030..648dbb4efa 100644
--- a/services/azurefunctions/keys_test.go
+++ b/services/azurefunctions/keys_test.go
@@ -1,7 +1,7 @@
package azurefunctions_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"path/filepath"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azurefunctions"
)
@@ -33,7 +34,7 @@ func loadSecrets(t *testing.T, dir, file string) secretsFile {
require.NoError(t, err)
var s secretsFile
- require.NoError(t, json.Unmarshal([]byte(raw), &s))
+ require.NoError(t, json.Unmarshal([]byte(raw), &s, jsonx.V1()))
return s
}
diff --git a/services/azurefunctions/kudu.go b/services/azurefunctions/kudu.go
index ff4fbdcfc7..8b64e1da45 100644
--- a/services/azurefunctions/kudu.go
+++ b/services/azurefunctions/kudu.go
@@ -2,13 +2,15 @@ package azurefunctions
import (
"crypto/subtle"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
"slices"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -74,7 +76,7 @@ func toDeploymentJSON(r *http.Request, name string, d Deployment, active bool) d
}
func writeJSON(w http.ResponseWriter, status int, v any) {
- raw, err := json.Marshal(v)
+ raw, err := json.Marshal(v, jsonx.V1())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -83,7 +85,7 @@ func writeJSON(w http.ResponseWriter, status int, v any) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(status)
- _, _ = w.Write(raw)
+ _, _ = w.Write(raw) // #nosec G705
}
// authorized accepts the app's publishing credentials as Basic auth, or any Bearer token (Kudu trusts the
diff --git a/services/azurefunctions/kudu_test.go b/services/azurefunctions/kudu_test.go
index 924827ba6d..93b17a6c65 100644
--- a/services/azurefunctions/kudu_test.go
+++ b/services/azurefunctions/kudu_test.go
@@ -1,13 +1,14 @@
package azurefunctions_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azurefunctions"
)
@@ -133,7 +134,7 @@ func TestKudu_DeploymentsAndDownload(t *testing.T) {
_, body = e.do(t, http.MethodPost, scm, "/api/zipdeploy", pkg, auth)
var first map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &first))
+ require.NoError(t, json.Unmarshal([]byte(body), &first, jsonx.V1()))
assert.InDelta(t, 4, first["status"], 0)
assert.Equal(t, true, first["complete"])
assert.Equal(t, "ZipDeploy", first["deployer"])
@@ -143,7 +144,7 @@ func TestKudu_DeploymentsAndDownload(t *testing.T) {
_, body = e.do(t, http.MethodGet, scm, "/api/deployments", nil, auth)
var list []map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &list))
+ require.NoError(t, json.Unmarshal([]byte(body), &list, jsonx.V1()))
require.Len(t, list, 2)
assert.Equal(t, "OneDeploy", list[0]["deployer"], "newest first")
assert.Equal(t, true, list[0]["active"])
diff --git a/services/azurefunctions/webapp.go b/services/azurefunctions/webapp.go
index 1ecd3d2eff..1d30d423b4 100644
--- a/services/azurefunctions/webapp.go
+++ b/services/azurefunctions/webapp.go
@@ -2,7 +2,7 @@ package azurefunctions
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"os"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/container"
"github.com/blackbirdworks/gopherstack/pkgs/gcpserve"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -193,7 +194,7 @@ func hasStartScript(dir string) bool {
Scripts map[string]string `json:"scripts"`
}
- return json.Unmarshal(raw, &pkg) == nil && pkg.Scripts["start"] != ""
+ return json.Unmarshal(raw, &pkg, jsonx.V1()) == nil && pkg.Scripts["start"] != ""
}
func dotnetEntry(dir string) string {
diff --git a/services/azurefunctions/zip.go b/services/azurefunctions/zip.go
index 2ce0fed56a..dd194f858b 100644
--- a/services/azurefunctions/zip.go
+++ b/services/azurefunctions/zip.go
@@ -3,7 +3,7 @@ package azurefunctions
import (
"archive/zip"
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"os"
@@ -12,6 +12,8 @@ import (
"regexp"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -143,7 +145,7 @@ func scanFunctions(data []byte) []FunctionDef {
_ = rc.Close()
var cfg map[string]any
- if readErr != nil || json.Unmarshal(bytes.TrimPrefix(raw, []byte("\xef\xbb\xbf")), &cfg) != nil {
+ if readErr != nil || json.Unmarshal(bytes.TrimPrefix(raw, []byte("\xef\xbb\xbf")), &cfg, jsonx.V1()) != nil {
continue
}
diff --git a/services/azuregraph/apps.go b/services/azuregraph/apps.go
index 2e4d9e980f..35106ba886 100644
--- a/services/azuregraph/apps.go
+++ b/services/azuregraph/apps.go
@@ -1,7 +1,7 @@
package azuregraph
import (
- "encoding/json"
+ "encoding/json/jsontext"
"net/url"
"slices"
"strings"
@@ -107,7 +107,7 @@ func validateAppRoles(roles []AppRole, scopes []PermissionScope) error {
}
// CreateApplication registers an application.
-func (b *Backend) CreateApplication(body map[string]json.RawMessage) (Application, error) {
+func (b *Backend) CreateApplication(body map[string]jsontext.Value) (Application, error) {
b.mu.Lock("CreateApplication")
defer b.mu.Unlock()
@@ -159,7 +159,7 @@ func (b *Backend) ListApplications() []Application {
}
// UpdateApplication applies a PATCH body.
-func (b *Backend) UpdateApplication(r Ref, body map[string]json.RawMessage) error {
+func (b *Backend) UpdateApplication(r Ref, body map[string]jsontext.Value) error {
b.mu.Lock("UpdateApplication")
defer b.mu.Unlock()
diff --git a/services/azuregraph/assign.go b/services/azuregraph/assign.go
index 34d148f992..5efc42c6af 100644
--- a/services/azuregraph/assign.go
+++ b/services/azuregraph/assign.go
@@ -1,7 +1,7 @@
package azuregraph
import (
- "encoding/json"
+ "encoding/json/jsontext"
"slices"
"strings"
)
@@ -24,7 +24,7 @@ func principalType(kind string) string {
// CreateAppRoleAssignment grants an app role. ownerID is the path service principal, user or group;
// asResource selects appRoleAssignedTo (owner is the resource) over appRoleAssignments (owner is the principal).
func (b *Backend) CreateAppRoleAssignment(
- ownerID string, asResource bool, body map[string]json.RawMessage,
+ ownerID string, asResource bool, body map[string]jsontext.Value,
) (AppRoleAssignment, error) {
if err := requireProps("AppRoleAssignment", body, "principalId", "resourceId", "appRoleId"); err != nil {
return AppRoleAssignment{}, err
@@ -135,7 +135,7 @@ func (b *Backend) DeleteAppRoleAssignment(id, assignmentID string, asResource bo
}
// CreateGrant records a delegated permission grant.
-func (b *Backend) CreateGrant(body map[string]json.RawMessage) (OAuth2PermissionGrant, error) {
+func (b *Backend) CreateGrant(body map[string]jsontext.Value) (OAuth2PermissionGrant, error) {
if err := requireProps("OAuth2PermissionGrant", body, "clientId", "consentType", "resourceId"); err != nil {
return OAuth2PermissionGrant{}, err
}
@@ -181,7 +181,7 @@ func (b *Backend) ListGrants() []OAuth2PermissionGrant {
}
// UpdateGrant changes the scope of a grant.
-func (b *Backend) UpdateGrant(id string, body map[string]json.RawMessage) error {
+func (b *Backend) UpdateGrant(id string, body map[string]jsontext.Value) error {
b.mu.Lock("UpdateGrant")
defer b.mu.Unlock()
diff --git a/services/azuregraph/collections.go b/services/azuregraph/collections.go
index c85233dfae..d54f58ad7f 100644
--- a/services/azuregraph/collections.go
+++ b/services/azuregraph/collections.go
@@ -1,10 +1,13 @@
package azuregraph
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"reflect"
"regexp"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// collection adapts one directory object type to the generic CRUD routes.
@@ -12,8 +15,8 @@ type collection struct {
fields map[string]bool
list func(*Backend) []map[string]any
get func(*Backend, Ref) (map[string]any, error)
- create func(*Backend, map[string]json.RawMessage) (map[string]any, error)
- patch func(*Backend, Ref, map[string]json.RawMessage) error
+ create func(*Backend, map[string]jsontext.Value) (map[string]any, error)
+ patch func(*Backend, Ref, map[string]jsontext.Value) error
del func(*Backend, Ref) error
name string
resource string
@@ -33,13 +36,13 @@ func splitKey(seg string) (string, Ref) {
// toMap renders an entity as its generic JSON object.
func toMap(v any) map[string]any {
- raw, err := json.Marshal(v)
+ raw, err := json.Marshal(v, jsonx.V1())
if err != nil {
return map[string]any{}
}
out := map[string]any{}
- _ = json.Unmarshal(raw, &out)
+ _ = json.Unmarshal(raw, &out, jsonx.V1())
if t := reflect.TypeOf(v); t.Kind() == reflect.Struct {
for name := range jsonFields(t) {
@@ -84,15 +87,15 @@ func newCollection[T any](
name, resource, kind string,
list func(*Backend) []T,
get func(*Backend, Ref) (T, error),
- create func(*Backend, map[string]json.RawMessage) (T, error),
- patch func(*Backend, Ref, map[string]json.RawMessage) error,
+ create func(*Backend, map[string]jsontext.Value) (T, error),
+ patch func(*Backend, Ref, map[string]jsontext.Value) error,
del func(*Backend, Ref) error,
) *collection {
return &collection{
name: name, resource: resource, kind: kind, fields: fieldsOf[T](), patch: patch, del: del,
list: func(b *Backend) []map[string]any { return mapAll(list(b)) },
get: func(b *Backend, r Ref) (map[string]any, error) { return one(get(b, r)) },
- create: func(b *Backend, m map[string]json.RawMessage) (map[string]any, error) { return one(create(b, m)) },
+ create: func(b *Backend, m map[string]jsontext.Value) (map[string]any, error) { return one(create(b, m)) },
}
}
diff --git a/services/azuregraph/groups.go b/services/azuregraph/groups.go
index a6dbc59768..e08edb32bc 100644
--- a/services/azuregraph/groups.go
+++ b/services/azuregraph/groups.go
@@ -1,7 +1,7 @@
package azuregraph
import (
- "encoding/json"
+ "encoding/json/jsontext"
"slices"
"strings"
)
@@ -55,7 +55,7 @@ func validateGroup(g *Group) error {
}
// CreateGroup creates a group; members@odata.bind and owners@odata.bind add references atomically.
-func (b *Backend) CreateGroup(body map[string]json.RawMessage) (Group, error) {
+func (b *Backend) CreateGroup(body map[string]jsontext.Value) (Group, error) {
if err := requireProps("Group", body, "displayName", "mailEnabled", "mailNickname", "securityEnabled"); err != nil {
return Group{}, err
}
@@ -119,7 +119,7 @@ func (b *Backend) ListGroups() []Group {
}
// UpdateGroup applies a PATCH body. isAssignableToRole can only be set at creation.
-func (b *Backend) UpdateGroup(r Ref, body map[string]json.RawMessage) error {
+func (b *Backend) UpdateGroup(r Ref, body map[string]jsontext.Value) error {
b.mu.Lock("UpdateGroup")
defer b.mu.Unlock()
diff --git a/services/azuregraph/helpers_test.go b/services/azuregraph/helpers_test.go
index 502485101b..0ffaf5be0d 100644
--- a/services/azuregraph/helpers_test.go
+++ b/services/azuregraph/helpers_test.go
@@ -2,7 +2,7 @@ package azuregraph_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/aadauth"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azuregraph"
)
@@ -98,7 +99,7 @@ func (e *env) req(t *testing.T, method, path string, body any, hdr ...string) re
case string:
rdr = strings.NewReader(b)
default:
- raw, err := json.Marshal(b)
+ raw, err := json.Marshal(b, jsonx.V1())
require.NoError(t, err)
rdr = bytes.NewReader(raw)
@@ -126,7 +127,7 @@ func (e *env) req(t *testing.T, method, path string, body any, hdr ...string) re
require.NoError(t, err)
out := resp{status: res.StatusCode, header: res.Header, raw: string(raw)}
- _ = json.Unmarshal(raw, &out.body)
+ _ = json.Unmarshal(raw, &out.body, jsonx.V1())
return out
}
diff --git a/services/azuregraph/model.go b/services/azuregraph/model.go
index bacadfe11b..c757302e8f 100644
--- a/services/azuregraph/model.go
+++ b/services/azuregraph/model.go
@@ -1,7 +1,7 @@
package azuregraph
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -124,15 +124,15 @@ type Application struct {
DisplayName string `json:"displayName"`
Description string `json:"description,omitempty"`
API APIApplication `json:"api"`
- Certification json.RawMessage `json:"certification,omitempty"`
- VerifiedPublisher json.RawMessage `json:"verifiedPublisher,omitempty"`
+ Certification jsontext.Value `json:"certification,omitempty"`
+ VerifiedPublisher jsontext.Value `json:"verifiedPublisher,omitempty"`
SPA RedirectFacet `json:"spa"`
- DisabledByMicrosoftStatus json.RawMessage `json:"disabledByMicrosoftStatus,omitempty"`
- AddIns json.RawMessage `json:"addIns,omitempty"`
- ParentalControlSettings json.RawMessage `json:"parentalControlSettings,omitempty"`
- Info json.RawMessage `json:"info,omitempty"`
- OptionalClaims json.RawMessage `json:"optionalClaims,omitempty"`
- AuthenticationBehaviors json.RawMessage `json:"authenticationBehaviors,omitempty"`
+ DisabledByMicrosoftStatus jsontext.Value `json:"disabledByMicrosoftStatus,omitempty"`
+ AddIns jsontext.Value `json:"addIns,omitempty"`
+ ParentalControlSettings jsontext.Value `json:"parentalControlSettings,omitempty"`
+ Info jsontext.Value `json:"info,omitempty"`
+ OptionalClaims jsontext.Value `json:"optionalClaims,omitempty"`
+ AuthenticationBehaviors jsontext.Value `json:"authenticationBehaviors,omitempty"`
PublicClient RedirectFacet `json:"publicClient"`
IdentifierURIs []string `json:"identifierUris"`
Tags []string `json:"tags"`
@@ -147,18 +147,18 @@ type Application struct {
// ServicePrincipal is the local instance of an application in the tenant.
type ServicePrincipal struct {
- AddIns json.RawMessage `json:"addIns,omitempty"`
- DisabledByMicrosoftStatus json.RawMessage `json:"disabledByMicrosoftStatus,omitempty"`
- ResourceSpecificPermissions json.RawMessage `json:"resourceSpecificApplicationPermissions,omitempty"`
- PasswordSSOSettings json.RawMessage `json:"passwordSingleSignOnSettings,omitempty"`
- PreferredKeyEndDateTime json.RawMessage `json:"preferredTokenSigningKeyEndDateTime,omitempty"`
- CustomSecurityAttributes json.RawMessage `json:"customSecurityAttributes,omitempty"`
+ AddIns jsontext.Value `json:"addIns,omitempty"`
+ DisabledByMicrosoftStatus jsontext.Value `json:"disabledByMicrosoftStatus,omitempty"`
+ ResourceSpecificPermissions jsontext.Value `json:"resourceSpecificApplicationPermissions,omitempty"`
+ PasswordSSOSettings jsontext.Value `json:"passwordSingleSignOnSettings,omitempty"`
+ PreferredKeyEndDateTime jsontext.Value `json:"preferredTokenSigningKeyEndDateTime,omitempty"`
+ CustomSecurityAttributes jsontext.Value `json:"customSecurityAttributes,omitempty"`
SAMLMetadataURL string `json:"samlMetadataUrl,omitempty"`
ApplicationTemplateID string `json:"applicationTemplateId,omitempty"`
CreatedDateTime time.Time `json:"createdDateTime"`
- Info json.RawMessage `json:"info,omitempty"`
- VerifiedPublisher json.RawMessage `json:"verifiedPublisher,omitempty"`
- SAMLSingleSignOnSettings json.RawMessage `json:"samlSingleSignOnSettings,omitempty"`
+ Info jsontext.Value `json:"info,omitempty"`
+ VerifiedPublisher jsontext.Value `json:"verifiedPublisher,omitempty"`
+ SAMLSingleSignOnSettings jsontext.Value `json:"samlSingleSignOnSettings,omitempty"`
ID string `json:"id"`
AppID string `json:"appId"`
DisplayName string `json:"displayName"`
diff --git a/services/azuregraph/patch.go b/services/azuregraph/patch.go
index 7feb1372fb..14c47ec55b 100644
--- a/services/azuregraph/patch.go
+++ b/services/azuregraph/patch.go
@@ -2,10 +2,13 @@ package azuregraph
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"reflect"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type normalizer[T any] interface {
@@ -28,7 +31,7 @@ func jsonFields(t reflect.Type) map[string]bool {
}
// mergeJSON overlays patch onto base: objects merge recursively, null deletes, anything else replaces.
-func mergeJSON(base, patch map[string]json.RawMessage) {
+func mergeJSON(base, patch map[string]jsontext.Value) {
for k, v := range patch {
if bytes.Equal(bytes.TrimSpace(v), []byte(jsonNull)) {
delete(base, k)
@@ -36,11 +39,12 @@ func mergeJSON(base, patch map[string]json.RawMessage) {
continue
}
- var pm, bm map[string]json.RawMessage
- if json.Unmarshal(v, &pm) == nil && pm != nil && json.Unmarshal(base[k], &bm) == nil && bm != nil {
+ var pm, bm map[string]jsontext.Value
+ if json.Unmarshal(v, &pm, jsonx.V1()) == nil && pm != nil && json.Unmarshal(base[k], &bm, jsonx.V1()) == nil &&
+ bm != nil {
mergeJSON(bm, pm)
- if merged, err := json.Marshal(bm); err == nil {
+ if merged, err := json.Marshal(bm, jsonx.V1()); err == nil {
base[k] = merged
continue
@@ -53,9 +57,9 @@ func mergeJSON(base, patch map[string]json.RawMessage) {
// applyBody merges a create or PATCH body onto cur and returns the resulting object.
// Unknown or read-only properties are rejected the way Graph does.
-func applyBody[T any, P normalizer[T]](cur *T, body map[string]json.RawMessage, readOnly ...string) (*T, error) {
+func applyBody[T any, P normalizer[T]](cur *T, body map[string]jsontext.Value, readOnly ...string) (*T, error) {
known := jsonFields(reflect.TypeFor[T]())
- patch := make(map[string]json.RawMessage, len(body))
+ patch := make(map[string]jsontext.Value, len(body))
for k, v := range body {
if strings.Contains(k, "@") {
@@ -73,25 +77,25 @@ func applyBody[T any, P normalizer[T]](cur *T, body map[string]json.RawMessage,
patch[k] = v
}
- raw, err := json.Marshal(cur)
+ raw, err := json.Marshal(cur, jsonx.V1())
if err != nil {
return nil, err //nolint:wrapcheck // marshal of an owned struct cannot fail on input
}
- base := map[string]json.RawMessage{}
- if err = json.Unmarshal(raw, &base); err != nil {
+ base := map[string]jsontext.Value{}
+ if err = json.Unmarshal(raw, &base, jsonx.V1()); err != nil {
return nil, err //nolint:wrapcheck // as above
}
mergeJSON(base, patch)
- merged, err := json.Marshal(base)
+ merged, err := json.Marshal(base, jsonx.V1())
if err != nil {
return nil, err //nolint:wrapcheck // as above
}
out := new(T)
- if err = json.Unmarshal(merged, out); err != nil {
+ if err = json.Unmarshal(merged, out, jsonx.V1()); err != nil {
return nil, badRequest(
"Unable to read JSON request payload. Please ensure Content-Type header is set and payload is of valid JSON format.",
)
diff --git a/services/azuregraph/relations.go b/services/azuregraph/relations.go
index 21d446c79d..5b56da07ec 100644
--- a/services/azuregraph/relations.go
+++ b/services/azuregraph/relations.go
@@ -1,9 +1,12 @@
package azuregraph
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Object is a directory object of any kind with its entity (Application, ServicePrincipal, User or Group).
@@ -55,13 +58,13 @@ func idFromRef(s string) string {
}
// bindTargets resolves a list of @odata.bind references, failing if any target does not exist.
-func (b *Backend) bindTargets(raw json.RawMessage) ([]string, error) {
+func (b *Backend) bindTargets(raw jsontext.Value) ([]string, error) {
if len(raw) == 0 {
return []string{}, nil
}
var refs []string
- if err := json.Unmarshal(raw, &refs); err != nil {
+ if err := json.Unmarshal(raw, &refs, jsonx.V1()); err != nil {
return nil, badRequest("Invalid value specified for an @odata.bind property.")
}
@@ -82,7 +85,7 @@ func (b *Backend) bindTargets(raw json.RawMessage) ([]string, error) {
}
// ownerBinds resolves owners@odata.bind; owners must be users or service principals.
-func (b *Backend) ownerBinds(body map[string]json.RawMessage) ([]string, error) {
+func (b *Backend) ownerBinds(body map[string]jsontext.Value) ([]string, error) {
ids, err := b.bindTargets(body["owners@odata.bind"])
if err != nil {
return nil, err
diff --git a/services/azuregraph/routes.go b/services/azuregraph/routes.go
index d03b1e07b5..7ad43b2a2b 100644
--- a/services/azuregraph/routes.go
+++ b/services/azuregraph/routes.go
@@ -1,7 +1,8 @@
package azuregraph
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"maps"
"net/http"
@@ -10,6 +11,8 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const maxGetByIDs = 1000
@@ -27,11 +30,11 @@ func odataBase(r *http.Request) string {
return "https://" + r.Host + "/" + version
}
-func readJSON(c *echo.Context) (map[string]json.RawMessage, error) {
+func readJSON(c *echo.Context) (map[string]jsontext.Value, error) {
data, err := io.ReadAll(http.MaxBytesReader(c.Response(), c.Request().Body, maxBodyBytes))
- out := map[string]json.RawMessage{}
+ out := map[string]jsontext.Value{}
- if err != nil || json.Unmarshal(data, &out) != nil || out == nil {
+ if err != nil || json.Unmarshal(data, &out, jsonx.V1()) != nil || out == nil {
return nil, badRequest(
"Unable to read JSON request payload. Please ensure Content-Type header is set and payload is of valid JSON format.",
)
@@ -314,7 +317,7 @@ func (h *Handler) getByIDs(c *echo.Context) error {
}
data, _ := io.ReadAll(http.MaxBytesReader(c.Response(), r.Body, maxBodyBytes))
- if err := json.Unmarshal(data, &in); err != nil || len(in.IDs) == 0 || len(in.IDs) > maxGetByIDs {
+ if err := json.Unmarshal(data, &in, jsonx.V1()); err != nil || len(in.IDs) == 0 || len(in.IDs) > maxGetByIDs {
return h.writeErr(
c,
badRequest("The 'ids' collection must contain between 1 and %d object identifiers.", maxGetByIDs),
diff --git a/services/azuregraph/routes_sub.go b/services/azuregraph/routes_sub.go
index 553cd9f49c..eb4f8fe26d 100644
--- a/services/azuregraph/routes_sub.go
+++ b/services/azuregraph/routes_sub.go
@@ -1,12 +1,15 @@
package azuregraph
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"slices"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func supports(kind string, allowed ...string) bool { return slices.Contains(allowed, kind) }
@@ -113,7 +116,7 @@ func (h *Handler) addPassword(c *echo.Context, id string) error {
var in PasswordCredential
if raw := body["passwordCredential"]; len(raw) > 0 && string(raw) != jsonNull {
- if err = json.Unmarshal(raw, &in); err != nil {
+ if err = json.Unmarshal(raw, &in, jsonx.V1()); err != nil {
return h.writeErr(c, badRequest("Invalid value specified for property 'passwordCredential'."))
}
}
@@ -143,7 +146,7 @@ func (h *Handler) addKey(c *echo.Context, id string) error {
}
var in KeyCredential
- if err = json.Unmarshal(body["keyCredential"], &in); err != nil {
+ if err = json.Unmarshal(body["keyCredential"], &in, jsonx.V1()); err != nil {
return h.writeErr(c, badRequest("A value is required for property 'keyCredential' of resource 'Application'."))
}
@@ -166,7 +169,7 @@ func (h *Handler) removeCred(c *echo.Context, id string, remove func(id, keyID s
}
var keyID string
- if json.Unmarshal(body["keyId"], &keyID) != nil || keyID == "" {
+ if json.Unmarshal(body["keyId"], &keyID, jsonx.V1()) != nil || keyID == "" {
return h.writeErr(c, badRequest("A value is required for property 'keyId'."))
}
@@ -190,9 +193,9 @@ func (h *Handler) relation(c *echo.Context, load func() ([]Object, error)) error
return h.list(c, "directoryObjects", "DirectoryObject", nil, typedObjects(objs))
}
-func odataRefTarget(body map[string]json.RawMessage) (string, bool) {
+func odataRefTarget(body map[string]jsontext.Value) (string, bool) {
var ref string
- if json.Unmarshal(body["@odata.id"], &ref) != nil || ref == "" {
+ if json.Unmarshal(body["@odata.id"], &ref, jsonx.V1()) != nil || ref == "" {
return "", false
}
diff --git a/services/azuregraph/sps.go b/services/azuregraph/sps.go
index 95a65d0d8e..ec14f0e1e6 100644
--- a/services/azuregraph/sps.go
+++ b/services/azuregraph/sps.go
@@ -1,7 +1,7 @@
package azuregraph
import (
- "encoding/json"
+ "encoding/json/jsontext"
"strings"
)
@@ -31,7 +31,7 @@ func (b *Backend) findSP(r Ref) (*ServicePrincipal, error) {
}
// CreateServicePrincipal instantiates an application in the tenant; body.appId must name a registered application.
-func (b *Backend) CreateServicePrincipal(body map[string]json.RawMessage) (ServicePrincipal, error) {
+func (b *Backend) CreateServicePrincipal(body map[string]jsontext.Value) (ServicePrincipal, error) {
b.mu.Lock("CreateServicePrincipal")
defer b.mu.Unlock()
@@ -111,7 +111,7 @@ func (b *Backend) ListServicePrincipals() []ServicePrincipal {
}
// UpdateServicePrincipal applies a PATCH body.
-func (b *Backend) UpdateServicePrincipal(r Ref, body map[string]json.RawMessage) error {
+func (b *Backend) UpdateServicePrincipal(r Ref, body map[string]jsontext.Value) error {
b.mu.Lock("UpdateServicePrincipal")
defer b.mu.Unlock()
diff --git a/services/azuregraph/users.go b/services/azuregraph/users.go
index a2b2bc4aae..b2f4328730 100644
--- a/services/azuregraph/users.go
+++ b/services/azuregraph/users.go
@@ -1,7 +1,7 @@
package azuregraph
import (
- "encoding/json"
+ "encoding/json/jsontext"
"strings"
"unicode"
)
@@ -80,7 +80,7 @@ func (b *Backend) validateUser(u *User, self string) error {
return nil
}
-func requireProps(kind string, body map[string]json.RawMessage, props ...string) error {
+func requireProps(kind string, body map[string]jsontext.Value, props ...string) error {
for _, p := range props {
if v, ok := body[p]; !ok || string(v) == jsonNull {
return badRequest("A value is required for property '%s' of resource '%s'.", p, kind)
@@ -91,7 +91,7 @@ func requireProps(kind string, body map[string]json.RawMessage, props ...string)
}
// CreateUser creates a user. passwordProfile is validated and discarded.
-func (b *Backend) CreateUser(body map[string]json.RawMessage) (User, error) {
+func (b *Backend) CreateUser(body map[string]jsontext.Value) (User, error) {
if err := requireProps(typeUser, body,
"accountEnabled", "displayName", "mailNickname", "passwordProfile", "userPrincipalName"); err != nil {
return User{}, err
@@ -147,7 +147,7 @@ func (b *Backend) ListUsers() []User {
}
// UpdateUser applies a PATCH body.
-func (b *Backend) UpdateUser(r Ref, body map[string]json.RawMessage) error {
+func (b *Backend) UpdateUser(r Ref, body map[string]jsontext.Value) error {
b.mu.Lock("UpdateUser")
defer b.mu.Unlock()
diff --git a/services/azurekeyvault/backup.go b/services/azurekeyvault/backup.go
index 92cc8a5f8a..9dabc0f03f 100644
--- a/services/azurekeyvault/backup.go
+++ b/services/azurekeyvault/backup.go
@@ -1,8 +1,10 @@
package azurekeyvault
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const backupPrefix = "gopherstack-keyvault-backup:"
@@ -25,7 +27,7 @@ type RestoreRequest struct {
}
func encodeBackup(bl *backupBlob) (BackupResult, error) {
- raw, err := json.Marshal(bl)
+ raw, err := json.Marshal(bl, jsonx.V1())
if err != nil {
return BackupResult{}, err
}
@@ -40,7 +42,7 @@ func decodeBackup(value, kind string) (*backupBlob, error) {
}
var bl backupBlob
- if err = json.Unmarshal(raw[len(backupPrefix):], &bl); err != nil || bl.Kind != kind {
+ if err = json.Unmarshal(raw[len(backupPrefix):], &bl, jsonx.V1()); err != nil || bl.Kind != kind {
return nil, errBadParameter("The backup blob is not a %s backup.", lowerKind(kind))
}
diff --git a/services/azurekeyvault/handler.go b/services/azurekeyvault/handler.go
index 4bd729ff1d..11055dc85a 100644
--- a/services/azurekeyvault/handler.go
+++ b/services/azurekeyvault/handler.go
@@ -3,7 +3,7 @@ package azurekeyvault
import (
"context"
"crypto/tls"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -18,6 +18,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
"github.com/blackbirdworks/gopherstack/pkgs/devtls"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -270,7 +271,7 @@ func decodeBody[T any](rc *reqCtx) (*T, error) {
return &in, nil
}
- if err := json.Unmarshal(rc.body, &in); err != nil {
+ if err := json.Unmarshal(rc.body, &in, jsonx.V1()); err != nil {
return nil, errBadParameter("The request body is not valid JSON: %s", err.Error())
}
diff --git a/services/azuremonitor/backend.go b/services/azuremonitor/backend.go
index a0d16cb026..a5a2a4bb59 100644
--- a/services/azuremonitor/backend.go
+++ b/services/azuremonitor/backend.go
@@ -3,13 +3,14 @@ package azuremonitor
import (
"crypto/rand"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"regexp"
"slices"
"strings"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
)
@@ -97,14 +98,14 @@ func cloneJSON(m map[string]any) map[string]any {
return nil
}
- raw, err := json.Marshal(m)
+ raw, err := json.Marshal(m, jsonx.V1())
if err != nil {
return nil
}
var out map[string]any
- _ = json.Unmarshal(raw, &out)
+ _ = json.Unmarshal(raw, &out, jsonx.V1())
return out
}
diff --git a/services/azuremonitor/handler.go b/services/azuremonitor/handler.go
index 394d49671e..e3c2b5a669 100644
--- a/services/azuremonitor/handler.go
+++ b/services/azuremonitor/handler.go
@@ -5,7 +5,8 @@ import (
"compress/gzip"
"context"
"crypto/tls"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -22,6 +23,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/aadauth"
"github.com/blackbirdworks/gopherstack/pkgs/devtls"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -347,11 +349,10 @@ func decodeContent(encoding string, data []byte) ([]byte, *apiError) {
}
func parseRecords(data []byte) ([]map[string]any, *apiError) {
- dec := json.NewDecoder(bytes.NewReader(data))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(data))
var records []map[string]any
- if err := dec.Decode(&records); err != nil {
+ if err := json.UnmarshalDecode(dec, &records, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, newErr(http.StatusBadRequest, "InvalidPayload",
"The request body must be a JSON array of objects: %v", err)
}
@@ -398,7 +399,7 @@ func parseQueryRequest(r *http.Request) (QueryRequest, bool, *apiError) {
return req, false, newErr(http.StatusBadRequest, "BadArgumentError", "The request body could not be read.")
}
- if err = json.Unmarshal(data, &req); err != nil {
+ if err = json.Unmarshal(data, &req, jsonx.V1()); err != nil {
return req, false, newErr(http.StatusBadRequest, "BadArgumentError",
"The request body is not valid JSON: %v", err)
}
@@ -440,7 +441,7 @@ func (h *Handler) handleBatch(c *echo.Context) error {
}
var req batchRequest
- if err = json.Unmarshal(data, &req); err != nil || len(req.Requests) == 0 {
+ if err = json.Unmarshal(data, &req, jsonx.V1()); err != nil || len(req.Requests) == 0 {
return writeAPIError(c, newErr(http.StatusBadRequest, "BadArgumentError",
"The batch must contain a non-empty 'requests' array."))
}
diff --git a/services/azuremonitor/ingest.go b/services/azuremonitor/ingest.go
index 44ba843586..66fe9b78c1 100644
--- a/services/azuremonitor/ingest.go
+++ b/services/azuremonitor/ingest.go
@@ -1,7 +1,6 @@
package azuremonitor
import (
- "encoding/json"
"errors"
"fmt"
"math"
@@ -9,6 +8,7 @@ import (
"slices"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azuremonitor/kql"
)
@@ -69,7 +69,7 @@ func convertDatetime(v any) (any, bool) {
func numberToInt(v any) (int64, bool) {
switch x := v.(type) {
- case json.Number:
+ case jsonx.Number:
n, err := x.Int64()
return n, err == nil
@@ -86,7 +86,7 @@ func numberToInt(v any) (int64, bool) {
func numberToFloat(v any) (float64, bool) {
switch x := v.(type) {
- case json.Number:
+ case jsonx.Number:
f, err := x.Float64()
return f, err == nil
@@ -103,7 +103,7 @@ func normalizeDynamic(v any) any {
switch x := v.(type) {
case float64:
return kql.NormalizeJSON(x)
- case json.Number:
+ case jsonx.Number:
if n, err := x.Int64(); err == nil {
return n
}
diff --git a/services/azuremonitor/kql/funcs.go b/services/azuremonitor/kql/funcs.go
index 02edf9a6a3..ac26cf2176 100644
--- a/services/azuremonitor/kql/funcs.go
+++ b/services/azuremonitor/kql/funcs.go
@@ -2,7 +2,8 @@ package kql
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"math"
"net/url"
@@ -11,6 +12,8 @@ import (
"strings"
"time"
"unicode/utf8"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -45,10 +48,9 @@ func sameAsFirst(a []Type) (Type, error) { return a[0], nil }
func parseJSONValue(s string) (any, error) {
var v any
- dec := json.NewDecoder(strings.NewReader(s))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(strings.NewReader(s))
- if err := dec.Decode(&v); err != nil {
+ if err := json.UnmarshalDecode(dec, &v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, err
}
diff --git a/services/azuremonitor/kql/golden_test.go b/services/azuremonitor/kql/golden_test.go
index 532e10044b..f6a7fe4dc9 100644
--- a/services/azuremonitor/kql/golden_test.go
+++ b/services/azuremonitor/kql/golden_test.go
@@ -1,7 +1,7 @@
package kql_test
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azuremonitor/kql"
)
@@ -78,7 +79,7 @@ func render(rel *kql.Rel) (string, string) {
}
}
- raw, _ := json.Marshal(rows)
+ raw, _ := json.Marshal(rows, jsonx.V1())
return cols, string(raw)
}
diff --git a/services/azuremonitor/kql/parser.go b/services/azuremonitor/kql/parser.go
index 15a6d826b1..5d31f2aea4 100644
--- a/services/azuremonitor/kql/parser.go
+++ b/services/azuremonitor/kql/parser.go
@@ -1,11 +1,13 @@
package kql
import (
- "encoding/json"
+ "encoding/json/v2"
"math"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type parser struct {
@@ -1385,9 +1387,9 @@ func dynamicLiteral(raw string) (node, error) {
var v any
- if err := json.Unmarshal([]byte(raw), &v); err != nil {
+ if err := json.Unmarshal([]byte(raw), &v, jsonx.V1()); err != nil {
alt := strings.ReplaceAll(raw, "'", `"`)
- if err2 := json.Unmarshal([]byte(alt), &v); err2 != nil {
+ if err2 := json.Unmarshal([]byte(alt), &v, jsonx.V1()); err2 != nil {
return nil, syntaxErr("invalid dynamic literal")
}
}
diff --git a/services/azuremonitor/kql/value.go b/services/azuremonitor/kql/value.go
index e7d13fe3c7..59d914d551 100644
--- a/services/azuremonitor/kql/value.go
+++ b/services/azuremonitor/kql/value.go
@@ -3,11 +3,13 @@
package kql
import (
- "encoding/json"
+ "encoding/json/v2"
"math"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Type is a KQL column type name as it appears in query results.
@@ -183,7 +185,7 @@ func groupKey(v any) string {
return "d" + strconv.FormatInt(int64(x), 10)
}
- raw, _ := json.Marshal(v)
+ raw, _ := json.Marshal(v, jsonx.V1())
return "j" + string(raw)
}
@@ -273,7 +275,7 @@ func formatReal(f float64) string {
}
func jsonString(v any) string {
- raw, err := json.Marshal(jsonable(v))
+ raw, err := json.Marshal(jsonable(v), jsonx.V1())
if err != nil {
return ""
}
@@ -342,7 +344,7 @@ func NormalizeJSON(v any) any {
}
return x
- case json.Number:
+ case jsonx.Number:
if i, err := x.Int64(); err == nil {
return i
}
diff --git a/services/azuremonitor/sdk_test.go b/services/azuremonitor/sdk_test.go
index e5c06fce97..5971a914c5 100644
--- a/services/azuremonitor/sdk_test.go
+++ b/services/azuremonitor/sdk_test.go
@@ -3,7 +3,7 @@ package azuremonitor_test
import (
"bytes"
"compress/gzip"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azuremonitor"
)
@@ -111,7 +112,7 @@ func TestIngestThenQuery(t *testing.T) {
assert.Equal(t, tt.wantCols, cols)
- raw, err := json.Marshal(res.Tables[0].Rows)
+ raw, err := json.Marshal(res.Tables[0].Rows, jsonx.V1())
require.NoError(t, err)
assert.JSONEq(t, tt.wantRows, string(raw))
})
@@ -461,7 +462,7 @@ func TestSnapshotRestoreKeepsRows(t *testing.T) {
require.NoError(t, e2.handler.Restore(t.Context(), snap))
res := e2.run(t, s, "AppEvents_CL | order by Count asc | project Count, Value, Ok, Extra")
- raw, err := json.Marshal(res.Tables[0].Rows)
+ raw, err := json.Marshal(res.Tables[0].Rows, jsonx.V1())
require.NoError(t, err)
assert.JSONEq(t, `[[1,1.5,true,"{\"a\":1}"],[2,4,false,"[1,2]"],[3,2.5,true,null]]`, string(raw))
assert.Equal(t, queryazlogs.ColumnTypeLong, *res.Tables[0].Columns[0].Type)
diff --git a/services/azureservicebus/handler.go b/services/azureservicebus/handler.go
index ff633d251d..dfdcc682e6 100644
--- a/services/azureservicebus/handler.go
+++ b/services/azureservicebus/handler.go
@@ -4,7 +4,7 @@ import (
"context"
"crypto/rand"
"crypto/tls"
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"errors"
"fmt"
@@ -17,6 +17,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -849,7 +850,7 @@ func brokerPropertiesFromHeader(r *http.Request) brokerProperties {
var bp brokerProperties
- _ = json.Unmarshal([]byte(raw), &bp)
+ _ = json.Unmarshal([]byte(raw), &bp, jsonx.V1())
return bp
}
@@ -979,7 +980,7 @@ func (h *Handler) writeLockedMessage(c *echo.Context, req parsedRequest, info Me
bp.LockToken = info.LockToken
bp.LockedUntilUtc = info.LockedUntil.UTC().Format(http.TimeFormat)
- bpJSON, err := json.Marshal(bp)
+ bpJSON, err := json.Marshal(bp, jsonx.V1())
if err != nil {
return h.writeError(c, http.StatusInternalServerError, "InternalError", "Failed to marshal BrokerProperties.")
}
@@ -1199,7 +1200,7 @@ func (h *Handler) writeBodyReadError(c *echo.Context, err error) error {
}
func (h *Handler) writeError(c *echo.Context, status int, code, message string) error {
- body, err := json.Marshal(azureError{Code: code, Message: message})
+ body, err := json.Marshal(azureError{Code: code, Message: message}, jsonx.V1())
if err != nil {
return c.String(http.StatusInternalServerError, "internal error")
}
diff --git a/services/azureservicebus/handler_test.go b/services/azureservicebus/handler_test.go
index ba8728e9ef..97c3fa1e57 100644
--- a/services/azureservicebus/handler_test.go
+++ b/services/azureservicebus/handler_test.go
@@ -2,7 +2,7 @@ package azureservicebus_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azureservicebus"
)
@@ -146,7 +147,7 @@ func TestHandler_MessageLifecycle_Queue(t *testing.T) {
var bp map[string]any
- require.NoError(t, json.Unmarshal([]byte(bpHeader), &bp))
+ require.NoError(t, json.Unmarshal([]byte(bpHeader), &bp, jsonx.V1()))
messageID, _ := bp["MessageId"].(string)
lockToken, _ := bp["LockToken"].(string)
require.NotEmpty(t, messageID)
diff --git a/services/azureservicebus/receive_handlers.go b/services/azureservicebus/receive_handlers.go
index 894cb4d081..2435d7731c 100644
--- a/services/azureservicebus/receive_handlers.go
+++ b/services/azureservicebus/receive_handlers.go
@@ -1,7 +1,7 @@
package azureservicebus
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/textproto"
@@ -9,6 +9,8 @@ import (
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Auto dead-letter reasons real Service Bus stamps on system dead-lettering.
@@ -124,7 +126,7 @@ func (h *Handler) handleReceiveAndDelete(c *echo.Context, req parsedRequest) err
}
func (h *Handler) writeReceivedMessage(c *echo.Context, info MessageInfo) error {
- bpJSON, err := json.Marshal(brokerPropertiesFor(info))
+ bpJSON, err := json.Marshal(brokerPropertiesFor(info), jsonx.V1())
if err != nil {
return h.writeError(c, http.StatusInternalServerError, "InternalError", "Failed to marshal BrokerProperties.")
}
diff --git a/services/azureservicebus/receive_test.go b/services/azureservicebus/receive_test.go
index 3a46b51012..07c3701339 100644
--- a/services/azureservicebus/receive_test.go
+++ b/services/azureservicebus/receive_test.go
@@ -1,7 +1,7 @@
package azureservicebus_test
import (
- "encoding/json"
+ "encoding/json/v2"
"math"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azureservicebus"
)
@@ -42,7 +43,7 @@ func peek(t *testing.T, h *azureservicebus.Handler, path string) []peekResp {
require.Equal(t, http.StatusOK, rec.Code)
var out []peekResp
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/azuretable/batch.go b/services/azuretable/batch.go
index 0a9dd6b331..02988f5f0f 100644
--- a/services/azuretable/batch.go
+++ b/services/azuretable/batch.go
@@ -1,13 +1,14 @@
package azuretable
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"time"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/odatatable"
)
@@ -102,7 +103,7 @@ func (h *Handler) batchSuccessPart(
level := odataLevelFromAccept(o.Headers.Get("Accept"))
- payload, err := json.Marshal(h.encodeEntity(info, o.Table, level, ""))
+ payload, err := json.Marshal(h.encodeEntity(info, o.Table, level, ""), jsonx.V1())
if err != nil {
return part, err
}
diff --git a/services/azuretable/entity_ops_test.go b/services/azuretable/entity_ops_test.go
index 1d8fcc5005..b04a6dc106 100644
--- a/services/azuretable/entity_ops_test.go
+++ b/services/azuretable/entity_ops_test.go
@@ -2,7 +2,7 @@ package azuretable_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azuretable"
)
@@ -31,7 +32,7 @@ func TestInsertEntity(t *testing.T) {
assert.NotEmpty(t, rec.Header().Get("ETag"))
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "p", got["PartitionKey"])
assert.Equal(t, "r", got["RowKey"])
assert.Equal(t, "hi", got["Name"])
@@ -173,7 +174,7 @@ func TestEntity_EDMTypeRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, tt.wantValue, got["Prop"], tt.name)
if tt.annPresent {
@@ -221,7 +222,7 @@ func TestGetEntity(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "x", got["A"])
assert.NotContains(t, got, "B")
assert.Contains(t, got, "PartitionKey")
@@ -255,7 +256,7 @@ func TestQueryEntities(t *testing.T) {
var body struct {
Value []map[string]any `json:"value"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
require.Len(t, body.Value, 2)
assert.Equal(t, "p1", body.Value[0]["PartitionKey"])
assert.Equal(t, "p2", body.Value[1]["PartitionKey"])
@@ -272,7 +273,7 @@ func TestQueryEntities(t *testing.T) {
var body struct {
Value []map[string]any `json:"value"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
require.Len(t, body.Value, 1)
assert.Equal(t, "p2", body.Value[0]["PartitionKey"])
})
@@ -297,7 +298,7 @@ func TestQueryEntities(t *testing.T) {
var body struct {
Value []map[string]any `json:"value"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body.Value, 1)
})
@@ -385,7 +386,7 @@ func TestReplaceEntity(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/"+testAccount+"/mytable(PartitionKey='p',RowKey='r')", nil)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "z", got["A"])
assert.NotContains(t, got, "B")
})
@@ -412,7 +413,7 @@ func TestMergeEntity(t *testing.T) {
rec2 := doRequest(t, h, http.MethodGet, "/"+testAccount+"/mytable(PartitionKey='p',RowKey='r')", nil)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "z", got["A"])
assert.Equal(t, "y", got["B"])
})
@@ -469,7 +470,7 @@ func TestMergeEntity(t *testing.T) {
// Merge semantics, not replace: B must survive.
rec2 := doRequest(t, h, http.MethodGet, "/"+testAccount+"/mytable(PartitionKey='p',RowKey='r')", nil)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "z", got["A"])
assert.Equal(t, "y", got["B"])
@@ -496,7 +497,7 @@ func TestMergeEntity(t *testing.T) {
rec2 := doRequest(t, h, http.MethodGet, "/"+testAccount+"/mytable(PartitionKey='p',RowKey='r')", nil)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "z", got["A"])
assert.Equal(t, "y", got["B"], "merge semantics must apply, not PUT's own replace semantics")
})
diff --git a/services/azuretable/handler.go b/services/azuretable/handler.go
index ecaa01c3ee..1ebca943db 100644
--- a/services/azuretable/handler.go
+++ b/services/azuretable/handler.go
@@ -3,7 +3,7 @@ package azuretable
import (
"context"
"crypto/rand"
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"errors"
"fmt"
@@ -15,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/azureauth"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/odatatable"
@@ -626,7 +627,7 @@ func odataLevelFromAccept(accept string) string {
func (h *Handler) writeJSON(c *echo.Context, status int, v any) error {
level := odataLevelFromAccept(c.Request().Header.Get("Accept"))
- body, err := json.Marshal(v)
+ body, err := json.Marshal(v, jsonx.V1())
if err != nil {
return h.writeErrorNoRecurse(c, http.StatusInternalServerError, "InternalError", "Failed to marshal response.")
}
@@ -670,7 +671,7 @@ func (h *Handler) writeErrorNoRecurse(c *echo.Context, status int, code, message
c.Response().Header().Set("X-Ms-Error-Code", code)
body, _ := json.Marshal(odataErrorEnvelope{
Error: odataErrorDetail{Code: code, Message: odataErrorMessage{Lang: "en-US", Value: message}},
- })
+ }, jsonx.V1())
return c.Blob(status, "application/json;odata=minimalmetadata;streaming=true;charset=utf-8", body)
}
diff --git a/services/azuretable/models_test.go b/services/azuretable/models_test.go
index 4e89b9fdca..fc1c55cdde 100644
--- a/services/azuretable/models_test.go
+++ b/services/azuretable/models_test.go
@@ -1,7 +1,7 @@
package azuretable_test
import (
- "encoding/json"
+ "encoding/json/v2"
"math"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azuretable"
)
@@ -60,11 +61,11 @@ func TestEntityProperty_JSONRoundTrip(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- data, err := json.Marshal(tt.prop)
+ data, err := json.Marshal(tt.prop, jsonx.V1())
require.NoError(t, err, tt.name)
var got azuretable.EntityProperty
- require.NoError(t, json.Unmarshal(data, &got), tt.name)
+ require.NoError(t, json.Unmarshal(data, &got, jsonx.V1()), tt.name)
assert.Equal(t, tt.prop.Type, got.Type, tt.name)
@@ -113,7 +114,7 @@ func TestEntityProperty_MarshalJSON_TypeMismatchErrors(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- _, err := json.Marshal(tt.prop)
+ _, err := json.Marshal(tt.prop, jsonx.V1())
require.Error(t, err, tt.name)
})
}
@@ -147,7 +148,7 @@ func TestEntityProperty_UnmarshalJSON_MalformedValueErrors(t *testing.T) {
t.Parallel()
var p azuretable.EntityProperty
- err := json.Unmarshal([]byte(tt.json), &p)
+ err := json.Unmarshal([]byte(tt.json), &p, jsonx.V1())
require.Error(t, err, tt.name)
})
}
diff --git a/services/azuretable/sdk_wire_test.go b/services/azuretable/sdk_wire_test.go
index a8e2a8dc77..dae611b2ca 100644
--- a/services/azuretable/sdk_wire_test.go
+++ b/services/azuretable/sdk_wire_test.go
@@ -1,7 +1,7 @@
package azuretable_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"net/http"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/azuretable"
)
@@ -42,7 +43,7 @@ func sdkEntity(t *testing.T, pk, rk string, extra map[string]any) []byte {
m := map[string]any{"PartitionKey": pk, "RowKey": rk}
maps.Copy(m, extra)
- b, err := json.Marshal(m)
+ b, err := json.Marshal(m, jsonx.V1())
require.NoError(t, err)
return b
@@ -224,7 +225,7 @@ func TestEdmDoubleRoundTripSDK(t *testing.T) {
PartitionKey: "p", RowKey: "r",
Properties: map[string]any{"D": 5.0},
}
- raw, err := json.Marshal(ent)
+ raw, err := json.Marshal(ent, jsonx.V1())
require.NoError(t, err)
_, err = client.AddEntity(ctx, raw, nil)
require.NoError(t, err)
@@ -233,7 +234,7 @@ func TestEdmDoubleRoundTripSDK(t *testing.T) {
require.NoError(t, err)
var back aztables.EDMEntity
- require.NoError(t, json.Unmarshal(got.Value, &back))
+ require.NoError(t, json.Unmarshal(got.Value, &back, jsonx.V1()))
assert.InDelta(t, 5.0, back.Properties["D"], 0)
}
diff --git a/services/azuretable/table_ops.go b/services/azuretable/table_ops.go
index 59a6074641..c9e930d37d 100644
--- a/services/azuretable/table_ops.go
+++ b/services/azuretable/table_ops.go
@@ -1,13 +1,14 @@
package azuretable
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/odatatable"
)
@@ -32,7 +33,7 @@ func (h *Handler) createTable(c *echo.Context) error {
}
var req createTableBody
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInput", "The input is not valid.")
}
diff --git a/services/azuretable/table_ops_test.go b/services/azuretable/table_ops_test.go
index 283f1b6464..0a97e5c938 100644
--- a/services/azuretable/table_ops_test.go
+++ b/services/azuretable/table_ops_test.go
@@ -1,7 +1,7 @@
package azuretable_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCreateTable(t *testing.T) {
@@ -24,7 +26,7 @@ func TestCreateTable(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "mytable", body["TableName"])
assert.Contains(t, body, "odata.metadata")
})
@@ -94,7 +96,7 @@ func TestListTables(t *testing.T) {
var body struct {
Value []map[string]any `json:"value"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
require.Len(t, body.Value, 2)
assert.Equal(t, "tablea", body.Value[0]["TableName"])
assert.Equal(t, "tableb", body.Value[1]["TableName"])
@@ -117,7 +119,7 @@ func TestListTables_NoMetadataLevel(t *testing.T) {
var body struct {
Value []map[string]any `json:"value"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
require.Len(t, body.Value, 1)
assert.NotContains(t, body.Value[0], "odata.metadata")
assert.Contains(t, rec.Header().Get("Content-Type"), "odata=nometadata")
@@ -140,7 +142,7 @@ func TestListTables_FullMetadataLevel(t *testing.T) {
var body struct {
Value []map[string]any `json:"value"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
require.Len(t, body.Value, 1)
assert.Contains(t, body.Value[0], "odata.type")
assert.Contains(t, body.Value[0], "odata.id")
@@ -200,7 +202,7 @@ func TestGetTable(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "m8table", body["TableName"])
})
diff --git a/services/backup/async_jobs.go b/services/backup/async_jobs.go
index 0022da64d2..371e3ae2e2 100644
--- a/services/backup/async_jobs.go
+++ b/services/backup/async_jobs.go
@@ -2,7 +2,7 @@ package backup
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"strings"
"time"
@@ -97,7 +97,7 @@ func (b *InMemoryBackend) restoreResourceLocked(job *RestoreJob) (string, error)
}
// captureSnapshotLocked snapshots the backed-up resource when an engine is wired for its type.
-func (b *InMemoryBackend) captureSnapshotLocked(job *Job) (json.RawMessage, error) {
+func (b *InMemoryBackend) captureSnapshotLocked(job *Job) (jsontext.Value, error) {
engine := b.engines[job.ResourceType]
if engine == nil {
return nil, nil
diff --git a/services/backup/backup_plans.go b/services/backup/backup_plans.go
index aa9013be8d..745a3d63b3 100644
--- a/services/backup/backup_plans.go
+++ b/services/backup/backup_plans.go
@@ -1,7 +1,7 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -211,7 +212,7 @@ func (b *InMemoryBackend) ExportBackupPlanTemplate(planID string) (string, error
doc.AdvancedBackupSettings = advancedSettingsToJSON(plan.AdvancedBackupSettings)
}
- out, err := json.Marshal(doc)
+ out, err := json.Marshal(doc, jsonx.V1())
if err != nil {
return "", err
}
diff --git a/services/backup/copy_jobs.go b/services/backup/copy_jobs.go
index 434401061b..598704cf85 100644
--- a/services/backup/copy_jobs.go
+++ b/services/backup/copy_jobs.go
@@ -1,7 +1,7 @@
package backup
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"slices"
"strings"
@@ -192,7 +192,7 @@ func (b *InMemoryBackend) settleCopyJobLocked(job *CopyJob, now time.Time) {
return
}
- var snapshot json.RawMessage
+ var snapshot jsontext.Value
if srcName, ok := b.vaultARNIndex[job.SourceBackupVaultArn]; ok {
if srcRP, rpFound := b.recoveryPoints.Get(recoveryPointKey(srcName, job.SourceRecoveryPointArn)); rpFound {
snapshot = srcRP.Snapshot
diff --git a/services/backup/ddb_item_codec.go b/services/backup/ddb_item_codec.go
index 88efbdd7ee..cfd6bda358 100644
--- a/services/backup/ddb_item_codec.go
+++ b/services/backup/ddb_item_codec.go
@@ -2,20 +2,23 @@ package backup
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
ddbtypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
-func encodeDDBItem(item map[string]ddbtypes.AttributeValue) (map[string]json.RawMessage, error) {
- out := make(map[string]json.RawMessage, len(item))
+func encodeDDBItem(item map[string]ddbtypes.AttributeValue) (map[string]jsontext.Value, error) {
+ out := make(map[string]jsontext.Value, len(item))
for k, v := range item {
w, err := ddbAttrToWire(v)
if err != nil {
return nil, err
}
- raw, err := json.Marshal(w)
+ raw, err := json.Marshal(w, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("encode attribute %s: %w", k, err)
}
@@ -25,11 +28,11 @@ func encodeDDBItem(item map[string]ddbtypes.AttributeValue) (map[string]json.Raw
return out, nil
}
-func decodeDDBItem(raw map[string]json.RawMessage) (map[string]ddbtypes.AttributeValue, error) {
+func decodeDDBItem(raw map[string]jsontext.Value) (map[string]ddbtypes.AttributeValue, error) {
out := make(map[string]ddbtypes.AttributeValue, len(raw))
for k, r := range raw {
var w map[string]any
- if err := json.Unmarshal(r, &w); err != nil {
+ if err := json.Unmarshal(r, &w, jsonx.V1()); err != nil {
return nil, fmt.Errorf("decode attribute %s: %w", k, err)
}
av, err := ddbWireToAttr(w)
diff --git a/services/backup/handler_backup_access_points.go b/services/backup/handler_backup_access_points.go
index f530564315..c823829225 100644
--- a/services/backup/handler_backup_access_points.go
+++ b/services/backup/handler_backup_access_points.go
@@ -1,10 +1,12 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createBackupAccessPointBody struct {
@@ -18,7 +20,7 @@ type createBackupAccessPointBody struct {
func (h *Handler) handleCreateBackupAccessPoint(c *echo.Context, body []byte) error {
var in createBackupAccessPointBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid request body"),
diff --git a/services/backup/handler_backup_jobs.go b/services/backup/handler_backup_jobs.go
index 99a76d29ad..e20b9e21b2 100644
--- a/services/backup/handler_backup_jobs.go
+++ b/services/backup/handler_backup_jobs.go
@@ -1,11 +1,13 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type startBackupJobBody struct {
@@ -22,7 +24,7 @@ type startBackupJobBody struct {
func (h *Handler) handleStartBackupJob(c *echo.Context, body []byte) error {
var in startBackupJobBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterValueException", "invalid request body"))
}
diff --git a/services/backup/handler_backup_jobs_test.go b/services/backup/handler_backup_jobs_test.go
index 3e4cd86d2d..0fc3189eae 100644
--- a/services/backup/handler_backup_jobs_test.go
+++ b/services/backup/handler_backup_jobs_test.go
@@ -1,13 +1,14 @@
package backup_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -51,14 +52,14 @@ func TestDescribeBackupJobProgressFields(t *testing.T) {
require.Equal(t, http.StatusOK, startResp.Code)
var startData map[string]any
- require.NoError(t, json.Unmarshal(startResp.Body.Bytes(), &startData))
+ require.NoError(t, json.Unmarshal(startResp.Body.Bytes(), &startData, jsonx.V1()))
jobID := startData["BackupJobId"].(string)
descResp := doRequest(t, h, http.MethodGet, "/backup-jobs/"+jobID, "")
require.Equal(t, http.StatusOK, descResp.Code)
var descData map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descData))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descData, jsonx.V1()))
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
diff --git a/services/backup/handler_backup_plans.go b/services/backup/handler_backup_plans.go
index deb1ec9700..3177a22427 100644
--- a/services/backup/handler_backup_plans.go
+++ b/services/backup/handler_backup_plans.go
@@ -1,12 +1,14 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createBackupPlanBody struct {
@@ -16,7 +18,7 @@ type createBackupPlanBody struct {
func (h *Handler) handleCreateBackupPlan(c *echo.Context, body []byte) error {
var in createBackupPlanBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterValueException", "invalid request body"))
}
@@ -146,7 +148,7 @@ type updateBackupPlanBody struct {
func (h *Handler) handleUpdateBackupPlan(c *echo.Context, id string, body []byte) error {
var in updateBackupPlanBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterValueException", "invalid request body"))
}
@@ -305,7 +307,7 @@ func (h *Handler) dispatchPlanTemplateCatalogOps(
var reqBody struct {
BackupPlanTemplateJSON string `json:"BackupPlanTemplateJson"`
}
- if err := json.Unmarshal(body, &reqBody); err != nil {
+ if err := json.Unmarshal(body, &reqBody, jsonx.V1()); err != nil {
return true, c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid request body"),
@@ -313,7 +315,7 @@ func (h *Handler) dispatchPlanTemplateCatalogOps(
}
var doc backupPlanBodyDoc
- if err := json.Unmarshal([]byte(reqBody.BackupPlanTemplateJSON), &doc); err != nil {
+ if err := json.Unmarshal([]byte(reqBody.BackupPlanTemplateJSON), &doc, jsonx.V1()); err != nil {
return true, c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid BackupPlanTemplateJson"),
diff --git a/services/backup/handler_backup_plans_test.go b/services/backup/handler_backup_plans_test.go
index b9253064df..81c79c52a1 100644
--- a/services/backup/handler_backup_plans_test.go
+++ b/services/backup/handler_backup_plans_test.go
@@ -1,13 +1,14 @@
package backup_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -33,7 +34,7 @@ func TestUpdateBackupPlanUpdateDate(t *testing.T) {
}`)
require.Equal(t, http.StatusOK, createResp.Code)
var createData map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createData))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createData, jsonx.V1()))
planID := createData["BackupPlanId"].(string)
updateResp := doRequest(t, h, http.MethodPost, "/backup/plans/"+planID, `{
@@ -45,7 +46,7 @@ func TestUpdateBackupPlanUpdateDate(t *testing.T) {
require.Equal(t, http.StatusOK, updateResp.Code)
var updateData map[string]any
- require.NoError(t, json.Unmarshal(updateResp.Body.Bytes(), &updateData))
+ require.NoError(t, json.Unmarshal(updateResp.Body.Bytes(), &updateData, jsonx.V1()))
assert.NotContains(t, updateData, "UpdateDate",
"UpdateBackupPlanOutput has no UpdateDate member")
@@ -105,7 +106,7 @@ func TestCreateBackupPlanAdvancedBackupSettings(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var data map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &data))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &data, jsonx.V1()))
if tc.wantInResponse {
settings, ok := data["AdvancedBackupSettings"].([]any)
@@ -255,14 +256,14 @@ func TestRuleLifecycle(t *testing.T) {
"BackupPlanName": "test-plan",
"Rules": tt.rules,
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
resp := doRequest(t, h, http.MethodPut, "/backup/plans", string(body))
require.Equal(t, http.StatusOK, resp.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &createResp, jsonx.V1()))
planID := createResp["BackupPlanId"].(string)
// Retrieve and verify.
@@ -271,7 +272,7 @@ func TestRuleLifecycle(t *testing.T) {
_ = b
var getPlan map[string]any
- require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &getPlan))
+ require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &getPlan, jsonx.V1()))
backupPlan := getPlan["BackupPlan"].(map[string]any)
rules := backupPlan["Rules"].([]any)
tt.wantInRules(t, rules)
@@ -335,21 +336,21 @@ func TestAdvancedBackupSettings(t *testing.T) {
planBody["BackupPlan"].(map[string]any)["AdvancedBackupSettings"] = tt.advancedSettings
}
- body, err := json.Marshal(planBody)
+ body, err := json.Marshal(planBody, jsonx.V1())
require.NoError(t, err)
resp := doRequest(t, h, http.MethodPut, "/backup/plans", string(body))
require.Equal(t, http.StatusOK, resp.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &createResp, jsonx.V1()))
planID := createResp["BackupPlanId"].(string)
getResp := doRequest(t, h, http.MethodGet, "/backup/plans/"+planID, "")
require.Equal(t, http.StatusOK, getResp.Code)
var getPlan map[string]any
- require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &getPlan))
+ require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &getPlan, jsonx.V1()))
backupPlan := getPlan["BackupPlan"].(map[string]any)
if tt.advancedSettings != nil {
diff --git a/services/backup/handler_copy_jobs.go b/services/backup/handler_copy_jobs.go
index d27fb51889..8ac655223d 100644
--- a/services/backup/handler_copy_jobs.go
+++ b/services/backup/handler_copy_jobs.go
@@ -1,10 +1,12 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleListCopyJobs(c *echo.Context) error {
@@ -100,7 +102,7 @@ func (h *Handler) dispatchCopyJobExtraOps(c *echo.Context, route backupRoute, bo
DestinationBackupVaultArn string `json:"DestinationBackupVaultArn"`
IamRoleArn string `json:"IamRoleArn"`
}
- if err := json.Unmarshal(body, ©JobReq); err != nil {
+ if err := json.Unmarshal(body, ©JobReq, jsonx.V1()); err != nil {
return true, c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid request body"),
diff --git a/services/backup/handler_copy_jobs_test.go b/services/backup/handler_copy_jobs_test.go
index 49e8367626..e3ee7958c8 100644
--- a/services/backup/handler_copy_jobs_test.go
+++ b/services/backup/handler_copy_jobs_test.go
@@ -1,13 +1,14 @@
package backup_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -35,7 +36,7 @@ func TestStartCopyJobCreationDateEpoch(t *testing.T) {
}`)
require.Equal(t, http.StatusOK, resp.Code)
var startData map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &startData))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &startData, jsonx.V1()))
jobID := startData["BackupJobId"].(string)
rpArn := "arn:aws:backup:us-east-1:000000000000:recovery-point:rp-copy-1"
@@ -49,7 +50,7 @@ func TestStartCopyJobCreationDateEpoch(t *testing.T) {
_ = jobID
var copyData map[string]any
- require.NoError(t, json.Unmarshal(copyResp.Body.Bytes(), ©Data))
+ require.NoError(t, json.Unmarshal(copyResp.Body.Bytes(), ©Data, jsonx.V1()))
assert.NotEmpty(t, copyData["CopyJobId"], "CopyJobId must be present")
creationDate, exists := copyData["CreationDate"]
diff --git a/services/backup/handler_dispatch.go b/services/backup/handler_dispatch.go
index 68b3b18eec..88fd9f0295 100644
--- a/services/backup/handler_dispatch.go
+++ b/services/backup/handler_dispatch.go
@@ -2,7 +2,8 @@ package backup
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"regexp"
@@ -14,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -184,9 +186,9 @@ func (h *Handler) Handler() echo.HandlerFunc {
var body []byte
if c.Request().Body != nil {
- decoder := json.NewDecoder(c.Request().Body)
- var raw json.RawMessage
- if err := decoder.Decode(&raw); err == nil {
+ decoder := jsontext.NewDecoder(c.Request().Body)
+ var raw jsontext.Value
+ if err := json.UnmarshalDecode(decoder, &raw, jsonx.V1()); err == nil {
body = raw
}
}
diff --git a/services/backup/handler_frameworks.go b/services/backup/handler_frameworks.go
index 8f0ed1a884..8e2d996d77 100644
--- a/services/backup/handler_frameworks.go
+++ b/services/backup/handler_frameworks.go
@@ -1,10 +1,12 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// controlInputParameterJSON is ControlInputParameter's wire shape: a single
@@ -98,7 +100,7 @@ type createFrameworkBody struct {
func (h *Handler) handleCreateFramework(c *echo.Context, body []byte) error {
var in createFrameworkBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterValueException", "invalid request body"))
}
@@ -193,7 +195,7 @@ func (h *Handler) handleUpdateFramework(c *echo.Context, name string, body []byt
var in updateFrameworkBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid request body"),
diff --git a/services/backup/handler_frameworks_test.go b/services/backup/handler_frameworks_test.go
index e6c251356c..2f2d567dad 100644
--- a/services/backup/handler_frameworks_test.go
+++ b/services/backup/handler_frameworks_test.go
@@ -1,13 +1,14 @@
package backup_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -70,7 +71,7 @@ func TestFrameworkControls(t *testing.T) {
require.Equal(t, tt.wantStatus, resp.Code)
var createData map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &createData))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &createData, jsonx.V1()))
fwName := createData["FrameworkName"].(string)
// Describe and verify controls.
@@ -78,7 +79,7 @@ func TestFrameworkControls(t *testing.T) {
require.Equal(t, http.StatusOK, getResp.Code)
var fwData map[string]any
- require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &fwData))
+ require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &fwData, jsonx.V1()))
assert.Equal(t, tt.wantStatus2, fwData["FrameworkStatus"])
assert.Equal(t, "COMPLETED", fwData["DeploymentStatus"])
if tt.wantControlLen > 0 {
diff --git a/services/backup/handler_legal_holds.go b/services/backup/handler_legal_holds.go
index 0622fddc5d..9d370a66dd 100644
--- a/services/backup/handler_legal_holds.go
+++ b/services/backup/handler_legal_holds.go
@@ -1,11 +1,13 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCancelLegalHold(c *echo.Context, legalHoldID string) error {
@@ -70,7 +72,7 @@ type createLegalHoldBody struct {
func (h *Handler) handleCreateLegalHold(c *echo.Context, body []byte) error {
var in createLegalHoldBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterValueException", "invalid request body"))
}
diff --git a/services/backup/handler_recovery_points.go b/services/backup/handler_recovery_points.go
index ef7d8fef80..82a839df16 100644
--- a/services/backup/handler_recovery_points.go
+++ b/services/backup/handler_recovery_points.go
@@ -1,12 +1,14 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// splitVaultRP splits a "vaultName|recoveryPointArn" resource string.
@@ -362,7 +364,7 @@ func (h *Handler) handleUpdateRecoveryPointIndexSettings(
var reqBody struct {
Index string `json:"Index"`
}
- _ = json.Unmarshal(body, &reqBody)
+ _ = json.Unmarshal(body, &reqBody, jsonx.V1())
if err := h.Backend.UpdateRecoveryPointIndexSettings(vaultName, rpArn, reqBody.Index); err != nil {
return h.handleError(c, err)
@@ -388,7 +390,7 @@ func (h *Handler) handleUpdateRecoveryPointLifecycle(
DeleteAfterDays int64 `json:"DeleteAfterDays"`
} `json:"Lifecycle"`
}
- _ = json.Unmarshal(body, &reqBody)
+ _ = json.Unmarshal(body, &reqBody, jsonx.V1())
if err := h.Backend.UpdateRecoveryPointLifecycle(vaultName, rpArn,
reqBody.Lifecycle.MoveToColdStorageAfterDays, reqBody.Lifecycle.DeleteAfterDays); err != nil {
diff --git a/services/backup/handler_recovery_points_test.go b/services/backup/handler_recovery_points_test.go
index d1a2bc7969..c5e2c6102e 100644
--- a/services/backup/handler_recovery_points_test.go
+++ b/services/backup/handler_recovery_points_test.go
@@ -1,7 +1,7 @@
package backup_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -111,7 +112,7 @@ func TestRecoveryPointNewFields(t *testing.T) {
require.Equal(t, http.StatusOK, descResp.Code)
var respData map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &respData))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &respData, jsonx.V1()))
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
diff --git a/services/backup/handler_report_plans.go b/services/backup/handler_report_plans.go
index 62e76ee4c9..6df6e35630 100644
--- a/services/backup/handler_report_plans.go
+++ b/services/backup/handler_report_plans.go
@@ -1,12 +1,14 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ScanJobsFilterFromQuery builds a ListScanJobsFilter from ListScanJobs
@@ -140,7 +142,7 @@ type createReportPlanBody struct {
func (h *Handler) handleCreateReportPlan(c *echo.Context, body []byte) error {
var in createReportPlanBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterValueException", "invalid request body"))
}
@@ -239,7 +241,7 @@ func (h *Handler) handleUpdateReportPlan(c *echo.Context, name string, body []by
var in updateReportPlanBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid request body"),
@@ -413,7 +415,7 @@ func (h *Handler) handleStartScanJob(c *echo.Context, body []byte) error {
IdempotencyToken string `json:"IdempotencyToken"`
ScanBaseRecoveryPointArn string `json:"ScanBaseRecoveryPointArn"`
}
- if err := json.Unmarshal(body, &reqBody); err != nil {
+ if err := json.Unmarshal(body, &reqBody, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid request body"),
diff --git a/services/backup/handler_report_plans_test.go b/services/backup/handler_report_plans_test.go
index 532823e84f..c1c12cf050 100644
--- a/services/backup/handler_report_plans_test.go
+++ b/services/backup/handler_report_plans_test.go
@@ -1,7 +1,7 @@
package backup_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -76,7 +77,7 @@ func TestReportPlanSubFields(t *testing.T) {
require.Equal(t, tt.wantStatus, resp.Code)
var createData map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &createData))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &createData, jsonx.V1()))
rpName := createData["ReportPlanName"].(string)
// Describe and verify sub-fields.
@@ -84,7 +85,7 @@ func TestReportPlanSubFields(t *testing.T) {
require.Equal(t, http.StatusOK, getResp.Code)
var getData map[string]any
- require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &getData))
+ require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &getData, jsonx.V1()))
rpDoc := getData["ReportPlan"].(map[string]any)
if tt.wantDelivery {
@@ -142,7 +143,7 @@ func TestReportPlanFullShape(t *testing.T) {
getResp := doRequest(t, h, http.MethodGet, "/audit/report-plans/rp-full-shape", "")
require.Equal(t, http.StatusOK, getResp.Code)
var getData map[string]any
- require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &getData))
+ require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &getData, jsonx.V1()))
rpDoc := getData["ReportPlan"].(map[string]any)
ch := rpDoc["ReportDeliveryChannel"].(map[string]any)
@@ -169,7 +170,7 @@ func TestReportPlanFullShape(t *testing.T) {
getResp2 := doRequest(t, h, http.MethodGet, "/audit/report-plans/rp-full-shape", "")
var getData2 map[string]any
- require.NoError(t, json.Unmarshal(getResp2.Body.Bytes(), &getData2))
+ require.NoError(t, json.Unmarshal(getResp2.Body.Bytes(), &getData2, jsonx.V1()))
rpDoc2 := getData2["ReportPlan"].(map[string]any)
assert.Equal(t, "updated", rpDoc2["ReportPlanDescription"])
ch2 := rpDoc2["ReportDeliveryChannel"].(map[string]any)
diff --git a/services/backup/handler_restore_jobs.go b/services/backup/handler_restore_jobs.go
index 2f8287ebb5..40a1042b13 100644
--- a/services/backup/handler_restore_jobs.go
+++ b/services/backup/handler_restore_jobs.go
@@ -1,11 +1,13 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// RestoreJobsFilterFromQuery builds a ListRestoreJobsFilter from ListRestoreJobs
@@ -92,7 +94,7 @@ func (h *Handler) handleStartRestoreJob(c *echo.Context, defaultRecoveryPointArn
ResourceType string `json:"ResourceType"`
IdempotencyToken string `json:"IdempotencyToken"`
}
- if err := json.Unmarshal(body, &reqBody); err != nil {
+ if err := json.Unmarshal(body, &reqBody, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterValueException", "invalid request body"))
}
if reqBody.RecoveryPointArn == "" {
@@ -126,7 +128,7 @@ func (h *Handler) handlePutRestoreValidationResult(c *echo.Context, restoreJobID
ValidationStatus string `json:"ValidationStatus"`
ValidationStatusMessage string `json:"ValidationStatusMessage"`
}
- if err := json.Unmarshal(body, &reqBody); err != nil {
+ if err := json.Unmarshal(body, &reqBody, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterValueException", "invalid request body"))
}
diff --git a/services/backup/handler_restore_testing.go b/services/backup/handler_restore_testing.go
index 3c44f6069b..745b62bf26 100644
--- a/services/backup/handler_restore_testing.go
+++ b/services/backup/handler_restore_testing.go
@@ -1,10 +1,12 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// restoreTestingRecoveryPointSelectionJSON is the wire shape of
@@ -71,7 +73,7 @@ type createRestoreTestingPlanBody struct {
func (h *Handler) handleCreateRestoreTestingPlan(c *echo.Context, body []byte) error {
var in createRestoreTestingPlanBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterValueException", "invalid request body"))
}
@@ -229,7 +231,7 @@ func (h *Handler) handleCreateRestoreTestingSelection(
}
var in createRestoreTestingSelectionBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterValueException", "invalid request body"))
}
@@ -331,7 +333,7 @@ func (h *Handler) handleUpdateRestoreTestingPlan(
var in updateRestoreTestingPlanBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid request body"),
@@ -445,7 +447,7 @@ func (h *Handler) handleUpdateRestoreTestingSelection(
var in updateRestoreTestingSelectionBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid request body"),
diff --git a/services/backup/handler_selections.go b/services/backup/handler_selections.go
index d30b133e5d..99fa684980 100644
--- a/services/backup/handler_selections.go
+++ b/services/backup/handler_selections.go
@@ -1,10 +1,12 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type backupSelectionDoc struct {
@@ -30,7 +32,7 @@ func (h *Handler) handleCreateBackupSelection(c *echo.Context, planID string, bo
}
var in createBackupSelectionBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterValueException", "invalid request body"))
}
diff --git a/services/backup/handler_selections_test.go b/services/backup/handler_selections_test.go
index 670fa1a696..61d1b49470 100644
--- a/services/backup/handler_selections_test.go
+++ b/services/backup/handler_selections_test.go
@@ -1,7 +1,7 @@
package backup_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -109,18 +110,18 @@ func TestBackupSelectionResourceFields(t *testing.T) {
`{"BackupPlan":{"BackupPlanName":"p","Rules":[{"RuleName":"r","TargetBackupVaultName":"v"}]}}`)
require.Equal(t, http.StatusOK, planResp.Code)
var planData map[string]any
- require.NoError(t, json.Unmarshal(planResp.Body.Bytes(), &planData))
+ require.NoError(t, json.Unmarshal(planResp.Body.Bytes(), &planData, jsonx.V1()))
planID := planData["BackupPlanId"].(string)
// Create selection.
- body, err := json.Marshal(map[string]any{"BackupSelection": tt.selection})
+ body, err := json.Marshal(map[string]any{"BackupSelection": tt.selection}, jsonx.V1())
require.NoError(t, err)
selResp := doRequest(t, h, http.MethodPut,
"/backup/plans/"+planID+"/selections", string(body))
require.Equal(t, http.StatusOK, selResp.Code)
var selData map[string]any
- require.NoError(t, json.Unmarshal(selResp.Body.Bytes(), &selData))
+ require.NoError(t, json.Unmarshal(selResp.Body.Bytes(), &selData, jsonx.V1()))
selID := selData["SelectionId"].(string)
// Get selection and verify fields.
@@ -129,7 +130,7 @@ func TestBackupSelectionResourceFields(t *testing.T) {
require.Equal(t, http.StatusOK, getResp.Code)
var getData map[string]any
- require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &getData))
+ require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &getData, jsonx.V1()))
selDoc := getData["BackupSelection"].(map[string]any)
tt.wantSel(t, selDoc)
})
diff --git a/services/backup/handler_settings.go b/services/backup/handler_settings.go
index f74adb5b5f..ff43bc0227 100644
--- a/services/backup/handler_settings.go
+++ b/services/backup/handler_settings.go
@@ -1,10 +1,12 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// dispatchSettingsOps handles the account-level global/region settings and
@@ -26,7 +28,7 @@ func (h *Handler) dispatchSettingsOps(
var reqGSBody struct {
GlobalSettings map[string]string `json:"GlobalSettings"`
}
- if err := json.Unmarshal(body, &reqGSBody); err == nil &&
+ if err := json.Unmarshal(body, &reqGSBody, jsonx.V1()); err == nil &&
reqGSBody.GlobalSettings != nil {
h.Backend.UpdateGlobalSettings(reqGSBody.GlobalSettings)
}
@@ -44,7 +46,7 @@ func (h *Handler) dispatchSettingsOps(
ResourceTypeManagementPreference map[string]bool `json:"ResourceTypeManagementPreference"`
ResourceTypeOptInPreference map[string]bool `json:"ResourceTypeOptInPreference"`
}
- if err := json.Unmarshal(body, &reqRSBody); err == nil {
+ if err := json.Unmarshal(body, &reqRSBody, jsonx.V1()); err == nil {
h.Backend.UpdateRegionSettings(
reqRSBody.ResourceTypeManagementPreference,
reqRSBody.ResourceTypeOptInPreference,
diff --git a/services/backup/handler_tags.go b/services/backup/handler_tags.go
index 667411460b..451d0dbd07 100644
--- a/services/backup/handler_tags.go
+++ b/services/backup/handler_tags.go
@@ -1,12 +1,14 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"slices"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type tagResourceBody struct {
@@ -15,7 +17,7 @@ type tagResourceBody struct {
func (h *Handler) handleTagResource(c *echo.Context, resourceArn string, body []byte) error {
var in tagResourceBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterValueException", "invalid request body"))
}
@@ -61,7 +63,7 @@ func (h *Handler) handleUntagResource(c *echo.Context, resourceArn string, body
var in untagResourceBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid request body"),
diff --git a/services/backup/handler_templates_test.go b/services/backup/handler_templates_test.go
index 139c14a672..b3beee0685 100644
--- a/services/backup/handler_templates_test.go
+++ b/services/backup/handler_templates_test.go
@@ -1,13 +1,14 @@
package backup_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -41,19 +42,19 @@ func TestExportBackupPlanTemplate_RealRules(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &created, jsonx.V1()))
planID := created["BackupPlanId"].(string)
resp := doRequest(t, h, http.MethodGet, "/backup/plans/"+planID+"/toTemplate", "")
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
tmplJSON, ok := out["BackupPlanTemplateJson"].(string)
require.True(t, ok)
var tmpl map[string]any
- require.NoError(t, json.Unmarshal([]byte(tmplJSON), &tmpl))
+ require.NoError(t, json.Unmarshal([]byte(tmplJSON), &tmpl, jsonx.V1()))
assert.Equal(t, "export-plan", tmpl["BackupPlanName"])
rules, ok := tmpl["Rules"].([]any)
require.True(t, ok)
@@ -75,7 +76,7 @@ func TestGetBackupPlanFromJSON_BuildsFromParsedBody(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
plan, ok := out["BackupPlan"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "json-plan", plan["BackupPlanName"])
@@ -107,7 +108,7 @@ func TestGetBackupPlanFromTemplate_ResolvesBuiltinTemplate(t *testing.T) {
require.Equal(t, http.StatusOK, listResp.Code)
var list map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &list))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &list, jsonx.V1()))
tmpls, ok := list["BackupPlanTemplatesList"].([]any)
require.True(t, ok)
require.NotEmpty(t, tmpls)
@@ -120,7 +121,7 @@ func TestGetBackupPlanFromTemplate_ResolvesBuiltinTemplate(t *testing.T) {
require.Equal(t, http.StatusOK, getResp.Code)
var doc map[string]any
- require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &doc))
+ require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &doc, jsonx.V1()))
planDoc, ok := doc["BackupPlanDocument"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tmplName, planDoc["BackupPlanName"])
@@ -148,7 +149,7 @@ func TestListBackupPlanTemplates_ReturnsBuiltinCatalog(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
items, ok := out["BackupPlanTemplatesList"].([]any)
require.True(t, ok)
require.NotEmpty(t, items)
diff --git a/services/backup/handler_tiering.go b/services/backup/handler_tiering.go
index 7e450eecc4..b63ec786e4 100644
--- a/services/backup/handler_tiering.go
+++ b/services/backup/handler_tiering.go
@@ -1,10 +1,12 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -103,7 +105,7 @@ func (h *Handler) dispatchTieringOps(
func (h *Handler) handleCreateTieringConfiguration(c *echo.Context, body []byte) error {
var in createTieringConfigBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterValueException", "invalid request body"))
}
@@ -167,7 +169,7 @@ func (h *Handler) handleListTieringConfigurations(c *echo.Context) error {
func (h *Handler) handleUpdateTieringConfiguration(c *echo.Context, name string, body []byte) error {
var in updateTieringConfigBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterValueException", "invalid request body"))
}
diff --git a/services/backup/handler_vault_policies.go b/services/backup/handler_vault_policies.go
index 39a10bf864..f92566be31 100644
--- a/services/backup/handler_vault_policies.go
+++ b/services/backup/handler_vault_policies.go
@@ -1,10 +1,12 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type putVaultAccessPolicyBody struct {
@@ -25,7 +27,7 @@ func (h *Handler) handlePutBackupVaultAccessPolicy(
var in putVaultAccessPolicyBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid request body"),
@@ -100,7 +102,7 @@ func (h *Handler) handlePutBackupVaultLockConfiguration(
var in putVaultLockConfigBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid request body"),
@@ -158,7 +160,7 @@ func (h *Handler) handlePutBackupVaultNotifications(
var in putVaultNotificationsBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid request body"),
diff --git a/services/backup/handler_vault_policies_test.go b/services/backup/handler_vault_policies_test.go
index 81874b9396..8c72d331d3 100644
--- a/services/backup/handler_vault_policies_test.go
+++ b/services/backup/handler_vault_policies_test.go
@@ -1,7 +1,7 @@
package backup_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -59,7 +60,7 @@ func TestDescribeBackupVaultLockedField(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var data map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &data))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &data, jsonx.V1()))
locked, exists := data["Locked"]
require.True(t, exists, "Locked must be present in DescribeBackupVault response")
diff --git a/services/backup/handler_vaults.go b/services/backup/handler_vaults.go
index 0f0236a652..5eefb43655 100644
--- a/services/backup/handler_vaults.go
+++ b/services/backup/handler_vaults.go
@@ -1,11 +1,13 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createBackupVaultBody struct {
@@ -24,7 +26,7 @@ func (h *Handler) handleCreateBackupVault(c *echo.Context, name string, body []b
var in createBackupVaultBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid request body"),
@@ -192,7 +194,7 @@ func (h *Handler) handleAssociateBackupVaultMpaApprovalTeam(
var in associateMpaApprovalTeamBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid request body"),
@@ -230,7 +232,7 @@ func (h *Handler) handleCreateLogicallyAirGappedBackupVault(
var in createLogicallyAirGappedBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterValueException", "invalid request body"),
@@ -264,7 +266,7 @@ type createRestoreAccessVaultBody struct {
func (h *Handler) handleCreateRestoreAccessBackupVault(c *echo.Context, body []byte) error {
var in createRestoreAccessVaultBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterValueException", "invalid request body"))
}
diff --git a/services/backup/handler_vaults_test.go b/services/backup/handler_vaults_test.go
index fba8592d38..1da6aea43d 100644
--- a/services/backup/handler_vaults_test.go
+++ b/services/backup/handler_vaults_test.go
@@ -1,7 +1,7 @@
package backup_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -33,7 +34,7 @@ func TestDescribeBackupVaultVaultState(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var data map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &data))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &data, jsonx.V1()))
state, exists := data["VaultState"]
require.True(t, exists, "VaultState must be present in DescribeBackupVault response")
@@ -67,7 +68,7 @@ func TestListBackupVaultsVaultState(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var data map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &data))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &data, jsonx.V1()))
list, ok := data["BackupVaultList"].([]any)
require.True(t, ok, "BackupVaultList must be present")
@@ -184,14 +185,14 @@ func TestCreateVaultCreatorRequestIdIdempotency(t *testing.T) {
r1 := doRequest(t, h, http.MethodPut, "/backup-vaults/idem-vault", tt.firstRequest)
require.Equal(t, http.StatusOK, r1.Code)
var d1 map[string]any
- require.NoError(t, json.Unmarshal(r1.Body.Bytes(), &d1))
+ require.NoError(t, json.Unmarshal(r1.Body.Bytes(), &d1, jsonx.V1()))
arn1 := d1["BackupVaultArn"].(string)
r2 := doRequest(t, h, http.MethodPut, "/backup-vaults/idem-vault", tt.secondRequest)
assert.Equal(t, tt.wantSecondCode, r2.Code)
if tt.wantSameArn {
var d2 map[string]any
- require.NoError(t, json.Unmarshal(r2.Body.Bytes(), &d2))
+ require.NoError(t, json.Unmarshal(r2.Body.Bytes(), &d2, jsonx.V1()))
assert.Equal(t, arn1, d2["BackupVaultArn"].(string))
}
})
diff --git a/services/backup/helpers_test.go b/services/backup/helpers_test.go
index 19c61685f9..cd53f98ad0 100644
--- a/services/backup/helpers_test.go
+++ b/services/backup/helpers_test.go
@@ -2,7 +2,7 @@ package backup_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -131,7 +132,7 @@ func doREST(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -151,7 +152,7 @@ func parseResp(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
diff --git a/services/backup/models.go b/services/backup/models.go
index bc78d665c9..ad7e7906dd 100644
--- a/services/backup/models.go
+++ b/services/backup/models.go
@@ -1,7 +1,7 @@
package backup
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
@@ -401,7 +401,7 @@ type RecoveryPoint struct {
SourceBackupVaultArn string `json:"sourceBackupVaultArn,omitempty"`
ParentRecoveryPointArn string `json:"parentRecoveryPointArn,omitempty"`
CompositeMemberIdentifier string `json:"compositeMemberIdentifier,omitempty"`
- Snapshot json.RawMessage `json:"snapshot,omitempty"`
+ Snapshot jsontext.Value `json:"snapshot,omitempty"`
BackupSizeInBytes int64 `json:"backupSizeInBytes,omitempty"`
IsEncrypted bool `json:"isEncrypted,omitempty"`
}
diff --git a/services/backup/multi_region_test.go b/services/backup/multi_region_test.go
index 79b3fe3527..288465938a 100644
--- a/services/backup/multi_region_test.go
+++ b/services/backup/multi_region_test.go
@@ -1,7 +1,7 @@
package backup_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -38,7 +39,7 @@ func regionCall(t *testing.T, h *backup.Handler, region, method, path, body stri
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
out := map[string]any{}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/backup/persistence.go b/services/backup/persistence.go
index da564feeeb..467e6710fe 100644
--- a/services/backup/persistence.go
+++ b/services/backup/persistence.go
@@ -2,7 +2,7 @@ package backup
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"time"
@@ -33,15 +33,15 @@ const backupSnapshotVersion = 1
// indexes (vaultARNIndex etc.) are NOT included here; they are rebuilt from
// the restored tables by rebuildARNIndexes instead of persisted.
type backendSnapshot struct {
- GlobalSettingsLastUpdate time.Time `json:"globalSettingsLastUpdate,omitzero"`
- Tables map[string]json.RawMessage `json:"tables"`
- MpaApprovals map[string]string `json:"mpaApprovals,omitempty"`
- GlobalSettings map[string]string `json:"globalSettings,omitempty"`
- RecoveryPointIndexStatus map[string]string `json:"recoveryPointIndexStatus,omitempty"`
- RegionSettings *RegionSettings `json:"regionSettings,omitempty"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ GlobalSettingsLastUpdate time.Time `json:"globalSettingsLastUpdate,omitzero"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ MpaApprovals map[string]string `json:"mpaApprovals,omitempty"`
+ GlobalSettings map[string]string `json:"globalSettings,omitempty"`
+ RecoveryPointIndexStatus map[string]string `json:"recoveryPointIndexStatus,omitempty"`
+ RegionSettings *RegionSettings `json:"regionSettings,omitempty"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/backup/protected_resources_test.go b/services/backup/protected_resources_test.go
index 08d1c466dd..e2d5c85813 100644
--- a/services/backup/protected_resources_test.go
+++ b/services/backup/protected_resources_test.go
@@ -1,13 +1,14 @@
package backup_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -46,7 +47,7 @@ func TestProtectedResourceLastBackupTimeEpoch(t *testing.T) {
"IamRoleArn": "arn:aws:iam::000000000000:role/backup-role"
}`)
var startData map[string]any
- require.NoError(t, json.Unmarshal(startResp.Body.Bytes(), &startData))
+ require.NoError(t, json.Unmarshal(startResp.Body.Bytes(), &startData, jsonx.V1()))
// Real AWS Backup jobs complete asynchronously; the emulator
// models that via the janitor. Complete it synchronously here
// so the resource is recorded as protected.
@@ -66,7 +67,7 @@ func TestProtectedResourceLastBackupTimeEpoch(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var data map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &data))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &data, jsonx.V1()))
lastBackup, exists := data["LastBackupTime"]
require.True(t, exists, "LastBackupTime must be present")
diff --git a/services/backup/recovery_point_by_vault_storage_class_test.go b/services/backup/recovery_point_by_vault_storage_class_test.go
index 02ed5e6e58..0c4e407a21 100644
--- a/services/backup/recovery_point_by_vault_storage_class_test.go
+++ b/services/backup/recovery_point_by_vault_storage_class_test.go
@@ -1,7 +1,7 @@
package backup_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -42,7 +43,7 @@ func TestListRecoveryPointsByBackupVault_NoStorageClassLeak(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
items, ok := listResp["RecoveryPoints"].([]any)
require.True(t, ok)
require.Len(t, items, 1)
@@ -58,6 +59,6 @@ func TestListRecoveryPointsByBackupVault_NoStorageClassLeak(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "WARM", descResp["StorageClass"], "DescribeRecoveryPoint genuinely has StorageClass")
}
diff --git a/services/backup/recovery_points_test.go b/services/backup/recovery_points_test.go
index 00ca5aae3c..db140880d9 100644
--- a/services/backup/recovery_points_test.go
+++ b/services/backup/recovery_points_test.go
@@ -1,7 +1,7 @@
package backup_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -246,7 +247,7 @@ func TestDeleteRecoveryPointVaultLock(t *testing.T) {
var probe struct {
RecoveryPointIndexStatus map[string]string `json:"recoveryPointIndexStatus"`
}
- require.NoError(t, json.Unmarshal(b.Snapshot(t.Context()), &probe))
+ require.NoError(t, json.Unmarshal(b.Snapshot(t.Context()), &probe, jsonx.V1()))
assert.NotContains(t, probe.RecoveryPointIndexStatus, "idx-vault:arn:aws:backup:::rp/1")
})
}
diff --git a/services/backup/report_delivery.go b/services/backup/report_delivery.go
index ac27853e3d..3dd82e1c82 100644
--- a/services/backup/report_delivery.go
+++ b/services/backup/report_delivery.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"encoding/csv"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"path"
"sort"
@@ -14,6 +14,8 @@ import (
"github.com/aws/aws-sdk-go-v2/aws"
sdk_s3 "github.com/aws/aws-sdk-go-v2/service/s3"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -89,7 +91,7 @@ func renderReport(format string, t reportTable) ([]byte, string, error) {
recs = append(recs, rec)
}
- body, err := json.Marshal(recs)
+ body, err := json.Marshal(recs, jsonx.V1())
if err != nil {
return nil, "", fmt.Errorf("encode JSON report: %w", err)
}
diff --git a/services/backup/resource_engines.go b/services/backup/resource_engines.go
index 2a1650fbf7..4617893f99 100644
--- a/services/backup/resource_engines.go
+++ b/services/backup/resource_engines.go
@@ -3,7 +3,8 @@ package backup
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
@@ -13,6 +14,7 @@ import (
sdk_s3 "github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -24,8 +26,8 @@ const (
// resourceEngine snapshots a live resource at backup time and recreates it from that snapshot on restore.
type resourceEngine interface {
// capture returns nil data when the resource does not exist.
- capture(ctx context.Context, resourceArn string) (json.RawMessage, error)
- restore(ctx context.Context, data json.RawMessage, metadata map[string]string) (string, error)
+ capture(ctx context.Context, resourceArn string) (jsontext.Value, error)
+ restore(ctx context.Context, data jsontext.Value, metadata map[string]string) (string, error)
}
// DynamoDBBackend is the DynamoDB surface used to snapshot and restore tables.
@@ -94,10 +96,10 @@ type ddbSnapshot struct {
Attributes []ddbtypes.AttributeDefinition `json:"attributes"`
GSIs []ddbtypes.GlobalSecondaryIndex `json:"gsis,omitempty"`
LSIs []ddbtypes.LocalSecondaryIndex `json:"lsis,omitempty"`
- Items []map[string]json.RawMessage `json:"items"`
+ Items []map[string]jsontext.Value `json:"items"`
}
-func (e *ddbEngine) capture(ctx context.Context, resourceArn string) (json.RawMessage, error) {
+func (e *ddbEngine) capture(ctx context.Context, resourceArn string) (jsontext.Value, error) {
name := resourceNameFromARN(resourceArn)
desc, err := e.ddb.DescribeTable(ctx, &dynamodb.DescribeTableInput{TableName: aws.String(name)})
@@ -139,12 +141,12 @@ func (e *ddbEngine) capture(ctx context.Context, resourceArn string) (json.RawMe
return nil, err
}
- return json.Marshal(snap)
+ return json.Marshal(snap, jsonx.V1())
}
-func (e *ddbEngine) scanAll(ctx context.Context, table string) ([]map[string]json.RawMessage, error) {
+func (e *ddbEngine) scanAll(ctx context.Context, table string) ([]map[string]jsontext.Value, error) {
var (
- items []map[string]json.RawMessage
+ items []map[string]jsontext.Value
start map[string]ddbtypes.AttributeValue
)
@@ -169,9 +171,9 @@ func (e *ddbEngine) scanAll(ctx context.Context, table string) ([]map[string]jso
}
}
-func (e *ddbEngine) restore(ctx context.Context, data json.RawMessage, md map[string]string) (string, error) {
+func (e *ddbEngine) restore(ctx context.Context, data jsontext.Value, md map[string]string) (string, error) {
var snap ddbSnapshot
- if err := json.Unmarshal(data, &snap); err != nil {
+ if err := json.Unmarshal(data, &snap, jsonx.V1()); err != nil {
return "", fmt.Errorf("decode DynamoDB snapshot: %w", err)
}
@@ -215,7 +217,7 @@ type s3SnapObject struct {
Body []byte `json:"body"`
}
-func (e *s3Engine) capture(ctx context.Context, resourceArn string) (json.RawMessage, error) {
+func (e *s3Engine) capture(ctx context.Context, resourceArn string) (jsontext.Value, error) {
bucket, ok := s3BucketFromARN(resourceArn)
if !ok {
return nil, nil
@@ -249,7 +251,7 @@ func (e *s3Engine) capture(ctx context.Context, resourceArn string) (json.RawMes
token = page.NextContinuationToken
}
- return json.Marshal(snap)
+ return json.Marshal(snap, jsonx.V1())
}
func (e *s3Engine) readObject(ctx context.Context, bucket, key string) (s3SnapObject, error) {
@@ -267,9 +269,9 @@ func (e *s3Engine) readObject(ctx context.Context, bucket, key string) (s3SnapOb
return s3SnapObject{Key: key, ContentType: aws.ToString(out.ContentType), Body: body}, nil
}
-func (e *s3Engine) restore(ctx context.Context, data json.RawMessage, md map[string]string) (string, error) {
+func (e *s3Engine) restore(ctx context.Context, data jsontext.Value, md map[string]string) (string, error) {
var snap s3Snapshot
- if err := json.Unmarshal(data, &snap); err != nil {
+ if err := json.Unmarshal(data, &snap, jsonx.V1()); err != nil {
return "", fmt.Errorf("decode S3 snapshot: %w", err)
}
@@ -303,18 +305,18 @@ type ebsEngine struct {
accountID string
}
-func (e *ebsEngine) capture(_ context.Context, resourceArn string) (json.RawMessage, error) {
+func (e *ebsEngine) capture(_ context.Context, resourceArn string) (jsontext.Value, error) {
info, ok := e.vols.DescribeVolume(resourceNameFromARN(resourceArn))
if !ok {
return nil, nil
}
- return json.Marshal(info)
+ return json.Marshal(info, jsonx.V1())
}
-func (e *ebsEngine) restore(_ context.Context, data json.RawMessage, md map[string]string) (string, error) {
+func (e *ebsEngine) restore(_ context.Context, data jsontext.Value, md map[string]string) (string, error) {
var info VolumeInfo
- if err := json.Unmarshal(data, &info); err != nil {
+ if err := json.Unmarshal(data, &info, jsonx.V1()); err != nil {
return "", fmt.Errorf("decode EBS snapshot: %w", err)
}
if v := md["availabilityZone"]; v != "" {
diff --git a/services/backup/resource_restore_test.go b/services/backup/resource_restore_test.go
index b9768aeee0..893fa51e8b 100644
--- a/services/backup/resource_restore_test.go
+++ b/services/backup/resource_restore_test.go
@@ -3,7 +3,7 @@ package backup_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"sort"
@@ -18,6 +18,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -359,7 +360,7 @@ func TestReportJob_JSONContent(t *testing.T) {
require.Len(t, got.ReportDestination.S3Keys, 1)
var rows []map[string]string
- require.NoError(t, json.Unmarshal(store.objects["rep/"+got.ReportDestination.S3Keys[0]], &rows))
+ require.NoError(t, json.Unmarshal(store.objects["rep/"+got.ReportDestination.S3Keys[0]], &rows, jsonx.V1()))
require.Len(t, rows, 1)
assert.Equal(t, testVolumeArn, rows[0]["Resource ARN"])
assert.Equal(t, "COMPLETED", rows[0]["Job Status"])
diff --git a/services/backup/settings_test.go b/services/backup/settings_test.go
index dbbab38d19..66a63872f0 100644
--- a/services/backup/settings_test.go
+++ b/services/backup/settings_test.go
@@ -1,13 +1,14 @@
package backup_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/backup"
)
@@ -44,7 +45,7 @@ func TestGlobalSettingsLastUpdateTimeEpoch(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var data map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &data))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &data, jsonx.V1()))
lastUpdate, exists := data["LastUpdateTime"]
require.True(t, exists, "LastUpdateTime must be present")
diff --git a/services/backup/vault_policies.go b/services/backup/vault_policies.go
index 1f1ee7e792..87adb6f98b 100644
--- a/services/backup/vault_policies.go
+++ b/services/backup/vault_policies.go
@@ -1,11 +1,13 @@
package backup
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
sdktypes "github.com/aws/aws-sdk-go-v2/service/backup/types"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// isValidVaultEvent derives its answer from types.BackupVaultEvent.Values()
@@ -35,7 +37,7 @@ func (b *InMemoryBackend) PutBackupVaultAccessPolicy(vaultName, policy string) e
if policy != "" {
var doc map[string]any
- if err := json.Unmarshal([]byte(policy), &doc); err != nil {
+ if err := json.Unmarshal([]byte(policy), &doc, jsonx.V1()); err != nil {
return fmt.Errorf(
"%w: Policy must be a valid JSON document: %s",
ErrValidation,
diff --git a/services/batch/exec_test.go b/services/batch/exec_test.go
index b38992a268..24e6395db5 100644
--- a/services/batch/exec_test.go
+++ b/services/batch/exec_test.go
@@ -2,7 +2,7 @@ package batch_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"log/slog"
"strings"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/batch"
)
@@ -300,7 +301,7 @@ func TestExecJobStateAndExitCode(t *testing.T) {
assert.Equal(t, done.Container.LogStreamName, done.Attempts[0].Container.LogStreamName)
assert.NotNil(t, done.Attempts[0].StoppedAt)
- raw, err := json.Marshal(done.Attempts[0].Container)
+ raw, err := json.Marshal(done.Attempts[0].Container, jsonx.V1())
require.NoError(t, err)
assert.Contains(t, string(raw), `"exitCode"`)
diff --git a/services/batch/handler.go b/services/batch/handler.go
index 26acb3482f..917ad42c2b 100644
--- a/services/batch/handler.go
+++ b/services/batch/handler.go
@@ -2,7 +2,7 @@ package batch
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"log/slog"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -331,7 +332,7 @@ func (h *Handler) Handler() echo.HandlerFunc {
return h.writeError(c, opErr)
}
- out, marshalErr := json.Marshal(result)
+ out, marshalErr := json.Marshal(result, jsonx.V1())
if marshalErr != nil {
log.ErrorContext(ctx, "batch: failed to marshal response", "error", marshalErr)
diff --git a/services/batch/handler_compute_environments_test.go b/services/batch/handler_compute_environments_test.go
index 31c4878362..101423e7aa 100644
--- a/services/batch/handler_compute_environments_test.go
+++ b/services/batch/handler_compute_environments_test.go
@@ -1,13 +1,15 @@
package batch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/batch"
)
@@ -293,7 +295,7 @@ func TestDescribeComputeEnvironments_TagsPresentNoTags(t *testing.T) {
items := out["computeEnvironments"].([]any)
require.Len(t, items, 1)
- itemBytes, err := json.Marshal(items[0])
+ itemBytes, err := json.Marshal(items[0], jsonx.V1())
require.NoError(t, err)
assertTagsPresent(t, itemBytes)
}
@@ -307,8 +309,8 @@ func TestDescribeComputeEnvironments_EmptyList(t *testing.T) {
rec := post(t, h, "/v1/describecomputeenvironments", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
- var rawMap map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rawMap))
+ var rawMap map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rawMap, jsonx.V1()))
raw, ok := rawMap["computeEnvironments"]
require.True(t, ok, "computeEnvironments key must be present")
assert.Equal(t, "[]", string(raw), "computeEnvironments must be [] not null when empty")
diff --git a/services/batch/handler_job_definitions_test.go b/services/batch/handler_job_definitions_test.go
index a5a640529e..8b96b2337f 100644
--- a/services/batch/handler_job_definitions_test.go
+++ b/services/batch/handler_job_definitions_test.go
@@ -1,12 +1,15 @@
package batch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_JobDefinition_CRUD(t *testing.T) {
@@ -194,7 +197,7 @@ func TestDescribeJobDefinitions_TagsPresentNoTags(t *testing.T) {
items := out["jobDefinitions"].([]any)
require.Len(t, items, 1)
- itemBytes, err := json.Marshal(items[0])
+ itemBytes, err := json.Marshal(items[0], jsonx.V1())
require.NoError(t, err)
assertTagsPresent(t, itemBytes)
}
@@ -208,8 +211,8 @@ func TestDescribeJobDefinitions_EmptyList(t *testing.T) {
rec := post(t, h, "/v1/describejobdefinitions", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
- var rawMap map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rawMap))
+ var rawMap map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rawMap, jsonx.V1()))
raw, ok := rawMap["jobDefinitions"]
require.True(t, ok, "jobDefinitions key must be present")
assert.Equal(t, "[]", string(raw), "jobDefinitions must be [] not null when empty")
diff --git a/services/batch/handler_job_queues_test.go b/services/batch/handler_job_queues_test.go
index c4e5996240..3f33061bbb 100644
--- a/services/batch/handler_job_queues_test.go
+++ b/services/batch/handler_job_queues_test.go
@@ -1,13 +1,15 @@
package batch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/batch"
)
@@ -378,7 +380,7 @@ func TestHandler_DeleteJobQueue_TerminatesJobs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var submitResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &submitResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &submitResp, jsonx.V1()))
jobID := submitResp["jobId"].(string)
// Delete the queue — associated jobs must be terminated (FAILED), not
@@ -402,7 +404,7 @@ func TestHandler_DeleteJobQueue_TerminatesJobs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
jobs := descResp["jobs"].([]any)
require.Len(t, jobs, 1, "job history must survive job queue deletion")
job, _ := jobs[0].(map[string]any)
@@ -524,21 +526,21 @@ func TestHandler_GetJobQueueSnapshot_WireShape(t *testing.T) {
rec = post(t, h, "/v1/getjobqueuesnapshot", map[string]any{"jobQueue": "q-snap-wire"})
require.Equal(t, http.StatusOK, rec.Code)
- var rawMap map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rawMap))
+ var rawMap map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rawMap, jsonx.V1()))
- var frontOfQueue map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rawMap["frontOfQueue"], &frontOfQueue))
+ var frontOfQueue map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rawMap["frontOfQueue"], &frontOfQueue, jsonx.V1()))
_, hasTimestamp := frontOfQueue["timestamp"]
assert.False(t, hasTimestamp, "wire shape must not have a legacy \"timestamp\" field")
var lastUpdatedAt int64
- require.NoError(t, json.Unmarshal(frontOfQueue["lastUpdatedAt"], &lastUpdatedAt))
+ require.NoError(t, json.Unmarshal(frontOfQueue["lastUpdatedAt"], &lastUpdatedAt, jsonx.V1()))
assert.Positive(t, lastUpdatedAt, "lastUpdatedAt should be a positive epoch-millisecond value")
var jobs []map[string]any
- require.NoError(t, json.Unmarshal(frontOfQueue["jobs"], &jobs))
+ require.NoError(t, json.Unmarshal(frontOfQueue["jobs"], &jobs, jsonx.V1()))
require.Len(t, jobs, 1)
assert.Contains(t, jobs[0]["jobArn"], "job/")
@@ -583,7 +585,7 @@ func TestDescribeJobQueues_TagsPresentNoTags(t *testing.T) {
items := out["jobQueues"].([]any)
require.Len(t, items, 1)
- itemBytes, err := json.Marshal(items[0])
+ itemBytes, err := json.Marshal(items[0], jsonx.V1())
require.NoError(t, err)
assertTagsPresent(t, itemBytes)
}
@@ -597,8 +599,8 @@ func TestDescribeJobQueues_EmptyList(t *testing.T) {
rec := post(t, h, "/v1/describejobqueues", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
- var rawMap map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rawMap))
+ var rawMap map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rawMap, jsonx.V1()))
raw, ok := rawMap["jobQueues"]
require.True(t, ok, "jobQueues key must be present")
assert.Equal(t, "[]", string(raw), "jobQueues must be [] not null when empty")
diff --git a/services/batch/handler_jobs_test.go b/services/batch/handler_jobs_test.go
index 45284792ff..6c64ac9464 100644
--- a/services/batch/handler_jobs_test.go
+++ b/services/batch/handler_jobs_test.go
@@ -1,7 +1,8 @@
package batch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/batch"
)
@@ -198,7 +200,7 @@ func TestHandler_SubmitJob_JobARNPresent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var submitResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &submitResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &submitResp, jsonx.V1()))
jobID := submitResp["jobId"].(string)
assert.NotEmpty(t, submitResp["jobArn"], "SubmitJob response should include jobArn")
@@ -209,7 +211,7 @@ func TestHandler_SubmitJob_JobARNPresent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
jobs := descResp["jobs"].([]any)
require.Len(t, jobs, 1)
@@ -621,7 +623,7 @@ func TestDescribeJobs_TagsPresentNoTags(t *testing.T) {
jobs := out["jobs"].([]any)
require.Len(t, jobs, 1)
- itemBytes, err := json.Marshal(jobs[0])
+ itemBytes, err := json.Marshal(jobs[0], jsonx.V1())
require.NoError(t, err)
assertTagsPresent(t, itemBytes)
}
@@ -668,8 +670,8 @@ func TestDescribeJobs_EmptyList(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
- var rawMap map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rawMap))
+ var rawMap map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rawMap, jsonx.V1()))
raw, ok := rawMap["jobs"]
require.True(t, ok, "jobs key must be present")
assert.Equal(t, "[]", string(raw), "jobs must be [] not null when no matching jobs found")
@@ -695,7 +697,7 @@ func TestListJobs_SummaryIncludesJobArn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries, _ := out["jobSummaryList"].([]any)
require.Len(t, summaries, 1)
@@ -720,7 +722,7 @@ func TestListJobs_SummaryIncludesTimestamps(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var submitOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &submitOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &submitOut, jsonx.V1()))
jobID := submitOut["jobId"]
rec = post(t, h, "/v1/terminatejob", map[string]any{
@@ -736,7 +738,7 @@ func TestListJobs_SummaryIncludesTimestamps(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries, _ := out["jobSummaryList"].([]any)
require.Len(t, summaries, 1)
diff --git a/services/batch/handler_tags.go b/services/batch/handler_tags.go
index f0b1dbe2d8..f163134652 100644
--- a/services/batch/handler_tags.go
+++ b/services/batch/handler_tags.go
@@ -2,11 +2,13 @@ package batch
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- Tags handlers ---
@@ -35,7 +37,7 @@ type tagResourceInput struct {
func (h *Handler) handleTagResource(ctx context.Context, c *echo.Context, resourceARN string, body []byte) error {
var in tagResourceInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid request body"))
}
}
diff --git a/services/batch/handler_tags_test.go b/services/batch/handler_tags_test.go
index dea1e9abc7..e02d46e958 100644
--- a/services/batch/handler_tags_test.go
+++ b/services/batch/handler_tags_test.go
@@ -2,7 +2,7 @@ package batch_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/batch"
)
@@ -259,7 +260,7 @@ func TestBatch_TagsOnNewResourceTypes(t *testing.T) {
e := echo.New()
tagPath := "/v1/tags/" + crARN
tagBody := map[string]any{"tags": map[string]string{"env": "prod"}}
- tagBytes, err := json.Marshal(tagBody)
+ tagBytes, err := json.Marshal(tagBody, jsonx.V1())
require.NoError(t, err)
tagReq := httptest.NewRequest(http.MethodPost, tagPath, bytes.NewReader(tagBytes))
@@ -277,7 +278,7 @@ func TestBatch_TagsOnNewResourceTypes(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec.Code)
var tagsOut map[string]map[string]string
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsOut, jsonx.V1()))
assert.Equal(t, "prod", tagsOut["tags"]["env"])
}
diff --git a/services/batch/handler_test.go b/services/batch/handler_test.go
index 7da8a819dd..58b97d1abe 100644
--- a/services/batch/handler_test.go
+++ b/services/batch/handler_test.go
@@ -2,7 +2,8 @@ package batch_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/batch"
)
@@ -33,7 +35,7 @@ func doRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -58,7 +60,7 @@ func post(t *testing.T, h *batch.Handler, path string, body any) *httptest.Respo
func mustUnmarshal(t *testing.T, rec *httptest.ResponseRecorder, v any) {
t.Helper()
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), v))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), v, jsonx.V1()))
}
// --- Handler metadata tests ---
@@ -418,15 +420,15 @@ func TestBatch_RequiredFieldValidation(t *testing.T) {
func assertTagsPresent(t *testing.T, raw []byte) {
t.Helper()
- var rawMap map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(raw, &rawMap))
+ var rawMap map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(raw, &rawMap, jsonx.V1()))
tagBytes, hasTags := rawMap["tags"]
assert.True(t, hasTags, "tags key must be present in describe response")
if hasTags {
var tags map[string]string
- require.NoError(t, json.Unmarshal(tagBytes, &tags), "tags must be a JSON object, not null")
+ require.NoError(t, json.Unmarshal(tagBytes, &tags, jsonx.V1()), "tags must be a JSON object, not null")
assert.NotNil(t, tags, "tags must be {} not null")
}
}
diff --git a/services/batch/job_overrides.go b/services/batch/job_overrides.go
index f7d5cf3f51..f09538a781 100644
--- a/services/batch/job_overrides.go
+++ b/services/batch/job_overrides.go
@@ -1,11 +1,13 @@
package batch
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const reservedEnvPrefix = "AWS_BATCH"
@@ -51,12 +53,12 @@ type EksPropertiesOverride struct {
func deepCopy[T any](v T) T {
var out T
- raw, err := json.Marshal(v)
+ raw, err := json.Marshal(v, jsonx.V1())
if err != nil {
return v
}
- if err = json.Unmarshal(raw, &out); err != nil {
+ if err = json.Unmarshal(raw, &out, jsonx.V1()); err != nil {
return v
}
diff --git a/services/batch/models.go b/services/batch/models.go
index dcd8309ffb..3e06cd167a 100644
--- a/services/batch/models.go
+++ b/services/batch/models.go
@@ -1,8 +1,10 @@
package batch
import (
- "encoding/json"
+ "encoding/json/v2"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- ComputeResources sub-types ---
@@ -320,7 +322,7 @@ func (n *NodeRangeProperty) UnmarshalJSON(data []byte) error {
Legacy *ContainerProperties `json:"containerProperties"`
}{plain: (*plain)(n)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/batch/node_range_property_json_test.go b/services/batch/node_range_property_json_test.go
index 30c2522330..20a8a292fa 100644
--- a/services/batch/node_range_property_json_test.go
+++ b/services/batch/node_range_property_json_test.go
@@ -1,12 +1,13 @@
package batch_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/batch"
)
@@ -26,11 +27,11 @@ func TestNodeRangeProperty_JSONKeys(t *testing.T) {
t.Parallel()
var r batch.NodeRangeProperty
- require.NoError(t, json.Unmarshal([]byte(tt.in), &r))
+ require.NoError(t, json.Unmarshal([]byte(tt.in), &r, jsonx.V1()))
require.NotNil(t, r.ContainerProperties)
assert.Equal(t, "busybox", r.ContainerProperties.Image)
- out, err := json.Marshal(r)
+ out, err := json.Marshal(r, jsonx.V1())
require.NoError(t, err)
assert.Contains(t, string(out), `"container":`)
assert.NotContains(t, string(out), `"containerProperties"`)
diff --git a/services/batch/persistence.go b/services/batch/persistence.go
index 1e7c5f8101..948a9b37b0 100644
--- a/services/batch/persistence.go
+++ b/services/batch/persistence.go
@@ -2,7 +2,7 @@ package batch
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/google/uuid"
@@ -55,7 +55,7 @@ func regionalDTOKeyFn[V any](d *regionalDTO[V]) string { return regionKey(d.Regi
// incompatible (older or newer) build of this backend as though it were the
// current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
JobDefRevisions map[string]map[string]int32 `json:"jobDefRevisions"`
AccountID string `json:"accountID"`
Region string `json:"region"`
@@ -271,7 +271,7 @@ func unwrapRegionalDTOs[V any](dtos *store.Table[regionalDTO[V]], setRegion func
// table (byRegion, byARN, byQueue, byName -- see store_setup.go), so unlike
// the pre-refactor Restore this needs no separate index-rebuild step.
// Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
dtos := buildPersistenceDTORegistry()
if err := dtos.registry.RestoreAll(tables); err != nil {
diff --git a/services/batch/validation.go b/services/batch/validation.go
index ae3cbb0539..862cc5aa14 100644
--- a/services/batch/validation.go
+++ b/services/batch/validation.go
@@ -1,10 +1,11 @@
package batch
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -40,7 +41,7 @@ func checkNextToken(body []byte) error {
NextToken *string `json:"nextToken"`
}
- if json.Unmarshal(body, &in) == nil && in.NextToken != nil && *in.NextToken != "" &&
+ if json.Unmarshal(body, &in, jsonx.V1()) == nil && in.NextToken != nil && *in.NextToken != "" &&
page.DecodeHMACToken(*in.NextToken, paginationSecret) <= 0 {
return fmt.Errorf("%w: invalid nextToken", ErrValidation)
}
diff --git a/services/bcmdataexports/errors.go b/services/bcmdataexports/errors.go
index 3dee148399..c70937e422 100644
--- a/services/bcmdataexports/errors.go
+++ b/services/bcmdataexports/errors.go
@@ -1,7 +1,7 @@
package bcmdataexports
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"hash/crc32"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Sentinels, one per exception in the pinned SDK's error set.
@@ -120,7 +121,7 @@ func writeErrorBody(c *echo.Context, e awserr.APIError, d detail) error {
body, _ := json.Marshal(errorBody{
Type: e.Code, Message: e.Message, ResourceID: d.resourceID,
ResourceType: d.resourceType, Reason: d.reason, Fields: d.fields,
- })
+ }, jsonx.V1())
c.Response().Header().Set("X-Amz-Crc32", strconv.FormatUint(uint64(crc32.ChecksumIEEE(body)), 10))
c.Response().Header().Set("Content-Type", "application/x-amz-json-1.1")
diff --git a/services/bcmdataexports/executions.go b/services/bcmdataexports/executions.go
index 9578f127df..152f13cbfb 100644
--- a/services/bcmdataexports/executions.go
+++ b/services/bcmdataexports/executions.go
@@ -2,13 +2,14 @@ package bcmdataexports
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"path"
"strings"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/billingexport"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -239,7 +240,7 @@ func writeResult(
man.DataFileKeys = []string{dataKey}
- mb, err := json.Marshal(&man)
+ mb, err := json.Marshal(&man, jsonx.V1())
if err != nil {
return fmt.Errorf("encode manifest: %w", err)
}
diff --git a/services/bcmdataexports/handler.go b/services/bcmdataexports/handler.go
index 2400b09779..7bf17bd94b 100644
--- a/services/bcmdataexports/handler.go
+++ b/services/bcmdataexports/handler.go
@@ -2,7 +2,7 @@ package bcmdataexports
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"slices"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/worker"
@@ -39,7 +40,7 @@ func bind[I, O any](fn func(*Handler, context.Context, *I) (*O, error)) opFunc {
var in I
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, failReason(reasonCannotParse, "malformed request body: %v", err)
}
}
diff --git a/services/bcmdataexports/persistence.go b/services/bcmdataexports/persistence.go
index c924c24a85..aa1f2efcd5 100644
--- a/services/bcmdataexports/persistence.go
+++ b/services/bcmdataexports/persistence.go
@@ -2,7 +2,7 @@ package bcmdataexports
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,10 +12,10 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/bcmdataexports/rows.go b/services/bcmdataexports/rows.go
index 8dcd6663e1..2576e2faad 100644
--- a/services/bcmdataexports/rows.go
+++ b/services/bcmdataexports/rows.go
@@ -2,12 +2,13 @@ package bcmdataexports
import (
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"hash/fnv"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/billingexport"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -35,7 +36,7 @@ func jsonMap(kv ...string) string {
m[kv[i]] = kv[i+1]
}
- b, _ := json.Marshal(m)
+ b, _ := json.Marshal(m, jsonx.V1())
return string(b)
}
diff --git a/services/bedrock/automated_reasoning_policies.go b/services/bedrock/automated_reasoning_policies.go
index 76c3ee6825..1af2f3c2fe 100644
--- a/services/bedrock/automated_reasoning_policies.go
+++ b/services/bedrock/automated_reasoning_policies.go
@@ -1,7 +1,7 @@
package bedrock
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"slices"
"sort"
@@ -56,7 +56,7 @@ func (b *InMemoryBackend) CreateAutomatedReasoningPolicy(
// ARPCreateOptions carries CreateAutomatedReasoningPolicy's optional policyDefinition and kmsKeyId.
type ARPCreateOptions struct {
KmsKeyID string
- PolicyDefinition json.RawMessage
+ PolicyDefinition jsontext.Value
}
// CreateAutomatedReasoningPolicyWithOptions is CreateAutomatedReasoningPolicy with ARPCreateOptions.
@@ -317,7 +317,7 @@ func (b *InMemoryBackend) arpVersionSummariesLocked(policyARN string) []*Automat
// would orphan that index instead of leaving the name unchanged.
func (b *InMemoryBackend) UpdateAutomatedReasoningPolicy(
policyARN, name, description string,
- policyDefinition json.RawMessage,
+ policyDefinition jsontext.Value,
) (*AutomatedReasoningPolicy, error) {
if len(policyDefinition) == 0 {
return nil, fmt.Errorf("%w: policyDefinition is required", ErrValidation)
@@ -451,7 +451,7 @@ func (b *InMemoryBackend) DeleteAutomatedReasoningPolicy(policyARN string, force
// request body (serializers.go:8008-8058).
func (b *InMemoryBackend) StartAutomatedReasoningPolicyBuildWorkflow(
policyARN, buildWorkflowType string,
- sourceContent json.RawMessage,
+ sourceContent jsontext.Value,
) (*AutomatedReasoningPolicyBuildWorkflow, error) {
if buildWorkflowType == "" {
return nil, fmt.Errorf("%w: buildWorkflowType is required", ErrValidation)
diff --git a/services/bedrock/export_test.go b/services/bedrock/export_test.go
index 3b39030b18..95752a90c3 100644
--- a/services/bedrock/export_test.go
+++ b/services/bedrock/export_test.go
@@ -1,7 +1,7 @@
package bedrock
import (
- "encoding/json"
+ "encoding/json/jsontext"
"strconv"
)
@@ -112,7 +112,7 @@ func (b *InMemoryBackend) AddBuildWorkflowForTest(policyARN string) *AutomatedRe
// conversion's registry mechanics directly, independent of the production
// Snapshot/Restore pair added in persistence.go (see persistence_test.go for
// black-box tests of that production path).
-func (b *InMemoryBackend) SnapshotTablesForTest() (map[string]json.RawMessage, error) {
+func (b *InMemoryBackend) SnapshotTablesForTest() (map[string]jsontext.Value, error) {
b.mu.RLock("SnapshotTablesForTest")
defer b.mu.RUnlock()
@@ -121,7 +121,7 @@ func (b *InMemoryBackend) SnapshotTablesForTest() (map[string]json.RawMessage, e
// RestoreTablesForTest replaces every store.Table registered on b.registry
// with the contents of data, as produced by SnapshotTablesForTest.
-func (b *InMemoryBackend) RestoreTablesForTest(data map[string]json.RawMessage) error {
+func (b *InMemoryBackend) RestoreTablesForTest(data map[string]jsontext.Value) error {
b.mu.Lock("RestoreTablesForTest")
defer b.mu.Unlock()
diff --git a/services/bedrock/ghost_row_jkiu_test.go b/services/bedrock/ghost_row_jkiu_test.go
index 8a3d83869e..79f567e7f4 100644
--- a/services/bedrock/ghost_row_jkiu_test.go
+++ b/services/bedrock/ghost_row_jkiu_test.go
@@ -1,7 +1,7 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"testing"
"github.com/stretchr/testify/assert"
@@ -24,7 +24,7 @@ func TestDeleteAutomatedReasoningPolicy_PrunesVersionCounterAndAnnotations(t *te
wf, err := b.StartAutomatedReasoningPolicyBuildWorkflow(
policy.PolicyArn,
"INGEST_CONTENT",
- json.RawMessage(`{}`),
+ jsontext.Value(`{}`),
)
require.NoError(t, err)
@@ -68,7 +68,7 @@ func TestDeleteAutomatedReasoningPolicyBuildWorkflow_PrunesAnnotations(t *testin
wf, err := b.StartAutomatedReasoningPolicyBuildWorkflow(
policy.PolicyArn,
"INGEST_CONTENT",
- json.RawMessage(`{}`),
+ jsontext.Value(`{}`),
)
require.NoError(t, err)
diff --git a/services/bedrock/handler.go b/services/bedrock/handler.go
index 5a129d7615..f559a0b97d 100644
--- a/services/bedrock/handler.go
+++ b/services/bedrock/handler.go
@@ -2,7 +2,7 @@ package bedrock
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
@@ -16,6 +16,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -140,7 +141,7 @@ type isoTime struct {
}
func (t isoTime) MarshalJSON() ([]byte, error) {
- return json.Marshal(t.Time.Format(time.RFC3339))
+ return json.Marshal(t.Time.Format(time.RFC3339), jsonx.V1())
}
// Handler is the Echo HTTP handler for Amazon Bedrock operations.
@@ -704,7 +705,7 @@ func parseBody[T any](body []byte) (*T, error) {
return &v, nil
}
- if err := json.Unmarshal(body, &v); err != nil {
+ if err := json.Unmarshal(body, &v, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/bedrock/handler_automated_reasoning_policies.go b/services/bedrock/handler_automated_reasoning_policies.go
index 2938662c1c..0973ab25ce 100644
--- a/services/bedrock/handler_automated_reasoning_policies.go
+++ b/services/bedrock/handler_automated_reasoning_policies.go
@@ -1,12 +1,14 @@
package bedrock
import (
- "encoding/json"
+ "encoding/json/jsontext"
"net/http"
"strconv"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// extractARPOperation mirrors routeARP's dispatch order exactly (same
@@ -347,12 +349,12 @@ func (h *Handler) routeARPSingleItem(
}
type createAutomatedReasoningPolicyInput struct {
- Name string `json:"name"`
- Description string `json:"description,omitempty"`
- KmsKeyID string `json:"kmsKeyId,omitempty"`
- ClientRequestToken string `json:"clientRequestToken,omitempty"`
- PolicyDefinition json.RawMessage `json:"policyDefinition,omitempty"`
- Tags []Tag `json:"tags,omitempty"`
+ Name string `json:"name"`
+ Description string `json:"description,omitempty"`
+ KmsKeyID string `json:"kmsKeyId,omitempty"`
+ ClientRequestToken string `json:"clientRequestToken,omitempty"`
+ PolicyDefinition jsontext.Value `json:"policyDefinition,omitempty"`
+ Tags []Tag `json:"tags,omitempty"`
}
type createAutomatedReasoningPolicyOutput struct {
@@ -809,9 +811,9 @@ func (h *Handler) handleListAutomatedReasoningPolicies(c *echo.Context) error {
}
type updateARPInput struct {
- Description string `json:"description,omitempty"`
- Name string `json:"name,omitempty"`
- PolicyDefinition json.RawMessage `json:"policyDefinition"`
+ Description string `json:"description,omitempty"`
+ Name string `json:"name,omitempty"`
+ PolicyDefinition jsontext.Value `json:"policyDefinition"`
}
func (h *Handler) handleUpdateAutomatedReasoningPolicy(c *echo.Context, policyARN string, body []byte) error {
@@ -858,7 +860,7 @@ func (h *Handler) handleDeleteAutomatedReasoningPolicy(c *echo.Context, policyAR
func (h *Handler) handleStartARPBuildWorkflow(c *echo.Context, path string, body []byte) error {
policyARN, buildWorkflowType := extractARPBuildWorkflowStart(path)
- if !json.Valid(body) {
+ if !jsontext.Value(body).IsValid(jsonx.V1()) {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid request body"))
}
@@ -873,7 +875,7 @@ func (h *Handler) handleStartARPBuildWorkflow(c *echo.Context, path string, body
return h.Backend.StartAutomatedReasoningPolicyBuildWorkflow(
policyARN,
buildWorkflowType,
- json.RawMessage(body),
+ jsontext.Value(body),
)
},
)
diff --git a/services/bedrock/handler_automated_reasoning_policies_test.go b/services/bedrock/handler_automated_reasoning_policies_test.go
index d93cdbe4ad..4abfd0af58 100644
--- a/services/bedrock/handler_automated_reasoning_policies_test.go
+++ b/services/bedrock/handler_automated_reasoning_policies_test.go
@@ -1,7 +1,7 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/url"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -362,7 +363,7 @@ func TestAccuracy_ARPVersion_ViaHTTP(t *testing.T) {
"/automated-reasoning-policies/"+url.PathEscape(policy.PolicyArn)+"/versions",
map[string]any{"notes": fmt.Sprintf("version %d", i+1)})
require.Equal(t, http.StatusCreated, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lastVersion))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lastVersion, jsonx.V1()))
}
assert.Equal(t, tt.wantLastVersion, lastVersion["version"])
@@ -384,14 +385,14 @@ func TestAccuracy_ARP_DescriptionPreservedOnCreate(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
policyARN := out["policyArn"].(string)
getRec := doRequest(t, h, http.MethodGet, "/automated-reasoning-policies/"+policyARN, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, desc, getOut["description"])
assert.Equal(t, "finance-arp", getOut["name"])
}
@@ -406,7 +407,7 @@ func TestAccuracy_ARP_UpdateDescriptionReflected(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
policyARN := out["policyArn"].(string)
// Update description (real AWS: UpdateAutomatedReasoningPolicy uses PATCH)
@@ -424,7 +425,7 @@ func TestAccuracy_ARP_UpdateDescriptionReflected(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "updated description", getOut["description"])
}
@@ -456,7 +457,7 @@ func TestAccuracy_ARP_VersionNumberingIsolatedPerPolicy(t *testing.T) {
// Verify second version for policy0 has version "2"
var verOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verOut, jsonx.V1()))
// policy1's first version is "1" (isolated counter)
assert.Equal(t, "1", verOut["version"], "policy1 version counter starts from 1 independent of policy0")
@@ -475,7 +476,7 @@ func TestAccuracy_ARP_BuildWorkflowStatusTransitions(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
policyARN := out["policyArn"].(string)
// Add a build workflow via test helper
@@ -488,7 +489,7 @@ func TestAccuracy_ARP_BuildWorkflowStatusTransitions(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "BUILDING", getOut["status"])
}
@@ -504,7 +505,7 @@ func TestAccuracy_ARP_TestCaseRoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
policyARN := out["policyArn"].(string)
// Create test case (needs URL-encoded ARN in path)
@@ -516,7 +517,7 @@ func TestAccuracy_ARP_TestCaseRoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, tcRec.Code)
var tcOut map[string]any
- require.NoError(t, json.Unmarshal(tcRec.Body.Bytes(), &tcOut))
+ require.NoError(t, json.Unmarshal(tcRec.Body.Bytes(), &tcOut, jsonx.V1()))
tcID := tcOut["testCaseId"].(string)
assert.NotEmpty(t, tcID)
@@ -526,7 +527,7 @@ func TestAccuracy_ARP_TestCaseRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, getTestRec.Code)
var getTestOut map[string]any
- require.NoError(t, json.Unmarshal(getTestRec.Body.Bytes(), &getTestOut))
+ require.NoError(t, json.Unmarshal(getTestRec.Body.Bytes(), &getTestOut, jsonx.V1()))
assert.Equal(t, policyARN, getTestOut["policyArn"])
testCase, ok := getTestOut["testCase"].(map[string]any)
require.True(t, ok, "response must wrap the test case under the required testCase key")
@@ -545,7 +546,7 @@ func TestAccuracy_ARP_AnnotationsGetAfterUpdate(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
policyARN := out["policyArn"].(string)
wf := h.Backend.AddBuildWorkflowForTest(policyARN)
@@ -560,7 +561,7 @@ func TestAccuracy_ARP_AnnotationsGetAfterUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, preRec.Code)
var preOut map[string]any
- require.NoError(t, json.Unmarshal(preRec.Body.Bytes(), &preOut))
+ require.NoError(t, json.Unmarshal(preRec.Body.Bytes(), &preOut, jsonx.V1()))
hash, ok := preOut["annotationSetHash"].(string)
require.True(t, ok)
require.NotEmpty(t, hash)
@@ -578,7 +579,7 @@ func TestAccuracy_ARP_AnnotationsGetAfterUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateOut map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut, jsonx.V1()))
assert.Equal(t, policyARN, updateOut["policyArn"])
assert.Equal(t, wf.BuildWorkflowID, updateOut["buildWorkflowId"])
assert.NotEmpty(t, updateOut["annotationSetHash"])
@@ -589,7 +590,7 @@ func TestAccuracy_ARP_AnnotationsGetAfterUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, getAnnRec.Code)
var annOut map[string]any
- require.NoError(t, json.Unmarshal(getAnnRec.Body.Bytes(), &annOut))
+ require.NoError(t, json.Unmarshal(getAnnRec.Body.Bytes(), &annOut, jsonx.V1()))
anns, ok := annOut["annotations"].([]any)
require.True(t, ok)
require.Len(t, anns, 1)
@@ -1021,7 +1022,7 @@ func TestAccuracy_ARP_UpdateOpsRejectOldPUTMethod(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
policyARN := out["policyArn"].(string)
tcRec := doRequest(t, h, http.MethodPost,
@@ -1029,7 +1030,7 @@ func TestAccuracy_ARP_UpdateOpsRejectOldPUTMethod(t *testing.T) {
require.Equal(t, http.StatusCreated, tcRec.Code)
var tcOut map[string]any
- require.NoError(t, json.Unmarshal(tcRec.Body.Bytes(), &tcOut))
+ require.NoError(t, json.Unmarshal(tcRec.Body.Bytes(), &tcOut, jsonx.V1()))
tcID := tcOut["testCaseId"].(string)
wf := h.Backend.AddBuildWorkflowForTest(policyARN)
@@ -1131,7 +1132,7 @@ func TestAccuracy_ARP_BuildWorkflowScopedSubResources(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
policyARN := created["policyArn"].(string)
wf := h.Backend.AddBuildWorkflowForTest(policyARN)
@@ -1141,7 +1142,7 @@ func TestAccuracy_ARP_BuildWorkflowScopedSubResources(t *testing.T) {
require.Equal(t, http.StatusCreated, tcRec.Code)
var tc map[string]any
- require.NoError(t, json.Unmarshal(tcRec.Body.Bytes(), &tc))
+ require.NoError(t, json.Unmarshal(tcRec.Body.Bytes(), &tc, jsonx.V1()))
tcID := tc["testCaseId"].(string)
path := "/automated-reasoning-policies/" + url.PathEscape(policyARN) +
@@ -1165,14 +1166,14 @@ func TestAccuracy_ARP_BuildWorkflowSubResourcesRejectWrongPolicy(t *testing.T) {
require.Equal(t, http.StatusCreated, rec1.Code)
var p1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &p1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &p1, jsonx.V1()))
policy1ARN := p1["policyArn"].(string)
rec2 := doRequest(t, h, http.MethodPost, "/automated-reasoning-policies", map[string]any{"name": "wrong-policy-2"})
require.Equal(t, http.StatusCreated, rec2.Code)
var p2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &p2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &p2, jsonx.V1()))
policy2ARN := p2["policyArn"].(string)
wf := h.Backend.AddBuildWorkflowForTest(policy1ARN)
@@ -1214,7 +1215,7 @@ func TestAccuracy_ARP_ExportVersion(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
policyARN := created["policyArn"].(string)
verRec := doRequest(t, h, http.MethodPost,
@@ -1223,7 +1224,7 @@ func TestAccuracy_ARP_ExportVersion(t *testing.T) {
require.Equal(t, http.StatusCreated, verRec.Code)
var ver map[string]any
- require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &ver))
+ require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &ver, jsonx.V1()))
versionedARN := ver["policyArn"].(string)
exportRec := doRequest(t, h, http.MethodGet,
@@ -1231,7 +1232,7 @@ func TestAccuracy_ARP_ExportVersion(t *testing.T) {
require.Equal(t, http.StatusOK, exportRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(exportRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(exportRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "hash-export", out["definitionHash"])
}
@@ -1249,7 +1250,7 @@ func TestAccuracy_ARP_OldInventedSubResourcePathsGone(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
policyARN := created["policyArn"].(string)
tcRec := doRequest(t, h, http.MethodPost,
@@ -1257,7 +1258,7 @@ func TestAccuracy_ARP_OldInventedSubResourcePathsGone(t *testing.T) {
require.Equal(t, http.StatusCreated, tcRec.Code)
var tc map[string]any
- require.NoError(t, json.Unmarshal(tcRec.Body.Bytes(), &tc))
+ require.NoError(t, json.Unmarshal(tcRec.Body.Bytes(), &tc, jsonx.V1()))
tcID := tc["testCaseId"].(string)
verRec := doRequest(t, h, http.MethodPost,
@@ -1266,7 +1267,7 @@ func TestAccuracy_ARP_OldInventedSubResourcePathsGone(t *testing.T) {
require.Equal(t, http.StatusCreated, verRec.Code)
var ver map[string]any
- require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &ver))
+ require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &ver, jsonx.V1()))
version := ver["version"].(string)
base := "/automated-reasoning-policies/" + url.PathEscape(policyARN)
diff --git a/services/bedrock/handler_custom_model_deployments_test.go b/services/bedrock/handler_custom_model_deployments_test.go
index c2e70dc8b6..6f7a6be407 100644
--- a/services/bedrock/handler_custom_model_deployments_test.go
+++ b/services/bedrock/handler_custom_model_deployments_test.go
@@ -1,7 +1,7 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/url"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -129,7 +130,7 @@ func TestAccuracy_CustomModelDeployment_StatusIsActive(t *testing.T) {
require.Equal(t, http.StatusCreated, modelRec.Code, modelRec.Body.String())
var modelOut map[string]any
- require.NoError(t, json.Unmarshal(modelRec.Body.Bytes(), &modelOut))
+ require.NoError(t, json.Unmarshal(modelRec.Body.Bytes(), &modelOut, jsonx.V1()))
modelARN := modelOut["modelArn"].(string)
// Create deployment for the model
@@ -140,7 +141,7 @@ func TestAccuracy_CustomModelDeployment_StatusIsActive(t *testing.T) {
require.Equal(t, http.StatusCreated, depRec.Code)
var depOut map[string]any
- require.NoError(t, json.Unmarshal(depRec.Body.Bytes(), &depOut))
+ require.NoError(t, json.Unmarshal(depRec.Body.Bytes(), &depOut, jsonx.V1()))
depARN := depOut["customModelDeploymentArn"].(string)
assert.NotEmpty(t, depARN)
@@ -148,7 +149,7 @@ func TestAccuracy_CustomModelDeployment_StatusIsActive(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "my-deployment", getOut["modelDeploymentName"])
// Creating is right immediately after create, but this test's own name
// promises "StatusIsActive" -- an assertion that only checks the first
@@ -162,7 +163,7 @@ func TestAccuracy_CustomModelDeployment_StatusIsActive(t *testing.T) {
require.Equal(t, http.StatusOK, getRec2.Code)
var getOut2 map[string]any
- require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getOut2))
+ require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getOut2, jsonx.V1()))
assert.Equal(t, "Active", getOut2["status"])
}
@@ -184,7 +185,7 @@ func TestAccuracy_CustomModelDeployment_ListAfterMultipleCreates(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
deployments := listOut["modelDeploymentSummaries"].([]any)
assert.GreaterOrEqual(t, len(deployments), 3)
}
@@ -203,7 +204,7 @@ func TestAccuracy_CustomModelDeployment_DeleteRemovesFromList(t *testing.T) {
require.Equal(t, http.StatusCreated, depRec.Code)
var depOut map[string]any
- require.NoError(t, json.Unmarshal(depRec.Body.Bytes(), &depOut))
+ require.NoError(t, json.Unmarshal(depRec.Body.Bytes(), &depOut, jsonx.V1()))
depARN := depOut["customModelDeploymentArn"].(string)
// Delete it
diff --git a/services/bedrock/handler_custom_models_test.go b/services/bedrock/handler_custom_models_test.go
index 49a8cdabcc..cfb2862f1f 100644
--- a/services/bedrock/handler_custom_models_test.go
+++ b/services/bedrock/handler_custom_models_test.go
@@ -1,7 +1,7 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -111,7 +112,7 @@ func TestAccuracy_CustomModel_GetViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, m.ModelArn, out["modelArn"])
assert.Equal(t, tt.modelName, out["modelName"])
assert.NotEmpty(t, out["creationTime"])
@@ -156,7 +157,7 @@ func TestAccuracy_CustomModel_ListViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
models := out["modelSummaries"].([]any)
assert.Len(t, models, tt.wantLen)
})
@@ -200,7 +201,7 @@ func TestAccuracy_CustomModel_ListFilters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
models := out["modelSummaries"].([]any)
gotNames := make([]string, 0, len(models))
@@ -266,7 +267,7 @@ func TestHandler_CustomModelLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
modelARN := createOut["modelArn"].(string)
// List custom models.
@@ -274,7 +275,7 @@ func TestHandler_CustomModelLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listOut, jsonx.V1()))
assert.NotEmpty(t, listOut["modelSummaries"])
// Get custom model by ARN.
diff --git a/services/bedrock/handler_evaluation_jobs_test.go b/services/bedrock/handler_evaluation_jobs_test.go
index 7cbe675512..a9655f3638 100644
--- a/services/bedrock/handler_evaluation_jobs_test.go
+++ b/services/bedrock/handler_evaluation_jobs_test.go
@@ -1,7 +1,8 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/url"
"strconv"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -290,7 +292,7 @@ func TestBatch1_EvaluationJob_RAGConfig_RoundTrip(t *testing.T) {
},
InferenceConfig: &bedrock.EvaluationInferenceConfig{
RagConfigs: []bedrock.RAGConfig{
- {KnowledgeBaseConfig: json.RawMessage(`{"retrieveConfig":{"knowledgeBaseId":"kb-0001"}}`)},
+ {KnowledgeBaseConfig: jsontext.Value(`{"retrieveConfig":{"knowledgeBaseId":"kb-0001"}}`)},
},
},
}
@@ -508,7 +510,7 @@ func TestAccuracy_EvaluationJob_StopTransitionsStatus(t *testing.T) {
require.Equal(t, http.StatusOK, recGet.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "Stopping", out["status"])
})
}
@@ -542,7 +544,7 @@ func TestAccuracy_EvaluationJob_ListResponseShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries := out["jobSummaries"].([]any)
assert.Len(t, summaries, tt.wantLen)
@@ -595,14 +597,14 @@ func TestAccuracy_EvaluationJob_EvaluatorModelConfigPreserved(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
jobArn := out["jobArn"].(string)
getRec := doRequest(t, h, http.MethodGet, "/evaluation-jobs/"+jobArn, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
automated := getOut["evaluationConfig"].(map[string]any)["automated"].(map[string]any)
evaluatorModelConfig := automated["evaluatorModelConfig"].(map[string]any)
@@ -631,14 +633,14 @@ func TestAccuracy_EvaluationJob_InferenceModelsPreserved(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
jobArn := out["jobArn"].(string)
getRec := doRequest(t, h, http.MethodGet, "/evaluation-jobs/"+jobArn, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
inferCfg := getOut["inferenceConfig"].(map[string]any)
models := inferCfg["models"].([]any)
@@ -663,14 +665,14 @@ func TestAccuracy_EvaluationJob_RAGConfigPreserved(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
jobArn := out["jobArn"].(string)
getRec := doRequest(t, h, http.MethodGet, "/evaluation-jobs/"+jobArn, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
inferCfg := getOut["inferenceConfig"].(map[string]any)
ragConfigs := inferCfg["ragConfigs"].([]any)
@@ -707,14 +709,14 @@ func TestAccuracy_EvaluationJob_EvalTaskConfigPreserved(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
jobArn := out["jobArn"].(string)
getRec := doRequest(t, h, http.MethodGet, "/evaluation-jobs/"+jobArn, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
automated := getOut["evaluationConfig"].(map[string]any)["automated"].(map[string]any)
datasetMetricConfigs := automated["datasetMetricConfigs"].([]any)
@@ -738,14 +740,14 @@ func TestAccuracy_EvaluationJob_JobDescriptionPreserved(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
jobArn := out["jobArn"].(string)
getRec := doRequest(t, h, http.MethodGet, "/evaluation-jobs/"+jobArn, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, desc, getOut["jobDescription"])
}
@@ -983,7 +985,7 @@ func TestBatch2Ops_StopEvaluationJob_AlreadyStopped_Rejected(t *testing.T) {
"stop of already-stopped job should return 400 ValidationException")
var errBody map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "ValidationException", errBody["__type"],
"error type must be ValidationException")
})
diff --git a/services/bedrock/handler_foundation_model_agreements_test.go b/services/bedrock/handler_foundation_model_agreements_test.go
index 92e4e92e3d..08b2583576 100644
--- a/services/bedrock/handler_foundation_model_agreements_test.go
+++ b/services/bedrock/handler_foundation_model_agreements_test.go
@@ -1,7 +1,7 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -71,7 +72,7 @@ func TestAccuracy_FoundationModelAgreementOffers_ListRequiresModelID(t *testing.
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "amazon.titan-text-express-v1", out["modelId"])
offers, _ := out["offers"].([]any)
diff --git a/services/bedrock/handler_foundation_models_test.go b/services/bedrock/handler_foundation_models_test.go
index f5a2415973..bf9031b687 100644
--- a/services/bedrock/handler_foundation_models_test.go
+++ b/services/bedrock/handler_foundation_models_test.go
@@ -1,7 +1,7 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/url"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -216,14 +217,14 @@ func TestBatch1_FoundationModel_ResponseStreamingSupported_Bool(t *testing.T) {
var out map[string]any
mustUnmarshal(t, rec, &out)
details := out["modelDetails"].(map[string]any)
- rawBytes, err := json.Marshal(details)
+ rawBytes, err := json.Marshal(details, jsonx.V1())
require.NoError(t, err)
var typed struct {
InferenceTypesSupported []string `json:"inferenceTypesSupported"`
ResponseStreamingSupported bool `json:"responseStreamingSupported"`
}
- require.NoError(t, json.Unmarshal(rawBytes, &typed))
+ require.NoError(t, json.Unmarshal(rawBytes, &typed, jsonx.V1()))
assert.True(t, typed.ResponseStreamingSupported)
assert.NotEmpty(t, typed.InferenceTypesSupported)
@@ -234,13 +235,13 @@ func TestBatch1_FoundationModel_ResponseStreamingSupported_Bool(t *testing.T) {
var out2 map[string]any
mustUnmarshal(t, rec2, &out2)
details2 := out2["modelDetails"].(map[string]any)
- rawBytes2, err := json.Marshal(details2)
+ rawBytes2, err := json.Marshal(details2, jsonx.V1())
require.NoError(t, err)
var typed2 struct {
ResponseStreamingSupported bool `json:"responseStreamingSupported"`
}
- require.NoError(t, json.Unmarshal(rawBytes2, &typed2))
+ require.NoError(t, json.Unmarshal(rawBytes2, &typed2, jsonx.V1()))
assert.False(t, typed2.ResponseStreamingSupported)
}
@@ -270,7 +271,7 @@ func TestAccuracy_FoundationModel_ListIsNonEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries := out["modelSummaries"].([]any)
assert.NotEmpty(t, summaries, "foundation models must be seeded on init")
})
@@ -294,7 +295,7 @@ func TestAccuracy_FoundationModel_FieldsPresentOnList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries := out["modelSummaries"].([]any)
require.NotEmpty(t, summaries)
@@ -336,7 +337,7 @@ func TestParity_FoundationModel_ARNIncludesRegion(t *testing.T) {
ModelID string `json:"modelId"`
} `json:"modelDetails"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp.ModelDetails.ModelArn
assert.True(t, strings.HasPrefix(arn, "arn:aws:bedrock:us-east-1::foundation-model/"),
@@ -363,7 +364,7 @@ func TestParity_ListFoundationModels_ARNFormat(t *testing.T) {
ModelArn string `json:"modelArn"`
} `json:"modelSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotEmpty(t, resp.ModelSummaries, "ListFoundationModels must return at least one model")
for _, m := range resp.ModelSummaries {
@@ -402,7 +403,7 @@ func TestParity_FoundationModel_ModelLifecyclePresent(t *testing.T) {
} `json:"modelLifecycle"`
} `json:"modelDetails"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ACTIVE", resp.ModelDetails.ModelLifecycle.Status,
"modelLifecycle.status must be ACTIVE for available models")
@@ -427,7 +428,7 @@ func TestParity_ListFoundationModels_ModelLifecyclePresent(t *testing.T) {
ModelID string `json:"modelId"`
} `json:"modelSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotEmpty(t, resp.ModelSummaries)
for _, m := range resp.ModelSummaries {
@@ -470,7 +471,7 @@ func TestParity_GetFoundationModel_ByARN(t *testing.T) {
ModelArn string `json:"modelArn"`
} `json:"modelDetails"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.modelID, resp.ModelDetails.ModelID,
"GetFoundationModel by ARN must return the correct model")
assert.Equal(t, expectedARN, resp.ModelDetails.ModelArn,
@@ -501,7 +502,7 @@ func TestParity_GetFoundationModel_NotFound(t *testing.T) {
assert.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
errType, _ := resp["__type"].(string)
assert.Contains(t, errType, "ResourceNotFoundException")
})
diff --git a/services/bedrock/handler_guardrails_test.go b/services/bedrock/handler_guardrails_test.go
index c38a5e3c6b..89be258aa9 100644
--- a/services/bedrock/handler_guardrails_test.go
+++ b/services/bedrock/handler_guardrails_test.go
@@ -2,7 +2,7 @@ package bedrock_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -1081,7 +1082,7 @@ func TestAccuracy_GuardrailVersion_HTTPCreate(t *testing.T) {
"/guardrails/"+g.GuardrailID,
map[string]any{"description": fmt.Sprintf("v%d", i+1)})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lastOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lastOut, jsonx.V1()))
}
assert.Equal(t, tt.wantVersion, lastOut["version"])
@@ -1137,7 +1138,7 @@ func TestAccuracy_Guardrail_UpdatePreservesAllFields(t *testing.T) {
require.Equal(t, http.StatusOK, recGet.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.newName, out["name"])
assert.Equal(t, tt.newDesc, out["description"])
})
@@ -1178,7 +1179,7 @@ func TestAccuracy_Tags_OnGuardrailViaListTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
returnedTags := out["tags"].([]any)
assert.Len(t, returnedTags, tt.tagCount)
})
@@ -1220,7 +1221,7 @@ func TestAccuracy_Tags_TagGuardrailAfterCreate(t *testing.T) {
require.Equal(t, http.StatusOK, recList.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &out, jsonx.V1()))
returnedTags := out["tags"].([]any)
assert.Len(t, returnedTags, 1)
assert.Equal(t, tt.key, returnedTags[0].(map[string]any)["key"])
@@ -1246,7 +1247,7 @@ func TestAccuracy_GuardrailVersion_PoliciesPreservedInVersion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
guardrailID := out["guardrailId"].(string)
// Create a version
@@ -1255,7 +1256,7 @@ func TestAccuracy_GuardrailVersion_PoliciesPreservedInVersion(t *testing.T) {
require.Equal(t, http.StatusOK, versionRec.Code)
var verOut map[string]any
- require.NoError(t, json.Unmarshal(versionRec.Body.Bytes(), &verOut))
+ require.NoError(t, json.Unmarshal(versionRec.Body.Bytes(), &verOut, jsonx.V1()))
version := verOut["version"].(string)
assert.NotEqual(t, "DRAFT", version)
@@ -1264,6 +1265,6 @@ func TestAccuracy_GuardrailVersion_PoliciesPreservedInVersion(t *testing.T) {
require.Equal(t, http.StatusOK, getVerRec.Code)
var verDetailOut map[string]any
- require.NoError(t, json.Unmarshal(getVerRec.Body.Bytes(), &verDetailOut))
+ require.NoError(t, json.Unmarshal(getVerRec.Body.Bytes(), &verDetailOut, jsonx.V1()))
assert.NotNil(t, verDetailOut["contentPolicy"], "version should preserve policies")
}
diff --git a/services/bedrock/handler_inference_profiles_test.go b/services/bedrock/handler_inference_profiles_test.go
index 394293d713..0a17521cf9 100644
--- a/services/bedrock/handler_inference_profiles_test.go
+++ b/services/bedrock/handler_inference_profiles_test.go
@@ -1,7 +1,7 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -49,7 +50,7 @@ func TestAccuracy_InferenceProfile_CreateResponseShape(t *testing.T) {
if tt.wantARN {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["inferenceProfileArn"])
assert.Equal(t, tt.wantProfileStatus, out["status"])
}
@@ -141,7 +142,7 @@ func TestAccuracy_InferenceProfile_GetReturnsAllFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, p.InferenceProfileArn, out["inferenceProfileArn"])
assert.Equal(t, p.InferenceProfileID, out["inferenceProfileId"])
@@ -198,7 +199,7 @@ func TestAccuracy_InferenceProfile_ListContainsCreated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries := out["inferenceProfileSummaries"].([]any)
assert.Len(t, summaries, len(tt.profileNames))
@@ -246,7 +247,7 @@ func TestAccuracy_InferenceProfile_ListTypeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries := out["inferenceProfileSummaries"].([]any)
assert.Len(t, summaries, tt.wantLen)
})
@@ -270,7 +271,7 @@ func TestAccuracy_InferenceProfile_DeleteRemovesFromList(t *testing.T) {
recList := doRequest(t, h, http.MethodGet, "/inference-profiles", nil)
require.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
assert.Empty(t, listOut["inferenceProfileSummaries"].([]any))
}
@@ -344,7 +345,7 @@ func TestHandler_InferenceProfileLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
profileARN := createOut["inferenceProfileArn"].(string)
// List profiles.
@@ -352,7 +353,7 @@ func TestHandler_InferenceProfileLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listOut, jsonx.V1()))
assert.NotEmpty(t, listOut["inferenceProfileSummaries"])
// Get profile.
diff --git a/services/bedrock/handler_marketplace_model_endpoints_test.go b/services/bedrock/handler_marketplace_model_endpoints_test.go
index 68ade9382b..de7f1d52e5 100644
--- a/services/bedrock/handler_marketplace_model_endpoints_test.go
+++ b/services/bedrock/handler_marketplace_model_endpoints_test.go
@@ -1,7 +1,7 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -50,7 +51,7 @@ func TestAccuracy_MarketplaceEndpoint_CreateStartsAsCreating(t *testing.T) {
if tt.wantStatus == http.StatusCreated {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
ep := out["marketplaceModelEndpoint"].(map[string]any)
assert.Equal(t, "REGISTERED", ep["status"])
assert.Equal(t, "Creating", ep["endpointStatus"])
@@ -185,7 +186,7 @@ func TestAccuracy_MarketplaceEndpoint_ListResponseShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
endpoints := out["marketplaceModelEndpoints"].([]any)
assert.Len(t, endpoints, 3)
@@ -243,7 +244,7 @@ func TestAccuracy_MarketplaceEndpoint_ListModelSourceFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
endpoints := out["marketplaceModelEndpoints"].([]any)
gotSources := make([]string, 0, len(endpoints))
@@ -277,7 +278,7 @@ func TestAccuracy_MarketplaceEndpoint_DeleteRemovesFromList(t *testing.T) {
require.Equal(t, http.StatusOK, recList.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out["marketplaceModelEndpoints"].([]any))
}
@@ -299,7 +300,7 @@ func TestAccuracy_MarketplaceEndpoint_UpdateReturnsEndpoint(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
updated := out["marketplaceModelEndpoint"].(map[string]any)
assert.Equal(t, ep.EndpointArn, updated["endpointArn"])
assert.NotEmpty(t, updated["updatedAt"])
@@ -428,7 +429,7 @@ func TestHandler_MarketplaceModelEndpointLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
endpointARN := createOut["marketplaceModelEndpoint"].(map[string]any)["endpointArn"].(string)
// List endpoints.
diff --git a/services/bedrock/handler_model_copy_jobs_test.go b/services/bedrock/handler_model_copy_jobs_test.go
index a736c78647..44e84b7242 100644
--- a/services/bedrock/handler_model_copy_jobs_test.go
+++ b/services/bedrock/handler_model_copy_jobs_test.go
@@ -1,7 +1,7 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -156,7 +157,7 @@ func TestParity_ValidModelCopyJob_Returns201(t *testing.T) {
assert.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "jobArn", "successful response must include jobArn")
}
diff --git a/services/bedrock/handler_model_customization_jobs_test.go b/services/bedrock/handler_model_customization_jobs_test.go
index 7d2cf485fa..d3b78e175f 100644
--- a/services/bedrock/handler_model_customization_jobs_test.go
+++ b/services/bedrock/handler_model_customization_jobs_test.go
@@ -1,7 +1,7 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/url"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -239,7 +240,7 @@ func TestAccuracy_CustomizationJob_StopTransitionsStatus(t *testing.T) {
require.Equal(t, http.StatusOK, recGet.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "Stopping", out["status"])
})
}
@@ -284,7 +285,7 @@ func TestAccuracy_CustomizationJob_ListViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries := out["modelCustomizationJobSummaries"].([]any)
assert.Len(t, summaries, tt.wantLen)
@@ -347,7 +348,7 @@ func TestAccuracy_CustomizationJob_ListFilters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries := out["modelCustomizationJobSummaries"].([]any)
gotNames := make([]string, 0, len(summaries))
@@ -447,7 +448,7 @@ func TestAccuracy_CustomizationJob_CustomizationTypePreserved(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
jobARN := createOut["jobArn"].(string)
recGet := doRequest(
@@ -460,7 +461,7 @@ func TestAccuracy_CustomizationJob_CustomizationTypePreserved(t *testing.T) {
require.Equal(t, http.StatusOK, recGet.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.customizationType, out["customizationType"])
})
}
@@ -481,7 +482,7 @@ func TestHandler_ModelCustomizationJobLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
jobARN := createOut["jobArn"].(string)
assert.NotEmpty(t, jobARN)
@@ -490,7 +491,7 @@ func TestHandler_ModelCustomizationJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listOut, jsonx.V1()))
assert.NotEmpty(t, listOut["modelCustomizationJobSummaries"])
// Get job.
diff --git a/services/bedrock/handler_model_import_jobs_test.go b/services/bedrock/handler_model_import_jobs_test.go
index cf2cb88982..8fe2907434 100644
--- a/services/bedrock/handler_model_import_jobs_test.go
+++ b/services/bedrock/handler_model_import_jobs_test.go
@@ -1,7 +1,7 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -185,7 +186,7 @@ func TestAccuracy_ImportedModel_VisibleAfterImportJob(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
models := out["modelSummaries"].([]any)
assert.Len(t, models, 1)
@@ -195,7 +196,7 @@ func TestAccuracy_ImportedModel_VisibleAfterImportJob(t *testing.T) {
require.Equal(t, http.StatusOK, recGet.Code)
var modelOut map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &modelOut))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &modelOut, jsonx.V1()))
assert.Equal(t, job.ImportedModelArn, modelOut["modelArn"])
assert.Equal(t, tt.jobName+"-model", modelOut["modelName"])
assert.Equal(t, job.JobArn, modelOut["jobArn"])
@@ -223,7 +224,7 @@ func TestAccuracy_ImportedModel_DeleteRemovesFromList(t *testing.T) {
require.Equal(t, http.StatusOK, recList.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out["modelSummaries"].([]any))
}
@@ -240,7 +241,7 @@ func TestAccuracy_ModelImportJob_EndTimeAbsentWhileInProgress(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "InProgress", out["status"])
_, hasEndTime := out["endTime"]
assert.False(t, hasEndTime, "endTime must not be present while InProgress")
@@ -282,14 +283,14 @@ func TestAccuracy_ModelImportJob_ImportedModelStatus(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
jobARN := out["jobArn"].(string)
getRec := doRequest(t, h, http.MethodGet, "/model-import-jobs/"+jobARN, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
// Status should be one of the valid statuses
assert.Contains(t, []any{"InProgress", "Completed", "Failed"}, getOut["status"])
@@ -307,14 +308,14 @@ func TestAccuracy_ModelImportJob_TagsPreserved(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
jobARN := out["jobArn"].(string)
getRec := doRequest(t, h, http.MethodGet, "/model-import-jobs/"+jobARN, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "tagged-import", getOut["jobName"])
}
@@ -330,7 +331,7 @@ func TestAccuracy_ImportedModel_AvailableAfterJobComplete(t *testing.T) {
require.Equal(t, http.StatusCreated, importRec.Code)
var importOut map[string]any
- require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importOut))
+ require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importOut, jsonx.V1()))
jobARN := importOut["jobArn"].(string)
// Advance job status to complete (zero duration = immediate)
@@ -341,7 +342,7 @@ func TestAccuracy_ImportedModel_AvailableAfterJobComplete(t *testing.T) {
require.Equal(t, http.StatusOK, getJobRec.Code)
var jobOut map[string]any
- require.NoError(t, json.Unmarshal(getJobRec.Body.Bytes(), &jobOut))
+ require.NoError(t, json.Unmarshal(getJobRec.Body.Bytes(), &jobOut, jsonx.V1()))
importedModelARN := jobOut["importedModelArn"].(string)
assert.NotEmpty(t, importedModelARN)
@@ -350,7 +351,7 @@ func TestAccuracy_ImportedModel_AvailableAfterJobComplete(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
models := listOut["modelSummaries"].([]any)
assert.NotEmpty(t, models)
}
diff --git a/services/bedrock/handler_model_invocation_jobs_test.go b/services/bedrock/handler_model_invocation_jobs_test.go
index 123bae51e7..96dd0ea179 100644
--- a/services/bedrock/handler_model_invocation_jobs_test.go
+++ b/services/bedrock/handler_model_invocation_jobs_test.go
@@ -1,7 +1,7 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -43,7 +44,7 @@ func TestAccuracy_ModelInvocationJob_StopTransitionsStatus(t *testing.T) {
require.Equal(t, http.StatusOK, recGet.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "Stopping", out["status"])
})
}
@@ -77,7 +78,7 @@ func TestAccuracy_ModelInvocationJob_ListViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries := out["invocationJobSummaries"].([]any)
assert.Len(t, summaries, tt.wantLen)
})
@@ -99,14 +100,14 @@ func TestAccuracy_ModelInvocationJob_JobNamePreserved(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
jobArn := out["jobArn"].(string)
getRec := doRequest(t, h, http.MethodGet, "/model-invocation-job/"+jobArn, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, jobName, getOut["jobName"])
})
}
@@ -121,14 +122,14 @@ func TestAccuracy_ModelInvocationJob_StatusProgression(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
jobArn := out["jobArn"].(string)
getRec := doRequest(t, h, http.MethodGet, "/model-invocation-job/"+jobArn, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Contains(t, []any{"Submitted", "InProgress", "Completed", "Failed", "Stopped"},
getOut["status"], "status should be a valid model invocation job status")
}
@@ -142,7 +143,7 @@ func TestAccuracy_ModelInvocationJob_StopChangesStatus(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
jobArn := out["jobArn"].(string)
stopRec := doRequest(t, h, http.MethodPost, "/model-invocation-job/"+jobArn+"/stop", nil)
@@ -152,7 +153,7 @@ func TestAccuracy_ModelInvocationJob_StopChangesStatus(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "Stopping", getOut["status"])
}
@@ -248,7 +249,7 @@ func TestBatch2Ops_StopModelInvocationJob_AlreadyStopped_Rejected(t *testing.T)
"stop of already-stopped invocation job should return 400 ValidationException")
var errBody map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "ValidationException", errBody["__type"])
})
}
diff --git a/services/bedrock/handler_model_invocation_logging_test.go b/services/bedrock/handler_model_invocation_logging_test.go
index 141120b756..e77b2a6f95 100644
--- a/services/bedrock/handler_model_invocation_logging_test.go
+++ b/services/bedrock/handler_model_invocation_logging_test.go
@@ -1,12 +1,14 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestAccuracy_LoggingConfig_RoundTrip proves the real
@@ -36,7 +38,7 @@ func TestAccuracy_LoggingConfig_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, recGet.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out, jsonx.V1()))
cfg, ok := out["loggingConfig"].(map[string]any)
require.True(t, ok, "loggingConfig must be present after Put")
@@ -73,7 +75,7 @@ func TestAccuracy_LoggingConfig_CloudWatchConfigRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, recGet.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out, jsonx.V1()))
cfg, ok := out["loggingConfig"].(map[string]any)
require.True(t, ok)
@@ -109,7 +111,7 @@ func TestAccuracy_LoggingConfig_DeleteClearsConfig(t *testing.T) {
require.Equal(t, http.StatusOK, recGet.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out, jsonx.V1()))
assert.Nil(t, out["loggingConfig"], "Delete must clear the config back to absent, not an empty object")
}
@@ -125,6 +127,6 @@ func TestAccuracy_LoggingConfig_GetBeforePutOmitsLoggingConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Nil(t, out["loggingConfig"])
}
diff --git a/services/bedrock/handler_prompt_routers_test.go b/services/bedrock/handler_prompt_routers_test.go
index 759e9301ad..c943fb865a 100644
--- a/services/bedrock/handler_prompt_routers_test.go
+++ b/services/bedrock/handler_prompt_routers_test.go
@@ -1,7 +1,7 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -78,7 +79,7 @@ func TestAccuracy_PromptRouter_CreateResponseShape(t *testing.T) {
if tt.wantRouterID {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["promptRouterArn"])
}
})
@@ -155,7 +156,7 @@ func TestAccuracy_PromptRouter_GetAndListAccuracy(t *testing.T) {
require.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
routers := listOut["promptRouterSummaries"].([]any)
assert.Len(t, routers, tt.wantLen)
@@ -165,7 +166,7 @@ func TestAccuracy_PromptRouter_GetAndListAccuracy(t *testing.T) {
require.Equal(t, http.StatusOK, recGet.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, lastARN, getOut["promptRouterArn"])
assert.Equal(t, "AVAILABLE", getOut["status"])
assert.Equal(t, "custom", getOut["type"])
@@ -232,7 +233,7 @@ func TestAccuracy_PromptRouter_NamePreservedInGetAndList(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
routerARN := out["promptRouterArn"].(string)
assert.NotEmpty(t, routerARN)
@@ -240,7 +241,7 @@ func TestAccuracy_PromptRouter_NamePreservedInGetAndList(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, tt.routerName, getOut["promptRouterName"])
assert.Equal(t, "AVAILABLE", getOut["status"])
})
@@ -260,7 +261,7 @@ func TestAccuracy_PromptRouter_TagsPreserved(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
routerARN := out["promptRouterArn"].(string)
getRec := doRequest(t, h, http.MethodGet, "/prompt-routers/"+routerARN, nil)
@@ -268,7 +269,7 @@ func TestAccuracy_PromptRouter_TagsPreserved(t *testing.T) {
// Router ARN is returned and non-empty
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "AVAILABLE", getOut["status"])
}
@@ -297,7 +298,7 @@ func TestAccuracy_PromptRouter_ListAfterDelete(t *testing.T) {
require.Equal(t, http.StatusCreated, delRec.Code)
var delOut map[string]any
- require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &delOut))
+ require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &delOut, jsonx.V1()))
delARN := delOut["promptRouterArn"].(string)
// Delete it
@@ -309,7 +310,7 @@ func TestAccuracy_PromptRouter_ListAfterDelete(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
routers := listOut["promptRouterSummaries"].([]any)
for _, r := range routers {
diff --git a/services/bedrock/handler_provisioned_throughput_test.go b/services/bedrock/handler_provisioned_throughput_test.go
index 26dd18df1a..2775344e6b 100644
--- a/services/bedrock/handler_provisioned_throughput_test.go
+++ b/services/bedrock/handler_provisioned_throughput_test.go
@@ -2,7 +2,7 @@ package bedrock_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -533,7 +534,7 @@ func TestParity_ListProvisionedModelThroughputs_NameContainsFilter(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries, ok := out["provisionedModelSummaries"].([]any)
require.True(t, ok)
require.Len(t, summaries, 1)
diff --git a/services/bedrock/handler_test.go b/services/bedrock/handler_test.go
index 55fecb2ea1..dd16f77b34 100644
--- a/services/bedrock/handler_test.go
+++ b/services/bedrock/handler_test.go
@@ -2,7 +2,7 @@ package bedrock_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -571,7 +572,7 @@ func TestHandler_RealSDKWireShapeRouteMatcher(t *testing.T) { //nolint:parallelt
require.NoError(t, h.Handler()(c2))
var out map[string]string
- _ = json.Unmarshal(rec2.Body.Bytes(), &out)
+ _ = json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1())
assert.NotEqual(
t, "UnknownOperationException", out["__type"],
"path/method must be dispatched to a real handler, not fall through as unrouted",
@@ -714,7 +715,7 @@ func TestParity_ValidationException_Returns400(t *testing.T) {
"ValidationException must return 400, not 500")
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
errType, _ := resp["__type"].(string)
assert.Contains(t, errType, "ValidationException",
@@ -772,7 +773,7 @@ func TestParity_ValidationException_ErrorShape(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasType := resp["__type"]
assert.True(t, hasType, "error response must have '__type' field")
diff --git a/services/bedrock/list_summary_shapes_test.go b/services/bedrock/list_summary_shapes_test.go
index c7be7b2e56..6ac44223c6 100644
--- a/services/bedrock/list_summary_shapes_test.go
+++ b/services/bedrock/list_summary_shapes_test.go
@@ -1,12 +1,15 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// listItems unmarshals a List response body and returns the array found
@@ -14,14 +17,14 @@ import (
func listItems(t *testing.T, body []byte, key string) []map[string]any {
t.Helper()
- var resp map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(body, &resp))
+ var resp map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
raw, ok := resp[key]
require.True(t, ok, "response missing key %q", key)
var items []map[string]any
- require.NoError(t, json.Unmarshal(raw, &items))
+ require.NoError(t, json.Unmarshal(raw, &items, jsonx.V1()))
return items
}
diff --git a/services/bedrock/models.go b/services/bedrock/models.go
index 49ed6f555d..8ab9d95063 100644
--- a/services/bedrock/models.go
+++ b/services/bedrock/models.go
@@ -1,7 +1,7 @@
package bedrock
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -252,7 +252,7 @@ type EvaluationDatasetMetricConfig struct {
type AutomatedEvaluationConfig struct {
EvaluatorModelConfig *EvaluatorModelConfig `json:"evaluatorModelConfig,omitempty"`
DatasetMetricConfigs []EvaluationDatasetMetricConfig `json:"datasetMetricConfigs,omitempty"`
- CustomMetricConfig json.RawMessage `json:"customMetricConfig,omitempty"`
+ CustomMetricConfig jsontext.Value `json:"customMetricConfig,omitempty"`
}
// HumanEvaluationCustomMetric mirrors types.HumanEvaluationCustomMetric
@@ -366,8 +366,8 @@ func (c *EvaluationConfig) EvaluatorModelIdentifiers() []string {
// than partially modeled, same rationale as AutomatedEvaluationConfig.
// CustomMetricConfig above.
type RAGConfig struct {
- KnowledgeBaseConfig json.RawMessage `json:"knowledgeBaseConfig,omitempty"`
- PrecomputedRagSourceConfig json.RawMessage `json:"precomputedRagSourceConfig,omitempty"`
+ KnowledgeBaseConfig jsontext.Value `json:"knowledgeBaseConfig,omitempty"`
+ PrecomputedRagSourceConfig jsontext.Value `json:"precomputedRagSourceConfig,omitempty"`
}
// EvaluationInferenceConfig models bedrock's inferenceConfig union
@@ -408,13 +408,13 @@ type AutomatedReasoningPolicy struct {
// api_op_UpdateAutomatedReasoningPolicy.go:37-63). Kept as raw JSON rather
// than the real nested rules/types/variables union -- inert, never
// interpreted, but genuinely the caller's own content, not fabricated.
- PolicyDefinition json.RawMessage `json:"policyDefinition,omitempty"`
- Description string `json:"description,omitempty"`
- Status string `json:"status"`
- DefinitionHash string `json:"definitionHash,omitempty"`
- Version string `json:"version,omitempty"`
- KmsKeyArn string `json:"kmsKeyArn,omitempty"`
- Tags []Tag `json:"tags,omitempty"`
+ PolicyDefinition jsontext.Value `json:"policyDefinition,omitempty"`
+ Description string `json:"description,omitempty"`
+ Status string `json:"status"`
+ DefinitionHash string `json:"definitionHash,omitempty"`
+ Version string `json:"version,omitempty"`
+ KmsKeyArn string `json:"kmsKeyArn,omitempty"`
+ Tags []Tag `json:"tags,omitempty"`
}
// AutomatedReasoningPolicyBuildWorkflow represents a build workflow for a policy.
@@ -430,8 +430,8 @@ type AutomatedReasoningPolicyBuildWorkflow struct {
// label, sourceContent is the entire JSON payload). SourceContent is
// stored verbatim and never interpreted -- same rationale as
// AutomatedReasoningPolicy.PolicyDefinition above.
- BuildWorkflowType string `json:"buildWorkflowType,omitempty"`
- SourceContent json.RawMessage `json:"sourceContent,omitempty"`
+ BuildWorkflowType string `json:"buildWorkflowType,omitempty"`
+ SourceContent jsontext.Value `json:"sourceContent,omitempty"`
}
// AutomatedReasoningPolicyTestCase represents a test case for a policy.
diff --git a/services/bedrock/persistence.go b/services/bedrock/persistence.go
index 82dd1eb414..d24caa025a 100644
--- a/services/bedrock/persistence.go
+++ b/services/bedrock/persistence.go
@@ -7,7 +7,7 @@ package bedrock
// [github.com/blackbirdworks/gopherstack/pkgs/persistence.Persistable].
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -75,7 +75,7 @@ const bedrockSnapshotVersion = 3
// CreateGuardrailVersion call reissues -- a real data-corruption risk, not
// just a cosmetic gap.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
LoggingConfig *ModelInvocationLoggingConfiguration `json:"loggingConfig,omitempty"`
GuardrailsByName map[string]string `json:"guardrailsByName"`
GuardrailsByARN map[string]string `json:"guardrailsByARN"`
diff --git a/services/bedrock/persistence_test.go b/services/bedrock/persistence_test.go
index 17c7ada266..2fcc73f0a1 100644
--- a/services/bedrock/persistence_test.go
+++ b/services/bedrock/persistence_test.go
@@ -2,7 +2,7 @@ package bedrock_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"testing"
"github.com/stretchr/testify/assert"
@@ -200,7 +200,7 @@ func seedJobResources(
require.NoError(t, err)
wf, err := b.StartAutomatedReasoningPolicyBuildWorkflow(
- arp.PolicyArn, "INGEST_CONTENT", json.RawMessage(`{}`),
+ arp.PolicyArn, "INGEST_CONTENT", jsontext.Value(`{}`),
)
require.NoError(t, err)
diff --git a/services/bedrock/request_realism_test.go b/services/bedrock/request_realism_test.go
index dea9697560..2a1113fa33 100644
--- a/services/bedrock/request_realism_test.go
+++ b/services/bedrock/request_realism_test.go
@@ -1,7 +1,7 @@
package bedrock_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -33,7 +34,7 @@ func TestRequestRealism_ErrorMessageHasNoCodePrefix(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", out["__type"])
assert.NotContains(t, out["message"], "ResourceNotFoundException")
})
diff --git a/services/bedrock/test_helpers_test.go b/services/bedrock/test_helpers_test.go
index 413b211749..9a5b843e1d 100644
--- a/services/bedrock/test_helpers_test.go
+++ b/services/bedrock/test_helpers_test.go
@@ -2,13 +2,14 @@ package bedrock_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http/httptest"
"testing"
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -72,7 +73,7 @@ func doRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -91,7 +92,7 @@ func doRequest(
func mustUnmarshal(t *testing.T, rec *httptest.ResponseRecorder, v any) {
t.Helper()
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), v))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), v, jsonx.V1()))
}
func typesAsStrings(raw []any) []string {
diff --git a/services/bedrockagent/delete_output_shape_test.go b/services/bedrockagent/delete_output_shape_test.go
index cf47f1fb17..4ac4b9d403 100644
--- a/services/bedrockagent/delete_output_shape_test.go
+++ b/services/bedrockagent/delete_output_shape_test.go
@@ -1,7 +1,7 @@
package bedrockagent_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"sort"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrockagent"
)
@@ -86,7 +87,7 @@ func TestDeleteOps_ExactWireKeySet(t *testing.T) {
require.Equal(t, http.StatusOK, agRec.Code, agRec.Body.String())
var agOut map[string]map[string]any
- require.NoError(t, json.Unmarshal(agRec.Body.Bytes(), &agOut))
+ require.NoError(t, json.Unmarshal(agRec.Body.Bytes(), &agOut, jsonx.V1()))
groupID, _ := agOut["agentActionGroup"]["actionGroupId"].(string)
require.NotEmpty(t, groupID)
@@ -121,7 +122,7 @@ func TestDeleteOps_ExactWireKeySet(t *testing.T) {
require.Equal(t, http.StatusCreated, verRec.Code, verRec.Body.String())
var verBody map[string]any
- require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &verBody))
+ require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &verBody, jsonx.V1()))
version, _ := verBody["version"].(string)
require.NotEmpty(t, version)
@@ -143,7 +144,7 @@ func TestDeleteOps_ExactWireKeySet(t *testing.T) {
require.Equal(t, http.StatusCreated, aliasRec.Code, aliasRec.Body.String())
var aliasBody map[string]any
- require.NoError(t, json.Unmarshal(aliasRec.Body.Bytes(), &aliasBody))
+ require.NoError(t, json.Unmarshal(aliasRec.Body.Bytes(), &aliasBody, jsonx.V1()))
aliasID, _ := aliasBody["id"].(string)
require.NotEmpty(t, aliasID)
@@ -242,7 +243,7 @@ func TestDeleteOps_ExactWireKeySet(t *testing.T) {
require.Equal(t, http.StatusCreated, verRec.Code, verRec.Body.String())
var verBody map[string]any
- require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &verBody))
+ require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &verBody, jsonx.V1()))
version, _ := verBody["version"].(string)
require.NotEmpty(t, version)
@@ -269,7 +270,7 @@ func TestDeleteOps_ExactWireKeySet(t *testing.T) {
}
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
gotKeys := make([]string, 0, len(out))
for k := range out {
@@ -296,7 +297,7 @@ func createTestAgentForDeleteSweep(t *testing.T, h *bedrockagent.Handler, e *ech
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
agentID, _ := out["agent"]["agentId"].(string)
require.NotEmpty(t, agentID)
@@ -318,7 +319,7 @@ func createTestAgentAliasForDeleteSweep(
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
aliasID, _ := out["agentAlias"]["agentAliasId"].(string)
require.NotEmpty(t, aliasID)
@@ -342,7 +343,7 @@ func createTestFlowForDeleteSweep(t *testing.T, h *bedrockagent.Handler, e *echo
require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
flowID, _ := out["id"].(string)
require.NotEmpty(t, flowID)
@@ -361,7 +362,7 @@ func createTestKBForDeleteSweep(t *testing.T, h *bedrockagent.Handler, e *echo.E
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
kbID, _ := out["knowledgeBase"]["knowledgeBaseId"].(string)
require.NotEmpty(t, kbID)
@@ -378,7 +379,7 @@ func createTestDSForDeleteSweep(t *testing.T, h *bedrockagent.Handler, e *echo.E
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
dsID, _ := out["dataSource"]["dataSourceId"].(string)
require.NotEmpty(t, dsID)
@@ -398,7 +399,7 @@ func createTestPromptForDeleteSweep(t *testing.T, h *bedrockagent.Handler, e *ec
require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
promptID, _ := out["id"].(string)
require.NotEmpty(t, promptID)
diff --git a/services/bedrockagent/delete_version_in_use_test.go b/services/bedrockagent/delete_version_in_use_test.go
index 681b23de3c..3eb7b34528 100644
--- a/services/bedrockagent/delete_version_in_use_test.go
+++ b/services/bedrockagent/delete_version_in_use_test.go
@@ -2,7 +2,7 @@ package bedrockagent_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrockagent"
)
@@ -150,7 +151,7 @@ func TestHandleDeleteAgentVersion_ConflictOverHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code, createRec.Body.String())
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
agentWrap, _ := created["agent"].(map[string]any)
agentID, _ := agentWrap["agentId"].(string)
require.NotEmpty(t, agentID)
@@ -161,7 +162,7 @@ func TestHandleDeleteAgentVersion_ConflictOverHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, aliasRec.Code, aliasRec.Body.String())
var aliasBody map[string]any
- require.NoError(t, json.Unmarshal(aliasRec.Body.Bytes(), &aliasBody))
+ require.NoError(t, json.Unmarshal(aliasRec.Body.Bytes(), &aliasBody, jsonx.V1()))
aliasWrap, _ := aliasBody["agentAlias"].(map[string]any)
routing, _ := aliasWrap["routingConfiguration"].([]any)
require.NotEmpty(t, routing)
diff --git a/services/bedrockagent/handler_agent_action_groups.go b/services/bedrockagent/handler_agent_action_groups.go
index e25a1810b2..0603556b9d 100644
--- a/services/bedrockagent/handler_agent_action_groups.go
+++ b/services/bedrockagent/handler_agent_action_groups.go
@@ -2,10 +2,12 @@ package bedrockagent
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -27,7 +29,7 @@ func (h *Handler) handleCreateAgentActionGroup(
ParentActionGroupSignature string `json:"parentActionGroupSignature"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
@@ -76,7 +78,7 @@ func (h *Handler) handleUpdateAgentActionGroup(
ParentActionGroupSignature string `json:"parentActionGroupSignature"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
diff --git a/services/bedrockagent/handler_agent_action_groups_test.go b/services/bedrockagent/handler_agent_action_groups_test.go
index 0e38088d97..0ca51f2e3f 100644
--- a/services/bedrockagent/handler_agent_action_groups_test.go
+++ b/services/bedrockagent/handler_agent_action_groups_test.go
@@ -1,9 +1,11 @@
package bedrockagent_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestCreateAgentActionGroupVersion locks in real AWS's constraint on the
@@ -31,7 +33,7 @@ func TestCreateAgentActionGroupVersion(t *testing.T) {
var agentResp map[string]map[string]any
- _ = json.Unmarshal(agentRec.Body.Bytes(), &agentResp)
+ _ = json.Unmarshal(agentRec.Body.Bytes(), &agentResp, jsonx.V1())
agentID, _ := agentResp["agent"]["agentId"].(string)
rec := doRequest(t, h, e, http.MethodPut,
@@ -44,7 +46,7 @@ func TestCreateAgentActionGroupVersion(t *testing.T) {
var resp map[string]map[string]any
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -69,7 +71,7 @@ func TestCreateAgentActionGroupVersion(t *testing.T) {
var agentResp map[string]map[string]any
- _ = json.Unmarshal(agentRec.Body.Bytes(), &agentResp)
+ _ = json.Unmarshal(agentRec.Body.Bytes(), &agentResp, jsonx.V1())
agentID, _ := agentResp["agent"]["agentId"].(string)
rec := doRequest(t, h, e, http.MethodPut,
diff --git a/services/bedrockagent/handler_agent_aliases.go b/services/bedrockagent/handler_agent_aliases.go
index b8ab5fcb57..a45b20e99a 100644
--- a/services/bedrockagent/handler_agent_aliases.go
+++ b/services/bedrockagent/handler_agent_aliases.go
@@ -2,10 +2,12 @@ package bedrockagent
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -24,7 +26,7 @@ func (h *Handler) handleCreateAgentAlias(
RoutingConfiguration []AliasRouting `json:"routingConfiguration"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
@@ -66,7 +68,7 @@ func (h *Handler) handleUpdateAgentAlias(
RoutingConfiguration []AliasRouting `json:"routingConfiguration"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
diff --git a/services/bedrockagent/handler_agent_collaborators.go b/services/bedrockagent/handler_agent_collaborators.go
index 515e2969f7..27cd5076d1 100644
--- a/services/bedrockagent/handler_agent_collaborators.go
+++ b/services/bedrockagent/handler_agent_collaborators.go
@@ -2,10 +2,12 @@ package bedrockagent
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -23,7 +25,7 @@ func (h *Handler) handleAssociateCollaborator(
ClientToken string `json:"clientToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
@@ -62,7 +64,7 @@ func (h *Handler) handleUpdateCollaborator(
RelayConversationHistory string `json:"relayConversationHistory"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
diff --git a/services/bedrockagent/handler_agent_collaborators_test.go b/services/bedrockagent/handler_agent_collaborators_test.go
index 28479cf24e..2d7e97e9d3 100644
--- a/services/bedrockagent/handler_agent_collaborators_test.go
+++ b/services/bedrockagent/handler_agent_collaborators_test.go
@@ -1,9 +1,11 @@
package bedrockagent_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestAssociateAgentCollaboratorVersion locks in real AWS's constraint on
@@ -38,7 +40,7 @@ func TestAssociateAgentCollaboratorVersion(t *testing.T) {
var agentResp map[string]map[string]any
- _ = json.Unmarshal(agentRec.Body.Bytes(), &agentResp)
+ _ = json.Unmarshal(agentRec.Body.Bytes(), &agentResp, jsonx.V1())
agentID, _ := agentResp["agent"]["agentId"].(string)
rec := doRequest(t, h, e, http.MethodPut,
@@ -55,7 +57,7 @@ func TestAssociateAgentCollaboratorVersion(t *testing.T) {
var resp map[string]map[string]any
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -80,7 +82,7 @@ func TestAssociateAgentCollaboratorVersion(t *testing.T) {
var agentResp map[string]map[string]any
- _ = json.Unmarshal(agentRec.Body.Bytes(), &agentResp)
+ _ = json.Unmarshal(agentRec.Body.Bytes(), &agentResp, jsonx.V1())
agentID, _ := agentResp["agent"]["agentId"].(string)
rec := doRequest(t, h, e, http.MethodPut,
diff --git a/services/bedrockagent/handler_agent_knowledge_bases.go b/services/bedrockagent/handler_agent_knowledge_bases.go
index 0b3f10bcf7..4c3e0bd5a3 100644
--- a/services/bedrockagent/handler_agent_knowledge_bases.go
+++ b/services/bedrockagent/handler_agent_knowledge_bases.go
@@ -2,10 +2,12 @@ package bedrockagent
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -21,7 +23,7 @@ func (h *Handler) handleAssociateAgentKB(
KnowledgeBaseState string `json:"knowledgeBaseState"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
@@ -54,7 +56,7 @@ func (h *Handler) handleUpdateAgentKB(
KnowledgeBaseState string `json:"knowledgeBaseState"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
diff --git a/services/bedrockagent/handler_agent_knowledge_bases_test.go b/services/bedrockagent/handler_agent_knowledge_bases_test.go
index b77461b59a..cab336c911 100644
--- a/services/bedrockagent/handler_agent_knowledge_bases_test.go
+++ b/services/bedrockagent/handler_agent_knowledge_bases_test.go
@@ -1,9 +1,11 @@
package bedrockagent_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestAssociateAgentKnowledgeBaseVersion locks in real AWS's constraint on
@@ -29,7 +31,7 @@ func TestAssociateAgentKnowledgeBaseVersion(t *testing.T) {
var agentResp map[string]map[string]any
- _ = json.Unmarshal(agentRec.Body.Bytes(), &agentResp)
+ _ = json.Unmarshal(agentRec.Body.Bytes(), &agentResp, jsonx.V1())
agentID, _ := agentResp["agent"]["agentId"].(string)
kbRec := doRequest(t, h, e, http.MethodPut, "/knowledgebases", map[string]any{
@@ -44,7 +46,7 @@ func TestAssociateAgentKnowledgeBaseVersion(t *testing.T) {
var kbResp map[string]map[string]any
- _ = json.Unmarshal(kbRec.Body.Bytes(), &kbResp)
+ _ = json.Unmarshal(kbRec.Body.Bytes(), &kbResp, jsonx.V1())
kbID, _ := kbResp["knowledgeBase"]["knowledgeBaseId"].(string)
rec := doRequest(t, h, e, http.MethodPut,
@@ -58,7 +60,7 @@ func TestAssociateAgentKnowledgeBaseVersion(t *testing.T) {
var resp map[string]map[string]any
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -83,7 +85,7 @@ func TestAssociateAgentKnowledgeBaseVersion(t *testing.T) {
var agentResp map[string]map[string]any
- _ = json.Unmarshal(agentRec.Body.Bytes(), &agentResp)
+ _ = json.Unmarshal(agentRec.Body.Bytes(), &agentResp, jsonx.V1())
agentID, _ := agentResp["agent"]["agentId"].(string)
kbRec := doRequest(t, h, e, http.MethodPut, "/knowledgebases", map[string]any{
@@ -98,7 +100,7 @@ func TestAssociateAgentKnowledgeBaseVersion(t *testing.T) {
var kbResp map[string]map[string]any
- _ = json.Unmarshal(kbRec.Body.Bytes(), &kbResp)
+ _ = json.Unmarshal(kbRec.Body.Bytes(), &kbResp, jsonx.V1())
kbID, _ := kbResp["knowledgeBase"]["knowledgeBaseId"].(string)
rec := doRequest(t, h, e, http.MethodPut,
diff --git a/services/bedrockagent/handler_agents.go b/services/bedrockagent/handler_agents.go
index 292e681366..cf9d85d1ed 100644
--- a/services/bedrockagent/handler_agents.go
+++ b/services/bedrockagent/handler_agents.go
@@ -2,12 +2,14 @@ package bedrockagent
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -33,7 +35,7 @@ func (h *Handler) handleCreateAgent(ctx context.Context, c *echo.Context, body [
IdleSessionTTLInSeconds int `json:"idleSessionTTLInSeconds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
@@ -92,7 +94,7 @@ func (h *Handler) handleUpdateAgent(
IdleSessionTTLInSeconds int `json:"idleSessionTTLInSeconds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
diff --git a/services/bedrockagent/handler_agents_test.go b/services/bedrockagent/handler_agents_test.go
index 89b26a4e26..ae9d82b70e 100644
--- a/services/bedrockagent/handler_agents_test.go
+++ b/services/bedrockagent/handler_agents_test.go
@@ -1,9 +1,11 @@
package bedrockagent_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandlerAgentCRUD(t *testing.T) {
@@ -31,7 +33,7 @@ func TestHandlerAgentCRUD(t *testing.T) {
}
var createResp map[string]map[string]any
- if err := json.Unmarshal(rec.Body.Bytes(), &createResp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal create response: %v", err)
}
@@ -74,7 +76,7 @@ func TestHandlerAgentCRUD(t *testing.T) {
}
var sr map[string]map[string]any
- _ = json.Unmarshal(r.Body.Bytes(), &sr)
+ _ = json.Unmarshal(r.Body.Bytes(), &sr, jsonx.V1())
aid := sr["agent"]["agentId"].(string)
path := tc.path
@@ -122,7 +124,7 @@ func TestHandlerAgentVersions(t *testing.T) {
rec := doRequest(t, h, e, http.MethodPut, "/agents", createBody)
var createResp map[string]map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &createResp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1())
agentID := createResp["agent"]["agentId"].(string)
// Prepare first so we can create a version
@@ -138,7 +140,7 @@ func TestHandlerAgentVersions(t *testing.T) {
r := doRequest(t, h2, e2, http.MethodPut, "/agents", createBody)
var resp map[string]map[string]any
- _ = json.Unmarshal(r.Body.Bytes(), &resp)
+ _ = json.Unmarshal(r.Body.Bytes(), &resp, jsonx.V1())
aid := resp["agent"]["agentId"].(string)
doRequest(t, h2, e2, http.MethodPost, "/agents/"+aid+"/prepare", nil)
@@ -148,7 +150,7 @@ func TestHandlerAgentVersions(t *testing.T) {
}
var listResp map[string]any
- _ = json.Unmarshal(rec2.Body.Bytes(), &listResp)
+ _ = json.Unmarshal(rec2.Body.Bytes(), &listResp, jsonx.V1())
if _, ok := listResp["agentVersionSummaries"]; !ok {
t.Errorf("response missing agentVersionSummaries: %s", rec2.Body.String())
@@ -181,7 +183,7 @@ func TestCreateAgent_DefaultIdleSessionTTL(t *testing.T) {
}
var resp map[string]map[string]any
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -208,7 +210,7 @@ func TestCreateAgent_CustomIdleSessionTTL(t *testing.T) {
}
var resp map[string]map[string]any
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -230,7 +232,7 @@ func TestUpdateAgent_IdleSessionTTL(t *testing.T) {
})
var createResp map[string]map[string]any
- if err := json.Unmarshal(createRec.Body.Bytes(), &createResp); err != nil {
+ if err := json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -248,7 +250,7 @@ func TestUpdateAgent_IdleSessionTTL(t *testing.T) {
}
var updateResp map[string]map[string]any
- if err := json.Unmarshal(updateRec.Body.Bytes(), &updateResp); err != nil {
+ if err := json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -277,7 +279,7 @@ func TestAgentVersionsPOST_IsListNotCreate(t *testing.T) {
})
var createResp map[string]map[string]any
- if err := json.Unmarshal(createRec.Body.Bytes(), &createResp); err != nil {
+ if err := json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -290,7 +292,7 @@ func TestAgentVersionsPOST_IsListNotCreate(t *testing.T) {
}
var resp map[string]any
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -315,7 +317,7 @@ func TestCreateAgentAlias_NoRoutingConfig_AutoCreatesVersion(t *testing.T) {
})
var createResp map[string]map[string]any
- if err := json.Unmarshal(createRec.Body.Bytes(), &createResp); err != nil {
+ if err := json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -330,7 +332,7 @@ func TestCreateAgentAlias_NoRoutingConfig_AutoCreatesVersion(t *testing.T) {
}
var aliasResp map[string]map[string]any
- if err := json.Unmarshal(aliasRec.Body.Bytes(), &aliasResp); err != nil {
+ if err := json.Unmarshal(aliasRec.Body.Bytes(), &aliasResp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -342,7 +344,7 @@ func TestCreateAgentAlias_NoRoutingConfig_AutoCreatesVersion(t *testing.T) {
listRec := doRequest(t, h, e, http.MethodGet, "/agents/"+agentID+"/agentversions", nil)
var listResp map[string]any
- if err := json.Unmarshal(listRec.Body.Bytes(), &listResp); err != nil {
+ if err := json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -364,7 +366,7 @@ func TestPrepareAgent_ResponseIncludesPreparedAt(t *testing.T) {
})
var createResp map[string]map[string]any
- if err := json.Unmarshal(createRec.Body.Bytes(), &createResp); err != nil {
+ if err := json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -377,7 +379,7 @@ func TestPrepareAgent_ResponseIncludesPreparedAt(t *testing.T) {
}
var resp map[string]any
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
diff --git a/services/bedrockagent/handler_data_sources.go b/services/bedrockagent/handler_data_sources.go
index fffef1344d..5d1979f2f7 100644
--- a/services/bedrockagent/handler_data_sources.go
+++ b/services/bedrockagent/handler_data_sources.go
@@ -2,10 +2,12 @@ package bedrockagent
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -23,7 +25,7 @@ func (h *Handler) handleCreateDS(ctx context.Context, c *echo.Context, kbID stri
DataDeletionPolicy string `json:"dataDeletionPolicy"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
@@ -66,7 +68,7 @@ func (h *Handler) handleUpdateDS(
DataDeletionPolicy string `json:"dataDeletionPolicy"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
diff --git a/services/bedrockagent/handler_data_sources_test.go b/services/bedrockagent/handler_data_sources_test.go
index bfacfbd9c4..70931ba468 100644
--- a/services/bedrockagent/handler_data_sources_test.go
+++ b/services/bedrockagent/handler_data_sources_test.go
@@ -1,9 +1,11 @@
package bedrockagent_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandlerDataSourceAndIngestion(t *testing.T) {
@@ -24,7 +26,7 @@ func TestHandlerDataSourceAndIngestion(t *testing.T) {
}
var kbResp map[string]map[string]any
- _ = json.Unmarshal(kbRec.Body.Bytes(), &kbResp)
+ _ = json.Unmarshal(kbRec.Body.Bytes(), &kbResp, jsonx.V1())
kbID := kbResp["knowledgeBase"]["knowledgeBaseId"].(string)
dsBody := map[string]any{
@@ -38,7 +40,7 @@ func TestHandlerDataSourceAndIngestion(t *testing.T) {
}
var dsResp map[string]map[string]any
- _ = json.Unmarshal(dsRec.Body.Bytes(), &dsResp)
+ _ = json.Unmarshal(dsRec.Body.Bytes(), &dsResp, jsonx.V1())
dsID := dsResp["dataSource"]["dataSourceId"].(string)
t.Run("start ingestion job", func(t *testing.T) {
diff --git a/services/bedrockagent/handler_flows.go b/services/bedrockagent/handler_flows.go
index 2ca5a42d1b..066de3a2eb 100644
--- a/services/bedrockagent/handler_flows.go
+++ b/services/bedrockagent/handler_flows.go
@@ -2,12 +2,14 @@ package bedrockagent
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -25,7 +27,7 @@ func (h *Handler) handleCreateFlow(ctx context.Context, c *echo.Context, body []
CustomerEncryptionKeyArn string `json:"customerEncryptionKeyArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
@@ -77,7 +79,7 @@ func (h *Handler) handleUpdateFlow(
CustomerEncryptionKeyArn string `json:"customerEncryptionKeyArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
@@ -133,7 +135,7 @@ func (h *Handler) handleValidateFlowDef(ctx context.Context, c *echo.Context, bo
Definition map[string]any `json:"definition"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
errs, err := h.Backend.ValidateFlowDefinition(ctx, req.Definition)
if err != nil {
@@ -155,7 +157,7 @@ func (h *Handler) handleCreateFlowVersion(
ClientToken string `json:"clientToken"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
fv, err := h.Backend.CreateFlowVersion(ctx, flowID, VersionConfig{
Description: req.Description, ClientToken: req.ClientToken,
@@ -228,7 +230,7 @@ func (h *Handler) handleCreateFlowAlias(
RoutingConfiguration []FlowAliasRouting `json:"routingConfiguration"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
@@ -270,7 +272,7 @@ func (h *Handler) handleUpdateFlowAlias(
RoutingConfiguration []FlowAliasRouting `json:"routingConfiguration"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
diff --git a/services/bedrockagent/handler_flows_test.go b/services/bedrockagent/handler_flows_test.go
index 4f426c2b66..7d1c411357 100644
--- a/services/bedrockagent/handler_flows_test.go
+++ b/services/bedrockagent/handler_flows_test.go
@@ -1,9 +1,11 @@
package bedrockagent_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandlerFlowCRUD(t *testing.T) {
@@ -26,7 +28,7 @@ func TestHandlerFlowCRUD(t *testing.T) {
}
var createResp map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &createResp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1())
flowID, _ := createResp["id"].(string)
if flowID == "" {
@@ -40,7 +42,7 @@ func TestHandlerFlowCRUD(t *testing.T) {
r := doRequest(t, h2, e2, http.MethodPost, "/flows", createBody)
var resp map[string]any
- _ = json.Unmarshal(r.Body.Bytes(), &resp)
+ _ = json.Unmarshal(r.Body.Bytes(), &resp, jsonx.V1())
id := resp["id"].(string)
rec2 := doRequest(t, h2, e2, http.MethodGet, "/flows/"+id, nil)
@@ -65,7 +67,7 @@ func TestHandlerFlowCRUD(t *testing.T) {
r := doRequest(t, h2, e2, http.MethodPost, "/flows", createBody)
var resp map[string]any
- _ = json.Unmarshal(r.Body.Bytes(), &resp)
+ _ = json.Unmarshal(r.Body.Bytes(), &resp, jsonx.V1())
id := resp["id"].(string)
// Real PrepareFlow POSTs to "/flows/{flowIdentifier}/" -- no
diff --git a/services/bedrockagent/handler_helpers.go b/services/bedrockagent/handler_helpers.go
index c74b8b50dc..462edaa5e7 100644
--- a/services/bedrockagent/handler_helpers.go
+++ b/services/bedrockagent/handler_helpers.go
@@ -2,7 +2,7 @@ package bedrockagent
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -15,13 +15,12 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var wireCodePrefix = regexp.MustCompile(`^(?:[A-Za-z]+Exception: )+`)
func handleErr(c *echo.Context, err error) error {
- var syntaxErr *json.SyntaxError
-
var code string
var status int
@@ -35,7 +34,7 @@ func handleErr(c *echo.Context, err error) error {
case errors.Is(err, awserr.ErrInvalidParameter):
status = http.StatusBadRequest
code = "ValidationException"
- case errors.As(err, &syntaxErr):
+ case jsonx.IsSyntaxError(err):
status = http.StatusBadRequest
code = "ValidationException"
default:
@@ -76,7 +75,7 @@ func bodyPageParams(body []byte) (int, string) {
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
maxResults := maxPageDefault
@@ -199,7 +198,7 @@ func validatePageRequest(query url.Values, body []byte) error {
}
var fromBody pageFields
- if len(body) > 0 && json.Unmarshal(body, &fromBody) == nil {
+ if len(body) > 0 && json.Unmarshal(body, &fromBody, jsonx.V1()) == nil {
req.overlay(fromBody)
}
diff --git a/services/bedrockagent/handler_ingestion_jobs.go b/services/bedrockagent/handler_ingestion_jobs.go
index 50fd2583d2..475859b58e 100644
--- a/services/bedrockagent/handler_ingestion_jobs.go
+++ b/services/bedrockagent/handler_ingestion_jobs.go
@@ -2,10 +2,12 @@ package bedrockagent
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -20,7 +22,7 @@ func (h *Handler) handleStartIngestionJob(
ClientToken string `json:"clientToken"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
job, err := h.Backend.StartIngestionJob(ctx, kbID, dsID, req.Description, req.ClientToken)
if err != nil {
@@ -70,7 +72,7 @@ func (h *Handler) handleListIngestionJobs(
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
}
diff --git a/services/bedrockagent/handler_ingestion_jobs_test.go b/services/bedrockagent/handler_ingestion_jobs_test.go
index c102d923c3..d50629d2fc 100644
--- a/services/bedrockagent/handler_ingestion_jobs_test.go
+++ b/services/bedrockagent/handler_ingestion_jobs_test.go
@@ -1,12 +1,13 @@
package bedrockagent_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrockagent"
)
@@ -39,7 +40,7 @@ func newIngestionFixture(t *testing.T) ingestionFixture {
var kbResp map[string]map[string]any
- _ = json.Unmarshal(kbRec.Body.Bytes(), &kbResp)
+ _ = json.Unmarshal(kbRec.Body.Bytes(), &kbResp, jsonx.V1())
kbID, _ := kbResp["knowledgeBase"]["knowledgeBaseId"].(string)
dsBody := map[string]any{
@@ -54,7 +55,7 @@ func newIngestionFixture(t *testing.T) ingestionFixture {
var dsResp map[string]map[string]any
- _ = json.Unmarshal(dsRec.Body.Bytes(), &dsResp)
+ _ = json.Unmarshal(dsRec.Body.Bytes(), &dsResp, jsonx.V1())
dsID, _ := dsResp["dataSource"]["dataSourceId"].(string)
return ingestionFixture{h: h, e: e, kbID: kbID, dsID: dsID}
@@ -95,7 +96,7 @@ func (f ingestionFixture) startJob(t *testing.T) map[string]any {
var resp map[string]map[string]any
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -199,7 +200,7 @@ func checkGetIngestionJobReturnsPersistedStats(t *testing.T) {
var resp map[string]map[string]any
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -232,7 +233,7 @@ func checkListIngestionJobsSummariesIncludeStats(t *testing.T) {
IngestionJobSummaries []map[string]any `json:"ingestionJobSummaries"`
}
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -284,7 +285,7 @@ func TestKBDocumentsRealWireRouting(t *testing.T) {
var ingestResp map[string]any
- if err := json.Unmarshal(putRec.Body.Bytes(), &ingestResp); err != nil {
+ if err := json.Unmarshal(putRec.Body.Bytes(), &ingestResp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal PUT response: %v", err)
}
@@ -308,7 +309,7 @@ func TestKBDocumentsRealWireRouting(t *testing.T) {
DocumentDetails []map[string]any `json:"documentDetails"`
}
- if err := json.Unmarshal(postRec.Body.Bytes(), &listResp); err != nil {
+ if err := json.Unmarshal(postRec.Body.Bytes(), &listResp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal POST response: %v", err)
}
@@ -335,7 +336,7 @@ func TestStopIngestionJob(t *testing.T) {
var resp map[string]map[string]any
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
diff --git a/services/bedrockagent/handler_knowledge_bases.go b/services/bedrockagent/handler_knowledge_bases.go
index ee3abe4aa4..facc16f3c2 100644
--- a/services/bedrockagent/handler_knowledge_bases.go
+++ b/services/bedrockagent/handler_knowledge_bases.go
@@ -2,11 +2,13 @@ package bedrockagent
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -24,7 +26,7 @@ func (h *Handler) handleCreateKB(ctx context.Context, c *echo.Context, body []by
ClientToken string `json:"clientToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
@@ -63,7 +65,7 @@ func (h *Handler) handleUpdateKB(ctx context.Context, c *echo.Context, kbID stri
RoleARN string `json:"roleArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
@@ -167,7 +169,7 @@ func (h *Handler) handleIngestKBDocs(
} `json:"documents"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
@@ -195,7 +197,7 @@ func (h *Handler) handleGetKBDocs(
DocumentIdentifiers []KBDocumentIdentifier `json:"documentIdentifiers"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
@@ -215,7 +217,7 @@ func (h *Handler) handleDeleteKBDocs(
DocumentIdentifiers []KBDocumentIdentifier `json:"documentIdentifiers"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
diff --git a/services/bedrockagent/handler_knowledge_bases_test.go b/services/bedrockagent/handler_knowledge_bases_test.go
index 7a9ad76ee8..e5e227f4c8 100644
--- a/services/bedrockagent/handler_knowledge_bases_test.go
+++ b/services/bedrockagent/handler_knowledge_bases_test.go
@@ -1,7 +1,7 @@
package bedrockagent_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrockagent"
)
@@ -35,7 +36,7 @@ func TestHandlerKnowledgeBaseCRUD(t *testing.T) {
}
var createResp map[string]map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &createResp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1())
kbID := createResp["knowledgeBase"]["knowledgeBaseId"].(string)
t.Run("get kb", func(t *testing.T) {
@@ -45,7 +46,7 @@ func TestHandlerKnowledgeBaseCRUD(t *testing.T) {
r := doRequest(t, h2, e2, http.MethodPut, "/knowledgebases", createBody)
var resp map[string]map[string]any
- _ = json.Unmarshal(r.Body.Bytes(), &resp)
+ _ = json.Unmarshal(r.Body.Bytes(), &resp, jsonx.V1())
id := resp["knowledgeBase"]["knowledgeBaseId"].(string)
rec2 := doRequest(t, h2, e2, http.MethodGet, "/knowledgebases/"+id, nil)
@@ -70,7 +71,7 @@ func TestHandlerKnowledgeBaseCRUD(t *testing.T) {
r := doRequest(t, h2, e2, http.MethodPut, "/knowledgebases", createBody)
var resp map[string]map[string]any
- _ = json.Unmarshal(r.Body.Bytes(), &resp)
+ _ = json.Unmarshal(r.Body.Bytes(), &resp, jsonx.V1())
id := resp["knowledgeBase"]["knowledgeBaseId"].(string)
rec2 := doRequest(t, h2, e2, http.MethodDelete, "/knowledgebases/"+id, nil)
@@ -98,7 +99,7 @@ func createKBForResourcePolicyTest(t *testing.T, h *bedrockagent.Handler, e *ech
}
var resp map[string]map[string]any
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal create kb response: %v", err)
}
@@ -123,7 +124,7 @@ func putKBResourcePolicy(
}
var resp map[string]string
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal put resource policy response: %v", err)
}
@@ -215,7 +216,7 @@ func TestHandlerResourcePolicyPut(t *testing.T) {
t.Helper()
var resp map[string]string
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -369,7 +370,7 @@ func TestHandlerResourcePolicyGet(t *testing.T) {
t.Helper()
var resp map[string]string
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -420,7 +421,7 @@ func TestHandlerResourcePolicyDelete(t *testing.T) {
t.Helper()
var resp map[string]string
- if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -528,7 +529,7 @@ func listKBDocsForIdentifierTest(t *testing.T, f ingestionFixture) []map[string]
var resp struct {
DocumentDetails []map[string]any `json:"documentDetails"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp.DocumentDetails
}
@@ -554,7 +555,7 @@ func TestHandlerGetKBDocuments_RealWireIdentifier(t *testing.T) {
var resp struct {
DocumentDetails []map[string]any `json:"documentDetails"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.DocumentDetails, 1)
assert.Equal(t, "INDEXED", resp.DocumentDetails[0]["status"])
@@ -588,7 +589,7 @@ func TestHandlerDeleteKBDocuments_RealWireIdentifier(t *testing.T) {
var deleteResp struct {
DocumentDetails []map[string]any `json:"documentDetails"`
}
- require.NoError(t, json.Unmarshal(deleteRec.Body.Bytes(), &deleteResp))
+ require.NoError(t, json.Unmarshal(deleteRec.Body.Bytes(), &deleteResp, jsonx.V1()))
require.Len(t, deleteResp.DocumentDetails, 1)
assert.Equal(t, "DELETING", deleteResp.DocumentDetails[0]["status"])
diff --git a/services/bedrockagent/handler_prompts.go b/services/bedrockagent/handler_prompts.go
index 5d873337c0..d59cfc9b3c 100644
--- a/services/bedrockagent/handler_prompts.go
+++ b/services/bedrockagent/handler_prompts.go
@@ -2,11 +2,13 @@ package bedrockagent
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -24,7 +26,7 @@ func (h *Handler) handleCreatePrompt(ctx context.Context, c *echo.Context, body
Variants []map[string]any `json:"variants"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
@@ -89,7 +91,7 @@ func (h *Handler) handleUpdatePrompt(
Variants []map[string]any `json:"variants"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
@@ -150,7 +152,7 @@ func (h *Handler) handleCreatePromptVersion(
ClientToken string `json:"clientToken"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
pv, err := h.Backend.CreatePromptVersion(ctx, promptID, VersionConfig{
Description: req.Description, ClientToken: req.ClientToken, Tags: req.Tags,
diff --git a/services/bedrockagent/handler_prompts_test.go b/services/bedrockagent/handler_prompts_test.go
index f343619833..f5d20a86d4 100644
--- a/services/bedrockagent/handler_prompts_test.go
+++ b/services/bedrockagent/handler_prompts_test.go
@@ -1,9 +1,11 @@
package bedrockagent_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandlerPromptCRUD(t *testing.T) {
@@ -28,7 +30,7 @@ func TestHandlerPromptCRUD(t *testing.T) {
}
var createResp map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &createResp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1())
promptID, _ := createResp["id"].(string)
if promptID == "" {
@@ -42,7 +44,7 @@ func TestHandlerPromptCRUD(t *testing.T) {
r := doRequest(t, h2, e2, http.MethodPost, "/prompts", createBody)
var resp map[string]any
- _ = json.Unmarshal(r.Body.Bytes(), &resp)
+ _ = json.Unmarshal(r.Body.Bytes(), &resp, jsonx.V1())
id := resp["id"].(string)
rec2 := doRequest(t, h2, e2, http.MethodGet, "/prompts/"+id, nil)
@@ -67,7 +69,7 @@ func TestHandlerPromptCRUD(t *testing.T) {
r := doRequest(t, h2, e2, http.MethodPost, "/prompts", createBody)
var resp map[string]any
- _ = json.Unmarshal(r.Body.Bytes(), &resp)
+ _ = json.Unmarshal(r.Body.Bytes(), &resp, jsonx.V1())
id := resp["id"].(string)
rec2 := doRequest(t, h2, e2, http.MethodPost, "/prompts/"+id+"/versions", map[string]any{
diff --git a/services/bedrockagent/handler_resource_policy.go b/services/bedrockagent/handler_resource_policy.go
index b57796caa2..d282b4a377 100644
--- a/services/bedrockagent/handler_resource_policy.go
+++ b/services/bedrockagent/handler_resource_policy.go
@@ -2,11 +2,13 @@ package bedrockagent
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// resourcePolicyBase is bedrock-agent's real PutResourcePolicy/
@@ -85,7 +87,7 @@ func (h *Handler) handlePutResourcePolicy(
ctx context.Context, c *echo.Context, resourceArn string, body []byte,
) error {
var req putResourcePolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
diff --git a/services/bedrockagent/handler_tags.go b/services/bedrockagent/handler_tags.go
index d6d39d0af0..0cb99faee0 100644
--- a/services/bedrockagent/handler_tags.go
+++ b/services/bedrockagent/handler_tags.go
@@ -2,11 +2,13 @@ package bedrockagent
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -31,7 +33,7 @@ func (h *Handler) handleTagResource(
Tags map[string]string `json:"tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
diff --git a/services/bedrockagent/handler_tags_test.go b/services/bedrockagent/handler_tags_test.go
index 1a5aee225d..2c939ee0aa 100644
--- a/services/bedrockagent/handler_tags_test.go
+++ b/services/bedrockagent/handler_tags_test.go
@@ -1,9 +1,11 @@
package bedrockagent_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandlerTagging(t *testing.T) {
@@ -20,7 +22,7 @@ func TestHandlerTagging(t *testing.T) {
rec := doRequest(t, h, e, http.MethodPut, "/agents", createBody)
var createResp map[string]map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &createResp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1())
arn := createResp["agent"]["agentArn"].(string)
t.Run("tag resource", func(t *testing.T) {
diff --git a/services/bedrockagent/handler_test.go b/services/bedrockagent/handler_test.go
index 050d1be4d3..24111b9d90 100644
--- a/services/bedrockagent/handler_test.go
+++ b/services/bedrockagent/handler_test.go
@@ -3,13 +3,14 @@ package bedrockagent_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrockagent"
)
@@ -36,7 +37,7 @@ func doRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
if err != nil {
t.Fatalf("marshal body: %v", err)
}
diff --git a/services/bedrockagent/handler_vpc_configurations.go b/services/bedrockagent/handler_vpc_configurations.go
index 480051047a..39a9c8daf6 100644
--- a/services/bedrockagent/handler_vpc_configurations.go
+++ b/services/bedrockagent/handler_vpc_configurations.go
@@ -2,13 +2,15 @@ package bedrockagent
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"strings"
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -77,7 +79,7 @@ func (h *Handler) handleCreateVpcConfiguration(
Port int32 `json:"port"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return handleErr(c, err)
}
diff --git a/services/bedrockagent/lifecycle_test.go b/services/bedrockagent/lifecycle_test.go
index 9cf8297221..613cfb6ace 100644
--- a/services/bedrockagent/lifecycle_test.go
+++ b/services/bedrockagent/lifecycle_test.go
@@ -1,7 +1,7 @@
package bedrockagent_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrockagent"
)
@@ -117,7 +118,7 @@ func TestRequestRealism_ErrorsAndPaging(t *testing.T) {
assert.Equal(t, tt.wantType, rec.Header().Get("X-Amzn-Errortype"))
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotContains(t, out["message"], "Exception")
})
}
diff --git a/services/bedrockagent/models.go b/services/bedrockagent/models.go
index 137a1c909e..be25ec1c1b 100644
--- a/services/bedrockagent/models.go
+++ b/services/bedrockagent/models.go
@@ -1,8 +1,10 @@
package bedrockagent
import (
- "encoding/json"
+ "encoding/json/v2"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// defaultIdleSessionTTLSeconds is the default agent idle session TTL (10 minutes),
@@ -493,7 +495,7 @@ func (d *DataSource) UnmarshalJSON(data []byte) error {
*dataSourceAlias
}{dataSourceAlias: (*dataSourceAlias)(d)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/bedrockagent/persistence.go b/services/bedrockagent/persistence.go
index a178925312..b203b4b90f 100644
--- a/services/bedrockagent/persistence.go
+++ b/services/bedrockagent/persistence.go
@@ -2,7 +2,7 @@ package bedrockagent
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -63,7 +63,7 @@ const bedrockagentSnapshotVersion = 3
// too, or a restored backend would start minting IDs that collide with
// pre-restore ones.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
AgentsByName map[string]string `json:"agentsByName"`
KBsByName map[string]string `json:"kbsByName"`
FlowsByName map[string]string `json:"flowsByName"`
diff --git a/services/bedrockagent/wire_field_fixes_test.go b/services/bedrockagent/wire_field_fixes_test.go
index 0aacb27ac4..b730bea210 100644
--- a/services/bedrockagent/wire_field_fixes_test.go
+++ b/services/bedrockagent/wire_field_fixes_test.go
@@ -1,12 +1,14 @@
package bedrockagent_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDeleteFlow_NoInventedStatusKey_RealClient guards against
@@ -28,7 +30,7 @@ func TestDeleteFlow_NoInventedStatusKey_RealClient(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code, createRec.Body.String())
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
flowID, _ := created["id"].(string)
require.NotEmpty(t, flowID)
@@ -58,7 +60,7 @@ func TestDeleteFlowVersion_NoInventedStatusKey_RealClient(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code, createRec.Body.String())
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
flowID, _ := created["id"].(string)
require.NotEmpty(t, flowID)
@@ -66,7 +68,7 @@ func TestDeleteFlowVersion_NoInventedStatusKey_RealClient(t *testing.T) {
require.Equal(t, http.StatusCreated, versionRec.Code, versionRec.Body.String())
var versionBody map[string]any
- require.NoError(t, json.Unmarshal(versionRec.Body.Bytes(), &versionBody))
+ require.NoError(t, json.Unmarshal(versionRec.Body.Bytes(), &versionBody, jsonx.V1()))
version, _ := versionBody["version"].(string)
require.NotEmpty(t, version)
diff --git a/services/bedrockagent/wire_field_omissions_test.go b/services/bedrockagent/wire_field_omissions_test.go
index c13b6215bc..72c8be6071 100644
--- a/services/bedrockagent/wire_field_omissions_test.go
+++ b/services/bedrockagent/wire_field_omissions_test.go
@@ -1,11 +1,13 @@
package bedrockagent_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestBedrockAgentLists_OmitGetOnlyFields asserts the raw decoded response
@@ -30,7 +32,7 @@ func TestBedrockAgentLists_OmitGetOnlyFields(t *testing.T) {
require.Equal(t, http.StatusOK, agentRec.Code, agentRec.Body.String())
var agentResp map[string]map[string]any
- require.NoError(t, json.Unmarshal(agentRec.Body.Bytes(), &agentResp))
+ require.NoError(t, json.Unmarshal(agentRec.Body.Bytes(), &agentResp, jsonx.V1()))
agentID, _ := agentResp["agent"]["agentId"].(string)
require.NotEmpty(t, agentID)
@@ -43,7 +45,7 @@ func TestBedrockAgentLists_OmitGetOnlyFields(t *testing.T) {
require.Equal(t, http.StatusOK, kbRec.Code, kbRec.Body.String())
var kbResp map[string]map[string]any
- require.NoError(t, json.Unmarshal(kbRec.Body.Bytes(), &kbResp))
+ require.NoError(t, json.Unmarshal(kbRec.Body.Bytes(), &kbResp, jsonx.V1()))
kbID, _ := kbResp["knowledgeBase"]["knowledgeBaseId"].(string)
require.NotEmpty(t, kbID)
@@ -59,7 +61,7 @@ func TestBedrockAgentLists_OmitGetOnlyFields(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code, listRec.Body.String())
var raw map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &raw, jsonx.V1()))
summaries, ok := raw["agentKnowledgeBaseSummaries"].([]any)
require.True(t, ok)
@@ -91,7 +93,7 @@ func TestBedrockAgentLists_OmitGetOnlyFields(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code, createRec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
flowID, _ := createResp["id"].(string)
require.NotEmpty(t, flowID)
@@ -103,7 +105,7 @@ func TestBedrockAgentLists_OmitGetOnlyFields(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code, listRec.Body.String())
var raw map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &raw, jsonx.V1()))
summaries, ok := raw["flowVersionSummaries"].([]any)
require.True(t, ok)
diff --git a/services/bedrockagentcore/identity.go b/services/bedrockagentcore/identity.go
index e3f68dadf6..e99853f220 100644
--- a/services/bedrockagentcore/identity.go
+++ b/services/bedrockagentcore/identity.go
@@ -3,11 +3,12 @@ package bedrockagentcore
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"strings"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/restroute"
)
@@ -96,7 +97,7 @@ func userFromJWT(in doc) (string, error) {
Sub string `json:"sub"`
}
- if err = json.Unmarshal(raw, &claims); err != nil || claims.Sub == "" {
+ if err = json.Unmarshal(raw, &claims, jsonx.V1()); err != nil || claims.Sub == "" {
return "", invalid("userToken must carry a sub claim")
}
diff --git a/services/bedrockagentcore/identity_test.go b/services/bedrockagentcore/identity_test.go
index 99ca6afd5a..ac81cc5e42 100644
--- a/services/bedrockagentcore/identity_test.go
+++ b/services/bedrockagentcore/identity_test.go
@@ -2,7 +2,8 @@ package bedrockagentcore_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrockagentcore"
)
@@ -32,7 +34,10 @@ func tokenServer(t *testing.T) *httptest.Server {
return
}
- _ = json.NewEncoder(w).Encode(map[string]string{"access_token": "tok-for-" + r.FormValue("scope")})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]string{"access_token": "tok-for-" + r.FormValue("scope")},
+ )
}))
t.Cleanup(srv.Close)
@@ -81,7 +86,7 @@ func TestIdentity_WorkloadTokens(t *testing.T) {
require.NoError(t, err)
assert.NotEmpty(t, *plain.WorkloadAccessToken)
- payload, _ := json.Marshal(map[string]string{"sub": "user-9"})
+ payload, _ := json.Marshal(map[string]string{"sub": "user-9"}, jsonx.V1())
jwt := "e30." + base64.RawURLEncoding.EncodeToString(payload) + ".sig"
viaJWT, err := f.client.GetWorkloadAccessTokenForJWT(t.Context(), &sdk.GetWorkloadAccessTokenForJWTInput{
diff --git a/services/bedrockagentcore/oauth.go b/services/bedrockagentcore/oauth.go
index 00447788ea..e17cc0f6b8 100644
--- a/services/bedrockagentcore/oauth.go
+++ b/services/bedrockagentcore/oauth.go
@@ -2,12 +2,14 @@ package bedrockagentcore
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/url"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/restroute"
)
@@ -101,7 +103,7 @@ func (b *InMemoryBackend) exchange(hreq *http.Request) (string, error) {
AccessToken string `json:"access_token"`
}
- if err = json.Unmarshal(body, &out); err != nil || out.AccessToken == "" {
+ if err = json.Unmarshal(body, &out, jsonx.V1()); err != nil || out.AccessToken == "" {
return "", invalid("the token endpoint did not return an access_token")
}
@@ -136,7 +138,11 @@ func (b *InMemoryBackend) tokenEndpoint(ctx context.Context, p ProviderInfo) (st
TokenEndpoint string `json:"token_endpoint"`
}
- if err = json.NewDecoder(io.LimitReader(resp.Body, maxTokenResponse)).Decode(&meta); err != nil ||
+ if err = json.UnmarshalDecode(
+ jsontext.NewDecoder(io.LimitReader(resp.Body, maxTokenResponse)),
+ &meta,
+ jsonx.V1(),
+ ); err != nil ||
meta.TokenEndpoint == "" {
return "", invalid("the discovery document of provider %q has no token_endpoint", p.Name)
}
diff --git a/services/bedrockagentcore/persistence.go b/services/bedrockagentcore/persistence.go
index 32b162b4d1..be7f0ae517 100644
--- a/services/bedrockagentcore/persistence.go
+++ b/services/bedrockagentcore/persistence.go
@@ -2,7 +2,7 @@ package bedrockagentcore
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,8 +12,8 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state. Containers and sandboxes are not part of it.
diff --git a/services/bedrockagentcore/runtime_docker_test.go b/services/bedrockagentcore/runtime_docker_test.go
index a52cad22a8..2af6cab527 100644
--- a/services/bedrockagentcore/runtime_docker_test.go
+++ b/services/bedrockagentcore/runtime_docker_test.go
@@ -2,7 +2,7 @@ package bedrockagentcore_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"os"
"os/exec"
@@ -18,6 +18,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrockagentcore"
)
@@ -160,7 +161,7 @@ func invokeRuntime(
) (map[string]any, *sdk.InvokeAgentRuntimeOutput) {
t.Helper()
- body, err := json.Marshal(payload)
+ body, err := json.Marshal(payload, jsonx.V1())
require.NoError(t, err)
out, err := f.client.InvokeAgentRuntime(t.Context(), &sdk.InvokeAgentRuntimeInput{
@@ -179,7 +180,7 @@ func invokeRuntime(
require.NoError(t, err)
var got map[string]any
- if json.Unmarshal(raw, &got) != nil {
+ if json.Unmarshal(raw, &got, jsonx.V1()) != nil {
got = map[string]any{"raw": string(raw)}
}
diff --git a/services/bedrockagentcore/wire.go b/services/bedrockagentcore/wire.go
index ba58c28172..47c7359f85 100644
--- a/services/bedrockagentcore/wire.go
+++ b/services/bedrockagentcore/wire.go
@@ -2,7 +2,7 @@ package bedrockagentcore
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"slices"
@@ -10,6 +10,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/restroute"
)
@@ -127,7 +128,7 @@ func sortedKeys[V any](m map[string]V) []string {
}
func mustJSON(v any) []byte {
- b, _ := json.Marshal(v)
+ b, _ := json.Marshal(v, jsonx.V1())
return b
}
@@ -147,7 +148,7 @@ func readJSONBody(r *http.Request) (doc, error) {
return d, nil
}
- if err = json.Unmarshal(body, &d); err != nil {
+ if err = json.Unmarshal(body, &d, jsonx.V1()); err != nil {
return nil, invalid("request body is not valid JSON")
}
diff --git a/services/bedrockagentcorecontrol/dataplane.go b/services/bedrockagentcorecontrol/dataplane.go
index 0f69f344aa..d00aeb6c19 100644
--- a/services/bedrockagentcorecontrol/dataplane.go
+++ b/services/bedrockagentcorecontrol/dataplane.go
@@ -1,8 +1,9 @@
package bedrockagentcorecontrol
import (
- "encoding/json"
+ "encoding/json/v2"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
dataplane "github.com/blackbirdworks/gopherstack/services/bedrockagentcore"
)
@@ -178,7 +179,7 @@ func (d dataSeam) SecretValue(arn, jsonKey string) (string, bool) {
}
var fields map[string]any
- if err := json.Unmarshal([]byte(raw), &fields); err != nil {
+ if err := json.Unmarshal([]byte(raw), &fields, jsonx.V1()); err != nil {
return raw, true
}
@@ -193,7 +194,7 @@ func (d dataSeam) SecretValue(arn, jsonKey string) (string, bool) {
}
func mustMarshal(v any) []byte {
- out, _ := json.Marshal(v)
+ out, _ := json.Marshal(v, jsonx.V1())
return out
}
diff --git a/services/bedrockagentcorecontrol/dataplane_test.go b/services/bedrockagentcorecontrol/dataplane_test.go
index 3dfb588665..1cc556d4b1 100644
--- a/services/bedrockagentcorecontrol/dataplane_test.go
+++ b/services/bedrockagentcorecontrol/dataplane_test.go
@@ -1,7 +1,7 @@
package bedrockagentcorecontrol_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
dataplane "github.com/blackbirdworks/gopherstack/services/bedrockagentcore"
)
@@ -98,7 +99,7 @@ func TestCredentialProviders_StoreRealSecrets(t *testing.T) {
assert.Contains(t, arn, ":secret:bedrock-agentcore-identity-default/apikey/vendor/apiKey")
var payload map[string]string
- require.NoError(t, json.Unmarshal([]byte(store.values[arn]), &payload))
+ require.NoError(t, json.Unmarshal([]byte(store.values[arn]), &payload, jsonx.V1()))
assert.Equal(t, "sk-1", payload["apiKey"], "the key is stored, not discarded")
info, ok := seam.Provider(dpScope(), dataplane.ProviderAPIKey, "vendor")
diff --git a/services/bedrockagentcorecontrol/engine.go b/services/bedrockagentcorecontrol/engine.go
index f9894ffb4c..7d7604a858 100644
--- a/services/bedrockagentcorecontrol/engine.go
+++ b/services/bedrockagentcorecontrol/engine.go
@@ -2,7 +2,7 @@ package bedrockagentcorecontrol
import (
"cmp"
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"slices"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/restroute"
)
@@ -511,7 +512,7 @@ func (b *InMemoryBackend) matches(r *Resource, sp *opSpec, in args) bool {
return false
}
case "targetResourceScope":
- raw, _ := json.Marshal(r.Doc["definition"])
+ raw, _ := json.Marshal(r.Doc["definition"], jsonx.V1())
if !strings.Contains(string(raw), want) {
return false
}
diff --git a/services/bedrockagentcorecontrol/hooks_data.go b/services/bedrockagentcorecontrol/hooks_data.go
index 8b435d6b24..5f340f4857 100644
--- a/services/bedrockagentcorecontrol/hooks_data.go
+++ b/services/bedrockagentcorecontrol/hooks_data.go
@@ -3,7 +3,7 @@ package bedrockagentcorecontrol
import (
"bufio"
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
@@ -11,6 +11,7 @@ import (
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -122,7 +123,7 @@ func (b *InMemoryBackend) loadExamples(source map[string]any) ([]any, string) {
}
var m any
- if err := json.Unmarshal([]byte(line), &m); err != nil {
+ if err := json.Unmarshal([]byte(line), &m, jsonx.V1()); err != nil {
return nil, fmt.Sprintf("line %d of the S3 object is not valid JSON", len(out)+1)
}
diff --git a/services/bedrockagentcorecontrol/hooks_policy.go b/services/bedrockagentcorecontrol/hooks_policy.go
index 8e378dd0bb..bbf6f3daa4 100644
--- a/services/bedrockagentcorecontrol/hooks_policy.go
+++ b/services/bedrockagentcorecontrol/hooks_policy.go
@@ -1,7 +1,7 @@
package bedrockagentcorecontrol
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
@@ -11,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -310,7 +311,7 @@ func (b *InMemoryBackend) managedSecret(sc scope, kind, name, member string, val
return b.managedSecretARN(sc, kind, name, member)
}
- payload, _ := json.Marshal(map[string]string{member: text})
+ payload, _ := json.Marshal(map[string]string{member: text}, jsonx.V1())
secretName := fmt.Sprintf("%s%s/%s/%s", managedSecretName, kind, name, member)
if secretARN, err := st.PutSecret(sc.account, sc.region, secretName, string(payload)); err == nil {
diff --git a/services/bedrockagentcorecontrol/ops_special.go b/services/bedrockagentcorecontrol/ops_special.go
index 2a60d73592..719f58f645 100644
--- a/services/bedrockagentcorecontrol/ops_special.go
+++ b/services/bedrockagentcorecontrol/ops_special.go
@@ -1,12 +1,13 @@
package bedrockagentcorecontrol
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"slices"
"strconv"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/restroute"
)
@@ -469,7 +470,7 @@ func (b *InMemoryBackend) putResourcePolicy(sc scope, in args) (*reply, error) {
var parsed struct {
Statement any `json:"Statement"`
}
- if err := json.Unmarshal([]byte(doc), &parsed); err != nil || parsed.Statement == nil {
+ if err := json.Unmarshal([]byte(doc), &parsed, jsonx.V1()); err != nil || parsed.Statement == nil {
return nil, invalid("policy must be a JSON policy document with a Statement")
}
diff --git a/services/bedrockagentcorecontrol/persistence.go b/services/bedrockagentcorecontrol/persistence.go
index d447b04922..da12c5b5a9 100644
--- a/services/bedrockagentcorecontrol/persistence.go
+++ b/services/bedrockagentcorecontrol/persistence.go
@@ -2,7 +2,7 @@ package bedrockagentcorecontrol
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,8 +12,8 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/bedrockagentcorecontrol/spec.go b/services/bedrockagentcorecontrol/spec.go
index 4f8ba89c7c..6a38841680 100644
--- a/services/bedrockagentcorecontrol/spec.go
+++ b/services/bedrockagentcorecontrol/spec.go
@@ -2,12 +2,13 @@ package bedrockagentcorecontrol
import (
_ "embed" // opsJSON
- "encoding/json"
+ "encoding/json/v2"
"slices"
"sort"
"strings"
"sync"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/restroute"
)
@@ -41,7 +42,7 @@ type specFile struct {
var specs = sync.OnceValue(func() map[string]*opSpec { //nolint:gochecknoglobals // immutable embedded spec
var f specFile
- if err := json.Unmarshal(opsJSON, &f); err != nil {
+ if err := json.Unmarshal(opsJSON, &f, jsonx.V1()); err != nil {
panic("bedrockagentcorecontrol: corrupt ops.json: " + err.Error())
}
diff --git a/services/bedrockruntime/converse_validation.go b/services/bedrockruntime/converse_validation.go
index f73659e973..69e7b5db0c 100644
--- a/services/bedrockruntime/converse_validation.go
+++ b/services/bedrockruntime/converse_validation.go
@@ -1,11 +1,14 @@
package bedrockruntime
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"regexp"
"slices"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const maxRequestMetadataEntries = 16
@@ -47,13 +50,13 @@ func validateRequestMetadata(md map[string]string) string {
// validateGuardrailConfig applies the patterns in API_runtime_GuardrailConfiguration.html and
// API_runtime_GuardrailStreamConfiguration.html.
-func validateGuardrailConfig(raw json.RawMessage) string {
+func validateGuardrailConfig(raw jsontext.Value) string {
if len(raw) == 0 {
return ""
}
var gc guardrailConfigBody
- if json.Unmarshal(raw, &gc) != nil {
+ if json.Unmarshal(raw, &gc, jsonx.V1()) != nil {
return ""
}
@@ -135,13 +138,13 @@ type inferenceConfigBody struct {
}
// validateInferenceConfig applies the InferenceConfiguration bounds.
-func validateInferenceConfig(raw json.RawMessage) string {
+func validateInferenceConfig(raw jsontext.Value) string {
if len(raw) == 0 {
return ""
}
var ic inferenceConfigBody
- if json.Unmarshal(raw, &ic) != nil {
+ if json.Unmarshal(raw, &ic, jsonx.V1()) != nil {
return ""
}
@@ -162,11 +165,11 @@ func validateInferenceConfig(raw json.RawMessage) string {
}
type toolConfigBody struct {
- ToolChoice map[string]json.RawMessage `json:"toolChoice"`
+ ToolChoice map[string]jsontext.Value `json:"toolChoice"`
Tools []struct {
ToolSpec *struct {
- Name string `json:"name"`
- InputSchema json.RawMessage `json:"inputSchema"`
+ Name string `json:"name"`
+ InputSchema jsontext.Value `json:"inputSchema"`
} `json:"toolSpec"`
} `json:"tools"`
}
@@ -177,13 +180,13 @@ type specificToolChoice struct {
// validateToolConfig applies the ToolConfiguration shape: at least one tool, valid tool names,
// a single-member toolChoice union, and a specific choice that names a declared tool.
-func validateToolConfig(raw json.RawMessage) string {
+func validateToolConfig(raw jsontext.Value) string {
if len(raw) == 0 {
return ""
}
var tc toolConfigBody
- if json.Unmarshal(raw, &tc) != nil {
+ if json.Unmarshal(raw, &tc, jsonx.V1()) != nil {
return ""
}
@@ -212,7 +215,7 @@ func validateToolConfig(raw json.RawMessage) string {
return validateToolChoice(tc.ToolChoice, names)
}
-func validateToolChoice(choice map[string]json.RawMessage, declared map[string]bool) string {
+func validateToolChoice(choice map[string]jsontext.Value, declared map[string]bool) string {
if len(choice) == 0 {
return ""
}
@@ -223,7 +226,7 @@ func validateToolChoice(choice map[string]json.RawMessage, declared map[string]b
if raw, ok := choice["tool"]; ok {
var st specificToolChoice
- if json.Unmarshal(raw, &st) == nil && !declared[st.Name] {
+ if json.Unmarshal(raw, &st, jsonx.V1()) == nil && !declared[st.Name] {
return "toolConfig.toolChoice.tool.name " + st.Name + " does not match a declared tool"
}
diff --git a/services/bedrockruntime/eventstream_test.go b/services/bedrockruntime/eventstream_test.go
index 97963a5f27..891db74db0 100644
--- a/services/bedrockruntime/eventstream_test.go
+++ b/services/bedrockruntime/eventstream_test.go
@@ -2,12 +2,14 @@ package bedrockruntime_test
import (
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// parseEventStreamFrames splits a raw binary event stream body into decoded
@@ -41,7 +43,7 @@ func parseEventStreamFrames(data []byte) []map[string]any {
if len(payload) > 0 {
var m map[string]any
- if err := json.Unmarshal(payload, &m); err == nil {
+ if err := json.Unmarshal(payload, &m, jsonx.V1()); err == nil {
frames = append(frames, m)
}
}
diff --git a/services/bedrockruntime/guardrail_lookup.go b/services/bedrockruntime/guardrail_lookup.go
index ef4fd80fbf..c15692b1be 100644
--- a/services/bedrockruntime/guardrail_lookup.go
+++ b/services/bedrockruntime/guardrail_lookup.go
@@ -1,12 +1,14 @@
package bedrockruntime
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/bedrock"
)
@@ -94,7 +96,7 @@ func (b *InMemoryBackend) inheritGuardrailLookup(home *InMemoryBackend) {
// converseGuardrailMissing writes ResourceNotFoundException when the request's guardrailConfig
// references a guardrail that is not registered; the bool reports whether a response was written.
-func (h *Handler) converseGuardrailMissing(c *echo.Context, raw json.RawMessage) (bool, error) {
+func (h *Handler) converseGuardrailMissing(c *echo.Context, raw jsontext.Value) (bool, error) {
if len(raw) == 0 {
return false, nil
}
@@ -123,8 +125,8 @@ func (h *Handler) invokeGuardrailMissing(c *echo.Context) (bool, error) {
return true, writeGuardrailNotFound(c, id, version)
}
-func parseGuardrailConfig(raw json.RawMessage) (guardrailConfigBody, bool) {
+func parseGuardrailConfig(raw jsontext.Value) (guardrailConfigBody, bool) {
var gc guardrailConfigBody
- return gc, json.Unmarshal(raw, &gc) == nil
+ return gc, json.Unmarshal(raw, &gc, jsonx.V1()) == nil
}
diff --git a/services/bedrockruntime/handler_async_invoke.go b/services/bedrockruntime/handler_async_invoke.go
index be87d60795..5132b16c15 100644
--- a/services/bedrockruntime/handler_async_invoke.go
+++ b/services/bedrockruntime/handler_async_invoke.go
@@ -1,7 +1,8 @@
package bedrockruntime
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"strconv"
@@ -12,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// startAsyncInvokeInput is the parsed request body for StartAsyncInvoke.
@@ -28,16 +30,16 @@ type startAsyncInvokeInput struct {
S3URI string `json:"s3Uri"`
} `json:"s3OutputDataConfig"`
} `json:"outputDataConfig"`
- ModelID string `json:"modelId"`
- ClientRequestToken string `json:"clientRequestToken"`
- ModelInput json.RawMessage `json:"modelInput"`
+ ModelID string `json:"modelId"`
+ ClientRequestToken string `json:"clientRequestToken"`
+ ModelInput jsontext.Value `json:"modelInput"`
}
// handleStartAsyncInvoke handles POST /async-invoke.
func (h *Handler) handleStartAsyncInvoke(c *echo.Context, body []byte) error {
var req startAsyncInvokeInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid request body"))
}
diff --git a/services/bedrockruntime/handler_async_invoke_test.go b/services/bedrockruntime/handler_async_invoke_test.go
index 7aa907fdc8..22d1cb298f 100644
--- a/services/bedrockruntime/handler_async_invoke_test.go
+++ b/services/bedrockruntime/handler_async_invoke_test.go
@@ -2,7 +2,7 @@ package bedrockruntime_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrockruntime"
)
@@ -76,7 +77,7 @@ func TestHandler_StartAsyncInvoke(t *testing.T) {
assert.Equal(t, http.StatusAccepted, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out, "invocationArn")
assert.NotEmpty(t, out["invocationArn"])
})
@@ -101,7 +102,7 @@ func TestHandler_StartAsyncInvoke_MissingS3URI(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out, "__type")
}
@@ -123,7 +124,7 @@ func TestHandler_StartAsyncInvoke_WithTags(t *testing.T) {
require.Equal(t, http.StatusAccepted, rec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startOut, jsonx.V1()))
invocationArn := startOut["invocationArn"].(string)
getPath := "/async-invoke/" + url.PathEscape(invocationArn)
@@ -131,7 +132,7 @@ func TestHandler_StartAsyncInvoke_WithTags(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.NotContains(
t, getOut, "tags",
"neither GetAsyncInvokeOutput nor AsyncInvokeSummary has a tags member; "+
@@ -230,7 +231,7 @@ func TestStartAsyncInvoke_ModelIDAsInferenceProfileARN(t *testing.T) {
assert.Equal(t, http.StatusAccepted, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["invocationArn"])
}
@@ -318,7 +319,7 @@ func TestHandler_GetAsyncInvoke(t *testing.T) {
require.Equal(t, http.StatusAccepted, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
invocationArn := startOut["invocationArn"].(string)
getPath := "/async-invoke/" + url.PathEscape(invocationArn)
@@ -327,7 +328,7 @@ func TestHandler_GetAsyncInvoke(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, invocationArn, out["invocationArn"])
assert.Contains(t, out, "modelArn")
assert.Contains(t, out, "status")
@@ -362,7 +363,7 @@ func TestHandler_GetAsyncInvoke_FullFields(t *testing.T) {
require.Equal(t, http.StatusAccepted, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
invocationArn := startOut["invocationArn"].(string)
getPath := "/async-invoke/" + url.PathEscape(invocationArn)
@@ -371,7 +372,7 @@ func TestHandler_GetAsyncInvoke_FullFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out, "clientRequestToken")
assert.Equal(t, "my-idempotency-token", out["clientRequestToken"])
assert.NotContains(
@@ -423,14 +424,14 @@ func TestAsyncInvoke_GetResponseShape(t *testing.T) {
require.Equal(t, http.StatusAccepted, recCreate.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut, jsonx.V1()))
arn := createOut["invocationArn"].(string)
recGet := doRequest(t, h, http.MethodGet, "/async-invoke/"+arn, nil)
require.Equal(t, http.StatusOK, recGet.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, arn, out["invocationArn"])
assert.Contains(t, out["modelArn"].(string), tt.modelID)
assert.Equal(t, "InProgress", out["status"])
@@ -501,7 +502,7 @@ func TestHandler_ListAsyncInvokes(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out, "asyncInvokeSummaries")
summaries, ok := out["asyncInvokeSummaries"].([]any)
@@ -568,7 +569,7 @@ func TestHandler_ListAsyncInvokes_StatusFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries, ok := out["asyncInvokeSummaries"].([]any)
require.True(t, ok)
@@ -595,7 +596,7 @@ func TestHandler_ListAsyncInvokes_WithClientToken(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
summaries, ok := out["asyncInvokeSummaries"].([]any)
require.True(t, ok)
@@ -613,7 +614,7 @@ func TestListAsyncInvokes_EmptyList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
invokes, ok := out["asyncInvokeSummaries"].([]any)
require.True(t, ok)
assert.Empty(t, invokes)
@@ -664,7 +665,7 @@ func TestListAsyncInvokes_AfterCreate(t *testing.T) {
require.Equal(t, http.StatusOK, recList.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &out, jsonx.V1()))
invokes := out["asyncInvokeSummaries"].([]any)
assert.Len(t, invokes, tt.wantLen)
diff --git a/services/bedrockruntime/handler_converse.go b/services/bedrockruntime/handler_converse.go
index 8cdd4c9fa8..148a997961 100644
--- a/services/bedrockruntime/handler_converse.go
+++ b/services/bedrockruntime/handler_converse.go
@@ -1,10 +1,13 @@
package bedrockruntime
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// converseMessage represents a single message in a Converse request.
@@ -25,9 +28,9 @@ type converseRequest struct {
PerformanceConfig *performanceConfigBody `json:"performanceConfig,omitempty"`
Messages []converseMessage `json:"messages"`
System []converseContent `json:"system,omitempty"`
- ToolConfig json.RawMessage `json:"toolConfig,omitempty"`
- GuardrailConfig json.RawMessage `json:"guardrailConfig,omitempty"`
- InferenceConfig json.RawMessage `json:"inferenceConfig,omitempty"`
+ ToolConfig jsontext.Value `json:"toolConfig,omitempty"`
+ GuardrailConfig jsontext.Value `json:"guardrailConfig,omitempty"`
+ InferenceConfig jsontext.Value `json:"inferenceConfig,omitempty"`
}
type serviceTierBody struct {
@@ -133,7 +136,7 @@ func (h *Handler) handleConverse(
) error {
var req converseRequest
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
if msg := validateConverseConfig(&req); msg != "" {
@@ -151,7 +154,7 @@ func (h *Handler) handleConverse(
resp := buildConverseResponse(&req)
echoConverseConfig(resp, &req)
- out, err := json.Marshal(resp)
+ out, err := json.Marshal(resp, jsonx.V1())
if err != nil {
return c.JSON(http.StatusInternalServerError, errorResponse("InternalServerException", "internal server error"))
}
@@ -173,7 +176,7 @@ func (h *Handler) handleConverseStream(
) error {
var req converseRequest
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
if msg := validateConverseConfig(&req); msg != "" {
@@ -191,7 +194,7 @@ func (h *Handler) handleConverseStream(
inputTokens := estimateTokensFromMessages(req.Messages, req.System)
respSummary := buildConverseResponse(&req)
- out, err := json.Marshal(respSummary)
+ out, err := json.Marshal(respSummary, jsonx.V1())
if err != nil {
return c.JSON(http.StatusInternalServerError, errorResponse("InternalServerException", "internal server error"))
}
@@ -202,7 +205,7 @@ func (h *Handler) handleConverseStream(
c.Response().WriteHeader(http.StatusOK)
writeStreamEvent := func(eventType string, payload any) {
- data, merr := json.Marshal(payload)
+ data, merr := json.Marshal(payload, jsonx.V1())
if merr != nil {
return
}
diff --git a/services/bedrockruntime/handler_converse_test.go b/services/bedrockruntime/handler_converse_test.go
index d2455a7554..b2d64cef13 100644
--- a/services/bedrockruntime/handler_converse_test.go
+++ b/services/bedrockruntime/handler_converse_test.go
@@ -2,7 +2,7 @@ package bedrockruntime_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -10,6 +10,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- Converse tests ---
@@ -52,7 +54,7 @@ func TestHandler_Converse(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out, "output")
assert.Contains(t, out, "stopReason")
@@ -87,7 +89,7 @@ func TestConverse_ReflectsInputTokens(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
usage, ok := out["usage"].(map[string]any)
require.True(t, ok)
@@ -118,7 +120,7 @@ func TestConverse_WithSystem(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out, "output")
assert.Contains(t, out, "stopReason")
assert.Contains(t, out, "usage")
@@ -158,7 +160,7 @@ func TestConverse_ResponseFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out, tt.wantKey)
assert.Contains(t, out, "usage")
assert.Contains(t, out, "stopReason")
@@ -201,7 +203,7 @@ func TestConverse_WithSystemMessages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out, "output")
})
}
@@ -222,7 +224,7 @@ func TestConverse_StopReasonEndTurn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "end_turn", out["stopReason"])
}
@@ -252,7 +254,7 @@ func TestConverse_WithToolConfig_ReturnsEndTurn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "end_turn", out["stopReason"])
assert.Contains(t, out, "output")
assert.Contains(t, out, "usage")
@@ -284,7 +286,7 @@ func TestConverse_MultiTurnMessages_AccumulatesTokens(t *testing.T) {
extractInputTokens := func(body []byte) float64 {
var out map[string]any
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
usage := out["usage"].(map[string]any)
return usage["inputTokens"].(float64)
diff --git a/services/bedrockruntime/handler_count_tokens_test.go b/services/bedrockruntime/handler_count_tokens_test.go
index 04f62b2c86..a0bd829f5f 100644
--- a/services/bedrockruntime/handler_count_tokens_test.go
+++ b/services/bedrockruntime/handler_count_tokens_test.go
@@ -1,13 +1,15 @@
package bedrockruntime_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// countTokensInvokeModelBody builds a real CountTokens request body wrapping
@@ -68,7 +70,7 @@ func TestHandler_CountTokens(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out, "inputTokens")
invocations := h.Backend.ListInvocations()
@@ -121,7 +123,7 @@ func TestHandler_CountTokens_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.InDelta(t, tt.wantTokens, out["inputTokens"], 0)
})
}
@@ -149,8 +151,8 @@ func TestCountTokens_ReflectsInputLength(t *testing.T) {
require.Equal(t, http.StatusOK, recLong.Code)
var shortOut, longOut map[string]any
- require.NoError(t, json.Unmarshal(recShort.Body.Bytes(), &shortOut))
- require.NoError(t, json.Unmarshal(recLong.Body.Bytes(), &longOut))
+ require.NoError(t, json.Unmarshal(recShort.Body.Bytes(), &shortOut, jsonx.V1()))
+ require.NoError(t, json.Unmarshal(recLong.Body.Bytes(), &longOut, jsonx.V1()))
shortTokens := shortOut["inputTokens"].(float64)
longTokens := longOut["inputTokens"].(float64)
@@ -175,8 +177,8 @@ func TestCountTokens_TitanUsesLargerDivisor(t *testing.T) {
require.Equal(t, http.StatusOK, recTitan.Code)
var claudeOut, titanOut map[string]any
- require.NoError(t, json.Unmarshal(recClaude.Body.Bytes(), &claudeOut))
- require.NoError(t, json.Unmarshal(recTitan.Body.Bytes(), &titanOut))
+ require.NoError(t, json.Unmarshal(recClaude.Body.Bytes(), &claudeOut, jsonx.V1()))
+ require.NoError(t, json.Unmarshal(recTitan.Body.Bytes(), &titanOut, jsonx.V1()))
claudeTokens := claudeOut["inputTokens"].(float64)
titanTokens := titanOut["inputTokens"].(float64)
@@ -221,7 +223,7 @@ func TestCountTokens_MultipleModelFamilies(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
count, ok := out["inputTokens"].(float64)
require.True(t, ok, "inputTokens should be a number")
assert.GreaterOrEqual(t, int(count), tt.minCount)
@@ -245,8 +247,8 @@ func TestCountTokens_TitanUsesHigherDivisor(t *testing.T) {
require.Equal(t, http.StatusOK, recTitan.Code)
var claudeOut, titanOut map[string]any
- require.NoError(t, json.Unmarshal(recClaude.Body.Bytes(), &claudeOut))
- require.NoError(t, json.Unmarshal(recTitan.Body.Bytes(), &titanOut))
+ require.NoError(t, json.Unmarshal(recClaude.Body.Bytes(), &claudeOut, jsonx.V1()))
+ require.NoError(t, json.Unmarshal(recTitan.Body.Bytes(), &titanOut, jsonx.V1()))
claudeTokens := claudeOut["inputTokens"].(float64)
titanTokens := titanOut["inputTokens"].(float64)
@@ -289,7 +291,7 @@ func TestCountTokens_MultiMessageBody(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
count := out["inputTokens"].(float64)
assert.GreaterOrEqual(t, int(count), tt.minCount)
})
diff --git a/services/bedrockruntime/handler_guardrail_checks.go b/services/bedrockruntime/handler_guardrail_checks.go
index 2dadced9a4..8f1a6f790f 100644
--- a/services/bedrockruntime/handler_guardrail_checks.go
+++ b/services/bedrockruntime/handler_guardrail_checks.go
@@ -1,7 +1,7 @@
package bedrockruntime
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"regexp"
"strconv"
@@ -9,6 +9,8 @@ import (
"unicode/utf8"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- InvokeGuardrailChecks (POST /guardrail-checks/invoke) ---
@@ -162,7 +164,7 @@ type guardrailChecksTextUnitsResponse struct {
func (h *Handler) handleInvokeGuardrailChecks(c *echo.Context, body []byte) error {
var req invokeGuardrailChecksRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid request body"))
}
}
@@ -204,7 +206,7 @@ func (h *Handler) handleInvokeGuardrailChecks(c *echo.Context, body []byte) erro
"usage": usage,
}
- out, err := json.Marshal(resp)
+ out, err := json.Marshal(resp, jsonx.V1())
if err != nil {
return c.JSON(http.StatusInternalServerError, errorResponse("InternalServerException", "internal server error"))
}
diff --git a/services/bedrockruntime/handler_guardrails.go b/services/bedrockruntime/handler_guardrails.go
index 098b783e69..936e301114 100644
--- a/services/bedrockruntime/handler_guardrails.go
+++ b/services/bedrockruntime/handler_guardrails.go
@@ -1,11 +1,13 @@
package bedrockruntime
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// applyGuardrailRequest represents the parsed ApplyGuardrail request body.
@@ -158,7 +160,7 @@ func (h *Handler) handleApplyGuardrail(
var req applyGuardrailRequest
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
if msg := validateApplyGuardrail(&req); msg != "" {
@@ -185,7 +187,7 @@ func (h *Handler) handleApplyGuardrail(
},
}
- out, err := json.Marshal(resp)
+ out, err := json.Marshal(resp, jsonx.V1())
if err != nil {
return c.JSON(http.StatusInternalServerError, errorResponse("InternalServerException", "internal server error"))
}
diff --git a/services/bedrockruntime/handler_guardrails_test.go b/services/bedrockruntime/handler_guardrails_test.go
index be49d951e1..8838c9d15a 100644
--- a/services/bedrockruntime/handler_guardrails_test.go
+++ b/services/bedrockruntime/handler_guardrails_test.go
@@ -1,12 +1,14 @@
package bedrockruntime_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- ApplyGuardrail tests ---
@@ -51,7 +53,7 @@ func TestHandler_ApplyGuardrail(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out, "action")
assert.Contains(t, out, "assessments")
assert.Contains(t, out, "outputs")
@@ -124,7 +126,7 @@ func TestApplyGuardrail_NoneForSafeContent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "NONE", out["action"])
}
@@ -143,7 +145,7 @@ func TestApplyGuardrail_BlockedForHarmfulContent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "GUARDRAIL_INTERVENED", out["action"])
}
@@ -162,7 +164,7 @@ func TestApplyGuardrail_BlockedForToxicContent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "GUARDRAIL_INTERVENED", out["action"])
}
@@ -182,7 +184,7 @@ func TestApplyGuardrail_OutputsReflectsInput(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
outputs, ok := out["outputs"].([]any)
require.True(t, ok)
@@ -231,7 +233,7 @@ func TestApplyGuardrail_ResponseShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantAction, out["action"])
assert.Contains(t, out, "outputs")
assert.Contains(t, out, "usage")
@@ -269,7 +271,7 @@ func TestApplyGuardrail_OutputsMirrorInput(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
outputs := out["outputs"].([]any)
assert.Len(t, outputs, tt.wantLen)
@@ -296,7 +298,7 @@ func TestApplyGuardrail_AssessmentsReflectBlockedMatch(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Equal(t, "GUARDRAIL_INTERVENED", out["action"])
assessments, ok := out["assessments"].([]any)
@@ -335,7 +337,7 @@ func TestApplyGuardrail_AssessmentsEmptyForNoneAction(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Equal(t, "NONE", out["action"])
assessments, ok := out["assessments"].([]any)
@@ -453,7 +455,7 @@ func TestHandler_InvokeGuardrailChecks(t *testing.T) {
}
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out, "results")
assert.Contains(t, out, "usage")
})
@@ -486,7 +488,7 @@ func TestInvokeGuardrailChecks_ContentFilterNeverFabricatesSeverityScore(t *test
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
results, ok := out["results"].(map[string]any)
require.True(t, ok)
@@ -557,7 +559,7 @@ func TestInvokeGuardrailChecks_SensitiveInformation_RealRegexDetection(t *testin
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
results := out["results"].(map[string]any)
sensitive := results["sensitiveInformation"].(map[string]any)
diff --git a/services/bedrockruntime/handler_invoke.go b/services/bedrockruntime/handler_invoke.go
index 3570170162..0b65c44c3b 100644
--- a/services/bedrockruntime/handler_invoke.go
+++ b/services/bedrockruntime/handler_invoke.go
@@ -2,12 +2,14 @@ package bedrockruntime
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Request/response header names used by the InvokeModel family (see
@@ -147,7 +149,7 @@ func modelResponsePayloadPart(out []byte) []byte {
part := map[string]string{"bytes": base64.StdEncoding.EncodeToString(out)}
// Marshaling a map[string]string with one entry never fails.
- b, _ := json.Marshal(part)
+ b, _ := json.Marshal(part, jsonx.V1())
return b
}
@@ -177,7 +179,7 @@ func (h *Handler) handleInvokeModel(
resp := mockInvokeModelResponse(modelID)
- out, err := json.Marshal(resp)
+ out, err := json.Marshal(resp, jsonx.V1())
if err != nil {
return c.JSON(http.StatusInternalServerError, errorResponse("InternalServerException", "internal server error"))
}
@@ -234,7 +236,7 @@ func (h *Handler) handleInvokeModelWithResponseStream(
resp := mockInvokeModelResponse(modelID)
- out, err := json.Marshal(resp)
+ out, err := json.Marshal(resp, jsonx.V1())
if err != nil {
return c.JSON(http.StatusInternalServerError, errorResponse("InternalServerException", "internal server error"))
}
@@ -271,7 +273,7 @@ func (h *Handler) handleInvokeModelWithBidirectionalStream(
) error {
resp := mockInvokeModelResponse(modelID)
- out, err := json.Marshal(resp)
+ out, err := json.Marshal(resp, jsonx.V1())
if err != nil {
return c.JSON(http.StatusInternalServerError, errorResponse("InternalServerException", "internal server error"))
}
@@ -341,7 +343,7 @@ func estimateTokenCount(body []byte, modelID string) int {
}
var req countTokensRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return max(1, len(body)/charsPerToken)
}
@@ -392,7 +394,7 @@ func (h *Handler) handleCountTokens(
keyInputTokens: inputTokens,
}
- out, err := json.Marshal(resp)
+ out, err := json.Marshal(resp, jsonx.V1())
if err != nil {
return c.JSON(http.StatusInternalServerError, errorResponse("InternalServerException", "internal server error"))
}
diff --git a/services/bedrockruntime/handler_invoke_test.go b/services/bedrockruntime/handler_invoke_test.go
index 7a59e9d61c..ef972f0fbf 100644
--- a/services/bedrockruntime/handler_invoke_test.go
+++ b/services/bedrockruntime/handler_invoke_test.go
@@ -3,7 +3,7 @@ package bedrockruntime_test
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- InvokeModel tests ---
@@ -60,7 +62,7 @@ func TestHandler_InvokeModel(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out, tt.wantKey)
invocations := h.Backend.ListInvocations()
@@ -107,7 +109,7 @@ func TestHandler_InvokeModel_ModelFamilies(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out, tt.wantKey)
})
}
@@ -160,7 +162,7 @@ func TestInvokeModel_AcceptHeader_JSON(t *testing.T) {
t.Parallel()
h := newTestHandler(t)
- body, _ := json.Marshal(map[string]any{"prompt": "Hello"})
+ body, _ := json.Marshal(map[string]any{"prompt": "Hello"}, jsonx.V1())
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/model/anthropic.claude-v2/invoke", bytes.NewReader(body))
@@ -180,7 +182,7 @@ func TestInvokeModel_AcceptHeader_Wildcard(t *testing.T) {
t.Parallel()
h := newTestHandler(t)
- body, _ := json.Marshal(map[string]any{"prompt": "Hello"})
+ body, _ := json.Marshal(map[string]any{"prompt": "Hello"}, jsonx.V1())
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/model/anthropic.claude-v2/invoke", bytes.NewReader(body))
@@ -342,7 +344,7 @@ func TestInvocationTruncation(t *testing.T) {
largePrompt[i] = 'x'
}
- body, _ := json.Marshal(map[string]any{"prompt": string(largePrompt)})
+ body, _ := json.Marshal(map[string]any{"prompt": string(largePrompt)}, jsonx.V1())
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/model/anthropic.claude-v2/invoke", bytes.NewReader(body))
@@ -391,7 +393,7 @@ func TestInvokeModel_Claude3_MessagesAPIFormat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// Messages API format fields (not Claude 2 `completion` field)
assert.Contains(t, out, "id", "Claude 3 response must include 'id' field")
@@ -439,7 +441,7 @@ func TestInvokeModel_Claude2_LegacyCompletionFormat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// Legacy completion format
assert.Contains(t, out, "completion", "Claude 2 response must use legacy 'completion' field")
@@ -478,7 +480,7 @@ func TestInvokeModel_ARNModelIDWithEmbeddedSlash(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// Claude 3 Messages API format fields (not the legacy `completion` field
// that the truncated-ARN bug would have produced).
@@ -579,7 +581,7 @@ func TestInvokeModelWithResponseStream_ChunkPayloadIsBase64WrappedBytes(t *testi
require.NoError(t, err)
var modelResp map[string]any
- require.NoError(t, json.Unmarshal(decoded, &modelResp))
+ require.NoError(t, json.Unmarshal(decoded, &modelResp, jsonx.V1()))
assert.Equal(t, "end_turn", modelResp["stop_reason"])
assert.Equal(t, "This is a mock response from Gopherstack.", modelResp["completion"])
}
@@ -614,7 +616,7 @@ func TestInvokeModel_GuardrailIdentifierWithoutVersion_Returns400(t *testing.T)
t.Parallel()
h := newTestHandler(t)
- body, _ := json.Marshal(map[string]any{"prompt": "Hello"})
+ body, _ := json.Marshal(map[string]any{"prompt": "Hello"}, jsonx.V1())
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/model/anthropic.claude-v2/invoke", bytes.NewReader(body))
@@ -629,7 +631,7 @@ func TestInvokeModel_GuardrailIdentifierWithoutVersion_Returns400(t *testing.T)
assert.Equal(t, http.StatusBadRequest, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "ValidationException", out["__type"])
}
@@ -637,7 +639,7 @@ func TestInvokeModel_GuardrailIdentifierWithVersion_Succeeds(t *testing.T) {
t.Parallel()
h := newTestHandler(t)
- body, _ := json.Marshal(map[string]any{"prompt": "Hello"})
+ body, _ := json.Marshal(map[string]any{"prompt": "Hello"}, jsonx.V1())
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/model/anthropic.claude-v2/invoke", bytes.NewReader(body))
@@ -657,7 +659,7 @@ func TestInvokeModelWithResponseStream_GuardrailIdentifierWithoutVersion_Returns
t.Parallel()
h := newTestHandler(t)
- body, _ := json.Marshal(map[string]any{"prompt": "Hello"})
+ body, _ := json.Marshal(map[string]any{"prompt": "Hello"}, jsonx.V1())
e := echo.New()
req := httptest.NewRequest(
@@ -680,7 +682,7 @@ func TestInvokeModel_PerformanceConfigLatency_EchoedInResponse(t *testing.T) {
t.Parallel()
h := newTestHandler(t)
- body, _ := json.Marshal(map[string]any{"prompt": "Hello"})
+ body, _ := json.Marshal(map[string]any{"prompt": "Hello"}, jsonx.V1())
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/model/anthropic.claude-v2/invoke", bytes.NewReader(body))
@@ -711,7 +713,7 @@ func TestInvokeModelWithResponseStream_PerformanceConfigLatency_EchoedInResponse
t.Parallel()
h := newTestHandler(t)
- body, _ := json.Marshal(map[string]any{"prompt": "Hello"})
+ body, _ := json.Marshal(map[string]any{"prompt": "Hello"}, jsonx.V1())
e := echo.New()
req := httptest.NewRequest(
diff --git a/services/bedrockruntime/handler_list_async_invokes_filters_test.go b/services/bedrockruntime/handler_list_async_invokes_filters_test.go
index 7a426a43ff..cf64a4537c 100644
--- a/services/bedrockruntime/handler_list_async_invokes_filters_test.go
+++ b/services/bedrockruntime/handler_list_async_invokes_filters_test.go
@@ -1,7 +1,7 @@
package bedrockruntime_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func startAsyncInvokeBody(i int) map[string]any {
@@ -71,7 +73,7 @@ func TestHandler_ListAsyncInvokes_SubmitTimeFilters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries, ok := out["asyncInvokeSummaries"].([]any)
require.True(t, ok)
@@ -93,7 +95,7 @@ func TestHandler_ListAsyncInvokes_SortOrder(t *testing.T) {
require.Equal(t, http.StatusAccepted, rec1.Code)
var firstBody map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &firstBody))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &firstBody, jsonx.V1()))
firstARN, ok := firstBody["invocationArn"].(string)
require.True(t, ok)
@@ -103,7 +105,7 @@ func TestHandler_ListAsyncInvokes_SortOrder(t *testing.T) {
require.Equal(t, http.StatusAccepted, rec2.Code)
var secondBody map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &secondBody))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &secondBody, jsonx.V1()))
secondARN, ok := secondBody["invocationArn"].(string)
require.True(t, ok)
@@ -111,7 +113,7 @@ func TestHandler_ListAsyncInvokes_SortOrder(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries, ok := out["asyncInvokeSummaries"].([]any)
require.True(t, ok)
diff --git a/services/bedrockruntime/handler_sdk_route_table_test.go b/services/bedrockruntime/handler_sdk_route_table_test.go
index c419ca717e..e371c8e2ae 100644
--- a/services/bedrockruntime/handler_sdk_route_table_test.go
+++ b/services/bedrockruntime/handler_sdk_route_table_test.go
@@ -1,7 +1,7 @@
package bedrockruntime_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http/httptest"
"strings"
"testing"
@@ -9,6 +9,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// sdkRouteCases is the authoritative method+path for every real Bedrock
@@ -97,7 +99,7 @@ func TestExtractOperation_SDKRouteTable(t *testing.T) {
var decoded struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &decoded))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &decoded, jsonx.V1()))
assert.NotEqual(t, "UnknownOperationException", decoded.Type,
"method=%s path=%s op=%s: dispatched to the unmatched-route default", tc.method, tc.path, tc.op)
}
diff --git a/services/bedrockruntime/handler_test.go b/services/bedrockruntime/handler_test.go
index afca33582c..d5d9c0c61b 100644
--- a/services/bedrockruntime/handler_test.go
+++ b/services/bedrockruntime/handler_test.go
@@ -2,7 +2,7 @@ package bedrockruntime_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/bedrockruntime"
)
@@ -34,7 +35,7 @@ func doRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
diff --git a/services/bedrockruntime/janitor_interval_test.go b/services/bedrockruntime/janitor_interval_test.go
index 68f10b209c..ee17b67cf0 100644
--- a/services/bedrockruntime/janitor_interval_test.go
+++ b/services/bedrockruntime/janitor_interval_test.go
@@ -2,7 +2,7 @@ package bedrockruntime_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/bedrockruntime"
)
@@ -44,7 +45,7 @@ func TestStartWorker_AdvancesAsyncInvoke_NearCompletionDelay(t *testing.T) {
require.Equal(t, http.StatusAccepted, rec.Code)
var started map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &started))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &started, jsonx.V1()))
arn, ok := started["invocationArn"].(string)
require.True(t, ok)
@@ -57,7 +58,7 @@ func TestStartWorker_AdvancesAsyncInvoke_NearCompletionDelay(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, bedrockruntime.AsyncInvokeStatusCompleted, got["status"])
})
diff --git a/services/bedrockruntime/ollama_backend_test.go b/services/bedrockruntime/ollama_backend_test.go
index 7f9416e617..3680e2d514 100644
--- a/services/bedrockruntime/ollama_backend_test.go
+++ b/services/bedrockruntime/ollama_backend_test.go
@@ -1,7 +1,8 @@
package bedrockruntime_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"maps"
@@ -22,6 +23,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/sdktest"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/bedrockruntime"
@@ -93,7 +95,7 @@ func newFakeOllama(t *testing.T, reply func(path string, req map[string]any) fak
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
raw, _ := io.ReadAll(r.Body)
var req map[string]any
- _ = json.Unmarshal(raw, &req)
+ _ = json.Unmarshal(raw, &req, jsonx.V1())
rec.mu.Lock()
rec.reqs = append(rec.reqs, req)
rec.path = append(rec.path, r.URL.Path)
@@ -102,20 +104,20 @@ func newFakeOllama(t *testing.T, reply func(path string, req map[string]any) fak
rp := reply(r.URL.Path, req)
if rp.status != 0 {
w.WriteHeader(rp.status)
- _ = json.NewEncoder(w).Encode(map[string]string{"error": rp.errMsg})
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), map[string]string{"error": rp.errMsg})
return
}
w.Header().Set("Content-Type", "application/x-ndjson")
- enc := json.NewEncoder(w)
+ enc := jsontext.NewEncoder(w, jsonx.V1())
if stream, _ := req["stream"].(bool); !stream {
- _ = enc.Encode(collapse(rp.lines))
+ _ = json.MarshalEncode(enc, collapse(rp.lines))
return
}
for _, l := range rp.lines {
- _ = enc.Encode(l)
+ _ = json.MarshalEncode(enc, l)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
@@ -734,7 +736,7 @@ func TestOllamaBackend_InvokeModel(t *testing.T) {
assert.Equal(t, tc.wantPath, path)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body, &out))
+ require.NoError(t, json.Unmarshal(resp.Body, &out, jsonx.V1()))
tc.check(t, req, out)
})
}
@@ -849,7 +851,7 @@ func TestOllamaBackend_InvokeModelStream(t *testing.T) {
part, ok := ev.(*types.ResponseStreamMemberChunk)
require.True(t, ok, "unexpected event %T", ev)
var m map[string]any
- require.NoError(t, json.Unmarshal(part.Value.Bytes, &m))
+ require.NoError(t, json.Unmarshal(part.Value.Bytes, &m, jsonx.V1()))
chunks = append(chunks, m)
}
require.NoError(t, stream.Err())
diff --git a/services/bedrockruntime/ollama_client.go b/services/bedrockruntime/ollama_client.go
index c4c5f3367f..c4043853a4 100644
--- a/services/bedrockruntime/ollama_client.go
+++ b/services/bedrockruntime/ollama_client.go
@@ -3,12 +3,15 @@ package bedrockruntime
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
"net/http"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const maxOllamaErrBody = 4096
@@ -22,10 +25,10 @@ type ollamaOptions struct {
}
type ollamaToolFunc struct {
- Name string `json:"name"`
- Description string `json:"description,omitempty"`
- Arguments json.RawMessage `json:"arguments,omitempty"`
- Parameters json.RawMessage `json:"parameters,omitempty"`
+ Name string `json:"name"`
+ Description string `json:"description,omitempty"`
+ Arguments jsontext.Value `json:"arguments,omitempty"`
+ Parameters jsontext.Value `json:"parameters,omitempty"`
}
type ollamaToolCall struct {
@@ -135,7 +138,7 @@ func (e *ollamaStatusError) Error() string {
func (o *ollamaBackend) post(ctx context.Context, call *ollamaCall) (*http.Response, error) {
endpoint, payload := call.endpoint()
- raw, err := json.Marshal(payload)
+ raw, err := json.Marshal(payload, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("marshal ollama request: %w", err)
}
@@ -159,7 +162,7 @@ func (o *ollamaBackend) post(ctx context.Context, call *ollamaCall) (*http.Respo
msg := strings.TrimSpace(string(b))
var e ollamaChunk
- if json.Unmarshal(b, &e) == nil && e.Error != "" {
+ if json.Unmarshal(b, &e, jsonx.V1()) == nil && e.Error != "" {
msg = e.Error
}
@@ -185,12 +188,12 @@ func (o *ollamaBackend) run(
defer resp.Body.Close()
res := &ollamaResult{}
- dec := json.NewDecoder(resp.Body)
+ dec := jsontext.NewDecoder(resp.Body)
for {
var ch ollamaChunk
- if derr := dec.Decode(&ch); derr != nil {
+ if derr := json.UnmarshalDecode(dec, &ch, jsonx.V1()); derr != nil {
return nil, fmt.Errorf("%w: reading ollama response: %w", errOllamaUnreachable, derr)
}
diff --git a/services/bedrockruntime/ollama_converse.go b/services/bedrockruntime/ollama_converse.go
index 69f9b52c7d..46a25ee42d 100644
--- a/services/bedrockruntime/ollama_converse.go
+++ b/services/bedrockruntime/ollama_converse.go
@@ -3,7 +3,8 @@ package bedrockruntime
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -14,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -50,16 +52,16 @@ type cvDocument struct {
}
type cvToolUse struct {
- ToolUseID string `json:"toolUseId"`
- Name string `json:"name"`
- Input json.RawMessage `json:"input"`
+ ToolUseID string `json:"toolUseId"`
+ Name string `json:"name"`
+ Input jsontext.Value `json:"input"`
}
type cvToolResult struct {
ToolUseID string `json:"toolUseId"`
Content []struct {
- Text *string `json:"text"`
- JSON json.RawMessage `json:"json"`
+ Text *string `json:"text"`
+ JSON jsontext.Value `json:"json"`
} `json:"content"`
}
@@ -73,8 +75,8 @@ type cvSystemBlock struct {
}
type cvRequest struct {
- InferenceConfig json.RawMessage `json:"inferenceConfig"`
- ToolConfig json.RawMessage `json:"toolConfig"`
+ InferenceConfig jsontext.Value `json:"inferenceConfig"`
+ ToolConfig jsontext.Value `json:"toolConfig"`
Messages []cvMessage `json:"messages"`
System []cvSystemBlock `json:"system"`
}
@@ -185,15 +187,15 @@ func ollamaMessages(req *cvRequest) ([]ollamaMessage, error) {
return out, nil
}
-func objectOrEmpty(raw json.RawMessage) json.RawMessage {
+func objectOrEmpty(raw jsontext.Value) jsontext.Value {
if len(raw) == 0 || string(raw) == "null" {
- return json.RawMessage("{}")
+ return jsontext.Value("{}")
}
return raw
}
-func ollamaTools(raw json.RawMessage) []ollamaTool {
+func ollamaTools(raw jsontext.Value) []ollamaTool {
if len(raw) == 0 {
return nil
}
@@ -204,12 +206,12 @@ func ollamaTools(raw json.RawMessage) []ollamaTool {
Name string `json:"name"`
Description string `json:"description"`
InputSchema struct {
- JSON json.RawMessage `json:"json"`
+ JSON jsontext.Value `json:"json"`
} `json:"inputSchema"`
} `json:"toolSpec"`
} `json:"tools"`
}
- if json.Unmarshal(raw, &tc) != nil {
+ if json.Unmarshal(raw, &tc, jsonx.V1()) != nil {
return nil
}
@@ -228,13 +230,13 @@ func ollamaTools(raw json.RawMessage) []ollamaTool {
return out
}
-func ollamaOptionsFromInference(raw json.RawMessage) *ollamaOptions {
+func ollamaOptionsFromInference(raw jsontext.Value) *ollamaOptions {
if len(raw) == 0 {
return nil
}
var ic inferenceConfigBody
- if json.Unmarshal(raw, &ic) != nil {
+ if json.Unmarshal(raw, &ic, jsonx.V1()) != nil {
return nil
}
@@ -248,7 +250,7 @@ func (o *ollamaBackend) converseCall(modelID string, body []byte) (*ollamaCall,
}
var req cvRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errUnsupportedContent, err)
}
@@ -335,7 +337,7 @@ func (h *Handler) handleOllamaConverse(
resp := buildOllamaConverseResponse(res, time.Since(start))
echoConverseConfig(resp, req)
- out, err := json.Marshal(resp)
+ out, err := json.Marshal(resp, jsonx.V1())
if err != nil {
return c.JSON(http.StatusInternalServerError, errorResponse("InternalServerException", "internal server error"))
}
@@ -421,7 +423,7 @@ func (s *converseStreamer) finish(res *ollamaResult, meta map[string]any) {
func frameWriter(c *echo.Context) (func(string, any), func(string, string)) {
write := func(headers [][2]string, payload any) {
- data, err := json.Marshal(payload)
+ data, err := json.Marshal(payload, jsonx.V1())
if err != nil {
return
}
@@ -486,7 +488,7 @@ func (h *Handler) handleOllamaConverseStream(
}
func mustJSON(v any) []byte {
- b, _ := json.Marshal(v)
+ b, _ := json.Marshal(v, jsonx.V1())
return b
}
diff --git a/services/bedrockruntime/ollama_invoke.go b/services/bedrockruntime/ollama_invoke.go
index 4bc547f5a9..6c7d0a3999 100644
--- a/services/bedrockruntime/ollama_invoke.go
+++ b/services/bedrockruntime/ollama_invoke.go
@@ -1,7 +1,8 @@
package bedrockruntime
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -12,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -82,7 +84,7 @@ func familyFor(id string) familyBuilder {
}
func parseBody(body []byte, v any) error {
- if err := json.Unmarshal(body, v); err != nil {
+ if err := json.Unmarshal(body, v, jsonx.V1()); err != nil {
return malformed(err)
}
@@ -108,13 +110,13 @@ type anthropicBlock struct {
Source *struct {
Data string `json:"data"`
} `json:"source"`
- Type string `json:"type"`
- Text string `json:"text"`
- ID string `json:"id"`
- Name string `json:"name"`
- ToolUseID string `json:"tool_use_id"`
- Input json.RawMessage `json:"input"`
- Content json.RawMessage `json:"content"`
+ Type string `json:"type"`
+ Text string `json:"text"`
+ ID string `json:"id"`
+ Name string `json:"name"`
+ ToolUseID string `json:"tool_use_id"`
+ Input jsontext.Value `json:"input"`
+ Content jsontext.Value `json:"content"`
}
type anthropicRequest struct {
@@ -125,36 +127,36 @@ type anthropicRequest struct {
TopK *int `json:"top_k"`
Prompt string `json:"prompt"`
Messages []struct {
- Role string `json:"role"`
- Content json.RawMessage `json:"content"`
+ Role string `json:"role"`
+ Content jsontext.Value `json:"content"`
} `json:"messages"`
- System json.RawMessage `json:"system"`
+ System jsontext.Value `json:"system"`
Tools []anthropicTool `json:"tools"`
StopSequences []string `json:"stop_sequences"`
}
type anthropicTool struct {
- Name string `json:"name"`
- Description string `json:"description"`
- InputSchema json.RawMessage `json:"input_schema"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ InputSchema jsontext.Value `json:"input_schema"`
}
// anthropicBlocks reads a content value that is either a string or an array of blocks.
-func anthropicBlocks(raw json.RawMessage) ([]anthropicBlock, error) {
+func anthropicBlocks(raw jsontext.Value) ([]anthropicBlock, error) {
var s string
- if json.Unmarshal(raw, &s) == nil {
+ if json.Unmarshal(raw, &s, jsonx.V1()) == nil {
return []anthropicBlock{{Type: typeText, Text: s}}, nil
}
var blocks []anthropicBlock
- if err := json.Unmarshal(raw, &blocks); err != nil {
+ if err := json.Unmarshal(raw, &blocks, jsonx.V1()); err != nil {
return nil, malformed(err)
}
return blocks, nil
}
-func anthropicToCv(role string, raw json.RawMessage) (cvMessage, error) {
+func anthropicToCv(role string, raw jsontext.Value) (cvMessage, error) {
blocks, err := anthropicBlocks(raw)
if err != nil {
return cvMessage{}, err
@@ -188,8 +190,8 @@ func anthropicToCv(role string, raw json.RawMessage) (cvMessage, error) {
for _, ib := range inner {
t := ib.Text
res.Content = append(res.Content, struct {
- Text *string `json:"text"`
- JSON json.RawMessage `json:"json"`
+ Text *string `json:"text"`
+ JSON jsontext.Value `json:"json"`
}{Text: &t})
}
@@ -202,7 +204,7 @@ func anthropicToCv(role string, raw json.RawMessage) (cvMessage, error) {
return m, nil
}
-func anthropicSystem(raw json.RawMessage) ([]cvSystemBlock, error) {
+func anthropicSystem(raw jsontext.Value) ([]cvSystemBlock, error) {
if len(raw) == 0 {
return nil, nil
}
@@ -907,7 +909,7 @@ func (h *Handler) handleOllamaInvoke(c *echo.Context, o *ollamaBackend, modelID
return writeInvokeError(c, err)
}
- out, err := json.Marshal(plan.render(res))
+ out, err := json.Marshal(plan.render(res), jsonx.V1())
if err != nil {
return c.JSON(http.StatusInternalServerError, errorResponse("InternalServerException", "internal server error"))
}
@@ -952,7 +954,7 @@ func (h *Handler) handleOllamaInvokeStream(c *echo.Context, o *ollamaBackend, mo
}
for _, ch := range chunks {
- emit("chunk", json.RawMessage(modelResponsePayloadPart(mustJSON(ch))))
+ emit("chunk", jsontext.Value(modelResponsePayloadPart(mustJSON(ch))))
}
}
diff --git a/services/bedrockruntime/persistence.go b/services/bedrockruntime/persistence.go
index fe2f73ed61..1bcb0ecb35 100644
--- a/services/bedrockruntime/persistence.go
+++ b/services/bedrockruntime/persistence.go
@@ -2,7 +2,7 @@ package bedrockruntime
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -45,13 +45,13 @@ const bedrockruntimeSnapshotVersion = 1
// freshly restored backend never reissues an ARN already handed out
// before the snapshot.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- TokenIndex map[string]string `json:"tokenIndex"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Invocations []*Invocation `json:"invocations"`
- AsyncInvokeCounter int `json:"asyncInvokeCounter"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ TokenIndex map[string]string `json:"tokenIndex"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Invocations []*Invocation `json:"invocations"`
+ AsyncInvokeCounter int `json:"asyncInvokeCounter"`
+ Version int `json:"version"`
}
// Snapshot serializes the backend state to JSON. It implements
diff --git a/services/bedrockruntime/wire_field_fixes_y1zn_test.go b/services/bedrockruntime/wire_field_fixes_y1zn_test.go
index b090888ebb..a7436123ba 100644
--- a/services/bedrockruntime/wire_field_fixes_y1zn_test.go
+++ b/services/bedrockruntime/wire_field_fixes_y1zn_test.go
@@ -1,7 +1,7 @@
package bedrockruntime_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
brtypes "github.com/aws/aws-sdk-go-v2/service/bedrockruntime/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestGetAsyncInvoke_NoInventedTagsKey_RealClient covers gopherstack-y1zn.
@@ -38,7 +40,7 @@ func TestGetAsyncInvoke_NoInventedTagsKey_RealClient(t *testing.T) {
"neither GetAsyncInvokeOutput nor AsyncInvokeSummary has a tags member")
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
_, hasTags := out["tags"]
assert.False(t, hasTags)
}
diff --git a/services/budgets/budgets.go b/services/budgets/budgets.go
index fd160e7631..5a6e8292ed 100644
--- a/services/budgets/budgets.go
+++ b/services/budgets/budgets.go
@@ -3,19 +3,20 @@ package budgets
import (
"cmp"
"context"
- "encoding/json"
+ "encoding/json/v2"
"slices"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
func deepCopy[T any](v *T) *T {
- data, _ := json.Marshal(v)
+ data, _ := json.Marshal(v, jsonx.V1())
var out T
- _ = json.Unmarshal(data, &out)
+ _ = json.Unmarshal(data, &out, jsonx.V1())
return &out
}
diff --git a/services/budgets/handler.go b/services/budgets/handler.go
index 8fcbcd5ecb..586e5d6e3c 100644
--- a/services/budgets/handler.go
+++ b/services/budgets/handler.go
@@ -2,7 +2,7 @@ package budgets
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"slices"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -37,7 +38,7 @@ func bind[I, O any](fn func(*Handler, context.Context, *I) (*O, error)) opFunc {
var in I
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fail(ErrInvalidParameter, "malformed request body: %v", err)
}
}
diff --git a/services/budgets/persistence.go b/services/budgets/persistence.go
index 73edc0e1e0..79dffffc92 100644
--- a/services/budgets/persistence.go
+++ b/services/budgets/persistence.go
@@ -2,7 +2,7 @@ package budgets
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,10 +12,10 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/budgets/wire.go b/services/budgets/wire.go
index 595592bc5b..572f8284ed 100644
--- a/services/budgets/wire.go
+++ b/services/budgets/wire.go
@@ -1,10 +1,11 @@
package budgets
import (
- "encoding/json"
+ "encoding/json/v2"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Wire shapes follow the pinned aws-sdk-go-v2/service/budgets v1.53.1 awsjson1.1 schemas;
@@ -14,12 +15,14 @@ import (
type Timestamp struct{ time.Time }
// MarshalJSON renders the epoch-seconds number the SDK expects.
-func (t Timestamp) MarshalJSON() ([]byte, error) { return json.Marshal(awstime.Epoch(t.Time)) }
+func (t Timestamp) MarshalJSON() ([]byte, error) {
+ return json.Marshal(awstime.Epoch(t.Time), jsonx.V1())
+}
// UnmarshalJSON reads an epoch-seconds number.
func (t *Timestamp) UnmarshalJSON(data []byte) error {
var f float64
- if err := json.Unmarshal(data, &f); err != nil {
+ if err := json.Unmarshal(data, &f, jsonx.V1()); err != nil {
return err
}
diff --git a/services/ce/cost_usage_granularity_test.go b/services/ce/cost_usage_granularity_test.go
index d4863146b6..d1c5189010 100644
--- a/services/ce/cost_usage_granularity_test.go
+++ b/services/ce/cost_usage_granularity_test.go
@@ -1,12 +1,15 @@
package ce_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// wireError decodes the AWS JSON-protocol error envelope (__type/message).
@@ -45,7 +48,7 @@ func TestGetCostAndUsage_InvalidGranularityRejected(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var wireErr wireError
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&wireErr))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &wireErr, jsonx.V1()))
assert.Equal(t, "ValidationError", wireErr.Type)
})
}
@@ -74,7 +77,7 @@ func TestGetCostAndUsage_HourlyGranularityBucketsHourly(t *testing.T) {
TimePeriod map[string]string `json:"TimePeriod"`
} `json:"ResultsByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.Len(t, out.ResultsByTime, 24, "one bucket per hour across a single day")
assert.Equal(t, "2024-01-01T00:00:00Z", out.ResultsByTime[0].TimePeriod["Start"])
diff --git a/services/ce/cost_usage_timeperiod_test.go b/services/ce/cost_usage_timeperiod_test.go
index af5286da01..e9944d984d 100644
--- a/services/ce/cost_usage_timeperiod_test.go
+++ b/services/ce/cost_usage_timeperiod_test.go
@@ -1,12 +1,15 @@
package ce_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestGetCostAndUsage_UnparseableTimePeriodRejected verifies that a
@@ -43,7 +46,7 @@ func TestGetCostAndUsage_UnparseableTimePeriodRejected(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var wireErr wireError
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&wireErr))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &wireErr, jsonx.V1()))
assert.Equal(t, "ValidationError", wireErr.Type)
})
}
diff --git a/services/ce/handler.go b/services/ce/handler.go
index fa4666a143..9ee3720c0c 100644
--- a/services/ce/handler.go
+++ b/services/ce/handler.go
@@ -2,7 +2,7 @@ package ce
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -196,19 +197,16 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, ErrUnknownMonitor):
payload, _ := json.Marshal(service.JSONErrorResponse{
Type: "UnknownMonitorException",
Message: err.Error(),
- })
+ }, jsonx.V1())
// Real AWS CE returns HTTP 400 for every modeled client-fault exception (verified
// against API_DeleteAnomalyMonitor/API_UpdateAnomalyMonitor/API_GetAnomalyMonitors),
@@ -218,21 +216,21 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
payload, _ := json.Marshal(service.JSONErrorResponse{
Type: "InvalidNextTokenException",
Message: err.Error(),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
case errors.Is(err, ErrUnknownSubscription):
payload, _ := json.Marshal(service.JSONErrorResponse{
Type: "UnknownSubscriptionException",
Message: err.Error(),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
case errors.Is(err, ErrNotFound):
payload, _ := json.Marshal(service.JSONErrorResponse{
Type: "ResourceNotFoundException",
Message: err.Error(),
- })
+ }, jsonx.V1())
// See API_DescribeCostCategoryDefinition: ResourceNotFoundException is documented
// as HTTP 400, not 404.
@@ -241,7 +239,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
payload, _ := json.Marshal(service.JSONErrorResponse{
Type: "ServiceQuotaExceededException",
Message: err.Error(),
- })
+ }, jsonx.V1())
// See API_CreateCostCategoryDefinition: ServiceQuotaExceededException is documented
// as HTTP 400, not 409.
@@ -250,18 +248,18 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
payload, _ := json.Marshal(service.JSONErrorResponse{
Type: "ValidationError",
Message: err.Error(),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
// "ValidationError" is CE's documented CommonErrors type for malformed/
// missing-required-member requests (see ErrValidation above) -- it is not
// tied to any one operation's model, so it fits here too.
case errors.Is(err, errInvalidRequest), errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
payload, _ := json.Marshal(service.JSONErrorResponse{
Type: "ValidationError",
Message: err.Error(),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
default:
diff --git a/services/ce/handler_anomalies_test.go b/services/ce/handler_anomalies_test.go
index 78bd01f3b8..dc3154adb3 100644
--- a/services/ce/handler_anomalies_test.go
+++ b/services/ce/handler_anomalies_test.go
@@ -1,7 +1,8 @@
package ce_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ce"
)
@@ -29,7 +31,7 @@ func TestGetAnomalyMonitors_HasCreationDateAndLastUpdatedDate(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
monARN := createOut["MonitorArn"].(string)
// Describe it
@@ -45,7 +47,7 @@ func TestGetAnomalyMonitors_HasCreationDateAndLastUpdatedDate(t *testing.T) {
LastUpdatedDate string `json:"LastUpdatedDate"`
} `json:"AnomalyMonitors"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
require.Len(t, getOut.AnomalyMonitors, 1)
m := getOut.AnomalyMonitors[0]
@@ -76,7 +78,7 @@ func TestGetAnomalyMonitors_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
page1 := out1["AnomalyMonitors"].([]any)
assert.Len(t, page1, 2)
nextToken, ok := out1["NextPageToken"].(string)
@@ -89,7 +91,7 @@ func TestGetAnomalyMonitors_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
page2 := out2["AnomalyMonitors"].([]any)
assert.Len(t, page2, 2)
}
@@ -110,7 +112,7 @@ func TestGetAnomalySubscriptions_HasAccountId(t *testing.T) {
require.Equal(t, http.StatusOK, monRec.Code)
var monOut map[string]any
- require.NoError(t, json.Unmarshal(monRec.Body.Bytes(), &monOut))
+ require.NoError(t, json.Unmarshal(monRec.Body.Bytes(), &monOut, jsonx.V1()))
monARN := monOut["MonitorArn"].(string)
subRec := doRequest(t, h, "CreateAnomalySubscription", map[string]any{
@@ -133,7 +135,7 @@ func TestGetAnomalySubscriptions_HasAccountId(t *testing.T) {
AccountID string `json:"AccountId"`
} `json:"AnomalySubscriptions"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
require.Len(t, getOut.AnomalySubscriptions, 1)
assert.NotEmpty(t, getOut.AnomalySubscriptions[0].AccountID, "AccountId must be present in subscription")
}
@@ -153,7 +155,7 @@ func TestGetAnomalySubscriptions_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, monRec.Code)
var monOut map[string]any
- require.NoError(t, json.Unmarshal(monRec.Body.Bytes(), &monOut))
+ require.NoError(t, json.Unmarshal(monRec.Body.Bytes(), &monOut, jsonx.V1()))
monARN := monOut["MonitorArn"].(string)
for i := range 5 {
@@ -176,7 +178,7 @@ func TestGetAnomalySubscriptions_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
page1 := out1["AnomalySubscriptions"].([]any)
assert.Len(t, page1, 2)
nextToken, ok := out1["NextPageToken"].(string)
@@ -203,7 +205,7 @@ func TestHandler_AnomalyMonitorCRUD(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out["MonitorArn"])
monARN := out["MonitorArn"].(string)
@@ -227,7 +229,7 @@ func TestHandler_AnomalyMonitorCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createOut, jsonx.V1()))
monARN := createOut["MonitorArn"].(string)
rec2 := doRequest(t, h, "UpdateAnomalyMonitor", map[string]any{
@@ -250,7 +252,7 @@ func TestHandler_AnomalyMonitorCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createOut, jsonx.V1()))
monARN := createOut["MonitorArn"].(string)
rec2 := doRequest(t, h, "DeleteAnomalyMonitor", map[string]any{
@@ -295,7 +297,7 @@ func TestHandler_AnomalySubscriptionCRUD(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out["SubscriptionArn"])
},
},
@@ -316,7 +318,7 @@ func TestHandler_AnomalySubscriptionCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createOut, jsonx.V1()))
subARN := createOut["SubscriptionArn"].(string)
rec2 := doRequest(t, h, "DeleteAnomalySubscription", map[string]any{
@@ -399,7 +401,7 @@ func TestHandler_GetAnomalySubscriptions(t *testing.T) {
var out struct {
AnomalySubscriptions []map[string]any `json:"AnomalySubscriptions"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.AnomalySubscriptions, tt.wantLen)
})
}
@@ -461,7 +463,7 @@ func TestHandler_UpdateAnomalySubscription(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
tt.updateBody["SubscriptionArn"] = createOut["SubscriptionArn"]
}
@@ -470,7 +472,7 @@ func TestHandler_UpdateAnomalySubscription(t *testing.T) {
if tt.wantFrequency != "" {
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out["SubscriptionArn"])
}
})
@@ -650,7 +652,7 @@ func TestHandler_GetAnomalySubscriptions_MonitorArnFilter(t *testing.T) {
require.Equal(t, http.StatusOK, monRec.Code)
var monOut map[string]any
- require.NoError(t, json.NewDecoder(monRec.Body).Decode(&monOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(monRec.Body), &monOut, jsonx.V1()))
monARN := monOut["MonitorArn"].(string)
// Create subscription attached to monitor.
@@ -693,7 +695,7 @@ func TestHandler_GetAnomalySubscriptions_MonitorArnFilter(t *testing.T) {
var out struct {
AnomalySubscriptions []map[string]any `json:"AnomalySubscriptions"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.AnomalySubscriptions, tt.wantLen)
})
}
@@ -765,7 +767,7 @@ func TestHandler_UpdateAnomalySubscription_AllBranches(t *testing.T) {
require.Equal(t, http.StatusOK, monRec.Code)
var monOut map[string]any
- require.NoError(t, json.NewDecoder(monRec.Body).Decode(&monOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(monRec.Body), &monOut, jsonx.V1()))
monARN := monOut["MonitorArn"].(string)
arns, ok := tt.updateBody["MonitorArnList"].([]string)
@@ -786,7 +788,7 @@ func TestHandler_UpdateAnomalySubscription_AllBranches(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
subARN := createOut["SubscriptionArn"].(string)
tt.updateBody["SubscriptionArn"] = subARN
@@ -807,7 +809,7 @@ func TestHandler_UpdateAnomalySubscription_AllBranches(t *testing.T) {
Threshold float64 `json:"Threshold"`
} `json:"AnomalySubscriptions"`
}
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&getOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &getOut, jsonx.V1()))
require.Len(t, getOut.AnomalySubscriptions, 1)
assert.Equal(t, tt.wantFrequency, getOut.AnomalySubscriptions[0].Frequency)
assert.Equal(t, tt.wantSubName, getOut.AnomalySubscriptions[0].SubscriptionName)
@@ -939,7 +941,7 @@ func TestHandler_UpdateAnomalyMonitor_OmittedNameUnchanged(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
monARN := createOut["MonitorArn"].(string)
updateRec := doRequest(t, h, "UpdateAnomalyMonitor", map[string]any{
@@ -957,7 +959,7 @@ func TestHandler_UpdateAnomalyMonitor_OmittedNameUnchanged(t *testing.T) {
MonitorName string `json:"MonitorName"`
} `json:"AnomalyMonitors"`
}
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&getOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &getOut, jsonx.V1()))
require.Len(t, getOut.AnomalyMonitors, 1)
assert.Equal(t, "KeepMyName", getOut.AnomalyMonitors[0].MonitorName)
}
diff --git a/services/ce/handler_anomaly_detection_test.go b/services/ce/handler_anomaly_detection_test.go
index f5b2792771..f318abe41a 100644
--- a/services/ce/handler_anomaly_detection_test.go
+++ b/services/ce/handler_anomaly_detection_test.go
@@ -1,13 +1,15 @@
package ce_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ce"
)
@@ -47,7 +49,7 @@ func TestAnomalyFeedback_PreviousFeedbackOverwritten(t *testing.T) {
Feedback string `json:"Feedback"`
} `json:"Anomalies"`
}
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &out, jsonx.V1()))
require.Len(t, out.Anomalies, 1)
assert.Equal(t, "feedback-overwrite-1", out.Anomalies[0].AnomalyID)
assert.Equal(t, "NO", out.Anomalies[0].Feedback)
@@ -120,7 +122,7 @@ func TestProvideAnomalyFeedback_PersistsAndValidates(t *testing.T) {
Feedback string `json:"Feedback"`
} `json:"Anomalies"`
}
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &out, jsonx.V1()))
require.Len(t, out.Anomalies, 1)
assert.Equal(t, "feedback-test-1", out.Anomalies[0].AnomalyID)
assert.Equal(t, tt.wantFeedback, out.Anomalies[0].Feedback)
@@ -181,7 +183,7 @@ func TestGetAnomalies_ScoreAndImpactAreObjects(t *testing.T) {
} `json:"Impact"`
} `json:"Anomalies"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.Len(t, out.Anomalies, 1)
a := out.Anomalies[0]
@@ -223,7 +225,7 @@ func TestGetAnomalies_DateIntervalFilters(t *testing.T) {
AnomalyID string `json:"AnomalyId"`
} `json:"Anomalies"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Anomalies, 1)
assert.Equal(t, "recent-anomaly", out.Anomalies[0].AnomalyID)
}
@@ -283,7 +285,7 @@ func TestGetAnomalies_DateIntervalMatchesOnAnomalyEndDateOnly(t *testing.T) {
AnomalyID string `json:"AnomalyId"`
} `json:"Anomalies"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tt.want {
require.Len(t, out.Anomalies, 1)
@@ -317,7 +319,7 @@ func TestGetAnomalies_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
page1 := out1["Anomalies"].([]any)
assert.Len(t, page1, 2)
nextToken, ok := out1["NextPageToken"].(string)
@@ -370,7 +372,7 @@ func TestHandler_GetAnomalies(t *testing.T) {
var out struct {
Anomalies []any `json:"Anomalies"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotNil(t, out.Anomalies)
})
}
@@ -452,7 +454,7 @@ func TestHandler_GetAnomalies_Filters(t *testing.T) {
var out struct {
Anomalies []map[string]any `json:"Anomalies"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.Anomalies, tt.wantLen)
})
}
@@ -483,7 +485,7 @@ func TestHandler_SnapshotRestoreWithAnomalies(t *testing.T) {
var out struct {
Anomalies []map[string]any `json:"Anomalies"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.Anomalies, 1)
assert.Equal(t, "snap-anomaly-1", out.Anomalies[0]["AnomalyId"])
}
diff --git a/services/ce/handler_commitment_purchase_analysis_test.go b/services/ce/handler_commitment_purchase_analysis_test.go
index cbf96d14ed..a3709ce5ca 100644
--- a/services/ce/handler_commitment_purchase_analysis_test.go
+++ b/services/ce/handler_commitment_purchase_analysis_test.go
@@ -1,13 +1,15 @@
package ce_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ce"
)
@@ -28,7 +30,7 @@ func TestCommitmentAnalysis_MultipleStartsListed(t *testing.T) {
var out struct {
AnalysisID string `json:"AnalysisId"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotEmpty(t, out.AnalysisID)
analysisIDs = append(analysisIDs, out.AnalysisID)
}
@@ -48,7 +50,7 @@ func TestCommitmentAnalysis_MultipleStartsListed(t *testing.T) {
AnalysisID string `json:"AnalysisId"`
} `json:"AnalysisSummaryList"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
require.Len(t, listOut.AnalysisSummaryList, 3)
listedIDs := make(map[string]struct{}, 3)
@@ -81,7 +83,7 @@ func TestCommitmentPurchaseAnalysis_Lifecycle(t *testing.T) {
AnalysisStartedTime string `json:"AnalysisStartedTime"`
EstimatedCompletionTime string `json:"EstimatedCompletionTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &startOut, jsonx.V1()))
require.NotEmpty(t, startOut.AnalysisID)
assert.NotEmpty(t, startOut.AnalysisStartedTime)
assert.NotEmpty(t, startOut.EstimatedCompletionTime)
@@ -96,7 +98,7 @@ func TestCommitmentPurchaseAnalysis_Lifecycle(t *testing.T) {
AnalysisID string `json:"AnalysisId"`
AnalysisStatus string `json:"AnalysisStatus"`
}
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&getOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &getOut, jsonx.V1()))
assert.Equal(t, startOut.AnalysisID, getOut.AnalysisID)
assert.Equal(t, "PROCESSING", getOut.AnalysisStatus)
@@ -109,7 +111,7 @@ func TestCommitmentPurchaseAnalysis_Lifecycle(t *testing.T) {
AnalysisID string `json:"AnalysisId"`
} `json:"AnalysisSummaryList"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
require.Len(t, listOut.AnalysisSummaryList, 1)
assert.Equal(t, startOut.AnalysisID, listOut.AnalysisSummaryList[0].AnalysisID)
}
@@ -128,7 +130,7 @@ func TestSnapshotRestore_IncludesCommitmentAnalyses(t *testing.T) {
var startOut struct {
AnalysisID string `json:"AnalysisId"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &startOut, jsonx.V1()))
// Snapshot + restore
snap := h.Snapshot(t.Context())
diff --git a/services/ce/handler_cost_allocation_tags_test.go b/services/ce/handler_cost_allocation_tags_test.go
index db09476275..c21ef62bae 100644
--- a/services/ce/handler_cost_allocation_tags_test.go
+++ b/services/ce/handler_cost_allocation_tags_test.go
@@ -1,13 +1,15 @@
package ce_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ce"
)
@@ -22,7 +24,7 @@ func TestListCostAllocationTags_EmptyWithNoTags(t *testing.T) {
var out struct {
CostAllocationTags []any `json:"CostAllocationTags"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotNil(t, out.CostAllocationTags)
assert.Empty(t, out.CostAllocationTags)
}
@@ -81,7 +83,7 @@ func TestUpdateCostAllocationTagsStatus_MultipleUpdates(t *testing.T) {
var out struct {
Errors []any `json:"Errors"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.Errors, tt.wantErrors)
})
}
@@ -107,7 +109,7 @@ func TestBackfillMultipleJobs(t *testing.T) {
var out struct {
BackfillRequests []map[string]any `json:"BackfillRequests"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &out, jsonx.V1()))
assert.Len(t, out.BackfillRequests, 3)
}
@@ -179,7 +181,7 @@ func TestCostAllocationTags_UpdateAndList(t *testing.T) {
Type string `json:"Type"`
} `json:"CostAllocationTags"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
assert.Len(t, listOut.CostAllocationTags, tt.wantTagCount)
for _, tag := range listOut.CostAllocationTags {
@@ -217,7 +219,7 @@ func TestCostAllocationTags_ListFiltersWork(t *testing.T) {
TagKey string `json:"TagKey"`
} `json:"CostAllocationTags"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.CostAllocationTags, 2)
})
@@ -235,7 +237,7 @@ func TestCostAllocationTags_ListFiltersWork(t *testing.T) {
Status string `json:"Status"`
} `json:"CostAllocationTags"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.CostAllocationTags, 2)
for _, tag := range out.CostAllocationTags {
@@ -262,7 +264,7 @@ func TestCostAllocationTagBackfill_StateTracking(t *testing.T) {
RequestedAt string `json:"requestedAt"`
} `json:"BackfillRequest"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &startOut, jsonx.V1()))
assert.Equal(t, "2024-01-01T00:00:00Z", startOut.BackfillRequest.BackfillFrom)
assert.Equal(t, "PROCESSING", startOut.BackfillRequest.BackfillStatus)
@@ -275,7 +277,7 @@ func TestCostAllocationTagBackfill_StateTracking(t *testing.T) {
BackfillStatus string `json:"backfillStatus"`
} `json:"BackfillRequests"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
require.Len(t, listOut.BackfillRequests, 1)
assert.Equal(t, "PROCESSING", listOut.BackfillRequests[0].BackfillStatus)
}
diff --git a/services/ce/handler_cost_categories_test.go b/services/ce/handler_cost_categories_test.go
index 007e036a37..b49b289bf9 100644
--- a/services/ce/handler_cost_categories_test.go
+++ b/services/ce/handler_cost_categories_test.go
@@ -1,7 +1,8 @@
package ce_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ce"
)
@@ -39,7 +41,7 @@ func TestGetCostCategories_MultiRuleCategory(t *testing.T) {
ReturnSize int `json:"ReturnSize"`
TotalSize int `json:"TotalSize"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.CostCategoryValues, 3)
assert.Equal(t, 3, out.ReturnSize)
assert.Equal(t, 3, out.TotalSize)
@@ -60,7 +62,7 @@ func TestListCostCategoryResourceAssociations(t *testing.T) {
var out struct {
CostCategoryResourceAssociations []map[string]any `json:"CostCategoryResourceAssociations"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Empty(t, out.CostCategoryResourceAssociations)
}
@@ -79,7 +81,7 @@ func TestDescribeCostCategory_HasProcessingStatus(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
catARN := createOut["CostCategoryArn"].(string)
descRec := doRequest(t, h, "DescribeCostCategoryDefinition", map[string]any{
@@ -95,7 +97,7 @@ func TestDescribeCostCategory_HasProcessingStatus(t *testing.T) {
} `json:"ProcessingStatus"`
} `json:"CostCategory"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
require.NotEmpty(t, descOut.CostCategory.ProcessingStatus, "ProcessingStatus must be present")
ps := descOut.CostCategory.ProcessingStatus[0]
assert.Equal(t, "COST_EXPLORER", ps.Component)
@@ -123,7 +125,7 @@ func TestListCostCategoryDefinitions_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
page1 := out1["CostCategoryReferences"].([]any)
assert.Len(t, page1, 2)
nextToken, ok := out1["NextPageToken"].(string)
@@ -136,7 +138,7 @@ func TestListCostCategoryDefinitions_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
page2 := out2["CostCategoryReferences"].([]any)
assert.Len(t, page2, 2)
}
@@ -160,7 +162,7 @@ func TestHandler_CostCategoryCRUD(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out["CostCategoryArn"])
assert.NotEmpty(t, out["EffectiveStart"])
},
@@ -180,7 +182,7 @@ func TestHandler_CostCategoryCRUD(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
refs, ok := out["CostCategoryReferences"].([]any)
require.True(t, ok)
assert.NotEmpty(t, refs)
@@ -199,7 +201,7 @@ func TestHandler_CostCategoryCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createOut, jsonx.V1()))
arn := createOut["CostCategoryArn"].(string)
// Describe
@@ -209,7 +211,7 @@ func TestHandler_CostCategoryCRUD(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code)
var describeOut map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&describeOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &describeOut, jsonx.V1()))
cat := describeOut["CostCategory"].(map[string]any)
assert.Equal(t, "DescribeCategory", cat["Name"])
},
@@ -227,7 +229,7 @@ func TestHandler_CostCategoryCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createOut, jsonx.V1()))
arn := createOut["CostCategoryArn"].(string)
// Update
@@ -239,7 +241,7 @@ func TestHandler_CostCategoryCRUD(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code)
var updateOut map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&updateOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &updateOut, jsonx.V1()))
assert.Equal(t, arn, updateOut["CostCategoryArn"])
},
},
@@ -256,7 +258,7 @@ func TestHandler_CostCategoryCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createOut, jsonx.V1()))
arn := createOut["CostCategoryArn"].(string)
// Delete
@@ -343,7 +345,7 @@ func TestCEHandler_UpdateCostCategoryDefinition_DeepCopy(t *testing.T) {
var createOut struct {
CostCategoryArn string `json:"CostCategoryArn"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
updateRec := doRequest(t, h, "UpdateCostCategoryDefinition", map[string]any{
"CostCategoryArn": createOut.CostCategoryArn,
@@ -363,7 +365,7 @@ func TestCEHandler_UpdateCostCategoryDefinition_DeepCopy(t *testing.T) {
} `json:"CostCategory"`
}
- require.NoError(t, json.NewDecoder(describeRec.Body).Decode(&describeOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(describeRec.Body), &describeOut, jsonx.V1()))
assert.Len(t, describeOut.CostCategory.Rules, tt.wantRulesAfterGet)
})
}
@@ -451,7 +453,7 @@ func TestHandler_GetCostCategories(t *testing.T) {
ReturnSize int `json:"ReturnSize"`
TotalSize int `json:"TotalSize"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantLen, out.ReturnSize)
assert.Equal(t, tt.wantLen, out.TotalSize)
@@ -523,7 +525,7 @@ func TestHandler_SortedOutput(t *testing.T) {
Name string `json:"Name"`
} `json:"CostCategoryReferences"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.Len(t, out.CostCategoryReferences, 3)
assert.Equal(t, "Alpha", out.CostCategoryReferences[0].Name)
assert.Equal(t, "Mango", out.CostCategoryReferences[1].Name)
@@ -555,7 +557,7 @@ func TestHandler_SortedOutput(t *testing.T) {
var out struct {
CostCategoryValues []string `json:"CostCategoryValues"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.Len(t, out.CostCategoryValues, 3)
assert.Equal(t, []string{"Alpha", "Mike", "Zulu"}, out.CostCategoryValues)
},
@@ -592,7 +594,7 @@ func TestHandler_SortedOutput(t *testing.T) {
Key string `json:"Key"`
} `json:"ResourceTags"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.Len(t, out.ResourceTags, 3)
assert.Equal(t, "Alpha", out.ResourceTags[0].Key)
assert.Equal(t, "Mike", out.ResourceTags[1].Key)
@@ -634,7 +636,7 @@ func TestHandler_DuplicateCostCategory_WireStatusIs400(t *testing.T) {
var out struct {
Type string `json:"__type"`
}
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &out, jsonx.V1()))
assert.Equal(t, "ServiceQuotaExceededException", out.Type)
}
@@ -734,7 +736,7 @@ func TestHandler_UpdateCostCategoryDefinition_RequiredFields(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
tt.update["CostCategoryArn"] = createOut["CostCategoryArn"]
rec := doRequest(t, h, "UpdateCostCategoryDefinition", tt.update)
diff --git a/services/ce/handler_cost_usage_test.go b/services/ce/handler_cost_usage_test.go
index 8af27f5906..8f7b064e63 100644
--- a/services/ce/handler_cost_usage_test.go
+++ b/services/ce/handler_cost_usage_test.go
@@ -1,7 +1,8 @@
package ce_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ce"
)
@@ -35,7 +37,7 @@ func TestGetCostAndUsageWithResources_ResourceIDs(t *testing.T) {
ResultsByTime []any `json:"ResultsByTime"`
DimensionValueAttributes []any `json:"DimensionValueAttributes"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotNil(t, out.ResultsByTime)
assert.NotNil(t, out.DimensionValueAttributes)
}
@@ -77,7 +79,7 @@ func TestGetApproximateUsageRecords_Shape(t *testing.T) {
Services map[string]int64 `json:"Services"`
TotalRecords int64 `json:"TotalRecords"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotNil(t, out.Services)
assert.Positive(t, out.TotalRecords)
})
@@ -109,7 +111,7 @@ func TestGetCostAndUsageComparisons_Shape(t *testing.T) {
} `json:"Metrics"`
} `json:"CostAndUsageComparisons"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotEmpty(t, out.CostAndUsageComparisons)
assert.NotEmpty(t, out.TotalCostAndUsage["BlendedCost"])
assert.NotEmpty(t, out.CostAndUsageComparisons[0].Metrics["BlendedCost"].ComparisonTimePeriodAmount)
@@ -238,7 +240,7 @@ func TestGetCostComparisonDrivers(t *testing.T) {
} `json:"CostDrivers"`
} `json:"CostComparisonDrivers"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotNil(t, out.CostComparisonDrivers)
assert.GreaterOrEqual(t, len(out.CostComparisonDrivers), tt.minGroups)
assert.LessOrEqual(t, len(out.CostComparisonDrivers), tt.maxGroups)
@@ -274,8 +276,8 @@ func TestGetUsageForecast_MatchesCostForecast(t *testing.T) {
require.Equal(t, http.StatusOK, usageRec.Code)
var costOut, usageOut map[string]any
- require.NoError(t, json.NewDecoder(costRec.Body).Decode(&costOut))
- require.NoError(t, json.NewDecoder(usageRec.Body).Decode(&usageOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(costRec.Body), &costOut, jsonx.V1()))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(usageRec.Body), &usageOut, jsonx.V1()))
// Both should have Total and ForecastResultsByTime
assert.NotNil(t, costOut["Total"])
@@ -299,7 +301,7 @@ func TestGetTags_EmptyKeyReturnsAllKeys(t *testing.T) {
ReturnSize int `json:"ReturnSize"`
TotalSize int `json:"TotalSize"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
// Ledger may have no tags seeded — assert shape is correct regardless
assert.NotNil(t, out.Tags)
assert.Equal(t, len(out.Tags), out.ReturnSize)
@@ -320,7 +322,7 @@ func TestGetTags_WithTagKey(t *testing.T) {
var out struct {
Tags []string `json:"Tags"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotNil(t, out.Tags)
}
@@ -348,7 +350,7 @@ func TestGetCostAndUsage_GroupByDimensions(t *testing.T) {
var out struct {
ResultsByTime []map[string]any `json:"ResultsByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotNil(t, out.ResultsByTime)
})
}
@@ -385,7 +387,7 @@ func TestGetCostAndUsage_AlternateMetrics(t *testing.T) {
var out struct {
ResultsByTime []map[string]any `json:"ResultsByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out.ResultsByTime)
})
}
@@ -467,7 +469,7 @@ func TestGetCostAndUsage_ReturnsResultsByTime(t *testing.T) {
var out struct {
ResultsByTime []any `json:"ResultsByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
if tt.wantNonEmpty {
assert.NotEmpty(t, out.ResultsByTime)
@@ -497,7 +499,7 @@ func TestGetCostAndUsage_GroupByProducesGroups(t *testing.T) {
} `json:"Groups"`
} `json:"ResultsByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotEmpty(t, out.ResultsByTime)
first := out.ResultsByTime[0]
@@ -528,7 +530,7 @@ func TestGetCostAndUsage_TotalPresentWhenNoGroupBy(t *testing.T) {
Estimated bool `json:"Estimated"`
} `json:"ResultsByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotEmpty(t, out.ResultsByTime)
first := out.ResultsByTime[0]
@@ -569,7 +571,7 @@ func TestGetDimensionValues_ServiceDimension(t *testing.T) {
ReturnSize int `json:"ReturnSize"`
TotalSize int `json:"TotalSize"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out.DimensionValues)
assert.Positive(t, out.ReturnSize)
assert.Equal(t, out.ReturnSize, out.TotalSize)
@@ -594,7 +596,7 @@ func TestGetDimensionValues_SearchStringFilters(t *testing.T) {
Value string `json:"Value"`
} `json:"DimensionValues"`
}
- require.NoError(t, json.NewDecoder(recAll.Body).Decode(&allOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(recAll.Body), &allOut, jsonx.V1()))
totalCount := len(allOut.DimensionValues)
// Filter by "Lambda"
@@ -610,7 +612,7 @@ func TestGetDimensionValues_SearchStringFilters(t *testing.T) {
Value string `json:"Value"`
} `json:"DimensionValues"`
}
- require.NoError(t, json.NewDecoder(recFiltered.Body).Decode(&filteredOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(recFiltered.Body), &filteredOut, jsonx.V1()))
assert.Less(t, len(filteredOut.DimensionValues), totalCount)
for _, dv := range filteredOut.DimensionValues {
assert.Contains(t, dv.Value, "Lambda")
@@ -671,7 +673,7 @@ func TestGetCostForecast_ReturnsTimeSeries(t *testing.T) {
MeanValue string `json:"MeanValue"`
} `json:"ForecastResultsByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out.Total.Amount)
assert.NotEmpty(t, out.Total.Unit)
assert.NotEmpty(t, out.ForecastResultsByTime)
@@ -892,7 +894,7 @@ func TestHandler_Forecast_ReturnsOK(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotNil(t, out["Total"])
forecastResults, ok := out["ForecastResultsByTime"].([]any)
require.True(t, ok)
@@ -950,7 +952,7 @@ func TestHandler_GetApproximateUsageRecords(t *testing.T) {
LookbackPeriod map[string]string `json:"LookbackPeriod"`
TotalRecords int64 `json:"TotalRecords"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Positive(t, out.TotalRecords)
assert.NotEmpty(t, out.LookbackPeriod["Start"])
assert.NotEmpty(t, out.LookbackPeriod["End"])
@@ -988,7 +990,7 @@ func TestHandler_GetCostAndUsageComparisons(t *testing.T) {
var out struct {
CostAndUsageComparisons []any `json:"CostAndUsageComparisons"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out.CostAndUsageComparisons)
})
}
@@ -1057,7 +1059,7 @@ func TestHandler_GetCostAndUsageWithResources(t *testing.T) {
var out struct {
ResultsByTime []any `json:"ResultsByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Empty(t, out.ResultsByTime)
})
}
@@ -1093,7 +1095,7 @@ func TestHandler_GetCostComparisonDrivers(t *testing.T) {
var out struct {
CostComparisonDrivers []any `json:"CostComparisonDrivers"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Empty(t, out.CostComparisonDrivers)
})
}
diff --git a/services/ce/handler_filters_test.go b/services/ce/handler_filters_test.go
index 87fffc2016..e0fb2127c7 100644
--- a/services/ce/handler_filters_test.go
+++ b/services/ce/handler_filters_test.go
@@ -1,7 +1,8 @@
package ce_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ce"
)
@@ -42,7 +44,7 @@ func TestGetCostCategoriesFilterAndSortNarrow(t *testing.T) {
var unfiltered struct {
CostCategoryValues []string `json:"CostCategoryValues"`
}
- require.NoError(t, json.NewDecoder(unfilteredRec.Body).Decode(&unfiltered))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(unfilteredRec.Body), &unfiltered, jsonx.V1()))
require.Equal(t, []string{"Alpha", "Bravo", "Charlie"}, unfiltered.CostCategoryValues)
filteredBody := map[string]any{
@@ -63,7 +65,7 @@ func TestGetCostCategoriesFilterAndSortNarrow(t *testing.T) {
ReturnSize int `json:"ReturnSize"`
TotalSize int `json:"TotalSize"`
}
- require.NoError(t, json.NewDecoder(filteredRec.Body).Decode(&filtered))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(filteredRec.Body), &filtered, jsonx.V1()))
assert.Equal(t, []string{"Alpha", "Charlie"}, filtered.CostCategoryValues)
assert.Equal(t, 2, filtered.ReturnSize)
assert.Equal(t, 2, filtered.TotalSize)
@@ -79,7 +81,7 @@ func TestGetCostCategoriesFilterAndSortNarrow(t *testing.T) {
var sorted struct {
CostCategoryValues []string `json:"CostCategoryValues"`
}
- require.NoError(t, json.NewDecoder(sortedRec.Body).Decode(&sorted))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(sortedRec.Body), &sorted, jsonx.V1()))
assert.Equal(t, []string{"Charlie", "Bravo", "Alpha"}, sorted.CostCategoryValues)
}
@@ -102,7 +104,7 @@ func TestGetDimensionValuesFilterAndSortNarrow(t *testing.T) {
Value string `json:"Value"`
} `json:"DimensionValues"`
}
- require.NoError(t, json.NewDecoder(unfilteredRec.Body).Decode(&unfiltered))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(unfilteredRec.Body), &unfiltered, jsonx.V1()))
require.Len(t, unfiltered.DimensionValues, 12, "synthetic catalog has 12 services")
// AWS Lambda is the only service seeded with usage type Lambda-GB-Second,
@@ -125,7 +127,7 @@ func TestGetDimensionValuesFilterAndSortNarrow(t *testing.T) {
} `json:"DimensionValues"`
ReturnSize int `json:"ReturnSize"`
}
- require.NoError(t, json.NewDecoder(filteredRec.Body).Decode(&filtered))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(filteredRec.Body), &filtered, jsonx.V1()))
require.Len(t, filtered.DimensionValues, 1)
assert.Equal(t, "AWS Lambda", filtered.DimensionValues[0].Value)
assert.Equal(t, 1, filtered.ReturnSize)
@@ -144,7 +146,7 @@ func TestGetDimensionValuesFilterAndSortNarrow(t *testing.T) {
Value string `json:"Value"`
} `json:"DimensionValues"`
}
- require.NoError(t, json.NewDecoder(sortedRec.Body).Decode(&sorted))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(sortedRec.Body), &sorted, jsonx.V1()))
require.NotEmpty(t, sorted.DimensionValues)
assert.Equal(t, "Amazon Elastic Compute Cloud - Compute", sorted.DimensionValues[0].Value)
}
@@ -174,7 +176,7 @@ func TestGetTagsFilterAndSortAccepted(t *testing.T) {
var out struct {
Tags []string `json:"Tags"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Empty(t, out.Tags, "no ledger entry is ever tagged in this emulator")
}
@@ -198,7 +200,7 @@ func TestGetSavingsPlansCoverageRegionFilterNarrows(t *testing.T) {
var matched struct {
SavingsPlansCoverages []any `json:"SavingsPlansCoverages"`
}
- require.NoError(t, json.NewDecoder(matchRec.Body).Decode(&matched))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(matchRec.Body), &matched, jsonx.V1()))
assert.Len(t, matched.SavingsPlansCoverages, 1)
missRec := doRequest(t, h, "GetSavingsPlansCoverage", map[string]any{
@@ -212,7 +214,7 @@ func TestGetSavingsPlansCoverageRegionFilterNarrows(t *testing.T) {
var missed struct {
SavingsPlansCoverages []any `json:"SavingsPlansCoverages"`
}
- require.NoError(t, json.NewDecoder(missRec.Body).Decode(&missed))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(missRec.Body), &missed, jsonx.V1()))
assert.Empty(t, missed.SavingsPlansCoverages)
}
@@ -232,7 +234,7 @@ func TestGetSavingsPlansPurchaseRecommendationAccountFilterNarrows(t *testing.T)
require.Equal(t, http.StatusOK, matchRec.Code)
var matched map[string]any
- require.NoError(t, json.NewDecoder(matchRec.Body).Decode(&matched))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(matchRec.Body), &matched, jsonx.V1()))
assert.NotNil(t, matched["SavingsPlansPurchaseRecommendation"])
missRec := doRequest(t, h, "GetSavingsPlansPurchaseRecommendation", map[string]any{
@@ -243,7 +245,7 @@ func TestGetSavingsPlansPurchaseRecommendationAccountFilterNarrows(t *testing.T)
require.Equal(t, http.StatusOK, missRec.Code)
var missed map[string]any
- require.NoError(t, json.NewDecoder(missRec.Body).Decode(&missed))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(missRec.Body), &missed, jsonx.V1()))
assert.Nil(t, missed["SavingsPlansPurchaseRecommendation"])
}
@@ -266,7 +268,7 @@ func TestGetReservationPurchaseRecommendationAccountFilterNarrows(t *testing.T)
var matched struct {
Recommendations []any `json:"Recommendations"`
}
- require.NoError(t, json.NewDecoder(matchRec.Body).Decode(&matched))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(matchRec.Body), &matched, jsonx.V1()))
assert.NotEmpty(t, matched.Recommendations)
missRec := doRequest(t, h, "GetReservationPurchaseRecommendation", map[string]any{
@@ -280,7 +282,7 @@ func TestGetReservationPurchaseRecommendationAccountFilterNarrows(t *testing.T)
var missed struct {
Recommendations []any `json:"Recommendations"`
}
- require.NoError(t, json.NewDecoder(missRec.Body).Decode(&missed))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(missRec.Body), &missed, jsonx.V1()))
assert.Empty(t, missed.Recommendations)
}
@@ -304,7 +306,7 @@ func TestGetReservationCoverageSortByTimeReorders(t *testing.T) {
TimePeriod map[string]string `json:"TimePeriod"`
} `json:"CoveragesByTime"`
}
- require.NoError(t, json.NewDecoder(ascRec.Body).Decode(&asc))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(ascRec.Body), &asc, jsonx.V1()))
require.Len(t, asc.CoveragesByTime, 3)
assert.Equal(t, "2024-01-01", asc.CoveragesByTime[0].TimePeriod["Start"])
assert.Equal(t, "2024-01-03", asc.CoveragesByTime[2].TimePeriod["Start"])
@@ -322,7 +324,7 @@ func TestGetReservationCoverageSortByTimeReorders(t *testing.T) {
TimePeriod map[string]string `json:"TimePeriod"`
} `json:"CoveragesByTime"`
}
- require.NoError(t, json.NewDecoder(descRec.Body).Decode(&desc))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(descRec.Body), &desc, jsonx.V1()))
require.Len(t, desc.CoveragesByTime, 3)
assert.Equal(t, "2024-01-03", desc.CoveragesByTime[0].TimePeriod["Start"])
assert.Equal(t, "2024-01-01", desc.CoveragesByTime[2].TimePeriod["Start"])
@@ -353,7 +355,7 @@ func TestGetReservationCoverageServiceFilterZeroesCost(t *testing.T) {
} `json:"CoverageCost"`
} `json:"Total"`
}
- require.NoError(t, json.NewDecoder(unfilteredRec.Body).Decode(&unfiltered))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(unfilteredRec.Body), &unfiltered, jsonx.V1()))
require.NotEqual(t, "0.0000", unfiltered.Total.CoverageCost.OnDemandCost)
filteredRec := doRequest(t, h, "GetReservationCoverage", map[string]any{
@@ -372,7 +374,7 @@ func TestGetReservationCoverageServiceFilterZeroesCost(t *testing.T) {
} `json:"CoverageCost"`
} `json:"Total"`
}
- require.NoError(t, json.NewDecoder(filteredRec.Body).Decode(&filtered))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(filteredRec.Body), &filtered, jsonx.V1()))
assert.Equal(t, "0.0000", filtered.Total.CoverageCost.OnDemandCost)
}
@@ -396,7 +398,7 @@ func TestGetReservationUtilizationSortByTimeReorders(t *testing.T) {
TimePeriod map[string]string `json:"TimePeriod"`
} `json:"UtilizationsByTime"`
}
- require.NoError(t, json.NewDecoder(descRec.Body).Decode(&desc))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(descRec.Body), &desc, jsonx.V1()))
require.Len(t, desc.UtilizationsByTime, 3)
assert.Equal(t, "2024-01-03", desc.UtilizationsByTime[0].TimePeriod["Start"])
assert.Equal(t, "2024-01-01", desc.UtilizationsByTime[2].TimePeriod["Start"])
@@ -424,6 +426,6 @@ func TestGetCostComparisonDriversFilterAccepted(t *testing.T) {
var out struct {
CostComparisonDrivers []any `json:"CostComparisonDrivers"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Empty(t, out.CostComparisonDrivers)
}
diff --git a/services/ce/handler_reservations_test.go b/services/ce/handler_reservations_test.go
index 858e005258..78866bf91d 100644
--- a/services/ce/handler_reservations_test.go
+++ b/services/ce/handler_reservations_test.go
@@ -1,13 +1,15 @@
package ce_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ce"
)
@@ -25,7 +27,7 @@ func TestGetReservationCoverage_DailyGranularity(t *testing.T) {
var out struct {
CoveragesByTime []any `json:"CoveragesByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.CoveragesByTime, 3)
}
@@ -43,7 +45,7 @@ func TestGetReservationUtilization_DailyGranularity(t *testing.T) {
var out struct {
UtilizationsByTime []any `json:"UtilizationsByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.UtilizationsByTime, 3)
}
@@ -62,7 +64,7 @@ func TestGetRightsizingRecommendation_IncludesSummary(t *testing.T) {
Summary map[string]string `json:"Summary"`
RightsizingRecommendations []any `json:"RightsizingRecommendations"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out.RightsizingRecommendations)
assert.NotNil(t, out.Summary)
_, hasTotalCount := out.Summary["TotalRecommendationCount"]
@@ -92,7 +94,7 @@ func TestGetReservationCoverage_Structure(t *testing.T) {
} `json:"Total"`
} `json:"CoveragesByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotEmpty(t, out.CoveragesByTime)
assert.NotNil(t, out.Total)
@@ -121,7 +123,7 @@ func TestGetReservationUtilization_Structure(t *testing.T) {
} `json:"Total"`
} `json:"UtilizationsByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotEmpty(t, out.UtilizationsByTime)
assert.NotNil(t, out.Total)
@@ -175,7 +177,7 @@ func TestGetReservationPurchaseRecommendation_FieldShape(t *testing.T) {
} `json:"RecommendationDetails"`
} `json:"Recommendations"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotEmpty(t, out.Recommendations)
require.NotEmpty(t, out.Recommendations[0].RecommendationDetails)
@@ -212,7 +214,7 @@ func TestGetRightsizingRecommendation_FieldShape(t *testing.T) {
} `json:"ModifyRecommendationDetail"`
} `json:"RightsizingRecommendations"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotEmpty(t, out.RightsizingRecommendations)
assert.NotNil(t, out.Summary)
@@ -253,7 +255,7 @@ func TestHandler_GetReservationCoverage(t *testing.T) {
var out struct {
CoveragesByTime []any `json:"CoveragesByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out.CoveragesByTime)
})
}
@@ -290,7 +292,7 @@ func TestHandler_GetReservationPurchaseRecommendation(t *testing.T) {
var out struct {
Recommendations []any `json:"Recommendations"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out.Recommendations)
})
}
@@ -325,7 +327,7 @@ func TestHandler_GetReservationUtilization(t *testing.T) {
var out struct {
UtilizationsByTime []any `json:"UtilizationsByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out.UtilizationsByTime)
})
}
diff --git a/services/ce/handler_savings_plans_test.go b/services/ce/handler_savings_plans_test.go
index 59817097fd..753d359666 100644
--- a/services/ce/handler_savings_plans_test.go
+++ b/services/ce/handler_savings_plans_test.go
@@ -1,7 +1,8 @@
package ce_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ce"
)
@@ -27,7 +29,7 @@ func TestGetSavingsPlanPurchaseRecommendationDetails(t *testing.T) {
RecommendationDetailData map[string]any `json:"RecommendationDetailData"`
RecommendationDetailID string `json:"RecommendationDetailId"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, "detail-abc123", out.RecommendationDetailID)
assert.NotEmpty(t, out.RecommendationDetailData)
}
@@ -56,7 +58,7 @@ func TestListSavingsPlansPurchaseRecommendationGeneration(t *testing.T) {
var out struct {
GenerationSummaryList []any `json:"GenerationSummaryList"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotNil(t, out.GenerationSummaryList)
}
@@ -75,7 +77,7 @@ func TestStartSavingsPlansPurchaseRecommendationGeneration(t *testing.T) {
GenerationStartedTime string `json:"GenerationStartedTime"`
EstimatedCompletionTime string `json:"EstimatedCompletionTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out.RecommendationID)
assert.NotEmpty(t, out.GenerationStartedTime)
assert.NotEmpty(t, out.EstimatedCompletionTime)
@@ -89,7 +91,7 @@ func TestStartSavingsPlansPurchaseRecommendationGeneration(t *testing.T) {
GenerationStatus string `json:"GenerationStatus"`
} `json:"GenerationSummaryList"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
require.Len(t, listOut.GenerationSummaryList, 1)
assert.Equal(t, out.RecommendationID, listOut.GenerationSummaryList[0].RecommendationID)
assert.Equal(t, "PROCESSING", listOut.GenerationSummaryList[0].GenerationStatus)
@@ -113,7 +115,7 @@ func TestListSavingsPlansPurchaseRecommendationGeneration_FiltersByStatus(t *tes
var out struct {
GenerationSummaryList []any `json:"GenerationSummaryList"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Empty(t, out.GenerationSummaryList, "job is PROCESSING, must not match a SUCCEEDED filter")
}
@@ -134,7 +136,7 @@ func TestGetSavingsPlansCoverage(t *testing.T) {
TimePeriod map[string]string `json:"TimePeriod"`
} `json:"SavingsPlansCoverages"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotEmpty(t, out.SavingsPlansCoverages)
assert.NotEmpty(t, out.SavingsPlansCoverages[0].Coverage)
}
@@ -153,7 +155,7 @@ func TestGetSavingsPlansUtilization_DailyGranularity(t *testing.T) {
var out struct {
SavingsPlansUtilizationsByTime []any `json:"SavingsPlansUtilizationsByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.SavingsPlansUtilizationsByTime, 7)
}
@@ -191,7 +193,7 @@ func TestCeRegion_SavingsPlansCoverageUsesCtxRegion(t *testing.T) {
Attributes map[string]string `json:"Attributes"`
} `json:"SavingsPlansCoverages"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotEmpty(t, out.SavingsPlansCoverages)
assert.Equal(t, tt.wantRegion, out.SavingsPlansCoverages[0].Attributes["Region"])
})
@@ -235,7 +237,7 @@ func TestCeRegion_SavingsPlansPurchaseRecommendationUsesCtxRegion(t *testing.T)
RecommendationDetails []map[string]any `json:"SavingsPlansPurchaseRecommendationDetails"`
} `json:"SavingsPlansPurchaseRecommendation"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
details := out.SavingsPlansPurchaseRecommendation.RecommendationDetails
require.NotEmpty(t, details)
spDetails, ok := details[0]["SavingsPlansDetails"].(map[string]any)
@@ -291,7 +293,7 @@ func TestGetSavingsPlansUtilization_ReturnsTotalAndByTime(t *testing.T) {
} `json:"Total"`
SavingsPlansUtilizationsByTime []any `json:"SavingsPlansUtilizationsByTime"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out.Total.Utilization.TotalCommitment)
assert.NotEmpty(t, out.Total.Utilization.UsedCommitment)
@@ -320,7 +322,7 @@ func TestGetSavingsPlansUtilizationDetails_ReturnsDetails(t *testing.T) {
} `json:"Utilization"`
} `json:"SavingsPlansUtilizationDetails"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out.SavingsPlansUtilizationDetails)
assert.NotNil(t, out.Total)
assert.NotEmpty(t, out.TimePeriod)
@@ -373,7 +375,7 @@ func TestGetSavingsPlansPurchaseRecommendation_ReturnsRecommendation(t *testing.
SavingsPlansPurchaseRecommendationDetails []map[string]any `json:"SavingsPlansPurchaseRecommendationDetails"`
} `json:"SavingsPlansPurchaseRecommendation"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out.SavingsPlansPurchaseRecommendation.SavingsPlansType)
assert.NotEmpty(t, out.SavingsPlansPurchaseRecommendation.SavingsPlansPurchaseRecommendationDetails)
assert.NotNil(t, out.Metadata)
diff --git a/services/ce/handler_tags_test.go b/services/ce/handler_tags_test.go
index 157531ccb5..9d6222fec8 100644
--- a/services/ce/handler_tags_test.go
+++ b/services/ce/handler_tags_test.go
@@ -1,13 +1,15 @@
package ce_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ce"
)
@@ -30,7 +32,7 @@ func TestHandler_TagOperations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createOut, jsonx.V1()))
arn := createOut["CostCategoryArn"].(string)
// Tag
@@ -47,7 +49,7 @@ func TestHandler_TagOperations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec3.Code)
var listOut map[string]any
- require.NoError(t, json.NewDecoder(rec3.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec3.Body), &listOut, jsonx.V1()))
tags, _ := listOut["ResourceTags"].([]any)
var envVal string
for _, tagAny := range tags {
@@ -94,7 +96,7 @@ func TestHandler_TagOperations_AnomalyMonitor(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
monitorARN := createOut["MonitorArn"].(string)
// Tag.
@@ -111,7 +113,7 @@ func TestHandler_TagOperations_AnomalyMonitor(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
tags, _ := listOut["ResourceTags"].([]any)
var found bool
@@ -153,7 +155,7 @@ func TestHandler_TagOperations_AnomalySubscription(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
subARN := createOut["SubscriptionArn"].(string)
// Tag.
@@ -170,7 +172,7 @@ func TestHandler_TagOperations_AnomalySubscription(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
tags, _ := listOut["ResourceTags"].([]any)
assert.NotEmpty(t, tags)
@@ -278,7 +280,7 @@ func TestHandler_ListTagsForResource_EmptyTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createOut, jsonx.V1()))
arn := createOut["CostCategoryArn"].(string)
listRec := doRequest(t, h, "ListTagsForResource", map[string]any{"ResourceArn": arn})
@@ -287,7 +289,7 @@ func TestHandler_ListTagsForResource_EmptyTags(t *testing.T) {
var out struct {
ResourceTags []map[string]string `json:"ResourceTags"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &out, jsonx.V1()))
// Must be an empty array, not nil/absent.
assert.NotNil(t, out.ResourceTags)
assert.Empty(t, out.ResourceTags)
diff --git a/services/ce/handler_test.go b/services/ce/handler_test.go
index 77205600ca..8cc7111f4f 100644
--- a/services/ce/handler_test.go
+++ b/services/ce/handler_test.go
@@ -3,7 +3,8 @@ package ce_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -14,6 +15,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/ce"
)
@@ -27,7 +29,7 @@ func doRequestWithMeta(
) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
ctx := awsmeta.Set(context.Background(), meta)
@@ -55,7 +57,7 @@ func doRequest(t *testing.T, h *ce.Handler, action string, body any) *httptest.R
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -369,7 +371,7 @@ func TestHandler_Reset(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
refs, _ := listOut["CostCategoryReferences"].([]any)
assert.Empty(t, refs)
}
@@ -472,7 +474,7 @@ func TestHandler_ErrorWireShape(t *testing.T) {
var out struct {
Type string `json:"__type"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out.Type)
})
}
diff --git a/services/ce/persistence.go b/services/ce/persistence.go
index eb07cbca95..b2f1c2c5da 100644
--- a/services/ce/persistence.go
+++ b/services/ce/persistence.go
@@ -2,10 +2,12 @@ package ce
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -36,12 +38,12 @@ const ceSnapshotVersion = 1
// from an incompatible (older or newer) build of this backend as though it
// were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- BackfillJobs []*BackfillJob `json:"backfillJobs"`
- AnomalyTTL time.Duration `json:"anomalyTTL"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ BackfillJobs []*BackfillJob `json:"backfillJobs"`
+ AnomalyTTL time.Duration `json:"anomalyTTL"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
@@ -66,7 +68,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
AnomalyTTL: b.anomalyTTL,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "ce: failed to marshal snapshot", "error", err)
diff --git a/services/ce/persistence_version_test.go b/services/ce/persistence_version_test.go
index dd6acf8b81..8bc1a7db62 100644
--- a/services/ce/persistence_version_test.go
+++ b/services/ce/persistence_version_test.go
@@ -1,12 +1,13 @@
package ce_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ce"
)
@@ -18,11 +19,11 @@ func tamperSnapshotVersion(t *testing.T, snap []byte, newVersion int) []byte {
var raw map[string]any
- require.NoError(t, json.Unmarshal(snap, &raw))
+ require.NoError(t, json.Unmarshal(snap, &raw, jsonx.V1()))
raw["version"] = newVersion
- out, err := json.Marshal(raw)
+ out, err := json.Marshal(raw, jsonx.V1())
require.NoError(t, err)
return out
@@ -82,10 +83,10 @@ func TestInMemoryBackend_RestoreMissingVersion(t *testing.T) {
var raw map[string]any
- require.NoError(t, json.Unmarshal(snap, &raw))
+ require.NoError(t, json.Unmarshal(snap, &raw, jsonx.V1()))
delete(raw, "version")
- legacy, err := json.Marshal(raw)
+ legacy, err := json.Marshal(raw, jsonx.V1())
require.NoError(t, err)
target := ce.NewInMemoryBackend("000000000000", "us-east-1")
diff --git a/services/ce/store.go b/services/ce/store.go
index 2b49011d8b..765d332cf0 100644
--- a/services/ce/store.go
+++ b/services/ce/store.go
@@ -3,12 +3,13 @@ package ce
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"sort"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/store"
)
@@ -233,7 +234,7 @@ func checkPageTokens(body []byte) error {
NextToken string `json:"NextToken"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
for _, tok := range []string{req.NextPageToken, req.NextToken} {
if tok == "" {
diff --git a/services/ce/usage_validation_test.go b/services/ce/usage_validation_test.go
index 00595edad4..d90abe82ec 100644
--- a/services/ce/usage_validation_test.go
+++ b/services/ce/usage_validation_test.go
@@ -1,13 +1,15 @@
package ce_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func usageBody(start, end, granularity string, extra map[string]any) map[string]any {
@@ -59,7 +61,7 @@ func TestGetCostAndUsage_RequestValidation(t *testing.T) {
if tt.wantType != "" {
var we wireError
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &we))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &we, jsonx.V1()))
assert.Contains(t, we.Type, tt.wantType)
}
})
@@ -80,7 +82,7 @@ func TestGetCostAndUsage_HourlyBuckets(t *testing.T) {
TimePeriod map[string]string `json:"TimePeriod"`
} `json:"ResultsByTime"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.ResultsByTime, 3)
assert.Equal(t, "2026-01-01T01:00:00Z", out.ResultsByTime[1].TimePeriod["Start"])
}
@@ -127,7 +129,7 @@ func TestRequestEnumValidation(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code, rec.Body.String())
var we wireError
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &we))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &we, jsonx.V1()))
assert.Contains(t, we.Type, "ValidationError")
})
}
diff --git a/services/cleanrooms/analysis_templates_test.go b/services/cleanrooms/analysis_templates_test.go
index 4716b0faed..05f6c9ce30 100644
--- a/services/cleanrooms/analysis_templates_test.go
+++ b/services/cleanrooms/analysis_templates_test.go
@@ -1,12 +1,14 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestAnalysisTemplateHasIDKeys verifies AnalysisTemplate canonical ID keys.
@@ -22,14 +24,14 @@ func TestAnalysisTemplateHasIDKeys(t *testing.T) {
})
var colResp map[string]any
rec := doRequest(t, e, "GET", "/collaborations", nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &colResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &colResp, jsonx.V1()))
colID := colResp["collaborationList"].([]any)[0].(map[string]any)["id"].(string)
rec2 := doRequest(t, e, "POST", "/memberships", map[string]any{
"collaborationIdentifier": colID, "queryLogStatus": "DISABLED",
})
var memResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &memResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &memResp, jsonx.V1()))
mID := memResp["membership"].(map[string]any)["id"].(string)
// Create analysis template
@@ -41,7 +43,7 @@ func TestAnalysisTemplateHasIDKeys(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code, rec3.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp, jsonx.V1()))
at := resp["analysisTemplate"].(map[string]any)
assert.Contains(t, at, "id", "analysisTemplate must have 'id' key")
diff --git a/services/cleanrooms/collaborations_test.go b/services/cleanrooms/collaborations_test.go
index 06c2ffdf39..7d96cf7719 100644
--- a/services/cleanrooms/collaborations_test.go
+++ b/services/cleanrooms/collaborations_test.go
@@ -1,12 +1,14 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestCollaborationWireShape verifies a Collaboration response uses only the
@@ -27,7 +29,7 @@ func TestCollaborationWireShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
collab := resp["collaboration"].(map[string]any)
id, hasID := collab["id"]
@@ -58,7 +60,7 @@ func TestCollaborationARNFormat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
collab := resp["collaboration"].(map[string]any)
arn, ok := collab["arn"].(string)
@@ -80,7 +82,7 @@ func TestCollaborationQueryLogStatusRoundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
collab := resp["collaboration"].(map[string]any)
assert.Equal(t, "ENABLED", collab["queryLogStatus"])
}
@@ -101,12 +103,12 @@ func TestCollaborationCreatorMemberAbilities(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
collabID := resp["collaboration"].(map[string]any)["id"].(string)
listRec := doRequest(t, e, "GET", "/collaborations/"+collabID+"/members", nil)
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
members, ok := listResp["memberSummaries"].([]any)
require.True(t, ok, "ListMembers must return memberSummaries")
require.Len(t, members, 1)
@@ -137,7 +139,7 @@ func TestDeleteMember_MarksRemoved(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
collabID := resp["collaboration"].(map[string]any)["id"].(string)
del := doRequest(t, e, "DELETE", "/collaborations/"+collabID+"/member/222222222222", nil)
@@ -146,7 +148,7 @@ func TestDeleteMember_MarksRemoved(t *testing.T) {
listRec := doRequest(t, e, "GET", "/collaborations/"+collabID+"/members", nil)
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
members := listResp["memberSummaries"].([]any)
require.Len(t, members, 2, "removed member must still appear in the member list")
@@ -178,7 +180,7 @@ func TestDeleteCollaboration_CascadesMembershipToCollaborationDeleted(t *testing
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
collab := resp["collaboration"].(map[string]any)
collabID := collab["id"].(string)
membershipID := collab["membershipId"].(string)
@@ -189,7 +191,7 @@ func TestDeleteCollaboration_CascadesMembershipToCollaborationDeleted(t *testing
getRec := doRequest(t, e, "GET", "/memberships/"+membershipID, nil)
require.Equal(t, http.StatusOK, getRec.Code, "membership must still be retrievable after collaboration delete")
var memResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &memResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &memResp, jsonx.V1()))
assert.Equal(t, "COLLABORATION_DELETED", memResp["membership"].(map[string]any)["status"])
}
@@ -210,7 +212,7 @@ func TestCollaborationChangeRequest_Lifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
collabID := resp["collaboration"].(map[string]any)["id"].(string)
createRec := doRequest(t, e, "POST", "/collaborations/"+collabID+"/changeRequests", map[string]any{
@@ -227,7 +229,7 @@ func TestCollaborationChangeRequest_Lifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, createRec.Code, createRec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
cr := createResp["collaborationChangeRequest"].(map[string]any)
assert.Equal(t, "PENDING", cr["status"])
assert.NotEmpty(t, cr["changes"])
@@ -239,7 +241,7 @@ func TestCollaborationChangeRequest_Lifecycle(t *testing.T) {
)
require.Equal(t, http.StatusOK, approveRec.Code, approveRec.Body.String())
var approveResp map[string]any
- require.NoError(t, json.Unmarshal(approveRec.Body.Bytes(), &approveResp))
+ require.NoError(t, json.Unmarshal(approveRec.Body.Bytes(), &approveResp, jsonx.V1()))
assert.Equal(t, "APPROVED", approveResp["collaborationChangeRequest"].(map[string]any)["status"])
commitRec := doRequest(
@@ -248,7 +250,7 @@ func TestCollaborationChangeRequest_Lifecycle(t *testing.T) {
)
require.Equal(t, http.StatusOK, commitRec.Code, commitRec.Body.String())
var commitResp map[string]any
- require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitResp))
+ require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitResp, jsonx.V1()))
assert.Equal(t, "COMMITTED", commitResp["collaborationChangeRequest"].(map[string]any)["status"])
// COMMIT applies the change's real semantic effect: the ADD_MEMBER change
@@ -256,7 +258,7 @@ func TestCollaborationChangeRequest_Lifecycle(t *testing.T) {
getRec := doRequest(t, e, "GET", "/collaborations/"+collabID+"/members", nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
members, _ := getResp["memberSummaries"].([]any)
var found bool
for _, m := range members {
diff --git a/services/cleanrooms/configured_tables_test.go b/services/cleanrooms/configured_tables_test.go
index 697e92b911..0644580c65 100644
--- a/services/cleanrooms/configured_tables_test.go
+++ b/services/cleanrooms/configured_tables_test.go
@@ -1,13 +1,15 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func createConfiguredTable(t *testing.T, e *echo.Echo) string {
@@ -22,7 +24,7 @@ func createConfiguredTable(t *testing.T, e *echo.Echo) string {
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["configuredTable"].(map[string]any)["id"].(string)
}
@@ -67,7 +69,7 @@ func TestConfiguredTables_Create(t *testing.T) {
require.Equal(t, tt.wants.status, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ct := resp["configuredTable"].(map[string]any)
id, hasID := ct["id"]
@@ -117,7 +119,7 @@ func TestConfiguredTableAnalysisRules_Create(t *testing.T) {
require.Equal(t, tt.wants.status, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ar := resp["analysisRule"].(map[string]any)
assert.Contains(t, ar, "configuredTableId", "analysisRule must have canonical 'configuredTableId' key")
diff --git a/services/cleanrooms/handler.go b/services/cleanrooms/handler.go
index e9789412b3..e801accf37 100644
--- a/services/cleanrooms/handler.go
+++ b/services/cleanrooms/handler.go
@@ -2,7 +2,7 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"maps"
"net/http"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -464,7 +465,7 @@ func (h *Handler) dispatch(
}
func mustJSON(v any) []byte {
- b, _ := json.Marshal(v)
+ b, _ := json.Marshal(v, jsonx.V1())
return b
}
diff --git a/services/cleanrooms/handler_analysis_log_exports.go b/services/cleanrooms/handler_analysis_log_exports.go
index f7569460a2..f3a88bb684 100644
--- a/services/cleanrooms/handler_analysis_log_exports.go
+++ b/services/cleanrooms/handler_analysis_log_exports.go
@@ -2,11 +2,12 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"strconv"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -24,7 +25,7 @@ func (h *Handler) handleStartAnalysisLogExport(_ context.Context, body []byte) (
AnalysisID string `json:"analysisId"`
AnalysisType string `json:"analysisType"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
e, err := h.Backend.StartAnalysisLogExport(
req.MembershipIdentifier, req.AnalysisID, req.AnalysisType, req.ResultConfiguration,
@@ -41,7 +42,7 @@ func (h *Handler) handleGetAnalysisLogExport(_ context.Context, body []byte) ([]
MembershipIdentifier string `json:"membershipIdentifier"`
AnalysisLogExportIdentifier string `json:"analysisLogExportIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
e, err := h.Backend.GetAnalysisLogExport(req.MembershipIdentifier, req.AnalysisLogExportIdentifier)
if err != nil {
@@ -55,7 +56,7 @@ func (h *Handler) handleListAnalysisLogExports(_ context.Context, body []byte, c
var req struct {
MembershipIdentifier string `json:"membershipIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
limit := 0
if raw := qp(c, exportMaxResultsParamName); raw != "" {
diff --git a/services/cleanrooms/handler_analysis_templates.go b/services/cleanrooms/handler_analysis_templates.go
index f24b79721a..26abbba470 100644
--- a/services/cleanrooms/handler_analysis_templates.go
+++ b/services/cleanrooms/handler_analysis_templates.go
@@ -2,10 +2,12 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
crtypes "github.com/aws/aws-sdk-go-v2/service/cleanrooms/types"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleGetCollaborationAnalysisTemplate(
@@ -16,7 +18,7 @@ func (h *Handler) handleGetCollaborationAnalysisTemplate(
CollaborationIdentifier string `json:"collaborationIdentifier"`
AnalysisTemplateArn string `json:"analysisTemplateArn"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
t, err := h.Backend.GetCollaborationAnalysisTemplate(
req.CollaborationIdentifier,
req.AnalysisTemplateArn,
@@ -36,7 +38,7 @@ func (h *Handler) handleListCollaborationAnalysisTemplates(
var req struct {
CollaborationIdentifier string `json:"collaborationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListCollaborationAnalysisTemplates(
req.CollaborationIdentifier,
qp(c, "maxResults"),
@@ -61,7 +63,7 @@ func (h *Handler) handleBatchGetCollaborationAnalysisTemplate(
CollaborationIdentifier string `json:"collaborationIdentifier"`
AnalysisTemplateArns []string `json:"analysisTemplateArns"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, errs, err := h.Backend.BatchGetCollaborationAnalysisTemplate(
req.CollaborationIdentifier,
req.AnalysisTemplateArns,
@@ -86,7 +88,7 @@ func (h *Handler) handleCreateAnalysisTemplate(_ context.Context, body []byte) (
Format string `json:"format"`
AnalysisParameters []map[string]any `json:"analysisParameters"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := checkEnum("format", crtypes.AnalysisFormat(req.Format)); err != nil {
return nil, err
}
@@ -117,7 +119,7 @@ func (h *Handler) handleGetAnalysisTemplate(_ context.Context, body []byte) ([]b
MembershipIdentifier string `json:"membershipIdentifier"`
AnalysisTemplateIdentifier string `json:"analysisTemplateIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
t, err := h.Backend.GetAnalysisTemplate(
req.MembershipIdentifier,
req.AnalysisTemplateIdentifier,
@@ -137,7 +139,7 @@ func (h *Handler) handleListAnalysisTemplates(
var req struct {
MembershipIdentifier string `json:"membershipIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListAnalysisTemplates(
req.MembershipIdentifier,
qp(c, "maxResults"),
@@ -160,7 +162,7 @@ func (h *Handler) handleUpdateAnalysisTemplate(_ context.Context, body []byte) (
AnalysisTemplateIdentifier string `json:"analysisTemplateIdentifier"`
Description string `json:"description"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
t, err := h.Backend.UpdateAnalysisTemplate(
req.MembershipIdentifier,
req.AnalysisTemplateIdentifier,
@@ -178,7 +180,7 @@ func (h *Handler) handleDeleteAnalysisTemplate(_ context.Context, body []byte) (
MembershipIdentifier string `json:"membershipIdentifier"`
AnalysisTemplateIdentifier string `json:"analysisTemplateIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return nil, h.Backend.DeleteAnalysisTemplate(
req.MembershipIdentifier,
diff --git a/services/cleanrooms/handler_analysis_templates_test.go b/services/cleanrooms/handler_analysis_templates_test.go
index d4c09bd3ac..2e5bb212ac 100644
--- a/services/cleanrooms/handler_analysis_templates_test.go
+++ b/services/cleanrooms/handler_analysis_templates_test.go
@@ -1,11 +1,13 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestAnalysisTemplates_Handlers(t *testing.T) {
@@ -20,14 +22,14 @@ func TestAnalysisTemplates_Handlers(t *testing.T) {
"members": []any{}, "queryLogStatus": "DISABLED",
})
var colResp map[string]any
- json.Unmarshal(res.Body.Bytes(), &colResp)
+ json.Unmarshal(res.Body.Bytes(), &colResp, jsonx.V1())
collabID := colResp["collaboration"].(map[string]any)["id"].(string)
rec2 := doRequest(t, e, "POST", "/memberships", map[string]any{
"collaborationIdentifier": collabID, "queryLogStatus": "DISABLED",
})
var memResp map[string]any
- json.Unmarshal(rec2.Body.Bytes(), &memResp)
+ json.Unmarshal(rec2.Body.Bytes(), &memResp, jsonx.V1())
memID := memResp["membership"].(map[string]any)["id"].(string)
rec3 := doRequest(t, e, "POST", "/memberships/"+memID+"/analysistemplates", map[string]any{
@@ -36,7 +38,7 @@ func TestAnalysisTemplates_Handlers(t *testing.T) {
"source": map[string]any{"text": "SELECT 1"},
})
var atResp map[string]any
- json.Unmarshal(rec3.Body.Bytes(), &atResp)
+ json.Unmarshal(rec3.Body.Bytes(), &atResp, jsonx.V1())
atID := atResp["analysisTemplate"].(map[string]any)["id"].(string)
tests := []struct {
diff --git a/services/cleanrooms/handler_collaborations.go b/services/cleanrooms/handler_collaborations.go
index 49d0543ec9..1fc8dd5d43 100644
--- a/services/cleanrooms/handler_collaborations.go
+++ b/services/cleanrooms/handler_collaborations.go
@@ -2,12 +2,14 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
crtypes "github.com/aws/aws-sdk-go-v2/service/cleanrooms/types"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// collaborationWire shadows the embedded Members tag at depth 0 so the
@@ -38,7 +40,7 @@ func (h *Handler) handleCreateCollaboration(_ context.Context, body []byte) ([]b
AllowedResultRegions []string `json:"allowedResultRegions"`
IsMetricsEnabled bool `json:"isMetricsEnabled"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := firstErr(
checkEnumList[crtypes.MemberAbility]("creatorMemberAbilities", req.CreatorMemberAbilities),
checkEnum("queryLogStatus", crtypes.CollaborationQueryLogStatus(req.QueryLogStatus)),
@@ -86,7 +88,7 @@ func (h *Handler) handleGetCollaboration(_ context.Context, body []byte) ([]byte
var req struct {
CollaborationIdentifier string `json:"collaborationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
c, err := h.Backend.GetCollaboration(req.CollaborationIdentifier)
if err != nil {
return nil, declaredCollaborationError(err)
@@ -119,7 +121,7 @@ func (h *Handler) handleUpdateCollaboration(_ context.Context, body []byte) ([]b
Description string `json:"description"`
AnalyticsEngine string `json:"analyticsEngine"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
col, err := h.Backend.UpdateCollaboration(
req.CollaborationIdentifier,
req.Name,
@@ -137,7 +139,7 @@ func (h *Handler) handleDeleteCollaboration(_ context.Context, body []byte) ([]b
var req struct {
CollaborationIdentifier string `json:"collaborationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return nil, h.Backend.DeleteCollaboration(req.CollaborationIdentifier)
}
@@ -150,7 +152,7 @@ func (h *Handler) handleListMembers(
var req struct {
CollaborationIdentifier string `json:"collaborationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListMembers(
req.CollaborationIdentifier,
qp(c, "maxResults"),
@@ -177,7 +179,7 @@ func (h *Handler) handleDeleteMember(_ context.Context, body []byte) ([]byte, er
CollaborationIdentifier string `json:"collaborationIdentifier"`
AccountID string `json:"accountId"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return nil, h.Backend.DeleteMember(req.CollaborationIdentifier, req.AccountID)
}
@@ -190,7 +192,7 @@ func (h *Handler) handleCreateCollaborationChangeRequest(
CollaborationIdentifier string `json:"collaborationIdentifier"`
Changes []Change `json:"changes"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
r, err := h.Backend.CreateCollaborationChangeRequest(
req.CollaborationIdentifier,
req.Changes,
@@ -210,7 +212,7 @@ func (h *Handler) handleGetCollaborationChangeRequest(
CollaborationIdentifier string `json:"collaborationIdentifier"`
ChangeRequestIdentifier string `json:"changeRequestIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
r, err := h.Backend.GetCollaborationChangeRequest(
req.CollaborationIdentifier,
req.ChangeRequestIdentifier,
@@ -230,7 +232,7 @@ func (h *Handler) handleListCollaborationChangeRequests(
var req struct {
CollaborationIdentifier string `json:"collaborationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListCollaborationChangeRequests(
req.CollaborationIdentifier,
qp(c, "status"),
@@ -257,7 +259,7 @@ func (h *Handler) handleUpdateCollaborationChangeRequest(
ChangeRequestIdentifier string `json:"changeRequestIdentifier"`
Action string `json:"action"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
r, err := h.Backend.UpdateCollaborationChangeRequest(
req.CollaborationIdentifier,
req.ChangeRequestIdentifier,
diff --git a/services/cleanrooms/handler_collaborations_test.go b/services/cleanrooms/handler_collaborations_test.go
index 624a25773d..7ca3d42596 100644
--- a/services/cleanrooms/handler_collaborations_test.go
+++ b/services/cleanrooms/handler_collaborations_test.go
@@ -1,11 +1,13 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCollaborations_Handlers(t *testing.T) {
@@ -22,7 +24,7 @@ func TestCollaborations_Handlers(t *testing.T) {
require.Equal(t, http.StatusOK, res.Code)
var colResp map[string]any
- json.Unmarshal(res.Body.Bytes(), &colResp)
+ json.Unmarshal(res.Body.Bytes(), &colResp, jsonx.V1())
collabID := colResp["collaboration"].(map[string]any)["id"].(string)
tests := []struct {
diff --git a/services/cleanrooms/handler_configured_audience_model_associations.go b/services/cleanrooms/handler_configured_audience_model_associations.go
index abc4416ece..b2236e3f54 100644
--- a/services/cleanrooms/handler_configured_audience_model_associations.go
+++ b/services/cleanrooms/handler_configured_audience_model_associations.go
@@ -2,9 +2,11 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleGetCollaborationConfiguredAudienceModelAssociation(
@@ -15,7 +17,7 @@ func (h *Handler) handleGetCollaborationConfiguredAudienceModelAssociation(
CollaborationIdentifier string `json:"collaborationIdentifier"`
ConfiguredAudienceModelAssociationIdentifier string `json:"configuredAudienceModelAssociationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
a, err := h.Backend.GetCollaborationConfiguredAudienceModelAssociation(
req.CollaborationIdentifier,
req.ConfiguredAudienceModelAssociationIdentifier,
@@ -35,7 +37,7 @@ func (h *Handler) handleListCollaborationConfiguredAudienceModelAssociations(
var req struct {
CollaborationIdentifier string `json:"collaborationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListCollaborationConfiguredAudienceModelAssociations(
req.CollaborationIdentifier,
qp(c, "maxResults"),
@@ -68,7 +70,7 @@ func (h *Handler) handleCreateConfiguredAudienceModelAssociation(
Description string `json:"description"`
ManageResourcePolicies bool `json:"manageResourcePolicies"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
a, err := h.Backend.CreateConfiguredAudienceModelAssociation(
req.MembershipIdentifier,
req.ConfiguredAudienceModelArn,
@@ -92,7 +94,7 @@ func (h *Handler) handleGetConfiguredAudienceModelAssociation(
MembershipIdentifier string `json:"membershipIdentifier"`
ConfiguredAudienceModelAssociationIdentifier string `json:"configuredAudienceModelAssociationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
a, err := h.Backend.GetConfiguredAudienceModelAssociation(
req.MembershipIdentifier,
req.ConfiguredAudienceModelAssociationIdentifier,
@@ -112,7 +114,7 @@ func (h *Handler) handleListConfiguredAudienceModelAssociations(
var req struct {
MembershipIdentifier string `json:"membershipIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListConfiguredAudienceModelAssociations(
req.MembershipIdentifier,
qp(c, "maxResults"),
@@ -139,7 +141,7 @@ func (h *Handler) handleUpdateConfiguredAudienceModelAssociation(
Name string `json:"name"`
Description string `json:"description"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
a, err := h.Backend.UpdateConfiguredAudienceModelAssociation(
req.MembershipIdentifier,
req.ConfiguredAudienceModelAssociationIdentifier,
@@ -161,7 +163,7 @@ func (h *Handler) handleDeleteConfiguredAudienceModelAssociation(
MembershipIdentifier string `json:"membershipIdentifier"`
ConfiguredAudienceModelAssociationIdentifier string `json:"configuredAudienceModelAssociationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return nil, h.Backend.DeleteConfiguredAudienceModelAssociation(
req.MembershipIdentifier,
diff --git a/services/cleanrooms/handler_configured_audience_model_associations_test.go b/services/cleanrooms/handler_configured_audience_model_associations_test.go
index fee26f9899..c7fb6026ea 100644
--- a/services/cleanrooms/handler_configured_audience_model_associations_test.go
+++ b/services/cleanrooms/handler_configured_audience_model_associations_test.go
@@ -1,11 +1,13 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestConfiguredAudienceModelAssociations_Handlers(t *testing.T) {
@@ -20,14 +22,14 @@ func TestConfiguredAudienceModelAssociations_Handlers(t *testing.T) {
"members": []any{}, "queryLogStatus": "DISABLED",
})
var colResp map[string]any
- json.Unmarshal(res.Body.Bytes(), &colResp)
+ json.Unmarshal(res.Body.Bytes(), &colResp, jsonx.V1())
collabID := colResp["collaboration"].(map[string]any)["id"].(string)
rec2 := doRequest(t, e, "POST", "/memberships", map[string]any{
"collaborationIdentifier": collabID, "queryLogStatus": "DISABLED",
})
var memResp map[string]any
- json.Unmarshal(rec2.Body.Bytes(), &memResp)
+ json.Unmarshal(rec2.Body.Bytes(), &memResp, jsonx.V1())
memID := memResp["membership"].(map[string]any)["id"].(string)
tests := []struct {
diff --git a/services/cleanrooms/handler_configured_table_associations.go b/services/cleanrooms/handler_configured_table_associations.go
index e9af44e4f8..4b7e79d71e 100644
--- a/services/cleanrooms/handler_configured_table_associations.go
+++ b/services/cleanrooms/handler_configured_table_associations.go
@@ -2,10 +2,12 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
crtypes "github.com/aws/aws-sdk-go-v2/service/cleanrooms/types"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateConfiguredTableAssociation(
@@ -20,7 +22,7 @@ func (h *Handler) handleCreateConfiguredTableAssociation(
ConfiguredTableIdentifier string `json:"configuredTableIdentifier"`
RoleArn string `json:"roleArn"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
a, err := h.Backend.CreateConfiguredTableAssociation(
req.MembershipIdentifier,
req.Name,
@@ -44,7 +46,7 @@ func (h *Handler) handleGetConfiguredTableAssociation(
MembershipIdentifier string `json:"membershipIdentifier"`
ConfiguredTableAssociationIdentifier string `json:"configuredTableAssociationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
a, err := h.Backend.GetConfiguredTableAssociation(
req.MembershipIdentifier,
req.ConfiguredTableAssociationIdentifier,
@@ -64,7 +66,7 @@ func (h *Handler) handleListConfiguredTableAssociations(
var req struct {
MembershipIdentifier string `json:"membershipIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListConfiguredTableAssociations(
req.MembershipIdentifier,
qp(c, "maxResults"),
@@ -91,7 +93,7 @@ func (h *Handler) handleUpdateConfiguredTableAssociation(
Description string `json:"description"`
RoleArn string `json:"roleArn"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
a, err := h.Backend.UpdateConfiguredTableAssociation(
req.MembershipIdentifier,
req.ConfiguredTableAssociationIdentifier,
@@ -113,7 +115,7 @@ func (h *Handler) handleDeleteConfiguredTableAssociation(
MembershipIdentifier string `json:"membershipIdentifier"`
ConfiguredTableAssociationIdentifier string `json:"configuredTableAssociationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return nil, h.Backend.DeleteConfiguredTableAssociation(
req.MembershipIdentifier,
@@ -131,7 +133,7 @@ func (h *Handler) handleCreateConfiguredTableAssociationAnalysisRule(
ConfiguredTableAssociationIdentifier string `json:"configuredTableAssociationIdentifier"`
AnalysisRuleType string `json:"analysisRuleType"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := checkEnum(
"analysisRuleType",
crtypes.ConfiguredTableAssociationAnalysisRuleType(req.AnalysisRuleType),
@@ -161,7 +163,7 @@ func (h *Handler) handleGetConfiguredTableAssociationAnalysisRule(
ConfiguredTableAssociationIdentifier string `json:"configuredTableAssociationIdentifier"`
AnalysisRuleType string `json:"analysisRuleType"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := checkEnum(
"analysisRuleType",
crtypes.ConfiguredTableAssociationAnalysisRuleType(req.AnalysisRuleType),
@@ -191,7 +193,7 @@ func (h *Handler) handleUpdateConfiguredTableAssociationAnalysisRule(
ConfiguredTableAssociationIdentifier string `json:"configuredTableAssociationIdentifier"`
AnalysisRuleType string `json:"analysisRuleType"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := checkEnum(
"analysisRuleType",
crtypes.ConfiguredTableAssociationAnalysisRuleType(req.AnalysisRuleType),
@@ -221,7 +223,7 @@ func (h *Handler) handleDeleteConfiguredTableAssociationAnalysisRule(
ConfiguredTableAssociationIdentifier string `json:"configuredTableAssociationIdentifier"`
AnalysisRuleType string `json:"analysisRuleType"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := checkEnum(
"analysisRuleType",
crtypes.ConfiguredTableAssociationAnalysisRuleType(req.AnalysisRuleType),
diff --git a/services/cleanrooms/handler_configured_table_associations_test.go b/services/cleanrooms/handler_configured_table_associations_test.go
index e3d7109ec5..946cbd5008 100644
--- a/services/cleanrooms/handler_configured_table_associations_test.go
+++ b/services/cleanrooms/handler_configured_table_associations_test.go
@@ -1,11 +1,13 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestConfiguredTableAssociations_Handlers(t *testing.T) {
@@ -20,14 +22,14 @@ func TestConfiguredTableAssociations_Handlers(t *testing.T) {
"members": []any{}, "queryLogStatus": "DISABLED",
})
var colResp map[string]any
- json.Unmarshal(res.Body.Bytes(), &colResp)
+ json.Unmarshal(res.Body.Bytes(), &colResp, jsonx.V1())
collabID := colResp["collaboration"].(map[string]any)["id"].(string)
rec2 := doRequest(t, e, "POST", "/memberships", map[string]any{
"collaborationIdentifier": collabID, "queryLogStatus": "DISABLED",
})
var memResp map[string]any
- json.Unmarshal(rec2.Body.Bytes(), &memResp)
+ json.Unmarshal(rec2.Body.Bytes(), &memResp, jsonx.V1())
memID := memResp["membership"].(map[string]any)["id"].(string)
rec3 := doRequest(t, e, "POST", "/configuredTables", map[string]any{
@@ -36,7 +38,7 @@ func TestConfiguredTableAssociations_Handlers(t *testing.T) {
"allowedColumns": []string{"id"}, "analysisMethod": "DIRECT_QUERY",
})
var ctResp map[string]any
- json.Unmarshal(rec3.Body.Bytes(), &ctResp)
+ json.Unmarshal(rec3.Body.Bytes(), &ctResp, jsonx.V1())
ctID := ctResp["configuredTable"].(map[string]any)["id"].(string)
tests := []struct {
diff --git a/services/cleanrooms/handler_configured_tables.go b/services/cleanrooms/handler_configured_tables.go
index 61474b68ad..990d126101 100644
--- a/services/cleanrooms/handler_configured_tables.go
+++ b/services/cleanrooms/handler_configured_tables.go
@@ -2,10 +2,12 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
crtypes "github.com/aws/aws-sdk-go-v2/service/cleanrooms/types"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateConfiguredTable(_ context.Context, body []byte) ([]byte, error) {
@@ -18,7 +20,7 @@ func (h *Handler) handleCreateConfiguredTable(_ context.Context, body []byte) ([
AllowedColumns []string `json:"allowedColumns"`
SelectedMethods []string `json:"selectedAnalysisMethods"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := firstErr(
checkEnum("analysisMethod", crtypes.AnalysisMethod(req.AnalysisMethod)),
@@ -47,7 +49,7 @@ func (h *Handler) handleGetConfiguredTable(_ context.Context, body []byte) ([]by
var req struct {
ConfiguredTableIdentifier string `json:"configuredTableIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
ct, err := h.Backend.GetConfiguredTable(req.ConfiguredTableIdentifier)
if err != nil {
return nil, err
@@ -76,7 +78,7 @@ func (h *Handler) handleUpdateConfiguredTable(_ context.Context, body []byte) ([
Description string `json:"description"`
SelectedMethods []string `json:"selectedAnalysisMethods"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
ct, err := h.Backend.UpdateConfiguredTable(
req.ConfiguredTableIdentifier,
req.Name,
@@ -94,7 +96,7 @@ func (h *Handler) handleDeleteConfiguredTable(_ context.Context, body []byte) ([
var req struct {
ConfiguredTableIdentifier string `json:"configuredTableIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return nil, h.Backend.DeleteConfiguredTable(req.ConfiguredTableIdentifier)
}
@@ -108,7 +110,7 @@ func (h *Handler) handleCreateConfiguredTableAnalysisRule(
ConfiguredTableIdentifier string `json:"configuredTableIdentifier"`
AnalysisRuleType string `json:"analysisRuleType"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := checkEnum(
"analysisRuleType",
crtypes.ConfiguredTableAnalysisRuleType(req.AnalysisRuleType),
@@ -136,7 +138,7 @@ func (h *Handler) handleGetConfiguredTableAnalysisRule(
ConfiguredTableIdentifier string `json:"configuredTableIdentifier"`
AnalysisRuleType string `json:"analysisRuleType"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := checkEnum(
"analysisRuleType",
crtypes.ConfiguredTableAnalysisRuleType(req.AnalysisRuleType),
@@ -164,7 +166,7 @@ func (h *Handler) handleUpdateConfiguredTableAnalysisRule(
ConfiguredTableIdentifier string `json:"configuredTableIdentifier"`
AnalysisRuleType string `json:"analysisRuleType"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := checkEnum(
"analysisRuleType",
crtypes.ConfiguredTableAnalysisRuleType(req.AnalysisRuleType),
@@ -192,7 +194,7 @@ func (h *Handler) handleDeleteConfiguredTableAnalysisRule(
ConfiguredTableIdentifier string `json:"configuredTableIdentifier"`
AnalysisRuleType string `json:"analysisRuleType"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := checkEnum(
"analysisRuleType",
crtypes.ConfiguredTableAnalysisRuleType(req.AnalysisRuleType),
diff --git a/services/cleanrooms/handler_configured_tables_test.go b/services/cleanrooms/handler_configured_tables_test.go
index a373d17b35..8f63564886 100644
--- a/services/cleanrooms/handler_configured_tables_test.go
+++ b/services/cleanrooms/handler_configured_tables_test.go
@@ -1,12 +1,14 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestConfiguredTables_Handlers(t *testing.T) {
@@ -23,7 +25,7 @@ func TestConfiguredTables_Handlers(t *testing.T) {
require.Equal(t, http.StatusOK, res.Code)
var ctResp map[string]any
- json.Unmarshal(res.Body.Bytes(), &ctResp)
+ json.Unmarshal(res.Body.Bytes(), &ctResp, jsonx.V1())
ct := ctResp["configuredTable"].(map[string]any)
id := ct["id"].(string)
diff --git a/services/cleanrooms/handler_id_mapping_tables.go b/services/cleanrooms/handler_id_mapping_tables.go
index eb47201eb7..2e56499935 100644
--- a/services/cleanrooms/handler_id_mapping_tables.go
+++ b/services/cleanrooms/handler_id_mapping_tables.go
@@ -2,10 +2,12 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateIDMappingTable(_ context.Context, body []byte) ([]byte, error) {
@@ -17,7 +19,7 @@ func (h *Handler) handleCreateIDMappingTable(_ context.Context, body []byte) ([]
Description string `json:"description"`
KmsKeyArn string `json:"kmsKeyArn"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
t, err := h.Backend.CreateIDMappingTable(
req.MembershipIdentifier,
req.Name,
@@ -38,7 +40,7 @@ func (h *Handler) handleGetIDMappingTable(_ context.Context, body []byte) ([]byt
MembershipIdentifier string `json:"membershipIdentifier"`
IDMappingTableIdentifier string `json:"idMappingTableIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
t, err := h.Backend.GetIDMappingTable(req.MembershipIdentifier, req.IDMappingTableIdentifier)
if err != nil {
return nil, err
@@ -55,7 +57,7 @@ func (h *Handler) handleListIDMappingTables(
var req struct {
MembershipIdentifier string `json:"membershipIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListIDMappingTables(
req.MembershipIdentifier,
qp(c, "maxResults"),
@@ -79,7 +81,7 @@ func (h *Handler) handleUpdateIDMappingTable(_ context.Context, body []byte) ([]
Description string `json:"description"`
KmsKeyArn string `json:"kmsKeyArn"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
t, err := h.Backend.UpdateIDMappingTable(
req.MembershipIdentifier,
req.IDMappingTableIdentifier,
@@ -98,7 +100,7 @@ func (h *Handler) handleDeleteIDMappingTable(_ context.Context, body []byte) ([]
MembershipIdentifier string `json:"membershipIdentifier"`
IDMappingTableIdentifier string `json:"idMappingTableIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return nil, h.Backend.DeleteIDMappingTable(
req.MembershipIdentifier,
@@ -112,7 +114,7 @@ func (h *Handler) handlePopulateIDMappingTable(_ context.Context, body []byte) (
IDMappingTableIdentifier string `json:"idMappingTableIdentifier"`
JobType string `json:"jobType"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
switch req.JobType {
case "", "INCREMENTAL", "BATCH", "DELETE_ONLY":
diff --git a/services/cleanrooms/handler_id_namespace_associations.go b/services/cleanrooms/handler_id_namespace_associations.go
index 2f678df1ff..ffde89ac6f 100644
--- a/services/cleanrooms/handler_id_namespace_associations.go
+++ b/services/cleanrooms/handler_id_namespace_associations.go
@@ -2,9 +2,11 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleGetCollaborationIDNamespaceAssociation(
@@ -15,7 +17,7 @@ func (h *Handler) handleGetCollaborationIDNamespaceAssociation(
CollaborationIdentifier string `json:"collaborationIdentifier"`
IDNamespaceAssociationIdentifier string `json:"idNamespaceAssociationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
a, err := h.Backend.GetCollaborationIDNamespaceAssociation(
req.CollaborationIdentifier,
req.IDNamespaceAssociationIdentifier,
@@ -35,7 +37,7 @@ func (h *Handler) handleListCollaborationIDNamespaceAssociations(
var req struct {
CollaborationIdentifier string `json:"collaborationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListCollaborationIDNamespaceAssociations(
req.CollaborationIdentifier,
qp(c, "maxResults"),
@@ -64,7 +66,7 @@ func (h *Handler) handleCreateIDNamespaceAssociation(
Name string `json:"name"`
Description string `json:"description"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
a, err := h.Backend.CreateIDNamespaceAssociation(
req.MembershipIdentifier,
req.Name,
@@ -85,7 +87,7 @@ func (h *Handler) handleGetIDNamespaceAssociation(_ context.Context, body []byte
MembershipIdentifier string `json:"membershipIdentifier"`
IDNamespaceAssociationIdentifier string `json:"idNamespaceAssociationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
a, err := h.Backend.GetIDNamespaceAssociation(
req.MembershipIdentifier,
req.IDNamespaceAssociationIdentifier,
@@ -105,7 +107,7 @@ func (h *Handler) handleListIDNamespaceAssociations(
var req struct {
MembershipIdentifier string `json:"membershipIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListIDNamespaceAssociations(
req.MembershipIdentifier,
qp(c, "maxResults"),
@@ -132,7 +134,7 @@ func (h *Handler) handleUpdateIDNamespaceAssociation(
IDNamespaceAssociationIdentifier string `json:"idNamespaceAssociationIdentifier"`
Description string `json:"description"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
a, err := h.Backend.UpdateIDNamespaceAssociation(
req.MembershipIdentifier,
req.IDNamespaceAssociationIdentifier,
@@ -154,7 +156,7 @@ func (h *Handler) handleDeleteIDNamespaceAssociation(
MembershipIdentifier string `json:"membershipIdentifier"`
IDNamespaceAssociationIdentifier string `json:"idNamespaceAssociationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return nil, h.Backend.DeleteIDNamespaceAssociation(
req.MembershipIdentifier,
diff --git a/services/cleanrooms/handler_intermediate_tables.go b/services/cleanrooms/handler_intermediate_tables.go
index 974457f51a..d8c6e384a3 100644
--- a/services/cleanrooms/handler_intermediate_tables.go
+++ b/services/cleanrooms/handler_intermediate_tables.go
@@ -2,10 +2,12 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
crtypes "github.com/aws/aws-sdk-go-v2/service/cleanrooms/types"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateIntermediateTable(_ context.Context, body []byte) ([]byte, error) {
@@ -18,7 +20,7 @@ func (h *Handler) handleCreateIntermediateTable(_ context.Context, body []byte)
KmsKeyArn string `json:"kmsKeyArn"`
RetentionInDays int32 `json:"retentionInDays"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
it, err := h.Backend.CreateIntermediateTable(
req.MembershipIdentifier,
req.Name,
@@ -40,7 +42,7 @@ func (h *Handler) handleGetIntermediateTable(_ context.Context, body []byte) ([]
MembershipIdentifier string `json:"membershipIdentifier"`
IntermediateTableIdentifier string `json:"intermediateTableIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
it, err := h.Backend.GetIntermediateTable(req.MembershipIdentifier, req.IntermediateTableIdentifier)
if err != nil {
return nil, err
@@ -57,7 +59,7 @@ func (h *Handler) handleListIntermediateTables(
var req struct {
MembershipIdentifier string `json:"membershipIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListIntermediateTables(
req.MembershipIdentifier,
qp(c, "maxResults"),
@@ -81,7 +83,7 @@ func (h *Handler) handleUpdateIntermediateTable(_ context.Context, body []byte)
Description string `json:"description"`
KmsKeyArn string `json:"kmsKeyArn"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
it, err := h.Backend.UpdateIntermediateTable(
req.MembershipIdentifier,
req.IntermediateTableIdentifier,
@@ -100,7 +102,7 @@ func (h *Handler) handleDeleteIntermediateTable(_ context.Context, body []byte)
MembershipIdentifier string `json:"membershipIdentifier"`
IntermediateTableIdentifier string `json:"intermediateTableIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return nil, h.Backend.DeleteIntermediateTable(req.MembershipIdentifier, req.IntermediateTableIdentifier)
}
@@ -114,7 +116,7 @@ func (h *Handler) handleListIntermediateTableVersions(
MembershipIdentifier string `json:"membershipIdentifier"`
IntermediateTableIdentifier string `json:"intermediateTableIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListIntermediateTableVersions(
req.MembershipIdentifier,
req.IntermediateTableIdentifier,
@@ -143,7 +145,7 @@ func (h *Handler) handlePopulateIntermediateTable(_ context.Context, body []byte
IntermediateTableIdentifier string `json:"intermediateTableIdentifier"`
AnalysisPayerAccountID string `json:"analysisPayerAccountId"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
result, err := h.Backend.PopulateIntermediateTable(
req.MembershipIdentifier,
req.IntermediateTableIdentifier,
@@ -164,7 +166,7 @@ func (h *Handler) handleDisallowIntermediateTable(_ context.Context, body []byte
MembershipIdentifier string `json:"membershipIdentifier"`
IntermediateTableName string `json:"intermediateTableName"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
// IncludeDescendants defaults to true on the wire (see
// DisallowIntermediateTableInput's doc comment in the real SDK).
includeDescendants := true
@@ -184,7 +186,7 @@ func (h *Handler) handleCreateIntermediateTableAnalysisRule(_ context.Context, b
IntermediateTableIdentifier string `json:"intermediateTableIdentifier"`
AnalysisRuleType string `json:"analysisRuleType"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := checkEnum(
"analysisRuleType",
crtypes.IntermediateTableAnalysisRuleType(req.AnalysisRuleType),
@@ -211,7 +213,7 @@ func (h *Handler) handleGetIntermediateTableAnalysisRule(_ context.Context, body
IntermediateTableIdentifier string `json:"intermediateTableIdentifier"`
AnalysisRuleType string `json:"analysisRuleType"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
r, err := h.Backend.GetIntermediateTableAnalysisRule(
req.MembershipIdentifier,
req.IntermediateTableIdentifier,
@@ -231,7 +233,7 @@ func (h *Handler) handleUpdateIntermediateTableAnalysisRule(_ context.Context, b
IntermediateTableIdentifier string `json:"intermediateTableIdentifier"`
AnalysisRuleType string `json:"analysisRuleType"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := checkEnum(
"analysisRuleType",
crtypes.IntermediateTableAnalysisRuleType(req.AnalysisRuleType),
@@ -258,7 +260,7 @@ func (h *Handler) handleDeleteIntermediateTableAnalysisRule(_ context.Context, b
IntermediateTableIdentifier string `json:"intermediateTableIdentifier"`
AnalysisRuleType string `json:"analysisRuleType"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := checkEnum(
"analysisRuleType",
crtypes.IntermediateTableAnalysisRuleType(req.AnalysisRuleType),
diff --git a/services/cleanrooms/handler_memberships.go b/services/cleanrooms/handler_memberships.go
index ee5184e3f4..c1e5c3283f 100644
--- a/services/cleanrooms/handler_memberships.go
+++ b/services/cleanrooms/handler_memberships.go
@@ -2,10 +2,12 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
crtypes "github.com/aws/aws-sdk-go-v2/service/cleanrooms/types"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateMembership(_ context.Context, body []byte) ([]byte, error) {
@@ -20,7 +22,7 @@ func (h *Handler) handleCreateMembership(_ context.Context, body []byte) ([]byte
MemberAbilities []string `json:"memberAbilities"`
IsMetricsEnabled bool `json:"isMetricsEnabled"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := firstErr(
checkEnum("queryLogStatus", crtypes.MembershipQueryLogStatus(req.QueryLogStatus)),
checkEnum("jobLogStatus", crtypes.MembershipJobLogStatus(req.JobLogStatus)),
@@ -50,7 +52,7 @@ func (h *Handler) handleGetMembership(_ context.Context, body []byte) ([]byte, e
var req struct {
MembershipIdentifier string `json:"membershipIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
m, err := h.Backend.GetMembership(req.MembershipIdentifier)
if err != nil {
return nil, err
@@ -84,7 +86,7 @@ func (h *Handler) handleUpdateMembership(_ context.Context, body []byte) ([]byte
QueryLogStatus string `json:"queryLogStatus"`
JobLogStatus string `json:"jobLogStatus"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := firstErr(
checkEnum("jobLogStatus", crtypes.MembershipJobLogStatus(req.JobLogStatus)),
checkEnum("queryLogStatus", crtypes.MembershipQueryLogStatus(req.QueryLogStatus)),
@@ -110,7 +112,7 @@ func (h *Handler) handleDeleteMembership(_ context.Context, body []byte) ([]byte
var req struct {
MembershipIdentifier string `json:"membershipIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return nil, h.Backend.DeleteMembership(req.MembershipIdentifier)
}
diff --git a/services/cleanrooms/handler_privacy_budgets.go b/services/cleanrooms/handler_privacy_budgets.go
index 00d6f523d1..dcd7b5108b 100644
--- a/services/cleanrooms/handler_privacy_budgets.go
+++ b/services/cleanrooms/handler_privacy_budgets.go
@@ -2,10 +2,12 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
crtypes "github.com/aws/aws-sdk-go-v2/service/cleanrooms/types"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleGetCollaborationPrivacyBudgetTemplate(
@@ -16,7 +18,7 @@ func (h *Handler) handleGetCollaborationPrivacyBudgetTemplate(
CollaborationIdentifier string `json:"collaborationIdentifier"`
PrivacyBudgetTemplateIdentifier string `json:"privacyBudgetTemplateIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
t, err := h.Backend.GetCollaborationPrivacyBudgetTemplate(
req.CollaborationIdentifier,
req.PrivacyBudgetTemplateIdentifier,
@@ -36,7 +38,7 @@ func (h *Handler) handleListCollaborationPrivacyBudgetTemplates(
var req struct {
CollaborationIdentifier string `json:"collaborationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListCollaborationPrivacyBudgetTemplates(
req.CollaborationIdentifier,
qp(c, "maxResults"),
@@ -61,7 +63,7 @@ func (h *Handler) handleListCollaborationPrivacyBudgets(
var req struct {
CollaborationIdentifier string `json:"collaborationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListCollaborationPrivacyBudgets(
req.CollaborationIdentifier,
qp(c, "privacyBudgetType"),
@@ -90,7 +92,7 @@ func (h *Handler) handleCreatePrivacyBudgetTemplate(
PrivacyBudgetType string `json:"privacyBudgetType"`
AutoRefresh string `json:"autoRefresh"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := checkEnum("privacyBudgetType", crtypes.PrivacyBudgetType(req.PrivacyBudgetType)); err != nil {
return nil, err
}
@@ -114,7 +116,7 @@ func (h *Handler) handleGetPrivacyBudgetTemplate(_ context.Context, body []byte)
MembershipIdentifier string `json:"membershipIdentifier"`
PrivacyBudgetTemplateIdentifier string `json:"privacyBudgetTemplateIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
t, err := h.Backend.GetPrivacyBudgetTemplate(
req.MembershipIdentifier,
req.PrivacyBudgetTemplateIdentifier,
@@ -134,7 +136,7 @@ func (h *Handler) handleListPrivacyBudgetTemplates(
var req struct {
MembershipIdentifier string `json:"membershipIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListPrivacyBudgetTemplates(
req.MembershipIdentifier,
qp(c, "privacyBudgetType"),
@@ -162,7 +164,7 @@ func (h *Handler) handleUpdatePrivacyBudgetTemplate(
PrivacyBudgetTemplateIdentifier string `json:"privacyBudgetTemplateIdentifier"`
AutoRefresh string `json:"autoRefresh"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
t, err := h.Backend.UpdatePrivacyBudgetTemplate(
req.MembershipIdentifier,
req.PrivacyBudgetTemplateIdentifier,
@@ -184,7 +186,7 @@ func (h *Handler) handleDeletePrivacyBudgetTemplate(
MembershipIdentifier string `json:"membershipIdentifier"`
PrivacyBudgetTemplateIdentifier string `json:"privacyBudgetTemplateIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return nil, h.Backend.DeletePrivacyBudgetTemplate(
req.MembershipIdentifier,
@@ -200,7 +202,7 @@ func (h *Handler) handleListPrivacyBudgets(
var req struct {
MembershipIdentifier string `json:"membershipIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListPrivacyBudgets(
req.MembershipIdentifier,
qp(c, "privacyBudgetType"),
@@ -223,7 +225,7 @@ func (h *Handler) handlePreviewPrivacyImpact(_ context.Context, body []byte) ([]
Parameters map[string]any `json:"parameters"`
MembershipIdentifier string `json:"membershipIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
result, err := h.Backend.PreviewPrivacyImpact(req.MembershipIdentifier, req.Parameters)
if err != nil {
return nil, err
diff --git a/services/cleanrooms/handler_protected_jobs.go b/services/cleanrooms/handler_protected_jobs.go
index 7001ced7f6..88a64fe9a8 100644
--- a/services/cleanrooms/handler_protected_jobs.go
+++ b/services/cleanrooms/handler_protected_jobs.go
@@ -2,9 +2,11 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// protectedJobWire shadows the request-only "type" key; types.ProtectedJob has no type member.
@@ -23,7 +25,7 @@ func (h *Handler) handleStartProtectedJob(_ context.Context, body []byte) ([]byt
Type string `json:"type"`
PayerAccountID string `json:"jobComputePayerAccountId"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
j, err := h.Backend.StartProtectedJob(
req.MembershipIdentifier,
req.Type,
@@ -43,7 +45,7 @@ func (h *Handler) handleGetProtectedJob(_ context.Context, body []byte) ([]byte,
MembershipIdentifier string `json:"membershipIdentifier"`
ProtectedJobIdentifier string `json:"protectedJobIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
j, err := h.Backend.GetProtectedJob(req.MembershipIdentifier, req.ProtectedJobIdentifier)
if err != nil {
return nil, err
@@ -60,7 +62,7 @@ func (h *Handler) handleListProtectedJobs(
var req struct {
MembershipIdentifier string `json:"membershipIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListProtectedJobs(
req.MembershipIdentifier,
qp(c, "status"),
@@ -84,7 +86,7 @@ func (h *Handler) handleUpdateProtectedJob(_ context.Context, body []byte) ([]by
ProtectedJobIdentifier string `json:"protectedJobIdentifier"`
TargetStatus string `json:"targetStatus"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
j, err := h.Backend.UpdateProtectedJob(
req.MembershipIdentifier,
req.ProtectedJobIdentifier,
diff --git a/services/cleanrooms/handler_protected_jobs_test.go b/services/cleanrooms/handler_protected_jobs_test.go
index 68ea59ca33..5f628c6a71 100644
--- a/services/cleanrooms/handler_protected_jobs_test.go
+++ b/services/cleanrooms/handler_protected_jobs_test.go
@@ -1,11 +1,13 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestProtectedJobs_Handlers(t *testing.T) {
@@ -20,14 +22,14 @@ func TestProtectedJobs_Handlers(t *testing.T) {
"members": []any{}, "queryLogStatus": "DISABLED",
})
var colResp map[string]any
- json.Unmarshal(res.Body.Bytes(), &colResp)
+ json.Unmarshal(res.Body.Bytes(), &colResp, jsonx.V1())
collabID := colResp["collaboration"].(map[string]any)["id"].(string)
rec2 := doRequest(t, e, "POST", "/memberships", map[string]any{
"collaborationIdentifier": collabID, "queryLogStatus": "DISABLED",
})
var memResp map[string]any
- json.Unmarshal(rec2.Body.Bytes(), &memResp)
+ json.Unmarshal(rec2.Body.Bytes(), &memResp, jsonx.V1())
memID := memResp["membership"].(map[string]any)["id"].(string)
tests := []struct {
diff --git a/services/cleanrooms/handler_protected_queries.go b/services/cleanrooms/handler_protected_queries.go
index c85a1d324c..b6c0d1883a 100644
--- a/services/cleanrooms/handler_protected_queries.go
+++ b/services/cleanrooms/handler_protected_queries.go
@@ -2,9 +2,11 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleStartProtectedQuery(_ context.Context, body []byte) ([]byte, error) {
@@ -16,7 +18,7 @@ func (h *Handler) handleStartProtectedQuery(_ context.Context, body []byte) ([]b
MembershipIdentifier string `json:"membershipIdentifier"`
PayerAccountID string `json:"queryComputePayerAccountId"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := validateProtectedType(req.Type); err != nil {
return nil, err
@@ -41,7 +43,7 @@ func (h *Handler) handleGetProtectedQuery(_ context.Context, body []byte) ([]byt
MembershipIdentifier string `json:"membershipIdentifier"`
ProtectedQueryIdentifier string `json:"protectedQueryIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
q, err := h.Backend.GetProtectedQuery(req.MembershipIdentifier, req.ProtectedQueryIdentifier)
if err != nil {
return nil, err
@@ -58,7 +60,7 @@ func (h *Handler) handleListProtectedQueries(
var req struct {
MembershipIdentifier string `json:"membershipIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListProtectedQueries(
req.MembershipIdentifier,
qp(c, "status"),
@@ -82,7 +84,7 @@ func (h *Handler) handleUpdateProtectedQuery(_ context.Context, body []byte) ([]
ProtectedQueryIdentifier string `json:"protectedQueryIdentifier"`
TargetStatus string `json:"targetStatus"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := validateTargetStatus(req.TargetStatus); err != nil {
return nil, err
diff --git a/services/cleanrooms/handler_routing.go b/services/cleanrooms/handler_routing.go
index 50ab501e41..0fde29aeb3 100644
--- a/services/cleanrooms/handler_routing.go
+++ b/services/cleanrooms/handler_routing.go
@@ -1,9 +1,12 @@
package cleanrooms
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// classifyPath maps (method, path) to an operation name and primary resource.
@@ -643,17 +646,17 @@ func injectPathParams(path, _ string, body []byte) []byte {
path = strings.TrimPrefix(path, "/")
segs := strings.Split(path, "/")
- var m map[string]json.RawMessage
+ var m map[string]jsontext.Value
if len(body) > 0 {
- _ = json.Unmarshal(body, &m)
+ _ = json.Unmarshal(body, &m, jsonx.V1())
}
if m == nil {
- m = make(map[string]json.RawMessage)
+ m = make(map[string]jsontext.Value)
}
setStr := func(key, val string) {
if val != "" {
- b, _ := json.Marshal(val)
+ b, _ := json.Marshal(val, jsonx.V1())
m[key] = b
}
}
@@ -673,7 +676,7 @@ func injectPathParams(path, _ string, body []byte) []byte {
setStr("resourceArn", arnVal)
}
- out, _ := json.Marshal(m)
+ out, _ := json.Marshal(m, jsonx.V1())
return out
}
diff --git a/services/cleanrooms/handler_schemas.go b/services/cleanrooms/handler_schemas.go
index 9921c283c3..c4f5226da6 100644
--- a/services/cleanrooms/handler_schemas.go
+++ b/services/cleanrooms/handler_schemas.go
@@ -2,10 +2,12 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
crtypes "github.com/aws/aws-sdk-go-v2/service/cleanrooms/types"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleBatchGetSchema(_ context.Context, body []byte) ([]byte, error) {
@@ -13,7 +15,7 @@ func (h *Handler) handleBatchGetSchema(_ context.Context, body []byte) ([]byte,
CollaborationIdentifier string `json:"collaborationIdentifier"`
Names []string `json:"names"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, errs, err := h.Backend.BatchGetSchema(req.CollaborationIdentifier, req.Names)
if err != nil {
return nil, err
@@ -30,7 +32,7 @@ func (h *Handler) handleBatchGetSchemaAnalysisRule(_ context.Context, body []byt
Type string `json:"type"`
} `json:"schemaAnalysisRuleRequests"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
names := make([]string, 0, len(req.SchemaAnalysisRuleRequests))
var ruleType string
for _, r := range req.SchemaAnalysisRuleRequests {
@@ -56,7 +58,7 @@ func (h *Handler) handleGetSchema(_ context.Context, body []byte) ([]byte, error
CollaborationIdentifier string `json:"collaborationIdentifier"`
Name string `json:"name"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
s, err := h.Backend.GetSchema(req.CollaborationIdentifier, req.Name)
if err != nil {
return nil, err
@@ -73,7 +75,7 @@ func (h *Handler) handleListSchemas(
var req struct {
CollaborationIdentifier string `json:"collaborationIdentifier"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
items, next, err := h.Backend.ListSchemas(
req.CollaborationIdentifier,
qp(c, "schemaType"),
@@ -97,7 +99,7 @@ func (h *Handler) handleGetSchemaAnalysisRule(_ context.Context, body []byte) ([
Name string `json:"name"`
Type string `json:"type"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := checkEnum("type", crtypes.AnalysisRuleType(req.Type)); err != nil {
return nil, err
}
diff --git a/services/cleanrooms/handler_schemas_test.go b/services/cleanrooms/handler_schemas_test.go
index d15490b244..5bb05ed47d 100644
--- a/services/cleanrooms/handler_schemas_test.go
+++ b/services/cleanrooms/handler_schemas_test.go
@@ -1,12 +1,14 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestSchemas_Handlers(t *testing.T) {
@@ -23,7 +25,7 @@ func TestSchemas_Handlers(t *testing.T) {
require.Equal(t, http.StatusOK, res.Code)
var colResp map[string]any
- json.Unmarshal(res.Body.Bytes(), &colResp)
+ json.Unmarshal(res.Body.Bytes(), &colResp, jsonx.V1())
collab := colResp["collaboration"].(map[string]any)
collabID := collab["id"].(string)
diff --git a/services/cleanrooms/handler_tags.go b/services/cleanrooms/handler_tags.go
index e259526472..44ceb5a767 100644
--- a/services/cleanrooms/handler_tags.go
+++ b/services/cleanrooms/handler_tags.go
@@ -2,16 +2,18 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleListTagsForResource(_ context.Context, body []byte) ([]byte, error) {
var req struct {
ResourceArn string `json:"resourceArn"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
tags, err := h.Backend.ListTagsForResource(req.ResourceArn)
if err != nil {
return nil, err
@@ -25,7 +27,7 @@ func (h *Handler) handleTagResource(_ context.Context, body []byte) ([]byte, err
Tags map[string]string `json:"tags"`
ResourceArn string `json:"resourceArn"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return nil, h.Backend.TagResource(req.ResourceArn, req.Tags)
}
@@ -39,7 +41,7 @@ func (h *Handler) handleUntagResource(
ResourceArn string `json:"resourceArn"`
TagKeys []string `json:"tagKeys"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
// tagKeys can also come from query params
if len(req.TagKeys) == 0 {
req.TagKeys = c.Request().URL.Query()["tagKeys"]
diff --git a/services/cleanrooms/handler_test.go b/services/cleanrooms/handler_test.go
index 11206a13ad..f3e8ae551e 100644
--- a/services/cleanrooms/handler_test.go
+++ b/services/cleanrooms/handler_test.go
@@ -2,13 +2,15 @@ package cleanrooms_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cleanrooms"
)
@@ -32,7 +34,7 @@ func doRequest(
var reqBody []byte
if body != nil {
var err error
- reqBody, err = json.Marshal(body)
+ reqBody, err = json.Marshal(body, jsonx.V1())
if err != nil {
t.Fatalf("marshal request: %v", err)
}
@@ -65,7 +67,7 @@ func TestCollaborationCRUD(t *testing.T) {
t.Fatalf("create: status %d want %d: %s", rec.Code, http.StatusOK, rec.Body.String())
}
var createResp map[string]any
- if err := json.NewDecoder(rec.Body).Decode(&createResp); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()); err != nil {
t.Fatalf("decode create: %v", err)
}
if _, ok := createResp["collaboration"]; !ok {
@@ -79,7 +81,7 @@ func TestCollaborationCRUD(t *testing.T) {
t.Fatalf("list: status %d want %d: %s", rec.Code, http.StatusOK, rec.Body.String())
}
var listResp map[string]any
- if err := json.NewDecoder(rec.Body).Decode(&listResp); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &listResp, jsonx.V1()); err != nil {
t.Fatalf("decode list: %v", err)
}
if _, ok := listResp["collaborationList"]; !ok {
@@ -126,7 +128,7 @@ func TestConfiguredTableCRUD(t *testing.T) {
t.Fatalf("create: status %d want %d: %s", rec.Code, http.StatusOK, rec.Body.String())
}
var createResp map[string]any
- if err := json.NewDecoder(rec.Body).Decode(&createResp); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()); err != nil {
t.Fatalf("decode: %v", err)
}
ctID := createResp["configuredTable"].(map[string]any)["id"].(string)
@@ -173,7 +175,7 @@ func TestMembershipCRUD(t *testing.T) {
t.Fatalf("create collab: %s", colRec.Body.String())
}
var colResp map[string]any
- _ = json.NewDecoder(colRec.Body).Decode(&colResp)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(colRec.Body), &colResp, jsonx.V1())
colID := colResp["collaboration"].(map[string]any)["id"].(string)
createBody := map[string]any{
@@ -187,7 +189,7 @@ func TestMembershipCRUD(t *testing.T) {
t.Fatalf("create: status %d want %d: %s", rec.Code, http.StatusOK, rec.Body.String())
}
var createResp map[string]any
- _ = json.NewDecoder(rec.Body).Decode(&createResp)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1())
mID := createResp["membership"].(map[string]any)["id"].(string)
// List memberships
@@ -223,7 +225,7 @@ func TestTagOperations(t *testing.T) {
"creatorMemberAbilities": []string{}, "members": []any{}, "queryLogStatus": "DISABLED",
})
var colResp map[string]any
- _ = json.NewDecoder(colRec.Body).Decode(&colResp)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(colRec.Body), &colResp, jsonx.V1())
testARN := colResp["collaboration"].(map[string]any)["arn"].(string)
// Tag resource
@@ -293,14 +295,14 @@ func TestProtectedQueryLifecycle(t *testing.T) {
"creatorMemberAbilities": []string{}, "members": []any{}, "queryLogStatus": "DISABLED",
})
var colResp map[string]any
- _ = json.NewDecoder(colRec.Body).Decode(&colResp)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(colRec.Body), &colResp, jsonx.V1())
colID := colResp["collaboration"].(map[string]any)["id"].(string)
// Create membership
memRec := doRequest(t, e, http.MethodPost, "/memberships",
map[string]any{"collaborationIdentifier": colID, "queryLogStatus": "DISABLED"})
var memResp map[string]any
- _ = json.NewDecoder(memRec.Body).Decode(&memResp)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(memRec.Body), &memResp, jsonx.V1())
mID := memResp["membership"].(map[string]any)["id"].(string)
// Start protected query
@@ -313,7 +315,7 @@ func TestProtectedQueryLifecycle(t *testing.T) {
t.Fatalf("start query: status %d: %s", rec.Code, rec.Body.String())
}
var resp map[string]any
- _ = json.NewDecoder(rec.Body).Decode(&resp)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1())
if _, ok := resp["protectedQuery"]; !ok {
t.Fatal("missing protectedQuery in response")
}
diff --git a/services/cleanrooms/id_mapping_tables_test.go b/services/cleanrooms/id_mapping_tables_test.go
index b06b8f5950..5aad3ecbed 100644
--- a/services/cleanrooms/id_mapping_tables_test.go
+++ b/services/cleanrooms/id_mapping_tables_test.go
@@ -1,13 +1,15 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHTTP_PopulateIDMappingTable(t *testing.T) {
@@ -22,7 +24,7 @@ func TestHTTP_PopulateIDMappingTable(t *testing.T) {
})
rec := doRequest(t, e, "GET", "/collaborations", nil)
var colResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &colResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &colResp, jsonx.V1()))
colID := colResp["collaborationList"].([]any)[0].(map[string]any)["id"].(string)
// Create Membership
@@ -30,7 +32,7 @@ func TestHTTP_PopulateIDMappingTable(t *testing.T) {
"collaborationIdentifier": colID, "queryLogStatus": "DISABLED",
})
var memResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &memResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &memResp, jsonx.V1()))
mID := memResp["membership"].(map[string]any)["id"].(string)
// Create Table
@@ -44,7 +46,7 @@ func TestHTTP_PopulateIDMappingTable(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["idMappingTable"]["id"].(string)
// Populate
@@ -68,7 +70,7 @@ func createIntermediateTable(t *testing.T, e *echo.Echo, mID string) string {
})
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["intermediateTable"].(map[string]any)["id"].(string)
}
@@ -220,7 +222,7 @@ func TestHTTP_UpdateIntermediateTable(t *testing.T) {
map[string]any{"description": "updated"})
require.Equal(t, http.StatusOK, updRec.Code, updRec.Body.String())
var updResp map[string]any
- require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updResp))
+ require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updResp, jsonx.V1()))
assert.Equal(t, "updated", updResp["intermediateTable"].(map[string]any)["description"])
})
}
@@ -268,7 +270,7 @@ func TestHTTP_UpdateIntermediateTableAnalysisRule(t *testing.T) {
"/memberships/"+mID+"/intermediateTables/"+tableID+"/analysisRule/CUSTOM", nil)
require.Equal(t, http.StatusOK, getRec.Code, getRec.Body.String())
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
rule := getResp["analysisRule"].(map[string]any)
policy := rule["analysisRulePolicy"].(map[string]any)
v1 := policy["v1"].(map[string]any)
@@ -312,7 +314,7 @@ func TestHTTP_PopulateIntermediateTable_AdvancesToSuccess(t *testing.T) {
"/memberships/"+mID+"/intermediateTables/"+tableID+"/populate", nil)
require.Equal(t, http.StatusOK, popRec.Code, popRec.Body.String())
var popResp map[string]any
- require.NoError(t, json.Unmarshal(popRec.Body.Bytes(), &popResp))
+ require.NoError(t, json.Unmarshal(popRec.Body.Bytes(), &popResp, jsonx.V1()))
assert.Equal(t, tt.wants.analysisType, popResp["analysisType"])
assert.NotEmpty(t, popResp["analysisId"])
assert.NotEmpty(t, popResp["versionId"])
@@ -321,7 +323,7 @@ func TestHTTP_PopulateIntermediateTable_AdvancesToSuccess(t *testing.T) {
"/memberships/"+mID+"/intermediateTables/"+tableID, nil)
require.Equal(t, http.StatusOK, getRec.Code, getRec.Body.String())
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
it := getResp["intermediateTable"].(map[string]any)
assert.Equal(t, "POPULATE_SUCCESS", it["status"])
assert.Contains(t, it, "intermediateTableVersion")
@@ -332,7 +334,7 @@ func TestHTTP_PopulateIntermediateTable_AdvancesToSuccess(t *testing.T) {
"/memberships/"+mID+"/intermediateTables/"+tableID+"/versions", nil)
require.Equal(t, http.StatusOK, verRec.Code, verRec.Body.String())
var verResp map[string]any
- require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &verResp))
+ require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &verResp, jsonx.V1()))
versions, ok := verResp["intermediateTableVersionSummaries"].([]any)
require.True(t, ok)
require.Len(t, versions, 1)
@@ -429,7 +431,7 @@ func TestIntermediateTables_WireShape(t *testing.T) {
getRec := doRequest(t, e, http.MethodGet, "/memberships/"+mID+"/intermediateTables/"+tableID, nil)
require.Equal(t, http.StatusOK, getRec.Code, getRec.Body.String())
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
it := getResp["intermediateTable"].(map[string]any)
assert.Contains(t, it, "id")
assert.NotContains(t, it, "intermediateTableIdentifier")
@@ -446,7 +448,7 @@ func TestIntermediateTables_WireShape(t *testing.T) {
})
require.Equal(t, http.StatusOK, ruleRec.Code, ruleRec.Body.String())
var ruleResp map[string]any
- require.NoError(t, json.Unmarshal(ruleRec.Body.Bytes(), &ruleResp))
+ require.NoError(t, json.Unmarshal(ruleRec.Body.Bytes(), &ruleResp, jsonx.V1()))
rule := ruleResp["analysisRule"].(map[string]any)
assert.Contains(t, rule, "intermediateTableIdentifier")
assert.Equal(t, tableID, rule["intermediateTableIdentifier"])
diff --git a/services/cleanrooms/id_namespace_associations_test.go b/services/cleanrooms/id_namespace_associations_test.go
index 69e1698f56..b06227e2e3 100644
--- a/services/cleanrooms/id_namespace_associations_test.go
+++ b/services/cleanrooms/id_namespace_associations_test.go
@@ -1,13 +1,15 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func createIDNamespaceAssociation(t *testing.T, e *echo.Echo, mID string) string {
@@ -21,7 +23,7 @@ func createIDNamespaceAssociation(t *testing.T, e *echo.Echo, mID string) string
})
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
return createResp["idNamespaceAssociation"]["id"].(string)
}
diff --git a/services/cleanrooms/list_configured_table_associations_rule_types_test.go b/services/cleanrooms/list_configured_table_associations_rule_types_test.go
index 497cab0016..007278aff2 100644
--- a/services/cleanrooms/list_configured_table_associations_rule_types_test.go
+++ b/services/cleanrooms/list_configured_table_associations_rule_types_test.go
@@ -1,11 +1,13 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestListConfiguredTableAssociations_SummaryHasAnalysisRuleTypes proves
@@ -30,14 +32,14 @@ func TestListConfiguredTableAssociations_SummaryHasAnalysisRuleTypes(t *testing.
"members": []any{}, "queryLogStatus": "DISABLED",
})
var colResp map[string]any
- require.NoError(t, json.Unmarshal(colRec.Body.Bytes(), &colResp))
+ require.NoError(t, json.Unmarshal(colRec.Body.Bytes(), &colResp, jsonx.V1()))
collabID := colResp["collaboration"].(map[string]any)["id"].(string)
memRec := doRequest(t, e, http.MethodPost, "/memberships", map[string]any{
"collaborationIdentifier": collabID, "queryLogStatus": "DISABLED",
})
var memResp map[string]any
- require.NoError(t, json.Unmarshal(memRec.Body.Bytes(), &memResp))
+ require.NoError(t, json.Unmarshal(memRec.Body.Bytes(), &memResp, jsonx.V1()))
memID := memResp["membership"].(map[string]any)["id"].(string)
ctRec := doRequest(t, e, http.MethodPost, "/configuredTables", map[string]any{
@@ -46,7 +48,7 @@ func TestListConfiguredTableAssociations_SummaryHasAnalysisRuleTypes(t *testing.
"allowedColumns": []string{"id"}, "analysisMethod": "DIRECT_QUERY",
})
var ctResp map[string]any
- require.NoError(t, json.Unmarshal(ctRec.Body.Bytes(), &ctResp))
+ require.NoError(t, json.Unmarshal(ctRec.Body.Bytes(), &ctResp, jsonx.V1()))
ctID := ctResp["configuredTable"].(map[string]any)["id"].(string)
ctaRec := doRequest(t, e, http.MethodPost, "/memberships/"+memID+"/configuredTableAssociations", map[string]any{
@@ -56,7 +58,7 @@ func TestListConfiguredTableAssociations_SummaryHasAnalysisRuleTypes(t *testing.
})
require.Equal(t, http.StatusOK, ctaRec.Code)
var ctaResp map[string]any
- require.NoError(t, json.Unmarshal(ctaRec.Body.Bytes(), &ctaResp))
+ require.NoError(t, json.Unmarshal(ctaRec.Body.Bytes(), &ctaResp, jsonx.V1()))
ctaID := ctaResp["configuredTableAssociation"].(map[string]any)["id"].(string)
ruleRec := doRequest(
@@ -74,7 +76,7 @@ func TestListConfiguredTableAssociations_SummaryHasAnalysisRuleTypes(t *testing.
var listResp struct {
Summaries []map[string]any `json:"configuredTableAssociationSummaries"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Summaries, 1)
ruleTypes, ok := listResp.Summaries[0]["analysisRuleTypes"].([]any)
diff --git a/services/cleanrooms/memberships_test.go b/services/cleanrooms/memberships_test.go
index 57048cce96..6ffc75e8e9 100644
--- a/services/cleanrooms/memberships_test.go
+++ b/services/cleanrooms/memberships_test.go
@@ -1,13 +1,15 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func createMembership(t *testing.T, e *echo.Echo, colID string, queryLog string) (map[string]any, string) {
@@ -19,7 +21,7 @@ func createMembership(t *testing.T, e *echo.Echo, colID string, queryLog string)
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
mem := resp["membership"].(map[string]any)
@@ -35,7 +37,7 @@ func createBaseCollaboration(t *testing.T, e *echo.Echo, name string) string {
})
var colResp map[string]any
rec := doRequest(t, e, "GET", "/collaborations", nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &colResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &colResp, jsonx.V1()))
return colResp["collaborationList"].([]any)[0].(map[string]any)["id"].(string)
}
@@ -135,7 +137,7 @@ func TestMemberships_List(t *testing.T) {
require.Equal(t, tt.wants.status, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
summaries := listResp["membershipSummaries"].([]any)
require.Len(t, summaries, 2)
summary := summaries[0].(map[string]any)
diff --git a/services/cleanrooms/overwide_collaboration_list_test.go b/services/cleanrooms/overwide_collaboration_list_test.go
index 9787ac3cac..0be703eeff 100644
--- a/services/cleanrooms/overwide_collaboration_list_test.go
+++ b/services/cleanrooms/overwide_collaboration_list_test.go
@@ -1,13 +1,15 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestOverWideCollaborationListResponses asserts on the raw response body
@@ -270,7 +272,7 @@ func TestOverWideCollaborationListResponses(t *testing.T) {
body := tt.raw(t)
var decoded map[string]any
- require.NoError(t, json.Unmarshal(body, &decoded))
+ require.NoError(t, json.Unmarshal(body, &decoded, jsonx.V1()))
rawItems, ok := decoded[tt.itemsKey]
require.True(t, ok, "missing items key %q in response: %s", tt.itemsKey, body)
@@ -309,7 +311,7 @@ func createCleanroomsCollabAndMembership(t *testing.T, e *echo.Echo) (string, st
})
require.Equal(t, http.StatusOK, colRec.Code)
var colResp map[string]any
- require.NoError(t, json.Unmarshal(colRec.Body.Bytes(), &colResp))
+ require.NoError(t, json.Unmarshal(colRec.Body.Bytes(), &colResp, jsonx.V1()))
collabID, _ := colResp["collaboration"].(map[string]any)["id"].(string)
require.NotEmpty(t, collabID)
@@ -318,7 +320,7 @@ func createCleanroomsCollabAndMembership(t *testing.T, e *echo.Echo) (string, st
})
require.Equal(t, http.StatusOK, memRec.Code)
var memResp map[string]any
- require.NoError(t, json.Unmarshal(memRec.Body.Bytes(), &memResp))
+ require.NoError(t, json.Unmarshal(memRec.Body.Bytes(), &memResp, jsonx.V1()))
memID, _ := memResp["membership"].(map[string]any)["id"].(string)
require.NotEmpty(t, memID)
diff --git a/services/cleanrooms/persistence.go b/services/cleanrooms/persistence.go
index 9fd8936602..af0c8e1d7a 100644
--- a/services/cleanrooms/persistence.go
+++ b/services/cleanrooms/persistence.go
@@ -2,7 +2,7 @@ package cleanrooms
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -39,7 +39,7 @@ const cleanroomsSnapshotVersion = 1
// key on (see backend.go's InMemoryBackend field doc comment). It is
// persisted directly here.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
TagsByArn map[string]map[string]string `json:"tagsByArn"`
AccountID string `json:"accountID"`
Region string `json:"region"`
diff --git a/services/cleanrooms/privacy_budgets_test.go b/services/cleanrooms/privacy_budgets_test.go
index 0549b4ce72..ddfd096c7b 100644
--- a/services/cleanrooms/privacy_budgets_test.go
+++ b/services/cleanrooms/privacy_budgets_test.go
@@ -1,13 +1,15 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// setupTestEnvironment is a helper to bootstrap a collaboration and membership
@@ -22,7 +24,7 @@ func setupTestEnvironment(t *testing.T, e *echo.Echo) (string, string) {
})
rec := doRequest(t, e, http.MethodGet, "/collaborations", nil)
var colResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &colResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &colResp, jsonx.V1()))
colID := colResp["collaborationList"].([]any)[0].(map[string]any)["id"].(string)
// Create Membership
@@ -30,7 +32,7 @@ func setupTestEnvironment(t *testing.T, e *echo.Echo) (string, string) {
"collaborationIdentifier": colID, "queryLogStatus": "DISABLED",
})
var memResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &memResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &memResp, jsonx.V1()))
mID := memResp["membership"].(map[string]any)["id"].(string)
return colID, mID
@@ -51,7 +53,7 @@ func createBudgetTemplate(t *testing.T, e *echo.Echo, mID string) string {
})
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
return createResp["privacyBudgetTemplate"]["id"].(string)
}
@@ -319,7 +321,7 @@ func TestPreviewPrivacyImpact(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
impact := resp["privacyImpact"].(map[string]any)["differentialPrivacy"].(map[string]any)
aggs := impact["aggregations"].([]any)
require.Len(t, aggs, 5)
@@ -342,7 +344,7 @@ func TestListPrivacyBudgets(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
budgets := resp["privacyBudgetSummaries"].([]any)
require.Len(t, budgets, 1)
diff --git a/services/cleanrooms/protected_jobs_test.go b/services/cleanrooms/protected_jobs_test.go
index 1f05a59dd8..9e9c6b91b1 100644
--- a/services/cleanrooms/protected_jobs_test.go
+++ b/services/cleanrooms/protected_jobs_test.go
@@ -1,13 +1,15 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func createProtectedJob(t *testing.T, e *echo.Echo, mID string) string {
@@ -21,7 +23,7 @@ func createProtectedJob(t *testing.T, e *echo.Echo, mID string) string {
})
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
return startResp["protectedJob"]["id"].(string)
}
diff --git a/services/cleanrooms/protected_queries_test.go b/services/cleanrooms/protected_queries_test.go
index 6b33ec6d15..102bc037b7 100644
--- a/services/cleanrooms/protected_queries_test.go
+++ b/services/cleanrooms/protected_queries_test.go
@@ -1,13 +1,15 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func createProtectedQuery(t *testing.T, e *echo.Echo, mID string) string {
@@ -21,7 +23,7 @@ func createProtectedQuery(t *testing.T, e *echo.Echo, mID string) string {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
return startResp["protectedQuery"]["id"].(string)
}
@@ -63,7 +65,7 @@ func TestProtectedQueries_Create(t *testing.T) {
require.Equal(t, tt.wants.status, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pq := resp["protectedQuery"].(map[string]any)
assert.Equal(t, "SUBMITTED", pq["status"],
diff --git a/services/cleanrooms/protected_query_lifecycle_test.go b/services/cleanrooms/protected_query_lifecycle_test.go
index c60b5ef325..c5b6ecb352 100644
--- a/services/cleanrooms/protected_query_lifecycle_test.go
+++ b/services/cleanrooms/protected_query_lifecycle_test.go
@@ -1,7 +1,7 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cleanrooms"
)
@@ -22,7 +23,7 @@ func queryStatus(t *testing.T, e *echo.Echo, memID, id string) string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["protectedQuery"]["status"].(string)
}
@@ -142,7 +143,7 @@ func TestStartProtectedQuery_KeepsSQLParameters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
got := resp["protectedQuery"]["sqlParameters"].(map[string]any)
assert.Equal(t, tmpl, got["analysisTemplateArn"])
assert.Equal(t, map[string]any{"k": "v"}, got["parameters"])
diff --git a/services/cleanrooms/undeclared_members_test.go b/services/cleanrooms/undeclared_members_test.go
index 47b0119745..b501244366 100644
--- a/services/cleanrooms/undeclared_members_test.go
+++ b/services/cleanrooms/undeclared_members_test.go
@@ -1,7 +1,7 @@
package cleanrooms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cleanrooms"
)
@@ -114,7 +115,7 @@ func decodeBody(t *testing.T, b []byte) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(b, &m))
+ require.NoError(t, json.Unmarshal(b, &m, jsonx.V1()))
return m
}
diff --git a/services/cloudcontrol/handler.go b/services/cloudcontrol/handler.go
index 5744ef34ed..36877139ad 100644
--- a/services/cloudcontrol/handler.go
+++ b/services/cloudcontrol/handler.go
@@ -2,7 +2,7 @@ package cloudcontrol
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -125,12 +126,12 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// marshalError serialises a typed AWS error response into bytes.
func marshalError(errType, message string) []byte {
- payload, _ := json.Marshal(service.JSONErrorResponse{Type: errType, Message: message})
+ payload, _ := json.Marshal(service.JSONErrorResponse{Type: errType, Message: message}, jsonx.V1())
return payload
}
@@ -142,9 +143,6 @@ func marshalError(errType, message string) []byte {
// exceptions (e.g. ConcurrentModificationException, ServiceInternalErrorException) use 500.
// See e.g. https://docs.aws.amazon.com/cloudcontrolapi/latest/APIReference/API_GetResource.html#API_GetResource_Errors
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, ErrClientTokenConflict):
return ccError(c, http.StatusBadRequest, "ClientTokenConflictException", err,
@@ -170,7 +168,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
case errors.Is(err, ErrValidation):
return ccError(c, http.StatusBadRequest, "InvalidRequestException", err,
"The request is invalid.")
- case errors.Is(err, errUnknownAction), errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ case errors.Is(err, errUnknownAction), jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
return ccError(c, http.StatusBadRequest, "InvalidRequestException", err, "The request is invalid.")
default:
return ccError(c, http.StatusInternalServerError, "ServiceInternalErrorException", err,
diff --git a/services/cloudcontrol/handler_test.go b/services/cloudcontrol/handler_test.go
index 1f929ac55b..609876b2b3 100644
--- a/services/cloudcontrol/handler_test.go
+++ b/services/cloudcontrol/handler_test.go
@@ -2,7 +2,7 @@ package cloudcontrol_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/cloudcontrol"
)
@@ -27,7 +28,7 @@ func doRequest(t *testing.T, h *cloudcontrol.Handler, action string, body any) *
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -55,7 +56,7 @@ func errType(t *testing.T, body []byte) string {
var env struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(body, &env))
+ require.NoError(t, json.Unmarshal(body, &env, jsonx.V1()))
return env.Type
}
diff --git a/services/cloudcontrol/models.go b/services/cloudcontrol/models.go
index 79734e1058..75d70111f7 100644
--- a/services/cloudcontrol/models.go
+++ b/services/cloudcontrol/models.go
@@ -1,8 +1,10 @@
package cloudcontrol
import (
- "encoding/json"
+ "encoding/json/v2"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -24,12 +26,12 @@ type unixEpochTime struct {
}
func (t unixEpochTime) MarshalJSON() ([]byte, error) {
- return json.Marshal(t.Unix())
+ return json.Marshal(t.Unix(), jsonx.V1())
}
func (t *unixEpochTime) UnmarshalJSON(b []byte) error {
var epoch int64
- if err := json.Unmarshal(b, &epoch); err != nil {
+ if err := json.Unmarshal(b, &epoch, jsonx.V1()); err != nil {
return err
}
diff --git a/services/cloudcontrol/models_test.go b/services/cloudcontrol/models_test.go
index d0f0f122c4..e9067fc76d 100644
--- a/services/cloudcontrol/models_test.go
+++ b/services/cloudcontrol/models_test.go
@@ -1,13 +1,14 @@
package cloudcontrol_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudcontrol"
)
@@ -30,7 +31,7 @@ func TestProgressEvent_OptionalFieldsOmittedWhenTerminalSuccess(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pe, ok := out["ProgressEvent"].(map[string]any)
require.True(t, ok, "ProgressEvent must be present")
@@ -57,7 +58,7 @@ func TestGetResourceRequestStatus_HooksProgressEventOmittedWhenEmpty(t *testing.
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
pe, ok := createOut["ProgressEvent"].(map[string]any)
require.True(t, ok)
@@ -71,7 +72,7 @@ func TestGetResourceRequestStatus_HooksProgressEventOmittedWhenEmpty(t *testing.
require.Equal(t, http.StatusOK, statusRec.Code)
var statusOut map[string]any
- require.NoError(t, json.Unmarshal(statusRec.Body.Bytes(), &statusOut))
+ require.NoError(t, json.Unmarshal(statusRec.Body.Bytes(), &statusOut, jsonx.V1()))
assert.NotContains(t, statusOut, "HooksProgressEvent",
"HooksProgressEvent must be omitted since this backend has no Hooks concept")
diff --git a/services/cloudcontrol/persistence.go b/services/cloudcontrol/persistence.go
index 43d226b661..96c48c137b 100644
--- a/services/cloudcontrol/persistence.go
+++ b/services/cloudcontrol/persistence.go
@@ -2,7 +2,7 @@ package cloudcontrol
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -42,7 +42,7 @@ const cloudcontrolSnapshotVersion = 2
// persisted directly here so a repeated ClientToken remains idempotent (and
// conflict-detectable) across a restart.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ClientTokens map[string]clientTokenEntry `json:"clientTokens"`
AccountID string `json:"accountID"`
Region string `json:"region"`
diff --git a/services/cloudcontrol/provisioned.go b/services/cloudcontrol/provisioned.go
index d087325c99..ee08aaa692 100644
--- a/services/cloudcontrol/provisioned.go
+++ b/services/cloudcontrol/provisioned.go
@@ -2,10 +2,12 @@ package cloudcontrol
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Provisioner creates, changes and destroys the real resource behind a Cloud
@@ -96,7 +98,7 @@ func (h *provisionedHandler) stored(identifier string) (map[string]any, error) {
}
var model map[string]any
- if err := json.Unmarshal([]byte(r.Properties), &model); err != nil {
+ if err := json.Unmarshal([]byte(r.Properties), &model, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: stored model is not a JSON object", ErrValidation)
}
diff --git a/services/cloudcontrol/resource_requests_test.go b/services/cloudcontrol/resource_requests_test.go
index 2c19918bfa..a78420a32c 100644
--- a/services/cloudcontrol/resource_requests_test.go
+++ b/services/cloudcontrol/resource_requests_test.go
@@ -1,7 +1,7 @@
package cloudcontrol_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudcontrol"
)
@@ -61,7 +62,7 @@ func TestHandler_GetResourceRequestStatus(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pe, ok := out["ProgressEvent"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantOp, pe["Operation"])
@@ -189,7 +190,7 @@ func TestHandler_CancelResourceRequest(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pe, ok := out["ProgressEvent"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantOpStatus, pe["OperationStatus"])
@@ -263,7 +264,7 @@ func TestHandler_CancelResourceRequest_OnlyAllowsInProgress(t *testing.T) {
if tt.wantHTTP == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pe, ok := out["ProgressEvent"].(map[string]any)
require.True(t, ok, "ProgressEvent must be present")
assert.Equal(t, "CANCEL_COMPLETE", pe["OperationStatus"])
@@ -350,7 +351,7 @@ func TestHandler_EventTimeIsUnixNumber(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
pe, ok := createOut["ProgressEvent"].(map[string]any)
require.True(t, ok)
@@ -375,7 +376,7 @@ func TestHandler_EventTimeIsUnixNumber(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pe, ok := out["ProgressEvent"].(map[string]any)
require.True(t, ok, "ProgressEvent should be present in response")
@@ -488,7 +489,7 @@ func TestHandler_ListResourceRequests(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries, ok := out["ResourceRequestStatusSummaries"].([]any)
require.True(t, ok, "ResourceRequestStatusSummaries should be an array")
assert.Len(t, summaries, tt.wantCount)
@@ -565,7 +566,7 @@ func TestHandler_ListResourceRequests_FilterByOperationStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries, _ := out["ResourceRequestStatusSummaries"].([]any)
assert.Len(t, summaries, tt.wantLen, "filter %v", tt.filter)
})
@@ -581,7 +582,7 @@ func TestHandler_ListResourceRequests_ContainsExpectedFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries, ok := out["ResourceRequestStatusSummaries"].([]any)
require.True(t, ok)
@@ -655,7 +656,7 @@ func TestHandler_ListResourceRequests_EnumValidation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries, ok := out["ResourceRequestStatusSummaries"].([]any)
if tt.wantMatches {
@@ -693,7 +694,7 @@ func TestHandler_ListResourceRequests_TypeNameFilterIsIgnored(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries, ok := out["ResourceRequestStatusSummaries"].([]any)
require.True(t, ok)
@@ -716,7 +717,7 @@ func TestHandler_ListResourceRequests_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries, ok := out["ResourceRequestStatusSummaries"].([]any)
require.True(t, ok)
@@ -734,7 +735,7 @@ func TestHandler_ListResourceRequests_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
summaries2, ok := out2["ResourceRequestStatusSummaries"].([]any)
require.True(t, ok)
diff --git a/services/cloudcontrol/resources.go b/services/cloudcontrol/resources.go
index ead679b243..dc380f1b0e 100644
--- a/services/cloudcontrol/resources.go
+++ b/services/cloudcontrol/resources.go
@@ -1,7 +1,7 @@
package cloudcontrol
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"sort"
@@ -11,6 +11,7 @@ import (
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -122,7 +123,7 @@ func (b *InMemoryBackend) ListResources(
var modelFilter map[string]any
if resourceModel != "" {
- if err := json.Unmarshal([]byte(resourceModel), &modelFilter); err != nil {
+ if err := json.Unmarshal([]byte(resourceModel), &modelFilter, jsonx.V1()); err != nil {
return []*Resource{}, ""
}
}
@@ -433,7 +434,7 @@ func extractIdentifier(desiredState string) string {
}
var props map[string]any
- if err := json.Unmarshal([]byte(desiredState), &props); err != nil {
+ if err := json.Unmarshal([]byte(desiredState), &props, jsonx.V1()); err != nil {
return ""
}
@@ -462,7 +463,7 @@ func matchesResourceModel(properties string, modelFilter map[string]any) bool {
}
var props map[string]any
- if err := json.Unmarshal([]byte(properties), &props); err != nil {
+ if err := json.Unmarshal([]byte(properties), &props, jsonx.V1()); err != nil {
return false
}
@@ -472,8 +473,8 @@ func matchesResourceModel(properties string, modelFilter map[string]any) bool {
return false
}
- wantJSON, errW := json.Marshal(want)
- gotJSON, errG := json.Marshal(got)
+ wantJSON, errW := json.Marshal(want, jsonx.V1())
+ gotJSON, errG := json.Marshal(got, jsonx.V1())
if errW != nil || errG != nil || string(wantJSON) != string(gotJSON) {
return false
@@ -508,13 +509,13 @@ type patchOperation struct {
// unchanged with no error (also pre-existing).
func applyPatch(document, patchDocument string) (string, error) {
var doc map[string]any
- if err := json.Unmarshal([]byte(document), &doc); err != nil {
+ if err := json.Unmarshal([]byte(document), &doc, jsonx.V1()); err != nil {
//nolint:nilerr // malformed document: return it unchanged, not an error (pre-existing behavior)
return document, nil
}
var ops []patchOperation
- if err := json.Unmarshal([]byte(patchDocument), &ops); err != nil {
+ if err := json.Unmarshal([]byte(patchDocument), &ops, jsonx.V1()); err != nil {
//nolint:nilerr // malformed patch JSON: return document unchanged, not an error (pre-existing behavior)
return document, nil
}
@@ -528,7 +529,7 @@ func applyPatch(document, patchDocument string) (string, error) {
}
}
- out, err := json.Marshal(doc)
+ out, err := json.Marshal(doc, jsonx.V1())
if err != nil {
//nolint:nilerr // doc built entirely from valid JSON decodes, so this can't actually fail; fail closed anyway
return document, nil
@@ -705,8 +706,8 @@ func deepCopyJSON(v any) any {
// comparing equal -- both decode to the same float64 and marshal back
// identically) -- same convention as matchesResourceModel above.
func jsonValuesEqual(a, b any) bool {
- aJSON, errA := json.Marshal(a)
- bJSON, errB := json.Marshal(b)
+ aJSON, errA := json.Marshal(a, jsonx.V1())
+ bJSON, errB := json.Marshal(b, jsonx.V1())
return errA == nil && errB == nil && string(aJSON) == string(bJSON)
}
diff --git a/services/cloudcontrol/resources_test.go b/services/cloudcontrol/resources_test.go
index 843c12a826..662e2653be 100644
--- a/services/cloudcontrol/resources_test.go
+++ b/services/cloudcontrol/resources_test.go
@@ -1,7 +1,7 @@
package cloudcontrol_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudcontrol"
)
@@ -59,7 +60,7 @@ func TestHandler_CreateResource(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pe, ok := out["ProgressEvent"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantOp, pe["Operation"])
@@ -119,7 +120,7 @@ func TestHandler_CreateResource_ClientToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
pe1 := out1["ProgressEvent"].(map[string]any)
// Same clientToken must return the same RequestToken (idempotent).
@@ -127,7 +128,7 @@ func TestHandler_CreateResource_ClientToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
pe2 := out2["ProgressEvent"].(map[string]any)
assert.Equal(t, pe1["RequestToken"], pe2["RequestToken"])
@@ -210,14 +211,14 @@ func TestHandler_ProgressEvent_ResourceModel(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
createPE := createOut["ProgressEvent"].(map[string]any)
createModel, ok := createPE["ResourceModel"].(string)
require.True(t, ok, "ResourceModel must be present as a string on CreateResource")
var createParsed map[string]any
- require.NoError(t, json.Unmarshal([]byte(createModel), &createParsed))
+ require.NoError(t, json.Unmarshal([]byte(createModel), &createParsed, jsonx.V1()))
assert.Equal(t, "model-test", createParsed["LogGroupName"])
updateRec := doRequest(t, h, "UpdateResource", map[string]any{
@@ -228,14 +229,14 @@ func TestHandler_ProgressEvent_ResourceModel(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateOut map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut, jsonx.V1()))
updatePE := updateOut["ProgressEvent"].(map[string]any)
updateModel, ok := updatePE["ResourceModel"].(string)
require.True(t, ok, "ResourceModel must be present as a string on UpdateResource")
var updateParsed map[string]any
- require.NoError(t, json.Unmarshal([]byte(updateModel), &updateParsed))
+ require.NoError(t, json.Unmarshal([]byte(updateModel), &updateParsed, jsonx.V1()))
assert.InEpsilon(t, float64(30), updateParsed["RetentionInDays"], 0, "ResourceModel must reflect the patched value")
}
func TestHandler_GetResource(t *testing.T) {
@@ -301,7 +302,7 @@ func TestHandler_GetResource(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
rd, ok := out["ResourceDescription"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantProps, rd["Properties"])
@@ -325,7 +326,7 @@ func TestHandler_GetResource_PropertiesIsJSONString(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
pe := createOut["ProgressEvent"].(map[string]any)
identifier := pe["Identifier"].(string)
@@ -336,7 +337,7 @@ func TestHandler_GetResource_PropertiesIsJSONString(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut, jsonx.V1()))
desc, ok := getOut["ResourceDescription"].(map[string]any)
require.True(t, ok, "ResourceDescription must be present")
@@ -348,7 +349,7 @@ func TestHandler_GetResource_PropertiesIsJSONString(t *testing.T) {
// The string must be valid JSON.
var parsed map[string]any
- require.NoError(t, json.Unmarshal([]byte(propertiesRaw.(string)), &parsed))
+ require.NoError(t, json.Unmarshal([]byte(propertiesRaw.(string)), &parsed, jsonx.V1()))
assert.Equal(t, "my-bucket", parsed["BucketName"])
}
@@ -479,7 +480,7 @@ func TestHandler_ListResources(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
descs, ok := out["ResourceDescriptions"].([]any)
require.True(t, ok)
assert.Len(t, descs, tt.wantCount)
@@ -506,7 +507,7 @@ func TestHandler_ListResources_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out1, jsonx.V1()))
descs1, ok := out1["ResourceDescriptions"].([]any)
require.True(t, ok)
@@ -526,7 +527,7 @@ func TestHandler_ListResources_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
descs2, ok := out2["ResourceDescriptions"].([]any)
require.True(t, ok)
@@ -598,7 +599,7 @@ func TestHandler_DeleteResource(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pe, ok := out["ProgressEvent"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantOp, pe["Operation"])
@@ -678,7 +679,7 @@ func TestHandler_UpdateResource(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pe, ok := out["ProgressEvent"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantOp, pe["Operation"])
@@ -713,14 +714,14 @@ func TestHandler_UpdateResource_ClientToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
pe1 := out1["ProgressEvent"].(map[string]any)
rec2 := doRequest(t, h, "UpdateResource", body)
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
pe2 := out2["ProgressEvent"].(map[string]any)
assert.Equal(t, pe1["RequestToken"], pe2["RequestToken"],
@@ -1109,7 +1110,7 @@ func TestHandler_DeleteResource_ClientToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
pe1 := out1["ProgressEvent"].(map[string]any)
// Without idempotency this second call would 400 with ResourceNotFoundException,
@@ -1118,7 +1119,7 @@ func TestHandler_DeleteResource_ClientToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
pe2 := out2["ProgressEvent"].(map[string]any)
assert.Equal(t, pe1["RequestToken"], pe2["RequestToken"],
@@ -1461,7 +1462,7 @@ func TestHandler_ProgressEvent_RequiredFields(t *testing.T) {
})
require.Equal(t, http.StatusOK, createRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out, jsonx.V1()))
pe := out["ProgressEvent"].(map[string]any)
return map[string]any{
@@ -1479,7 +1480,7 @@ func TestHandler_ProgressEvent_RequiredFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pe, ok := out["ProgressEvent"].(map[string]any)
require.True(t, ok, "ProgressEvent must be present")
diff --git a/services/cloudcontrol/type_handlers.go b/services/cloudcontrol/type_handlers.go
index 3b1fcf67e4..e8259c3748 100644
--- a/services/cloudcontrol/type_handlers.go
+++ b/services/cloudcontrol/type_handlers.go
@@ -2,13 +2,14 @@ package cloudcontrol
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"sort"
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -38,7 +39,7 @@ func (b *InMemoryBackend) typeHandler(typeName string) TypeHandler {
}
func marshalModel(model map[string]any) (string, error) {
- raw, err := json.Marshal(model)
+ raw, err := json.Marshal(model, jsonx.V1())
if err != nil {
return "", fmt.Errorf("%w: %w", ErrValidation, err)
}
@@ -90,7 +91,7 @@ func (b *InMemoryBackend) CreateResourceContext(
}
var desired map[string]any
- if err := json.Unmarshal([]byte(desiredState), &desired); err != nil {
+ if err := json.Unmarshal([]byte(desiredState), &desired, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: DesiredState must be a JSON object", ErrValidation)
}
@@ -202,7 +203,7 @@ func (b *InMemoryBackend) UpdateResourceContext(
}
var desired map[string]any
- if jsonErr := json.Unmarshal([]byte(patched), &desired); jsonErr != nil {
+ if jsonErr := json.Unmarshal([]byte(patched), &desired, jsonx.V1()); jsonErr != nil {
return nil, fmt.Errorf("%w: patched document is not a JSON object", ErrValidation)
}
@@ -278,7 +279,7 @@ func parseModelFilter(resourceModel string) (map[string]any, bool) {
var filter map[string]any
- return filter, json.Unmarshal([]byte(resourceModel), &filter) == nil
+ return filter, json.Unmarshal([]byte(resourceModel), &filter, jsonx.V1()) == nil
}
var errInvalidNextToken = fmt.Errorf("%w: NextToken is not valid", ErrValidation)
diff --git a/services/cloudformation/cfn_attributes.go b/services/cloudformation/cfn_attributes.go
index 9cb2da75de..ecf168b665 100644
--- a/services/cloudformation/cfn_attributes.go
+++ b/services/cloudformation/cfn_attributes.go
@@ -2,8 +2,10 @@ package cloudformation
import (
_ "embed"
- "encoding/json"
+ "encoding/json/v2"
"sync"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// cfn_attributes.json is generated by cmd/cfnattrgen from the CloudFormation
@@ -23,7 +25,7 @@ var (
func resourceAttributeTable() map[string]map[string]struct{} {
cfnResourceAttributesOnce.Do(func() {
var raw map[string][]string
- if err := json.Unmarshal(cfnAttributesJSON, &raw); err != nil {
+ if err := json.Unmarshal(cfnAttributesJSON, &raw, jsonx.V1()); err != nil {
panic("cloudformation: invalid embedded cfn_attributes.json: " + err.Error())
}
diff --git a/services/cloudformation/cfn_property_schemas.go b/services/cloudformation/cfn_property_schemas.go
index 3f3c1e99e8..60398ec59e 100644
--- a/services/cloudformation/cfn_property_schemas.go
+++ b/services/cloudformation/cfn_property_schemas.go
@@ -2,12 +2,14 @@ package cloudformation
import (
_ "embed"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"math"
"strconv"
"strings"
"sync"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Property kinds in cfn_property_schemas.json, written by cmd/cfnpropgen.
@@ -37,7 +39,7 @@ var (
func embeddedPropertySchemas() propertySchemas {
cfnPropertySchemasOnce.Do(func() {
- if err := json.Unmarshal(cfnPropertySchemasJSON, &cfnPropertySchemas); err != nil {
+ if err := json.Unmarshal(cfnPropertySchemasJSON, &cfnPropertySchemas, jsonx.V1()); err != nil {
panic("cloudformation: invalid embedded cfn_property_schemas.json: " + err.Error())
}
})
diff --git a/services/cloudformation/changeset_diff.go b/services/cloudformation/changeset_diff.go
index 1ae698fc62..f7bb9d742d 100644
--- a/services/cloudformation/changeset_diff.go
+++ b/services/cloudformation/changeset_diff.go
@@ -1,12 +1,13 @@
package cloudformation
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"sort"
"github.com/blackbirdworks/gopherstack/pkgs/collections"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Replacement / recreation constants used by change-set previews.
@@ -431,8 +432,8 @@ func unionMapKeys(a, b map[string]any) []string {
}
func jsonEqual(a, b any) bool {
- ab, err1 := json.Marshal(a)
- bb, err2 := json.Marshal(b)
+ ab, err1 := json.Marshal(a, jsonx.V1())
+ bb, err2 := json.Marshal(b, jsonx.V1())
if err1 != nil || err2 != nil {
return fmt.Sprintf("%v", a) == fmt.Sprintf("%v", b)
}
@@ -447,7 +448,7 @@ func toJSONString(v any) string {
if s, ok := v.(string); ok {
return s
}
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Sprintf("%v", v)
}
diff --git a/services/cloudformation/cloudcontrol_provisioner.go b/services/cloudformation/cloudcontrol_provisioner.go
index bbac51d002..f487216c18 100644
--- a/services/cloudformation/cloudcontrol_provisioner.go
+++ b/services/cloudformation/cloudcontrol_provisioner.go
@@ -3,7 +3,7 @@ package cloudformation
import (
"context"
_ "embed"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"reflect"
@@ -13,6 +13,7 @@ import (
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudcontrol"
)
@@ -29,7 +30,7 @@ var (
// against real backends; Cloud Control serves the same set.
func ProvisionableTypes() []string {
cfnProvisionableTypesOnce.Do(func() {
- if err := json.Unmarshal(cfnProvisionableTypesJSON, &cfnProvisionableTypes); err != nil {
+ if err := json.Unmarshal(cfnProvisionableTypesJSON, &cfnProvisionableTypes, jsonx.V1()); err != nil {
panic("cloudformation: invalid embedded cfn_provisionable_types.json: " + err.Error())
}
})
diff --git a/services/cloudformation/deletion_policy_test.go b/services/cloudformation/deletion_policy_test.go
index 82180ce0eb..2d09b77ca8 100644
--- a/services/cloudformation/deletion_policy_test.go
+++ b/services/cloudformation/deletion_policy_test.go
@@ -2,13 +2,14 @@ package cloudformation_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudformation"
)
@@ -53,7 +54,7 @@ func templateWithDeletionPolicy(policy string) string {
body, _ := json.Marshal(map[string]any{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": map[string]any{"Bucket": res},
- })
+ }, jsonx.V1())
return string(body)
}
diff --git a/services/cloudformation/drift_detection.go b/services/cloudformation/drift_detection.go
index 65b482fff1..17ba7ce3db 100644
--- a/services/cloudformation/drift_detection.go
+++ b/services/cloudformation/drift_detection.go
@@ -1,12 +1,14 @@
package cloudformation
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// pruneDriftDetections removes all drift detection entries associated with a stack
@@ -218,12 +220,12 @@ func deepCopyProps(props map[string]any) map[string]any {
if props == nil {
return nil
}
- data, err := json.Marshal(props)
+ data, err := json.Marshal(props, jsonx.V1())
if err != nil {
return props
}
var out map[string]any
- if uerr := json.Unmarshal(data, &out); uerr != nil {
+ if uerr := json.Unmarshal(data, &out, jsonx.V1()); uerr != nil {
return props
}
diff --git a/services/cloudformation/drift_detection_test.go b/services/cloudformation/drift_detection_test.go
index 836f35bf0e..87918a1de6 100644
--- a/services/cloudformation/drift_detection_test.go
+++ b/services/cloudformation/drift_detection_test.go
@@ -2,12 +2,13 @@ package cloudformation_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudformation"
)
@@ -306,7 +307,7 @@ func TestDeleteStack_ClearsDriftMaps(t *testing.T) {
ResourceDriftStatus map[string]map[string]string `json:"resourceDriftStatus"`
ResourceDriftDetail map[string]map[string]any `json:"resourceDriftDetail"`
}
- require.NoError(t, json.Unmarshal(b.Snapshot(context.Background()), &probe))
+ require.NoError(t, json.Unmarshal(b.Snapshot(context.Background()), &probe, jsonx.V1()))
assert.NotContains(t, probe.ResourceDriftStatus, stack.StackID)
assert.NotContains(t, probe.ResourceDriftDetail, stack.StackID)
assert.Contains(t, probe.ResourceDriftStatus, otherStack.StackID,
diff --git a/services/cloudformation/dynamic_refs.go b/services/cloudformation/dynamic_refs.go
index 29989c99d5..5fd024164c 100644
--- a/services/cloudformation/dynamic_refs.go
+++ b/services/cloudformation/dynamic_refs.go
@@ -2,11 +2,13 @@ package cloudformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"regexp"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// dynamicRefPattern matches CloudFormation dynamic reference strings:
@@ -186,7 +188,7 @@ func ResolveDynamicRefsInTemplate(ctx context.Context, tmpl *Template, resolver
// If parsing fails or the key is absent, an error is returned.
func resolveJSONKey(secretValue, key string) (string, error) {
var obj map[string]any
- if err := json.Unmarshal([]byte(secretValue), &obj); err != nil {
+ if err := json.Unmarshal([]byte(secretValue), &obj, jsonx.V1()); err != nil {
return "", fmt.Errorf("secret is not valid JSON: %w", err)
}
diff --git a/services/cloudformation/generated_templates.go b/services/cloudformation/generated_templates.go
index 09cb5b3f40..44c55ab745 100644
--- a/services/cloudformation/generated_templates.go
+++ b/services/cloudformation/generated_templates.go
@@ -1,7 +1,7 @@
package cloudformation
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"sort"
@@ -9,6 +9,7 @@ import (
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -79,7 +80,7 @@ func marshalGeneratedTemplate(resources map[string]cfnResource) string {
AWSTemplateFormatVersion: "2010-09-09",
Description: "Generated template",
Resources: resources,
- })
+ }, jsonx.V1())
if err != nil {
return `{"AWSTemplateFormatVersion":"2010-09-09","Resources":{}}`
}
diff --git a/services/cloudformation/handler_type_registry.go b/services/cloudformation/handler_type_registry.go
index d848973e63..d7aa1fd2bf 100644
--- a/services/cloudformation/handler_type_registry.go
+++ b/services/cloudformation/handler_type_registry.go
@@ -1,7 +1,7 @@
package cloudformation
import (
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"errors"
"fmt"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// typeSchemaFor returns the primary identifier property name for a given CloudFormation resource type.
@@ -112,11 +113,11 @@ func describeTypeSchemaJSON(typeName string) (string, error) {
// Round-trip through a generic map to normalize formatting and to fail
// loudly (via the error return) if a catalog entry is ever malformed.
var generic map[string]any
- if err := json.Unmarshal([]byte(raw), &generic); err != nil {
+ if err := json.Unmarshal([]byte(raw), &generic, jsonx.V1()); err != nil {
return "", err
}
- normalized, err := json.Marshal(generic)
+ normalized, err := json.Marshal(generic, jsonx.V1())
if err != nil {
return "", err
}
@@ -143,7 +144,7 @@ func describeTypeSchemaJSON(typeName string) (string, error) {
AdditionalProperties: true,
}
- schemaBytes, err := json.Marshal(schemaObj)
+ schemaBytes, err := json.Marshal(schemaObj, jsonx.V1())
if err != nil {
return "", err
}
diff --git a/services/cloudformation/persistence.go b/services/cloudformation/persistence.go
index 4294a104c3..584c527c42 100644
--- a/services/cloudformation/persistence.go
+++ b/services/cloudformation/persistence.go
@@ -2,7 +2,7 @@ package cloudformation
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -29,7 +29,7 @@ const cfnSnapshotVersion = 1
// comment on registerAllTables) and are persisted exactly as before the
// conversion.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Events map[string][]StackEvent `json:"events"`
Resources map[string]map[string]*StackResource `json:"resources"`
ChangeSets map[string]map[string]*ChangeSet `json:"changeSets"`
diff --git a/services/cloudformation/resource_type_schemas_test.go b/services/cloudformation/resource_type_schemas_test.go
index f235ffd9b4..a13d193811 100644
--- a/services/cloudformation/resource_type_schemas_test.go
+++ b/services/cloudformation/resource_type_schemas_test.go
@@ -1,13 +1,15 @@
package cloudformation_test
import (
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"net/url"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// describeTypeXMLResult mirrors the DescribeTypeResult XML shape returned by the
@@ -170,7 +172,7 @@ func TestHandler_DescribeType_CatalogSchemas(t *testing.T) {
require.NotEmpty(t, parsed.Schema)
var schema parsedProviderSchema
- require.NoError(t, json.Unmarshal([]byte(parsed.Schema), &schema))
+ require.NoError(t, json.Unmarshal([]byte(parsed.Schema), &schema, jsonx.V1()))
assert.Equal(t, tc.typeName, schema.TypeName)
assert.NotEmpty(t, schema.Description)
require.Contains(t, schema.PrimaryIdentifier, tc.wantPrimaryID)
@@ -214,7 +216,7 @@ func TestHandler_DescribeType_UnknownType_ValidFallback(t *testing.T) {
require.NotEmpty(t, parsed.Schema)
var schema parsedProviderSchema
- require.NoError(t, json.Unmarshal([]byte(parsed.Schema), &schema))
+ require.NoError(t, json.Unmarshal([]byte(parsed.Schema), &schema, jsonx.V1()))
assert.Equal(t, "AWS::Totally::Unmodeled", schema.TypeName)
require.NotEmpty(t, schema.PrimaryIdentifier)
assert.NotEmpty(t, schema.Properties)
diff --git a/services/cloudformation/resource_update_inplace.go b/services/cloudformation/resource_update_inplace.go
index 21c0bfd90a..5acb836766 100644
--- a/services/cloudformation/resource_update_inplace.go
+++ b/services/cloudformation/resource_update_inplace.go
@@ -4,12 +4,13 @@ import (
"context"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
sqsbackend "github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -114,7 +115,7 @@ func sqsAttributesFromProps(props map[string]any, params, physicalIDs map[string
}
if rp, ok := props["RedrivePolicy"]; ok {
- if b, err := json.Marshal(resolveDeep(rp, params, physicalIDs)); err == nil {
+ if b, err := json.Marshal(resolveDeep(rp, params, physicalIDs), jsonx.V1()); err == nil {
attrs["RedrivePolicy"] = string(b)
}
}
diff --git a/services/cloudformation/resources.go b/services/cloudformation/resources.go
index 32f57ebd4a..6e0df898e2 100644
--- a/services/cloudformation/resources.go
+++ b/services/cloudformation/resources.go
@@ -2,7 +2,6 @@ package cloudformation
import (
"context"
- "encoding/json"
"fmt"
"maps"
"math"
@@ -16,6 +15,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/ctxval"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
acmbackend "github.com/blackbirdworks/gopherstack/services/acm"
apigwbackend "github.com/blackbirdworks/gopherstack/services/apigateway"
apigatewayv2backend "github.com/blackbirdworks/gopherstack/services/apigatewayv2"
@@ -3510,7 +3510,7 @@ func int64Val(v any) int64 {
return int64(n)
case int64:
return n
- case json.Number:
+ case jsonx.Number:
i, err := n.Int64()
if err == nil {
return i
diff --git a/services/cloudformation/resources_apigatewayv2.go b/services/cloudformation/resources_apigatewayv2.go
index bb678e056c..9a7ba36910 100644
--- a/services/cloudformation/resources_apigatewayv2.go
+++ b/services/cloudformation/resources_apigatewayv2.go
@@ -2,10 +2,11 @@ package cloudformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
apigatewayv2backend "github.com/blackbirdworks/gopherstack/services/apigatewayv2"
)
@@ -99,11 +100,11 @@ func (rc *ResourceCreator) deleteAPIGatewayV2Authorizer(physicalID string) error
// decodeProps resolves intrinsics in props and decodes them into out; the
// backend inputs' JSON tags match CloudFormation's property names case-insensitively.
func decodeProps(props map[string]any, params, physicalIDs map[string]string, out any) error {
- raw, err := json.Marshal(resolveDeep(props, params, physicalIDs))
+ raw, err := json.Marshal(resolveDeep(props, params, physicalIDs), jsonx.V1())
if err != nil {
return fmt.Errorf("encode properties: %w", err)
}
- if err = json.Unmarshal(raw, out); err != nil {
+ if err = json.Unmarshal(raw, out, jsonx.V1()); err != nil {
return fmt.Errorf("decode properties: %w", err)
}
diff --git a/services/cloudformation/resources_cfnrest.go b/services/cloudformation/resources_cfnrest.go
index 1f9dbefb60..e95c045f71 100644
--- a/services/cloudformation/resources_cfnrest.go
+++ b/services/cloudformation/resources_cfnrest.go
@@ -1,12 +1,13 @@
package cloudformation
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
"slices"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/restroute"
)
@@ -28,12 +29,12 @@ func decodeCFNProps(dst any, props map[string]any, params, physicalIDs map[strin
resolved["Tags"] = tagListProp(props, params, physicalIDs)
}
- raw, err := json.Marshal(resolved)
+ raw, err := json.Marshal(resolved, jsonx.V1())
if err != nil {
return fmt.Errorf("encode properties: %w", err)
}
- if err = json.Unmarshal(raw, dst); err != nil {
+ if err = json.Unmarshal(raw, dst, jsonx.V1()); err != nil {
return fmt.Errorf("decode properties: %w", err)
}
diff --git a/services/cloudformation/resources_connect_content.go b/services/cloudformation/resources_connect_content.go
index 1ec915703f..a425ae49f2 100644
--- a/services/cloudformation/resources_connect_content.go
+++ b/services/cloudformation/resources_connect_content.go
@@ -1,12 +1,13 @@
package cloudformation
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strconv"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
connectbackend "github.com/blackbirdworks/gopherstack/services/connect"
)
@@ -21,7 +22,7 @@ func (c *connectCall) viewContent() (*connectbackend.ViewInputContent, error) {
return &connectbackend.ViewInputContent{Template: s, Actions: c.actionStrings()}, nil
}
- raw, err := json.Marshal(template)
+ raw, err := json.Marshal(template, jsonx.V1())
if err != nil {
return nil, err
}
diff --git a/services/cloudformation/resources_connect_core_test.go b/services/cloudformation/resources_connect_core_test.go
index ef8ca381c2..f9bf3c17bb 100644
--- a/services/cloudformation/resources_connect_core_test.go
+++ b/services/cloudformation/resources_connect_core_test.go
@@ -1,7 +1,7 @@
package cloudformation_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudformation"
connectbackend "github.com/blackbirdworks/gopherstack/services/connect"
)
@@ -32,7 +33,7 @@ func connectFlowContent(t *testing.T) string {
"Identifier": "end", "Type": "DisconnectParticipant", "Parameters": map[string]any{},
"Transitions": map[string]any{},
}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
return string(doc)
diff --git a/services/cloudformation/resources_connect_people.go b/services/cloudformation/resources_connect_people.go
index 4b937f0fda..0e47efad98 100644
--- a/services/cloudformation/resources_connect_people.go
+++ b/services/cloudformation/resources_connect_people.go
@@ -1,9 +1,11 @@
package cloudformation
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
connectbackend "github.com/blackbirdworks/gopherstack/services/connect"
)
@@ -348,10 +350,10 @@ func connectRuleActionMembers() map[string][2]string {
// ruleActions turns the template's Actions object (one list per action kind) into the API's list of
// {ActionType, Action} entries.
-func (c *connectCall) ruleActions() ([]json.RawMessage, error) {
+func (c *connectCall) ruleActions() ([]jsontext.Value, error) {
actions, _ := c.wire["Actions"].(map[string]any)
- var out []json.RawMessage
+ var out []jsontext.Value
for _, key := range sortedKeys(actions) {
kind, known := connectRuleActionMembers()[key]
@@ -361,7 +363,7 @@ func (c *connectCall) ruleActions() ([]json.RawMessage, error) {
list, _ := actions[key].([]any)
for _, item := range list {
- raw, err := json.Marshal(map[string]any{"ActionType": kind[0], kind[1]: item})
+ raw, err := json.Marshal(map[string]any{"ActionType": kind[0], kind[1]: item}, jsonx.V1())
if err != nil {
return nil, err
}
diff --git a/services/cloudformation/resources_connect_workspace.go b/services/cloudformation/resources_connect_workspace.go
index 77d79f0fa2..09a6b48b2f 100644
--- a/services/cloudformation/resources_connect_workspace.go
+++ b/services/cloudformation/resources_connect_workspace.go
@@ -1,9 +1,11 @@
package cloudformation
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
connectbackend "github.com/blackbirdworks/gopherstack/services/connect"
)
@@ -56,11 +58,11 @@ func updateConnectWorkspace(c *connectCall, physicalID string) error {
return err
}
- theme := connectbackend.UpdateWorkspaceThemeInput{Theme: json.RawMessage("null")}
+ theme := connectbackend.UpdateWorkspaceThemeInput{Theme: jsontext.Value("null")}
theme.InstanceID, theme.WorkspaceID = meta.InstanceID, meta.WorkspaceID
if t, ok := c.wire["Theme"]; ok {
- raw, err := json.Marshal(t)
+ raw, err := json.Marshal(t, jsonx.V1())
if err != nil {
return err
}
diff --git a/services/cloudformation/resources_connectfamily_helpers_test.go b/services/cloudformation/resources_connectfamily_helpers_test.go
index 105c65407a..31bbe9f474 100644
--- a/services/cloudformation/resources_connectfamily_helpers_test.go
+++ b/services/cloudformation/resources_connectfamily_helpers_test.go
@@ -1,7 +1,7 @@
package cloudformation_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -11,6 +11,7 @@ import (
cfntypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/sdktest"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/cloudformation"
@@ -84,7 +85,7 @@ func stackTemplate(t *testing.T, resources map[string]any, outputs map[string]an
outs[k] = map[string]any{"Value": v}
}
- doc, err := json.Marshal(map[string]any{"Resources": resources, "Outputs": outs})
+ doc, err := json.Marshal(map[string]any{"Resources": resources, "Outputs": outs}, jsonx.V1())
require.NoError(t, err)
return string(doc)
diff --git a/services/cloudformation/resources_deep.go b/services/cloudformation/resources_deep.go
index 95a65af3a8..c6f8721631 100644
--- a/services/cloudformation/resources_deep.go
+++ b/services/cloudformation/resources_deep.go
@@ -4,10 +4,11 @@ import (
"archive/zip"
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ebbackend "github.com/blackbirdworks/gopherstack/services/eventbridge"
lambdabackend "github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -58,7 +59,7 @@ func resolvedJSONProp(props map[string]any, key string, params, physicalIDs map[
case string:
return t
default:
- raw, err := json.Marshal(resolveDeep(t, params, physicalIDs))
+ raw, err := json.Marshal(resolveDeep(t, params, physicalIDs), jsonx.V1())
if err != nil {
return ""
}
diff --git a/services/cloudformation/resources_extensibility.go b/services/cloudformation/resources_extensibility.go
index c88cc090c0..adce9a8607 100644
--- a/services/cloudformation/resources_extensibility.go
+++ b/services/cloudformation/resources_extensibility.go
@@ -2,7 +2,8 @@ package cloudformation
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net"
@@ -13,6 +14,7 @@ import (
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
lambdabackend "github.com/blackbirdworks/gopherstack/services/lambda"
snsbackend "github.com/blackbirdworks/gopherstack/services/sns"
)
@@ -227,7 +229,7 @@ func startCFNResponseServer(
}
var resp cfnCustomResourceResponse
- _ = json.NewDecoder(r.Body).Decode(&resp)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(r.Body), &resp, jsonx.V1())
select {
case respCh <- resp:
@@ -318,7 +320,7 @@ func (rc *ResourceCreator) createCustomResource(
ResourceProperties: props,
}
- payload, err := json.Marshal(event)
+ payload, err := json.Marshal(event, jsonx.V1())
if err != nil {
return defaultPhysID, nil
}
@@ -421,7 +423,7 @@ func (rc *ResourceCreator) updateCustomResource(
OldResourceProperties: oldProps,
}
- payload, merr := json.Marshal(event)
+ payload, merr := json.Marshal(event, jsonx.V1())
if merr != nil {
return nil
}
@@ -487,7 +489,7 @@ func (rc *ResourceCreator) deleteCustomResource(
ResourceProperties: props,
}
- payload, merr := json.Marshal(event)
+ payload, merr := json.Marshal(event, jsonx.V1())
if merr != nil {
return nil
}
@@ -674,14 +676,14 @@ func (r *MacroRegistry) InvokeMacro(
event := map[string]any{
"region": cfnDefaultRegion,
"accountId": "000000000000",
- "fragment": json.RawMessage(fragmentJSON),
+ "fragment": jsontext.Value(fragmentJSON),
"transformId": macroName,
"params": map[string]any{},
"templateParameterValues": params,
"requestedTransforms": []string{macroName},
}
- payload, err := json.Marshal(event)
+ payload, err := json.Marshal(event, jsonx.V1())
if err != nil {
return fragmentJSON, err
}
@@ -699,12 +701,12 @@ func (r *MacroRegistry) InvokeMacro(
// Parse macro response: {"status": "success", "fragment": {...}}
var resp struct {
- Status string `json:"status"`
- ErrorMessage string `json:"errorMessage,omitempty"`
- Fragment json.RawMessage `json:"fragment"`
+ Status string `json:"status"`
+ ErrorMessage string `json:"errorMessage,omitempty"`
+ Fragment jsontext.Value `json:"fragment"`
}
- if unmarshalErr := json.Unmarshal(result, &resp); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(result, &resp, jsonx.V1()); unmarshalErr != nil {
return fragmentJSON, unmarshalErr
}
diff --git a/services/cloudformation/resources_extensibility_customresource_lifecycle_test.go b/services/cloudformation/resources_extensibility_customresource_lifecycle_test.go
index a810cd49e6..67ced1c868 100644
--- a/services/cloudformation/resources_extensibility_customresource_lifecycle_test.go
+++ b/services/cloudformation/resources_extensibility_customresource_lifecycle_test.go
@@ -2,7 +2,7 @@ package cloudformation_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudformation"
lambdabackend "github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -38,7 +39,7 @@ func TestResourceCreator_CustomResource_DeleteEvent(t *testing.T) {
ResponseURL string `json:"ResponseURL"`
}
- if err := json.Unmarshal(payload, &ev); err != nil {
+ if err := json.Unmarshal(payload, &ev, jsonx.V1()); err != nil {
return nil, 0, err
}
@@ -62,7 +63,7 @@ func TestResourceCreator_CustomResource_DeleteEvent(t *testing.T) {
"Status": "SUCCESS",
"PhysicalResourceId": ev.PhysicalResourceID,
}
- body, _ := json.Marshal(resp)
+ body, _ := json.Marshal(resp, jsonx.V1())
client := &http.Client{Timeout: 2 * time.Second}
req, reqErr := http.NewRequestWithContext(
ctx,
@@ -129,7 +130,7 @@ func TestResourceCreator_CustomResource_UpdateEvent(t *testing.T) {
ResponseURL string `json:"ResponseURL"`
}
- if err := json.Unmarshal(payload, &ev); err != nil {
+ if err := json.Unmarshal(payload, &ev, jsonx.V1()); err != nil {
return nil, 0, err
}
@@ -157,7 +158,7 @@ func TestResourceCreator_CustomResource_UpdateEvent(t *testing.T) {
"Status": "SUCCESS",
"PhysicalResourceId": ev.PhysicalResourceID,
}
- body, _ := json.Marshal(resp)
+ body, _ := json.Marshal(resp, jsonx.V1())
client := &http.Client{Timeout: 2 * time.Second}
req, reqErr := http.NewRequestWithContext(
ctx,
@@ -248,7 +249,7 @@ func TestStack_CustomResource_UpdateStack_SendsUpdateEvent(t *testing.T) {
ResponseURL string `json:"ResponseURL"`
}
- if err := json.Unmarshal(payload, &ev); err != nil {
+ if err := json.Unmarshal(payload, &ev, jsonx.V1()); err != nil {
return nil, 0, err
}
@@ -266,7 +267,7 @@ func TestStack_CustomResource_UpdateStack_SendsUpdateEvent(t *testing.T) {
"Status": "SUCCESS",
"PhysicalResourceId": physID,
}
- body, _ := json.Marshal(resp)
+ body, _ := json.Marshal(resp, jsonx.V1())
client := &http.Client{Timeout: 2 * time.Second}
req, reqErr := http.NewRequestWithContext(
ctx,
diff --git a/services/cloudformation/resources_extensibility_customresource_test.go b/services/cloudformation/resources_extensibility_customresource_test.go
index 167266a818..2161711146 100644
--- a/services/cloudformation/resources_extensibility_customresource_test.go
+++ b/services/cloudformation/resources_extensibility_customresource_test.go
@@ -2,7 +2,7 @@ package cloudformation_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/services/cloudformation"
lambdabackend "github.com/blackbirdworks/gopherstack/services/lambda"
@@ -112,7 +113,7 @@ func TestResourceCreator_CustomResource_ResponseURL_RoundTrip(t *testing.T) {
var event struct {
ResponseURL string `json:"ResponseURL"`
}
- if err := json.Unmarshal(payload, &event); err == nil && event.ResponseURL != "" {
+ if err := json.Unmarshal(payload, &event, jsonx.V1()); err == nil && event.ResponseURL != "" {
select {
case capturedURL <- event.ResponseURL:
default:
@@ -160,7 +161,7 @@ func TestResourceCreator_CustomResource_ResponseURL_RoundTrip(t *testing.T) {
"Data": map[string]any{"BucketName": "my-bucket"},
}
- body, _ := json.Marshal(resp)
+ body, _ := json.Marshal(resp, jsonx.V1())
client := &http.Client{}
putReq, _ := http.NewRequestWithContext(t.Context(), http.MethodPut, url,
newBytesReader(body))
@@ -319,7 +320,7 @@ func TestResourceCreator_CustomResource_FAILEDResponse(t *testing.T) {
var event struct {
ResponseURL string `json:"ResponseURL"`
}
- if err := json.Unmarshal(payload, &event); err == nil {
+ if err := json.Unmarshal(payload, &event, jsonx.V1()); err == nil {
select {
case capturedURL <- event.ResponseURL:
default:
@@ -363,7 +364,7 @@ func TestResourceCreator_CustomResource_FAILEDResponse(t *testing.T) {
"Reason": "Validation error: bad-data is not allowed",
"PhysicalResourceId": "failing-resource-001",
}
- body, _ := json.Marshal(resp)
+ body, _ := json.Marshal(resp, jsonx.V1())
client := &http.Client{}
req, _ := http.NewRequestWithContext(t.Context(), http.MethodPut, url, newBytesReader(body))
req.ContentLength = int64(len(body))
@@ -395,7 +396,7 @@ func TestStack_CustomResource_GetAtt_DataOutputs(t *testing.T) {
var event struct {
ResponseURL string `json:"ResponseURL"`
}
- if err := json.Unmarshal(payload, &event); err == nil {
+ if err := json.Unmarshal(payload, &event, jsonx.V1()); err == nil {
select {
case capturedURL <- event.ResponseURL:
default:
@@ -459,7 +460,7 @@ func TestStack_CustomResource_GetAtt_DataOutputs(t *testing.T) {
"BucketArn": "arn:aws:s3:::my-prefix-generated-bucket",
},
}
- body, _ := json.Marshal(resp)
+ body, _ := json.Marshal(resp, jsonx.V1())
client := &http.Client{}
req, _ := http.NewRequestWithContext(t.Context(), http.MethodPut, url, newBytesReader(body))
req.ContentLength = int64(len(body))
diff --git a/services/cloudformation/resources_extensibility_macro_test.go b/services/cloudformation/resources_extensibility_macro_test.go
index d4327d90f5..4e4e6cccdf 100644
--- a/services/cloudformation/resources_extensibility_macro_test.go
+++ b/services/cloudformation/resources_extensibility_macro_test.go
@@ -2,12 +2,13 @@ package cloudformation_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudformation"
lambdabackend "github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -170,7 +171,7 @@ func TestMacroRegistry_InvokeMacro_WithMockLambda(t *testing.T) {
mockLambda := &mockLambdaStorageBackend{
invokeFunc: func(_ context.Context, _ string, _ lambdabackend.InvocationType, _ []byte) ([]byte, int, error) {
- respBody, _ := json.Marshal(tt.lambdaResp)
+ respBody, _ := json.Marshal(tt.lambdaResp, jsonx.V1())
return respBody, 200, nil
},
@@ -183,14 +184,14 @@ func TestMacroRegistry_InvokeMacro_WithMockLambda(t *testing.T) {
"",
)
- fragmentJSON, _ := json.Marshal(tt.fragment)
+ fragmentJSON, _ := json.Marshal(tt.fragment, jsonx.V1())
result, err := registry.InvokeMacro(
t.Context(), lambdabackend.NewHandler(mockLambda), tt.macroName, fragmentJSON, nil,
)
require.NoError(t, err)
var got map[string]any
- require.NoError(t, json.Unmarshal(result, &got))
+ require.NoError(t, json.Unmarshal(result, &got, jsonx.V1()))
assert.Equal(t, tt.wantFragment, got)
})
}
@@ -205,7 +206,7 @@ func TestMacroRegistry_InvokeMacro_NoLambdaBackend(t *testing.T) {
registry.RegisterForTest("MyMacro", "arn:aws:lambda:us-east-1:000000000000:function:fn", "")
fragment := map[string]any{"unchanged": true}
- fragmentJSON, _ := json.Marshal(fragment)
+ fragmentJSON, _ := json.Marshal(fragment, jsonx.V1())
// nil lambda backend → returns fragment unchanged.
result, err := registry.InvokeMacro(t.Context(), nil, "MyMacro", fragmentJSON, nil)
@@ -221,7 +222,7 @@ func TestMacroRegistry_InvokeMacro_UnknownMacro(t *testing.T) {
registry := cloudformation.NewMacroRegistry()
fragment := map[string]any{"pass": "through"}
- fragmentJSON, _ := json.Marshal(fragment)
+ fragmentJSON, _ := json.Marshal(fragment, jsonx.V1())
mockLambda := &mockLambdaStorageBackend{}
result, err := registry.InvokeMacro(
diff --git a/services/cloudformation/resources_iot_more_test.go b/services/cloudformation/resources_iot_more_test.go
index 81940b5a48..1449883eeb 100644
--- a/services/cloudformation/resources_iot_more_test.go
+++ b/services/cloudformation/resources_iot_more_test.go
@@ -6,7 +6,7 @@ import (
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"testing"
@@ -14,6 +14,8 @@ import (
cfnsdk "github.com/aws/aws-sdk-go-v2/service/cloudformation"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCreateStack_IoTMoreTypes(t *testing.T) {
@@ -203,7 +205,10 @@ func testIoTCertificate(t *testing.T) {
&x509.CertificateRequest{Subject: pkix.Name{CommonName: "device"}}, key)
require.NoError(t, err)
- csrJSON, err := json.Marshal(string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrDER})))
+ csrJSON, err := json.Marshal(
+ string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrDER})),
+ jsonx.V1(),
+ )
require.NoError(t, err)
tmpl := `{
diff --git a/services/cloudformation/resources_networkmanager.go b/services/cloudformation/resources_networkmanager.go
index 006a9ca8d2..d6625aac73 100644
--- a/services/cloudformation/resources_networkmanager.go
+++ b/services/cloudformation/resources_networkmanager.go
@@ -2,11 +2,12 @@ package cloudformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
networkmanagerbackend "github.com/blackbirdworks/gopherstack/services/networkmanager"
)
@@ -538,7 +539,7 @@ func nmCoreNetworkResource() nmResource {
create: func(bk *networkmanagerbackend.InMemoryBackend, c nmCreateCtx) (string, error) {
policy := c.str("PolicyDocument")
if doc, ok := c.props["PolicyDocument"].(map[string]any); ok {
- raw, err := json.Marshal(resolveDeep(doc, c.params, c.physicalIDs))
+ raw, err := json.Marshal(resolveDeep(doc, c.params, c.physicalIDs), jsonx.V1())
if err != nil {
return "", err
}
diff --git a/services/cloudformation/resources_oam_test.go b/services/cloudformation/resources_oam_test.go
index 023c7d9553..31a64bf564 100644
--- a/services/cloudformation/resources_oam_test.go
+++ b/services/cloudformation/resources_oam_test.go
@@ -1,7 +1,7 @@
package cloudformation_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/sdktest"
"github.com/blackbirdworks/gopherstack/services/cloudformation"
oambackend "github.com/blackbirdworks/gopherstack/services/oam"
@@ -96,7 +97,7 @@ func oamTemplate(t *testing.T, sinkName string, linkTypes []string, linkFilter,
"LinkArn": map[string]any{"Value": map[string]any{"Fn::GetAtt": []string{"Link", "Arn"}}},
"LinkLabl": map[string]any{"Value": map[string]any{"Fn::GetAtt": []string{"Link", "Label"}}},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
return string(doc)
diff --git a/services/cloudformation/resources_rum_test.go b/services/cloudformation/resources_rum_test.go
index ad0a638a92..7b66e924c4 100644
--- a/services/cloudformation/resources_rum_test.go
+++ b/services/cloudformation/resources_rum_test.go
@@ -1,7 +1,7 @@
package cloudformation_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"testing"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/sdktest"
"github.com/blackbirdworks/gopherstack/services/cloudformation"
rumbackend "github.com/blackbirdworks/gopherstack/services/rum"
@@ -87,7 +88,7 @@ func rumTemplate(name, domain, cwLog, custom, metric, team string) string {
rumResMetric: "com.amazon.rum.performance_resource_event",
}[metric]
- policy, _ := json.Marshal(rumPolicyDocument)
+ policy, _ := json.Marshal(rumPolicyDocument, jsonx.V1())
return fmt.Sprintf(rumMonitorTemplate, name, domain, cwLog, custom, metric, eventType, policy, team)
}
diff --git a/services/cloudformation/resources_sagemaker.go b/services/cloudformation/resources_sagemaker.go
index 81dbac7d9a..f2c15aa03c 100644
--- a/services/cloudformation/resources_sagemaker.go
+++ b/services/cloudformation/resources_sagemaker.go
@@ -2,7 +2,7 @@ package cloudformation
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
"strconv"
@@ -267,13 +267,13 @@ func sagemakerFloatProp(props map[string]any, key string) float64 {
// rawJSONProp reads a property as opaque JSON, returning nil when absent
// (unlike jsonProp's "", which is not valid json.RawMessage).
-func rawJSONProp(props map[string]any, key string) json.RawMessage {
+func rawJSONProp(props map[string]any, key string) jsontext.Value {
s := jsonProp(props, key)
if s == "" {
return nil
}
- return json.RawMessage(s)
+ return jsontext.Value(s)
}
// ---- AWS::SageMaker::Model ----
diff --git a/services/cloudformation/resources_sagemaker_athena_wafv2_test.go b/services/cloudformation/resources_sagemaker_athena_wafv2_test.go
index a11b2cb7f9..e050745bbc 100644
--- a/services/cloudformation/resources_sagemaker_athena_wafv2_test.go
+++ b/services/cloudformation/resources_sagemaker_athena_wafv2_test.go
@@ -1,7 +1,7 @@
package cloudformation_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"testing"
@@ -553,7 +553,7 @@ func testWAFv2WebACLAssociation(t *testing.T) {
// association template below can reference its real ARN.
acl, err := backends.WAFv2.Backend.CreateWebACL(
t.Context(), "acl-1", "REGIONAL", "",
- json.RawMessage(`{"Allow":{}}`), nil,
+ jsontext.Value(`{"Allow":{}}`), nil,
nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil,
nil,
diff --git a/services/cloudformation/resources_ssm.go b/services/cloudformation/resources_ssm.go
index d147af7838..a1eb48d2d1 100644
--- a/services/cloudformation/resources_ssm.go
+++ b/services/cloudformation/resources_ssm.go
@@ -2,9 +2,10 @@ package cloudformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ssmbackend "github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -209,5 +210,5 @@ func (rc *ResourceCreator) deleteSSMDocument(ctx context.Context, name string) e
// marshalJSON serializes a value to compact JSON bytes.
func marshalJSON(v any) ([]byte, error) {
- return json.Marshal(v)
+ return json.Marshal(v, jsonx.V1())
}
diff --git a/services/cloudformation/resources_ssm_more.go b/services/cloudformation/resources_ssm_more.go
index bcb61a67e2..d3edc31559 100644
--- a/services/cloudformation/resources_ssm_more.go
+++ b/services/cloudformation/resources_ssm_more.go
@@ -2,10 +2,11 @@ package cloudformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ssmbackend "github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -382,7 +383,7 @@ func jsonProp(props map[string]any, key string) string {
return s
}
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return ""
}
diff --git a/services/cloudformation/resources_wafv2.go b/services/cloudformation/resources_wafv2.go
index 1fa6b9c3f9..bd31053531 100644
--- a/services/cloudformation/resources_wafv2.go
+++ b/services/cloudformation/resources_wafv2.go
@@ -2,7 +2,7 @@ package cloudformation
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
)
@@ -33,7 +33,7 @@ func (rc *ResourceCreator) createWAFv2WebACL(
acl, err := rc.backends.WAFv2.Backend.CreateWebACL(
ctx,
name, scope, "",
- json.RawMessage(`{"Allow":{}}`), nil,
+ jsontext.Value(`{"Allow":{}}`), nil,
nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil,
nil,
diff --git a/services/cloudformation/sam_api.go b/services/cloudformation/sam_api.go
index 35154935f7..5739d68406 100644
--- a/services/cloudformation/sam_api.go
+++ b/services/cloudformation/sam_api.go
@@ -1,10 +1,12 @@
package cloudformation
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -188,11 +190,11 @@ func (a *samAPI) stageProps(depID string) map[string]any {
func (a *samAPI) body() (map[string]any, error) {
doc := map[string]any{}
if def := asMap(a.props["DefinitionBody"]); def != nil {
- raw, err := json.Marshal(def)
+ raw, err := json.Marshal(def, jsonx.V1())
if err != nil {
return nil, err
}
- if err = json.Unmarshal(raw, &doc); err != nil {
+ if err = json.Unmarshal(raw, &doc, jsonx.V1()); err != nil {
return nil, err
}
} else {
diff --git a/services/cloudformation/sam_events_test.go b/services/cloudformation/sam_events_test.go
index d5ab71bba7..d82dfee656 100644
--- a/services/cloudformation/sam_events_test.go
+++ b/services/cloudformation/sam_events_test.go
@@ -2,7 +2,7 @@ package cloudformation_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -20,6 +20,7 @@ import (
cfnsdk "github.com/aws/aws-sdk-go-v2/service/cloudformation"
"github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
apigatewayv2backend "github.com/blackbirdworks/gopherstack/services/apigatewayv2"
"github.com/blackbirdworks/gopherstack/services/cloudformation"
@@ -54,12 +55,12 @@ func (r *recordingInvoker) InvokeFunction(
"Version": "2012-10-17", "Statement": []any{map[string]any{
"Action": "execute-api:Invoke", "Effect": effect, "Resource": "*",
}},
- }})
+ }}, jsonx.V1())
return policy, http.StatusOK, nil
}
r.calls <- invocation{name: fn, payload: string(payload)}
- resp, _ := json.Marshal(map[string]any{"statusCode": 200, "body": fn})
+ resp, _ := json.Marshal(map[string]any{"statusCode": 200, "body": fn}, jsonx.V1())
return resp, http.StatusOK, nil
}
diff --git a/services/cloudformation/sam_misc.go b/services/cloudformation/sam_misc.go
index ea165a9402..c68816bbb3 100644
--- a/services/cloudformation/sam_misc.go
+++ b/services/cloudformation/sam_misc.go
@@ -1,6 +1,10 @@
package cloudformation
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
func samTableAllowed() map[string]bool {
return keySet("PrimaryKey", "ProvisionedThroughput", samKeyTableName, "Tags", "SSESpecification",
@@ -134,7 +138,7 @@ func (t *samTranslator) translateStateMachine(id string, r map[string]any) error
func smDefinition(id string, props, sm map[string]any) error {
switch {
case props["Definition"] != nil:
- raw, err := json.Marshal(props["Definition"])
+ raw, err := json.Marshal(props["Definition"], jsonx.V1())
if err != nil {
return samErr(id, "Definition is not valid JSON: %v", err)
}
diff --git a/services/cloudformation/sam_statemachine_events_test.go b/services/cloudformation/sam_statemachine_events_test.go
index 14b9ede776..35b530fdae 100644
--- a/services/cloudformation/sam_statemachine_events_test.go
+++ b/services/cloudformation/sam_statemachine_events_test.go
@@ -1,7 +1,7 @@
package cloudformation_test
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/cloudformation/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const samSMDefinition = ` Definition:
@@ -47,7 +49,7 @@ func processedResources(t *testing.T, env *samEventEnv) map[string]map[string]an
require.NoError(
t,
- json.Unmarshal([]byte(getTemplate(t, env.client, "sam-stack", types.TemplateStageProcessed)), &doc),
+ json.Unmarshal([]byte(getTemplate(t, env.client, "sam-stack", types.TemplateStageProcessed)), &doc, jsonx.V1()),
)
return doc.Resources
diff --git a/services/cloudformation/sam_transform.go b/services/cloudformation/sam_transform.go
index f3177abd7a..bda238ffbc 100644
--- a/services/cloudformation/sam_transform.go
+++ b/services/cloudformation/sam_transform.go
@@ -3,12 +3,15 @@ package cloudformation
import (
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -83,7 +86,7 @@ func samGetAtt(id, attr string) map[string]any {
}
func samHash(v any) string {
- raw, _ := json.Marshal(v)
+ raw, _ := json.Marshal(v, jsonx.V1())
sum := sha256.Sum256(raw)
return hex.EncodeToString(sum[:])[:samHashLen]
@@ -361,7 +364,7 @@ func processedTemplateBody(body string) (string, error) {
} else {
delete(doc, "Transform")
}
- out, err := json.MarshalIndent(doc, "", " ")
+ out, err := json.Marshal(doc, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
return string(out), err
}
@@ -379,7 +382,7 @@ func expandSAMBody(body string) (string, error) {
if err = expandSAMDocument(doc); err != nil {
return "", err
}
- out, err := json.Marshal(doc)
+ out, err := json.Marshal(doc, jsonx.V1())
return string(out), err
}
@@ -394,7 +397,7 @@ func decodeTemplateDoc(body string) (map[string]any, error) {
jsonBody = converted
}
var doc map[string]any
- if err := json.Unmarshal([]byte(jsonBody), &doc); err != nil {
+ if err := json.Unmarshal([]byte(jsonBody), &doc, jsonx.V1()); err != nil {
return nil, fmt.Errorf("failed to parse JSON template: %w", err)
}
diff --git a/services/cloudformation/stack_policy_eval.go b/services/cloudformation/stack_policy_eval.go
index bdb8cfed6a..eed967eae2 100644
--- a/services/cloudformation/stack_policy_eval.go
+++ b/services/cloudformation/stack_policy_eval.go
@@ -1,10 +1,12 @@
package cloudformation
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Stack policy evaluation semantics below are transcribed from AWS's
@@ -59,14 +61,14 @@ type stackPolicyStringSet []string
func (s *stackPolicyStringSet) UnmarshalJSON(data []byte) error {
var single string
- if err := json.Unmarshal(data, &single); err == nil {
+ if err := json.Unmarshal(data, &single, jsonx.V1()); err == nil {
*s = stackPolicyStringSet{single}
return nil
}
var multi []string
- if err := json.Unmarshal(data, &multi); err != nil {
+ if err := json.Unmarshal(data, &multi, jsonx.V1()); err != nil {
return err
}
@@ -91,7 +93,7 @@ const (
// rather than silently never enforcing anything at UpdateStack time.
func parseStackPolicyDocument(policy string) (*stackPolicyDocument, error) {
var doc stackPolicyDocument
- if err := json.Unmarshal([]byte(policy), &doc); err != nil {
+ if err := json.Unmarshal([]byte(policy), &doc, jsonx.V1()); err != nil {
return nil, fmt.Errorf("malformed stack policy: %w", err)
}
diff --git a/services/cloudformation/template.go b/services/cloudformation/template.go
index 9a5d478237..fc7f0112f5 100644
--- a/services/cloudformation/template.go
+++ b/services/cloudformation/template.go
@@ -2,7 +2,7 @@ package cloudformation
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -18,6 +18,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/collections"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
directconnectbackend "github.com/blackbirdworks/gopherstack/services/directconnect"
)
@@ -106,7 +107,7 @@ func (t *Template) fromPlain(p templatePlain) {
// the real CloudFormation template schema.
func (t *Template) UnmarshalJSON(data []byte) error {
var p templatePlain
- if err := json.Unmarshal(data, &p); err != nil {
+ if err := json.Unmarshal(data, &p, jsonx.V1()); err != nil {
return err
}
t.fromPlain(p)
@@ -189,7 +190,7 @@ func (r *TemplateResource) UnmarshalJSON(data []byte) error {
}
var p plain
- if err := json.Unmarshal(data, &p); err != nil {
+ if err := json.Unmarshal(data, &p, jsonx.V1()); err != nil {
return err
}
@@ -301,7 +302,7 @@ func ParseTemplate(body string) (*Template, error) {
}
var tmpl Template
- if err := json.Unmarshal([]byte(body), &tmpl); err != nil {
+ if err := json.Unmarshal([]byte(body), &tmpl, jsonx.V1()); err != nil {
return nil, fmt.Errorf("failed to parse JSON template: %w", err)
}
@@ -360,7 +361,7 @@ func yamlToJSON(body string) (string, error) {
return "", err
}
- out, err := json.Marshal(val)
+ out, err := json.Marshal(val, jsonx.V1())
if err != nil {
return "", fmt.Errorf("failed to serialise normalised YAML template: %w", err)
}
@@ -492,7 +493,7 @@ func expandLanguageExtensions(body string) (string, error) {
}
doc["Resources"] = expanded
- out, err := json.Marshal(doc)
+ out, err := json.Marshal(doc, jsonx.V1())
if err != nil {
return "", fmt.Errorf("failed to serialise expanded template: %w", err)
}
@@ -513,7 +514,7 @@ func parseGenericTemplate(body string) (map[string]any, error) {
}
var doc map[string]any
- if err := json.Unmarshal([]byte(trimmed), &doc); err != nil {
+ if err := json.Unmarshal([]byte(trimmed), &doc, jsonx.V1()); err != nil {
return nil, fmt.Errorf("failed to parse JSON template: %w", err)
}
@@ -1073,7 +1074,7 @@ func resolveMiscIntrinsic(val map[string]any, ctx resolveCtx) string {
// Fn::ToJsonString: serialize value to JSON string (#13)
if jsonVal, hasJSON := val["Fn::ToJsonString"]; hasJSON {
- data, err := json.Marshal(jsonVal)
+ data, err := json.Marshal(jsonVal, jsonx.V1())
if err != nil {
return ""
}
@@ -1914,7 +1915,7 @@ func invokeMacroTransform(macroName string, transformSpec map[string]any, ctx re
}
// Build a minimal fragment from the transform spec parameters.
- fragmentJSON, err := json.Marshal(resolvedParams)
+ fragmentJSON, err := json.Marshal(resolvedParams, jsonx.V1())
if err != nil {
return ""
}
diff --git a/services/cloudformation/template_preflight.go b/services/cloudformation/template_preflight.go
index 59687e5ed7..c4045b4cc8 100644
--- a/services/cloudformation/template_preflight.go
+++ b/services/cloudformation/template_preflight.go
@@ -1,13 +1,14 @@
package cloudformation
import (
- "encoding/json"
"errors"
"reflect"
"regexp"
"slices"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrTemplateFormat is the "Template format error: ..." family real
@@ -65,18 +66,18 @@ func lineColumn(body string, offset int64) (int, int) {
// templateParseFormatErr maps a ParseTemplate failure to AWS's synchronous
// "Template format error" wording; non-syntax failures pass through unchanged.
func templateParseFormatErr(body string, err error) error {
- if syn, ok := errors.AsType[*json.SyntaxError](err); ok {
- line, col := lineColumn(strings.TrimSpace(body), syn.Offset)
+ if offset, ok := jsonx.SyntaxError(err); ok {
+ line, col := lineColumn(strings.TrimSpace(body), offset)
return templateFormatErr("JSON not well-formed. (line %d, column %d)", line, col)
}
- if typ, ok := errors.AsType[*json.UnmarshalTypeError](err); ok {
- if strings.HasSuffix(typ.Field, "Description") {
+ if field, ok := jsonx.TypeError(err); ok {
+ if strings.HasSuffix(field, "Description") {
return templateFormatErr("Every Description member must be a string.")
}
- return templateFormatErr("%s has an invalid type", typ.Field)
+ return templateFormatErr("%s has an invalid type", field)
}
if strings.Contains(err.Error(), "failed to parse YAML template") {
diff --git a/services/cloudformation/template_strictness.go b/services/cloudformation/template_strictness.go
index fe5707dcf2..05e949eb03 100644
--- a/services/cloudformation/template_strictness.go
+++ b/services/cloudformation/template_strictness.go
@@ -2,12 +2,14 @@ package cloudformation
import (
_ "embed"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"sort"
"strings"
"sync"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
//go:embed cfn_resource_services.json
@@ -22,7 +24,7 @@ var (
func knownResourceServices() map[string]struct{} {
cfnResourceServicesOnce.Do(func() {
var raw []string
- if err := json.Unmarshal(cfnResourceServicesJSON, &raw); err != nil {
+ if err := json.Unmarshal(cfnResourceServicesJSON, &raw, jsonx.V1()); err != nil {
panic("cloudformation: invalid embedded cfn_resource_services.json: " + err.Error())
}
@@ -272,7 +274,7 @@ var (
// every other type stays unchecked rather than guessed.
func requiredResourceProperties() map[string][]string {
cfnRequiredPropertiesOnce.Do(func() {
- if err := json.Unmarshal(cfnRequiredPropertiesJSON, &cfnRequiredProperties); err != nil {
+ if err := json.Unmarshal(cfnRequiredPropertiesJSON, &cfnRequiredProperties, jsonx.V1()); err != nil {
panic("cloudformation: invalid embedded cfn_required_properties.json: " + err.Error())
}
})
diff --git a/services/cloudfront/dataplane_edge.go b/services/cloudfront/dataplane_edge.go
index f34aac37f4..31f97d097b 100644
--- a/services/cloudfront/dataplane_edge.go
+++ b/services/cloudfront/dataplane_edge.go
@@ -3,13 +3,15 @@ package cloudfront
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"slices"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -246,7 +248,7 @@ func (h *Handler) edgeInvoke(
}
}
- payload, _ := json.Marshal(map[string]any{"Records": []any{map[string]any{"cf": cf}}})
+ payload, _ := json.Marshal(map[string]any{"Records": []any{map[string]any{"cf": cf}}}, jsonx.V1())
res, fnErr, err := edge.InvokeEdge(ctx, arn.name, arn.version, payload)
@@ -264,7 +266,7 @@ func (h *Handler) edgeInvoke(
}
var out map[string]any
- if json.Unmarshal(res, &out) != nil || out == nil {
+ if json.Unmarshal(res, &out, jsonx.V1()) != nil || out == nil {
return nil, edgeValidation(
"The Lambda function returned a value that is not a valid request or response object.",
)
diff --git a/services/cloudfront/dataplane_function.go b/services/cloudfront/dataplane_function.go
index 5f26a50e0e..0ca63448de 100644
--- a/services/cloudfront/dataplane_function.go
+++ b/services/cloudfront/dataplane_function.go
@@ -1,12 +1,14 @@
package cloudfront
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"net/url"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -115,7 +117,7 @@ func (h *Handler) runFunction(
}
var out map[string]any
- if err := json.Unmarshal([]byte(res.Output), &out); err != nil {
+ if err := json.Unmarshal([]byte(res.Output), &out, jsonx.V1()); err != nil {
return nil, res, false
}
diff --git a/services/cloudfront/dataplane_s3_policy.go b/services/cloudfront/dataplane_s3_policy.go
index b9c27e2788..7e3fa673fd 100644
--- a/services/cloudfront/dataplane_s3_policy.go
+++ b/services/cloudfront/dataplane_s3_policy.go
@@ -1,9 +1,12 @@
package cloudfront
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -39,7 +42,7 @@ const (
)
type policyDoc struct {
- Statement json.RawMessage `json:"Statement"`
+ Statement jsontext.Value `json:"Statement"`
}
type policyStatement struct {
@@ -52,17 +55,17 @@ type policyStatement struct {
func parseStatements(policyJSON string) []policyStatement {
var doc policyDoc
- if err := json.Unmarshal([]byte(policyJSON), &doc); err != nil {
+ if err := json.Unmarshal([]byte(policyJSON), &doc, jsonx.V1()); err != nil {
return nil
}
var many []policyStatement
- if err := json.Unmarshal(doc.Statement, &many); err == nil {
+ if err := json.Unmarshal(doc.Statement, &many, jsonx.V1()); err == nil {
return many
}
var one policyStatement
- if err := json.Unmarshal(doc.Statement, &one); err == nil {
+ if err := json.Unmarshal(doc.Statement, &one, jsonx.V1()); err == nil {
return []policyStatement{one}
}
diff --git a/services/cloudfront/dataplane_signing.go b/services/cloudfront/dataplane_signing.go
index 86f91e57d9..ab2770938f 100644
--- a/services/cloudfront/dataplane_signing.go
+++ b/services/cloudfront/dataplane_signing.go
@@ -6,13 +6,15 @@ import (
"crypto/sha1" //nolint:gosec // CloudFront signed URLs mandate RSA-SHA1
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"net"
"net/http"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func isSignedQueryKey(name string) bool {
@@ -224,7 +226,7 @@ func cannedPolicyDoc(resource string, expires int64) ([]byte, error) {
return json.Marshal(cannedPolicy{Statement: []cannedStatement{{
Resource: resource,
Condition: cannedCondition{DateLessThan: cannedEpoch{EpochTime: expires}},
- }}})
+ }}}, jsonx.V1())
}
func rsaSHA1Verify(key *rsa.PublicKey, doc, sig []byte) bool {
@@ -240,7 +242,7 @@ func verifyCustomPolicy(v *viewerReq, d *Distribution, key *rsa.PublicKey, polic
}
var pol signedPolicy
- if json.Unmarshal(doc, &pol) != nil || len(pol.Statement) == 0 {
+ if json.Unmarshal(doc, &pol, jsonx.V1()) != nil || len(pol.Statement) == 0 {
return false
}
diff --git a/services/cloudfront/dataplane_signing_internal_test.go b/services/cloudfront/dataplane_signing_internal_test.go
index 7964ec99b7..7e30c91562 100644
--- a/services/cloudfront/dataplane_signing_internal_test.go
+++ b/services/cloudfront/dataplane_signing_internal_test.go
@@ -1,11 +1,13 @@
package cloudfront
import (
- "encoding/json"
+ "encoding/json/jsontext"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCannedPolicyDoc(t *testing.T) {
@@ -37,7 +39,7 @@ func TestCannedPolicyDoc(t *testing.T) {
doc, err := cannedPolicyDoc(tt.resource, 42)
require.NoError(t, err)
assert.Equal(t, tt.want, string(doc))
- assert.True(t, json.Valid(doc))
+ assert.True(t, jsontext.Value(doc).IsValid(jsonx.V1()))
})
}
}
diff --git a/services/cloudfront/distributions_transition_test.go b/services/cloudfront/distributions_transition_test.go
index 08464133f4..f060b6d5f8 100644
--- a/services/cloudfront/distributions_transition_test.go
+++ b/services/cloudfront/distributions_transition_test.go
@@ -1,12 +1,13 @@
package cloudfront_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudfront"
)
@@ -131,7 +132,7 @@ func TestDeleteDistribution_CleansUpSideMaps(t *testing.T) {
require.NotEmpty(t, data)
var probe distSnapshotProbe
- require.NoError(t, json.Unmarshal(data, &probe))
+ require.NoError(t, json.Unmarshal(data, &probe, jsonx.V1()))
_, leaked := probe.FuncAssocs[d.ID]
require.False(t, leaked, "distributionFunctionAssociations must not retain a deleted distribution's entry")
diff --git a/services/cloudfront/function_exec.go b/services/cloudfront/function_exec.go
index c8befa45ca..03461c20b5 100644
--- a/services/cloudfront/function_exec.go
+++ b/services/cloudfront/function_exec.go
@@ -1,13 +1,15 @@
package cloudfront
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"html"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// storeKVS adapts the backend's Key Value Stores to a running function.
@@ -74,7 +76,7 @@ func (b *InMemoryBackend) ExecuteFunction(name, stage string, event any) (*Funct
func (h *Handler) runTestFunction(c *echo.Context, name, stage string, eventObject []byte) error {
var event any
- if err := json.Unmarshal(eventObject, &event); err != nil {
+ if err := json.Unmarshal(eventObject, &event, jsonx.V1()); err != nil {
return xmlResp(c, http.StatusBadRequest, cfErrorXML("InvalidArgument", "EventObject must be valid JSON"))
}
diff --git a/services/cloudfront/handler_distribution_tenants.go b/services/cloudfront/handler_distribution_tenants.go
index f77e1f182b..954f57e886 100644
--- a/services/cloudfront/handler_distribution_tenants.go
+++ b/services/cloudfront/handler_distribution_tenants.go
@@ -1,7 +1,7 @@
package cloudfront
import (
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"errors"
"fmt"
@@ -11,6 +11,8 @@ import (
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleDomainAssociationError maps UpdateDomainAssociation errors. Its own
@@ -194,13 +196,13 @@ func (c *tenantCustomizationsXML) toMap() map[string]any {
return nil
}
- raw, err := json.Marshal(c)
+ raw, err := json.Marshal(c, jsonx.V1())
if err != nil {
return nil
}
var out map[string]any
- if json.Unmarshal(raw, &out) != nil {
+ if json.Unmarshal(raw, &out, jsonx.V1()) != nil {
return nil
}
@@ -212,13 +214,13 @@ func tenantCustomizationsFromMap(m map[string]any) *tenantCustomizationsXML {
return nil
}
- raw, err := json.Marshal(m)
+ raw, err := json.Marshal(m, jsonx.V1())
if err != nil {
return nil
}
var out tenantCustomizationsXML
- if json.Unmarshal(raw, &out) != nil {
+ if json.Unmarshal(raw, &out, jsonx.V1()) != nil {
return nil
}
diff --git a/services/cloudfront/handler_functions.go b/services/cloudfront/handler_functions.go
index 538931e81c..5fd0936fb3 100644
--- a/services/cloudfront/handler_functions.go
+++ b/services/cloudfront/handler_functions.go
@@ -2,13 +2,15 @@ package cloudfront
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
"encoding/xml"
"fmt"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type functionConfigXML struct {
@@ -383,7 +385,7 @@ func (h *Handler) handleTestFunction(c *echo.Context, name string) error {
)
}
- if !json.Valid(eventObject) {
+ if !jsontext.Value(eventObject).IsValid(jsonx.V1()) {
return xmlResp(
c,
http.StatusBadRequest,
diff --git a/services/cloudfront/handler_key_value_store_test.go b/services/cloudfront/handler_key_value_store_test.go
index 68b7458743..6e8c188a41 100644
--- a/services/cloudfront/handler_key_value_store_test.go
+++ b/services/cloudfront/handler_key_value_store_test.go
@@ -1,7 +1,7 @@
package cloudfront_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudfront"
)
@@ -412,7 +413,7 @@ func TestInMemoryBackend_KeyValueStore(t *testing.T) {
KeyValueStoreData map[string]map[string]string `json:"keyValueStoreData"`
KeyValueDataETags map[string]string `json:"keyValueDataETags"`
}
- require.NoError(t, json.Unmarshal(snap, &decoded))
+ require.NoError(t, json.Unmarshal(snap, &decoded, jsonx.V1()))
assert.NotContains(t, decoded.KeyValueStoreData, kvs.ID,
"deleted KVS's data-plane values must not survive in the persisted snapshot")
assert.NotContains(t, decoded.KeyValueDataETags, kvs.ID,
diff --git a/services/cloudfront/kvs_import.go b/services/cloudfront/kvs_import.go
index 0290080d45..01ce59e584 100644
--- a/services/cloudfront/kvs_import.go
+++ b/services/cloudfront/kvs_import.go
@@ -2,9 +2,11 @@ package cloudfront
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -64,7 +66,7 @@ func (h *Handler) readKVSImport(ctx context.Context, src *kvsImportSourceXML) ([
func parseKVSImport(raw []byte) ([]*KVSItem, error) {
var doc kvsImportDocument
- if err := json.Unmarshal(raw, &doc); err != nil {
+ if err := json.Unmarshal(raw, &doc, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: import source is not valid JSON: %w", ErrValidation, err)
}
diff --git a/services/cloudfront/lambda_edge_test.go b/services/cloudfront/lambda_edge_test.go
index 8d6be91e34..5505e2da27 100644
--- a/services/cloudfront/lambda_edge_test.go
+++ b/services/cloudfront/lambda_edge_test.go
@@ -3,7 +3,7 @@ package cloudfront_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudfront"
)
@@ -48,7 +49,7 @@ func (f *fakeEdge) InvokeEdge(_ context.Context, name, version string, payload [
} `json:"Records"`
}
- if err := json.Unmarshal(payload, &in); err != nil || len(in.Records) != 1 {
+ if err := json.Unmarshal(payload, &in, jsonx.V1()); err != nil || len(in.Records) != 1 {
return nil, "", fmt.Errorf("%w: bad event", errFakeEdge)
}
@@ -62,7 +63,7 @@ func (f *fakeEdge) InvokeEdge(_ context.Context, name, version string, payload [
} `json:"Records"`
}
- _ = json.Unmarshal(payload, &snapshot)
+ _ = json.Unmarshal(payload, &snapshot, jsonx.V1())
f.mu.Lock()
f.calls[name]++
@@ -75,7 +76,7 @@ func (f *fakeEdge) InvokeEdge(_ context.Context, name, version string, payload [
return nil, fnErr, err
}
- b, _ := json.Marshal(out)
+ b, _ := json.Marshal(out, jsonx.V1())
return b, "", nil
}
diff --git a/services/cloudfront/persistence.go b/services/cloudfront/persistence.go
index 715b1c985a..1c1af6d512 100644
--- a/services/cloudfront/persistence.go
+++ b/services/cloudfront/persistence.go
@@ -2,11 +2,13 @@ package cloudfront
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -112,7 +114,7 @@ var dirtyTableNames = struct {
// registerAllTables doc for the clean/dirty split), merged into one map so a
// single Tables blob round-trips the whole backend.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
DistributionFunctionAssociations map[string][]FunctionAssociation `json:"distributionFunctionAssociations,omitempty"`
DistributionAliases map[string][]string `json:"distributionAliases,omitempty"`
@@ -199,7 +201,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.region,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "cloudfront: Snapshot marshal failure", "error", err)
@@ -277,7 +279,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// b.registry -- see store_setup.go's registerAllTables doc) from an ephemeral DTO
// [store.Registry] built from tables, converting each DTO back to a live
// Invalidation. Must be called with the lock held.
-func (b *InMemoryBackend) restoreDirtyTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTables(tables map[string]jsontext.Value) error {
dtoReg := store.NewRegistry()
invalidationDTOs := store.Register(dtoReg, dirtyTableNames.invalidations, store.New(invalidationSnapshotKey))
tenantInvalidationDTOs := store.Register(
@@ -550,7 +552,7 @@ func tableFrom[T any](snap *backendSnapshot, name string) []*T {
}
var out []*T
- if err := json.Unmarshal(raw, &out); err != nil {
+ if err := json.Unmarshal(raw, &out, jsonx.V1()); err != nil {
return nil
}
diff --git a/services/cloudfrontkeyvaluestore/handler.go b/services/cloudfrontkeyvaluestore/handler.go
index b8d025af7e..cd15e2b7c3 100644
--- a/services/cloudfrontkeyvaluestore/handler.go
+++ b/services/cloudfrontkeyvaluestore/handler.go
@@ -6,7 +6,7 @@
package cloudfrontkeyvaluestore
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"strconv"
@@ -16,6 +16,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -451,7 +452,7 @@ func (h *Handler) handlePutKey(c *echo.Context, kvs *cloudfrontbackend.KeyValueS
var req putKeyInput
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeAWSError(c, http.StatusBadRequest, exceptionValidation, "invalid JSON body")
}
}
@@ -549,7 +550,7 @@ func (h *Handler) handleUpdateKeys(c *echo.Context, kvs *cloudfrontbackend.KeyVa
var req updateKeysInput
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeAWSError(c, http.StatusBadRequest, exceptionValidation, "invalid JSON body")
}
}
@@ -656,7 +657,7 @@ func epochSeconds(rfc3339 string) float64 {
// typical REST delete, their outputs carry a body, so 204 No Content is not
// an option).
func writeJSON(c *echo.Context, v any) error {
- body, err := json.Marshal(v)
+ body, err := json.Marshal(v, jsonx.V1())
if err != nil {
return writeAWSError(c, http.StatusInternalServerError, exceptionInternalServer, "response marshal failed")
}
diff --git a/services/cloudhsmv2/backups.go b/services/cloudhsmv2/backups.go
index c87ed61208..13875e07e2 100644
--- a/services/cloudhsmv2/backups.go
+++ b/services/cloudhsmv2/backups.go
@@ -7,12 +7,13 @@ import (
"cmp"
"context"
- "encoding/json"
+ "encoding/json/v2"
"slices"
"strings"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -110,7 +111,7 @@ func policyGrants(policy, account string) bool {
Effect string `json:"Effect"`
} `json:"Statement"`
}
- if json.Unmarshal([]byte(policy), &doc) != nil {
+ if json.Unmarshal([]byte(policy), &doc, jsonx.V1()) != nil {
return false
}
diff --git a/services/cloudhsmv2/handler.go b/services/cloudhsmv2/handler.go
index c9c6c786e1..f178614d76 100644
--- a/services/cloudhsmv2/handler.go
+++ b/services/cloudhsmv2/handler.go
@@ -2,7 +2,7 @@ package cloudhsmv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"slices"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -36,7 +37,7 @@ func bind[I, O any](fn func(*Handler, context.Context, *I) (*O, error)) opFunc {
var in I
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fail(ErrInvalidRequest, "malformed request body: %v", err)
}
}
diff --git a/services/cloudhsmv2/persistence.go b/services/cloudhsmv2/persistence.go
index f3efa4b1ed..ba92833366 100644
--- a/services/cloudhsmv2/persistence.go
+++ b/services/cloudhsmv2/persistence.go
@@ -2,7 +2,7 @@ package cloudhsmv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,10 +12,10 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/cloudhsmv2/policies.go b/services/cloudhsmv2/policies.go
index 39a851ac7d..14d3087bc2 100644
--- a/services/cloudhsmv2/policies.go
+++ b/services/cloudhsmv2/policies.go
@@ -2,8 +2,11 @@ package cloudhsmv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (b *InMemoryBackend) policyBackupLocked(sc scope, resourceARN string) (*Backup, error) {
@@ -21,10 +24,10 @@ func (b *InMemoryBackend) policyBackupLocked(sc scope, resourceARN string) (*Bac
// PutResourcePolicy attaches a policy to a READY backup the caller owns.
func (b *InMemoryBackend) PutResourcePolicy(ctx context.Context, resourceARN, policy string) (string, error) {
var doc struct {
- Statement json.RawMessage `json:"Statement"`
+ Statement jsontext.Value `json:"Statement"`
}
- if policy == "" || json.Unmarshal([]byte(policy), &doc) != nil || len(doc.Statement) == 0 {
+ if policy == "" || json.Unmarshal([]byte(policy), &doc, jsonx.V1()) != nil || len(doc.Statement) == 0 {
return "", fail(ErrInvalidRequest, "Policy must be a JSON policy document with a Statement")
}
diff --git a/services/cloudtrail/delivery.go b/services/cloudtrail/delivery.go
index a9774446d5..19aa741590 100644
--- a/services/cloudtrail/delivery.go
+++ b/services/cloudtrail/delivery.go
@@ -4,7 +4,8 @@ import (
"bytes"
"compress/gzip"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strings"
"sync"
@@ -13,6 +14,8 @@ import (
"github.com/aws/aws-sdk-go-v2/aws"
sdk_s3 "github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// logFileRecords is the top-level shape of a CloudTrail log file: a single
@@ -20,7 +23,7 @@ import (
// User Guide "CloudTrail log file examples"), not part of the pinned SDK --
// see PARITY.md.
type logFileRecords struct {
- Records []json.RawMessage `json:"Records"`
+ Records []jsontext.Value `json:"Records"`
}
// deliveryTarget is a snapshot of the fields deliverLogFile needs from one
@@ -107,7 +110,7 @@ var gzipWriterPool = sync.Pool{ //nolint:gochecknoglobals // sync.Pool requires
// logFileBody gzip-compresses a single-record CloudTrail log file body.
func logFileBody(ev Event) ([]byte, error) {
encoded, err := json.Marshal(
- logFileRecords{Records: []json.RawMessage{json.RawMessage(ev.CloudTrailEvent)}},
+ logFileRecords{Records: []jsontext.Value{jsontext.Value(ev.CloudTrailEvent)}}, jsonx.V1(),
)
if err != nil {
return nil, err
diff --git a/services/cloudtrail/handler.go b/services/cloudtrail/handler.go
index 1c3ed6b8aa..59216625ce 100644
--- a/services/cloudtrail/handler.go
+++ b/services/cloudtrail/handler.go
@@ -1,7 +1,8 @@
package cloudtrail
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -9,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -184,9 +186,9 @@ func (h *Handler) Handler() echo.HandlerFunc {
var body []byte
if c.Request().Body != nil {
- decoder := json.NewDecoder(c.Request().Body)
- var raw json.RawMessage
- if err := decoder.Decode(&raw); err == nil {
+ decoder := jsontext.NewDecoder(c.Request().Body)
+ var raw jsontext.Value
+ if err := json.UnmarshalDecode(decoder, &raw, jsonx.V1()); err == nil {
body = raw
}
}
diff --git a/services/cloudtrail/handler_channels.go b/services/cloudtrail/handler_channels.go
index 664471deed..c783e5497f 100644
--- a/services/cloudtrail/handler_channels.go
+++ b/services/cloudtrail/handler_channels.go
@@ -1,11 +1,12 @@
package cloudtrail
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -27,7 +28,7 @@ type createChannelBody struct {
func (h *Handler) handleCreateChannel(c *echo.Context, body []byte) error {
var in createChannelBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -81,7 +82,7 @@ type deleteChannelBody struct {
func (h *Handler) handleDeleteChannel(c *echo.Context, body []byte) error {
var in deleteChannelBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -100,7 +101,7 @@ type getChannelBody struct {
func (h *Handler) handleGetChannel(c *echo.Context, body []byte) error {
var in getChannelBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -127,7 +128,7 @@ type updateChannelBody struct {
func (h *Handler) handleUpdateChannel(c *echo.Context, body []byte) error {
var in updateChannelBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -154,7 +155,7 @@ type listChannelsBody struct {
func (h *Handler) handleListChannels(c *echo.Context, body []byte) error {
var in listChannelsBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterCombinationException", "invalid request body"),
diff --git a/services/cloudtrail/handler_dashboards.go b/services/cloudtrail/handler_dashboards.go
index e6d3c9c8c3..284b4b545f 100644
--- a/services/cloudtrail/handler_dashboards.go
+++ b/services/cloudtrail/handler_dashboards.go
@@ -1,12 +1,13 @@
package cloudtrail
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -74,7 +75,7 @@ type createDashboardBody struct {
func (h *Handler) handleCreateDashboard(c *echo.Context, body []byte) error {
var in createDashboardBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -104,7 +105,7 @@ type deleteDashboardBody struct {
func (h *Handler) handleDeleteDashboard(c *echo.Context, body []byte) error {
var in deleteDashboardBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -123,7 +124,7 @@ type getDashboardBody struct {
func (h *Handler) handleGetDashboard(c *echo.Context, body []byte) error {
var in getDashboardBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -151,7 +152,7 @@ type updateDashboardBody struct {
func (h *Handler) handleUpdateDashboard(c *echo.Context, body []byte) error {
var in updateDashboardBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -180,7 +181,7 @@ type listDashboardsBody struct {
func (h *Handler) handleListDashboards(c *echo.Context, body []byte) error {
var in listDashboardsBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterCombinationException", "invalid request body"),
@@ -232,7 +233,7 @@ type startDashboardRefreshBody struct {
func (h *Handler) handleStartDashboardRefresh(c *echo.Context, body []byte) error {
var in startDashboardRefreshBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
diff --git a/services/cloudtrail/handler_event_data_stores.go b/services/cloudtrail/handler_event_data_stores.go
index 4e1112ebe5..e5314f57af 100644
--- a/services/cloudtrail/handler_event_data_stores.go
+++ b/services/cloudtrail/handler_event_data_stores.go
@@ -1,11 +1,12 @@
package cloudtrail
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -33,7 +34,7 @@ type createEventDataStoreBody struct {
func (h *Handler) handleCreateEventDataStore(c *echo.Context, body []byte) error {
var in createEventDataStoreBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -74,7 +75,7 @@ type deleteEventDataStoreBody struct {
func (h *Handler) handleDeleteEventDataStore(c *echo.Context, body []byte) error {
var in deleteEventDataStoreBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -93,7 +94,7 @@ type getEventDataStoreBody struct {
func (h *Handler) handleGetEventDataStore(c *echo.Context, body []byte) error {
var in getEventDataStoreBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -121,7 +122,7 @@ type updateEventDataStoreBody struct {
func (h *Handler) handleUpdateEventDataStore(c *echo.Context, body []byte) error {
var in updateEventDataStoreBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -150,7 +151,7 @@ type listEventDataStoresBody struct {
func (h *Handler) handleListEventDataStores(c *echo.Context, body []byte) error {
var in listEventDataStoresBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterCombinationException", "invalid request body"),
@@ -186,7 +187,7 @@ type restoreEventDataStoreBody struct {
func (h *Handler) handleRestoreEventDataStore(c *echo.Context, body []byte) error {
var in restoreEventDataStoreBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -206,7 +207,7 @@ type startEventDataStoreIngestionBody struct {
func (h *Handler) handleStartEventDataStoreIngestion(c *echo.Context, body []byte) error {
var in startEventDataStoreIngestionBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -225,7 +226,7 @@ type stopEventDataStoreIngestionBody struct {
func (h *Handler) handleStopEventDataStoreIngestion(c *echo.Context, body []byte) error {
var in stopEventDataStoreIngestionBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -244,7 +245,7 @@ type disableFederationBody struct {
func (h *Handler) handleDisableFederation(c *echo.Context, body []byte) error {
var in disableFederationBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -275,7 +276,7 @@ type enableFederationBody struct {
func (h *Handler) handleEnableFederation(c *echo.Context, body []byte) error {
var in enableFederationBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
diff --git a/services/cloudtrail/handler_event_selectors.go b/services/cloudtrail/handler_event_selectors.go
index 5e68e4ba3e..36fa093faa 100644
--- a/services/cloudtrail/handler_event_selectors.go
+++ b/services/cloudtrail/handler_event_selectors.go
@@ -1,12 +1,13 @@
package cloudtrail
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
)
@@ -53,7 +54,7 @@ type putEventSelectorsBody struct {
func (h *Handler) handlePutEventSelectors(c *echo.Context, body []byte) error {
var in putEventSelectorsBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -95,7 +96,7 @@ type getEventSelectorsBody struct {
func (h *Handler) handleGetEventSelectors(c *echo.Context, body []byte) error {
var in getEventSelectorsBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -133,7 +134,7 @@ type putInsightSelectorsBody struct {
func (h *Handler) handlePutInsightSelectors(c *echo.Context, body []byte) error {
var in putInsightSelectorsBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -182,7 +183,7 @@ type getInsightSelectorsBody struct {
func (h *Handler) handleGetInsightSelectors(c *echo.Context, body []byte) error {
var in getInsightSelectorsBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -229,7 +230,7 @@ type getEventConfigurationBody struct {
func (h *Handler) handleGetEventConfiguration(c *echo.Context, body []byte) error {
var in getEventConfigurationBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -255,7 +256,7 @@ type putEventConfigurationBody struct {
func (h *Handler) handlePutEventConfiguration(c *echo.Context, body []byte) error {
var in putEventConfigurationBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -336,7 +337,7 @@ type listInsightsDataBody struct {
func (h *Handler) handleListInsightsData(c *echo.Context, body []byte) error {
var in listInsightsDataBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterCombinationException", "invalid request body"),
@@ -382,7 +383,7 @@ type listInsightsMetricDataBody struct {
func (h *Handler) handleListInsightsMetricData(c *echo.Context, body []byte) error {
var in listInsightsMetricDataBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
if in.EventName == "" {
diff --git a/services/cloudtrail/handler_events.go b/services/cloudtrail/handler_events.go
index 72354ef6bd..6018e849bb 100644
--- a/services/cloudtrail/handler_events.go
+++ b/services/cloudtrail/handler_events.go
@@ -1,7 +1,7 @@
package cloudtrail
import (
- "encoding/json"
+ "encoding/json/v2"
"math"
"net/http"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- LookupEvents ---
@@ -25,7 +26,7 @@ type lookupEventsBody struct {
func (h *Handler) handleLookupEvents(c *echo.Context, body []byte) error {
var in lookupEventsBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterCombinationException", "invalid request body"),
diff --git a/services/cloudtrail/handler_imports.go b/services/cloudtrail/handler_imports.go
index c090c0fb80..fcadc39111 100644
--- a/services/cloudtrail/handler_imports.go
+++ b/services/cloudtrail/handler_imports.go
@@ -1,7 +1,7 @@
package cloudtrail
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -65,7 +66,7 @@ func (in startImportBody) toBackendImportSource() *ImportSource {
func (h *Handler) handleStartImport(c *echo.Context, body []byte) error {
var in startImportBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -101,7 +102,7 @@ type getImportBody struct {
func (h *Handler) handleGetImport(c *echo.Context, body []byte) error {
var in getImportBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -125,7 +126,7 @@ type listImportsBody struct {
func (h *Handler) handleListImports(c *echo.Context, body []byte) error {
var in listImportsBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterCombinationException", "invalid request body"),
@@ -182,7 +183,7 @@ type stopImportBody struct {
func (h *Handler) handleStopImport(c *echo.Context, body []byte) error {
var in stopImportBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -233,7 +234,7 @@ type listImportFailuresBody struct {
func (h *Handler) handleListImportFailures(c *echo.Context, body []byte) error {
var in listImportFailuresBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
diff --git a/services/cloudtrail/handler_queries.go b/services/cloudtrail/handler_queries.go
index c6900281dc..ffbc9b79ac 100644
--- a/services/cloudtrail/handler_queries.go
+++ b/services/cloudtrail/handler_queries.go
@@ -1,13 +1,14 @@
package cloudtrail
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"time"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -36,7 +37,7 @@ type startQueryBody struct {
func (h *Handler) handleStartQuery(c *echo.Context, body []byte) error {
var in startQueryBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -87,7 +88,7 @@ type cancelQueryBody struct {
func (h *Handler) handleCancelQuery(c *echo.Context, body []byte) error {
var in cancelQueryBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -125,7 +126,7 @@ type describeQueryBody struct {
func (h *Handler) handleDescribeQuery(c *echo.Context, body []byte) error {
var in describeQueryBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -188,7 +189,7 @@ type getQueryResultsBody struct {
func (h *Handler) handleGetQueryResults(c *echo.Context, body []byte) error {
var in getQueryResultsBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -241,7 +242,7 @@ type listQueriesBody struct {
func (h *Handler) handleListQueries(c *echo.Context, body []byte) error {
var in listQueriesBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterCombinationException", "invalid request body"),
@@ -329,7 +330,7 @@ type generateQueryBody struct {
func (h *Handler) handleGenerateQuery(c *echo.Context, body []byte) error {
var in generateQueryBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
diff --git a/services/cloudtrail/handler_resource_policies.go b/services/cloudtrail/handler_resource_policies.go
index a7f93d7db4..ef84552a63 100644
--- a/services/cloudtrail/handler_resource_policies.go
+++ b/services/cloudtrail/handler_resource_policies.go
@@ -1,10 +1,12 @@
package cloudtrail
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- DeleteResourcePolicy ---
@@ -15,7 +17,7 @@ type deleteResourcePolicyBody struct {
func (h *Handler) handleDeleteResourcePolicy(c *echo.Context, body []byte) error {
var in deleteResourcePolicyBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -34,7 +36,7 @@ type getResourcePolicyBody struct {
func (h *Handler) handleGetResourcePolicy(c *echo.Context, body []byte) error {
var in getResourcePolicyBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -58,7 +60,7 @@ type putResourcePolicyBody struct {
func (h *Handler) handlePutResourcePolicy(c *echo.Context, body []byte) error {
var in putResourcePolicyBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
diff --git a/services/cloudtrail/handler_tags.go b/services/cloudtrail/handler_tags.go
index 44325dafa7..747dd87297 100644
--- a/services/cloudtrail/handler_tags.go
+++ b/services/cloudtrail/handler_tags.go
@@ -1,10 +1,12 @@
package cloudtrail
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- AddTags ---
@@ -19,7 +21,7 @@ type addTagsBody struct {
func (h *Handler) handleAddTags(c *echo.Context, body []byte) error {
var in addTagsBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -46,7 +48,7 @@ type removeTagsBody struct {
func (h *Handler) handleRemoveTags(c *echo.Context, body []byte) error {
var in removeTagsBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -70,7 +72,7 @@ type listTagsBody struct {
func (h *Handler) handleListTags(c *echo.Context, body []byte) error {
var in listTagsBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
diff --git a/services/cloudtrail/handler_test.go b/services/cloudtrail/handler_test.go
index bf4f236574..71fd921b31 100644
--- a/services/cloudtrail/handler_test.go
+++ b/services/cloudtrail/handler_test.go
@@ -2,7 +2,8 @@ package cloudtrail_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/cloudtrail"
)
@@ -34,7 +36,7 @@ func doCloudTrailOp(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -55,7 +57,7 @@ func parseCloudTrailResp(t *testing.T, rec *httptest.ResponseRecorder) map[strin
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
@@ -535,7 +537,7 @@ func TestCloudTrailListOperationsSmoke(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code, "op %s should return 200", op)
var m map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&m))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &m, jsonx.V1()))
}
// ListQueries takes EventDataStore as required input (real
@@ -551,7 +553,7 @@ func TestCloudTrailListOperationsSmoke(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code, "op ListQueries should return 200")
var m map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&m))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &m, jsonx.V1()))
}
// TestCloudTrailAncillaryOperationsSmoke covers ancillary read-only handlers
diff --git a/services/cloudtrail/handler_trails.go b/services/cloudtrail/handler_trails.go
index ed8f58559a..60bf5429e8 100644
--- a/services/cloudtrail/handler_trails.go
+++ b/services/cloudtrail/handler_trails.go
@@ -1,12 +1,13 @@
package cloudtrail
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -44,7 +45,7 @@ type createTrailBody struct {
func (h *Handler) handleCreateTrail(c *echo.Context, body []byte) error {
var in createTrailBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -87,7 +88,7 @@ type getTrailBody struct {
func (h *Handler) handleGetTrail(c *echo.Context, body []byte) error {
var in getTrailBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -109,7 +110,7 @@ type describeTrailsBody struct {
func (h *Handler) handleDescribeTrails(c *echo.Context, body []byte) error {
var in describeTrailsBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterCombinationException", "invalid request body"),
@@ -144,7 +145,7 @@ type updateTrailBody struct {
func (h *Handler) handleUpdateTrail(c *echo.Context, body []byte) error {
var in updateTrailBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -176,7 +177,7 @@ type deleteTrailBody struct {
func (h *Handler) handleDeleteTrail(c *echo.Context, body []byte) error {
var in deleteTrailBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -195,7 +196,7 @@ type startLoggingBody struct {
func (h *Handler) handleStartLogging(c *echo.Context, body []byte) error {
var in startLoggingBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -218,7 +219,7 @@ type stopLoggingBody struct {
func (h *Handler) handleStopLogging(c *echo.Context, body []byte) error {
var in stopLoggingBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -241,7 +242,7 @@ type getTrailStatusBody struct {
func (h *Handler) handleGetTrailStatus(c *echo.Context, body []byte) error {
var in getTrailStatusBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -277,7 +278,7 @@ type listTrailsBody struct {
func (h *Handler) handleListTrails(c *echo.Context, body []byte) error {
var in listTrailsBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("InvalidParameterCombinationException", "invalid request body"),
@@ -313,7 +314,7 @@ type deregisterOrgDelegatedAdminBody struct {
func (h *Handler) handleDeregisterOrganizationDelegatedAdmin(c *echo.Context, body []byte) error {
var in deregisterOrgDelegatedAdminBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
@@ -332,7 +333,7 @@ type registerOrgDelegatedAdminBody struct {
func (h *Handler) handleRegisterOrganizationDelegatedAdmin(c *echo.Context, body []byte) error {
var in registerOrgDelegatedAdminBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterCombinationException", "invalid request body"))
}
diff --git a/services/cloudtrail/insights_metrics.go b/services/cloudtrail/insights_metrics.go
index acbd6db797..5e3989f2e3 100644
--- a/services/cloudtrail/insights_metrics.go
+++ b/services/cloudtrail/insights_metrics.go
@@ -1,11 +1,13 @@
package cloudtrail
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strconv"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -80,7 +82,7 @@ func eventErrorCode(ev Event) string {
ErrorCode string `json:"errorCode"`
}
- if ev.CloudTrailEvent == "" || json.Unmarshal([]byte(ev.CloudTrailEvent), &rec) != nil {
+ if ev.CloudTrailEvent == "" || json.Unmarshal([]byte(ev.CloudTrailEvent), &rec, jsonx.V1()) != nil {
return ""
}
diff --git a/services/cloudtrail/management_event.go b/services/cloudtrail/management_event.go
index c4870507c9..18b93a1bc6 100644
--- a/services/cloudtrail/management_event.go
+++ b/services/cloudtrail/management_event.go
@@ -1,12 +1,13 @@
package cloudtrail
import (
- "encoding/json"
+ "encoding/json/v2"
"strconv"
"time"
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -126,7 +127,7 @@ func (b *InMemoryBackend) RecordManagementEvent(ev service.CloudTrailEventInput)
// common case where every string is plain printable ASCII needing no escaping.
func renderManagementEvent(d *managementEventDetail) string {
if !d.plainStrings() {
- blob, err := json.Marshal(d)
+ blob, err := json.Marshal(d, jsonx.V1())
if err != nil {
return ""
}
diff --git a/services/cloudtrail/management_event_render_whitebox_test.go b/services/cloudtrail/management_event_render_whitebox_test.go
index 180805f302..00aa993623 100644
--- a/services/cloudtrail/management_event_render_whitebox_test.go
+++ b/services/cloudtrail/management_event_render_whitebox_test.go
@@ -1,10 +1,12 @@
package cloudtrail
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestRenderManagementEventMatchesJSONMarshal(t *testing.T) {
@@ -56,7 +58,7 @@ func TestRenderManagementEventMatchesJSONMarshal(t *testing.T) {
d := base
tt.mutate(&d)
- want, err := json.Marshal(d)
+ want, err := json.Marshal(d, jsonx.V1())
require.NoError(t, err)
require.Equal(t, string(want), renderManagementEvent(&d))
})
diff --git a/services/cloudtrail/models.go b/services/cloudtrail/models.go
index c42ae1818b..50311a9d2b 100644
--- a/services/cloudtrail/models.go
+++ b/services/cloudtrail/models.go
@@ -1,10 +1,11 @@
package cloudtrail
import (
- "encoding/json"
+ "encoding/json/v2"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -75,7 +76,7 @@ func (e Event) MarshalJSON() ([]byte, error) {
}{
alias: alias(e),
EventTime: awstime.Epoch(e.EventTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of MarshalJSON: it decodes the epoch-seconds
@@ -92,7 +93,7 @@ func (e *Event) UnmarshalJSON(data []byte) error {
EventTime float64 `json:"EventTime"`
}{alias: (*alias)(e)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/cloudtrail/multi_region_test.go b/services/cloudtrail/multi_region_test.go
index bdf01dc7f4..f32c31125b 100644
--- a/services/cloudtrail/multi_region_test.go
+++ b/services/cloudtrail/multi_region_test.go
@@ -2,7 +2,7 @@ package cloudtrail_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/cloudtrail"
)
@@ -42,7 +43,7 @@ func decode(t *testing.T, body string) map[string]any {
out := map[string]any{}
if body != "" {
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
}
return out
diff --git a/services/cloudtrail/persistence.go b/services/cloudtrail/persistence.go
index 4d6b3990fd..77911dc1be 100644
--- a/services/cloudtrail/persistence.go
+++ b/services/cloudtrail/persistence.go
@@ -2,7 +2,7 @@ package cloudtrail
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -30,7 +30,7 @@ const cloudtrailSnapshotVersion = 1
// decoding a snapshot from an incompatible (older or newer) build of this
// backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
EventConfigs map[string]*EventConfiguration `json:"eventConfigs,omitempty"`
AccountID string `json:"accountID"`
Region string `json:"region"`
diff --git a/services/cloudtrail/query_exec.go b/services/cloudtrail/query_exec.go
index 2cb1b7eec2..0cc89fc4d4 100644
--- a/services/cloudtrail/query_exec.go
+++ b/services/cloudtrail/query_exec.go
@@ -2,13 +2,15 @@ package cloudtrail
import (
"cmp"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"slices"
"sort"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// This file implements a bounded, honest subset of CloudTrail Lake SQL
@@ -96,7 +98,7 @@ func eventToRow(ev Event) map[string]string {
if ev.CloudTrailEvent != "" {
var detail map[string]any
- if err := json.Unmarshal([]byte(ev.CloudTrailEvent), &detail); err == nil {
+ if err := json.Unmarshal([]byte(ev.CloudTrailEvent), &detail, jsonx.V1()); err == nil {
flattenJSONInto(row, "", detail)
}
}
@@ -126,7 +128,7 @@ func flattenJSONInto(row map[string]string, prefix string, obj map[string]any) {
// missing/null field.
default:
if _, exists := row[key]; !exists {
- if b, err := json.Marshal(tv); err == nil {
+ if b, err := json.Marshal(tv, jsonx.V1()); err == nil {
row[key] = string(b)
}
}
diff --git a/services/cloudtrail/s3_delivery_test.go b/services/cloudtrail/s3_delivery_test.go
index 50f71ceeae..a1596762db 100644
--- a/services/cloudtrail/s3_delivery_test.go
+++ b/services/cloudtrail/s3_delivery_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"compress/gzip"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/cloudtrail"
)
@@ -252,7 +253,7 @@ func TestRecordManagementEvent_DeliversLogFileToS3(t *testing.T) {
var records struct {
Records []map[string]any `json:"Records"`
}
- require.NoError(t, json.Unmarshal(decoded, &records))
+ require.NoError(t, json.Unmarshal(decoded, &records, jsonx.V1()))
require.Len(t, records.Records, 1)
assert.Equal(t, "CreateBucket", records.Records[0]["eventName"])
}
diff --git a/services/cloudwatch/alarm_actions.go b/services/cloudwatch/alarm_actions.go
index 10ae72f587..562af00410 100644
--- a/services/cloudwatch/alarm_actions.go
+++ b/services/cloudwatch/alarm_actions.go
@@ -2,12 +2,13 @@ package cloudwatch
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
"time"
arnpkg "github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -53,7 +54,7 @@ func (b *InMemoryBackend) buildAlarmActionPayload(
"StateChangeTime": time.Now().UTC().Format(time.RFC3339),
}
// map[string]string marshaling cannot fail; error is intentionally ignored.
- bs, _ := json.Marshal(data)
+ bs, _ := json.Marshal(data, jsonx.V1())
return bs
}
diff --git a/services/cloudwatch/alarm_history.go b/services/cloudwatch/alarm_history.go
index 93014f2eb4..8f73087bac 100644
--- a/services/cloudwatch/alarm_history.go
+++ b/services/cloudwatch/alarm_history.go
@@ -1,10 +1,11 @@
package cloudwatch
import (
- "encoding/json"
+ "encoding/json/v2"
"sort"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -182,7 +183,7 @@ func stateChangeHistoryData(oldState, newState, reason, reasonData string) strin
}
if reasonData != "" {
var parsed any
- if json.Unmarshal([]byte(reasonData), &parsed) == nil {
+ if json.Unmarshal([]byte(reasonData), &parsed, jsonx.V1()) == nil {
next["stateReasonData"] = parsed
}
}
@@ -191,7 +192,7 @@ func stateChangeHistoryData(oldState, newState, reason, reasonData string) strin
"version": "1.0",
"oldState": map[string]any{"stateValue": oldState},
"newState": next,
- })
+ }, jsonx.V1())
return string(bs)
}
diff --git a/services/cloudwatch/alarm_reason.go b/services/cloudwatch/alarm_reason.go
index 575738d9d7..762f289da2 100644
--- a/services/cloudwatch/alarm_reason.go
+++ b/services/cloudwatch/alarm_reason.go
@@ -1,12 +1,14 @@
package cloudwatch
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -212,7 +214,7 @@ func alarmReasonData(a MetricAlarm, d alarmEvalDetail, now time.Time) string {
"evaluatedDatapoints": evaluated,
}
- bs, err := json.Marshal(data)
+ bs, err := json.Marshal(data, jsonx.V1())
if err != nil {
return ""
}
diff --git a/services/cloudwatch/alarm_validation_sdk_test.go b/services/cloudwatch/alarm_validation_sdk_test.go
index b468cfd6a8..de91b43197 100644
--- a/services/cloudwatch/alarm_validation_sdk_test.go
+++ b/services/cloudwatch/alarm_validation_sdk_test.go
@@ -1,7 +1,8 @@
package cloudwatch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strconv"
"strings"
"testing"
@@ -14,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatch"
)
@@ -76,7 +78,7 @@ func TestSDK_AlarmStateTransitionRealism(t *testing.T) {
got := out.MetricAlarms[0]
assert.Equal(t, tt.wantState, got.StateValue)
assert.Contains(t, aws.ToString(got.StateReason), tt.wantReason)
- assert.True(t, json.Valid([]byte(aws.ToString(got.StateReasonData))))
+ assert.True(t, jsontext.Value([]byte(aws.ToString(got.StateReasonData))).IsValid(jsonx.V1()))
putBreachAlarm(t, client, "a", 6)
out, err = client.DescribeAlarms(t.Context(), &cwsdk.DescribeAlarmsInput{})
@@ -96,7 +98,10 @@ func TestSDK_AlarmStateTransitionRealism(t *testing.T) {
StateValue string `json:"stateValue"`
} `json:"newState"`
}
- require.NoError(t, json.Unmarshal([]byte(aws.ToString(hist.AlarmHistoryItems[0].HistoryData)), &data))
+ require.NoError(
+ t,
+ json.Unmarshal([]byte(aws.ToString(hist.AlarmHistoryItems[0].HistoryData)), &data, jsonx.V1()),
+ )
assert.Equal(t, string(tt.wantState), data.NewState.StateValue)
})
}
diff --git a/services/cloudwatch/anomaly_detectors.go b/services/cloudwatch/anomaly_detectors.go
index 103bcacca4..ccb213f0a4 100644
--- a/services/cloudwatch/anomaly_detectors.go
+++ b/services/cloudwatch/anomaly_detectors.go
@@ -3,7 +3,7 @@ package cloudwatch
import (
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"math"
"slices"
@@ -12,6 +12,7 @@ import (
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -27,7 +28,7 @@ func detectorKey(d *AnomalyDetector) string {
return anomalyDetectorKey(d.Namespace, d.MetricName, d.Stat, d.Dimensions)
}
- raw, err := json.Marshal(d.MetricMath)
+ raw, err := json.Marshal(d.MetricMath, jsonx.V1())
if err != nil {
return "math/" + d.ID
}
diff --git a/services/cloudwatch/contributor_insights_sdk_test.go b/services/cloudwatch/contributor_insights_sdk_test.go
index 19503121fd..9f1a5f2c30 100644
--- a/services/cloudwatch/contributor_insights_sdk_test.go
+++ b/services/cloudwatch/contributor_insights_sdk_test.go
@@ -3,7 +3,7 @@ package cloudwatch_test
import (
"cmp"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"testing"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatch"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -431,7 +432,7 @@ func TestInsightRule_PutValidation(t *testing.T) {
fiveFilters := jsonRule(`"$.a"`, `,"Filters":[`+
`{"Match":"$.a","IsPresent":true},{"Match":"$.b","IsPresent":true},{"Match":"$.c","IsPresent":true},`+
`{"Match":"$.d","IsPresent":true},{"Match":"$.e","IsPresent":true}]`) + `}`
- elevenValues, err := json.Marshal([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"})
+ elevenValues, err := json.Marshal([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}, jsonx.V1())
require.NoError(t, err)
invalid := &cwtypes.InvalidParameterValueException{}
diff --git a/services/cloudwatch/dashboard_validation.go b/services/cloudwatch/dashboard_validation.go
index dcd1d76bd1..6955e8e67c 100644
--- a/services/cloudwatch/dashboard_validation.go
+++ b/services/cloudwatch/dashboard_validation.go
@@ -1,9 +1,12 @@
package cloudwatch
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// knownDashboardWidgetTypes are the CloudWatch dashboard widget "type" values
@@ -57,10 +60,10 @@ func validateDashboardBody(body string) []DashboardValidationMessage {
}}
}
- dec := json.NewDecoder(strings.NewReader(trimmed))
+ dec := jsontext.NewDecoder(strings.NewReader(trimmed), jsonx.V1())
var raw any
- if err := dec.Decode(&raw); err != nil {
+ if err := json.UnmarshalDecode(dec, &raw, jsonx.V1()); err != nil {
return []DashboardValidationMessage{{
DataPath: "/",
Message: fmt.Sprintf("The dashboard body is not valid JSON: %s", err.Error()),
@@ -68,7 +71,11 @@ func validateDashboardBody(body string) []DashboardValidationMessage {
}}
}
- if dec.More() {
+ if rest := strings.TrimLeft(
+ trimmed[dec.InputOffset():],
+ " \t\r\n",
+ ); rest != "" && rest[0] != ']' &&
+ rest[0] != '}' {
return []DashboardValidationMessage{{
DataPath: "/",
Message: "The dashboard body contains trailing content after the JSON value",
diff --git a/services/cloudwatch/firehose_interop_test.go b/services/cloudwatch/firehose_interop_test.go
index 1f8cadd450..3cf5c687b5 100644
--- a/services/cloudwatch/firehose_interop_test.go
+++ b/services/cloudwatch/firehose_interop_test.go
@@ -2,7 +2,7 @@ package cloudwatch_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"sync"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatch"
"github.com/blackbirdworks/gopherstack/services/firehose"
)
@@ -110,7 +111,7 @@ func TestMetricStream_EndToEnd_RealFirehoseBackend(t *testing.T) {
body := s3mock.captured()[0]
var rec map[string]any
- require.NoError(t, json.Unmarshal([]byte(firstLine(body)), &rec))
+ require.NoError(t, json.Unmarshal([]byte(firstLine(body)), &rec, jsonx.V1()))
assert.Equal(t, "CPUUtilization", rec["metric_name"])
assert.Equal(t, "AWS/EC2", rec["namespace"])
}
diff --git a/services/cloudwatch/insight_rule_eval.go b/services/cloudwatch/insight_rule_eval.go
index f5a9470a4b..d352eb62f2 100644
--- a/services/cloudwatch/insight_rule_eval.go
+++ b/services/cloudwatch/insight_rule_eval.go
@@ -2,11 +2,14 @@ package cloudwatch
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"math"
"slices"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// fieldLookup returns the leaf value at a rule field path in one log event.
@@ -27,11 +30,10 @@ func (s *insightRuleSpec) lookupFor(msg string) (fieldLookup, bool) {
}, true
}
- dec := json.NewDecoder(bytes.NewReader([]byte(msg)))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader([]byte(msg)))
var root map[string]any
- if err := dec.Decode(&root); err != nil || root == nil {
+ if err := json.UnmarshalDecode(dec, &root, jsonx.V1(), jsonx.UseNumber()); err != nil || root == nil {
return nil, false
}
@@ -69,7 +71,7 @@ func jsonLeaf(root any, path string) (string, bool) {
switch v := cur.(type) {
case string:
return v, true
- case json.Number:
+ case jsonx.Number:
return v.String(), true
case bool:
return strconv.FormatBool(v), true
diff --git a/services/cloudwatch/insight_rule_spec.go b/services/cloudwatch/insight_rule_spec.go
index 5bd476adaf..b421eb3727 100644
--- a/services/cloudwatch/insight_rule_spec.go
+++ b/services/cloudwatch/insight_rule_spec.go
@@ -1,11 +1,13 @@
package cloudwatch
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -49,7 +51,7 @@ type insightRuleSpec struct {
func parseInsightRuleSpec(definition string) (*insightRuleSpec, error) {
var spec insightRuleSpec
- if err := json.Unmarshal([]byte(definition), &spec); err != nil {
+ if err := json.Unmarshal([]byte(definition), &spec, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: RuleDefinition has an invalid field type: %s", ErrValidation, err.Error())
}
diff --git a/services/cloudwatch/insight_rule_validation.go b/services/cloudwatch/insight_rule_validation.go
index d2a142976a..97d14aa7ee 100644
--- a/services/cloudwatch/insight_rule_validation.go
+++ b/services/cloudwatch/insight_rule_validation.go
@@ -1,9 +1,11 @@
package cloudwatch
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// maxContributionKeys is Contributor Insights' documented limit on
@@ -26,7 +28,7 @@ func validateInsightRuleDefinition(definition string) error {
}
var raw any
- if err := json.Unmarshal([]byte(trimmed), &raw); err != nil {
+ if err := json.Unmarshal([]byte(trimmed), &raw, jsonx.V1()); err != nil {
return fmt.Errorf("%w: RuleDefinition is not valid JSON: %s", ErrValidation, err.Error())
}
diff --git a/services/cloudwatch/json_target.go b/services/cloudwatch/json_target.go
index ba9562bc39..5cb7d12745 100644
--- a/services/cloudwatch/json_target.go
+++ b/services/cloudwatch/json_target.go
@@ -3,13 +3,16 @@ package cloudwatch
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"math"
"net/http"
"strings"
"github.com/aws/smithy-go/encoding/cbor"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const jsonContentTypePrefix = "application/x-amz-json"
@@ -47,11 +50,10 @@ func (h *Handler) dispatchJSONTarget(op string, body []byte, c *echo.Context) er
input := cbor.Map{}
if len(bytes.TrimSpace(body)) > 0 {
- dec := json.NewDecoder(bytes.NewReader(body))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(body))
var raw any
- if err := dec.Decode(&raw); err != nil {
+ if err := json.UnmarshalDecode(dec, &raw, jsonx.V1(), jsonx.UseNumber()); err != nil {
return writeJSONError(c, http.StatusBadRequest, "SerializationException", err.Error())
}
@@ -100,7 +102,7 @@ func writeBufferedAsJSON(c *echo.Context, buf *bufferedResponse) error {
}
func writeJSON(c *echo.Context, status int, v any) error {
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return err
}
@@ -138,14 +140,14 @@ func jsonToCBOR(v any) cbor.Value {
return cbor.String(t)
case bool:
return cbor.Bool(t)
- case json.Number:
+ case jsonx.Number:
return jsonNumberToCBOR(t)
default:
return &cbor.Nil{}
}
}
-func jsonNumberToCBOR(n json.Number) cbor.Value {
+func jsonNumberToCBOR(n jsonx.Number) cbor.Value {
if i, err := n.Int64(); err == nil {
if i >= 0 {
return cbor.Uint(i)
diff --git a/services/cloudwatch/json_target_test.go b/services/cloudwatch/json_target_test.go
index 7476b280f0..8699a20ce9 100644
--- a/services/cloudwatch/json_target_test.go
+++ b/services/cloudwatch/json_target_test.go
@@ -3,7 +3,7 @@ package cloudwatch_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -69,7 +70,7 @@ func TestJSONTarget_RoundTrip(t *testing.T) {
MetricName string `json:"MetricName"`
} `json:"Metrics"`
}
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
require.Len(t, out.Metrics, 1)
assert.Equal(t, "M", out.Metrics[0].MetricName)
},
@@ -90,7 +91,7 @@ func TestJSONTarget_RoundTrip(t *testing.T) {
Values []float64 `json:"Values"`
} `json:"MetricDataResults"`
}
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
require.Len(t, out.MetricDataResults, 1)
assert.Equal(t, []float64{-2.5}, out.MetricDataResults[0].Values)
assert.Equal(t, []float64{float64(ts)}, out.MetricDataResults[0].Timestamps)
@@ -119,7 +120,7 @@ func TestJSONTarget_RoundTrip(t *testing.T) {
}
func epochStr(n int64) string {
- b, _ := json.Marshal(n)
+ b, _ := json.Marshal(n, jsonx.V1())
return string(b)
}
diff --git a/services/cloudwatch/metric_stream_delivery.go b/services/cloudwatch/metric_stream_delivery.go
index 38d0af6f47..bb2ecf7af3 100644
--- a/services/cloudwatch/metric_stream_delivery.go
+++ b/services/cloudwatch/metric_stream_delivery.go
@@ -2,9 +2,10 @@ package cloudwatch
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -105,7 +106,7 @@ func buildMetricStreamRecords(
Unit: d.Unit,
}
- body, err := json.Marshal(rec)
+ body, err := json.Marshal(rec, jsonx.V1())
if err != nil {
continue
}
diff --git a/services/cloudwatch/metric_stream_delivery_test.go b/services/cloudwatch/metric_stream_delivery_test.go
index 64669e3598..d45100dc18 100644
--- a/services/cloudwatch/metric_stream_delivery_test.go
+++ b/services/cloudwatch/metric_stream_delivery_test.go
@@ -2,13 +2,15 @@ package cloudwatch_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// mockFirehosePutter captures PutRecordBatch calls for assertions.
@@ -111,7 +113,7 @@ func TestMetricStream_DeliversToFirehose(t *testing.T) {
require.Len(t, calls[0].records, 1)
var rec2 map[string]any
- require.NoError(t, json.Unmarshal(calls[0].records[0], &rec2))
+ require.NoError(t, json.Unmarshal(calls[0].records[0], &rec2, jsonx.V1()))
assert.Equal(t, tt.wantMetricName, rec2["metric_name"])
assert.Equal(t, "AWS/EC2", rec2["namespace"])
value := rec2["value"].(map[string]any)
diff --git a/services/cloudwatch/metricdata.go b/services/cloudwatch/metricdata.go
index 110821c7cd..ab045d826a 100644
--- a/services/cloudwatch/metricdata.go
+++ b/services/cloudwatch/metricdata.go
@@ -2,12 +2,14 @@ package cloudwatch
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"math"
"sort"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -36,7 +38,7 @@ type metricDataCursor struct {
// encodeMetricDataToken encodes a pagination cursor into an opaque base64 token.
func encodeMetricDataToken(c metricDataCursor) string {
- raw, err := json.Marshal(c)
+ raw, err := json.Marshal(c, jsonx.V1())
if err != nil {
return ""
}
@@ -59,7 +61,7 @@ func validateMetricDataWindow(startTime, endTime time.Time, nextToken string) er
}
var c metricDataCursor
- if err = json.Unmarshal(raw, &c); err != nil || c.ResultIndex < 0 || c.PointOffset < 0 {
+ if err = json.Unmarshal(raw, &c, jsonx.V1()); err != nil || c.ResultIndex < 0 || c.PointOffset < 0 {
return ErrInvalidNextToken
}
@@ -79,7 +81,7 @@ func decodeMetricDataToken(token string) metricDataCursor {
}
var c metricDataCursor
- if err = json.Unmarshal(raw, &c); err != nil {
+ if err = json.Unmarshal(raw, &c, jsonx.V1()); err != nil {
return metricDataCursor{}
}
diff --git a/services/cloudwatch/persistence.go b/services/cloudwatch/persistence.go
index 190556775a..bc021588d7 100644
--- a/services/cloudwatch/persistence.go
+++ b/services/cloudwatch/persistence.go
@@ -2,9 +2,11 @@ package cloudwatch
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
@@ -21,7 +23,7 @@ import (
const cloudwatchSnapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Metrics map[string]map[string]*metricRecord `json:"metrics"`
AlarmHistory map[string][]AlarmHistoryItem `json:"alarmHistory"`
AccountID string `json:"accountID"`
@@ -171,7 +173,7 @@ func (h *Handler) Snapshot(ctx context.Context) []byte {
}
h.tagsMu.RUnlock()
- data, err := json.Marshal(handlerSnapshot{Backend: backendData, Tags: tagsCopy})
+ data, err := json.Marshal(handlerSnapshot{Backend: backendData, Tags: tagsCopy}, jsonx.V1())
if err != nil {
return backendData
}
@@ -196,7 +198,7 @@ func (h *Handler) Restore(ctx context.Context, data []byte) error {
func (h *Handler) restoreHome(ctx context.Context, data []byte) error {
// Attempt to unmarshal as a handlerSnapshot first.
var hs handlerSnapshot
- if unmarshalErr := json.Unmarshal(data, &hs); unmarshalErr == nil && hs.Backend != nil {
+ if unmarshalErr := json.Unmarshal(data, &hs, jsonx.V1()); unmarshalErr == nil && hs.Backend != nil {
type restorer interface {
Restore(context.Context, []byte) error
}
diff --git a/services/cloudwatch/persistence_test.go b/services/cloudwatch/persistence_test.go
index bb7bf1db55..80f13224ee 100644
--- a/services/cloudwatch/persistence_test.go
+++ b/services/cloudwatch/persistence_test.go
@@ -1,7 +1,7 @@
package cloudwatch_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatch"
)
@@ -625,9 +626,9 @@ func TestInMemoryBackend_Restore_VersionGuard(t *testing.T) {
// Corrupt the version field to simulate an incompatible (older/newer) snapshot.
var raw map[string]any
- require.NoError(t, json.Unmarshal(snap, &raw))
+ require.NoError(t, json.Unmarshal(snap, &raw, jsonx.V1()))
raw["version"] = 999999
- corrupted, err := json.Marshal(raw)
+ corrupted, err := json.Marshal(raw, jsonx.V1())
require.NoError(t, err)
// Restore into a backend that already has unrelated state, to confirm the
diff --git a/services/cloudwatch/widget.go b/services/cloudwatch/widget.go
index 3e765a7feb..1aecd84050 100644
--- a/services/cloudwatch/widget.go
+++ b/services/cloudwatch/widget.go
@@ -2,13 +2,16 @@ package cloudwatch
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"image/png"
"math"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Widget rendering constants.
@@ -40,14 +43,14 @@ const (
// widgetSpec is the decoded MetricWidget JSON document.
type widgetSpec struct {
- Title string `json:"title"`
- Start string `json:"start"`
- End string `json:"end"`
- Stat string `json:"stat"`
- Metrics [][]json.RawMessage `json:"metrics"`
- Width int `json:"width"`
- Height int `json:"height"`
- Period int `json:"period"`
+ Title string `json:"title"`
+ Start string `json:"start"`
+ End string `json:"end"`
+ Stat string `json:"stat"`
+ Metrics [][]jsontext.Value `json:"metrics"`
+ Width int `json:"width"`
+ Height int `json:"height"`
+ Period int `json:"period"`
}
// widgetSeriesQuery describes a single metric line to plot.
@@ -108,7 +111,7 @@ func parseWidgetSpec(widgetJSON string) widgetSpec {
if strings.TrimSpace(widgetJSON) == "" {
return spec
}
- _ = json.Unmarshal([]byte(widgetJSON), &spec)
+ _ = json.Unmarshal([]byte(widgetJSON), &spec, jsonx.V1())
return spec
}
@@ -241,24 +244,24 @@ func scanDurationComponent(s string, unit byte, scale time.Duration) (time.Durat
// namespace, metric name, then dimension name/value pairs, optionally followed by
// an options object ({"stat":...,"period":...,"label":...}). Render-only entries
// (leading object, e.g. metric-math expressions) are skipped.
-func parseWidgetSeries(elem []json.RawMessage, spec widgetSpec) (widgetSeriesQuery, bool) {
+func parseWidgetSeries(elem []jsontext.Value, spec widgetSpec) (widgetSeriesQuery, bool) {
q := widgetSeriesQuery{
stat: firstNonEmpty(spec.Stat, widgetDefaultStat),
period: firstPositive(spec.Period, widgetDefaultPeriod),
}
var strs []string
- var opts map[string]json.RawMessage
+ var opts map[string]jsontext.Value
for _, raw := range elem {
trimmed := strings.TrimSpace(string(raw))
if strings.HasPrefix(trimmed, "{") {
- _ = json.Unmarshal(raw, &opts)
+ _ = json.Unmarshal(raw, &opts, jsonx.V1())
continue
}
var s string
- if err := json.Unmarshal(raw, &s); err == nil {
+ if err := json.Unmarshal(raw, &s, jsonx.V1()); err == nil {
strs = append(strs, s)
}
}
@@ -284,25 +287,25 @@ func parseWidgetSeries(elem []json.RawMessage, spec widgetSpec) (widgetSeriesQue
}
// applyWidgetOptions overlays the trailing options object onto a series query.
-func applyWidgetOptions(q *widgetSeriesQuery, opts map[string]json.RawMessage) {
+func applyWidgetOptions(q *widgetSeriesQuery, opts map[string]jsontext.Value) {
if opts == nil {
return
}
if raw, ok := opts["stat"]; ok {
var s string
- if json.Unmarshal(raw, &s) == nil && s != "" {
+ if json.Unmarshal(raw, &s, jsonx.V1()) == nil && s != "" {
q.stat = s
}
}
if raw, ok := opts["label"]; ok {
var s string
- if json.Unmarshal(raw, &s) == nil && s != "" {
+ if json.Unmarshal(raw, &s, jsonx.V1()) == nil && s != "" {
q.label = s
}
}
if raw, ok := opts["period"]; ok {
var p int
- if json.Unmarshal(raw, &p) == nil && p > 0 {
+ if json.Unmarshal(raw, &p, jsonx.V1()) == nil && p > 0 {
q.period = p
}
}
diff --git a/services/cloudwatchlogs/data_protection_mask.go b/services/cloudwatchlogs/data_protection_mask.go
index 7917ca629b..c20ccfa5f1 100644
--- a/services/cloudwatchlogs/data_protection_mask.go
+++ b/services/cloudwatchlogs/data_protection_mask.go
@@ -2,11 +2,13 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"regexp"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type unmaskContextKey struct{}
@@ -133,7 +135,7 @@ func luhnValid(s string) bool {
// parseMaskPolicy compiles the masking (Deidentify) statements of doc; unsupported managed identifiers are skipped.
func parseMaskPolicy(doc string, since int64) (maskPolicy, error) {
var parsed protectionPolicyDoc
- if err := json.Unmarshal([]byte(doc), &parsed); err != nil {
+ if err := json.Unmarshal([]byte(doc), &parsed, jsonx.V1()); err != nil {
return maskPolicy{}, fmt.Errorf("%w: policyDocument must be valid JSON: %w", ErrValidation, err)
}
diff --git a/services/cloudwatchlogs/filter_pattern_json.go b/services/cloudwatchlogs/filter_pattern_json.go
index dc046571f0..f8ebfa00bb 100644
--- a/services/cloudwatchlogs/filter_pattern_json.go
+++ b/services/cloudwatchlogs/filter_pattern_json.go
@@ -1,10 +1,12 @@
package cloudwatchlogs
import (
- "encoding/json"
+ "encoding/json/v2"
"regexp"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// compileJSONFilterPattern compiles an AWS CloudWatch Logs JSON selector pattern
@@ -26,7 +28,7 @@ func compileJSONFilterPattern(pattern string) func(string) bool {
return func(message string) bool {
var root any
- if err := json.Unmarshal([]byte(strings.TrimSpace(message)), &root); err != nil {
+ if err := json.Unmarshal([]byte(strings.TrimSpace(message)), &root, jsonx.V1()); err != nil {
return false
}
@@ -42,7 +44,7 @@ func compileJSONFilterPattern(pattern string) func(string) bool {
func compileJSONFilterPatternExtract(_ string) func(message, fieldRef string) (string, bool) {
return func(message, fieldRef string) (string, bool) {
var root any
- if err := json.Unmarshal([]byte(strings.TrimSpace(message)), &root); err != nil {
+ if err := json.Unmarshal([]byte(strings.TrimSpace(message)), &root, jsonx.V1()); err != nil {
return "", false
}
diff --git a/services/cloudwatchlogs/golden_perf_test.go b/services/cloudwatchlogs/golden_perf_test.go
index c031a848e5..9209c1d173 100644
--- a/services/cloudwatchlogs/golden_perf_test.go
+++ b/services/cloudwatchlogs/golden_perf_test.go
@@ -12,7 +12,8 @@ package cloudwatchlogs_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"os"
@@ -22,6 +23,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -63,13 +65,13 @@ func checkGoldenLogs(t *testing.T, name string, got []byte) {
t.Helper()
var parsed any
- require.NoError(t, json.Unmarshal(got, &parsed))
- redacted, err := json.Marshal(redactVolatile(parsed))
+ require.NoError(t, json.Unmarshal(got, &parsed, jsonx.V1()))
+ redacted, err := json.Marshal(redactVolatile(parsed), jsonx.V1())
require.NoError(t, err)
- var buf bytes.Buffer
- require.NoError(t, json.Indent(&buf, redacted, "", " "))
- pretty := buf.Bytes()
+ indented := jsontext.Value(bytes.Clone(redacted))
+ require.NoError(t, indented.Indent(jsonx.V1(), jsontext.WithIndent(" ")))
+ pretty := []byte(indented)
path := goldenLogsPath(name)
if os.Getenv("CWL_GOLDEN_UPDATE") != "" {
@@ -107,7 +109,7 @@ func seedGoldenStream(
}
body, err := json.Marshal(map[string]any{
"logGroupName": group, "logStreamName": stream, "logEvents": events,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec = doLogsRequest(t, h, e, "PutLogEvents", string(body))
@@ -135,7 +137,7 @@ func TestGolden_FilterLogEventsInterleaved(t *testing.T) {
"logGroupName": "golden-grp",
"filterPattern": "line",
"limit": 50,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec = doLogsRequest(t, h, e, "FilterLogEvents", string(body))
@@ -161,7 +163,7 @@ func TestGolden_GetLogEvents(t *testing.T) {
"logGroupName": "golden-grp2",
"logStreamName": "s",
"limit": 10,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec = doLogsRequest(t, h, e, "GetLogEvents", string(body))
diff --git a/services/cloudwatchlogs/handler.go b/services/cloudwatchlogs/handler.go
index 17960eb777..275138af19 100644
--- a/services/cloudwatchlogs/handler.go
+++ b/services/cloudwatchlogs/handler.go
@@ -2,7 +2,7 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -259,7 +260,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var data map[string]any
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
@@ -406,7 +407,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(response)
+ return json.Marshal(response, jsonx.V1())
}
// handleError writes a standardized JSON error response.
@@ -475,7 +476,7 @@ func (h *Handler) handleError(ctx context.Context, c *echo.Context, action strin
Message: reqErr.Error(),
}
- payload, _ := json.Marshal(errResp)
+ payload, _ := json.Marshal(errResp, jsonx.V1())
return c.JSONBlob(statusCode, payload)
}
diff --git a/services/cloudwatchlogs/handler_anomaly_detectors.go b/services/cloudwatchlogs/handler_anomaly_detectors.go
index f049b5c04e..deeeca8996 100644
--- a/services/cloudwatchlogs/handler_anomaly_detectors.go
+++ b/services/cloudwatchlogs/handler_anomaly_detectors.go
@@ -2,7 +2,9 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createLogAnomalyDetectorInput struct {
@@ -111,7 +113,7 @@ func (h *Handler) handleCreateLogAnomalyDetector(
b []byte,
) (any, error) {
var input createLogAnomalyDetectorInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -135,7 +137,7 @@ func (h *Handler) handleDeleteLogAnomalyDetector(
b []byte,
) (any, error) {
var input deleteLogAnomalyDetectorInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.DeleteLogAnomalyDetector(input.AnomalyDetectorArn); err != nil {
@@ -150,7 +152,7 @@ func (h *Handler) handleListLogAnomalyDetectors(
b []byte,
) (any, error) {
var input listLogAnomalyDetectorsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
var filter []string
@@ -171,7 +173,7 @@ func (h *Handler) handleUpdateLogAnomalyDetector(
b []byte,
) (any, error) {
var input updateLogAnomalyDetectorInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.UpdateLogAnomalyDetector(
@@ -192,7 +194,7 @@ func (h *Handler) handleGetLogAnomalyDetector(
b []byte,
) (any, error) {
var input getLogAnomalyDetectorInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
d, err := h.Backend.GetLogAnomalyDetector(input.AnomalyDetectorArn)
@@ -215,7 +217,7 @@ func (h *Handler) handleGetLogAnomalyDetector(
func (h *Handler) handleListAnomalies(ctx context.Context, b []byte) (any, error) { //nolint:revive // existing issue.
var input listAnomaliesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
anomalies, next, err := h.Backend.ListAnomalies(input.AnomalyDetectorArn, input.Limit, input.NextToken)
@@ -228,7 +230,7 @@ func (h *Handler) handleListAnomalies(ctx context.Context, b []byte) (any, error
func (h *Handler) handleUpdateAnomaly(ctx context.Context, b []byte) (any, error) { //nolint:revive // existing issue.
var input updateAnomalyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/cloudwatchlogs/handler_anomaly_detectors_test.go b/services/cloudwatchlogs/handler_anomaly_detectors_test.go
index dcdaf41a4b..50b1cc0209 100644
--- a/services/cloudwatchlogs/handler_anomaly_detectors_test.go
+++ b/services/cloudwatchlogs/handler_anomaly_detectors_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -34,7 +35,7 @@ func TestHandler_LogAnomalyDetector_Create(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
arn, ok := out["anomalyDetectorArn"].(string)
require.True(t, ok)
assert.Contains(t, arn, "log-anomaly-detector")
@@ -193,7 +194,7 @@ func TestHandler_CreateLogAnomalyDetectorOperations(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
@@ -201,7 +202,7 @@ func TestHandler_CreateLogAnomalyDetectorOperations(t *testing.T) {
if tt.wantCode == http.StatusOK && tt.wantKey != "" {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tt.wantVal != "" {
assert.Equal(t, tt.wantVal, out[tt.wantKey])
} else {
diff --git a/services/cloudwatchlogs/handler_data_protection.go b/services/cloudwatchlogs/handler_data_protection.go
index 63ddeb10cd..3d95342fde 100644
--- a/services/cloudwatchlogs/handler_data_protection.go
+++ b/services/cloudwatchlogs/handler_data_protection.go
@@ -2,8 +2,10 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type deleteAccountPolicyInput struct {
@@ -48,7 +50,7 @@ func (h *Handler) handleDeleteAccountPolicy(
b []byte,
) (any, error) {
var input deleteAccountPolicyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -64,7 +66,7 @@ func (h *Handler) handlePutAccountPolicy(
b []byte,
) (any, error) {
var input putAccountPolicyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
policy, err := h.Backend.PutAccountPolicy(
@@ -86,7 +88,7 @@ func (h *Handler) handleDescribeAccountPolicies(
b []byte,
) (any, error) {
var input describeAccountPoliciesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
policies, nextToken, err := h.Backend.DescribeAccountPolicies(
@@ -112,7 +114,7 @@ func (h *Handler) handleDeleteDataProtectionPolicy(
body []byte,
) (any, error) {
var in deleteDataProtectionPolicyInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -134,7 +136,7 @@ func (h *Handler) handleGetDataProtectionPolicy(
body []byte,
) (any, error) {
var in getDataProtectionPolicyInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -170,7 +172,7 @@ func (h *Handler) handlePutDataProtectionPolicy(
body []byte,
) (any, error) {
var in putDataProtectionPolicyInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
diff --git a/services/cloudwatchlogs/handler_data_protection_test.go b/services/cloudwatchlogs/handler_data_protection_test.go
index 3c4b4343cd..977031c0d8 100644
--- a/services/cloudwatchlogs/handler_data_protection_test.go
+++ b/services/cloudwatchlogs/handler_data_protection_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -82,7 +83,7 @@ func TestHandler_DataProtectionPolicy(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
assert.Equal(t, tt.wantCode, rec.Code)
@@ -138,7 +139,7 @@ func TestHandler_DeleteAccountPolicyOperations(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
@@ -146,7 +147,7 @@ func TestHandler_DeleteAccountPolicyOperations(t *testing.T) {
if tt.wantCode == http.StatusOK && tt.wantKey != "" {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tt.wantVal != "" {
assert.Equal(t, tt.wantVal, out[tt.wantKey])
} else {
diff --git a/services/cloudwatchlogs/handler_deliveries.go b/services/cloudwatchlogs/handler_deliveries.go
index ea5418eaa4..fe019982b5 100644
--- a/services/cloudwatchlogs/handler_deliveries.go
+++ b/services/cloudwatchlogs/handler_deliveries.go
@@ -2,8 +2,10 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createDeliveryInput struct {
@@ -48,7 +50,7 @@ type deleteDeliveryOutput struct{}
func (h *Handler) handleCreateDelivery(ctx context.Context, b []byte) (any, error) { //nolint:revive // existing issue.
var input createDeliveryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -68,7 +70,7 @@ func (h *Handler) handleDescribeDeliveries(
b []byte,
) (any, error) {
var input describeDeliveriesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
deliveries, next, err := h.Backend.DescribeDeliveries(input.Limit, input.NextToken)
@@ -81,7 +83,7 @@ func (h *Handler) handleDescribeDeliveries(
func (h *Handler) handleGetDelivery(ctx context.Context, b []byte) (any, error) { //nolint:revive // existing issue.
var input getDeliveryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
d, err := h.Backend.GetDelivery(input.ID)
@@ -94,7 +96,7 @@ func (h *Handler) handleGetDelivery(ctx context.Context, b []byte) (any, error)
func (h *Handler) handleDeleteDelivery(ctx context.Context, b []byte) (any, error) { //nolint:revive // existing issue.
var input deleteDeliveryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.DeleteDelivery(input.ID); err != nil {
@@ -144,7 +146,7 @@ func (h *Handler) handlePutDeliveryDestination(
body []byte,
) (any, error) {
var in putDeliveryDestinationInputFull
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidationException, err)
}
@@ -171,7 +173,7 @@ func (h *Handler) handleGetDeliveryDestination(
body []byte,
) (any, error) {
var in getDeliveryDestinationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidationException, err)
}
@@ -197,7 +199,7 @@ func (h *Handler) handleDescribeDeliveryDestinations(
body []byte,
) (any, error) {
var in describeDeliveryDestinationsInput
- _ = json.Unmarshal(body, &in)
+ _ = json.Unmarshal(body, &in, jsonx.V1())
if b := cwlBackend(h); b != nil {
dests, next := b.DescribeDeliveryDestinations(in.NextToken, in.Limit)
@@ -226,7 +228,7 @@ func (h *Handler) handleDeleteDeliveryDestination(
body []byte,
) (any, error) {
var in deleteDeliveryDestinationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidationException, err)
}
@@ -249,7 +251,7 @@ func (h *Handler) handlePutDeliveryDestinationPolicy(
body []byte,
) (any, error) {
var in putDeliveryDestinationPolicyInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidationException, err)
}
@@ -273,7 +275,7 @@ func (h *Handler) handleGetDeliveryDestinationPolicy(
body []byte,
) (any, error) {
var in getDeliveryDestinationPolicyInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidationException, err)
}
@@ -298,7 +300,7 @@ func (h *Handler) handleDeleteDeliveryDestinationPolicy(
body []byte,
) (any, error) {
var in deleteDeliveryDestinationPolicyInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidationException, err)
}
@@ -353,7 +355,7 @@ func (h *Handler) handlePutDeliverySource(
body []byte,
) (any, error) {
var in putDeliverySourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidationException, err)
}
@@ -383,7 +385,7 @@ func (h *Handler) handleGetDeliverySource(
body []byte,
) (any, error) {
var in getDeliverySourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidationException, err)
}
@@ -409,7 +411,7 @@ func (h *Handler) handleDescribeDeliverySources(
body []byte,
) (any, error) {
var in describeDeliverySourcesInput
- _ = json.Unmarshal(body, &in)
+ _ = json.Unmarshal(body, &in, jsonx.V1())
if b := cwlBackend(h); b != nil {
srcs, next := b.DescribeDeliverySources(in.NextToken, in.Limit)
@@ -438,7 +440,7 @@ func (h *Handler) handleDeleteDeliverySource(
body []byte,
) (any, error) {
var in deleteDeliverySourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidationException, err)
}
@@ -463,7 +465,7 @@ func (h *Handler) handleUpdateDeliveryConfiguration(
body []byte,
) (any, error) {
var in updateDeliveryConfigurationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidationException, err)
}
diff --git a/services/cloudwatchlogs/handler_deliveries_test.go b/services/cloudwatchlogs/handler_deliveries_test.go
index a485b3757b..5da0e86ba1 100644
--- a/services/cloudwatchlogs/handler_deliveries_test.go
+++ b/services/cloudwatchlogs/handler_deliveries_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -26,7 +27,7 @@ func TestHandler_Delivery_CreateWithTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
delivery, ok := out["delivery"].(map[string]any)
require.True(t, ok)
@@ -242,7 +243,7 @@ func TestHandler_DeliveryDestination(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
assert.Equal(t, tt.wantCode, rec.Code)
@@ -284,7 +285,7 @@ func TestHandler_DeliveryDestination_WireShape(t *testing.T) {
}
var putOut map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut, jsonx.V1()))
putDest, ok := putOut["deliveryDestination"].(map[string]any)
require.True(t, ok)
checkShape(t, putDest)
@@ -293,7 +294,7 @@ func TestHandler_DeliveryDestination_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
getDest, ok := getOut["deliveryDestination"].(map[string]any)
require.True(t, ok)
checkShape(t, getDest)
@@ -302,7 +303,7 @@ func TestHandler_DeliveryDestination_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, describeRec.Code)
var describeOut map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeOut))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeOut, jsonx.V1()))
dests, ok := describeOut["deliveryDestinations"].([]any)
require.True(t, ok)
require.Len(t, dests, 1)
@@ -394,7 +395,7 @@ func TestHandler_DeliverySource(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
assert.Equal(t, tt.wantCode, rec.Code)
@@ -437,7 +438,7 @@ func TestHandler_DeliverySource_WireShape(t *testing.T) {
}
var putOut map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut, jsonx.V1()))
putSrc, ok := putOut["deliverySource"].(map[string]any)
require.True(t, ok)
checkShape(t, putSrc)
@@ -446,7 +447,7 @@ func TestHandler_DeliverySource_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
getSrc, ok := getOut["deliverySource"].(map[string]any)
require.True(t, ok)
checkShape(t, getSrc)
@@ -455,7 +456,7 @@ func TestHandler_DeliverySource_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, describeRec.Code)
var describeOut map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeOut))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeOut, jsonx.V1()))
srcs, ok := describeOut["deliverySources"].([]any)
require.True(t, ok)
require.Len(t, srcs, 1)
@@ -488,7 +489,7 @@ func TestHandler_UpdateDeliveryConfiguration(t *testing.T) {
)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id, _ := resp["delivery"]["id"].(string)
return id
@@ -512,7 +513,7 @@ func TestHandler_UpdateDeliveryConfiguration(t *testing.T) {
)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id, _ := resp["delivery"]["id"].(string)
return id
@@ -545,7 +546,7 @@ func TestHandler_UpdateDeliveryConfiguration(t *testing.T) {
deliveryID = tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body(deliveryID))
+ bodyBytes, err := json.Marshal(tt.body(deliveryID), jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
assert.Equal(t, tt.wantCode, rec.Code)
@@ -604,7 +605,7 @@ func TestHandler_CreateDeliveryOperations(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
@@ -612,7 +613,7 @@ func TestHandler_CreateDeliveryOperations(t *testing.T) {
if tt.wantCode == http.StatusOK && tt.wantKey != "" {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tt.wantVal != "" {
assert.Equal(t, tt.wantVal, out[tt.wantKey])
} else {
@@ -679,13 +680,13 @@ func TestHandler_DeliveryResponseShape(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
require.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for _, field := range tt.wantFields {
assert.Contains(t, resp, field, "response should contain field %q", field)
}
diff --git a/services/cloudwatchlogs/handler_destinations.go b/services/cloudwatchlogs/handler_destinations.go
index e542412472..7d80f3868f 100644
--- a/services/cloudwatchlogs/handler_destinations.go
+++ b/services/cloudwatchlogs/handler_destinations.go
@@ -2,8 +2,10 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type putDestinationInput struct {
@@ -18,7 +20,7 @@ func (h *Handler) handlePutDestination(
body []byte,
) (any, error) {
var in putDestinationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -69,7 +71,7 @@ func (h *Handler) handlePutDestinationPolicy(
body []byte,
) (any, error) {
var in putDestinationPolicyInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -93,7 +95,7 @@ func (h *Handler) handleDescribeDestinations(
body []byte,
) (any, error) {
var in describeDestinationsInput
- _ = json.Unmarshal(body, &in)
+ _ = json.Unmarshal(body, &in, jsonx.V1())
if b := cwlBackend(h); b != nil {
dests, next := b.DescribeDestinations(in.DestinationNamePrefix, in.Limit, in.NextToken)
@@ -122,7 +124,7 @@ func (h *Handler) handleDeleteDestination(
body []byte,
) (any, error) {
var in deleteDestinationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
diff --git a/services/cloudwatchlogs/handler_destinations_test.go b/services/cloudwatchlogs/handler_destinations_test.go
index 73c10713d4..decfac1762 100644
--- a/services/cloudwatchlogs/handler_destinations_test.go
+++ b/services/cloudwatchlogs/handler_destinations_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -132,7 +133,7 @@ func TestHandler_Destination(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
assert.Equal(t, tt.wantCode, rec.Code)
@@ -155,7 +156,7 @@ func TestHandler_Destination_WireShapeIncludesCreationTimeAndAccessPolicy(t *tes
require.Equal(t, http.StatusOK, putRec.Code)
var putOut map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut, jsonx.V1()))
putDest, ok := putOut["destination"].(map[string]any)
require.True(t, ok)
ct, hasCreationTime := putDest["creationTime"]
@@ -172,7 +173,7 @@ func TestHandler_Destination_WireShapeIncludesCreationTimeAndAccessPolicy(t *tes
require.Equal(t, http.StatusOK, describeRec.Code)
var describeOut map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeOut))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeOut, jsonx.V1()))
dests, ok := describeOut["destinations"].([]any)
require.True(t, ok)
require.Len(t, dests, 1)
@@ -225,13 +226,13 @@ func TestHandler_DestinationResponseShape(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
require.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for _, field := range tt.wantFields {
assert.Contains(t, resp, field, "response should contain field %q", field)
}
diff --git a/services/cloudwatchlogs/handler_export_tasks.go b/services/cloudwatchlogs/handler_export_tasks.go
index 796b4370bc..d22576415f 100644
--- a/services/cloudwatchlogs/handler_export_tasks.go
+++ b/services/cloudwatchlogs/handler_export_tasks.go
@@ -2,8 +2,10 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type cancelExportTaskInput struct {
@@ -146,7 +148,7 @@ func (h *Handler) handleCancelExportTask(
b []byte,
) (any, error) {
var input cancelExportTaskInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.CancelExportTask(input.TaskID); err != nil {
@@ -161,7 +163,7 @@ func (h *Handler) handleCancelImportTask(
b []byte,
) (any, error) {
var input cancelImportTaskInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -183,7 +185,7 @@ func (h *Handler) handleCreateExportTask(
b []byte,
) (any, error) {
var input createExportTaskInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -204,7 +206,7 @@ func (h *Handler) handleCreateImportTask(
b []byte,
) (any, error) {
var input createImportTaskInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -227,7 +229,7 @@ func (h *Handler) handleDescribeExportTasks(
b []byte,
) (any, error) {
var input describeExportTasksInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
tasks, next, err := h.Backend.DescribeExportTasks(input.TaskID, input.StatusCode, input.Limit, input.NextToken)
@@ -248,7 +250,7 @@ func (h *Handler) handleDescribeImportTasks(
b []byte,
) (any, error) {
var input describeImportTasksInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
tasks, next, err := h.Backend.DescribeImportTasks(input.ImportID, input.Limit, input.NextToken)
@@ -279,7 +281,7 @@ func (h *Handler) handleDescribeImportTaskBatches(
ImportID string `json:"importId"`
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, err
}
}
diff --git a/services/cloudwatchlogs/handler_export_tasks_test.go b/services/cloudwatchlogs/handler_export_tasks_test.go
index c87881b59a..000ef98f10 100644
--- a/services/cloudwatchlogs/handler_export_tasks_test.go
+++ b/services/cloudwatchlogs/handler_export_tasks_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -31,13 +32,13 @@ func TestHandler_ExportTask_CancelRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
taskID, ok := createOut["taskId"].(string)
require.True(t, ok)
require.NotEmpty(t, taskID)
// Cancel the task.
- bodyBytes, err := json.Marshal(map[string]any{"taskId": taskID})
+ bodyBytes, err := json.Marshal(map[string]any{"taskId": taskID}, jsonx.V1())
require.NoError(t, err)
rec = doLogsRequest(t, h, e, "CancelExportTask", string(bodyBytes))
assert.Equal(t, http.StatusOK, rec.Code)
@@ -87,7 +88,7 @@ func TestHandler_DescribeExportTasks_WireShape(t *testing.T) {
TaskID string `json:"taskId"`
} `json:"exportTasks"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.ExportTasks, 1)
task := out.ExportTasks[0]
@@ -102,7 +103,7 @@ func TestHandler_DescribeExportTasks_WireShape(t *testing.T) {
// The raw JSON must not carry the old flat keys at the top level of an
// export task entry.
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
rawTasks, ok := raw["exportTasks"].([]any)
require.True(t, ok)
require.Len(t, rawTasks, 1)
@@ -130,7 +131,7 @@ func TestHandler_ImportTask_CancelRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
importID, ok := createOut["importId"].(string)
require.True(t, ok)
require.NotEmpty(t, importID)
@@ -140,13 +141,13 @@ func TestHandler_ImportTask_CancelRoundTrip(t *testing.T) {
require.NotEmpty(t, importDestARN)
// Cancel the task.
- bodyBytes, err := json.Marshal(map[string]any{"importId": importID})
+ bodyBytes, err := json.Marshal(map[string]any{"importId": importID}, jsonx.V1())
require.NoError(t, err)
rec = doLogsRequest(t, h, e, "CancelImportTask", string(bodyBytes))
require.Equal(t, http.StatusOK, rec.Code)
var cancelOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cancelOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cancelOut, jsonx.V1()))
assert.Equal(t, importID, cancelOut["importId"])
assert.Equal(t, "CANCELLED", cancelOut["importStatus"])
}
@@ -254,7 +255,7 @@ func TestHandler_CancelExportTask_StateValidation(t *testing.T) {
e := echo.New()
h := cloudwatchlogs.NewHandler(backend)
- bodyBytes, _ := json.Marshal(map[string]any{"taskId": taskID})
+ bodyBytes, _ := json.Marshal(map[string]any{"taskId": taskID}, jsonx.V1())
rec := doLogsRequest(t, h, e, "CancelExportTask", string(bodyBytes))
assert.Equal(t, tt.wantCode, rec.Code)
})
@@ -313,7 +314,7 @@ func TestHandler_CancelImportTask_StateValidation(t *testing.T) {
e := echo.New()
h := cloudwatchlogs.NewHandler(backend)
- bodyBytes, _ := json.Marshal(map[string]any{"importId": importID})
+ bodyBytes, _ := json.Marshal(map[string]any{"importId": importID}, jsonx.V1())
rec := doLogsRequest(t, h, e, "CancelImportTask", string(bodyBytes))
assert.Equal(t, tt.wantCode, rec.Code)
})
@@ -490,7 +491,7 @@ func TestHandler_ExportImportTaskOperations(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
@@ -498,7 +499,7 @@ func TestHandler_ExportImportTaskOperations(t *testing.T) {
if tt.wantCode == http.StatusOK && tt.wantKey != "" {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tt.wantVal != "" {
assert.Equal(t, tt.wantVal, out[tt.wantKey])
} else {
@@ -533,7 +534,7 @@ func TestHandler_ImportTaskBatchesValidation(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := makeLogsRequest(t, tt.action, string(bodyBytes))
@@ -541,7 +542,7 @@ func TestHandler_ImportTaskBatchesValidation(t *testing.T) {
if tt.wantListField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp[tt.wantListField].([]any)
require.True(t, ok, "expected list field %q in response", tt.wantListField)
assert.Empty(t, list)
diff --git a/services/cloudwatchlogs/handler_index_policies.go b/services/cloudwatchlogs/handler_index_policies.go
index 3e6812d3e4..87c91380a4 100644
--- a/services/cloudwatchlogs/handler_index_policies.go
+++ b/services/cloudwatchlogs/handler_index_policies.go
@@ -2,8 +2,10 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type putIndexPolicyInput struct {
@@ -20,7 +22,7 @@ func (h *Handler) handlePutIndexPolicy(
body []byte,
) (any, error) {
var in putIndexPolicyInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -51,7 +53,7 @@ func (h *Handler) handleDescribeIndexPolicies(
body []byte,
) (any, error) {
var in describeIndexPoliciesInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -77,7 +79,7 @@ func (h *Handler) handleDeleteIndexPolicy(
body []byte,
) (any, error) {
var in deleteIndexPolicyInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
diff --git a/services/cloudwatchlogs/handler_index_policies_test.go b/services/cloudwatchlogs/handler_index_policies_test.go
index 1fde81d7ee..7729809ba7 100644
--- a/services/cloudwatchlogs/handler_index_policies_test.go
+++ b/services/cloudwatchlogs/handler_index_policies_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -103,7 +104,7 @@ func TestHandler_IndexPolicy(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
assert.Equal(t, tt.wantCode, rec.Code)
@@ -141,7 +142,7 @@ func TestHandler_ConfigTemplatesAndFieldIndexes(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := makeLogsRequest(t, tt.action, string(bodyBytes))
@@ -149,7 +150,7 @@ func TestHandler_ConfigTemplatesAndFieldIndexes(t *testing.T) {
if tt.wantListField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp[tt.wantListField].([]any)
require.True(t, ok, "expected list field %q in response", tt.wantListField)
assert.Empty(t, list)
@@ -204,13 +205,13 @@ func TestHandler_IndexPolicyResponseShape(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
require.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for _, field := range tt.wantFields {
assert.Contains(t, resp, field, "response should contain field %q", field)
}
diff --git a/services/cloudwatchlogs/handler_integrations.go b/services/cloudwatchlogs/handler_integrations.go
index c1060397f2..dac48c8931 100644
--- a/services/cloudwatchlogs/handler_integrations.go
+++ b/services/cloudwatchlogs/handler_integrations.go
@@ -2,8 +2,10 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type associateSourceToS3TableIntegrationInput struct {
@@ -20,7 +22,7 @@ func (h *Handler) handleAssociateSourceToS3TableIntegration(
b []byte,
) (any, error) {
var input associateSourceToS3TableIntegrationInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -72,7 +74,7 @@ func (h *Handler) handlePutIntegration(
body []byte,
) (any, error) {
var in putIntegrationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -159,7 +161,7 @@ func (h *Handler) handleGetIntegration(
body []byte,
) (any, error) {
var in getIntegrationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -194,7 +196,7 @@ func (h *Handler) handleListIntegrations(
body []byte,
) (any, error) {
var in listIntegrationsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
@@ -224,7 +226,7 @@ func (h *Handler) handleDeleteIntegration(
body []byte,
) (any, error) {
var in deleteIntegrationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -250,7 +252,7 @@ func (h *Handler) handleDisassociateSourceFromS3TableIntegration(
body []byte,
) (any, error) {
var in disassociateSourceFromS3TableIntegrationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidationException, err)
}
@@ -289,7 +291,7 @@ func (h *Handler) handleListSourcesForS3TableIntegration(
body []byte,
) (any, error) {
var input listSourcesForS3TableIntegrationInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidationException, err)
}
diff --git a/services/cloudwatchlogs/handler_integrations_test.go b/services/cloudwatchlogs/handler_integrations_test.go
index fa860558e7..5f16fbbd78 100644
--- a/services/cloudwatchlogs/handler_integrations_test.go
+++ b/services/cloudwatchlogs/handler_integrations_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -135,7 +136,7 @@ func TestHandler_Integration(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
assert.Equal(t, tt.wantCode, rec.Code)
@@ -186,7 +187,7 @@ func TestHandler_AssociateSourceToS3TableIntegrationOperations(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
@@ -194,7 +195,7 @@ func TestHandler_AssociateSourceToS3TableIntegrationOperations(t *testing.T) {
if tt.wantCode == http.StatusOK && tt.wantKey != "" {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tt.wantVal != "" {
assert.Equal(t, tt.wantVal, out[tt.wantKey])
} else {
@@ -252,7 +253,7 @@ func TestHandler_S3TableIntegrationSourceOperations(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := makeLogsRequest(t, tt.action, string(bodyBytes))
@@ -260,7 +261,7 @@ func TestHandler_S3TableIntegrationSourceOperations(t *testing.T) {
if tt.wantListField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp[tt.wantListField].([]any)
require.True(t, ok, "expected list field %q in response", tt.wantListField)
assert.Empty(t, list)
@@ -280,7 +281,7 @@ func TestHandler_DisassociateSourceFromS3TableIntegration_RoundTrip(t *testing.T
require.Equal(t, http.StatusOK, assocRec.Code)
var assocResp map[string]any
- require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp))
+ require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp, jsonx.V1()))
identifier, ok := assocResp["identifier"].(string)
require.True(t, ok, "expected identifier in AssociateSourceToS3TableIntegration response")
require.NotEmpty(t, identifier)
@@ -290,7 +291,7 @@ func TestHandler_DisassociateSourceFromS3TableIntegration_RoundTrip(t *testing.T
require.Equal(t, http.StatusOK, disRec.Code)
var disResp map[string]any
- require.NoError(t, json.Unmarshal(disRec.Body.Bytes(), &disResp))
+ require.NoError(t, json.Unmarshal(disRec.Body.Bytes(), &disResp, jsonx.V1()))
assert.Equal(t, identifier, disResp["identifier"])
// A second disassociate of the same identifier must now fail: the
@@ -340,13 +341,13 @@ func TestHandler_IntegrationResponseShape(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
require.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for _, field := range tt.wantFields {
assert.Contains(t, resp, field, "response should contain field %q", field)
}
diff --git a/services/cloudwatchlogs/handler_log_events.go b/services/cloudwatchlogs/handler_log_events.go
index e8c76da697..d01248126d 100644
--- a/services/cloudwatchlogs/handler_log_events.go
+++ b/services/cloudwatchlogs/handler_log_events.go
@@ -2,8 +2,10 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type putLogEventsInput struct {
@@ -103,7 +105,7 @@ func (h *Handler) logEventActions() map[string]actionFn {
return map[string]actionFn{
"PutLogEvents": func(ctx context.Context, b []byte) (any, error) {
var input putLogEventsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
result, err := h.Backend.PutLogEvents(
@@ -125,7 +127,7 @@ func (h *Handler) logEventActions() map[string]actionFn {
},
"GetLogEvents": func(ctx context.Context, b []byte) (any, error) {
var input getLogEventsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
groupName, err := resolveLogGroupRef(input.LogGroupName, input.LogGroupIdentifier)
@@ -147,7 +149,7 @@ func (h *Handler) logEventActions() map[string]actionFn {
},
"FilterLogEvents": func(ctx context.Context, b []byte) (any, error) {
var input filterLogEventsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
groupName, err := resolveLogGroupRef(input.LogGroupName, input.LogGroupIdentifier)
@@ -180,7 +182,7 @@ func (h *Handler) logEventActions() map[string]actionFn {
func (h *Handler) handleGetLogRecord(ctx context.Context, b []byte) (any, error) {
var input getLogRecordInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
record, err := h.Backend.GetLogRecord(withUnmask(ctx, input.Unmask), input.LogRecordPointer)
@@ -205,7 +207,7 @@ func (h *Handler) handleGetLogFields(ctx context.Context, body []byte) (any, err
LogGroupName string `json:"logGroupName"`
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -253,7 +255,7 @@ func (h *Handler) handleGetLogObject(ctx context.Context, body []byte) (any, err
Unmask bool `json:"unmask"`
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -293,7 +295,7 @@ func (h *Handler) handlePutBearerTokenAuthentication(
ctx context.Context, body []byte,
) (any, error) {
var in putBearerTokenAuthenticationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -330,7 +332,7 @@ func (h *Handler) handleStartLiveTail(ctx context.Context, body []byte) (any, er
LogGroupIdentifiers []string `json:"logGroupIdentifiers"`
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, err
}
}
diff --git a/services/cloudwatchlogs/handler_log_events_test.go b/services/cloudwatchlogs/handler_log_events_test.go
index ae10b15188..98fdc12bdb 100644
--- a/services/cloudwatchlogs/handler_log_events_test.go
+++ b/services/cloudwatchlogs/handler_log_events_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -57,7 +58,7 @@ func TestHandler_PutLogEvents_MismatchedSequenceToken_StillAccepted(t *testing.T
"logEvents": []any{
map[string]any{"message": "setup", "timestamp": now + int64(i)},
},
- })
+ }, jsonx.V1())
doLogsRequest(t, h, e, "PutLogEvents", string(body))
}
@@ -66,7 +67,7 @@ func TestHandler_PutLogEvents_MismatchedSequenceToken_StillAccepted(t *testing.T
"logStreamName": "s",
"sequenceToken": tt.sequenceToken,
"logEvents": []any{map[string]any{"message": "new", "timestamp": now}},
- })
+ }, jsonx.V1())
rec := doLogsRequest(t, h, e, "PutLogEvents", string(reqBody))
assert.Equal(t, http.StatusOK, rec.Code,
@@ -75,7 +76,7 @@ func TestHandler_PutLogEvents_MismatchedSequenceToken_StillAccepted(t *testing.T
var okResp struct {
NextSequenceToken string `json:"nextSequenceToken"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &okResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &okResp, jsonx.V1()))
assert.Equal(t, strconv.Itoa(tt.setupEvents+1), okResp.NextSequenceToken)
})
}
@@ -212,7 +213,7 @@ func TestHandler_LogEventsOperations(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
@@ -220,13 +221,13 @@ func TestHandler_LogEventsOperations(t *testing.T) {
if tt.wantListField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp[tt.wantListField].([]any), tt.wantListLen)
}
if tt.wantNotEmptyField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp[tt.wantNotEmptyField])
}
})
@@ -258,7 +259,7 @@ func TestHandler_PutBearerTokenAuthentication_RoundTrip(t *testing.T) {
var desc struct {
LogGroups []map[string]any `json:"logGroups"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc, jsonx.V1()))
require.Len(t, desc.LogGroups, 1)
assert.Equal(t, true, desc.LogGroups[0]["bearerTokenAuthenticationEnabled"])
@@ -314,7 +315,7 @@ func TestHandler_LiveTailAndLogFieldOperations(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := makeLogsRequest(t, tt.action, string(bodyBytes))
@@ -322,7 +323,7 @@ func TestHandler_LiveTailAndLogFieldOperations(t *testing.T) {
if tt.wantListField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp[tt.wantListField].([]any)
require.True(t, ok, "expected list field %q in response", tt.wantListField)
assert.Empty(t, list)
@@ -368,7 +369,7 @@ func TestHandler_PutLogEvents_EntityRejection(t *testing.T) {
if tt.entity != nil {
req["entity"] = tt.entity
}
- body, _ := json.Marshal(req)
+ body, _ := json.Marshal(req, jsonx.V1())
rec := doLogsRequest(t, h, e, "PutLogEvents", string(body))
require.Equal(t, http.StatusOK, rec.Code)
@@ -377,7 +378,7 @@ func TestHandler_PutLogEvents_EntityRejection(t *testing.T) {
ErrorType string `json:"errorType"`
} `json:"rejectedEntityInfo"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tt.wantType == "" {
assert.Nil(t, out.RejectedEntityInfo)
diff --git a/services/cloudwatchlogs/handler_log_groups.go b/services/cloudwatchlogs/handler_log_groups.go
index ccbcd39359..1f27cb4199 100644
--- a/services/cloudwatchlogs/handler_log_groups.go
+++ b/services/cloudwatchlogs/handler_log_groups.go
@@ -2,11 +2,13 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
sdktypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createLogGroupInput struct {
@@ -124,7 +126,7 @@ func (h *Handler) logGroupActions() map[string]actionFn {
// keep that function's cognitive complexity under the gocognit limit.
func (h *Handler) handleCreateLogGroup(ctx context.Context, b []byte) (any, error) {
var input createLogGroupInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -158,7 +160,7 @@ func (h *Handler) handleCreateLogGroup(ctx context.Context, b []byte) (any, erro
func (h *Handler) handleDeleteLogGroup(ctx context.Context, b []byte) (any, error) {
var input deleteLogGroupInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.DeleteLogGroup(ctx, input.LogGroupName); err != nil {
@@ -170,7 +172,7 @@ func (h *Handler) handleDeleteLogGroup(ctx context.Context, b []byte) (any, erro
func (h *Handler) handleDescribeLogGroups(ctx context.Context, b []byte) (any, error) {
var input describeLogGroupsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -234,7 +236,7 @@ func (h *Handler) retentionActions() map[string]actionFn {
return map[string]actionFn{
"PutRetentionPolicy": func(ctx context.Context, b []byte) (any, error) {
var input putRetentionPolicyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
days := input.RetentionInDays
@@ -246,7 +248,7 @@ func (h *Handler) retentionActions() map[string]actionFn {
},
"DeleteRetentionPolicy": func(ctx context.Context, b []byte) (any, error) {
var input deleteRetentionPolicyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.SetRetentionPolicy(ctx, input.LogGroupName, nil); err != nil {
@@ -260,7 +262,7 @@ func (h *Handler) retentionActions() map[string]actionFn {
func (h *Handler) handleAssociateKmsKey(ctx context.Context, b []byte) (any, error) { //nolint:revive // existing issue.
var input associateKmsKeyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.AssociateKmsKey(input.LogGroupName, input.ResourceIdentifier, input.KmsKeyID); err != nil {
@@ -275,7 +277,7 @@ func (h *Handler) handleDisassociateKmsKey(
b []byte,
) (any, error) {
var input disassociateKmsKeyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.DisassociateKmsKey(input.LogGroupName, input.ResourceIdentifier); err != nil {
@@ -287,7 +289,7 @@ func (h *Handler) handleDisassociateKmsKey(
func (h *Handler) handleGetLogGroupFields(ctx context.Context, b []byte) (any, error) {
var input getLogGroupFieldsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -306,7 +308,7 @@ func (h *Handler) handleGetLogGroupFields(ctx context.Context, b []byte) (any, e
func (h *Handler) handleListLogGroups(ctx context.Context, b []byte) (any, error) {
var input listLogGroupsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
for _, f := range input.LogGroupTags {
@@ -344,7 +346,7 @@ func (h *Handler) handlePutLogGroupDeletionProtection(
body []byte,
) (any, error) {
var in putLogGroupDeletionProtectionInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -388,7 +390,7 @@ func (h *Handler) handleListAggregateLogGroupSummaries(
GroupBy string `json:"groupBy"`
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidationException, err)
}
}
diff --git a/services/cloudwatchlogs/handler_log_groups_test.go b/services/cloudwatchlogs/handler_log_groups_test.go
index ddf06b5b3a..2e18ddbcfe 100644
--- a/services/cloudwatchlogs/handler_log_groups_test.go
+++ b/services/cloudwatchlogs/handler_log_groups_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -131,7 +132,7 @@ func TestHandler_LogGroupDeletionProtection(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
assert.Equal(t, tt.wantCode, rec.Code)
@@ -168,7 +169,7 @@ func TestHandler_PutLogGroupDeletionProtection_WireName(t *testing.T) {
body, err := json.Marshal(map[string]any{
"logGroupIdentifier": lg,
"deletionProtectionEnabled": tt.enabled,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, handler, e, "PutLogGroupDeletionProtection", string(body))
@@ -217,7 +218,7 @@ func TestHandler_PutRetentionPolicy_ZeroDays_ReturnsInvalidParameter(t *testing.
body, _ := json.Marshal(map[string]any{
"logGroupName": "ret-grp",
"retentionInDays": tt.retentionInDays,
- })
+ }, jsonx.V1())
rec := doLogsRequest(t, h, e, "PutRetentionPolicy", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code,
@@ -226,7 +227,7 @@ func TestHandler_PutRetentionPolicy_ZeroDays_ReturnsInvalidParameter(t *testing.
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp.Type,
"retentionInDays=%d must return InvalidParameterException", tt.retentionInDays)
})
@@ -317,7 +318,7 @@ func TestHandler_LogGroupOperations(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
@@ -325,13 +326,13 @@ func TestHandler_LogGroupOperations(t *testing.T) {
if tt.wantListField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp[tt.wantListField].([]any), tt.wantListLen)
}
if tt.wantNotEmptyField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp[tt.wantNotEmptyField])
}
})
@@ -386,7 +387,7 @@ func TestHandler_RetentionPolicyOperations(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
@@ -394,13 +395,13 @@ func TestHandler_RetentionPolicyOperations(t *testing.T) {
if tt.wantListField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp[tt.wantListField].([]any), tt.wantListLen)
}
if tt.wantNotEmptyField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp[tt.wantNotEmptyField])
}
})
@@ -458,7 +459,7 @@ func TestHandler_AssociateKmsKeyOperations(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
@@ -466,7 +467,7 @@ func TestHandler_AssociateKmsKeyOperations(t *testing.T) {
if tt.wantCode == http.StatusOK && tt.wantKey != "" {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tt.wantVal != "" {
assert.Equal(t, tt.wantVal, out[tt.wantKey])
} else {
@@ -500,7 +501,7 @@ func TestHandler_AggregateLogGroupSummariesEmpty(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := makeLogsRequest(t, tt.action, string(bodyBytes))
@@ -508,7 +509,7 @@ func TestHandler_AggregateLogGroupSummariesEmpty(t *testing.T) {
if tt.wantListField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp[tt.wantListField].([]any)
require.True(t, ok, "expected list field %q in response", tt.wantListField)
assert.Empty(t, list)
diff --git a/services/cloudwatchlogs/handler_log_streams.go b/services/cloudwatchlogs/handler_log_streams.go
index 0fa2046618..1a2d853064 100644
--- a/services/cloudwatchlogs/handler_log_streams.go
+++ b/services/cloudwatchlogs/handler_log_streams.go
@@ -2,7 +2,9 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createLogStreamInput struct {
@@ -38,7 +40,7 @@ func (h *Handler) logStreamActions() map[string]actionFn {
return map[string]actionFn{
"CreateLogStream": func(ctx context.Context, b []byte) (any, error) {
var input createLogStreamInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if _, err := h.Backend.CreateLogStream(ctx, input.LogGroupName, input.LogStreamName); err != nil {
@@ -49,7 +51,7 @@ func (h *Handler) logStreamActions() map[string]actionFn {
},
"DeleteLogStream": func(ctx context.Context, b []byte) (any, error) {
var input deleteLogStreamInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.DeleteLogStream(ctx, input.LogGroupName, input.LogStreamName); err != nil {
@@ -60,7 +62,7 @@ func (h *Handler) logStreamActions() map[string]actionFn {
},
"DescribeLogStreams": func(ctx context.Context, b []byte) (any, error) {
var input describeLogStreamsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := validateDescribeLimit(input.Limit); err != nil {
diff --git a/services/cloudwatchlogs/handler_log_streams_test.go b/services/cloudwatchlogs/handler_log_streams_test.go
index 5d78f05e13..2d4a094925 100644
--- a/services/cloudwatchlogs/handler_log_streams_test.go
+++ b/services/cloudwatchlogs/handler_log_streams_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -25,7 +26,7 @@ func TestHandler_DeleteLogStream(t *testing.T) {
rec := doLogsRequest(t, h, e, "DescribeLogStreams", `{"logGroupName":"g"}`)
require.Equal(t, http.StatusOK, rec.Code)
var desc1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc1, jsonx.V1()))
assert.Len(t, desc1["logStreams"], 1)
// Delete it.
@@ -36,7 +37,7 @@ func TestHandler_DeleteLogStream(t *testing.T) {
rec = doLogsRequest(t, h, e, "DescribeLogStreams", `{"logGroupName":"g"}`)
require.Equal(t, http.StatusOK, rec.Code)
var desc2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc2, jsonx.V1()))
assert.Empty(t, desc2["logStreams"])
// Delete non-existent stream → 404.
@@ -171,7 +172,7 @@ func TestHandler_LogStreamOperations(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
@@ -179,13 +180,13 @@ func TestHandler_LogStreamOperations(t *testing.T) {
if tt.wantListField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp[tt.wantListField].([]any), tt.wantListLen)
}
if tt.wantNotEmptyField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp[tt.wantNotEmptyField])
}
})
diff --git a/services/cloudwatchlogs/handler_lookup_tables.go b/services/cloudwatchlogs/handler_lookup_tables.go
index 80ebb3626b..10de41158a 100644
--- a/services/cloudwatchlogs/handler_lookup_tables.go
+++ b/services/cloudwatchlogs/handler_lookup_tables.go
@@ -2,8 +2,10 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createLookupTableInput struct {
@@ -20,7 +22,7 @@ func (h *Handler) handleCreateLookupTable(
body []byte,
) (any, error) {
var in createLookupTableInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -53,7 +55,7 @@ func (h *Handler) handleGetLookupTable(
body []byte,
) (any, error) {
var in getLookupTableInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -83,7 +85,7 @@ func (h *Handler) handleUpdateLookupTable(
body []byte,
) (any, error) {
var in updateLookupTableInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -112,7 +114,7 @@ func (h *Handler) handleDeleteLookupTable(
body []byte,
) (any, error) {
var in deleteLookupTableInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -136,7 +138,7 @@ func (h *Handler) handleDescribeLookupTables(
body []byte,
) (any, error) {
var in describeLookupTablesInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
diff --git a/services/cloudwatchlogs/handler_metric_filters.go b/services/cloudwatchlogs/handler_metric_filters.go
index 00a6582879..2c86af05b2 100644
--- a/services/cloudwatchlogs/handler_metric_filters.go
+++ b/services/cloudwatchlogs/handler_metric_filters.go
@@ -2,7 +2,9 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- PutMetricFilter ---.
@@ -53,7 +55,7 @@ type testMetricFilterOutput struct {
func (h *Handler) handlePutMetricFilter(ctx context.Context, b []byte) (any, error) {
var input putMetricFilterInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.PutMetricFilterWithOptions(
@@ -76,7 +78,7 @@ func (h *Handler) handlePutMetricFilter(ctx context.Context, b []byte) (any, err
func (h *Handler) handleDescribeMetricFilters(ctx context.Context, b []byte) (any, error) {
var input describeMetricFiltersInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
filters, next, err := h.Backend.DescribeMetricFilters(
@@ -97,7 +99,7 @@ func (h *Handler) handleDescribeMetricFilters(ctx context.Context, b []byte) (an
func (h *Handler) handleDeleteMetricFilter(ctx context.Context, b []byte) (any, error) {
var input deleteMetricFilterInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.DeleteMetricFilter(ctx, input.LogGroupName, input.FilterName); err != nil {
@@ -112,7 +114,7 @@ func (h *Handler) handleTestMetricFilter(
b []byte,
) (any, error) {
var input testMetricFilterInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
matches, err := h.Backend.TestMetricFilter(input.FilterPattern, input.LogEventMessages)
diff --git a/services/cloudwatchlogs/handler_queries.go b/services/cloudwatchlogs/handler_queries.go
index 4887014d96..c6a1fd70ed 100644
--- a/services/cloudwatchlogs/handler_queries.go
+++ b/services/cloudwatchlogs/handler_queries.go
@@ -2,9 +2,11 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type startQueryInput struct {
@@ -74,7 +76,7 @@ type listLogGroupsForQueryOutput struct {
func (h *Handler) handleStartQuery(ctx context.Context, b []byte) (any, error) {
var input startQueryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -103,7 +105,7 @@ func (h *Handler) handleStartQuery(ctx context.Context, b []byte) (any, error) {
func (h *Handler) handleGetQueryResults(ctx context.Context, b []byte) (any, error) { //nolint:revive // existing issue.
var input getQueryResultsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
results, stats, status, err := h.Backend.GetQueryResults(input.QueryID)
@@ -140,7 +142,7 @@ func (h *Handler) handleGetQueryResults(ctx context.Context, b []byte) (any, err
func (h *Handler) handleStopQuery(ctx context.Context, b []byte) (any, error) { //nolint:revive // existing issue.
var input stopQueryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.StopQuery(input.QueryID); err != nil {
@@ -152,7 +154,7 @@ func (h *Handler) handleStopQuery(ctx context.Context, b []byte) (any, error) {
func (h *Handler) handleDescribeQueries(ctx context.Context, b []byte) (any, error) { //nolint:revive // existing issue.
var input describeQueriesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
queries, next, err := h.Backend.DescribeQueries(
@@ -179,7 +181,7 @@ func (h *Handler) handleListLogGroupsForQuery(
b []byte,
) (any, error) {
var input listLogGroupsForQueryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
groups, err := h.Backend.ListLogGroupsForQuery(input.QueryID)
diff --git a/services/cloudwatchlogs/handler_queries_test.go b/services/cloudwatchlogs/handler_queries_test.go
index 16ef2d1511..5267878b5c 100644
--- a/services/cloudwatchlogs/handler_queries_test.go
+++ b/services/cloudwatchlogs/handler_queries_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -45,7 +46,7 @@ func TestHandler_InsightsWorkflow(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startOut, jsonx.V1()))
queryID := startOut["queryId"]
require.NotEmpty(t, queryID)
@@ -54,7 +55,7 @@ func TestHandler_InsightsWorkflow(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resultsOut map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resultsOut))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resultsOut, jsonx.V1()))
assert.Equal(t, "Complete", resultsOut["status"])
rows, ok := resultsOut["results"].([]any)
require.True(t, ok)
@@ -65,7 +66,7 @@ func TestHandler_InsightsWorkflow(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &descOut, jsonx.V1()))
queries, ok := descOut["queries"].([]any)
require.True(t, ok)
assert.Len(t, queries, 1)
@@ -83,7 +84,7 @@ func TestHandler_InsightsWorkflow(t *testing.T) {
require.Equal(t, http.StatusOK, rec4.Code)
var stopOut map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &stopOut))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &stopOut, jsonx.V1()))
assert.Equal(t, true, stopOut["success"])
// GetQueryResults now shows Cancelled.
@@ -91,7 +92,7 @@ func TestHandler_InsightsWorkflow(t *testing.T) {
require.Equal(t, http.StatusOK, rec5.Code)
var resultsOut2 map[string]any
- require.NoError(t, json.Unmarshal(rec5.Body.Bytes(), &resultsOut2))
+ require.NoError(t, json.Unmarshal(rec5.Body.Bytes(), &resultsOut2, jsonx.V1()))
assert.Equal(t, "Cancelled", resultsOut2["status"])
}
@@ -163,21 +164,21 @@ func TestHandler_InsightsQueryLanguage(t *testing.T) {
"queryString": tt.query,
"startTime": 0,
"endTime": 0,
- })
+ }, jsonx.V1())
require.NoError(t, merr)
rec := doLogsRequest(t, h, e, "StartQuery", string(bodyJSON))
require.Equal(t, http.StatusOK, rec.Code)
var startOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startOut, jsonx.V1()))
queryID := startOut["queryId"]
rec2 := doLogsRequest(t, h, e, "GetQueryResults", `{"queryId":"`+queryID+`"}`)
require.Equal(t, http.StatusOK, rec2.Code)
var resultsOut map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resultsOut))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resultsOut, jsonx.V1()))
rows, ok := resultsOut["results"].([]any)
require.True(t, ok, "results should be an array")
assert.Len(t, rows, tt.wantResults)
@@ -232,14 +233,14 @@ func TestHandler_InsightsARNIdentifier(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, "StartQuery", string(bodyBytes))
require.Equal(t, http.StatusOK, rec.Code)
var startOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startOut, jsonx.V1()))
queryID := startOut["queryId"]
require.NotEmpty(t, queryID)
@@ -247,7 +248,7 @@ func TestHandler_InsightsARNIdentifier(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resultsOut map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resultsOut))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resultsOut, jsonx.V1()))
rows, ok := resultsOut["results"].([]any)
require.True(t, ok)
assert.Len(t, rows, tt.wantResults)
@@ -271,7 +272,7 @@ func TestHandler_DescribeQueries_Pagination(t *testing.T) {
"queryString": "fields @message",
"startTime": i,
"endTime": 0,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, "StartQuery", string(bodyBytes))
@@ -283,7 +284,7 @@ func TestHandler_DescribeQueries_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
page1Queries, ok := page1["queries"].([]any)
require.True(t, ok, "page1 queries should be an array")
assert.Len(t, page1Queries, 2)
@@ -295,14 +296,14 @@ func TestHandler_DescribeQueries_Pagination(t *testing.T) {
"logGroupName": "/pg",
"maxResults": 2,
"nextToken": nextToken,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec2 := doLogsRequest(t, h, e, "DescribeQueries", string(bodyBytes))
require.Equal(t, http.StatusOK, rec2.Code)
var page2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
page2Queries, ok := page2["queries"].([]any)
require.True(t, ok, "page2 queries should be an array")
assert.Len(t, page2Queries, 1)
@@ -416,7 +417,7 @@ func TestHandler_QueryOperations(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
@@ -424,13 +425,13 @@ func TestHandler_QueryOperations(t *testing.T) {
if tt.wantListField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp[tt.wantListField].([]any), tt.wantListLen)
}
if tt.wantNotEmptyField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp[tt.wantNotEmptyField])
}
})
diff --git a/services/cloudwatchlogs/handler_query_definitions.go b/services/cloudwatchlogs/handler_query_definitions.go
index ab82acbb26..b849622350 100644
--- a/services/cloudwatchlogs/handler_query_definitions.go
+++ b/services/cloudwatchlogs/handler_query_definitions.go
@@ -2,7 +2,9 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- PutQueryDefinition ---.
@@ -46,7 +48,7 @@ func (h *Handler) handlePutQueryDefinition(
b []byte,
) (any, error) {
var input putQueryDefinitionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
id, err := h.Backend.PutQueryDefinitionWithLanguage(
@@ -69,7 +71,7 @@ func (h *Handler) handleDescribeQueryDefinitions(
b []byte,
) (any, error) {
var input describeQueryDefinitionsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
defs, next, err := h.Backend.DescribeQueryDefinitionsByLanguage(
@@ -90,7 +92,7 @@ func (h *Handler) handleDeleteQueryDefinition(
b []byte,
) (any, error) {
var input deleteQueryDefinitionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.DeleteQueryDefinition(input.QueryDefinitionID); err != nil {
diff --git a/services/cloudwatchlogs/handler_resource_policies.go b/services/cloudwatchlogs/handler_resource_policies.go
index 244539152f..39aa9697f9 100644
--- a/services/cloudwatchlogs/handler_resource_policies.go
+++ b/services/cloudwatchlogs/handler_resource_policies.go
@@ -2,8 +2,10 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type putResourcePolicyInput struct {
@@ -23,7 +25,7 @@ func (h *Handler) handlePutResourcePolicy(
body []byte,
) (any, error) {
var in putResourcePolicyInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -58,7 +60,7 @@ func (h *Handler) handleDescribeResourcePolicies(
body []byte,
) (any, error) {
var in describeResourcePoliciesInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -82,7 +84,7 @@ func (h *Handler) handleDeleteResourcePolicy(
body []byte,
) (any, error) {
var in deleteResourcePolicyInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
diff --git a/services/cloudwatchlogs/handler_resource_policies_test.go b/services/cloudwatchlogs/handler_resource_policies_test.go
index 888a2b5f77..6c9c72186b 100644
--- a/services/cloudwatchlogs/handler_resource_policies_test.go
+++ b/services/cloudwatchlogs/handler_resource_policies_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -82,7 +83,7 @@ func TestHandler_ResourcePolicy(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
assert.Equal(t, tt.wantCode, rec.Code)
@@ -126,13 +127,13 @@ func TestHandler_ResourcePolicyResponseShape(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
require.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for _, field := range tt.wantFields {
assert.Contains(t, resp, field, "response should contain field %q", field)
}
diff --git a/services/cloudwatchlogs/handler_scheduled_queries.go b/services/cloudwatchlogs/handler_scheduled_queries.go
index 836e74bd55..ae5688bcf9 100644
--- a/services/cloudwatchlogs/handler_scheduled_queries.go
+++ b/services/cloudwatchlogs/handler_scheduled_queries.go
@@ -2,8 +2,10 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createScheduledQueryInput struct {
@@ -153,7 +155,7 @@ func (h *Handler) handleCreateScheduledQuery(
b []byte,
) (any, error) {
var input createScheduledQueryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -195,7 +197,7 @@ func (h *Handler) handleDeleteScheduledQuery(
b []byte,
) (any, error) {
var input deleteScheduledQueryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.DeleteScheduledQuery(input.Identifier); err != nil {
@@ -210,7 +212,7 @@ func (h *Handler) handleListScheduledQueries(
b []byte,
) (any, error) {
var input listScheduledQueriesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
queries, next, err := h.Backend.ListScheduledQueries(
@@ -233,7 +235,7 @@ func (h *Handler) handleUpdateScheduledQuery(
b []byte,
) (any, error) {
var input updateScheduledQueryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -252,7 +254,7 @@ func (h *Handler) handleGetScheduledQuery(
b []byte,
) (any, error) {
var input getScheduledQueryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
sq, err := h.Backend.GetScheduledQuery(input.Identifier)
@@ -274,7 +276,7 @@ func (h *Handler) handleGetScheduledQueryHistory(
b []byte,
) (any, error) {
var input getScheduledQueryHistoryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/cloudwatchlogs/handler_scheduled_queries_test.go b/services/cloudwatchlogs/handler_scheduled_queries_test.go
index 393d065a8f..e4d2e7e1d7 100644
--- a/services/cloudwatchlogs/handler_scheduled_queries_test.go
+++ b/services/cloudwatchlogs/handler_scheduled_queries_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -31,7 +32,7 @@ func TestHandler_ScheduledQuery_Create(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
queryARN, ok := out["scheduledQueryArn"].(string)
require.True(t, ok)
assert.Contains(t, queryARN, "scheduled-query")
@@ -60,7 +61,7 @@ func TestHandler_GetScheduledQuery_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
queryARN, ok := createOut["scheduledQueryArn"].(string)
require.True(t, ok)
require.NotEmpty(t, queryARN)
@@ -69,7 +70,7 @@ func TestHandler_GetScheduledQuery_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var sq map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &sq))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &sq, jsonx.V1()))
assert.Equal(t, queryARN, sq["scheduledQueryArn"], "wire key must be scheduledQueryArn, not arn")
_, hasOldKey := sq["arn"]
@@ -129,7 +130,7 @@ func TestHandler_ScheduledQuery_DestinationConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
queryARN, arnOK := createOut["scheduledQueryArn"].(string)
require.True(t, arnOK)
@@ -137,7 +138,7 @@ func TestHandler_ScheduledQuery_DestinationConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var sq map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &sq))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &sq, jsonx.V1()))
destRaw, hasDest := sq["destinationConfiguration"]
if !tt.wantS3 && !tt.wantLookup {
@@ -282,7 +283,7 @@ func TestHandler_CreateScheduledQueryOperations(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
@@ -290,7 +291,7 @@ func TestHandler_CreateScheduledQueryOperations(t *testing.T) {
if tt.wantCode == http.StatusOK && tt.wantKey != "" {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tt.wantVal != "" {
assert.Equal(t, tt.wantVal, out[tt.wantKey])
} else {
@@ -321,7 +322,7 @@ func TestHandler_GetScheduledQueryHistory_RequiresTimeRange(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
arn, _ := created["scheduledQueryArn"].(string)
require.NotEmpty(t, arn)
diff --git a/services/cloudwatchlogs/handler_storage_tier_policy.go b/services/cloudwatchlogs/handler_storage_tier_policy.go
index 1853cf8301..14ec12c599 100644
--- a/services/cloudwatchlogs/handler_storage_tier_policy.go
+++ b/services/cloudwatchlogs/handler_storage_tier_policy.go
@@ -2,8 +2,10 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// storageTierPolicyWireShape maps a StorageTierPolicy to the real
@@ -42,7 +44,7 @@ func (h *Handler) handlePutStorageTierPolicy(
body []byte,
) (any, error) {
var in putStorageTierPolicyInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
diff --git a/services/cloudwatchlogs/handler_subscription_filters.go b/services/cloudwatchlogs/handler_subscription_filters.go
index 4da6f705ab..735e1133b0 100644
--- a/services/cloudwatchlogs/handler_subscription_filters.go
+++ b/services/cloudwatchlogs/handler_subscription_filters.go
@@ -2,7 +2,9 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type putSubscriptionFilterInput struct {
@@ -43,7 +45,7 @@ func (h *Handler) subscriptionFilterActions() map[string]actionFn {
return map[string]actionFn{
"PutSubscriptionFilter": func(ctx context.Context, b []byte) (any, error) {
var input putSubscriptionFilterInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.PutSubscriptionFilterWithOptions(
@@ -63,7 +65,7 @@ func (h *Handler) subscriptionFilterActions() map[string]actionFn {
},
"DescribeSubscriptionFilters": func(ctx context.Context, b []byte) (any, error) {
var input describeSubscriptionFiltersInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
filters, next, err := h.Backend.DescribeSubscriptionFilters(
@@ -78,7 +80,7 @@ func (h *Handler) subscriptionFilterActions() map[string]actionFn {
},
"DeleteSubscriptionFilter": func(ctx context.Context, b []byte) (any, error) {
var input deleteSubscriptionFilterInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.DeleteSubscriptionFilter(ctx, input.LogGroupName, input.FilterName); err != nil {
diff --git a/services/cloudwatchlogs/handler_subscription_filters_test.go b/services/cloudwatchlogs/handler_subscription_filters_test.go
index f5146adba7..962cff3059 100644
--- a/services/cloudwatchlogs/handler_subscription_filters_test.go
+++ b/services/cloudwatchlogs/handler_subscription_filters_test.go
@@ -2,7 +2,7 @@ package cloudwatchlogs_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -152,7 +153,7 @@ func TestHandler_SubscriptionFilterOperations(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
@@ -160,13 +161,13 @@ func TestHandler_SubscriptionFilterOperations(t *testing.T) {
if tt.wantListField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp[tt.wantListField].([]any), tt.wantListLen)
}
if tt.wantNotEmptyField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp[tt.wantNotEmptyField])
}
})
diff --git a/services/cloudwatchlogs/handler_syslog_configurations.go b/services/cloudwatchlogs/handler_syslog_configurations.go
index 5609fd91a7..681532efa3 100644
--- a/services/cloudwatchlogs/handler_syslog_configurations.go
+++ b/services/cloudwatchlogs/handler_syslog_configurations.go
@@ -2,8 +2,10 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type putSyslogConfigurationInput struct {
@@ -13,7 +15,7 @@ type putSyslogConfigurationInput struct {
func (h *Handler) handlePutSyslogConfiguration(ctx context.Context, body []byte) (any, error) {
var in putSyslogConfigurationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -39,7 +41,7 @@ func (h *Handler) handleDeleteSyslogConfiguration(
body []byte,
) (any, error) {
var in deleteSyslogConfigurationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -64,7 +66,7 @@ func (h *Handler) handleListSyslogConfigurations(
body []byte,
) (any, error) {
var in listSyslogConfigurationsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
diff --git a/services/cloudwatchlogs/handler_tags.go b/services/cloudwatchlogs/handler_tags.go
index 3f457eadc1..da61a90e39 100644
--- a/services/cloudwatchlogs/handler_tags.go
+++ b/services/cloudwatchlogs/handler_tags.go
@@ -2,10 +2,11 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"maps"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -136,7 +137,7 @@ func (h *Handler) logTagActions() map[string]actionFn {
return map[string]actionFn{
"ListTagsLogGroup": func(ctx context.Context, b []byte) (any, error) { //nolint:revive // existing issue.
var input listTagsLogGroupInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -144,7 +145,7 @@ func (h *Handler) logTagActions() map[string]actionFn {
},
"ListTagsForResource": func(ctx context.Context, b []byte) (any, error) { //nolint:revive // existing issue.
var input listTagsForResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -152,7 +153,7 @@ func (h *Handler) logTagActions() map[string]actionFn {
},
"TagLogGroup": func(ctx context.Context, b []byte) (any, error) { //nolint:revive // existing issue.
var input tagLogGroupInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
var kv map[string]string
@@ -165,7 +166,7 @@ func (h *Handler) logTagActions() map[string]actionFn {
},
"UntagLogGroup": func(ctx context.Context, b []byte) (any, error) { //nolint:revive // existing issue.
var input untagLogGroupInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
h.removeTags(input.LogGroupName, input.Tags)
@@ -174,7 +175,7 @@ func (h *Handler) logTagActions() map[string]actionFn {
},
"TagResource": func(ctx context.Context, b []byte) (any, error) { //nolint:revive // existing issue.
var input tagResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
h.setTags(input.ResourceArn, maps.Clone(input.Tags))
@@ -183,7 +184,7 @@ func (h *Handler) logTagActions() map[string]actionFn {
},
"UntagResource": func(ctx context.Context, b []byte) (any, error) { //nolint:revive // existing issue.
var input untagResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
h.removeTags(input.ResourceArn, input.TagKeys)
diff --git a/services/cloudwatchlogs/handler_tags_test.go b/services/cloudwatchlogs/handler_tags_test.go
index ef4204dd59..9da29d833a 100644
--- a/services/cloudwatchlogs/handler_tags_test.go
+++ b/services/cloudwatchlogs/handler_tags_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -33,7 +34,7 @@ func TestHandler_TagRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Equal(t, "prod", listResp["tags"]["env"])
assert.Equal(t, "ops", listResp["tags"]["team"])
@@ -42,7 +43,7 @@ func TestHandler_TagRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var listResp2 map[string]map[string]string
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listResp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listResp2, jsonx.V1()))
assert.Len(t, listResp2["tags"], 2)
// Remove one tag.
@@ -53,7 +54,7 @@ func TestHandler_TagRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var listResp3 map[string]map[string]string
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listResp3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listResp3, jsonx.V1()))
assert.Len(t, listResp3["tags"], 1)
assert.Equal(t, "ops", listResp3["tags"]["team"])
}
@@ -75,7 +76,7 @@ func TestHandler_TagResource_UntagResource(t *testing.T) {
rec = doLogsRequest(t, h, e, "ListTagsForResource", `{"resourceArn":"`+arn+`"}`)
require.Equal(t, http.StatusOK, rec.Code)
var listOut map[string]map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Equal(t, "prod", listOut["tags"]["env"])
assert.Equal(t, "platform", listOut["tags"]["team"])
@@ -87,7 +88,7 @@ func TestHandler_TagResource_UntagResource(t *testing.T) {
// Verify env tag removed, team tag remains.
rec = doLogsRequest(t, h, e, "ListTagsForResource", `{"resourceArn":"`+arn+`"}`)
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Empty(t, listOut["tags"]["env"])
assert.Equal(t, "platform", listOut["tags"]["team"])
}
@@ -111,7 +112,7 @@ func TestHandler_Reset_ClearsTags(t *testing.T) {
`{"resourceArn":"arn:aws:logs:us-east-1:123:log-group:/g"}`)
require.Equal(t, http.StatusOK, rec.Code)
var listOut map[string]map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Empty(t, listOut["tags"])
}
@@ -205,7 +206,7 @@ func TestHandler_TagOperations(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
@@ -213,13 +214,13 @@ func TestHandler_TagOperations(t *testing.T) {
if tt.wantListField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp[tt.wantListField].([]any), tt.wantListLen)
}
if tt.wantNotEmptyField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp[tt.wantNotEmptyField])
}
})
diff --git a/services/cloudwatchlogs/handler_test.go b/services/cloudwatchlogs/handler_test.go
index ed85c2b0d2..4e23db3a6d 100644
--- a/services/cloudwatchlogs/handler_test.go
+++ b/services/cloudwatchlogs/handler_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -65,7 +66,7 @@ func TestHandler_GetSupportedOperations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var ops []string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops, jsonx.V1()))
assert.Contains(t, ops, "CreateLogGroup")
assert.Contains(t, ops, "PutLogEvents")
}
@@ -299,14 +300,14 @@ func TestHandler_LookupTables(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
assert.Equal(t, tt.wantCode, rec.Code)
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for _, field := range tt.wantFields {
assert.Contains(t, resp, field, "response should contain field %q", field)
}
@@ -325,7 +326,7 @@ func TestHandler_LookupTable_GetRealResponseShape(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
tableArn, ok := created["lookupTableArn"].(string)
require.True(t, ok)
require.NotEmpty(t, tableArn)
@@ -334,7 +335,7 @@ func TestHandler_LookupTable_GetRealResponseShape(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "shape_table", got["lookupTableName"])
assert.Equal(t, "id,name\n1,foo\n", got["tableBody"])
assert.Contains(t, got, "sizeBytes")
@@ -402,7 +403,7 @@ func TestHandler_SyslogConfigurations(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
assert.Equal(t, tt.wantCode, rec.Code)
@@ -426,7 +427,7 @@ func TestHandler_SyslogConfiguration_ListResponseShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp["syslogConfigurations"].([]any)
require.True(t, ok)
require.Len(t, list, 1)
@@ -496,7 +497,7 @@ func TestHandler_StorageTierPolicy(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
require.Equal(t, tt.wantCode, rec.Code)
@@ -506,7 +507,7 @@ func TestHandler_StorageTierPolicy(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantTier, resp["storageTier"])
if tt.wantHasLUT {
@@ -550,7 +551,7 @@ func TestHandler_UnknownOperation(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
@@ -558,13 +559,13 @@ func TestHandler_UnknownOperation(t *testing.T) {
if tt.wantListField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp[tt.wantListField].([]any), tt.wantListLen)
}
if tt.wantNotEmptyField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp[tt.wantNotEmptyField])
}
})
diff --git a/services/cloudwatchlogs/handler_transformers.go b/services/cloudwatchlogs/handler_transformers.go
index 2af8d8dd9c..4f141b167f 100644
--- a/services/cloudwatchlogs/handler_transformers.go
+++ b/services/cloudwatchlogs/handler_transformers.go
@@ -2,8 +2,10 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type putTransformerInput struct {
@@ -16,7 +18,7 @@ func (h *Handler) handlePutTransformer(
body []byte,
) (any, error) {
var in putTransformerInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -38,7 +40,7 @@ func (h *Handler) handleGetTransformer(
body []byte,
) (any, error) {
var in getTransformerInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -78,7 +80,7 @@ func (h *Handler) handleDeleteTransformer(
body []byte,
) (any, error) {
var in deleteTransformerInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
@@ -103,7 +105,7 @@ func (h *Handler) handleTestTransformer(
LogEventMessages []string `json:"logEventMessages"`
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, err
}
}
diff --git a/services/cloudwatchlogs/handler_transformers_test.go b/services/cloudwatchlogs/handler_transformers_test.go
index 040bd90f76..e69fc09d2b 100644
--- a/services/cloudwatchlogs/handler_transformers_test.go
+++ b/services/cloudwatchlogs/handler_transformers_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -112,7 +113,7 @@ func TestHandler_Transformer(t *testing.T) {
tt.setup(t, h, e)
}
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, h, e, tt.action, string(bodyBytes))
assert.Equal(t, tt.wantCode, rec.Code)
diff --git a/services/cloudwatchlogs/insights_parse.go b/services/cloudwatchlogs/insights_parse.go
index 9fe13e8782..487e3ac956 100644
--- a/services/cloudwatchlogs/insights_parse.go
+++ b/services/cloudwatchlogs/insights_parse.go
@@ -1,12 +1,14 @@
package cloudwatchlogs
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"strconv"
"strings"
"sync"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// parseParseCommand parses "parse [source] <"glob"|/regex/> [as f1, f2, ...]".
@@ -199,7 +201,7 @@ func resolveJSONField(message, name string) cwValue {
}
var root any
- if err := json.Unmarshal([]byte(message), &root); err != nil {
+ if err := json.Unmarshal([]byte(message), &root, jsonx.V1()); err != nil {
return cwValue{}
}
@@ -273,7 +275,7 @@ func jsonScalar(v any) cwValue {
case nil:
return cwValue{}
default:
- if b, err := json.Marshal(t); err == nil {
+ if b, err := json.Marshal(t, jsonx.V1()); err == nil {
return literalString(string(b))
}
diff --git a/services/cloudwatchlogs/log_events.go b/services/cloudwatchlogs/log_events.go
index c8e807d3f8..518be23d78 100644
--- a/services/cloudwatchlogs/log_events.go
+++ b/services/cloudwatchlogs/log_events.go
@@ -4,7 +4,8 @@ import (
"cmp"
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"slices"
"sort"
@@ -13,6 +14,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/cwmetric"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// validatePutLogEventsBatch checks the batch size constraints for PutLogEvents.
@@ -954,8 +956,8 @@ func jsonMessageFields(message string) []string {
return nil
}
- var obj map[string]json.RawMessage
- if err := json.Unmarshal([]byte(trimmed), &obj); err != nil {
+ var obj map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(trimmed), &obj, jsonx.V1()); err != nil {
return nil
}
diff --git a/services/cloudwatchlogs/perf_sweep_bench_test.go b/services/cloudwatchlogs/perf_sweep_bench_test.go
index ab5cb2f792..074e4a388a 100644
--- a/services/cloudwatchlogs/perf_sweep_bench_test.go
+++ b/services/cloudwatchlogs/perf_sweep_bench_test.go
@@ -6,7 +6,7 @@ package cloudwatchlogs_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -16,6 +16,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -29,7 +30,7 @@ func invokeLogsB(
) (int, map[string]any) {
tb.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(tb, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
@@ -41,7 +42,7 @@ func invokeLogsB(
var resp map[string]any
if rec.Body.Len() > 0 {
- require.NoError(tb, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(tb, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
}
return rec.Code, resp
diff --git a/services/cloudwatchlogs/persistence.go b/services/cloudwatchlogs/persistence.go
index c04fc4a132..ec5d874167 100644
--- a/services/cloudwatchlogs/persistence.go
+++ b/services/cloudwatchlogs/persistence.go
@@ -2,7 +2,7 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"time"
@@ -319,10 +319,10 @@ func newDirtyDTORegistry() (
// snapshot from an incompatible (older or newer) build of this backend as
// though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/cloudwatchlogs/presence_validation_test.go b/services/cloudwatchlogs/presence_validation_test.go
index a4437127fc..1f31e55408 100644
--- a/services/cloudwatchlogs/presence_validation_test.go
+++ b/services/cloudwatchlogs/presence_validation_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -50,7 +51,7 @@ func TestGetLogFields_DataSourceTypePresenceValidation(t *testing.T) {
handler := cloudwatchlogs.NewHandler(backend)
doLogsRequest(t, handler, e, "CreateLogGroup", `{"logGroupName":"grp"}`)
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := doLogsRequest(t, handler, e, "GetLogFields", string(bodyBytes))
@@ -58,7 +59,7 @@ func TestGetLogFields_DataSourceTypePresenceValidation(t *testing.T) {
if tt.wantCode == http.StatusBadRequest {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", resp["__type"])
}
})
@@ -115,14 +116,14 @@ func TestListAggregateLogGroupSummaries_GroupByPresenceValidation(t *testing.T)
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := makeLogsRequest(t, "ListAggregateLogGroupSummaries", string(bodyBytes))
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tt.wantCode == http.StatusBadRequest {
assert.Equal(t, tt.wantError, resp["__type"])
diff --git a/services/cloudwatchlogs/subscription_filters.go b/services/cloudwatchlogs/subscription_filters.go
index f8b5fddd2f..53b66e2216 100644
--- a/services/cloudwatchlogs/subscription_filters.go
+++ b/services/cloudwatchlogs/subscription_filters.go
@@ -5,7 +5,7 @@ import (
"compress/gzip"
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -283,7 +284,7 @@ func (b *InMemoryBackend) deliverToFilters(
// encodeSubscriptionPayload gzips the JSON payload and base64-encodes it.
func encodeSubscriptionPayload(payload subscriptionPayload) ([]byte, error) {
- raw, err := json.Marshal(payload)
+ raw, err := json.Marshal(payload, jsonx.V1())
if err != nil {
return nil, err
}
diff --git a/services/cloudwatchlogs/transformers.go b/services/cloudwatchlogs/transformers.go
index a47387ce62..0d18ea0701 100644
--- a/services/cloudwatchlogs/transformers.go
+++ b/services/cloudwatchlogs/transformers.go
@@ -2,10 +2,12 @@ package cloudwatchlogs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// PutTransformer creates or updates a log transformer.
@@ -134,7 +136,7 @@ func applyProcessorsToMessage(message string, processors []map[string]any) strin
return message
}
- out, err := json.Marshal(obj)
+ out, err := json.Marshal(obj, jsonx.V1())
if err != nil {
return message
}
@@ -149,7 +151,7 @@ func decodeJSONObject(message string) (map[string]any, bool) {
}
var obj map[string]any
- if err := json.Unmarshal([]byte(trimmed), &obj); err != nil {
+ if err := json.Unmarshal([]byte(trimmed), &obj, jsonx.V1()); err != nil {
return nil, false
}
diff --git a/services/cloudwatchlogs/undeclared_members_test.go b/services/cloudwatchlogs/undeclared_members_test.go
index 1e1b3c99c9..1e6c7c42e4 100644
--- a/services/cloudwatchlogs/undeclared_members_test.go
+++ b/services/cloudwatchlogs/undeclared_members_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -41,7 +42,7 @@ func TestHandler_DescribeExportTasks_OmitsLogStreamNamePrefix(t *testing.T) {
var out struct {
ExportTasks []map[string]any `json:"exportTasks"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.ExportTasks, 1)
assert.NotContains(t, out.ExportTasks[0], "logStreamNamePrefix")
assert.Equal(t, "/grp", out.ExportTasks[0]["logGroupName"])
diff --git a/services/cloudwatchlogs/wire_field_fixes_test.go b/services/cloudwatchlogs/wire_field_fixes_test.go
index 37e966cf35..b2b17b3a5b 100644
--- a/services/cloudwatchlogs/wire_field_fixes_test.go
+++ b/services/cloudwatchlogs/wire_field_fixes_test.go
@@ -1,7 +1,7 @@
package cloudwatchlogs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -711,7 +712,7 @@ func TestGetScheduledQueryHistory_RealShape(t *testing.T) {
require.Equal(t, http.StatusOK, rawRec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rawRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rawRec.Body.Bytes(), &raw, jsonx.V1()))
_, hasReal := raw["triggerHistory"]
assert.True(t, hasReal, "wrapper key must be triggerHistory")
_, hasFabricated := raw["scheduledQueryRunSummaries"]
diff --git a/services/codeartifact/domain_owner_and_error_message_test.go b/services/codeartifact/domain_owner_and_error_message_test.go
index 6ce1d3cf9d..4e5241bddd 100644
--- a/services/codeartifact/domain_owner_and_error_message_test.go
+++ b/services/codeartifact/domain_owner_and_error_message_test.go
@@ -1,7 +1,7 @@
package codeartifact_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDomainOwnerQuery(t *testing.T) {
@@ -64,7 +65,7 @@ func TestPackageVersionErrors_CarryMessage(t *testing.T) {
} `json:"failedVersions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Contains(t, out.Failed, "99.0.0")
assert.Equal(t, "NOT_FOUND", out.Failed["99.0.0"].Code)
assert.Contains(t, out.Failed["99.0.0"].Message, "99.0.0")
diff --git a/services/codeartifact/handler.go b/services/codeartifact/handler.go
index 1ad712b2be..992331a2cc 100644
--- a/services/codeartifact/handler.go
+++ b/services/codeartifact/handler.go
@@ -2,7 +2,8 @@ package codeartifact
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -14,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -632,9 +634,9 @@ func (h *Handler) readRequestBody(c *echo.Context, op string) ([]byte, error) {
return raw, nil
}
- decoder := json.NewDecoder(c.Request().Body)
- var raw json.RawMessage
- if err := decoder.Decode(&raw); err != nil {
+ decoder := jsontext.NewDecoder(c.Request().Body)
+ var raw jsontext.Value
+ if err := json.UnmarshalDecode(decoder, &raw, jsonx.V1()); err != nil {
raw = nil
}
diff --git a/services/codeartifact/handler_domains.go b/services/codeartifact/handler_domains.go
index 96055ddb47..4550877349 100644
--- a/services/codeartifact/handler_domains.go
+++ b/services/codeartifact/handler_domains.go
@@ -3,12 +3,14 @@ package codeartifact
import (
"crypto/rand"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const authTokenBytes = 48
@@ -60,7 +62,7 @@ func (h *Handler) handleCreateDomain(c *echo.Context, name string, body []byte)
var in createDomainBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("ValidationException", "invalid request body"))
}
}
@@ -105,7 +107,7 @@ type listDomainsBody struct {
func (h *Handler) handleListDomains(c *echo.Context, body []byte) error {
var in listDomainsBody
if len(body) > 0 {
- _ = json.Unmarshal(body, &in)
+ _ = json.Unmarshal(body, &in, jsonx.V1())
}
all := h.Backend.ListDomains(c.Request().Context())
@@ -217,7 +219,7 @@ type putDomainPermissionsPolicyBody struct {
func (h *Handler) handlePutDomainPermissionsPolicy(c *echo.Context, body []byte) error {
var in putDomainPermissionsPolicyBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("ValidationException", "invalid request body"))
}
}
diff --git a/services/codeartifact/handler_domains_test.go b/services/codeartifact/handler_domains_test.go
index 670e0b0415..37d14142cb 100644
--- a/services/codeartifact/handler_domains_test.go
+++ b/services/codeartifact/handler_domains_test.go
@@ -1,7 +1,7 @@
package codeartifact_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codeartifact"
)
@@ -50,7 +51,7 @@ func TestHandler_CreateAndDescribeDomain(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
domain, _ := resp["domain"].(map[string]any)
assert.Equal(t, tt.domainName, domain["name"])
assert.NotEmpty(t, domain["arn"])
@@ -60,7 +61,7 @@ func TestHandler_CreateAndDescribeDomain(t *testing.T) {
descRec := doRequest(t, h, http.MethodGet, "/v1/domain?domain="+tt.domainName, nil)
assert.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
ddomain, _ := descResp["domain"].(map[string]any)
assert.Equal(t, tt.domainName, ddomain["name"])
}
@@ -119,7 +120,7 @@ func TestHandler_ListDomains(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
domains, _ := resp["domains"].([]any)
assert.Len(t, domains, 2)
}
@@ -134,7 +135,7 @@ func TestHandler_GetAuthorizationToken(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["authorizationToken"])
assert.NotEmpty(t, resp["expiration"])
}
@@ -167,7 +168,7 @@ func TestHandler_DomainPermissionsPolicy(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(b, &resp))
+ require.NoError(t, json.Unmarshal(b, &resp, jsonx.V1()))
pol, _ := resp["policy"].(map[string]any)
assert.NotEmpty(t, pol["revision"])
assert.NotEmpty(t, pol["resourceArn"])
@@ -189,7 +190,7 @@ func TestHandler_DomainPermissionsPolicy(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(b, &resp))
+ require.NoError(t, json.Unmarshal(b, &resp, jsonx.V1()))
pol, _ := resp["policy"].(map[string]any)
assert.Contains(t, pol["document"], "Allow")
},
@@ -279,7 +280,7 @@ func TestHandler_DomainPermissionsPolicy_RevisionLocking(t *testing.T) {
})
require.Equal(t, http.StatusOK, putRec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
pol, _ := putResp["policy"].(map[string]any)
revision, _ := pol["revision"].(string)
require.NotEmpty(t, revision)
@@ -296,7 +297,7 @@ func TestHandler_DomainPermissionsPolicy_RevisionLocking(t *testing.T) {
getRec := doRequest(t, h, http.MethodGet, "/v1/domain/permissions/policy?domain=lock-domain", nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
getPol, _ := getResp["policy"].(map[string]any)
assert.NotContains(t, getPol["document"], "Deny")
@@ -318,7 +319,7 @@ func TestHandler_DomainPermissionsPolicy_RevisionLocking(t *testing.T) {
})
require.Equal(t, http.StatusOK, matchPutRec.Code)
var matchPutResp map[string]any
- require.NoError(t, json.Unmarshal(matchPutRec.Body.Bytes(), &matchPutResp))
+ require.NoError(t, json.Unmarshal(matchPutRec.Body.Bytes(), &matchPutResp, jsonx.V1()))
newPol, _ := matchPutResp["policy"].(map[string]any)
newRevision, _ := newPol["revision"].(string)
require.NotEmpty(t, newRevision)
@@ -387,7 +388,7 @@ func TestHandler_RepositoryCount(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
domainMap, _ := resp["domain"].(map[string]any)
count, _ := domainMap["repositoryCount"].(float64)
assert.InEpsilon(t, float64(2), count, 0)
@@ -405,7 +406,7 @@ func TestHandler_DomainSummaryVsFullDescription(t *testing.T) {
listRec := doRequest(t, h, http.MethodPost, "/v1/domains", nil)
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
domains, _ := listResp["domains"].([]any)
require.Len(t, domains, 1)
d, _ := domains[0].(map[string]any)
@@ -416,7 +417,7 @@ func TestHandler_DomainSummaryVsFullDescription(t *testing.T) {
descRec := doRequest(t, h, http.MethodGet, "/v1/domain?domain=summary-domain", nil)
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
dd, _ := descResp["domain"].(map[string]any)
count, _ := dd["repositoryCount"].(float64)
assert.InEpsilon(t, float64(2), count, 0)
@@ -438,7 +439,7 @@ func TestListDomains_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
page1, _ := out1["domains"].([]any)
assert.Len(t, page1, 2)
nextToken, ok := out1["nextToken"].(string)
@@ -448,7 +449,7 @@ func TestListDomains_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
page2, _ := out2["domains"].([]any)
assert.Len(t, page2, 2)
}
diff --git a/services/codeartifact/handler_internal_error_test.go b/services/codeartifact/handler_internal_error_test.go
index c8207c798f..7d927a1b9e 100644
--- a/services/codeartifact/handler_internal_error_test.go
+++ b/services/codeartifact/handler_internal_error_test.go
@@ -1,7 +1,7 @@
package codeartifact //nolint:testpackage // needs access to the unexported handleError method.
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/http/httptest"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errUnmatchedForTest = errors.New("boom: matches none of handleError's sentinel checks")
@@ -48,7 +50,7 @@ func TestHandleError_DefaultBranchEmitsInternalServerException(t *testing.T) {
assert.Equal(t, http.StatusInternalServerError, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InternalServerException", body["code"])
assert.NotEqual(t, "InternalFailure", body["code"])
}
diff --git a/services/codeartifact/handler_list_summary_test.go b/services/codeartifact/handler_list_summary_test.go
index 0775835fe2..7f2b7dfa15 100644
--- a/services/codeartifact/handler_list_summary_test.go
+++ b/services/codeartifact/handler_list_summary_test.go
@@ -1,7 +1,7 @@
package codeartifact_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,8 @@ import (
casdk "github.com/aws/aws-sdk-go-v2/service/codeartifact"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_ListPackages_SummaryShape locks that ListPackages emits the
@@ -36,7 +38,7 @@ func TestHandler_ListPackages_SummaryShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pkgs, ok := resp["packages"].([]any)
require.True(t, ok)
require.Len(t, pkgs, 1)
@@ -112,7 +114,7 @@ func TestHandler_ListPackageVersions_SummaryShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions, ok := resp["versions"].([]any)
require.True(t, ok)
require.Len(t, versions, 1)
diff --git a/services/codeartifact/handler_package_groups.go b/services/codeartifact/handler_package_groups.go
index 89e2d28fca..8305fb8ba8 100644
--- a/services/codeartifact/handler_package_groups.go
+++ b/services/codeartifact/handler_package_groups.go
@@ -2,11 +2,13 @@ package codeartifact
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// createPackageGroupBody's Pattern field is tagged json:"packageGroup" --
@@ -120,7 +122,7 @@ func (h *Handler) handleCreatePackageGroup(c *echo.Context, domainName string, b
var in createPackageGroupBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("ValidationException", "invalid request body"),
@@ -423,7 +425,7 @@ func (h *Handler) handleUpdatePackageGroup(c *echo.Context, domainName string, b
var in updatePackageGroupBody
if len(body) > 0 {
- _ = json.Unmarshal(body, &in)
+ _ = json.Unmarshal(body, &in, jsonx.V1())
}
pattern := c.Request().URL.Query().Get(keyPackageGroup)
@@ -496,7 +498,7 @@ func (h *Handler) handleUpdatePackageGroupOriginConfiguration(
var in updatePackageGroupOriginConfigurationBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errResp("ValidationException", "invalid request body"),
diff --git a/services/codeartifact/handler_package_groups_list_test.go b/services/codeartifact/handler_package_groups_list_test.go
index 8469ae50c2..4c49438684 100644
--- a/services/codeartifact/handler_package_groups_list_test.go
+++ b/services/codeartifact/handler_package_groups_list_test.go
@@ -2,7 +2,7 @@ package codeartifact_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codeartifact"
)
@@ -94,7 +95,7 @@ func TestHandler_ListAllowedRepositoriesForGroup(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["allowedRepositories"])
}
})
@@ -161,7 +162,7 @@ func TestHandler_ListAssociatedPackages(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pkgs, _ := resp["packages"].([]any)
assert.NotNil(t, pkgs)
}
@@ -199,7 +200,7 @@ func TestHandler_ListAssociatedPackages_Preview(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pkgs, _ := resp["packages"].([]any)
require.Len(t, pkgs, 1)
assert.Equal(t, "react", pkgs[0].(map[string]any)["package"])
@@ -256,7 +257,7 @@ func TestHandler_ListAssociatedPackages_MostSpecificMatch(t *testing.T) {
require.Equal(t, http.StatusOK, broadRec.Code)
var broadResp map[string]any
- require.NoError(t, json.Unmarshal(broadRec.Body.Bytes(), &broadResp))
+ require.NoError(t, json.Unmarshal(broadRec.Body.Bytes(), &broadResp, jsonx.V1()))
broadPkgs, _ := broadResp["packages"].([]any)
require.Len(t, broadPkgs, 1)
assert.Equal(t, "react", broadPkgs[0].(map[string]any)["package"])
@@ -272,7 +273,7 @@ func TestHandler_ListAssociatedPackages_MostSpecificMatch(t *testing.T) {
require.Equal(t, http.StatusOK, specificRec.Code)
var specificResp map[string]any
- require.NoError(t, json.Unmarshal(specificRec.Body.Bytes(), &specificResp))
+ require.NoError(t, json.Unmarshal(specificRec.Body.Bytes(), &specificResp, jsonx.V1()))
specificPkgs, _ := specificResp["packages"].([]any)
require.Len(t, specificPkgs, 1)
assert.Equal(t, "utils", specificPkgs[0].(map[string]any)["package"])
@@ -323,7 +324,7 @@ func TestHandler_ListAssociatedPackages_WeakMatch(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pkgs, _ := resp["packages"].([]any)
require.Len(t, pkgs, 2, "both the strong and weak variant are associated with the same group")
@@ -342,7 +343,7 @@ func TestHandler_ListAssociatedPackages_WeakMatch(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "WEAK", getResp["associationType"])
}
@@ -383,7 +384,7 @@ func TestHandler_GetAssociatedPackageGroup_MostSpecificMatch(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pg, _ := resp["packageGroup"].(map[string]any)
require.NotNil(t, pg)
assert.Equal(t, "/npm/space/react$", pg["pattern"])
@@ -398,7 +399,7 @@ func TestHandler_GetAssociatedPackageGroup_MostSpecificMatch(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
pg2, _ := resp2["packageGroup"].(map[string]any)
require.NotNil(t, pg2)
assert.Equal(t, "/npm/*", pg2["pattern"])
@@ -495,7 +496,7 @@ func TestHandler_ListPackageGroups(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups, _ := resp["packageGroups"].([]any)
assert.Len(t, groups, tt.wantCount)
}
@@ -582,7 +583,7 @@ func TestHandler_UpdatePackageGroup(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pg, _ := resp["packageGroup"].(map[string]any)
assert.NotNil(t, pg)
}
@@ -690,7 +691,7 @@ func TestHandler_UpdatePackageGroupOriginConfiguration(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pg, _ := resp["packageGroup"].(map[string]any)
require.NotNil(t, pg)
@@ -735,7 +736,7 @@ func TestHandler_UpdatePackageGroupOriginConfiguration_AllowedRepositories(t *te
require.Equal(t, http.StatusOK, addRec.Code)
var addResp map[string]any
- require.NoError(t, json.Unmarshal(addRec.Body.Bytes(), &addResp))
+ require.NoError(t, json.Unmarshal(addRec.Body.Bytes(), &addResp, jsonx.V1()))
updates, _ := addResp["allowedRepositoryUpdates"].(map[string]any)
require.NotNil(t, updates)
extUpstream, _ := updates["EXTERNAL_UPSTREAM"].(map[string]any)
@@ -752,7 +753,7 @@ func TestHandler_UpdatePackageGroupOriginConfiguration_AllowedRepositories(t *te
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
allowed, _ := listResp["allowedRepositories"].([]any)
assert.Equal(t, []any{"upgocr-repo"}, allowed)
@@ -776,7 +777,7 @@ func TestHandler_UpdatePackageGroupOriginConfiguration_AllowedRepositories(t *te
require.Equal(t, http.StatusOK, listRec2.Code)
var listResp2 map[string]any
- require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp2))
+ require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp2, jsonx.V1()))
allowed2, _ := listResp2["allowedRepositories"].([]any)
assert.Empty(t, allowed2)
}
@@ -828,7 +829,7 @@ func TestHandler_UpdatePackageGroupOriginConfiguration_Inheritance(t *testing.T)
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
pg, _ := descResp["packageGroup"].(map[string]any)
originConfig, _ := pg["originConfiguration"].(map[string]any)
restrictions, _ := originConfig["restrictions"].(map[string]any)
@@ -859,7 +860,7 @@ func TestHandler_UpdatePackageGroupOriginConfiguration_Inheritance(t *testing.T)
require.Equal(t, http.StatusOK, descRec2.Code)
var descResp2 map[string]any
- require.NoError(t, json.Unmarshal(descRec2.Body.Bytes(), &descResp2))
+ require.NoError(t, json.Unmarshal(descRec2.Body.Bytes(), &descResp2, jsonx.V1()))
pg2, _ := descResp2["packageGroup"].(map[string]any)
originConfig2, _ := pg2["originConfiguration"].(map[string]any)
restrictions2, _ := originConfig2["restrictions"].(map[string]any)
@@ -891,7 +892,7 @@ func TestListPackageGroups_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
page1, _ := out1["packageGroups"].([]any)
assert.Len(t, page1, 2)
nextToken, ok := out1["nextToken"].(string)
@@ -902,7 +903,7 @@ func TestListPackageGroups_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
page2, _ := out2["packageGroups"].([]any)
assert.Len(t, page2, 2)
}
diff --git a/services/codeartifact/handler_package_groups_test.go b/services/codeartifact/handler_package_groups_test.go
index b1983944ee..e9fcf5a5bb 100644
--- a/services/codeartifact/handler_package_groups_test.go
+++ b/services/codeartifact/handler_package_groups_test.go
@@ -2,7 +2,7 @@ package codeartifact_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codeartifact"
)
@@ -94,7 +95,7 @@ func TestHandler_CreatePackageGroup(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pg, _ := resp["packageGroup"].(map[string]any)
assert.NotEmpty(t, pg["arn"])
assert.Equal(t, "pg-domain", pg["domainName"])
@@ -125,7 +126,7 @@ func TestHandler_PackageGroupCRUD(t *testing.T) {
assert.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
pg, _ := createResp["packageGroup"].(map[string]any)
assert.Equal(t, "/npm/mygroup/*", pg["pattern"])
assert.Equal(t, "test group", pg["description"])
@@ -302,7 +303,7 @@ func TestHandler_ListSubPackageGroups(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups, _ := resp["packageGroups"].([]any)
assert.Len(t, groups, tt.wantCount)
}
@@ -329,7 +330,7 @@ func TestHandler_PackageGroupTags(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
pg, _ := createResp["packageGroup"].(map[string]any)
pgARN, _ := pg["arn"].(string)
require.NotEmpty(t, pgARN)
@@ -338,7 +339,7 @@ func TestHandler_PackageGroupTags(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tagList, _ := listResp["tags"].([]any)
assert.Len(t, tagList, 1)
}
@@ -383,7 +384,7 @@ func TestHandler_PackageGroupHierarchy(t *testing.T) {
allRec := doRequest(t, h, http.MethodPost, "/v1/package-groups?domain=hier-domain", nil)
require.Equal(t, http.StatusOK, allRec.Code)
var allResp map[string]any
- require.NoError(t, json.Unmarshal(allRec.Body.Bytes(), &allResp))
+ require.NoError(t, json.Unmarshal(allRec.Body.Bytes(), &allResp, jsonx.V1()))
allGroups, _ := allResp["packageGroups"].([]any)
assert.Len(t, allGroups, 5)
@@ -397,7 +398,7 @@ func TestHandler_PackageGroupHierarchy(t *testing.T) {
)
require.Equal(t, http.StatusOK, subRec.Code)
var subResp map[string]any
- require.NoError(t, json.Unmarshal(subRec.Body.Bytes(), &subResp))
+ require.NoError(t, json.Unmarshal(subRec.Body.Bytes(), &subResp, jsonx.V1()))
subGroups, _ := subResp["packageGroups"].([]any)
assert.Len(t, subGroups, 2)
@@ -411,7 +412,7 @@ func TestHandler_PackageGroupHierarchy(t *testing.T) {
)
require.Equal(t, http.StatusOK, subRec2.Code)
var subResp2 map[string]any
- require.NoError(t, json.Unmarshal(subRec2.Body.Bytes(), &subResp2))
+ require.NoError(t, json.Unmarshal(subRec2.Body.Bytes(), &subResp2, jsonx.V1()))
subGroups2, _ := subResp2["packageGroups"].([]any)
assert.Empty(t, subGroups2)
}
diff --git a/services/codeartifact/handler_package_version_rejection_test.go b/services/codeartifact/handler_package_version_rejection_test.go
index 694ff15b47..ac1f054df4 100644
--- a/services/codeartifact/handler_package_version_rejection_test.go
+++ b/services/codeartifact/handler_package_version_rejection_test.go
@@ -1,12 +1,14 @@
package codeartifact_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_PackageVersionOps_RejectionNotBypassed guards
@@ -54,7 +56,7 @@ func TestHandler_PackageVersionOps_RejectionNotBypassed(t *testing.T) {
var resp map[string]any
require.NoError(
- t, json.Unmarshal(rec.Body.Bytes(), &resp),
+ t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()),
"response body must be a single well-formed JSON document, not two concatenated: %s", rec.Body.String(),
)
assert.Equal(t, "ValidationException", resp["code"])
diff --git a/services/codeartifact/handler_package_versions.go b/services/codeartifact/handler_package_versions.go
index 4c47974849..23a100774b 100644
--- a/services/codeartifact/handler_package_versions.go
+++ b/services/codeartifact/handler_package_versions.go
@@ -3,13 +3,15 @@ package codeartifact
import (
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"slices"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func packageVersionToMap(pv *PackageVersion) map[string]any {
@@ -182,7 +184,7 @@ func (h *Handler) handleDeletePackageVersions(
var in deletePackageVersionsBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("ValidationException", "invalid request body"))
}
}
@@ -238,7 +240,7 @@ func (h *Handler) handleCopyPackageVersions(
var in copyPackageVersionsBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("ValidationException", "invalid request body"))
}
}
@@ -291,7 +293,7 @@ func (h *Handler) handleDisposePackageVersions(
var in disposeVersionsBody
if len(body) > 0 {
- _ = json.Unmarshal(body, &in)
+ _ = json.Unmarshal(body, &in, jsonx.V1())
}
successful, failed, err := h.Backend.DisposePackageVersions(
@@ -742,7 +744,7 @@ func (h *Handler) handleUpdatePackageVersionsStatus(
var in updateVersionsStatusBody
if len(body) > 0 {
- _ = json.Unmarshal(body, &in)
+ _ = json.Unmarshal(body, &in, jsonx.V1())
}
if in.TargetStatus == "" {
diff --git a/services/codeartifact/handler_package_versions_assets_test.go b/services/codeartifact/handler_package_versions_assets_test.go
index 35b6f5813f..fec917a576 100644
--- a/services/codeartifact/handler_package_versions_assets_test.go
+++ b/services/codeartifact/handler_package_versions_assets_test.go
@@ -2,7 +2,7 @@ package codeartifact_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codeartifact"
)
@@ -44,7 +45,7 @@ func TestHandler_PublishPackageVersion_AppearInList(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
versions, _ := resp["versions"].([]any)
assert.Len(t, versions, 2)
}
@@ -206,7 +207,7 @@ func TestHandler_GetPackageVersionReadme(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp["readme"]
assert.True(t, ok)
}
@@ -240,7 +241,7 @@ func TestHandler_GetPackageVersionReadme_FromPublishedPackageJSON(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "# My Package\n\nUsage instructions.", resp["readme"])
assert.Equal(t, "npm", resp["format"])
assert.Equal(t, "mypkg", resp["package"])
@@ -273,7 +274,7 @@ func TestHandler_GetPackageVersionReadme_NoPackageJSON(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["readme"])
}
@@ -310,7 +311,7 @@ func TestHandler_ListPackageVersionDependencies_FromPublishedPackageJSON(t *test
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
deps, _ := resp["dependencies"].([]any)
require.Len(t, deps, 4)
@@ -394,7 +395,7 @@ func TestHandler_ListPackageVersionAssets(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assets, _ := resp["assets"].([]any)
assert.NotNil(t, assets)
}
@@ -470,7 +471,7 @@ func TestHandler_ListPackageVersionDependencies(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
deps, _ := resp["dependencies"].([]any)
assert.NotNil(t, deps)
}
@@ -551,7 +552,7 @@ func TestHandler_ListPackageVersions(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions, _ := resp["versions"].([]any)
assert.Len(t, versions, tt.wantCount)
}
@@ -646,7 +647,7 @@ func TestHandler_PublishPackageVersion(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["version"])
assert.NotEmpty(t, resp["status"])
assert.NotEmpty(t, resp["asset"])
diff --git a/services/codeartifact/handler_package_versions_test.go b/services/codeartifact/handler_package_versions_test.go
index 71e314f95b..8829446637 100644
--- a/services/codeartifact/handler_package_versions_test.go
+++ b/services/codeartifact/handler_package_versions_test.go
@@ -1,7 +1,7 @@
package codeartifact_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codeartifact"
)
@@ -72,7 +73,7 @@ func TestHandler_DescribePackageVersion(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pv, _ := resp["packageVersion"].(map[string]any)
assert.Equal(t, "1.0.0", pv["version"])
assert.NotEmpty(t, pv["status"])
@@ -110,7 +111,7 @@ func TestHandler_DeletePackageVersions(t *testing.T) {
// aws-sdk-go-v2 deserializers.go's ...PackageVersionErrorMap/
// ...SuccessfulPackageVersionInfoMap.
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
failed, _ := resp["failedVersions"].(map[string]any)
require.Len(t, failed, 1)
failedEntry, _ := failed["99.0.0"].(map[string]any)
@@ -162,7 +163,7 @@ func TestHandler_CopyPackageVersions(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
failed, _ := resp["failedVersions"].(map[string]any)
require.Len(t, failed, 1)
failedEntry, _ := failed["9.9.9"].(map[string]any)
@@ -215,7 +216,7 @@ func TestHandler_PackageVersionRevision(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pv, _ := resp["packageVersion"].(map[string]any)
assert.NotEmpty(t, pv["revision"])
}
@@ -244,7 +245,7 @@ func TestHandler_SuccessfulVersions(t *testing.T) {
// deserializers.go's ...SuccessfulPackageVersionInfoMap/
// ...PackageVersionErrorMap.
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
successList, _ := resp["successfulVersions"].(map[string]any)
require.Len(t, successList, 1)
@@ -279,7 +280,7 @@ func TestHandler_SuccessfulVersions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// "Copied" is not a real PackageVersionStatus enum value (real values:
// Published/Unfinished/Unlisted/Archived/Disposed/Deleted) -- the copied
@@ -309,7 +310,7 @@ func TestHandler_PackageVersionMap(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pv, _ := resp["packageVersion"].(map[string]any)
publishedTime, _ := pv["publishedTime"].(float64)
@@ -340,7 +341,7 @@ func TestHandler_DisposePackageVersions_StatusChange(t *testing.T) {
// aws-sdk-go-v2 deserializers.go's
// ...SuccessfulPackageVersionInfoMap/...PackageVersionErrorMap.
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
success, _ := resp["successfulVersions"].(map[string]any)
entry, _ := success["1.0.0"].(map[string]any)
assert.Equal(t, "Disposed", entry["status"])
@@ -358,7 +359,7 @@ func TestHandler_DisposePackageVersions_StatusChange(t *testing.T) {
)
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
pv, _ := descResp["packageVersion"].(map[string]any)
assert.Equal(t, "Disposed", pv["status"])
}
@@ -386,7 +387,7 @@ func TestHandler_UpdatePackageVersionsStatus_StatusChange(t *testing.T) {
)
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
pv, _ := descResp["packageVersion"].(map[string]any)
assert.Equal(t, "Archived", pv["status"])
}
@@ -408,7 +409,7 @@ func TestHandler_PackageVersionLifecycle(t *testing.T) {
)
require.Equal(t, http.StatusOK, pubRec.Code)
var pubResp map[string]any
- require.NoError(t, json.Unmarshal(pubRec.Body.Bytes(), &pubResp))
+ require.NoError(t, json.Unmarshal(pubRec.Body.Bytes(), &pubResp, jsonx.V1()))
assert.Equal(t, "1.0.0", pubResp["version"])
assert.Equal(t, "Published", pubResp["status"])
asset, _ := pubResp["asset"].(map[string]any)
@@ -422,7 +423,7 @@ func TestHandler_PackageVersionLifecycle(t *testing.T) {
)
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
pv, _ := descResp["packageVersion"].(map[string]any)
assert.Equal(t, "Published", pv["status"])
@@ -442,7 +443,7 @@ func TestHandler_PackageVersionLifecycle(t *testing.T) {
)
require.Equal(t, http.StatusOK, descRec2.Code)
var descResp2 map[string]any
- require.NoError(t, json.Unmarshal(descRec2.Body.Bytes(), &descResp2))
+ require.NoError(t, json.Unmarshal(descRec2.Body.Bytes(), &descResp2, jsonx.V1()))
pv2, _ := descResp2["packageVersion"].(map[string]any)
assert.Equal(t, "Archived", pv2["status"])
@@ -462,7 +463,7 @@ func TestHandler_PackageVersionLifecycle(t *testing.T) {
)
require.Equal(t, http.StatusOK, descRec3.Code)
var descResp3 map[string]any
- require.NoError(t, json.Unmarshal(descRec3.Body.Bytes(), &descResp3))
+ require.NoError(t, json.Unmarshal(descRec3.Body.Bytes(), &descResp3, jsonx.V1()))
pv3, _ := descResp3["packageVersion"].(map[string]any)
assert.Equal(t, "Disposed", pv3["status"])
@@ -482,7 +483,7 @@ func TestHandler_PackageVersionLifecycle(t *testing.T) {
)
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
versions, _ := listResp["versions"].([]any)
assert.Empty(t, versions)
}
@@ -520,7 +521,7 @@ func TestHandler_CopyPackageVersions_ToSelf(t *testing.T) {
)
require.Equal(t, http.StatusOK, copyRec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(copyRec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(copyRec2.Body.Bytes(), &resp, jsonx.V1()))
failed, _ := resp["failedVersions"].(map[string]any)
require.Len(t, failed, 1)
entry, _ := failed["18.0.0"].(map[string]any)
@@ -603,7 +604,7 @@ func TestHandler_DisposePackageVersions(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["successfulVersions"])
assert.NotNil(t, resp["failedVersions"])
}
@@ -691,7 +692,7 @@ func TestHandler_UpdatePackageVersionsStatus(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["successfulVersions"])
assert.NotNil(t, resp["failedVersions"])
}
@@ -715,7 +716,7 @@ func TestListPackageVersions_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
page1, _ := out1["versions"].([]any)
assert.Len(t, page1, 2)
nextToken, ok := out1["nextToken"].(string)
@@ -728,7 +729,7 @@ func TestListPackageVersions_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
page2, _ := out2["versions"].([]any)
assert.Len(t, page2, 2)
}
@@ -768,7 +769,7 @@ func TestPackageVersion_HasPackageName(t *testing.T) {
Version string `json:"version"`
} `json:"packageVersion"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.pkgName, out.PackageVersion.PackageName)
assert.Equal(t, "1.0.0", out.PackageVersion.Version)
})
diff --git a/services/codeartifact/handler_packages.go b/services/codeartifact/handler_packages.go
index 43e6755026..b452d4e6c8 100644
--- a/services/codeartifact/handler_packages.go
+++ b/services/codeartifact/handler_packages.go
@@ -1,10 +1,12 @@
package codeartifact
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// packageToMap builds the types.PackageDescription shape used by
@@ -188,7 +190,7 @@ func (h *Handler) handlePutPackageOriginConfiguration(
var in putPackageOriginConfigurationBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("ValidationException", "invalid request body"))
}
}
diff --git a/services/codeartifact/handler_packages_test.go b/services/codeartifact/handler_packages_test.go
index f68167d6f6..01f711a220 100644
--- a/services/codeartifact/handler_packages_test.go
+++ b/services/codeartifact/handler_packages_test.go
@@ -1,7 +1,7 @@
package codeartifact_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codeartifact"
)
@@ -81,7 +82,7 @@ func TestHandler_DescribePackage(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pkg, _ := resp["package"].(map[string]any)
assert.Equal(t, "npm", pkg["format"])
assert.Equal(t, "my-pkg", pkg["name"])
@@ -110,7 +111,7 @@ func TestHandler_DeletePackage(t *testing.T) {
assert.Equal(t, http.StatusOK, delRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["deletedPackage"])
// Confirm deletion: a second delete should return 404 since the package no longer exists.
@@ -144,7 +145,7 @@ func TestHandler_PackageMap(t *testing.T) {
// declare only format/name/namespace/originConfiguration); "format" and
// "name" are the ones actually on the real shape.
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pkg, _ := resp["package"].(map[string]any)
assert.Equal(t, "npm", pkg["format"])
assert.Equal(t, "mypkg", pkg["name"])
@@ -179,7 +180,7 @@ func TestHandler_MultiFormatPackages(t *testing.T) {
allRec := doRequest(t, h, http.MethodPost, "/v1/packages?domain=fmt-multi-domain&repository=fmt-multi-repo", nil)
require.Equal(t, http.StatusOK, allRec.Code)
var allResp map[string]any
- require.NoError(t, json.Unmarshal(allRec.Body.Bytes(), &allResp))
+ require.NoError(t, json.Unmarshal(allRec.Body.Bytes(), &allResp, jsonx.V1()))
allPkgs, _ := allResp["packages"].([]any)
assert.Len(t, allPkgs, 4)
@@ -193,7 +194,7 @@ func TestHandler_MultiFormatPackages(t *testing.T) {
)
require.Equal(t, http.StatusOK, npmRec.Code)
var npmResp map[string]any
- require.NoError(t, json.Unmarshal(npmRec.Body.Bytes(), &npmResp))
+ require.NoError(t, json.Unmarshal(npmRec.Body.Bytes(), &npmResp, jsonx.V1()))
npmPkgs, _ := npmResp["packages"].([]any)
assert.Len(t, npmPkgs, 2)
@@ -207,7 +208,7 @@ func TestHandler_MultiFormatPackages(t *testing.T) {
)
require.Equal(t, http.StatusOK, pypiRec.Code)
var pypiResp map[string]any
- require.NoError(t, json.Unmarshal(pypiRec.Body.Bytes(), &pypiResp))
+ require.NoError(t, json.Unmarshal(pypiRec.Body.Bytes(), &pypiResp, jsonx.V1()))
pypiPkgs, _ := pypiResp["packages"].([]any)
assert.Len(t, pypiPkgs, 1)
}
@@ -311,7 +312,7 @@ func TestHandler_ListPackages(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pkgs, _ := resp["packages"].([]any)
assert.Len(t, pkgs, tt.wantCount)
}
@@ -383,7 +384,7 @@ func TestHandler_PutPackageOriginConfiguration(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
originConfig, _ := resp["originConfiguration"].(map[string]any)
require.NotNil(t, originConfig)
restrictions, _ := originConfig["restrictions"].(map[string]any)
@@ -410,7 +411,7 @@ func TestListPackages_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
page1, _ := out1["packages"].([]any)
assert.Len(t, page1, 2)
nextToken, ok := out1["nextToken"].(string)
@@ -421,7 +422,7 @@ func TestListPackages_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
page2, _ := out2["packages"].([]any)
assert.Len(t, page2, 2)
}
diff --git a/services/codeartifact/handler_repositories.go b/services/codeartifact/handler_repositories.go
index 89509b049d..6d96591bba 100644
--- a/services/codeartifact/handler_repositories.go
+++ b/services/codeartifact/handler_repositories.go
@@ -1,11 +1,13 @@
package codeartifact
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type upstreamRepoEntry struct {
@@ -70,7 +72,7 @@ func (h *Handler) handleCreateRepository(c *echo.Context, domainName, repoName s
var in createRepositoryBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("ValidationException", "invalid request body"))
}
}
@@ -316,7 +318,7 @@ func (h *Handler) handlePutRepositoryPermissionsPolicy(
var in putRepositoryPermissionsPolicyBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("ValidationException", "invalid request body"))
}
}
@@ -408,7 +410,7 @@ func (h *Handler) handleUpdateRepository(c *echo.Context, domainName, repoName s
var in updateRepositoryBody
if len(body) > 0 {
- _ = json.Unmarshal(body, &in)
+ _ = json.Unmarshal(body, &in, jsonx.V1())
}
var upstreams []string
diff --git a/services/codeartifact/handler_repositories_test.go b/services/codeartifact/handler_repositories_test.go
index 3adf1efd76..fb75713afd 100644
--- a/services/codeartifact/handler_repositories_test.go
+++ b/services/codeartifact/handler_repositories_test.go
@@ -1,7 +1,7 @@
package codeartifact_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codeartifact"
)
@@ -75,7 +76,7 @@ func TestHandler_CreateRepository(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repo, _ := resp["repository"].(map[string]any)
assert.Equal(t, tt.repo, repo["name"])
assert.Equal(t, tt.domain, repo["domainName"])
@@ -96,7 +97,7 @@ func TestHandler_DescribeRepository(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repo, _ := resp["repository"].(map[string]any)
assert.Equal(t, "r1", repo["name"])
assert.Equal(t, "d1", repo["domainName"])
@@ -128,7 +129,7 @@ func TestHandler_ListRepositoriesInDomain(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repos, _ := resp["repositories"].([]any)
assert.Len(t, repos, 2)
}
@@ -146,7 +147,7 @@ func TestHandler_ListRepositories(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repos, _ := resp["repositories"].([]any)
assert.Len(t, repos, 2)
}
@@ -168,7 +169,7 @@ func TestHandler_GetRepositoryEndpoint(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["repositoryEndpoint"])
}
@@ -243,7 +244,7 @@ func TestHandler_AssociateExternalConnection(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["repository"])
}
})
@@ -280,7 +281,7 @@ func TestHandler_RepositoryPermissionsPolicy(t *testing.T) {
assert.Equal(t, http.StatusOK, putRec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
pol, _ := putResp["policy"].(map[string]any)
assert.NotEmpty(t, pol["revision"])
assert.NotEmpty(t, pol["resourceArn"])
@@ -349,7 +350,7 @@ func TestHandler_RepositoryPermissionsPolicy_RevisionLocking(t *testing.T) {
)
require.Equal(t, http.StatusOK, putRec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
pol, _ := putResp["policy"].(map[string]any)
revision, _ := pol["revision"].(string)
require.NotEmpty(t, revision)
@@ -380,7 +381,7 @@ func TestHandler_RepositoryPermissionsPolicy_RevisionLocking(t *testing.T) {
)
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
getPol, _ := getResp["policy"].(map[string]any)
assert.NotContains(t, getPol["document"], "Deny")
@@ -448,7 +449,7 @@ func TestHandler_ExternalConnectionFormat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repo, _ := resp["repository"].(map[string]any)
conns, _ := repo["externalConnections"].([]any)
require.Len(t, conns, 1)
@@ -485,7 +486,7 @@ func TestHandler_UpdateRepository_DescriptionPersists(t *testing.T) {
descRec := doRequest(t, h, http.MethodGet, "/v1/repository?domain=ur-desc-domain&repository=ur-desc-repo", nil)
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
repo, _ := resp["repository"].(map[string]any)
assert.Equal(t, "updated", repo["description"])
}
@@ -517,7 +518,7 @@ func TestHandler_ExternalConnections_MultipleConnections(t *testing.T) {
)
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
repo, _ := descResp["repository"].(map[string]any)
conns, _ := repo["externalConnections"].([]any)
assert.Len(t, conns, 3)
@@ -533,7 +534,7 @@ func TestHandler_ExternalConnections_MultipleConnections(t *testing.T) {
)
require.Equal(t, http.StatusOK, disRec.Code)
var disResp map[string]any
- require.NoError(t, json.Unmarshal(disRec.Body.Bytes(), &disResp))
+ require.NoError(t, json.Unmarshal(disRec.Body.Bytes(), &disResp, jsonx.V1()))
disRepo, _ := disResp["repository"].(map[string]any)
disConns, _ := disRepo["externalConnections"].([]any)
assert.Len(t, disConns, 2)
@@ -613,7 +614,7 @@ func TestHandler_DisassociateExternalConnection(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repo, _ := resp["repository"].(map[string]any)
assert.NotNil(t, repo)
}
@@ -684,7 +685,7 @@ func TestHandler_UpdateRepository(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repo, _ := resp["repository"].(map[string]any)
assert.NotNil(t, repo)
}
@@ -706,7 +707,7 @@ func TestListRepositoriesInDomain_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
page1, _ := out1["repositories"].([]any)
assert.Len(t, page1, 2)
nextToken, ok := out1["nextToken"].(string)
@@ -717,7 +718,7 @@ func TestListRepositoriesInDomain_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
page2, _ := out2["repositories"].([]any)
assert.Len(t, page2, 2)
}
@@ -741,7 +742,7 @@ func TestCreateRepository_UpstreamRepositories(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
repo, _ := out["repository"].(map[string]any)
upstreams, _ := repo["upstreams"].([]any)
require.Len(t, upstreams, 1)
@@ -770,7 +771,7 @@ func TestUpdateRepository_UpstreamRepositories(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
repo, _ := out["repository"].(map[string]any)
upstreams, _ := repo["upstreams"].([]any)
require.Len(t, upstreams, 1)
diff --git a/services/codeartifact/handler_tags.go b/services/codeartifact/handler_tags.go
index 0f0505673d..6222fad6c4 100644
--- a/services/codeartifact/handler_tags.go
+++ b/services/codeartifact/handler_tags.go
@@ -1,12 +1,14 @@
package codeartifact
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type tagResourceBody struct {
@@ -46,7 +48,7 @@ func (h *Handler) handleTagResource(c *echo.Context, resourceARN string, body []
}
var in tagResourceBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("ValidationException", "invalid request body"))
}
@@ -63,7 +65,7 @@ func (h *Handler) handleUntagResource(c *echo.Context, resourceARN string, body
}
var in untagResourceBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("ValidationException", "invalid request body"))
}
diff --git a/services/codeartifact/handler_tags_test.go b/services/codeartifact/handler_tags_test.go
index e4426b11e3..b0457742dc 100644
--- a/services/codeartifact/handler_tags_test.go
+++ b/services/codeartifact/handler_tags_test.go
@@ -1,12 +1,14 @@
package codeartifact_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_TagsForDomain(t *testing.T) {
@@ -21,7 +23,7 @@ func TestHandler_TagsForDomain(t *testing.T) {
assert.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
domainMap, _ := createResp["domain"].(map[string]any)
domainARN := domainMap["arn"].(string)
@@ -30,7 +32,7 @@ func TestHandler_TagsForDomain(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tagList, _ := listResp["tags"].([]any)
assert.Len(t, tagList, 1)
@@ -45,7 +47,7 @@ func TestHandler_TagsForDomain(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec2.Code)
var listResp2 map[string]any
- require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp2))
+ require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp2, jsonx.V1()))
tagList2, _ := listResp2["tags"].([]any)
assert.Len(t, tagList2, 2)
@@ -60,7 +62,7 @@ func TestHandler_TagsForDomain(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec3.Code)
var listResp3 map[string]any
- require.NoError(t, json.Unmarshal(listRec3.Body.Bytes(), &listResp3))
+ require.NoError(t, json.Unmarshal(listRec3.Body.Bytes(), &listResp3, jsonx.V1()))
tagList3, _ := listResp3["tags"].([]any)
assert.Len(t, tagList3, 1)
}
@@ -79,7 +81,7 @@ func TestHandler_SortedTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
domainMap, _ := createResp["domain"].(map[string]any)
domainARN, _ := domainMap["arn"].(string)
@@ -87,7 +89,7 @@ func TestHandler_SortedTags(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tagList, _ := listResp["tags"].([]any)
require.Len(t, tagList, 3)
diff --git a/services/codeartifact/handler_test.go b/services/codeartifact/handler_test.go
index d0aa8400bb..94c6ff3338 100644
--- a/services/codeartifact/handler_test.go
+++ b/services/codeartifact/handler_test.go
@@ -5,7 +5,7 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/codeartifact"
)
@@ -33,7 +34,7 @@ func doRequest(t *testing.T, h *codeartifact.Handler, method, path string, body
var bodyReader io.Reader
if body != nil {
- data, err := json.Marshal(body)
+ data, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
bodyReader = bytes.NewReader(data)
}
@@ -684,7 +685,7 @@ func TestHandler_SortedOutput(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
domains, _ := resp["domains"].([]any)
require.Len(t, domains, 3)
@@ -709,7 +710,7 @@ func TestHandler_SortedOutput(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repos, _ := resp["repositories"].([]any)
require.Len(t, repos, 3)
diff --git a/services/codeartifact/package_versions.go b/services/codeartifact/package_versions.go
index db9fe4cf3f..2ce7bfa15b 100644
--- a/services/codeartifact/package_versions.go
+++ b/services/codeartifact/package_versions.go
@@ -2,13 +2,16 @@ package codeartifact
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"slices"
"sort"
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- Package version methods ---
@@ -372,20 +375,20 @@ type npmPackageJSON struct {
Readme string `json:"readme"`
Description string `json:"description"`
Homepage string `json:"homepage"`
- Repository json.RawMessage `json:"repository"`
- License json.RawMessage `json:"license"`
+ Repository jsontext.Value `json:"repository"`
+ License jsontext.Value `json:"license"`
}
// repositoryURL reads package.json's repository, a bare string or an object with a url.
func (m *npmPackageJSON) repositoryURL() string {
var str string
- if json.Unmarshal(m.Repository, &str) == nil {
+ if json.Unmarshal(m.Repository, &str, jsonx.V1()) == nil {
return str
}
var obj struct {
URL string `json:"url"`
}
- if json.Unmarshal(m.Repository, &obj) == nil {
+ if json.Unmarshal(m.Repository, &obj, jsonx.V1()) == nil {
return obj.URL
}
@@ -395,13 +398,13 @@ func (m *npmPackageJSON) repositoryURL() string {
// licenseName reads package.json's license, a bare SPDX string or an object with a type.
func (m *npmPackageJSON) licenseName() string {
var str string
- if json.Unmarshal(m.License, &str) == nil {
+ if json.Unmarshal(m.License, &str, jsonx.V1()) == nil {
return str
}
var obj struct {
Type string `json:"type"`
}
- if json.Unmarshal(m.License, &obj) == nil {
+ if json.Unmarshal(m.License, &obj, jsonx.V1()) == nil {
return obj.Type
}
@@ -417,7 +420,7 @@ func findPackageJSONMetadata(assets []AssetInfo) *npmPackageJSON {
}
var meta npmPackageJSON
- if err := json.Unmarshal(a.Content, &meta); err != nil {
+ if err := json.Unmarshal(a.Content, &meta, jsonx.V1()); err != nil {
return nil
}
diff --git a/services/codeartifact/persistence.go b/services/codeartifact/persistence.go
index a87720a84b..c78c4fd404 100644
--- a/services/codeartifact/persistence.go
+++ b/services/codeartifact/persistence.go
@@ -2,7 +2,7 @@ package codeartifact
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -103,7 +103,7 @@ func buildPersistenceDTORegistry() persistenceDTOTables {
// value, []ExternalConnection, has no identity of its own -- see
// store_setup.go) and is persisted here directly, as before.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ExternalConnections map[string]map[string][]ExternalConnection `json:"externalConnections"`
AccountID string `json:"accountID"`
Region string `json:"region"`
@@ -235,7 +235,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// store_setup.go's registerAllTables doc) from tables via the ephemeral DTO
// registry, factored out of Restore to keep Restore's own cognitive
// complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreDirtyTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTables(tables map[string]jsontext.Value) error {
dtos := buildPersistenceDTORegistry()
if err := dtos.registry.RestoreAll(tables); err != nil {
return fmt.Errorf("codeartifact: restore snapshot DTO tables: %w", err)
diff --git a/services/codebuild/build_batches_test.go b/services/codebuild/build_batches_test.go
index 1e86b446af..245d044b74 100644
--- a/services/codebuild/build_batches_test.go
+++ b/services/codebuild/build_batches_test.go
@@ -1,12 +1,15 @@
package codebuild_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestCodeBuild_BuildBatch covers RetryBuildBatch, StopBuildBatch, ListBuildBatchesForProject.
@@ -27,7 +30,7 @@ func TestCodeBuild_BuildBatch(t *testing.T) {
ID string `json:"id"`
} `json:"buildBatch"`
}
- require.NoError(t, json.NewDecoder(startRec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec.Body), &startOut, jsonx.V1()))
batchID := startOut.BuildBatch.ID
stopRec := doRequest(t, h, "StopBuildBatch", map[string]any{"id": batchID})
@@ -38,7 +41,7 @@ func TestCodeBuild_BuildBatch(t *testing.T) {
BuildBatchStatus string `json:"buildBatchStatus"`
} `json:"buildBatch"`
}
- require.NoError(t, json.NewDecoder(stopRec.Body).Decode(&stopOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(stopRec.Body), &stopOut, jsonx.V1()))
assert.Equal(t, "STOPPED", stopOut.BuildBatch.BuildBatchStatus)
})
@@ -64,7 +67,7 @@ func TestCodeBuild_BuildBatch(t *testing.T) {
ID string `json:"id"`
} `json:"buildBatch"`
}
- require.NoError(t, json.NewDecoder(startRec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec.Body), &startOut, jsonx.V1()))
batchID := startOut.BuildBatch.ID
require.Equal(t, http.StatusOK, doRequest(t, h, "StopBuildBatch", map[string]any{"id": batchID}).Code)
@@ -78,7 +81,7 @@ func TestCodeBuild_BuildBatch(t *testing.T) {
ProjectName string `json:"projectName"`
} `json:"buildBatch"`
}
- require.NoError(t, json.NewDecoder(retryRec.Body).Decode(&retryOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(retryRec.Body), &retryOut, jsonx.V1()))
assert.NotEqual(t, batchID, retryOut.BuildBatch.ID)
assert.Equal(t, "IN_PROGRESS", retryOut.BuildBatch.BuildBatchStatus)
assert.Equal(t, "batch-retry-proj", retryOut.BuildBatch.ProjectName)
@@ -110,7 +113,7 @@ func TestCodeBuild_BuildBatch(t *testing.T) {
var out struct {
IDs []string `json:"ids"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &out, jsonx.V1()))
assert.Len(t, out.IDs, 2)
})
@@ -166,7 +169,7 @@ func TestHandler_DeleteBuildBatch_RemovesBatch(t *testing.T) {
ID string `json:"id"`
} `json:"buildBatch"`
}
- require.NoError(t, json.NewDecoder(startRec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec.Body), &startOut, jsonx.V1()))
batchID = startOut.BuildBatch.ID
} else {
batchID = "ghost-proj:ghost-batch-id"
@@ -187,7 +190,7 @@ func TestHandler_DeleteBuildBatch_RemovesBatch(t *testing.T) {
var listOut struct {
IDs []string `json:"ids"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
assert.NotContains(t, listOut.IDs, batchID, "batch should be removed after delete")
}
})
@@ -226,7 +229,7 @@ func TestHandler_StartBuildBatch_SetsInProgress(t *testing.T) {
StartTime float64 `json:"startTime"`
} `json:"buildBatch"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantStatus, out.BuildBatch.BuildBatchStatus)
if tt.wantStartTimeSet {
assert.Greater(t, out.BuildBatch.StartTime, float64(0))
@@ -266,7 +269,7 @@ func TestHandler_StopBuildBatch_SetsEndTime(t *testing.T) {
ID string `json:"id"`
} `json:"buildBatch"`
}
- require.NoError(t, json.NewDecoder(startRec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec.Body), &startOut, jsonx.V1()))
stopRec := doRequest(t, h, "StopBuildBatch", map[string]any{"id": startOut.BuildBatch.ID})
require.Equal(t, http.StatusOK, stopRec.Code)
@@ -277,7 +280,7 @@ func TestHandler_StopBuildBatch_SetsEndTime(t *testing.T) {
EndTime float64 `json:"endTime"`
} `json:"buildBatch"`
}
- require.NoError(t, json.NewDecoder(stopRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(stopRec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantStatus, out.BuildBatch.BuildBatchStatus)
if tt.wantEndTimeSet {
assert.Greater(t, out.BuildBatch.EndTime, float64(0))
@@ -300,7 +303,7 @@ func TestHandler_StartBuildBatch_ArnSet(t *testing.T) {
var out struct {
BuildBatch map[string]any `json:"buildBatch"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
batchArn, _ := out.BuildBatch["arn"].(string)
assert.NotEmpty(t, batchArn, "StartBuildBatch must return a non-empty arn")
assert.Contains(t, batchArn, "arn:aws:codebuild", "arn must be a valid ARN format")
@@ -319,7 +322,7 @@ func TestHandler_RetryBuildBatch_ArnSet(t *testing.T) {
var startOut struct {
BuildBatch map[string]any `json:"buildBatch"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
batchID, _ := startOut.BuildBatch["id"].(string)
require.NotEmpty(t, batchID)
require.Equal(t, http.StatusOK, doRequest(t, h, "StopBuildBatch", map[string]any{"id": batchID}).Code)
@@ -330,7 +333,7 @@ func TestHandler_RetryBuildBatch_ArnSet(t *testing.T) {
var retryOut struct {
BuildBatch map[string]any `json:"buildBatch"`
}
- require.NoError(t, json.Unmarshal(retryRec.Body.Bytes(), &retryOut))
+ require.NoError(t, json.Unmarshal(retryRec.Body.Bytes(), &retryOut, jsonx.V1()))
arn, _ := retryOut.BuildBatch["arn"].(string)
assert.NotEmpty(t, arn, "RetryBuildBatch must return a non-empty arn")
}
diff --git a/services/codebuild/builds_semantics_test.go b/services/codebuild/builds_semantics_test.go
index 50d4302e5c..c95ed63638 100644
--- a/services/codebuild/builds_semantics_test.go
+++ b/services/codebuild/builds_semantics_test.go
@@ -1,12 +1,15 @@
package codebuild_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_RetryBuild_InheritsFields verifies RetryBuild inherits environment/source/serviceRole
@@ -31,7 +34,7 @@ func TestHandler_RetryBuild_InheritsFields(t *testing.T) {
var startOut struct {
Build map[string]any `json:"build"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
buildID, _ := startOut.Build["id"].(string)
require.NotEmpty(t, buildID)
@@ -41,7 +44,7 @@ func TestHandler_RetryBuild_InheritsFields(t *testing.T) {
var retryOut struct {
Build map[string]any `json:"build"`
}
- require.NoError(t, json.Unmarshal(retryRec.Body.Bytes(), &retryOut))
+ require.NoError(t, json.Unmarshal(retryRec.Body.Bytes(), &retryOut, jsonx.V1()))
b := retryOut.Build
assert.Equal(t, "arn:aws:iam::000000000000:role/my-role", b["serviceRole"],
@@ -64,7 +67,7 @@ func TestHandler_BatchGetBuilds_ARNLookup(t *testing.T) {
var startOut struct {
Build map[string]any `json:"build"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
buildArn, _ := startOut.Build["arn"].(string)
require.NotEmpty(t, buildArn)
@@ -75,7 +78,7 @@ func TestHandler_BatchGetBuilds_ARNLookup(t *testing.T) {
Builds []any `json:"builds"`
BuildsNotFound []string `json:"buildsNotFound"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Len(t, getOut.Builds, 1, "BatchGetBuilds must find build by ARN")
assert.Empty(t, getOut.BuildsNotFound, "ARN lookup must not produce buildsNotFound")
}
@@ -108,7 +111,7 @@ func TestHandler_StartBuild_OverrideFields(t *testing.T) {
var out struct {
Build map[string]any `json:"build"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
b := out.Build
assert.Equal(t, "arn:aws:iam::000000000000:role/override-role", b["serviceRole"],
@@ -190,7 +193,7 @@ func TestHandler_StartBuild_InheritsProjectFields(t *testing.T) {
Phases []map[string]any `json:"phases"`
} `json:"build"`
}
- require.NoError(t, json.NewDecoder(startRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantServiceRole, out.Build.ServiceRole)
assert.Equal(t, "GITHUB", out.Build.Source.Type)
assert.Len(t, out.Build.Environment.EnvironmentVariables, 1)
@@ -233,7 +236,7 @@ func TestHandler_StartBuild_InitialPhase(t *testing.T) {
} `json:"phases"`
} `json:"build"`
}
- require.NoError(t, json.NewDecoder(startRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec.Body), &out, jsonx.V1()))
if tt.wantPhasesNonEmpty {
require.NotEmpty(t, out.Build.Phases)
assert.Equal(t, tt.wantInitialPhase, out.Build.Phases[0].PhaseType)
@@ -351,7 +354,7 @@ func TestHandler_StartBuild_EnvVarOverrideMerge(t *testing.T) {
} `json:"environment"`
} `json:"build"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
envVars := out.Build.Environment.EnvironmentVariables
assert.Len(t, envVars, tt.wantEnvCount)
diff --git a/services/codebuild/builds_test.go b/services/codebuild/builds_test.go
index 0bf78f2557..37fa1917e6 100644
--- a/services/codebuild/builds_test.go
+++ b/services/codebuild/builds_test.go
@@ -1,13 +1,16 @@
package codebuild_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"sort"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_StartBuild(t *testing.T) {
@@ -64,7 +67,7 @@ func TestHandler_StartBuild(t *testing.T) {
BuildStatus string `json:"buildStatus"`
} `json:"build"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out.Build.ID)
assert.Equal(t, "IN_PROGRESS", out.Build.BuildStatus)
}
@@ -94,7 +97,7 @@ func TestHandler_StartBuild_TimestampsAreNumbers(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.NewDecoder(startRec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec.Body), &startOut, jsonx.V1()))
build, ok := startOut["build"].(map[string]any)
require.True(t, ok, "response should contain 'build' object")
@@ -109,7 +112,7 @@ func TestHandler_StartBuild_TimestampsAreNumbers(t *testing.T) {
require.Equal(t, http.StatusOK, stopRec.Code)
var stopOut map[string]any
- require.NoError(t, json.NewDecoder(stopRec.Body).Decode(&stopOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(stopRec.Body), &stopOut, jsonx.V1()))
stoppedBuild, ok := stopOut["build"].(map[string]any)
require.True(t, ok, "stop response should contain 'build' object")
@@ -170,7 +173,7 @@ func TestHandler_BatchGetBuilds(t *testing.T) {
ID string `json:"id"`
} `json:"build"`
}
- require.NoError(t, json.NewDecoder(startRec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec.Body), &startOut, jsonx.V1()))
buildID = startOut.Build.ID
} else {
buildID = "nonexistent:build123"
@@ -185,7 +188,7 @@ func TestHandler_BatchGetBuilds(t *testing.T) {
Builds []any `json:"builds"`
BuildsNotFound []string `json:"buildsNotFound"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.Builds, tt.wantBuildCount)
assert.Equal(t, tt.wantNotFound, out.BuildsNotFound)
})
@@ -242,7 +245,7 @@ func TestHandler_StopBuild(t *testing.T) {
ID string `json:"id"`
} `json:"build"`
}
- require.NoError(t, json.NewDecoder(startRec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec.Body), &startOut, jsonx.V1()))
buildID = startOut.Build.ID
} else {
buildID = "nonexistent:build999"
@@ -257,7 +260,7 @@ func TestHandler_StopBuild(t *testing.T) {
BuildStatus string `json:"buildStatus"`
} `json:"build"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, "STOPPED", out.Build.BuildStatus)
}
})
@@ -297,7 +300,7 @@ func TestHandler_StopBuild_SetsStoppedNotSucceeded(t *testing.T) {
ID string `json:"id"`
} `json:"build"`
}
- require.NoError(t, json.NewDecoder(startRec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec.Body), &startOut, jsonx.V1()))
stopRec := doRequest(t, h, "StopBuild", map[string]any{"id": startOut.Build.ID})
require.Equal(t, http.StatusOK, stopRec.Code)
@@ -310,7 +313,7 @@ func TestHandler_StopBuild_SetsStoppedNotSucceeded(t *testing.T) {
EndTime float64 `json:"endTime"`
} `json:"build"`
}
- require.NoError(t, json.NewDecoder(stopRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(stopRec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantBuildStatus, out.Build.BuildStatus)
assert.Equal(t, tt.wantCurrentPhase, out.Build.CurrentPhase)
assert.Equal(t, tt.wantBuildComplete, out.Build.BuildComplete)
@@ -376,7 +379,7 @@ func TestHandler_ListBuildsForProject(t *testing.T) {
var out struct {
IDs []string `json:"ids"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.IDs, tt.buildCount)
}
})
@@ -470,7 +473,7 @@ func TestHandler_BatchDeleteBuilds(t *testing.T) {
"projectName": "my-project",
})
var startResp map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
build := startResp["build"].(map[string]any)
buildID = build["id"].(string)
tt.buildIDs = []string{buildID}
@@ -482,7 +485,7 @@ func TestHandler_BatchDeleteBuilds(t *testing.T) {
assert.Equal(t, tt.wantHTTP, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
deleted := resp["buildsDeleted"].([]any)
assert.Len(t, deleted, tt.wantDeleted)
})
@@ -505,7 +508,7 @@ func TestHandler_BatchDeleteBuilds_NotDeletedList(t *testing.T) {
BuildsDeleted []string `json:"buildsDeleted"`
BuildsNotDeleted []map[string]any `json:"buildsNotDeleted"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.BuildsDeleted, "no builds deleted when IDs do not exist")
assert.Len(t, out.BuildsNotDeleted, 2, "both missing IDs must appear in buildsNotDeleted")
@@ -539,7 +542,7 @@ func TestHandler_ListBuilds(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ids := resp["ids"].([]any)
assert.Len(t, ids, 2)
}
@@ -573,7 +576,7 @@ func TestHandler_SortedBuilds(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &out, jsonx.V1()))
ids, _ := out["ids"].([]any)
require.Len(t, ids, 3)
@@ -627,7 +630,7 @@ func TestHandler_RetryBuild(t *testing.T) {
if id == "" {
startRec := doRequest(t, h, "StartBuild", map[string]any{"projectName": "proj"})
var startResp map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
build := startResp["build"].(map[string]any)
id = build["id"].(string)
}
diff --git a/services/codebuild/command_executions_test.go b/services/codebuild/command_executions_test.go
index 62aefd9470..c9fb9e59b2 100644
--- a/services/codebuild/command_executions_test.go
+++ b/services/codebuild/command_executions_test.go
@@ -1,7 +1,8 @@
package codebuild_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"sort"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codebuild"
)
@@ -70,7 +72,7 @@ func TestHandler_BatchGetCommandExecutions(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
executions, _ := out["commandExecutions"].([]any)
assert.Len(t, executions, tt.wantFound)
@@ -121,7 +123,7 @@ func TestHandler_StartCommandExecution_TypeStored(t *testing.T) {
ID string `json:"id"`
} `json:"sandbox"`
}
- require.NoError(t, json.NewDecoder(sbRec.Body).Decode(&sbOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(sbRec.Body), &sbOut, jsonx.V1()))
execRec := doRequest(t, h, "StartCommandExecution", map[string]any{
"sandboxId": sbOut.Sandbox.ID,
@@ -139,7 +141,7 @@ func TestHandler_StartCommandExecution_TypeStored(t *testing.T) {
EndTime float64 `json:"endTime"`
} `json:"commandExecution"`
}
- require.NoError(t, json.NewDecoder(execRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(execRec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out.CommandExecution.Type)
assert.Equal(t, tt.command, out.CommandExecution.Command)
assert.Equal(t, "SUCCEEDED", out.CommandExecution.Status)
@@ -167,7 +169,7 @@ func TestCodeBuild_CommandExecutionsForSandbox(t *testing.T) {
ID string `json:"id"`
} `json:"sandbox"`
}
- require.NoError(t, json.NewDecoder(startRec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec.Body), &startOut, jsonx.V1()))
sandboxID := startOut.Sandbox.ID
doRequest(t, h, "StartCommandExecution", map[string]any{
@@ -189,7 +191,7 @@ func TestCodeBuild_CommandExecutionsForSandbox(t *testing.T) {
var out struct {
CommandExecutions []map[string]any `json:"commandExecutions"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &out, jsonx.V1()))
assert.Len(t, out.CommandExecutions, 2)
})
@@ -222,7 +224,7 @@ func TestCodeBuild_CommandExecutionsForSandbox(t *testing.T) {
ID string `json:"id"`
} `json:"sandbox"`
}
- require.NoError(t, json.NewDecoder(startRec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec.Body), &startOut, jsonx.V1()))
sandboxID := startOut.Sandbox.ID
ids := make([]string, 0, 3)
@@ -240,7 +242,7 @@ func TestCodeBuild_CommandExecutionsForSandbox(t *testing.T) {
ID string `json:"id"`
} `json:"commandExecution"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
ids = append(ids, out.CommandExecution.ID)
}
@@ -258,7 +260,7 @@ func TestCodeBuild_CommandExecutionsForSandbox(t *testing.T) {
NextToken string `json:"nextToken"`
CommandExecutions []map[string]any `json:"commandExecutions"`
}
- require.NoError(t, json.NewDecoder(firstPage.Body).Decode(&firstOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(firstPage.Body), &firstOut, jsonx.V1()))
require.Len(t, firstOut.CommandExecutions, 2)
assert.Equal(
t,
@@ -280,7 +282,7 @@ func TestCodeBuild_CommandExecutionsForSandbox(t *testing.T) {
var secondOut struct {
CommandExecutions []map[string]any `json:"commandExecutions"`
}
- require.NoError(t, json.NewDecoder(secondPage.Body).Decode(&secondOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(secondPage.Body), &secondOut, jsonx.V1()))
require.Len(t, secondOut.CommandExecutions, 1)
assert.Equal(t, sortedAsc[0], secondOut.CommandExecutions[0]["id"], "the remainder must be the smallest ID")
})
diff --git a/services/codebuild/curated_environments_test.go b/services/codebuild/curated_environments_test.go
index a301260ad4..f457487e9d 100644
--- a/services/codebuild/curated_environments_test.go
+++ b/services/codebuild/curated_environments_test.go
@@ -1,12 +1,15 @@
package codebuild_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestCodeBuild_CuratedEnvironmentImages verifies ListCuratedEnvironmentImages returns
@@ -21,7 +24,7 @@ func TestCodeBuild_CuratedEnvironmentImages(t *testing.T) {
var out struct {
Platforms []map[string]any `json:"platforms"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotEmpty(t, out.Platforms)
assert.Equal(t, "UBUNTU", out.Platforms[0]["platform"])
}
diff --git a/services/codebuild/engine_env.go b/services/codebuild/engine_env.go
index 74f195c96d..c4287e52eb 100644
--- a/services/codebuild/engine_env.go
+++ b/services/codebuild/engine_env.go
@@ -2,11 +2,13 @@ package codebuild
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errEnvResolve = errors.New("cannot resolve environment variable")
@@ -65,7 +67,7 @@ func (r *buildRunner) resolveSecret(ctx context.Context, ref string) (string, er
}
var obj map[string]any
- if jerr := json.Unmarshal([]byte(val), &obj); jerr != nil {
+ if jerr := json.Unmarshal([]byte(val), &obj, jsonx.V1()); jerr != nil {
return "", fmt.Errorf("%w: secret %s is not JSON: %w", errEnvResolve, id, jerr)
}
diff --git a/services/codebuild/fleets_test.go b/services/codebuild/fleets_test.go
index 7442a5027e..0f8eb316b4 100644
--- a/services/codebuild/fleets_test.go
+++ b/services/codebuild/fleets_test.go
@@ -1,13 +1,16 @@
package codebuild_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateFleet(t *testing.T) {
@@ -115,7 +118,7 @@ func TestHandler_BatchGetFleets(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
fleets, _ := out["fleets"].([]any)
assert.Len(t, fleets, tt.wantFound)
@@ -146,7 +149,7 @@ func TestCodeBuild_Fleet(t *testing.T) {
BaseCapacity int `json:"baseCapacity"`
} `json:"fleet"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
fleetArn := createOut.Fleet.Arn
updRec := doRequest(t, h, "UpdateFleet", map[string]any{
@@ -160,7 +163,7 @@ func TestCodeBuild_Fleet(t *testing.T) {
BaseCapacity int `json:"baseCapacity"`
} `json:"fleet"`
}
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&updOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &updOut, jsonx.V1()))
assert.Equal(t, 5, updOut.Fleet.BaseCapacity)
})
@@ -215,7 +218,7 @@ func TestHandler_CreateFleet_StatusSchema(t *testing.T) {
BaseCapacity float64 `json:"baseCapacity"`
} `json:"fleet"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.InDelta(t, tt.wantBaseCapacity, out.Fleet.BaseCapacity, 0)
assert.Equal(t, tt.wantStatusCode, out.Fleet.Status.StatusCode)
})
@@ -258,7 +261,7 @@ func TestHandler_CreateFleet_ExtendedFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out fleetExtendedFields
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out.Fleet.ID)
assert.Equal(t, "ON_DEMAND", out.Fleet.OverflowBehavior)
assert.Equal(t, "aws/codebuild/standard:7.0", out.Fleet.ImageID)
@@ -282,7 +285,7 @@ func TestHandler_UpdateFleet_ExtendedFields(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut fleetExtendedFields
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
updRec := doRequest(t, h, "UpdateFleet", map[string]any{
"arn": createOut.Fleet.Arn,
@@ -294,7 +297,7 @@ func TestHandler_UpdateFleet_ExtendedFields(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var out fleetExtendedFields
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &out, jsonx.V1()))
assert.Equal(t, createOut.Fleet.ID, out.Fleet.ID, "id must not change across an update")
assert.Equal(t, "QUEUE", out.Fleet.OverflowBehavior)
assert.Equal(t, "aws/codebuild/standard:6.0", out.Fleet.ImageID)
@@ -498,7 +501,7 @@ func TestHandler_Fleet_NestedConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut fleetNestedConfig
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
out := createOut
if tt.updateBody != nil {
@@ -509,7 +512,7 @@ func TestHandler_Fleet_NestedConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var updOut fleetNestedConfig
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&updOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &updOut, jsonx.V1()))
out = updOut
}
@@ -566,7 +569,7 @@ func TestHandler_DeleteFleet_RemovesFleet(t *testing.T) {
Arn string `json:"arn"`
} `json:"fleet"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
fleetArn = createOut.Fleet.Arn
} else {
fleetArn = tt.deleteArn
@@ -582,7 +585,7 @@ func TestHandler_DeleteFleet_RemovesFleet(t *testing.T) {
var listOut struct {
Fleets []string `json:"fleets"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
assert.Empty(t, listOut.Fleets, "fleet should be removed from list after delete")
}
})
diff --git a/services/codebuild/handler.go b/services/codebuild/handler.go
index 91db4fbd44..485fe18ce3 100644
--- a/services/codebuild/handler.go
+++ b/services/codebuild/handler.go
@@ -2,7 +2,7 @@ package codebuild
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -12,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -268,18 +269,15 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
respond := func(errType string) error {
payload, _ := json.Marshal(service.JSONErrorResponse{
Type: errType,
Message: strings.TrimPrefix(err.Error(), errType+": "),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
}
@@ -298,7 +296,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
// (all but the parameterless ListCuratedEnvironmentImages, which never
// raises errInvalidRequest), so it is a safe blanket fallback here.
case errors.Is(err, errInvalidRequest), errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
return respond("InvalidInputException")
default:
// codebuild@v1.72.4's types/errors.go declares no internal-server/
diff --git a/services/codebuild/handler_test.go b/services/codebuild/handler_test.go
index 570d69e400..2a68019561 100644
--- a/services/codebuild/handler_test.go
+++ b/services/codebuild/handler_test.go
@@ -2,7 +2,8 @@ package codebuild_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/codebuild"
)
@@ -32,7 +34,7 @@ func doRequest(t *testing.T, h *codebuild.Handler, action string, body any) *htt
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -125,7 +127,7 @@ func projectARN(t *testing.T, h *codebuild.Handler, name string) string {
Arn string `json:"arn"`
} `json:"projects"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotEmpty(t, out.Projects)
return out.Projects[0].Arn
@@ -498,7 +500,7 @@ func TestHandler_Reset(t *testing.T) {
listRec := doRequest(t, h, "ListProjects", nil)
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
assert.Len(t, listOut["projects"], 1)
// Reset.
@@ -508,7 +510,7 @@ func TestHandler_Reset(t *testing.T) {
listRec2 := doRequest(t, h, "ListProjects", nil)
require.Equal(t, http.StatusOK, listRec2.Code)
var listOut2 map[string]any
- require.NoError(t, json.NewDecoder(listRec2.Body).Decode(&listOut2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec2.Body), &listOut2, jsonx.V1()))
assert.Empty(t, listOut2["projects"])
}
@@ -665,7 +667,7 @@ func TestHandler_ErrorTypeMapping(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, errResp.Type)
}
})
diff --git a/services/codebuild/multi_region_test.go b/services/codebuild/multi_region_test.go
index ccf810d15e..885074aa91 100644
--- a/services/codebuild/multi_region_test.go
+++ b/services/codebuild/multi_region_test.go
@@ -2,7 +2,7 @@ package codebuild_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codebuild"
)
@@ -41,7 +42,7 @@ func decode(t *testing.T, body string) map[string]any {
out := map[string]any{}
if body != "" {
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
}
return out
diff --git a/services/codebuild/pagination_test.go b/services/codebuild/pagination_test.go
index 8ef075d487..a7058fc47c 100644
--- a/services/codebuild/pagination_test.go
+++ b/services/codebuild/pagination_test.go
@@ -1,13 +1,15 @@
package codebuild_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codebuild"
)
@@ -27,7 +29,7 @@ func TestHandler_ListProjects_SortOrderDescending(t *testing.T) {
var out struct {
Projects []string `json:"projects"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, []string{"sort-c", "sort-b", "sort-a"}, out.Projects)
}
@@ -70,7 +72,7 @@ func TestHandler_ListFleets_MaxResultsPagination(t *testing.T) {
NextToken string `json:"nextToken"`
Fleets []string `json:"fleets"`
}
- require.NoError(t, json.NewDecoder(firstRec.Body).Decode(&firstPage))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(firstRec.Body), &firstPage, jsonx.V1()))
assert.Len(t, firstPage.Fleets, 2)
require.NotEmpty(t, firstPage.NextToken, "a partial page must carry a nextToken")
@@ -84,7 +86,7 @@ func TestHandler_ListFleets_MaxResultsPagination(t *testing.T) {
NextToken string `json:"nextToken"`
Fleets []string `json:"fleets"`
}
- require.NoError(t, json.NewDecoder(secondRec.Body).Decode(&secondPage))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(secondRec.Body), &secondPage, jsonx.V1()))
assert.Len(t, secondPage.Fleets, 1)
assert.Empty(t, secondPage.NextToken, "the final page must not carry a nextToken")
@@ -110,7 +112,7 @@ func TestHandler_ListBuildBatches_FilterByStatus(t *testing.T) {
ID string `json:"id"`
} `json:"buildBatch"`
}
- require.NoError(t, json.NewDecoder(startRec1.Body).Decode(&started1))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec1.Body), &started1, jsonx.V1()))
stopRec := doRequest(t, h, "StopBuildBatch", map[string]any{"id": started1.BuildBatch.ID})
require.Equal(t, http.StatusOK, stopRec.Code)
@@ -126,7 +128,7 @@ func TestHandler_ListBuildBatches_FilterByStatus(t *testing.T) {
var out struct {
IDs []string `json:"ids"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, []string{started1.BuildBatch.ID}, out.IDs)
}
@@ -165,7 +167,7 @@ func TestHandler_ListReports_FilterByStatus(t *testing.T) {
var out struct {
Reports []string `json:"reports"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, []string{"arn:aws:codebuild:us-east-1:000000000000:report/filter-rg:bad"}, out.Reports)
})
@@ -181,7 +183,7 @@ func TestHandler_ListReports_FilterByStatus(t *testing.T) {
var out struct {
Reports []string `json:"reports"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, []string{"arn:aws:codebuild:us-east-1:000000000000:report/filter-rg:ok"}, out.Reports)
})
}
diff --git a/services/codebuild/persistence.go b/services/codebuild/persistence.go
index 204efca8b8..7cc3e75606 100644
--- a/services/codebuild/persistence.go
+++ b/services/codebuild/persistence.go
@@ -2,7 +2,7 @@ package codebuild
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -30,12 +30,12 @@ const codebuildSnapshotVersion = 2
// snapshot from an incompatible (older or newer) build of this backend as
// though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- ResourcePolicies map[string]string `json:"resourcePolicies"`
- BuildBatchNumbers map[string]int64 `json:"buildBatchNumbers"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ ResourcePolicies map[string]string `json:"resourcePolicies"`
+ BuildBatchNumbers map[string]int64 `json:"buildBatchNumbers"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/codebuild/phase_duration_internal_test.go b/services/codebuild/phase_duration_internal_test.go
index 0bb0b204dc..08a6cd22c5 100644
--- a/services/codebuild/phase_duration_internal_test.go
+++ b/services/codebuild/phase_duration_internal_test.go
@@ -1,12 +1,14 @@
package codebuild
import (
- "encoding/json"
+ "encoding/json/v2"
"regexp"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestRecordSuccessfulPhasesDurationsAreWholeSeconds(t *testing.T) {
@@ -31,7 +33,7 @@ func TestRecordSuccessfulPhasesDurationsAreWholeSeconds(t *testing.T) {
recordSuccessfulPhases(build, build.StartTime+tt.elapsed)
for _, p := range build.Phases {
- raw, err := json.Marshal(p)
+ raw, err := json.Marshal(p, jsonx.V1())
require.NoError(t, err)
assert.NotRegexp(t, `"durationInSeconds":\d*\.`, string(raw))
diff --git a/services/codebuild/projects_schema_test.go b/services/codebuild/projects_schema_test.go
index fad71fbfc6..5dfe4c1871 100644
--- a/services/codebuild/projects_schema_test.go
+++ b/services/codebuild/projects_schema_test.go
@@ -1,12 +1,15 @@
package codebuild_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_CreateProject_SourceSchema tests the expanded source schema.
@@ -82,7 +85,7 @@ func TestHandler_CreateProject_SourceSchema(t *testing.T) {
} `json:"source"`
} `json:"project"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantLocation, out.Project.Source.Location)
assert.Equal(t, tt.wantBuildspec, out.Project.Source.Buildspec)
assert.Equal(t, tt.wantCloneDepth, out.Project.Source.GitCloneDepth)
@@ -157,7 +160,7 @@ func TestHandler_CreateProject_ArtifactsSchema(t *testing.T) {
} `json:"artifacts"`
} `json:"project"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out.Project.Artifacts.Type)
assert.Equal(t, tt.wantPath, out.Project.Artifacts.Path)
assert.Equal(t, tt.wantNamespaceType, out.Project.Artifacts.NamespaceType)
@@ -245,7 +248,7 @@ func TestHandler_CreateProject_EnvironmentSchema(t *testing.T) {
} `json:"environment"`
} `json:"project"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out.Project.Environment.Type)
assert.Equal(t, tt.wantComputeType, out.Project.Environment.ComputeType)
assert.Equal(t, tt.wantPrivilegedMode, out.Project.Environment.PrivilegedMode)
@@ -317,7 +320,7 @@ func TestHandler_CreateProject_CacheSchema(t *testing.T) {
} `json:"cache"`
} `json:"project"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out.Project.Cache.Type)
assert.Len(t, out.Project.Cache.Modes, tt.wantModeLen)
})
@@ -393,7 +396,7 @@ func TestHandler_CreateProject_Timeouts(t *testing.T) {
AutoRetryLimit float64 `json:"autoRetryLimit"`
} `json:"project"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.InDelta(t, float64(tt.timeoutInMinutes), out.Project.TimeoutInMinutes, 0)
assert.InDelta(t, float64(tt.queuedTimeoutInMinutes), out.Project.QueuedTimeoutInMinutes, 0)
assert.InDelta(t, float64(tt.concurrentBuildLimit), out.Project.ConcurrentBuildLimit, 0)
@@ -468,7 +471,7 @@ func TestHandler_CreateProject_SecondarySources(t *testing.T) {
SecondarySourceVersions []map[string]any `json:"secondarySourceVersions"`
} `json:"project"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.Project.SecondarySources, tt.wantSecondaryCount)
if tt.secondarySourceVersions != nil {
assert.Len(t, out.Project.SecondarySourceVersions, len(tt.secondarySourceVersions))
@@ -524,7 +527,7 @@ func TestHandler_CreateProject_VpcConfig(t *testing.T) {
} `json:"vpcConfig"`
} `json:"project"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantVpcID, out.Project.VpcConfig.VpcID)
})
}
@@ -588,7 +591,7 @@ func TestHandler_UpdateProject_Tags(t *testing.T) {
} `json:"tags"`
} `json:"project"`
}
- require.NoError(t, json.NewDecoder(updateRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updateRec.Body), &out, jsonx.V1()))
var gotVal string
for _, tag := range out.Project.Tags {
@@ -679,7 +682,7 @@ func TestHandler_CreateProject_LogsConfig(t *testing.T) {
} `json:"logsConfig"`
} `json:"project"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantCloudWatchStatus, out.Project.LogsConfig.CloudWatchLogs.Status)
assert.Equal(t, tt.wantS3Status, out.Project.LogsConfig.S3Logs.Status)
})
@@ -739,7 +742,7 @@ func TestHandler_CreateProject_BuildBatchConfig(t *testing.T) {
} `json:"buildBatchConfig"`
} `json:"project"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantServiceRole, out.Project.BuildBatchConfig.ServiceRole)
assert.InDelta(t, tt.wantMaxBuilds, out.Project.BuildBatchConfig.Restrictions.MaximumBuildsAllowed, 0)
})
diff --git a/services/codebuild/projects_test.go b/services/codebuild/projects_test.go
index b94ff70da2..1beccbb196 100644
--- a/services/codebuild/projects_test.go
+++ b/services/codebuild/projects_test.go
@@ -1,12 +1,15 @@
package codebuild_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateProject(t *testing.T) {
@@ -87,7 +90,7 @@ func TestHandler_CreateProject_TimestampsAreNumbers(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
project, ok := out["project"].(map[string]any)
require.True(t, ok, "response should contain 'project' object")
@@ -151,7 +154,7 @@ func TestHandler_BatchGetProjects(t *testing.T) {
Projects []any `json:"projects"`
ProjectsNotFound []string `json:"projectsNotFound"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.Projects, tt.wantProjectCount)
assert.Equal(t, tt.wantNotFound, out.ProjectsNotFound)
})
@@ -236,7 +239,7 @@ func TestHandler_Project_SourceVersion(t *testing.T) {
SourceVersion string `json:"sourceVersion"`
} `json:"project"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
assert.Equal(t, "refs/heads/main", createOut.Project.SourceVersion)
updateRec := doRequest(t, h, "UpdateProject", map[string]any{
@@ -250,7 +253,7 @@ func TestHandler_Project_SourceVersion(t *testing.T) {
SourceVersion string `json:"sourceVersion"`
} `json:"project"`
}
- require.NoError(t, json.NewDecoder(updateRec.Body).Decode(&updateOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updateRec.Body), &updateOut, jsonx.V1()))
assert.Equal(t, "pr/42", updateOut.Project.SourceVersion, "UpdateProject must overwrite sourceVersion")
}
@@ -347,7 +350,7 @@ func TestHandler_ListProjects(t *testing.T) {
var out struct {
Projects []string `json:"projects"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
for _, pn := range tt.projectNames {
assert.Contains(t, out.Projects, pn)
@@ -382,7 +385,7 @@ func TestHandler_SortedOutput(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
projects, _ := out["projects"].([]any)
require.Len(t, projects, 3)
@@ -411,7 +414,7 @@ func TestHandler_UpdateProjectVisibility_PublicAlias(t *testing.T) {
var createOut struct {
Project map[string]any `json:"project"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
projectArn, _ := createOut.Project["arn"].(string)
require.NotEmpty(t, projectArn)
@@ -426,7 +429,7 @@ func TestHandler_UpdateProjectVisibility_PublicAlias(t *testing.T) {
ProjectVisibility string `json:"projectVisibility"`
PublicProjectAlias string `json:"publicProjectAlias"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "PUBLIC_READ", out.ProjectVisibility)
assert.NotEmpty(t, out.PublicProjectAlias,
"UpdateProjectVisibility with PUBLIC_READ must return publicProjectAlias")
@@ -453,7 +456,7 @@ func TestCodeBuild_Project(t *testing.T) {
ProjectArn string `json:"projectArn"`
ProjectVisibility string `json:"projectVisibility"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, arn, out.ProjectArn)
assert.Equal(t, "PUBLIC_READ", out.ProjectVisibility)
})
@@ -509,6 +512,6 @@ func TestCodeBuild_ListSharedProjects(t *testing.T) {
var out struct {
Projects []string `json:"projects"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Empty(t, out.Projects)
}
diff --git a/services/codebuild/report_formats.go b/services/codebuild/report_formats.go
index 0e39fe2da2..5b61ac8d41 100644
--- a/services/codebuild/report_formats.go
+++ b/services/codebuild/report_formats.go
@@ -1,13 +1,16 @@
package codebuild
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"encoding/xml"
"fmt"
"sort"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -291,7 +294,7 @@ type cucumberFeature struct {
func parseCucumber(data []byte, source string) ([]TestCase, error) {
var features []cucumberFeature
- if err := json.Unmarshal(data, &features); err != nil {
+ if err := json.Unmarshal(data, &features, jsonx.V1()); err != nil {
return nil, fmt.Errorf("parse cucumber %s: %w", source, err)
}
@@ -513,9 +516,9 @@ type simpleCovFile struct {
func parseSimpleCov(data []byte, source string) ([]CodeCoverage, error) {
var doc map[string]struct {
- Coverage map[string]json.RawMessage `json:"coverage"`
+ Coverage map[string]jsontext.Value `json:"coverage"`
}
- if err := json.Unmarshal(data, &doc); err != nil {
+ if err := json.Unmarshal(data, &doc, jsonx.V1()); err != nil {
return nil, fmt.Errorf("parse simplecov %s: %w", source, err)
}
@@ -536,13 +539,13 @@ func parseSimpleCov(data []byte, source string) ([]CodeCoverage, error) {
}
// decodeSimpleCovFile accepts both the bare line array and the {lines, branches} object.
-func decodeSimpleCovFile(raw json.RawMessage) (simpleCovFile, error) {
+func decodeSimpleCovFile(raw jsontext.Value) (simpleCovFile, error) {
var f simpleCovFile
- if err := json.Unmarshal(raw, &f.Lines); err == nil {
+ if err := json.Unmarshal(raw, &f.Lines, jsonx.V1()); err == nil {
return f, nil
}
- err := json.Unmarshal(raw, &f)
+ err := json.Unmarshal(raw, &f, jsonx.V1())
return f, err
}
diff --git a/services/codebuild/reports_test.go b/services/codebuild/reports_test.go
index 524d51c82c..cd0195baf1 100644
--- a/services/codebuild/reports_test.go
+++ b/services/codebuild/reports_test.go
@@ -1,13 +1,15 @@
package codebuild_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codebuild"
)
@@ -103,7 +105,7 @@ func TestHandler_BatchGetReportGroups(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
rg, _ := out["reportGroup"].(map[string]any)
arns = append(arns, rg["arn"].(string))
}
@@ -116,7 +118,7 @@ func TestHandler_BatchGetReportGroups(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
groups, _ := out["reportGroups"].([]any)
assert.Len(t, groups, tt.wantFound)
@@ -177,7 +179,7 @@ func TestHandler_BatchGetReports(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
reports, _ := out["reports"].([]any)
assert.Len(t, reports, tt.wantFound)
@@ -202,7 +204,7 @@ func TestCodeBuild_Reports(t *testing.T) {
var out struct {
Reports []string `json:"reports"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Empty(t, out.Reports)
})
@@ -225,7 +227,7 @@ func TestCodeBuild_Reports(t *testing.T) {
var out struct {
Reports []string `json:"reports"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.Reports, 2)
})
@@ -247,7 +249,7 @@ func TestCodeBuild_Reports(t *testing.T) {
var out struct {
Reports []string `json:"reports"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &out, jsonx.V1()))
assert.Empty(t, out.Reports)
})
@@ -302,7 +304,7 @@ func TestCodeBuild_Reports(t *testing.T) {
var out struct {
Reports []string `json:"reports"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.Reports, 2)
for _, r := range out.Reports {
assert.Contains(t, r, "my-rg")
@@ -343,7 +345,7 @@ func TestCodeBuild_ReportGroups(t *testing.T) {
Arn string `json:"arn"`
} `json:"reportGroup"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
rgArn := createOut.ReportGroup.Arn
delRec := doRequest(t, h, "DeleteReportGroup", map[string]any{"arn": rgArn})
@@ -356,7 +358,7 @@ func TestCodeBuild_ReportGroups(t *testing.T) {
var batchOut struct {
ReportGroupsNotFound []string `json:"reportGroupsNotFound"`
}
- require.NoError(t, json.NewDecoder(batchRec.Body).Decode(&batchOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(batchRec.Body), &batchOut, jsonx.V1()))
assert.Len(t, batchOut.ReportGroupsNotFound, 1)
})
@@ -390,7 +392,7 @@ func TestCodeBuild_ReportGroups(t *testing.T) {
Arn string `json:"arn"`
} `json:"reportGroup"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
rgArn := createOut.ReportGroup.Arn
updateRec := doRequest(t, h, "UpdateReportGroup", map[string]any{
@@ -408,7 +410,7 @@ func TestCodeBuild_ReportGroups(t *testing.T) {
} `json:"exportConfig"`
} `json:"reportGroup"`
}
- require.NoError(t, json.NewDecoder(updateRec.Body).Decode(&updateOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updateRec.Body), &updateOut, jsonx.V1()))
assert.Equal(t, "S3", updateOut.ReportGroup.ExportConfig.ExportConfigType)
})
@@ -444,7 +446,7 @@ func TestCodeBuild_ReportExtras(t *testing.T) {
var out struct {
CodeCoverages []any `json:"codeCoverages"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Empty(t, out.CodeCoverages)
})
@@ -470,7 +472,7 @@ func TestCodeBuild_ReportExtras(t *testing.T) {
var out struct {
TestCases []any `json:"testCases"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Empty(t, out.TestCases)
})
@@ -502,7 +504,7 @@ func TestCodeBuild_ReportExtras(t *testing.T) {
var out struct {
Stats map[string]any `json:"stats"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotNil(t, out.Stats)
})
@@ -543,7 +545,7 @@ func TestCodeBuild_ReportExtras(t *testing.T) {
var out struct {
ReportGroups []string `json:"reportGroups"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Empty(t, out.ReportGroups)
})
}
diff --git a/services/codebuild/resource_policies_test.go b/services/codebuild/resource_policies_test.go
index c46bbac726..7eab2816b1 100644
--- a/services/codebuild/resource_policies_test.go
+++ b/services/codebuild/resource_policies_test.go
@@ -1,12 +1,15 @@
package codebuild_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestCodeBuild_ResourcePolicy covers Put, Get, Delete.
@@ -30,7 +33,7 @@ func TestCodeBuild_ResourcePolicy(t *testing.T) {
var putOut struct {
ResourceArn string `json:"resourceArn"`
}
- require.NoError(t, json.NewDecoder(putRec.Body).Decode(&putOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(putRec.Body), &putOut, jsonx.V1()))
assert.Equal(t, resArn, putOut.ResourceArn)
getRec := doRequest(t, h, "GetResourcePolicy", map[string]any{"resourceArn": resArn})
@@ -39,7 +42,7 @@ func TestCodeBuild_ResourcePolicy(t *testing.T) {
var getOut struct {
Policy string `json:"policy"`
}
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&getOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &getOut, jsonx.V1()))
assert.JSONEq(t, policy, getOut.Policy)
})
@@ -129,7 +132,7 @@ func TestHandler_ResourcePolicy_PutGetRoundTrip(t *testing.T) {
var out struct {
Policy string `json:"policy"`
}
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &out, jsonx.V1()))
assert.JSONEq(t, tt.wantPolicy, out.Policy)
}
})
diff --git a/services/codebuild/sandboxes_test.go b/services/codebuild/sandboxes_test.go
index fd8dab2814..db29a0a446 100644
--- a/services/codebuild/sandboxes_test.go
+++ b/services/codebuild/sandboxes_test.go
@@ -1,13 +1,15 @@
package codebuild_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codebuild"
)
@@ -59,7 +61,7 @@ func TestHandler_BatchGetSandboxes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
sandboxes, _ := out["sandboxes"].([]any)
assert.Len(t, sandboxes, tt.wantFound)
@@ -88,7 +90,7 @@ func TestCodeBuild_Sandbox(t *testing.T) {
ID string `json:"id"`
} `json:"sandbox"`
}
- require.NoError(t, json.NewDecoder(startRec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec.Body), &startOut, jsonx.V1()))
sandboxID := startOut.Sandbox.ID
stopRec := doRequest(t, h, "StopSandbox", map[string]any{"id": sandboxID})
@@ -99,7 +101,7 @@ func TestCodeBuild_Sandbox(t *testing.T) {
Status string `json:"status"`
} `json:"sandbox"`
}
- require.NoError(t, json.NewDecoder(stopRec.Body).Decode(&stopOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(stopRec.Body), &stopOut, jsonx.V1()))
assert.Equal(t, "STOPPED", stopOut.Sandbox.Status)
})
@@ -128,7 +130,7 @@ func TestCodeBuild_Sandbox(t *testing.T) {
var out struct {
IDs []string `json:"ids"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &out, jsonx.V1()))
assert.Len(t, out.IDs, 2)
})
@@ -177,7 +179,7 @@ func TestHandler_StartSandbox_Schema(t *testing.T) {
StartTime float64 `json:"startTime"`
} `json:"sandbox"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantStatus, out.Sandbox.Status)
if tt.wantArnSet {
assert.NotEmpty(t, out.Sandbox.Arn)
@@ -219,7 +221,7 @@ func TestHandler_StopSandbox_SetsEndTime(t *testing.T) {
ID string `json:"id"`
} `json:"sandbox"`
}
- require.NoError(t, json.NewDecoder(startRec.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(startRec.Body), &startOut, jsonx.V1()))
stopRec := doRequest(t, h, "StopSandbox", map[string]any{"id": startOut.Sandbox.ID})
require.Equal(t, http.StatusOK, stopRec.Code)
@@ -230,7 +232,7 @@ func TestHandler_StopSandbox_SetsEndTime(t *testing.T) {
EndTime float64 `json:"endTime"`
} `json:"sandbox"`
}
- require.NoError(t, json.NewDecoder(stopRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(stopRec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantStatus, out.Sandbox.Status)
if tt.wantEndTimeSet {
assert.Greater(t, out.Sandbox.EndTime, float64(0))
diff --git a/services/codebuild/source_credentials_test.go b/services/codebuild/source_credentials_test.go
index 39b8fa755f..92aa9811c9 100644
--- a/services/codebuild/source_credentials_test.go
+++ b/services/codebuild/source_credentials_test.go
@@ -1,12 +1,15 @@
package codebuild_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestCodeBuild_SourceCredentials covers Import, List, Delete.
@@ -27,7 +30,7 @@ func TestCodeBuild_SourceCredentials(t *testing.T) {
var out struct {
Arn string `json:"arn"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Contains(t, out.Arn, "GITHUB")
})
@@ -58,7 +61,7 @@ func TestCodeBuild_SourceCredentials(t *testing.T) {
var out struct {
SourceCredentialsInfos []map[string]any `json:"sourceCredentialsInfos"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.Len(t, out.SourceCredentialsInfos, 1)
assert.Equal(t, "BITBUCKET", out.SourceCredentialsInfos[0]["serverType"])
assert.Equal(t, "PERSONAL_ACCESS_TOKEN", out.SourceCredentialsInfos[0]["authType"])
@@ -78,7 +81,7 @@ func TestCodeBuild_SourceCredentials(t *testing.T) {
var importOut struct {
Arn string `json:"arn"`
}
- require.NoError(t, json.NewDecoder(importRec.Body).Decode(&importOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(importRec.Body), &importOut, jsonx.V1()))
delRec := doRequest(t, h, "DeleteSourceCredentials", map[string]any{"arn": importOut.Arn})
assert.Equal(t, http.StatusOK, delRec.Code)
@@ -88,7 +91,7 @@ func TestCodeBuild_SourceCredentials(t *testing.T) {
var listOut struct {
SourceCredentialsInfos []map[string]any `json:"sourceCredentialsInfos"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
assert.Empty(t, listOut.SourceCredentialsInfos)
})
diff --git a/services/codebuild/tagswire.go b/services/codebuild/tagswire.go
index 312a52a52a..aab1a32c96 100644
--- a/services/codebuild/tagswire.go
+++ b/services/codebuild/tagswire.go
@@ -1,8 +1,10 @@
package codebuild
import (
- "encoding/json"
+ "encoding/json/v2"
"sort"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// wireTags is the wire representation of CodeBuild's tags field: a JSON
@@ -25,12 +27,12 @@ func (t wireTags) MarshalJSON() ([]byte, error) {
sort.Slice(items, func(i, j int) bool { return items[i].Key < items[j].Key })
- return json.Marshal(items)
+ return json.Marshal(items, jsonx.V1())
}
func (t *wireTags) UnmarshalJSON(data []byte) error {
var items []wireTag
- if err := json.Unmarshal(data, &items); err != nil {
+ if err := json.Unmarshal(data, &items, jsonx.V1()); err != nil {
return err
}
diff --git a/services/codebuild/webhooks_test.go b/services/codebuild/webhooks_test.go
index ecb0f689df..c8cac33ef9 100644
--- a/services/codebuild/webhooks_test.go
+++ b/services/codebuild/webhooks_test.go
@@ -1,12 +1,15 @@
package codebuild_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateWebhook(t *testing.T) {
@@ -107,7 +110,7 @@ func TestHandler_CreateWebhook_ResponseFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
webhook, ok := out["webhook"].(map[string]any)
require.True(t, ok, "response should contain 'webhook' object")
@@ -167,7 +170,7 @@ func TestCodeBuild_Webhooks(t *testing.T) {
BuildType string `json:"buildType"`
} `json:"webhook"`
}
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &out, jsonx.V1()))
assert.Equal(t, "develop", out.Webhook.BranchFilter)
assert.Equal(t, "BUILD_BATCH", out.Webhook.BuildType)
})
@@ -215,7 +218,7 @@ func TestHandler_CreateWebhook_FilterGroups(t *testing.T) {
var out struct {
Webhook map[string]any `json:"webhook"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
w := out.Webhook
fgs, hasFGs := w["filterGroups"]
@@ -263,7 +266,7 @@ func TestHandler_CreateWebhook_ExtendedFields(t *testing.T) {
ManualCreation bool `json:"manualCreation"`
} `json:"webhook"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, "ACTIVE", out.Webhook.Status)
assert.NotEmpty(t, out.Webhook.Secret, "CreateWebhook must return a secret")
@@ -295,7 +298,7 @@ func TestHandler_UpdateWebhook_RotateSecret(t *testing.T) {
LastModifiedSecret float64 `json:"lastModifiedSecret"`
} `json:"webhook"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
originalSecret := createOut.Webhook.Secret
require.NotEmpty(t, originalSecret)
@@ -312,7 +315,7 @@ func TestHandler_UpdateWebhook_RotateSecret(t *testing.T) {
Secret string `json:"secret"`
} `json:"webhook"`
}
- require.NoError(t, json.NewDecoder(noRotateRec.Body).Decode(&noRotateOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(noRotateRec.Body), &noRotateOut, jsonx.V1()))
assert.Equal(t, originalSecret, noRotateOut.Webhook.Secret, "secret must be unchanged without rotateSecret")
rotateRec := doRequest(t, h, "UpdateWebhook", map[string]any{
@@ -327,7 +330,7 @@ func TestHandler_UpdateWebhook_RotateSecret(t *testing.T) {
LastModifiedSecret float64 `json:"lastModifiedSecret"`
} `json:"webhook"`
}
- require.NoError(t, json.NewDecoder(rotateRec.Body).Decode(&rotateOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rotateRec.Body), &rotateOut, jsonx.V1()))
assert.NotEqual(t, originalSecret, rotateOut.Webhook.Secret, "rotateSecret must regenerate the secret")
assert.GreaterOrEqual(t, rotateOut.Webhook.LastModifiedSecret, createOut.Webhook.LastModifiedSecret)
}
@@ -348,7 +351,7 @@ func TestHandler_Webhook_MirroredOnProject(t *testing.T) {
var out struct {
Projects []map[string]any `json:"projects"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Projects, 1)
w, _ := out.Projects[0]["webhook"].(map[string]any)
diff --git a/services/codebuild/whitebox_test.go b/services/codebuild/whitebox_test.go
index e42202386c..c93a7fdae0 100644
--- a/services/codebuild/whitebox_test.go
+++ b/services/codebuild/whitebox_test.go
@@ -2,7 +2,8 @@ package codebuild
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,12 +12,14 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func doTestRequest(t *testing.T, h *Handler, action string, body any) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -81,7 +84,7 @@ func TestHandler_ListProjects_SortByCreatedTime(t *testing.T) {
var out struct {
Projects []string `json:"projects"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, []string{"created-z", "created-a", "created-m"}, out.Projects,
"CREATED_TIME order must ignore name ordering")
}
diff --git a/services/codecommit/actor_arn_test.go b/services/codecommit/actor_arn_test.go
index 3efe0e891e..85ffedd558 100644
--- a/services/codecommit/actor_arn_test.go
+++ b/services/codecommit/actor_arn_test.go
@@ -2,7 +2,7 @@ package codecommit_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codecommit"
)
@@ -30,7 +31,7 @@ func doRequestAsActor(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -80,7 +81,7 @@ func createTestPRForActorArn(t *testing.T, h *codecommit.Handler) (string, strin
RevisionID string `json:"revisionId"`
} `json:"pullRequest"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotEmpty(t, resp.PullRequest.PullRequestID)
require.NotEmpty(t, resp.PullRequest.RevisionID)
@@ -153,7 +154,7 @@ func TestOverridePullRequestApprovalRules_RecordsCallerAsActorArn(t *testing.T)
var resp struct {
PullRequestEvents []map[string]any `json:"pullRequestEvents"`
}
- require.NoError(t, json.Unmarshal(evRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(evRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.PullRequestEvents, tt.wantCount)
if tt.wantCount > 0 {
diff --git a/services/codecommit/handler.go b/services/codecommit/handler.go
index e5876418d0..4513a98381 100644
--- a/services/codecommit/handler.go
+++ b/services/codecommit/handler.go
@@ -2,7 +2,7 @@ package codecommit
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -329,7 +330,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
var input struct {
RepositoryName string `json:"repositoryName"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return ""
}
@@ -366,7 +367,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
if action == "PutCommentReaction" {
@@ -375,7 +376,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
if option, isPRMerge := pullRequestMergeOption(action); isPRMerge {
@@ -384,7 +385,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
if ctxFn, isCtxOp := h.ctxOps[action]; isCtxOp {
@@ -393,7 +394,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
fn, ok := h.ops[action]
@@ -406,7 +407,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// errCodeEntry maps a backend sentinel error to its AWS HTTP status and exception type.
diff --git a/services/codecommit/handler_approval_rule_associations_test.go b/services/codecommit/handler_approval_rule_associations_test.go
index d7026455bc..28198947bf 100644
--- a/services/codecommit/handler_approval_rule_associations_test.go
+++ b/services/codecommit/handler_approval_rule_associations_test.go
@@ -1,12 +1,14 @@
package codecommit_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_AssociateApprovalRuleTemplateWithRepository(t *testing.T) {
@@ -154,7 +156,7 @@ func TestHandler_BatchAssociateApprovalRuleTemplateWithRepositories(t *testing.T
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assoc, _ := resp["associatedRepositoryNames"].([]any)
assert.Len(t, assoc, tt.wantAssocCount)
@@ -237,7 +239,7 @@ func TestHandler_BatchDisassociateApprovalRuleTemplateFromRepositories(t *testin
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
disassoc, _ := resp["disassociatedRepositoryNames"].([]any)
assert.Len(t, disassoc, tt.wantDisassocCount)
@@ -262,7 +264,7 @@ func TestHandler_BatchAssociate_EmptySlicesNotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// Must be JSON array, not null
associated := resp["associatedRepositoryNames"]
@@ -284,7 +286,7 @@ func TestHandler_BatchDisassociate_EmptySlicesNotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
disassoc := resp["disassociatedRepositoryNames"]
assert.NotNil(t, disassoc, "disassociatedRepositoryNames must not be null")
@@ -310,7 +312,7 @@ func TestHandler_ListAssociatedApprovalRuleTemplatesForRepository(t *testing.T)
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
names := resp["approvalRuleTemplateNames"].([]any)
assert.Contains(t, names, "assoc-tmpl")
}
@@ -335,7 +337,7 @@ func TestHandler_ListRepositoriesForApprovalRuleTemplate(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repos := resp["repositoryNames"].([]any)
assert.Contains(t, repos, "repo-for-tmpl")
}
@@ -355,7 +357,7 @@ func TestHandler_ListRepositoriesForApprovalRuleTemplate_Empty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repos := resp["repositoryNames"].([]any)
assert.Empty(t, repos)
}
@@ -389,7 +391,7 @@ func TestHandler_ListAssociatedApprovalRuleTemplatesForRepository_AfterAssoc(t *
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
names := resp["approvalRuleTemplateNames"].([]any)
assert.Len(t, names, 2)
}
@@ -525,7 +527,7 @@ func TestHandler_BatchAssociateAndDisassociate_TableDriven(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assoc := resp["associatedRepositoryNames"].([]any)
errs := resp["errors"].([]any)
assert.Len(t, assoc, tt.wantAssocLen)
@@ -539,7 +541,7 @@ func TestHandler_BatchAssociateAndDisassociate_TableDriven(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
disassoc := resp["disassociatedRepositoryNames"].([]any)
assert.Len(t, disassoc, tt.wantAssocLen)
}
@@ -589,13 +591,13 @@ func TestListAssociatedApprovalRuleTemplatesForRepository_RepoNotFound(t *testin
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
names, ok := resp["approvalRuleTemplateNames"].([]any)
require.True(t, ok, "approvalRuleTemplateNames must be a JSON array")
assert.Empty(t, names)
} else {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "RepositoryDoesNotExistException", errResp["__type"])
}
})
@@ -647,13 +649,13 @@ func TestListRepositoriesForApprovalRuleTemplate_TemplateNotFound(t *testing.T)
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repos, ok := resp["repositoryNames"].([]any)
require.True(t, ok, "repositoryNames must be a JSON array")
assert.Empty(t, repos)
} else {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ApprovalRuleTemplateDoesNotExistException", errResp["__type"])
}
})
diff --git a/services/codecommit/handler_approval_rules.go b/services/codecommit/handler_approval_rules.go
index 87658bff9a..2483bed47f 100644
--- a/services/codecommit/handler_approval_rules.go
+++ b/services/codecommit/handler_approval_rules.go
@@ -1,8 +1,10 @@
package codecommit
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createApprovalRuleTemplateInput struct {
@@ -48,7 +50,7 @@ func approvalRuleTemplateToMap(t *ApprovalRuleTemplate) map[string]any {
func (h *Handler) handleCreateApprovalRuleTemplate(body []byte) (any, error) {
var in createApprovalRuleTemplateInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -76,7 +78,7 @@ func (h *Handler) handleCreateApprovalRuleTemplate(body []byte) (any, error) {
func (h *Handler) handleAssociateApprovalRuleTemplateWithRepository(body []byte) (any, error) {
var in associateApprovalRuleTemplateWithRepositoryInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -100,7 +102,7 @@ func (h *Handler) handleAssociateApprovalRuleTemplateWithRepository(body []byte)
func (h *Handler) handleBatchAssociateApprovalRuleTemplateWithRepositories(body []byte) (any, error) {
var in batchAssociateApprovalRuleTemplateInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -129,7 +131,7 @@ func (h *Handler) handleBatchAssociateApprovalRuleTemplateWithRepositories(body
func (h *Handler) handleBatchDisassociateApprovalRuleTemplateFromRepositories(body []byte) (any, error) {
var in batchDisassociateApprovalRuleTemplateInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -160,7 +162,7 @@ func (h *Handler) handleDeleteApprovalRuleTemplate(body []byte) (any, error) {
var req struct {
ApprovalRuleTemplateName string `json:"approvalRuleTemplateName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.ApprovalRuleTemplateName == "" {
@@ -184,7 +186,7 @@ func (h *Handler) handleGetApprovalRuleTemplate(body []byte) (any, error) {
var req struct {
ApprovalRuleTemplateName string `json:"approvalRuleTemplateName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.ApprovalRuleTemplateName == "" {
@@ -209,7 +211,7 @@ type listPageRequest struct {
func (h *Handler) handleListApprovalRuleTemplates(body []byte) (any, error) {
var req listPageRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -242,7 +244,7 @@ func (h *Handler) handleUpdateApprovalRuleTemplateContent(body []byte) (any, err
NewRuleContent string `json:"newRuleContent"`
ExistingRuleContentSha string `json:"existingRuleContentSha256"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.ApprovalRuleTemplateName == "" {
@@ -271,7 +273,7 @@ func (h *Handler) handleUpdateApprovalRuleTemplateDescription(body []byte) (any,
ApprovalRuleTemplateName string `json:"approvalRuleTemplateName"`
ApprovalRuleTemplateDescription string `json:"approvalRuleTemplateDescription"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.ApprovalRuleTemplateName == "" {
@@ -299,7 +301,7 @@ func (h *Handler) handleUpdateApprovalRuleTemplateName(body []byte) (any, error)
OldApprovalRuleTemplateName string `json:"oldApprovalRuleTemplateName"`
NewApprovalRuleTemplateName string `json:"newApprovalRuleTemplateName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.OldApprovalRuleTemplateName == "" || req.NewApprovalRuleTemplateName == "" {
@@ -329,7 +331,7 @@ func (h *Handler) handleListAssociatedApprovalRuleTemplatesForRepository(body []
RepositoryName string `json:"repositoryName"`
listPageRequest
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
@@ -354,7 +356,7 @@ func (h *Handler) handleListRepositoriesForApprovalRuleTemplate(body []byte) (an
ApprovalRuleTemplateName string `json:"approvalRuleTemplateName"`
listPageRequest
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.ApprovalRuleTemplateName == "" {
@@ -384,7 +386,7 @@ func (h *Handler) handleDisassociateApprovalRuleTemplateFromRepository(body []by
ApprovalRuleTemplateName string `json:"approvalRuleTemplateName"`
RepositoryName string `json:"repositoryName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.ApprovalRuleTemplateName == "" || req.RepositoryName == "" {
diff --git a/services/codecommit/handler_approval_rules_test.go b/services/codecommit/handler_approval_rules_test.go
index cddf5fbd9b..e25831ec3d 100644
--- a/services/codecommit/handler_approval_rules_test.go
+++ b/services/codecommit/handler_approval_rules_test.go
@@ -1,7 +1,7 @@
package codecommit_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codecommit"
)
@@ -75,7 +76,7 @@ func TestHandler_CreateApprovalRuleTemplate(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tmpl, ok := resp["approvalRuleTemplate"].(map[string]any)
require.True(t, ok)
@@ -125,7 +126,7 @@ func TestHandler_GetApprovalRuleTemplate(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tmpl := resp["approvalRuleTemplate"].(map[string]any)
assert.Equal(t, "tmpl-get", tmpl["approvalRuleTemplateName"])
@@ -153,7 +154,7 @@ func TestHandler_ListApprovalRuleTemplates(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
names := resp["approvalRuleTemplateNames"].([]any)
assert.Len(t, names, 2)
@@ -203,7 +204,7 @@ func TestHandler_UpdateApprovalRuleTemplateContent_Reflected(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tmpl := resp["approvalRuleTemplate"].(map[string]any)
assert.Equal(t, newContent, tmpl["approvalRuleTemplateContent"])
}
@@ -330,7 +331,7 @@ func TestHandler_ApprovalRuleTemplate_CRUD_TableDriven(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tmpl := resp["approvalRuleTemplate"].(map[string]any)
assert.Equal(t, tt.tmplName, tmpl["approvalRuleTemplateName"])
assert.Equal(t, tt.content, tmpl["approvalRuleTemplateContent"])
diff --git a/services/codecommit/handler_branches.go b/services/codecommit/handler_branches.go
index 05691dfcbe..aa98392084 100644
--- a/services/codecommit/handler_branches.go
+++ b/services/codecommit/handler_branches.go
@@ -1,8 +1,10 @@
package codecommit
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createBranchInput struct {
@@ -13,7 +15,7 @@ type createBranchInput struct {
func (h *Handler) handleCreateBranch(body []byte) (any, error) {
var in createBranchInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -41,7 +43,7 @@ func (h *Handler) handleDeleteBranch(body []byte) (any, error) {
RepositoryName string `json:"repositoryName"`
BranchName string `json:"branchName"`
}
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -70,7 +72,7 @@ func (h *Handler) handleGetBranch(body []byte) (any, error) {
RepositoryName string `json:"repositoryName"`
BranchName string `json:"branchName"`
}
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -93,7 +95,7 @@ func (h *Handler) handleListBranches(body []byte) (any, error) {
NextToken string `json:"nextToken"`
MaxResults int `json:"maxResults"`
}
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -123,7 +125,7 @@ func (h *Handler) handleUpdateDefaultBranch(body []byte) (any, error) {
RepositoryName string `json:"repositoryName"`
DefaultBranchName string `json:"defaultBranchName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
diff --git a/services/codecommit/handler_branches_test.go b/services/codecommit/handler_branches_test.go
index 09ad7b4b2a..4b3180c9ee 100644
--- a/services/codecommit/handler_branches_test.go
+++ b/services/codecommit/handler_branches_test.go
@@ -1,12 +1,14 @@
package codecommit_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateBranch(t *testing.T) {
@@ -29,7 +31,7 @@ func TestHandler_CreateBranch(t *testing.T) {
require.Equal(t, http.StatusOK, commitRec.Code)
var commitResp map[string]any
- require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitResp))
+ require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitResp, jsonx.V1()))
commitID := commitResp["commitId"].(string)
rec := doRequest(t, h, "CreateBranch", map[string]any{
@@ -108,7 +110,7 @@ func TestHandler_CreateBranch(t *testing.T) {
require.Equal(t, http.StatusOK, commitRec.Code)
var commitResp map[string]any
- require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitResp))
+ require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitResp, jsonx.V1()))
commitID := commitResp["commitId"].(string)
doRequest(t, h, "CreateBranch", map[string]any{
@@ -142,7 +144,7 @@ func TestHandler_CreateBranch_CommitValidation(t *testing.T) {
require.Equal(t, http.StatusOK, commitRec.Code)
var commitResp map[string]any
- require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitResp))
+ require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitResp, jsonx.V1()))
realCommitID := commitResp["commitId"].(string)
tests := []struct {
@@ -181,7 +183,7 @@ func TestHandler_CreateBranch_CommitValidation(t *testing.T) {
})
require.Equal(t, http.StatusOK, cr.Code)
var cr2 map[string]any
- require.NoError(t, json.Unmarshal(cr.Body.Bytes(), &cr2))
+ require.NoError(t, json.Unmarshal(cr.Body.Bytes(), &cr2, jsonx.V1()))
tt.commitID = cr2["commitId"].(string)
}
diff --git a/services/codecommit/handler_comments.go b/services/codecommit/handler_comments.go
index e1f1630c53..29f1d26da8 100644
--- a/services/codecommit/handler_comments.go
+++ b/services/codecommit/handler_comments.go
@@ -2,10 +2,11 @@ package codecommit
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -66,7 +67,7 @@ func (h *Handler) handlePostCommentForComparedCommit(ctx context.Context, body [
Content string `json:"content"`
ClientRequestToken string `json:"clientRequestToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" || req.Content == "" {
@@ -104,7 +105,7 @@ func (h *Handler) handlePostCommentForPullRequest(ctx context.Context, body []by
Content string `json:"content"`
ClientRequestToken string `json:"clientRequestToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.PullRequestID == "" || req.Content == "" {
@@ -139,7 +140,7 @@ func (h *Handler) handlePostCommentReply(ctx context.Context, body []byte) (any,
Content string `json:"content"`
ClientRequestToken string `json:"clientRequestToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.InReplyTo == "" || req.Content == "" {
@@ -167,7 +168,7 @@ func (h *Handler) handleGetComment(body []byte) (any, error) {
var req struct {
CommentID string `json:"commentId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.CommentID == "" {
@@ -229,7 +230,7 @@ func (h *Handler) handleGetCommentsForComparedCommit(body []byte) (any, error) {
NextToken string `json:"nextToken"`
MaxResults int `json:"maxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
@@ -275,7 +276,7 @@ func (h *Handler) handleGetCommentsForPullRequest(body []byte) (any, error) {
NextToken string `json:"nextToken"`
MaxResults int `json:"maxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.PullRequestID == "" {
@@ -324,7 +325,7 @@ func (h *Handler) handleUpdateComment(body []byte) (any, error) {
CommentID string `json:"commentId"`
Content string `json:"content"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.CommentID == "" {
@@ -349,7 +350,7 @@ func (h *Handler) handleDeleteCommentContent(body []byte) (any, error) {
var req struct {
CommentID string `json:"commentId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.CommentID == "" {
diff --git a/services/codecommit/handler_comments_test.go b/services/codecommit/handler_comments_test.go
index 58ce95da1d..936665621d 100644
--- a/services/codecommit/handler_comments_test.go
+++ b/services/codecommit/handler_comments_test.go
@@ -1,13 +1,15 @@
package codecommit_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_PostCommentForComparedCommit(t *testing.T) {
@@ -25,7 +27,7 @@ func TestHandler_PostCommentForComparedCommit(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
comment := resp["comment"].(map[string]any)
assert.Equal(t, "hello world", comment["content"])
assert.NotEmpty(t, comment["commentId"])
@@ -45,7 +47,7 @@ func TestHandler_PostCommentForPullRequest(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
comment := resp["comment"].(map[string]any)
assert.Equal(t, "PR comment", comment["content"])
}
@@ -64,7 +66,7 @@ func TestHandler_PostCommentReply(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var parentResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &parentResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &parentResp, jsonx.V1()))
parentID := parentResp["comment"].(map[string]any)["commentId"].(string)
rec = doRequest(t, h, "PostCommentReply", map[string]any{
@@ -74,7 +76,7 @@ func TestHandler_PostCommentReply(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "reply comment", resp["comment"].(map[string]any)["content"])
}
@@ -92,7 +94,7 @@ func TestHandler_GetComment(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
cID := cr["comment"].(map[string]any)["commentId"].(string)
rec = doRequest(t, h, "GetComment", map[string]any{"commentId": cID})
@@ -121,7 +123,7 @@ func TestHandler_GetCommentsForComparedCommit(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups := resp["commentsForComparedCommitData"].([]any)
require.Len(t, groups, 1)
assert.Len(t, groups[0].(map[string]any)["comments"], 1)
@@ -163,7 +165,7 @@ func TestHandler_GetCommentsForComparedCommit_TableDriven(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups := resp["commentsForComparedCommitData"].([]any)
if tt.commentCount == 0 {
assert.Empty(t, groups)
@@ -194,7 +196,7 @@ func TestHandler_GetCommentsForPullRequest(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups := resp["commentsForPullRequestData"].([]any)
require.Len(t, groups, 1)
assert.Len(t, groups[0].(map[string]any)["comments"], 1)
@@ -234,7 +236,7 @@ func TestHandler_GetCommentsForPullRequest_TableDriven(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups := resp["commentsForPullRequestData"].([]any)
if tt.commentCount == 0 {
assert.Empty(t, groups)
@@ -261,7 +263,7 @@ func TestHandler_UpdateComment(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
cID := cr["comment"].(map[string]any)["commentId"].(string)
rec = doRequest(t, h, "UpdateComment", map[string]any{
@@ -271,7 +273,7 @@ func TestHandler_UpdateComment(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "updated", resp["comment"].(map[string]any)["content"])
}
@@ -289,14 +291,14 @@ func TestHandler_DeleteCommentContent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
cID := cr["comment"].(map[string]any)["commentId"].(string)
rec = doRequest(t, h, "DeleteCommentContent", map[string]any{"commentId": cID})
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, true, resp["comment"].(map[string]any)["deleted"])
}
@@ -315,7 +317,7 @@ func TestHandler_CommentLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
commentID := resp["comment"].(map[string]any)["commentId"].(string)
// Update the comment.
@@ -325,7 +327,7 @@ func TestHandler_CommentLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "updated content", resp["comment"].(map[string]any)["content"])
// Add reaction.
@@ -339,7 +341,7 @@ func TestHandler_CommentLifecycle(t *testing.T) {
rec = doRequest(t, h, "GetCommentReactions", map[string]any{"commentId": commentID})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
reactions := resp["reactionsForComment"].([]any)
assert.Len(t, reactions, 1)
@@ -354,7 +356,7 @@ func TestHandler_CommentLifecycle(t *testing.T) {
rec = doRequest(t, h, "DeleteCommentContent", map[string]any{"commentId": commentID})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
c := resp["comment"].(map[string]any)
assert.Equal(t, true, c["deleted"])
assert.Empty(t, c["content"])
diff --git a/services/codecommit/handler_commits.go b/services/codecommit/handler_commits.go
index baf9860ae0..7d0128030b 100644
--- a/services/codecommit/handler_commits.go
+++ b/services/codecommit/handler_commits.go
@@ -2,9 +2,11 @@ package codecommit
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strconv"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type batchGetCommitsInput struct {
@@ -70,7 +72,7 @@ func commitToMap(c *Commit) map[string]any {
func (h *Handler) handleBatchGetCommits(body []byte) (any, error) {
var in batchGetCommitsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -100,7 +102,7 @@ func (h *Handler) handleBatchGetCommits(body []byte) (any, error) {
func (h *Handler) handleCreateCommit(body []byte) (any, error) {
var in createCommitInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -209,7 +211,7 @@ func (h *Handler) handleGetCommit(body []byte) (any, error) {
RepositoryName string `json:"repositoryName"`
CommitID string `json:"commitId"`
}
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -237,7 +239,7 @@ func (h *Handler) handleGetDifferences(body []byte) (any, error) {
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
diff --git a/services/codecommit/handler_commits_test.go b/services/codecommit/handler_commits_test.go
index 1c6412a4c4..981da6dece 100644
--- a/services/codecommit/handler_commits_test.go
+++ b/services/codecommit/handler_commits_test.go
@@ -1,13 +1,15 @@
package codecommit_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateCommit(t *testing.T) {
@@ -69,7 +71,7 @@ func TestHandler_CreateCommit(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["commitId"])
assert.NotEmpty(t, resp["treeId"])
}
@@ -97,7 +99,7 @@ func TestHandler_CreateCommit_FilesAddedBlobID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
filesAdded, ok := resp["filesAdded"].([]any)
require.True(t, ok)
require.Len(t, filesAdded, 2)
@@ -143,7 +145,7 @@ func TestHandler_CreateCommit_FilesDeletedBlobID(t *testing.T) {
require.Equal(t, http.StatusOK, addRec.Code)
var addResp map[string]any
- require.NoError(t, json.Unmarshal(addRec.Body.Bytes(), &addResp))
+ require.NoError(t, json.Unmarshal(addRec.Body.Bytes(), &addResp, jsonx.V1()))
filesAdded, ok := addResp["filesAdded"].([]any)
require.True(t, ok)
require.Len(t, filesAdded, 1)
@@ -162,7 +164,7 @@ func TestHandler_CreateCommit_FilesDeletedBlobID(t *testing.T) {
require.Equal(t, http.StatusOK, delRec.Code)
var delResp map[string]any
- require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &delResp))
+ require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &delResp, jsonx.V1()))
filesDeleted, ok := delResp["filesDeleted"].([]any)
require.True(t, ok)
require.Len(t, filesDeleted, 1)
@@ -237,7 +239,7 @@ func TestHandler_BatchGetCommits(t *testing.T) {
require.Equal(t, http.StatusOK, r.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(r.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(r.Body.Bytes(), &resp, jsonx.V1()))
commitIDs = append(commitIDs, resp["commitId"].(string))
}
@@ -252,7 +254,7 @@ func TestHandler_BatchGetCommits(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
commits, _ := resp["commits"].([]any)
assert.Len(t, commits, tt.wantFoundCount)
@@ -308,7 +310,7 @@ func TestHandler_BatchGetCommits_FullCommitMap(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
commitID := createResp["commitId"].(string)
rec = doRequest(t, h, "BatchGetCommits", map[string]any{
@@ -318,7 +320,7 @@ func TestHandler_BatchGetCommits_FullCommitMap(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
commits, ok := resp["commits"].([]any)
require.True(t, ok)
@@ -392,7 +394,7 @@ func TestHandler_BatchGetCommits_TableDriven(t *testing.T) {
require.Equal(t, http.StatusOK, branchRec.Code)
var brResp map[string]any
- require.NoError(t, json.Unmarshal(branchRec.Body.Bytes(), &brResp))
+ require.NoError(t, json.Unmarshal(branchRec.Body.Bytes(), &brResp, jsonx.V1()))
realCommitID := brResp["branch"].(map[string]any)["commitId"].(string)
var ids []string
@@ -410,7 +412,7 @@ func TestHandler_BatchGetCommits_TableDriven(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
commits := resp["commits"].([]any)
errors := resp["errors"].([]any)
@@ -437,7 +439,7 @@ func TestHandler_CommitParentTracking(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var firstResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp, jsonx.V1()))
firstCommitID := firstResp["commitId"].(string)
// Second commit on same branch — should have firstCommitID as parent
@@ -449,7 +451,7 @@ func TestHandler_CommitParentTracking(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var secondResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &secondResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &secondResp, jsonx.V1()))
secondCommitID := secondResp["commitId"].(string)
// Fetch both commits via BatchGetCommits and check parent linkage
@@ -460,7 +462,7 @@ func TestHandler_CommitParentTracking(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var batchResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &batchResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &batchResp, jsonx.V1()))
commits, ok := batchResp["commits"].([]any)
require.True(t, ok)
@@ -504,7 +506,7 @@ func TestHandler_CreateCommit_ParentCommitIdOutdated(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ParentCommitIdOutdatedException", resp["__type"])
}
@@ -535,7 +537,7 @@ func TestHandler_CreateCommit_NoChange(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var firstResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp, jsonx.V1()))
firstCommitID, _ := firstResp["commitId"].(string)
require.NotEmpty(t, firstCommitID)
@@ -549,7 +551,7 @@ func TestHandler_CreateCommit_NoChange(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "NoChangeException", errResp["__type"])
branchRec := doRequest(t, h, "GetBranch", map[string]any{
@@ -558,7 +560,7 @@ func TestHandler_CreateCommit_NoChange(t *testing.T) {
require.Equal(t, http.StatusOK, branchRec.Code)
var branchResp map[string]any
- require.NoError(t, json.Unmarshal(branchRec.Body.Bytes(), &branchResp))
+ require.NoError(t, json.Unmarshal(branchRec.Body.Bytes(), &branchResp, jsonx.V1()))
branch, ok := branchResp["branch"].(map[string]any)
require.True(t, ok)
assert.Equal(t, firstCommitID, branch["commitId"], "rejected commit must not move the branch tip")
@@ -602,7 +604,7 @@ func TestGetCommit_ParentsNotNull(t *testing.T) {
require.Equal(t, http.StatusOK, r1.Code)
var firstResp map[string]any
- require.NoError(t, json.Unmarshal(r1.Body.Bytes(), &firstResp))
+ require.NoError(t, json.Unmarshal(r1.Body.Bytes(), &firstResp, jsonx.V1()))
firstID := firstResp["commitId"].(string)
// Create second commit (has first as parent).
@@ -614,7 +616,7 @@ func TestGetCommit_ParentsNotNull(t *testing.T) {
require.Equal(t, http.StatusOK, r2.Code)
var secondResp map[string]any
- require.NoError(t, json.Unmarshal(r2.Body.Bytes(), &secondResp))
+ require.NoError(t, json.Unmarshal(r2.Body.Bytes(), &secondResp, jsonx.V1()))
secondID := secondResp["commitId"].(string)
ids := []string{firstID, secondID}
@@ -627,7 +629,7 @@ func TestGetCommit_ParentsNotNull(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
commit, ok := resp["commit"].(map[string]any)
require.True(t, ok, "commit field must be present")
@@ -664,7 +666,7 @@ func TestGetCommit_FullFieldSet(t *testing.T) {
require.Equal(t, http.StatusOK, r.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(r.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(r.Body.Bytes(), &createResp, jsonx.V1()))
commitID := createResp["commitId"].(string)
rec := doRequest(t, h, "GetCommit", map[string]any{
@@ -674,7 +676,7 @@ func TestGetCommit_FullFieldSet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
commit, ok := resp["commit"].(map[string]any)
require.True(t, ok)
@@ -719,7 +721,7 @@ func TestHandler_GetCommit_CommitIDNotFound(t *testing.T) {
assert.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "CommitIdDoesNotExistException", resp["__type"])
}
@@ -747,7 +749,7 @@ func TestHandler_BatchGetCommits_ErrorCodeIsCommitIdDoesNotExist(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
errs, ok := resp["errors"].([]any)
require.True(t, ok)
@@ -771,7 +773,7 @@ func TestHandler_GetDifferences(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["differences"])
}
@@ -831,7 +833,7 @@ func TestGetDifferences_BlobObjectShape(t *testing.T) {
require.Equal(t, http.StatusOK, commitRec.Code)
var commitOut map[string]any
- require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitOut))
+ require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitOut, jsonx.V1()))
commitID := commitOut["commitId"].(string)
rec := doRequest(t, h, "GetDifferences", map[string]any{
@@ -843,7 +845,7 @@ func TestGetDifferences_BlobObjectShape(t *testing.T) {
var resp struct {
Differences []map[string]any `json:"differences"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Differences, tt.wantCount)
// Each difference must have afterBlob as an object, not a string.
@@ -878,7 +880,7 @@ func TestGetDifferences_FilePathInBlob(t *testing.T) {
require.Equal(t, http.StatusOK, commitRec.Code)
var commitOut map[string]any
- require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitOut))
+ require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitOut, jsonx.V1()))
rec := doRequest(t, h, "GetDifferences", map[string]any{
"repositoryName": "path-repo",
@@ -889,7 +891,7 @@ func TestGetDifferences_FilePathInBlob(t *testing.T) {
var resp struct {
Differences []map[string]any `json:"differences"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Differences, 1)
afterBlob := resp.Differences[0]["afterBlob"].(map[string]any)
@@ -912,7 +914,7 @@ func TestGetDifferences_EmptyRepoReturnsEmptyArray(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
diffs, ok := resp["differences"].([]any)
require.True(t, ok, "differences must be a JSON array, not null")
assert.Empty(t, diffs)
@@ -946,7 +948,7 @@ func TestGetDifferences_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, commitRec.Code)
var commitOut map[string]any
- require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitOut))
+ require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitOut, jsonx.V1()))
commitID := commitOut["commitId"].(string)
seenPaths := map[string]bool{}
@@ -966,7 +968,7 @@ func TestGetDifferences_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
diffs, ok := resp["differences"].([]any)
require.True(t, ok)
assert.LessOrEqual(t, len(diffs), 2, "each page must respect MaxResults")
diff --git a/services/codecommit/handler_files.go b/services/codecommit/handler_files.go
index 274bf0cf15..4eccf4ca15 100644
--- a/services/codecommit/handler_files.go
+++ b/services/codecommit/handler_files.go
@@ -4,9 +4,11 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handlePutFile(body []byte) (any, error) {
@@ -21,7 +23,7 @@ func (h *Handler) handlePutFile(body []byte) (any, error) {
CommitMessage string `json:"commitMessage"`
ParentCommitID string `json:"parentCommitId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" || req.FilePath == "" {
@@ -61,7 +63,7 @@ func (h *Handler) handleGetFile(body []byte) (any, error) {
CommitSpecifier string `json:"commitSpecifier"`
FilePath string `json:"filePath"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" || req.FilePath == "" {
@@ -89,7 +91,7 @@ func (h *Handler) handleGetFolder(body []byte) (any, error) {
CommitSpecifier string `json:"commitSpecifier"`
FolderPath string `json:"folderPath"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
@@ -165,7 +167,7 @@ func (h *Handler) handleDeleteFile(body []byte) (any, error) {
CommitMessage string `json:"commitMessage"`
KeepEmptyFolders bool `json:"keepEmptyFolders"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" || req.FilePath == "" {
@@ -196,7 +198,7 @@ func (h *Handler) handleGetBlob(body []byte) (any, error) {
RepositoryName string `json:"repositoryName"`
BlobID string `json:"blobId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" || req.BlobID == "" {
@@ -223,7 +225,7 @@ func (h *Handler) handleGetBlobDifferences(body []byte) (any, error) {
BeforeBlobID string `json:"beforeBlobId"`
NextToken string `json:"NextToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -265,7 +267,7 @@ func (h *Handler) handleListFileCommitHistory(body []byte) (any, error) {
NextToken string `json:"nextToken"`
MaxResults int `json:"maxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
diff --git a/services/codecommit/handler_files_test.go b/services/codecommit/handler_files_test.go
index c90375c158..6ac2d9c63d 100644
--- a/services/codecommit/handler_files_test.go
+++ b/services/codecommit/handler_files_test.go
@@ -2,13 +2,15 @@ package codecommit_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_PutFile_GetFile(t *testing.T) {
@@ -26,7 +28,7 @@ func TestHandler_PutFile_GetFile(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
putBlobID, _ := putResp["blobId"].(string)
assert.NotEmpty(t, putBlobID, "PutFileOutput.blobId is a required AWS field")
@@ -38,7 +40,7 @@ func TestHandler_PutFile_GetFile(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "hello.txt", resp["filePath"])
assert.Equal(
t,
@@ -68,7 +70,7 @@ func TestHandler_PutFile_GetFile(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var blobResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &blobResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &blobResp, jsonx.V1()))
assert.Equal(t, "aGVsbG8=", blobResp["content"])
}
@@ -95,7 +97,7 @@ func TestHandler_PutFile_SameFileContent(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "SameFileContentException", errResp["__type"])
// Different content at the same path must still succeed.
@@ -126,7 +128,7 @@ func TestHandler_GetFolder(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
files := resp["files"].([]any)
assert.Len(t, files, 1)
}
@@ -144,7 +146,7 @@ func TestHandler_DeleteFile(t *testing.T) {
})
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
putBlobID, _ := putResp["blobId"].(string)
require.NotEmpty(t, putBlobID)
putCommitID, _ := putResp["commitId"].(string)
@@ -161,7 +163,7 @@ func TestHandler_DeleteFile(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["commitId"])
assert.Equal(t, putBlobID, resp["blobId"],
"DeleteFileOutput.blobId must report the blob that was removed from the tree")
@@ -183,7 +185,7 @@ func TestHandler_DeleteFile_NotFound(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "FileDoesNotExistException", resp["__type"])
}
@@ -211,7 +213,7 @@ func TestHandler_DeleteFile_ParentCommitIdRequired(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ParentCommitIdRequiredException", resp["__type"])
}
@@ -243,7 +245,7 @@ func TestHandler_DeleteFile_ParentCommitIdOutdated(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ParentCommitIdOutdatedException", resp["__type"])
}
@@ -268,7 +270,7 @@ func TestHandler_GetBlob(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var fr map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &fr))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &fr, jsonx.V1()))
blobID := fr["blobId"].(string)
rec = doRequest(t, h, "GetBlob", map[string]any{
@@ -278,7 +280,7 @@ func TestHandler_GetBlob(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["content"])
}
@@ -295,7 +297,7 @@ func TestHandler_ListFileCommitHistory(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["revisionDag"])
}
@@ -322,7 +324,7 @@ func TestHandler_ListFileCommitHistory_RevisionDagShape(t *testing.T) {
})
require.Equal(t, http.StatusOK, firstRec.Code)
var firstResp map[string]any
- require.NoError(t, json.Unmarshal(firstRec.Body.Bytes(), &firstResp))
+ require.NoError(t, json.Unmarshal(firstRec.Body.Bytes(), &firstResp, jsonx.V1()))
firstCommitID, _ := firstResp["commitId"].(string)
require.NotEmpty(t, firstCommitID)
@@ -335,7 +337,7 @@ func TestHandler_ListFileCommitHistory_RevisionDagShape(t *testing.T) {
})
require.Equal(t, http.StatusOK, secondRec.Code)
var secondResp map[string]any
- require.NoError(t, json.Unmarshal(secondRec.Body.Bytes(), &secondResp))
+ require.NoError(t, json.Unmarshal(secondRec.Body.Bytes(), &secondResp, jsonx.V1()))
secondCommitID, _ := secondResp["commitId"].(string)
require.NotEmpty(t, secondCommitID)
@@ -346,7 +348,7 @@ func TestHandler_ListFileCommitHistory_RevisionDagShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dag, ok := resp["revisionDag"].([]any)
require.True(t, ok)
require.Len(t, dag, 2)
@@ -406,7 +408,7 @@ func TestHandler_ListFileCommitHistory_IncludesPutFileWrites(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dag, ok := resp["revisionDag"].([]any)
require.True(t, ok)
require.Len(t, dag, 1, "the PutFile commit must appear in the file's history")
@@ -437,7 +439,7 @@ func TestHandler_ListFileCommitHistory_Pagination(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id, _ := resp["commitId"].(string)
require.NotEmpty(t, id)
commitIDs = append(commitIDs, id)
@@ -460,7 +462,7 @@ func TestHandler_ListFileCommitHistory_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dag, ok := resp["revisionDag"].([]any)
require.True(t, ok)
assert.LessOrEqual(t, len(dag), 2, "each page must respect maxResults")
@@ -536,7 +538,7 @@ func TestHandler_ListFileCommitHistory_TableDriven(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dag := resp["revisionDag"].([]any)
assert.Len(t, dag, tt.seedCommits)
})
@@ -566,7 +568,7 @@ func TestHandler_FileLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "src/main.go", resp["filePath"])
assert.NotEmpty(t, resp["blobId"])
putCommitID, _ := resp["commitId"].(string)
@@ -583,7 +585,7 @@ func TestHandler_FileLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
files := resp["files"].([]any)
assert.Len(t, files, 1)
@@ -664,7 +666,7 @@ func TestGetFolder_AlwaysIncludesSubArrays(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// files array must always be present and correct count.
files, ok := resp["files"].([]any)
@@ -747,7 +749,7 @@ func TestListFileCommitHistory_FiltersByFilePath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dag := resp["revisionDag"].([]any)
assert.Len(t, dag, tt.wantCount,
"revisionDag must contain %d commits for path %q", tt.wantCount, tt.queryPath)
diff --git a/services/codecommit/handler_merges.go b/services/codecommit/handler_merges.go
index 8967a767a0..ca59538d2b 100644
--- a/services/codecommit/handler_merges.go
+++ b/services/codecommit/handler_merges.go
@@ -2,10 +2,11 @@ package codecommit
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type batchDescribeMergeConflictsInput struct {
@@ -29,7 +30,7 @@ func isValidMergeOption(opt string) bool {
func (h *Handler) handleBatchDescribeMergeConflicts(body []byte) (any, error) {
var in batchDescribeMergeConflictsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -106,7 +107,7 @@ func (h *Handler) handleMergePullRequest(ctx context.Context, option string, bod
Email string `json:"email"`
mergeSettingsWire
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.PullRequestID == "" {
@@ -157,7 +158,7 @@ func (h *Handler) handleMergeBranchesByFastForward(body []byte) (any, error) {
DestinationCommitSpecifier string `json:"destinationCommitSpecifier"`
TargetBranch string `json:"targetBranch"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
@@ -184,7 +185,7 @@ func (h *Handler) handleGetMergeOptions(body []byte) (any, error) {
DestinationCommitSpecifier string `json:"destinationCommitSpecifier"`
mergeSettingsWire
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
@@ -229,7 +230,7 @@ func (h *Handler) handleCreateUnreferencedMergeCommit(body []byte) (any, error)
CommitMessage string `json:"commitMessage"`
mergeSettingsWire
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
@@ -273,7 +274,7 @@ func (h *Handler) handleGetMergeCommit(body []byte) (any, error) {
DestinationCommitSpecifier string `json:"destinationCommitSpecifier"`
mergeSettingsWire
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
@@ -307,7 +308,7 @@ func (h *Handler) handleGetMergeConflicts(body []byte) (any, error) {
MergeOption string `json:"mergeOption"`
mergeQueryWire
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
@@ -372,7 +373,7 @@ func (h *Handler) handleDescribeMergeConflicts(body []byte) (any, error) {
FilePath string `json:"filePath"`
mergeQueryWire
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -458,7 +459,7 @@ func (r mergeBranchesRequest) options() (MergeBranchesOptions, error) {
func (h *Handler) handleMergeBranchesBySquash(body []byte) (any, error) {
var req mergeBranchesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
@@ -485,7 +486,7 @@ func (h *Handler) handleMergeBranchesBySquash(body []byte) (any, error) {
func (h *Handler) handleMergeBranchesByThreeWay(body []byte) (any, error) {
var req mergeBranchesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
diff --git a/services/codecommit/handler_merges_test.go b/services/codecommit/handler_merges_test.go
index f7b32ac05b..0bbd37d313 100644
--- a/services/codecommit/handler_merges_test.go
+++ b/services/codecommit/handler_merges_test.go
@@ -1,13 +1,14 @@
package codecommit_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codecommit"
)
@@ -86,7 +87,7 @@ func TestHandler_BatchDescribeMergeConflicts(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "conflicts")
assert.Contains(t, resp, "destinationCommitId")
assert.Contains(t, resp, "sourceCommitId")
@@ -151,7 +152,7 @@ func TestHandler_BatchDescribeMergeConflicts_TableDriven(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
conflicts := resp["conflicts"].([]any)
assert.Len(t, conflicts, tt.wantConflicts)
assert.Len(t, resp["errors"], tt.wantErrors)
@@ -174,7 +175,7 @@ func TestHandler_MergePullRequestByFastForward(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pr := resp["pullRequest"].(map[string]any)
assert.Equal(t, "CLOSED", pr["pullRequestStatus"])
}
@@ -193,7 +194,7 @@ func TestHandler_MergePullRequestBySquash(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pr := resp["pullRequest"].(map[string]any)
assert.Equal(t, "CLOSED", pr["pullRequestStatus"])
}
@@ -212,7 +213,7 @@ func TestHandler_MergePullRequestByThreeWay(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pr := resp["pullRequest"].(map[string]any)
assert.Equal(t, "CLOSED", pr["pullRequestStatus"])
}
@@ -233,7 +234,7 @@ func TestHandler_MergeBranchesByFastForward(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(
t,
sourceTip,
@@ -271,7 +272,7 @@ func TestHandler_MergeBranchesByFastForward_UnknownSpecifier(t *testing.T) {
assert.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "CommitDoesNotExistException", resp["__type"])
})
}
@@ -295,7 +296,7 @@ func TestHandler_MergeBranchesBySquash(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
commitID, _ := resp["commitId"].(string)
require.NotEmpty(t, commitID)
@@ -306,7 +307,7 @@ func TestHandler_MergeBranchesBySquash(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
commit, ok := getResp["commit"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "squash it", commit["message"])
@@ -333,7 +334,7 @@ func TestHandler_MergeBranchesByThreeWay(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
commitID, _ := resp["commitId"].(string)
require.NotEmpty(t, commitID)
@@ -344,7 +345,7 @@ func TestHandler_MergeBranchesByThreeWay(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
commit, ok := getResp["commit"].(map[string]any)
require.True(t, ok)
parents, ok := commit["parents"].([]any)
@@ -373,7 +374,7 @@ func TestHandler_MergeBranchesBySquash_TargetBranch(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
commitID, _ := resp["commitId"].(string)
assert.Equal(t, commitID, mustBranchTip(t, h, "sq-target-repo", "release"),
@@ -408,7 +409,7 @@ func mustBranchTip(t *testing.T, h *codecommit.Handler, repoName, branchName str
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
branch, ok := resp["branch"].(map[string]any)
require.True(t, ok)
commitID, _ := branch["commitId"].(string)
@@ -443,7 +444,7 @@ func TestHandler_MergeBranches_AllStrategies(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["commitId"])
assert.NotEmpty(t, resp["treeId"])
})
@@ -465,7 +466,7 @@ func TestHandler_GetMergeOptions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
opts := resp["mergeOptions"].([]any)
assert.Len(t, opts, 3)
@@ -475,7 +476,7 @@ func TestHandler_GetMergeOptions(t *testing.T) {
CommitID string `json:"commitId"`
} `json:"branch"`
}
- require.NoError(t, json.Unmarshal(branch.Body.Bytes(), &branchResp))
+ require.NoError(t, json.Unmarshal(branch.Body.Bytes(), &branchResp, jsonx.V1()))
assert.Equal(t, branchResp.Branch.CommitID, resp["destinationCommitId"])
assert.Equal(t, branchResp.Branch.CommitID, resp["baseCommitId"], "feature forked from main's tip")
}
@@ -512,7 +513,7 @@ func TestHandler_MergeOptions_AllStrategies(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
opts := resp["mergeOptions"].([]any)
assert.Len(t, opts, 3)
@@ -541,7 +542,7 @@ func TestHandler_CreateUnreferencedMergeCommit(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["commitId"])
}
@@ -593,7 +594,7 @@ func TestHandler_CreateUnreferencedMergeCommit_Success(t *testing.T) {
require.Equal(t, http.StatusOK, branchRec.Code)
var brResp map[string]any
- require.NoError(t, json.Unmarshal(branchRec.Body.Bytes(), &brResp))
+ require.NoError(t, json.Unmarshal(branchRec.Body.Bytes(), &brResp, jsonx.V1()))
commitID := brResp["branch"].(map[string]any)["commitId"].(string)
rec := doRequest(t, h, "CreateUnreferencedMergeCommit", map[string]any{
@@ -605,7 +606,7 @@ func TestHandler_CreateUnreferencedMergeCommit_Success(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["commitId"])
assert.NotEmpty(t, resp["treeId"])
}
@@ -631,7 +632,7 @@ func TestHandler_GetMergeCommit(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["mergedCommitId"])
assert.NotEmpty(
t,
@@ -672,7 +673,7 @@ func TestGetMergeCommit_ResolvesParents(t *testing.T) {
require.Equal(t, http.StatusOK, baseRec.Code)
var baseOut map[string]any
- require.NoError(t, json.Unmarshal(baseRec.Body.Bytes(), &baseOut))
+ require.NoError(t, json.Unmarshal(baseRec.Body.Bytes(), &baseOut, jsonx.V1()))
baseCommitID := baseOut["commitId"].(string)
// Create feature branch from base.
@@ -691,7 +692,7 @@ func TestGetMergeCommit_ResolvesParents(t *testing.T) {
require.Equal(t, http.StatusOK, featureRec.Code)
var featureOut map[string]any
- require.NoError(t, json.Unmarshal(featureRec.Body.Bytes(), &featureOut))
+ require.NoError(t, json.Unmarshal(featureRec.Body.Bytes(), &featureOut, jsonx.V1()))
featureCommitID := featureOut["commitId"].(string)
// Fast-forward merge.
@@ -712,7 +713,7 @@ func TestGetMergeCommit_ResolvesParents(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["mergedCommitId"],
"GetMergeCommit must return a non-empty mergedCommitId")
}
@@ -733,7 +734,7 @@ func TestHandler_GetMergeConflicts(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// No content-diff engine backs this emulator, so a merge between two
// resolvable specifiers is always reported mergeable (see PARITY.md gaps).
assert.Equal(t, true, resp["mergeable"])
@@ -757,7 +758,7 @@ func TestHandler_GetMergeConflicts_NoConflicts(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, true, resp["mergeable"])
}
@@ -855,7 +856,7 @@ func TestHandler_DescribeMergeConflicts(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEqual(t, "feature", resp["sourceCommitId"], "specifiers resolve to commit IDs")
assert.NotEmpty(t, resp["sourceCommitId"])
assert.NotEmpty(t, resp["destinationCommitId"])
diff --git a/services/codecommit/handler_pull_request_approvals_test.go b/services/codecommit/handler_pull_request_approvals_test.go
index f93034a7bc..1d047eb431 100644
--- a/services/codecommit/handler_pull_request_approvals_test.go
+++ b/services/codecommit/handler_pull_request_approvals_test.go
@@ -1,13 +1,15 @@
package codecommit_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_GetPullRequestApprovalStates(t *testing.T) {
@@ -23,7 +25,7 @@ func TestHandler_GetPullRequestApprovalStates(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["approvals"])
}
@@ -109,7 +111,7 @@ func TestHandler_GetPullRequestApprovalStates_TableDriven(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
approvals := resp["approvals"].([]any)
// Each UpdatePullRequestApprovalState with the same (empty) userARN just overwrites, so max 1.
if tt.approveUsers > 0 {
@@ -148,7 +150,7 @@ func TestHandler_GetPullRequestOverrideState(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, false, resp["overridden"])
}
@@ -166,7 +168,7 @@ func TestHandler_GetPullRequestOverrideState_AfterOverride(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, false, resp["overridden"])
// Override it.
@@ -182,7 +184,7 @@ func TestHandler_GetPullRequestOverrideState_AfterOverride(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, true, resp["overridden"])
}
@@ -214,7 +216,7 @@ func TestHandler_CreatePullRequestApprovalRule(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rule := resp["approvalRule"].(map[string]any)
assert.Equal(t, "my-rule", rule["approvalRuleName"])
}
@@ -270,7 +272,7 @@ func TestHandler_DescribePullRequestEvents(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["pullRequestEvents"])
}
@@ -343,7 +345,7 @@ func TestHandler_DescribePullRequestEvents_TableDriven(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
events := resp["pullRequestEvents"].([]any)
assert.Len(t, events, tt.wantEvents)
})
@@ -369,7 +371,7 @@ func TestHandler_EvaluatePullRequestApprovalRules(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
eval := resp["evaluation"].(map[string]any)
satisfied := eval["approvalRulesSatisfied"].([]any)
require.Len(t, satisfied, 1)
@@ -404,7 +406,7 @@ func TestHandler_EvaluatePullRequestApprovalRules_WithRules(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
eval := resp["evaluation"].(map[string]any)
satisfied := eval["approvalRulesSatisfied"].([]any)
assert.Len(t, satisfied, 2)
@@ -425,7 +427,7 @@ func TestHandler_PullRequestApprovalRule_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rule := resp["approvalRule"].(map[string]any)
assert.Equal(t, "my-rule", rule["approvalRuleName"])
assert.NotEmpty(t, rule["approvalRuleId"])
@@ -445,7 +447,7 @@ func TestHandler_PullRequestApprovalRule_Lifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
eval := resp["evaluation"].(map[string]any)
assert.Len(t, eval["approvalRulesSatisfied"].([]any), 1)
@@ -463,7 +465,7 @@ func TestHandler_PullRequestApprovalRule_Lifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
eval = resp["evaluation"].(map[string]any)
assert.Empty(t, eval["approvalRulesSatisfied"])
}
diff --git a/services/codecommit/handler_pull_requests.go b/services/codecommit/handler_pull_requests.go
index 8ea8e04f99..456b06b35c 100644
--- a/services/codecommit/handler_pull_requests.go
+++ b/services/codecommit/handler_pull_requests.go
@@ -2,11 +2,12 @@ package codecommit
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strconv"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -96,7 +97,7 @@ func pullRequestToMap(pr *PullRequest) map[string]any {
func (h *Handler) handleCreatePullRequest(body []byte) (any, error) {
var in createPullRequestInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -156,7 +157,7 @@ func (h *Handler) handleGetPullRequest(body []byte) (any, error) {
var in struct {
PullRequestID string `json:"pullRequestId"`
}
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -186,7 +187,7 @@ func (h *Handler) handleListPullRequests(body []byte) (any, error) {
NextToken string `json:"nextToken"`
MaxResults int `json:"maxResults"`
}
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -226,7 +227,7 @@ func (h *Handler) handleGetPullRequestApprovalStates(body []byte) (any, error) {
PullRequestID string `json:"pullRequestId"`
RevisionID string `json:"revisionId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.PullRequestID == "" {
@@ -252,7 +253,7 @@ func (h *Handler) handleGetPullRequestOverrideState(body []byte) (any, error) {
PullRequestID string `json:"pullRequestId"`
RevisionID string `json:"revisionId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.PullRequestID == "" {
@@ -287,7 +288,7 @@ func (h *Handler) handleOverridePullRequestApprovalRules(ctx context.Context, bo
RevisionID string `json:"revisionId"`
OverrideStatus string `json:"overrideStatus"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.PullRequestID == "" {
@@ -309,7 +310,7 @@ func (h *Handler) handleUpdatePullRequestApprovalState(body []byte) (any, error)
RevisionID string `json:"revisionId"`
ApprovalState string `json:"approvalState"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.PullRequestID == "" {
@@ -328,7 +329,7 @@ func (h *Handler) handleUpdatePullRequestDescription(body []byte) (any, error) {
PullRequestID string `json:"pullRequestId"`
Description string `json:"description"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.PullRequestID == "" {
@@ -354,7 +355,7 @@ func (h *Handler) handleUpdatePullRequestStatus(body []byte) (any, error) {
PullRequestID string `json:"pullRequestId"`
PullRequestStatus string `json:"pullRequestStatus"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.PullRequestID == "" {
@@ -383,7 +384,7 @@ func (h *Handler) handleUpdatePullRequestTitle(body []byte) (any, error) {
PullRequestID string `json:"pullRequestId"`
Title string `json:"title"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.PullRequestID == "" {
@@ -410,7 +411,7 @@ func (h *Handler) handleCreatePullRequestApprovalRule(body []byte) (any, error)
ApprovalRuleName string `json:"approvalRuleName"`
ApprovalRuleContent string `json:"approvalRuleContent"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.PullRequestID == "" || req.ApprovalRuleName == "" {
@@ -441,7 +442,7 @@ func (h *Handler) handleDeletePullRequestApprovalRule(body []byte) (any, error)
PullRequestID string `json:"pullRequestId"`
ApprovalRuleName string `json:"approvalRuleName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.PullRequestID == "" || req.ApprovalRuleName == "" {
@@ -468,7 +469,7 @@ func (h *Handler) handleUpdatePullRequestApprovalRuleContent(body []byte) (any,
NewRuleContent string `json:"newRuleContent"`
ExistingSha string `json:"existingRuleContentSha256"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.PullRequestID == "" || req.ApprovalRuleName == "" {
@@ -500,7 +501,7 @@ func (h *Handler) handleDescribePullRequestEvents(body []byte) (any, error) {
NextToken string `json:"nextToken"`
MaxResults int `json:"maxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.PullRequestID == "" {
@@ -548,7 +549,7 @@ func (h *Handler) handleEvaluatePullRequestApprovalRules(body []byte) (any, erro
PullRequestID string `json:"pullRequestId"`
RevisionID string `json:"revisionId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.PullRequestID == "" {
diff --git a/services/codecommit/handler_pull_requests_test.go b/services/codecommit/handler_pull_requests_test.go
index ba106176d6..d286eb645b 100644
--- a/services/codecommit/handler_pull_requests_test.go
+++ b/services/codecommit/handler_pull_requests_test.go
@@ -1,13 +1,15 @@
package codecommit_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreatePullRequest(t *testing.T) {
@@ -82,7 +84,7 @@ func TestHandler_CreatePullRequest(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pr, ok := resp["pullRequest"].(map[string]any)
require.True(t, ok, "pullRequest key should be present")
@@ -154,7 +156,7 @@ func TestHandler_CreatePullRequest_RepoMetadataInResponse(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pr := resp["pullRequest"].(map[string]any)
assert.NotEmpty(t, pr["pullRequestId"])
@@ -226,7 +228,7 @@ func TestHandler_ListPullRequests_StatusFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["pullRequest"].(map[string]any)["pullRequestId"].(string)
}
@@ -249,7 +251,7 @@ func TestHandler_ListPullRequests_StatusFilter(t *testing.T) {
if tt.wantHTTPCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ids := resp["pullRequestIds"].([]any)
assert.Len(t, ids, tt.wantCount)
}
@@ -276,7 +278,7 @@ func TestHandler_ListPullRequests_ClosedFilterIncludesMerged(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var mergeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mergeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mergeResp, jsonx.V1()))
mergedPR := mergeResp["pullRequest"].(map[string]any)
assert.Equal(
t, "CLOSED", mergedPR["pullRequestStatus"],
@@ -290,7 +292,7 @@ func TestHandler_ListPullRequests_ClosedFilterIncludesMerged(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
ids := listResp["pullRequestIds"].([]any)
require.Len(t, ids, 1)
assert.Equal(t, prID, ids[0])
@@ -317,7 +319,7 @@ func TestHandler_ListPullRequests_NumericDescendingOrder(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ids := resp["pullRequestIds"].([]any)
require.Len(t, ids, 3)
@@ -361,7 +363,7 @@ func TestHandler_MergePullRequest_AlreadyMerged(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
prID := resp["pullRequest"].(map[string]any)["pullRequestId"].(string)
// First merge — should succeed.
@@ -373,7 +375,7 @@ func TestHandler_MergePullRequest_AlreadyMerged(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "PullRequestAlreadyClosedException", errResp["__type"])
})
}
@@ -405,13 +407,13 @@ func TestHandler_MergePullRequest_StatusBecomesClosed(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
prID := resp["pullRequest"].(map[string]any)["pullRequestId"].(string)
rec = doRequest(t, h, strategy, map[string]any{"pullRequestId": prID})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
mergedPR := resp["pullRequest"].(map[string]any)
assert.Equal(t, "CLOSED", mergedPR["pullRequestStatus"])
})
@@ -431,7 +433,7 @@ func TestHandler_UpdatePullRequestStatus(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pr := resp["pullRequest"].(map[string]any)
assert.Equal(t, "CLOSED", pr["pullRequestStatus"])
}
@@ -488,7 +490,7 @@ func TestHandler_UpdatePullRequestStatus_Validation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
prID := resp["pullRequest"].(map[string]any)["pullRequestId"].(string)
rec = doRequest(t, h, "UpdatePullRequestStatus", map[string]any{
@@ -552,7 +554,7 @@ func TestHandler_UpdatePullRequestStatus_TableDriven(t *testing.T) {
if tt.wantErrType != "" {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, errResp["__type"])
}
})
@@ -581,7 +583,7 @@ func TestHandler_PullRequest_FieldsPresent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pr, ok := resp["pullRequest"].(map[string]any)
require.True(t, ok)
@@ -619,7 +621,7 @@ func TestHandler_UpdatePullRequestDescription(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pr := resp["pullRequest"].(map[string]any)
assert.Equal(t, "new description", pr["description"])
}
@@ -637,7 +639,7 @@ func TestHandler_UpdatePullRequestDescription_Reflected(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pr := resp["pullRequest"].(map[string]any)
assert.Equal(t, "A much better description", pr["description"])
}
@@ -655,7 +657,7 @@ func TestHandler_UpdatePullRequestTitle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pr := resp["pullRequest"].(map[string]any)
assert.Equal(t, "updated title", pr["title"])
}
@@ -673,7 +675,7 @@ func TestHandler_UpdatePullRequestTitle_Reflected(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pr := resp["pullRequest"].(map[string]any)
assert.Equal(t, "Updated Title", pr["title"])
assert.Equal(t, prID, pr["pullRequestId"])
diff --git a/services/codecommit/handler_reactions.go b/services/codecommit/handler_reactions.go
index a55c68a7e5..97e15f499a 100644
--- a/services/codecommit/handler_reactions.go
+++ b/services/codecommit/handler_reactions.go
@@ -2,11 +2,12 @@ package codecommit
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -22,7 +23,7 @@ func (h *Handler) handleGetCommentReactions(body []byte) (any, error) {
NextToken string `json:"nextToken"`
MaxResults int `json:"maxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.CommentID == "" {
@@ -97,7 +98,7 @@ func (h *Handler) handlePutCommentReaction(ctx context.Context, body []byte) (an
CommentID string `json:"commentId"`
ReactionValue string `json:"reactionValue"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.CommentID == "" || req.ReactionValue == "" {
diff --git a/services/codecommit/handler_reactions_test.go b/services/codecommit/handler_reactions_test.go
index d76ff6c898..ece4dfdd1d 100644
--- a/services/codecommit/handler_reactions_test.go
+++ b/services/codecommit/handler_reactions_test.go
@@ -1,12 +1,14 @@
package codecommit_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_GetCommentReactions(t *testing.T) {
@@ -23,7 +25,7 @@ func TestHandler_GetCommentReactions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
cID := cr["comment"].(map[string]any)["commentId"].(string)
doRequest(t, h, "PutCommentReaction", map[string]any{
@@ -35,7 +37,7 @@ func TestHandler_GetCommentReactions(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
reactions := resp["reactionsForComment"].([]any)
assert.Len(t, reactions, 1)
}
@@ -54,7 +56,7 @@ func TestHandler_PutCommentReaction(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
cID := cr["comment"].(map[string]any)["commentId"].(string)
rec = doRequest(t, h, "PutCommentReaction", map[string]any{
diff --git a/services/codecommit/handler_repositories.go b/services/codecommit/handler_repositories.go
index 7ec8f411f1..d2c5ec6aef 100644
--- a/services/codecommit/handler_repositories.go
+++ b/services/codecommit/handler_repositories.go
@@ -1,10 +1,12 @@
package codecommit
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createRepositoryInput struct {
@@ -52,7 +54,7 @@ func repoMetadata(r *Repository) map[string]any {
func (h *Handler) handleCreateRepository(body []byte) (any, error) {
var in createRepositoryInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -72,7 +74,7 @@ func (h *Handler) handleCreateRepository(body []byte) (any, error) {
func (h *Handler) handleGetRepository(body []byte) (any, error) {
var in getRepositoryInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -88,7 +90,7 @@ func (h *Handler) handleGetRepository(body []byte) (any, error) {
func (h *Handler) handleDeleteRepository(body []byte) (any, error) {
var in deleteRepositoryInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -113,7 +115,7 @@ func (h *Handler) handleListRepositories(body []byte) (any, error) {
MaxResults int `json:"maxResults"`
}
// Ignore parse errors — all fields are optional.
- _ = json.Unmarshal(body, &in)
+ _ = json.Unmarshal(body, &in, jsonx.V1())
if in.SortBy != "" && in.SortBy != "repositoryName" && in.SortBy != "lastModifiedDate" {
return nil, fmt.Errorf("%w: sortBy %q is not valid", ErrInvalidSortBy, in.SortBy)
@@ -169,7 +171,7 @@ func (h *Handler) handleListRepositories(body []byte) (any, error) {
func (h *Handler) handleBatchGetRepositories(body []byte) (any, error) {
var in batchGetRepositoriesInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -198,7 +200,7 @@ func (h *Handler) handleUpdateRepositoryDescription(body []byte) (any, error) {
RepositoryName string `json:"repositoryName"`
RepositoryDescription string `json:"repositoryDescription"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
@@ -213,7 +215,7 @@ func (h *Handler) handleUpdateRepositoryName(body []byte) (any, error) {
OldName string `json:"oldName"`
NewName string `json:"newName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.OldName == "" || req.NewName == "" {
@@ -228,7 +230,7 @@ func (h *Handler) handleUpdateRepositoryEncryptionKey(body []byte) (any, error)
RepositoryName string `json:"repositoryName"`
KmsKeyID string `json:"kmsKeyId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
diff --git a/services/codecommit/handler_repositories_test.go b/services/codecommit/handler_repositories_test.go
index 9a69db037a..9977cb07bf 100644
--- a/services/codecommit/handler_repositories_test.go
+++ b/services/codecommit/handler_repositories_test.go
@@ -1,13 +1,15 @@
package codecommit_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateRepository(t *testing.T) {
@@ -48,7 +50,7 @@ func TestHandler_CreateRepository(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
meta, ok := resp["repositoryMetadata"].(map[string]any)
require.True(t, ok, "repositoryMetadata should be present")
@@ -182,7 +184,7 @@ func TestHandler_ListRepositories(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repos, ok := resp["repositories"].([]any)
require.True(t, ok)
@@ -221,7 +223,7 @@ func TestHandler_SortedListRepositories(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["repositories"].([]any)
require.True(t, ok)
@@ -287,7 +289,7 @@ func TestHandler_DeleteRepository_Cascade_Comments(t *testing.T) {
})
require.Equal(t, http.StatusOK, commitRec.Code)
var commitOut map[string]any
- require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitOut))
+ require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitOut, jsonx.V1()))
commitID := commitOut["commitId"].(string)
commentRec := doRequest(t, h, "PostCommentForComparedCommit", map[string]any{
@@ -297,7 +299,7 @@ func TestHandler_DeleteRepository_Cascade_Comments(t *testing.T) {
})
require.Equal(t, http.StatusOK, commentRec.Code)
var commentOut map[string]any
- require.NoError(t, json.Unmarshal(commentRec.Body.Bytes(), &commentOut))
+ require.NoError(t, json.Unmarshal(commentRec.Body.Bytes(), &commentOut, jsonx.V1()))
comment, ok := commentOut["comment"].(map[string]any)
require.True(t, ok)
commentID, _ := comment["commentId"].(string)
@@ -346,7 +348,7 @@ func TestHandler_DeleteRepository_Cascade_FileHistory(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dag, ok := resp["revisionDag"].([]any)
require.True(t, ok)
assert.Empty(t, dag,
@@ -365,7 +367,7 @@ func TestHandler_RepoMetadataTimestamps(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
meta, ok := resp["repositoryMetadata"].(map[string]any)
require.True(t, ok)
@@ -446,7 +448,7 @@ func TestHandler_RepoMetadata_DefaultBranchAndKmsKey(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
meta := resp["repositoryMetadata"].(map[string]any)
_, hasDefault := meta["defaultBranch"]
_, hasKms := meta["kmsKeyId"]
@@ -479,7 +481,7 @@ func TestHandler_RepoMetadata_DefaultBranchAndKmsKey(t *testing.T) {
rec = doRequest(t, h, "GetRepository", map[string]any{"repositoryName": "repo"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
meta = resp["repositoryMetadata"].(map[string]any)
assert.Equal(t, "main", meta["defaultBranch"])
assert.Equal(t, "arn:aws:kms:us-east-1:123456789012:key/my-key", meta["kmsKeyId"])
@@ -539,7 +541,7 @@ func TestHandler_BatchGetRepositories(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repos, _ := resp["repositories"].([]any)
assert.Len(t, repos, tt.wantFoundCount)
@@ -569,7 +571,7 @@ func TestHandler_BatchGetRepositories_MaxLimit(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "MaximumRepositoryNamesExceededException", errResp["__type"])
// Request 25 — should succeed.
@@ -591,7 +593,7 @@ func TestHandler_BatchGetRepositories_PartialFound(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repos := resp["repositories"].([]any)
notFound := resp["repositoriesNotFound"].([]any)
@@ -615,7 +617,7 @@ func TestHandler_GetRepository_FullMetadata(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
meta := resp["repositoryMetadata"].(map[string]any)
assert.NotEmpty(t, meta["repositoryId"])
@@ -644,7 +646,7 @@ func TestHandler_UpdateRepositoryDescription_Reflected(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
meta := resp["repositoryMetadata"].(map[string]any)
assert.Equal(t, "new description", meta["repositoryDescription"])
}
@@ -657,7 +659,7 @@ func TestHandler_CreateRepository_InvalidName_ErrorType(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidRepositoryNameException", errResp["__type"])
}
diff --git a/services/codecommit/handler_tags.go b/services/codecommit/handler_tags.go
index 67cd8da3ab..b365ac1fbc 100644
--- a/services/codecommit/handler_tags.go
+++ b/services/codecommit/handler_tags.go
@@ -1,8 +1,10 @@
package codecommit
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type tagResourceInput struct {
@@ -21,7 +23,7 @@ type listTagsForResourceInput struct {
func (h *Handler) handleTagResource(body []byte) (any, error) {
var in tagResourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -38,7 +40,7 @@ func (h *Handler) handleTagResource(body []byte) (any, error) {
func (h *Handler) handleUntagResource(body []byte) (any, error) {
var in untagResourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
@@ -55,7 +57,7 @@ func (h *Handler) handleUntagResource(body []byte) (any, error) {
func (h *Handler) handleListTagsForResource(body []byte) (any, error) {
var in listTagsForResourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request body: %w", err)
}
diff --git a/services/codecommit/handler_tags_test.go b/services/codecommit/handler_tags_test.go
index f6e6e7c6bb..750b9b5c6c 100644
--- a/services/codecommit/handler_tags_test.go
+++ b/services/codecommit/handler_tags_test.go
@@ -1,13 +1,14 @@
package codecommit_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codecommit"
)
@@ -31,7 +32,7 @@ func TestHandler_TagAndUntagResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
metaRaw, ok := resp["repositoryMetadata"]
require.True(t, ok)
@@ -61,7 +62,7 @@ func TestHandler_TagAndUntagResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
metaRaw, ok := resp["repositoryMetadata"]
require.True(t, ok)
@@ -127,7 +128,7 @@ func TestHandler_ListTagsForResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
metaRaw, ok := resp["repositoryMetadata"]
require.True(t, ok)
@@ -169,7 +170,7 @@ func TestHandler_ListTagsForResource(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tagsRaw, ok := resp["tags"].(map[string]any)
require.True(t, ok)
@@ -192,7 +193,7 @@ func TestHandler_ARN_TagOps_O1(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
meta := createResp["repositoryMetadata"].(map[string]any)
repoARN := meta["Arn"].(string)
@@ -212,7 +213,7 @@ func TestHandler_ARN_TagOps_O1(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1()))
tagMap, ok := tagsResp["tags"].(map[string]any)
require.True(t, ok)
@@ -231,7 +232,7 @@ func TestHandler_ARN_TagOps_O1(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1()))
tagMap, _ = tagsResp["tags"].(map[string]any)
assert.Empty(t, tagMap)
}
diff --git a/services/codecommit/handler_test.go b/services/codecommit/handler_test.go
index 4a121736db..c50b9d72aa 100644
--- a/services/codecommit/handler_test.go
+++ b/services/codecommit/handler_test.go
@@ -2,7 +2,7 @@ package codecommit_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/codecommit"
)
@@ -29,7 +30,7 @@ func doRequest(t *testing.T, h *codecommit.Handler, action string, body any) *ht
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -74,7 +75,7 @@ func createBranchFromMain(t *testing.T, h *codecommit.Handler, repoName, branchN
require.Equal(t, http.StatusOK, rec.Code)
var branchResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &branchResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &branchResp, jsonx.V1()))
branchInfo, ok := branchResp["branch"].(map[string]any)
require.True(t, ok, "GetBranch response must include a branch object")
tipCommitID, _ := branchInfo["commitId"].(string)
@@ -111,7 +112,7 @@ func setupPR(t *testing.T, h *codecommit.Handler, repoName string) string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pr := resp["pullRequest"].(map[string]any)
@@ -578,7 +579,7 @@ func TestHandler_ErrorTypes(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
})
}
@@ -643,7 +644,7 @@ func TestHandler_NotFoundErrorTypes_AreResourceSpecific(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, resp["__type"])
})
}
diff --git a/services/codecommit/handler_triggers.go b/services/codecommit/handler_triggers.go
index 4f9a2ebbc1..ae54ab1d90 100644
--- a/services/codecommit/handler_triggers.go
+++ b/services/codecommit/handler_triggers.go
@@ -1,15 +1,17 @@
package codecommit
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleGetRepositoryTriggers(body []byte) (any, error) {
var req struct {
RepositoryName string `json:"repositoryName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
@@ -32,7 +34,7 @@ func (h *Handler) handlePutRepositoryTriggers(body []byte) (any, error) {
RepositoryName string `json:"repositoryName"`
Triggers []RepositoryTrigger `json:"triggers"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
@@ -53,7 +55,7 @@ func (h *Handler) handleTestRepositoryTriggers(body []byte) (any, error) {
RepositoryName string `json:"repositoryName"`
Triggers []RepositoryTrigger `json:"triggers"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
if req.RepositoryName == "" {
diff --git a/services/codecommit/handler_triggers_test.go b/services/codecommit/handler_triggers_test.go
index 9ac61811bf..6b013cc8f6 100644
--- a/services/codecommit/handler_triggers_test.go
+++ b/services/codecommit/handler_triggers_test.go
@@ -1,12 +1,14 @@
package codecommit_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_PutGetRepositoryTriggers(t *testing.T) {
@@ -33,7 +35,7 @@ func TestHandler_PutGetRepositoryTriggers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
triggers := resp["triggers"].([]any)
assert.Len(t, triggers, 1)
}
@@ -76,7 +78,7 @@ func TestHandler_TestRepositoryTriggers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
succeeded := resp["successfulExecutions"].([]any)
require.Len(t, succeeded, 1)
assert.Equal(t, "ad-hoc-trigger", succeeded[0])
@@ -89,7 +91,7 @@ func TestHandler_TestRepositoryTriggers(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
succeeded = resp["successfulExecutions"].([]any)
assert.Empty(t, succeeded)
}
@@ -123,7 +125,7 @@ func TestHandler_TriggerLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
got := resp["triggers"].([]any)
assert.Len(t, got, 2)
@@ -134,7 +136,7 @@ func TestHandler_TriggerLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
succeeded := resp["successfulExecutions"].([]any)
assert.Len(t, succeeded, 2)
@@ -148,7 +150,7 @@ func TestHandler_TriggerLifecycle(t *testing.T) {
rec = doRequest(t, h, "GetRepositoryTriggers", map[string]any{"repositoryName": "repo"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
got = resp["triggers"].([]any)
assert.Empty(t, got)
}
diff --git a/services/codecommit/multi_region_test.go b/services/codecommit/multi_region_test.go
index d6fc3fc89c..60b7e22645 100644
--- a/services/codecommit/multi_region_test.go
+++ b/services/codecommit/multi_region_test.go
@@ -2,7 +2,7 @@ package codecommit_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codecommit"
)
@@ -40,7 +41,7 @@ func regionCall(t *testing.T, h *codecommit.Handler, region, op, body string) ma
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
out := map[string]any{}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/codecommit/persistence.go b/services/codecommit/persistence.go
index a30c3ec124..4c3b82a9d8 100644
--- a/services/codecommit/persistence.go
+++ b/services/codecommit/persistence.go
@@ -2,7 +2,7 @@ package codecommit
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"time"
@@ -196,7 +196,7 @@ func buildPersistenceDTORegistry() (
// Repositories, and is rebuilt by rebuildRepositoriesByARN on Restore rather
// than persisted.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
RepoTemplateAssoc map[string]map[string]struct{} `json:"repoTemplateAssoc"`
PRApprovals map[string]map[string]string `json:"prApprovals"`
PROverrides map[string]bool `json:"prOverrides"`
@@ -337,7 +337,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// prApprovalRules; see store_setup.go's registerAllTables doc) from tables
// via the ephemeral DTO registry, factored out of Restore to keep Restore's
// own cognitive complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreDirtyTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTables(tables map[string]jsontext.Value) error {
dtoReg, commentDTOs, fileDTOs, prApprovalRuleDTOs := buildPersistenceDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
return fmt.Errorf("codecommit: restore snapshot DTO tables: %w", err)
diff --git a/services/codeconnections/handler.go b/services/codeconnections/handler.go
index f43f296419..def97581ec 100644
--- a/services/codeconnections/handler.go
+++ b/services/codeconnections/handler.go
@@ -2,7 +2,7 @@ package codeconnections
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -180,7 +181,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
ResourceName string `json:"ResourceName"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
switch {
case req.ConnectionArn != "":
@@ -228,7 +229,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleEchoError(_ context.Context, c *echo.Context, _ string, err error) error {
@@ -237,7 +238,7 @@ func (h *Handler) handleEchoError(_ context.Context, c *echo.Context, _ string,
payload, marshalErr := json.Marshal(service.JSONErrorResponse{
Type: errType,
Message: wireMessage(err, errType),
- })
+ }, jsonx.V1())
if marshalErr != nil {
return c.String(http.StatusInternalServerError, "internal server error")
}
diff --git a/services/codeconnections/handler_test.go b/services/codeconnections/handler_test.go
index 4a04a2608c..7372e4c5a4 100644
--- a/services/codeconnections/handler_test.go
+++ b/services/codeconnections/handler_test.go
@@ -10,7 +10,7 @@ package codeconnections_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codeconnections"
)
@@ -69,7 +70,7 @@ func doJSON(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -90,7 +91,7 @@ func parseResp(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
@@ -401,7 +402,7 @@ func TestExtractOperationAndResource(t *testing.T) {
if tt.body != nil {
var err error
- bodyBytes, err = json.Marshal(tt.body)
+ bodyBytes, err = json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
}
diff --git a/services/codeconnections/persistence.go b/services/codeconnections/persistence.go
index ae5fa540dd..ed5ea92dfb 100644
--- a/services/codeconnections/persistence.go
+++ b/services/codeconnections/persistence.go
@@ -2,7 +2,7 @@ package codeconnections
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -51,10 +51,10 @@ func regionalDTOKeyFn[V any](d *regionalDTO[V]) string { return regionKey(d.Regi
// decoding a snapshot from an incompatible (older or newer) build of this
// backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// persistenceDTOTables groups the ephemeral DTO tables Snapshot/Restore
@@ -187,7 +187,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// syncConfigurations, syncBlockers; see store_setup.go's registerAllTables
// doc) from tables via the ephemeral DTO registry, factored out of Restore to
// keep Restore's own cognitive complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreDirtyTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTables(tables map[string]jsontext.Value) error {
dtos := buildPersistenceDTORegistry()
if err := dtos.registry.RestoreAll(tables); err != nil {
return fmt.Errorf("codeconnections: restore snapshot DTO tables: %w", err)
diff --git a/services/codedeploy/application_revisions_test.go b/services/codedeploy/application_revisions_test.go
index e1de1c7bf0..893663e256 100644
--- a/services/codedeploy/application_revisions_test.go
+++ b/services/codedeploy/application_revisions_test.go
@@ -1,13 +1,14 @@
package codedeploy_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codedeploy"
)
@@ -42,7 +43,7 @@ func TestHandler_RegisterApplicationRevision_GetApplicationRevision(t *testing.T
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "my-app", resp["applicationName"])
revisionInfo, ok := resp["revisionInfo"].(map[string]any)
@@ -67,7 +68,7 @@ func TestHandler_GetApplicationRevision_NotRegistered(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "RevisionDoesNotExistException", resp["__type"])
}
@@ -106,7 +107,7 @@ func TestHandler_RegisterApplicationRevision_Idempotent(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
revisions, ok := listResp["revisions"].([]any)
require.True(t, ok)
require.Len(t, revisions, 1)
@@ -116,7 +117,7 @@ func TestHandler_RegisterApplicationRevision_Idempotent(t *testing.T) {
"revision": s3Revision("bucket", "key"),
})
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
revisionInfo, ok := getResp["revisionInfo"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "v2", revisionInfo["description"])
@@ -141,7 +142,7 @@ func TestHandler_ListApplicationRevisions_MultipleAndFilters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
revisions, ok := resp["revisions"].([]any)
require.True(t, ok)
require.Len(t, revisions, 2)
@@ -153,7 +154,7 @@ func TestHandler_ListApplicationRevisions_MultipleAndFilters(t *testing.T) {
require.Equal(t, http.StatusOK, filteredRec.Code)
var filteredResp map[string]any
- require.NoError(t, json.Unmarshal(filteredRec.Body.Bytes(), &filteredResp))
+ require.NoError(t, json.Unmarshal(filteredRec.Body.Bytes(), &filteredResp, jsonx.V1()))
filtered, ok := filteredResp["revisions"].([]any)
require.True(t, ok)
require.Len(t, filtered, 1)
@@ -298,7 +299,7 @@ func TestHandler_BatchGetApplicationRevisions(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "my-app", resp["applicationName"])
}
})
@@ -330,7 +331,7 @@ func TestHandler_BatchGetApplicationRevisions_PopulatesRegistered(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
revisions, ok := resp["revisions"].([]any)
require.True(t, ok)
require.Len(t, revisions, 2)
diff --git a/services/codedeploy/applications_test.go b/services/codedeploy/applications_test.go
index f009d3fc19..0f7df24d61 100644
--- a/services/codedeploy/applications_test.go
+++ b/services/codedeploy/applications_test.go
@@ -1,7 +1,7 @@
package codedeploy_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codedeploy"
)
@@ -71,7 +72,7 @@ func TestHandler_CreateApplication(t *testing.T) {
if tt.wantAppID {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["applicationId"])
}
})
@@ -118,7 +119,7 @@ func TestHandler_CreateApplication_ComputePlatformValidation(t *testing.T) {
if tt.wantErrType != "" {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, resp["__type"])
}
})
@@ -175,7 +176,7 @@ func TestHandler_ListApplications(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
apps, ok := resp["applications"].([]any)
require.True(t, ok)
assert.Empty(t, apps)
@@ -184,7 +185,7 @@ func TestHandler_ListApplications(t *testing.T) {
doRequest(t, h, "CreateApplication", map[string]any{"applicationName": "app2"})
rec = doRequest(t, h, "ListApplications", map[string]any{})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
apps, ok = resp["applications"].([]any)
require.True(t, ok)
assert.Len(t, apps, 2)
@@ -206,7 +207,7 @@ func TestApplications_SortedList(t *testing.T) {
Applications []string `json:"applications"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, []string{"apple", "mango", "zebra"}, resp.Applications)
}
@@ -329,7 +330,7 @@ func TestHandler_BatchGetApplications(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
infos, ok := resp["applicationsInfo"].([]any)
require.True(t, ok)
assert.Len(t, infos, tt.wantCount)
diff --git a/services/codedeploy/deployment_configs_test.go b/services/codedeploy/deployment_configs_test.go
index 2eca2a61c7..49ade1a09a 100644
--- a/services/codedeploy/deployment_configs_test.go
+++ b/services/codedeploy/deployment_configs_test.go
@@ -1,12 +1,14 @@
package codedeploy_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateDeploymentConfig(t *testing.T) {
@@ -57,7 +59,7 @@ func TestHandler_CreateDeploymentConfig(t *testing.T) {
if tt.wantID {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["deploymentConfigId"])
}
})
@@ -240,7 +242,7 @@ func TestDeploymentConfigs_SubStructs(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
tt.checkFn(t, resp["deploymentConfigInfo"])
}
})
@@ -270,7 +272,7 @@ func TestDeploymentConfigs_DefaultsSeeded(t *testing.T) {
var resp struct {
DeploymentConfigsList []string `json:"deploymentConfigsList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for _, name := range expectedDefaults {
assert.Contains(t, resp.DeploymentConfigsList, name, "missing default config: %s", name)
@@ -324,7 +326,7 @@ func TestDeploymentConfigs_DefaultsReturnSubStructs(t *testing.T) {
ComputePlatform string `json:"computePlatform"`
} `json:"deploymentConfigInfo"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantPlatform, resp.DeploymentConfigInfo.ComputePlatform)
require.NotNil(t, resp.DeploymentConfigInfo.TrafficRoutingConfig)
assert.Equal(t, tt.wantRoutingType, resp.DeploymentConfigInfo.TrafficRoutingConfig.Type)
@@ -342,7 +344,7 @@ func TestDeploymentConfigs_DefaultsCannotDelete(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// DeleteDeploymentConfig's own deserializer models InvalidOperationException
// for this case, not DeploymentConfigInUseException (verified against
// aws-sdk-go-v2/service/codedeploy deserializers.go).
@@ -382,7 +384,7 @@ func TestDeploymentConfigs_InUseCannotDelete(t *testing.T) {
assert.Equal(t, http.StatusConflict, delRec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "DeploymentConfigInUseException", resp["__type"])
}
@@ -400,7 +402,7 @@ func TestDeploymentConfigs_ErrInvalidComputePlatformMapping(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidComputePlatformException", resp["__type"])
}
diff --git a/services/codedeploy/deployment_group_tag_filters_test.go b/services/codedeploy/deployment_group_tag_filters_test.go
index c9ce92ccf2..7c7783d46a 100644
--- a/services/codedeploy/deployment_group_tag_filters_test.go
+++ b/services/codedeploy/deployment_group_tag_filters_test.go
@@ -1,13 +1,15 @@
package codedeploy_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func ec2TagSetPayload() map[string]any {
@@ -124,7 +126,7 @@ func TestDeploymentGroups_TagFilterValidation_Create(t *testing.T) {
if tt.wantType != "" {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
}
})
@@ -219,7 +221,7 @@ func TestDeploymentGroups_TagFilterValidation_Update(t *testing.T) {
if tt.wantType != "" {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
}
})
diff --git a/services/codedeploy/deployment_groups_test.go b/services/codedeploy/deployment_groups_test.go
index e75db1b623..d45aa51e3b 100644
--- a/services/codedeploy/deployment_groups_test.go
+++ b/services/codedeploy/deployment_groups_test.go
@@ -1,7 +1,7 @@
package codedeploy_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codedeploy"
)
@@ -68,7 +69,7 @@ func TestHandler_CreateDeploymentGroup(t *testing.T) {
if tt.wantID {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["deploymentGroupId"])
}
})
@@ -169,7 +170,7 @@ func TestDeploymentGroups_RichFieldsRoundTrip(t *testing.T) {
TerminationHookEnabled bool `json:"terminationHookEnabled"`
} `json:"deploymentGroupInfo"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
info := resp.DeploymentGroupInfo
assert.Equal(t, "arn:aws:iam::123:role/role", info.ServiceRoleArn)
@@ -310,7 +311,7 @@ func TestDeploymentGroups_SortedList(t *testing.T) {
DeploymentGroups []string `json:"deploymentGroups"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, []string{"a-group", "m-group", "z-group"}, resp.DeploymentGroups)
}
@@ -483,7 +484,7 @@ func TestDeploymentGroups_Update(t *testing.T) {
if tt.wantStatus == http.StatusOK && tt.wantHooksClean {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
hooksNotCleanedUp, _ := resp["hooksNotCleanedUp"].(bool)
assert.True(t, hooksNotCleanedUp, "expected hooksNotCleanedUp=true when alarms were removed")
}
@@ -551,7 +552,7 @@ func TestDeploymentGroups_InheritsComputePlatform(t *testing.T) {
ComputePlatform string `json:"computePlatform"`
} `json:"deploymentGroupInfo"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.platform, resp.DeploymentGroupInfo.ComputePlatform)
})
}
@@ -598,7 +599,7 @@ func TestDeploymentGroups_ComputePlatformInheritedViaHandler(t *testing.T) {
ComputePlatform string `json:"computePlatform"`
} `json:"deploymentGroupInfo"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.platform, out.DeploymentGroupInfo.ComputePlatform,
"deployment group must inherit computePlatform from application")
})
@@ -655,7 +656,7 @@ func TestHandler_BatchGetDeploymentGroups(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
infos, ok := resp["deploymentGroupsInfo"].([]any)
require.True(t, ok)
assert.Len(t, infos, tt.wantCount)
@@ -681,7 +682,7 @@ func TestDeploymentGroups_BatchGetEmptySlice(t *testing.T) {
DeploymentGroupsInfo []any `json:"deploymentGroupsInfo"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// Should be an empty array not null
assert.NotNil(t, resp.DeploymentGroupsInfo)
assert.Empty(t, resp.DeploymentGroupsInfo)
@@ -717,7 +718,7 @@ func TestDeploymentGroups_BatchGetRichFields(t *testing.T) {
} `json:"ecsServices"`
} `json:"deploymentGroupsInfo"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.DeploymentGroupsInfo, 1)
info := resp.DeploymentGroupsInfo[0]
diff --git a/services/codedeploy/deployment_instances_test.go b/services/codedeploy/deployment_instances_test.go
index b933f43a46..788a9e566b 100644
--- a/services/codedeploy/deployment_instances_test.go
+++ b/services/codedeploy/deployment_instances_test.go
@@ -1,13 +1,14 @@
package codedeploy_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codedeploy"
)
@@ -65,7 +66,7 @@ func TestDeploymentInstances_BatchGetMissingDeployment(t *testing.T) {
assert.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "DeploymentDoesNotExistException", resp["__type"])
}
@@ -87,7 +88,7 @@ func TestHandler_BatchGetDeploymentInstances_RealTargets(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["instancesSummary"].([]any)
require.True(t, ok)
// Only the two tag-filter-matched instances resolve as real targets;
@@ -144,7 +145,7 @@ func TestHandler_BatchGetDeploymentTargets_ECS(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
targets, ok := resp["deploymentTargets"].([]any)
require.True(t, ok)
require.Len(t, targets, 1)
@@ -207,7 +208,7 @@ func TestHandler_GetDeploymentTarget_Lambda(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
target, ok := resp["deploymentTarget"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "LambdaTarget", target["deploymentTargetType"])
@@ -222,7 +223,7 @@ func TestHandler_GetDeploymentTarget_Lambda(t *testing.T) {
assert.Equal(t, http.StatusNotFound, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "DeploymentTargetDoesNotExistException", errResp["__type"])
}
@@ -250,7 +251,7 @@ func TestHandler_GetDeploymentInstance_RealMatch(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summary, ok := resp["instanceSummary"].(map[string]any)
require.True(t, ok)
assert.Equal(t, matched[0], summary["instanceId"])
@@ -277,7 +278,7 @@ func TestHandler_ListDeploymentTargets_ListDeploymentInstances_RealTargets(t *te
require.Equal(t, http.StatusOK, rec.Code)
var targetsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &targetsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &targetsResp, jsonx.V1()))
targetIDs, ok := targetsResp["targetIds"].([]any)
require.True(t, ok)
require.Len(t, targetIDs, 2)
@@ -287,7 +288,7 @@ func TestHandler_ListDeploymentTargets_ListDeploymentInstances_RealTargets(t *te
require.Equal(t, http.StatusOK, rec.Code)
var instResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &instResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &instResp, jsonx.V1()))
instances, ok := instResp["instancesList"].([]any)
require.True(t, ok)
assert.ElementsMatch(t, []any{matched[0], matched[1]}, instances)
@@ -339,7 +340,7 @@ func TestHandler_ListDeploymentInstances_StatusAndTypeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
instances, ok := resp["instancesList"].([]any)
require.True(t, ok)
assert.Len(t, instances, tt.wantLen)
@@ -390,7 +391,7 @@ func TestHandler_ListDeploymentTargets_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
targetIDs, ok := resp["targetIds"].([]any)
require.True(t, ok)
assert.Len(t, targetIDs, tt.wantLen)
@@ -425,7 +426,7 @@ func TestHandler_ListDeploymentInstances_ECSIsEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
instances, ok := resp["instancesList"].([]any)
require.True(t, ok)
assert.Empty(t, instances)
@@ -450,7 +451,7 @@ func TestDeploymentTargets_StatusTracksDeploymentStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
target, ok := resp["deploymentTarget"].(map[string]any)
require.True(t, ok)
instanceTarget, ok := target["instanceTarget"].(map[string]any)
diff --git a/services/codedeploy/deployments_test.go b/services/codedeploy/deployments_test.go
index c97a1a6dec..2ebac12df6 100644
--- a/services/codedeploy/deployments_test.go
+++ b/services/codedeploy/deployments_test.go
@@ -1,7 +1,7 @@
package codedeploy_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"regexp"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codedeploy"
)
@@ -111,14 +112,14 @@ func TestHandler_CreateDeployment(t *testing.T) {
if tt.wantID {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["deploymentId"])
assert.True(t, len(resp["deploymentId"]) > 2 && resp["deploymentId"][:2] == "d-")
}
if tt.wantErrType != "" {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, resp["__type"])
}
})
@@ -153,7 +154,7 @@ func TestDeployments_CreateIDFormat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id := resp["deploymentId"]
assert.True(t, deployIDRe.MatchString(id), "deploymentId %q does not match pattern d-[A-Z0-9]{9}", id)
}
@@ -184,7 +185,7 @@ func TestDeployments_CreateIDFormat_TableDriven(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Regexp(t, deployIDRe, out["deploymentId"],
"deploymentId must match d-[A-Z0-9]{9}")
})
@@ -209,7 +210,7 @@ func TestDeployments_CreateExtraFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
rec2 := doRequest(t, h, "GetDeployment", map[string]any{"deploymentId": createResp["deploymentId"]})
require.Equal(t, http.StatusOK, rec2.Code)
@@ -222,7 +223,7 @@ func TestDeployments_CreateExtraFields(t *testing.T) {
IgnoreApplicationStopFailures bool `json:"ignoreApplicationStopFailures"`
} `json:"deploymentInfo"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "my-description", getResp.DeploymentInfo.Description)
assert.Equal(t, "OVERWRITE", getResp.DeploymentInfo.FileExistsBehavior)
@@ -253,7 +254,7 @@ func TestDeployments_S3RevisionRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
deployID := createOut["deploymentId"]
require.NotEmpty(t, deployID)
@@ -274,7 +275,7 @@ func TestDeployments_S3RevisionRoundTrip(t *testing.T) {
} `json:"revision"`
} `json:"deploymentInfo"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
rev := getOut.DeploymentInfo.Revision
assert.Equal(t, "S3", rev.RevisionType, "revisionType must round-trip")
@@ -305,7 +306,7 @@ func TestDeployments_GitHubRevisionRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
deployID := createOut["deploymentId"]
getRec := doRequest(t, h, "GetDeployment", map[string]any{"deploymentId": deployID})
@@ -322,7 +323,7 @@ func TestDeployments_GitHubRevisionRoundTrip(t *testing.T) {
} `json:"revision"`
} `json:"deploymentInfo"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
rev := getOut.DeploymentInfo.Revision
assert.Equal(t, "GitHub", rev.RevisionType)
@@ -397,7 +398,7 @@ func TestDeployments_InfoIncludesConfigName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
info := resp["deploymentInfo"]
assert.Equal(t, "CodeDeployDefault.AllAtOnce", info["deploymentConfigName"])
@@ -416,7 +417,7 @@ func TestDeployments_GetDeploymentOverview(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
getRec := doRequest(t, h, "GetDeployment", map[string]any{"deploymentId": createOut["deploymentId"]})
require.Equal(t, http.StatusOK, getRec.Code)
@@ -426,7 +427,7 @@ func TestDeployments_GetDeploymentOverview(t *testing.T) {
DeploymentOverview map[string]any `json:"deploymentOverview"`
} `json:"deploymentInfo"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
ov := getOut.DeploymentInfo.DeploymentOverview
assert.NotNil(t, ov, "GetDeployment must include deploymentOverview")
@@ -455,7 +456,7 @@ func TestHandler_ListDeployments(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
deployments, ok := resp["deployments"].([]any)
require.True(t, ok)
assert.Len(t, deployments, 1)
@@ -483,7 +484,7 @@ func TestDeployments_SortedList(t *testing.T) {
Deployments []string `json:"deployments"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Deployments, 2)
assert.Contains(t, resp.Deployments, d1.DeploymentID)
@@ -535,7 +536,7 @@ func TestDeployments_ListStatusFilter(t *testing.T) {
var resp struct {
Deployments []string `json:"deployments"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Deployments, tt.wantLen)
})
}
@@ -578,7 +579,7 @@ func TestDeployments_ListExternalIDFilter(t *testing.T) {
var resp struct {
Deployments []string `json:"deployments"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Deployments, tt.wantLen)
})
}
@@ -714,7 +715,7 @@ func TestHandler_ContinueDeployment(t *testing.T) {
if tt.wantErrType != "" {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, resp["__type"])
}
})
@@ -738,7 +739,7 @@ func TestDeployments_StopDeployment_AlreadyStopped(t *testing.T) {
assert.Equal(t, http.StatusConflict, secondStop.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(secondStop.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(secondStop.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "DeploymentAlreadyCompletedException", errResp["__type"])
}
@@ -756,7 +757,7 @@ func TestDeployments_StopDeploymentStatus(t *testing.T) {
var stopOut struct {
Status string `json:"status"`
}
- require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopOut))
+ require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopOut, jsonx.V1()))
assert.Equal(t, "Succeeded", stopOut.Status,
"StopDeploymentOutput.status is the StopStatus enum (Pending|Succeeded), not the deployment status")
@@ -768,7 +769,7 @@ func TestDeployments_StopDeploymentStatus(t *testing.T) {
Status string `json:"status"`
} `json:"deploymentInfo"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "Stopped", getOut.DeploymentInfo.Status,
"GetDeployment must reflect Stopped status after StopDeployment")
}
@@ -821,7 +822,7 @@ func TestHandler_BatchGetDeployments(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
infos, ok := resp["deploymentsInfo"].([]any)
require.True(t, ok)
assert.Len(t, infos, tt.wantCount)
@@ -847,7 +848,7 @@ func TestDeployments_BatchGetReturnsRevision(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
batchRec := doRequest(t, h, "BatchGetDeployments", map[string]any{
"deploymentIds": []string{createOut["deploymentId"]},
@@ -864,7 +865,7 @@ func TestDeployments_BatchGetReturnsRevision(t *testing.T) {
var batchOut struct {
DeploymentsInfo []batchItem `json:"deploymentsInfo"`
}
- require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batchOut))
+ require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batchOut, jsonx.V1()))
require.Len(t, batchOut.DeploymentsInfo, 1)
info := batchOut.DeploymentsInfo[0]
@@ -887,7 +888,7 @@ func TestDeployments_SkipWaitTime_DeploymentNotFound(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "DeploymentDoesNotExistException", resp["__type"])
}
@@ -907,7 +908,7 @@ func TestDeployments_SkipWaitTime_DeploymentFound(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
deployID := createOut["deploymentId"]
skipRec := doRequest(t, h, "SkipWaitTimeForInstanceTermination", map[string]any{
diff --git a/services/codedeploy/github_tokens_test.go b/services/codedeploy/github_tokens_test.go
index 6caf26e03f..7a3c379f5d 100644
--- a/services/codedeploy/github_tokens_test.go
+++ b/services/codedeploy/github_tokens_test.go
@@ -1,12 +1,14 @@
package codedeploy_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestGitHubTokens_ListAddDelete(t *testing.T) {
@@ -21,7 +23,7 @@ func TestGitHubTokens_ListAddDelete(t *testing.T) {
var listResp struct {
TokenNameList []string `json:"tokenNameList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Empty(t, listResp.TokenNameList)
// Add token via internal method and verify it appears.
@@ -29,7 +31,7 @@ func TestGitHubTokens_ListAddDelete(t *testing.T) {
rec = doRequest(t, h, "ListGitHubAccountTokenNames", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Equal(t, []string{"my-token"}, listResp.TokenNameList)
// Delete the token.
@@ -39,7 +41,7 @@ func TestGitHubTokens_ListAddDelete(t *testing.T) {
// List should be empty again.
rec = doRequest(t, h, "ListGitHubAccountTokenNames", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Empty(t, listResp.TokenNameList)
}
@@ -51,7 +53,7 @@ func TestGitHubTokens_DeleteNotFound(t *testing.T) {
assert.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "GitHubAccountTokenDoesNotExistException", resp["__type"])
}
diff --git a/services/codedeploy/handler.go b/services/codedeploy/handler.go
index 63367ebb0f..728c0243fc 100644
--- a/services/codedeploy/handler.go
+++ b/services/codedeploy/handler.go
@@ -2,7 +2,7 @@ package codedeploy
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -176,7 +177,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
var input struct {
ApplicationName string `json:"applicationName"`
}
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return ""
}
@@ -263,7 +264,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// errorMapping maps sentinel errors to HTTP status and exception type.
@@ -341,7 +342,7 @@ func stripExceptionPrefixes(msg string) string {
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
makePayload := func(code, msg string) []byte {
- b, _ := json.Marshal(service.JSONErrorResponse{Type: code, Message: stripExceptionPrefixes(msg)})
+ b, _ := json.Marshal(service.JSONErrorResponse{Type: code, Message: stripExceptionPrefixes(msg)}, jsonx.V1())
return b
}
@@ -352,10 +353,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
}
}
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
- if errors.As(err, &syntaxErr) || errors.As(err, &typeErr) {
+ if jsonx.IsSyntaxError(err) || jsonx.IsTypeError(err) {
return c.JSONBlob(http.StatusBadRequest,
makePayload("InvalidRequestException", err.Error()))
}
diff --git a/services/codedeploy/handler_deployment_groups.go b/services/codedeploy/handler_deployment_groups.go
index b8a2b34870..569cf84310 100644
--- a/services/codedeploy/handler_deployment_groups.go
+++ b/services/codedeploy/handler_deployment_groups.go
@@ -2,10 +2,11 @@ package codedeploy
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
)
@@ -58,7 +59,7 @@ type autoScalingGroupEntry struct {
// UnmarshalJSON accepts a bare name (SDK AutoScalingGroups []string) or the object form.
func (e *autoScalingGroupEntry) UnmarshalJSON(data []byte) error {
var name string
- if err := json.Unmarshal(data, &name); err == nil {
+ if err := json.Unmarshal(data, &name, jsonx.V1()); err == nil {
*e = autoScalingGroupEntry{Name: name}
return nil
@@ -67,7 +68,7 @@ func (e *autoScalingGroupEntry) UnmarshalJSON(data []byte) error {
type plain autoScalingGroupEntry
var p plain
- if err := json.Unmarshal(data, &p); err != nil {
+ if err := json.Unmarshal(data, &p, jsonx.V1()); err != nil {
return err
}
diff --git a/services/codedeploy/handler_test.go b/services/codedeploy/handler_test.go
index edb31c703f..48a995c23b 100644
--- a/services/codedeploy/handler_test.go
+++ b/services/codedeploy/handler_test.go
@@ -2,7 +2,7 @@ package codedeploy_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/codedeploy"
)
@@ -29,7 +30,7 @@ func doRequest(t *testing.T, h *codedeploy.Handler, action string, body any) *ht
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -175,7 +176,7 @@ func TestHandler_ExtractResource(t *testing.T) {
h := newTestHandler(t)
e := echo.New()
- bodyBytes, _ := json.Marshal(map[string]string{"applicationName": "my-app"})
+ bodyBytes, _ := json.Marshal(map[string]string{"applicationName": "my-app"}, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
diff --git a/services/codedeploy/lifecycle_hooks_test.go b/services/codedeploy/lifecycle_hooks_test.go
index 5706b77e97..bcf8e91155 100644
--- a/services/codedeploy/lifecycle_hooks_test.go
+++ b/services/codedeploy/lifecycle_hooks_test.go
@@ -1,12 +1,14 @@
package codedeploy_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestLifecycleHooks_PutExecutionStatus_DeploymentNotFound verifies that
@@ -26,7 +28,7 @@ func TestLifecycleHooks_PutExecutionStatus_DeploymentNotFound(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "DeploymentDoesNotExistException", resp["__type"])
}
@@ -46,7 +48,7 @@ func TestLifecycleHooks_PutExecutionStatus_DeploymentFound(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
deployID := createOut["deploymentId"]
hookRec := doRequest(t, h, "PutLifecycleEventHookExecutionStatus", map[string]any{
@@ -59,6 +61,6 @@ func TestLifecycleHooks_PutExecutionStatus_DeploymentFound(t *testing.T) {
var hookOut struct {
LifecycleEventHookExecutionID string `json:"lifecycleEventHookExecutionId"`
}
- require.NoError(t, json.Unmarshal(hookRec.Body.Bytes(), &hookOut))
+ require.NoError(t, json.Unmarshal(hookRec.Body.Bytes(), &hookOut, jsonx.V1()))
assert.Equal(t, "hook-1", hookOut.LifecycleEventHookExecutionID)
}
diff --git a/services/codedeploy/lifecycle_validation_test.go b/services/codedeploy/lifecycle_validation_test.go
index 81e1d63746..9c2acb7c6b 100644
--- a/services/codedeploy/lifecycle_validation_test.go
+++ b/services/codedeploy/lifecycle_validation_test.go
@@ -1,13 +1,15 @@
package codedeploy_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCreateDeploymentGroup_InputValidation(t *testing.T) {
@@ -68,7 +70,7 @@ func TestCreateDeploymentGroup_InputValidation(t *testing.T) {
}
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantCode, resp["__type"])
assert.NotContains(t, resp["message"], "Exception:")
})
diff --git a/services/codedeploy/multi_region_test.go b/services/codedeploy/multi_region_test.go
index 62cbae9fa4..a10c8ca8e5 100644
--- a/services/codedeploy/multi_region_test.go
+++ b/services/codedeploy/multi_region_test.go
@@ -2,7 +2,7 @@ package codedeploy_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codedeploy"
)
@@ -38,7 +39,7 @@ func regionCall(t *testing.T, h *codedeploy.Handler, region, op, body string) ma
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
out := map[string]any{}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/codedeploy/on_premises_instances_test.go b/services/codedeploy/on_premises_instances_test.go
index 84dbf6e05a..24f31218c9 100644
--- a/services/codedeploy/on_premises_instances_test.go
+++ b/services/codedeploy/on_premises_instances_test.go
@@ -1,7 +1,7 @@
package codedeploy_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codedeploy"
)
@@ -90,7 +91,7 @@ func TestHandler_BatchGetOnPremisesInstances(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
infos, ok := resp["instanceInfos"].([]any)
require.True(t, ok)
assert.Len(t, infos, tt.wantCount)
@@ -120,7 +121,7 @@ func TestOnPremisesInstances_TagsAlwaysSlice(t *testing.T) {
} `json:"instanceInfos"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.InstanceInfos, 1)
// Tags should be an empty slice, not nil/missing
assert.NotNil(t, resp.InstanceInfos[0].Tags)
@@ -230,7 +231,7 @@ func TestOnPremisesInstances_IamValidation(t *testing.T) {
if tt.wantErrType != "" {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, resp["__type"])
}
})
@@ -290,7 +291,7 @@ func TestOnPremisesInstances_ListTagFilter(t *testing.T) {
var resp struct {
InstanceNames []string `json:"instanceNames"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.ElementsMatch(t, tt.wantNames, resp.InstanceNames)
})
}
@@ -337,7 +338,7 @@ func TestOnPremisesInstances_NotFoundErrorMapping(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
})
}
@@ -354,7 +355,7 @@ func TestGetOnPremisesInstance_NotRegisteredNotDoesNotExist(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InstanceNotRegisteredException", resp["__type"])
assert.NotEqual(t, "InstanceDoesNotExistException", resp["__type"])
@@ -364,6 +365,6 @@ func TestGetOnPremisesInstance_NotRegisteredNotDoesNotExist(t *testing.T) {
var listResp struct {
InstanceNames []string `json:"instanceNames"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Empty(t, listResp.InstanceNames)
}
diff --git a/services/codedeploy/persistence.go b/services/codedeploy/persistence.go
index f996c46e61..63f92a36f0 100644
--- a/services/codedeploy/persistence.go
+++ b/services/codedeploy/persistence.go
@@ -2,7 +2,7 @@ package codedeploy
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -149,11 +149,11 @@ func buildPersistenceDTORegistry() persistenceDTOTables {
// identity-less set left unconverted (see store_setup.go) and is persisted
// here directly, as before.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- GitHubTokens []string `json:"githubTokens,omitempty"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ GitHubTokens []string `json:"githubTokens,omitempty"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON. Returns nil on marshal failure.
@@ -275,7 +275,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// registerAllTables doc) from tables via the ephemeral DTO registry,
// factored out of Restore to keep Restore's own cognitive complexity low.
// Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreDirtyTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTables(tables map[string]jsontext.Value) error {
dtos := buildPersistenceDTORegistry()
if err := dtos.registry.RestoreAll(tables); err != nil {
return fmt.Errorf("codedeploy: restore snapshot DTO tables: %w", err)
diff --git a/services/codedeploy/store_setup.go b/services/codedeploy/store_setup.go
index 0fba4b10c0..e22d1e97de 100644
--- a/services/codedeploy/store_setup.go
+++ b/services/codedeploy/store_setup.go
@@ -30,8 +30,9 @@ package codedeploy
// NOT converted: there is no *T value for store.Table to key on. It remains
// a plain map, unchanged by this refactor (see backend.go).
import (
- "encoding/json"
+ "encoding/json/v2"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/store"
)
@@ -61,7 +62,7 @@ func deploymentConfigTableKeyFn(v *DeploymentConfig) string { return v.Deploymen
// which is exactly the identity real CodeDeploy uses to deduplicate
// registrations of the same revision.
func applicationRevisionKey(appName string, r RevisionLocation) string {
- b, _ := json.Marshal(r)
+ b, _ := json.Marshal(r, jsonx.V1())
return appName + "\x00" + string(b)
}
diff --git a/services/codedeploy/tags_test.go b/services/codedeploy/tags_test.go
index f5ea2af983..32dbe20c28 100644
--- a/services/codedeploy/tags_test.go
+++ b/services/codedeploy/tags_test.go
@@ -1,13 +1,15 @@
package codedeploy_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_Tagging(t *testing.T) {
@@ -69,7 +71,7 @@ func TestTags_SortedListTagsForResource(t *testing.T) {
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Tags, 3)
assert.Equal(t, "aaa", resp.Tags[0].Key)
assert.Equal(t, "mmm", resp.Tags[1].Key)
@@ -104,7 +106,7 @@ func TestTags_OnDeploymentGroups(t *testing.T) {
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Tags, 1)
assert.Equal(t, "env", resp.Tags[0].Key)
assert.Equal(t, "prod", resp.Tags[0].Value)
@@ -189,7 +191,7 @@ func TestTags_ResourceTagLimits(t *testing.T) {
if tt.wantErrType != "" {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, resp["__type"])
}
})
@@ -219,7 +221,7 @@ func TestTags_ResourceExceedsMaxTags(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// TagResource's own deserializer models only InvalidTagsToAddException for
// tag content, not TagLimitExceededException (that code belongs to
// AddTagsToOnPremisesInstances/RemoveTagsFromOnPremisesInstances/
diff --git a/services/codegurureviewer/persistence.go b/services/codegurureviewer/persistence.go
index 0ca94e738d..5c7be4e197 100644
--- a/services/codegurureviewer/persistence.go
+++ b/services/codegurureviewer/persistence.go
@@ -2,7 +2,7 @@ package codegurureviewer
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,10 +12,10 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/codepipeline/async_builtin_actions_test.go b/services/codepipeline/async_builtin_actions_test.go
index a945dce975..166ee30585 100644
--- a/services/codepipeline/async_builtin_actions_test.go
+++ b/services/codepipeline/async_builtin_actions_test.go
@@ -4,7 +4,7 @@ import (
"archive/zip"
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"sync"
"testing"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codepipeline"
)
@@ -198,7 +199,7 @@ func TestBuiltinActions_WaitForBackendState(t *testing.T) {
details, lErr := h.Backend.ListActionExecutions(ctx, "p", exec.PipelineExecutionID)
require.NoError(t, lErr)
- raw, mErr := json.Marshal(details)
+ raw, mErr := json.Marshal(details, jsonx.V1())
require.NoError(t, mErr)
assert.Contains(t, string(raw), tt.wantMsg)
}
diff --git a/services/codepipeline/custom_action_types_test.go b/services/codepipeline/custom_action_types_test.go
index 1cda05da9d..faeb26b739 100644
--- a/services/codepipeline/custom_action_types_test.go
+++ b/services/codepipeline/custom_action_types_test.go
@@ -2,7 +2,8 @@ package codepipeline_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codepipeline"
)
@@ -80,7 +82,7 @@ func TestHandler_CreateCustomActionType(t *testing.T) {
if !tt.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
at, ok := out["actionType"].(map[string]any)
require.True(t, ok)
id, ok := at["id"].(map[string]any)
@@ -130,7 +132,7 @@ func TestCreateCustomActionType_ActionConfigurationProperties_EmptyNotAbsent(t *
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
actionType, ok := raw["actionType"].(map[string]any)
require.True(t, ok, "actionType must be an object")
@@ -164,7 +166,7 @@ func TestCreateCustomActionType_ActionConfigurationProperties_RoundTrip(t *testi
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
actionType, ok := raw["actionType"].(map[string]any)
require.True(t, ok)
@@ -294,7 +296,7 @@ func TestHandler_DeleteCustomActionType_InUse(t *testing.T) {
if tt.wantType != "" {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out["__type"])
}
})
@@ -358,7 +360,7 @@ func TestHandler_GetActionType(t *testing.T) {
if !tt.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
at, ok := out["actionType"].(map[string]any)
require.True(t, ok)
id, ok := at["id"].(map[string]any)
@@ -402,7 +404,7 @@ func TestGetActionType_ActionTypeDeclarationShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
actionType, ok := raw["actionType"].(map[string]any)
require.True(t, ok, "actionType must be an object")
@@ -484,7 +486,7 @@ func TestGetActionType_AfterUpdateActionType_ReturnsDeclarationData(t *testing.T
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
actionType, ok := raw["actionType"].(map[string]any)
require.True(t, ok)
@@ -507,7 +509,7 @@ func TestGetActionType_AfterUpdateActionType_ReturnsDeclarationData(t *testing.T
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
items, _ := listOut["actionTypes"].([]any)
require.Len(t, items, 1)
item0, _ := items[0].(map[string]any)
@@ -627,7 +629,7 @@ func TestHandler_ListActionTypes_FullShape(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
items, _ := out["actionTypes"].([]any)
assert.Len(t, items, tt.wantCount)
@@ -667,7 +669,7 @@ func TestCPPagination_ListActionTypes(t *testing.T) {
NextToken string `json:"nextToken"`
ActionTypes []map[string]any `json:"actionTypes"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.ActionTypes, 5)
assert.Empty(t, out.NextToken, "5 items fit within the 25-cap default page")
@@ -707,7 +709,7 @@ func TestCPPagination_ListActionTypes_MultiPage(t *testing.T) {
NextToken string `json:"nextToken"`
ActionTypes []map[string]any `json:"actionTypes"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
total += len(out.ActionTypes)
pages++
@@ -771,7 +773,7 @@ func TestHandler_UpdateActionType_FullFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
at, _ := out["actionType"].(map[string]any)
inArt, _ := at["inputArtifactDetails"].(map[string]any)
diff --git a/services/codepipeline/deploy_cloudformation.go b/services/codepipeline/deploy_cloudformation.go
index bb52766062..8f8de5a2c3 100644
--- a/services/codepipeline/deploy_cloudformation.go
+++ b/services/codepipeline/deploy_cloudformation.go
@@ -2,11 +2,14 @@ package codepipeline
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -285,7 +288,7 @@ func (b *InMemoryBackend) cfnPublishOutputs(
return
}
- body, err := json.Marshal(st.Outputs)
+ body, err := json.Marshal(st.Outputs, jsonx.V1())
if err == nil {
err = b.putOutputFiles(ctx, p, execID, action, map[string][]byte{file: body})
}
@@ -431,12 +434,12 @@ func (b *InMemoryBackend) applyTemplateConfiguration(
}
var doc struct {
- Parameters map[string]any `json:"Parameters"`
- Tags map[string]any `json:"Tags"`
- StackPolicy json.RawMessage `json:"StackPolicy"`
+ Parameters map[string]any `json:"Parameters"`
+ Tags map[string]any `json:"Tags"`
+ StackPolicy jsontext.Value `json:"StackPolicy"`
}
- if err = json.Unmarshal(body, &doc); err != nil {
+ if err = json.Unmarshal(body, &doc, jsonx.V1()); err != nil {
return fmt.Errorf("template configuration %q is not valid JSON: %w", ref, err)
}
@@ -469,7 +472,7 @@ func (b *InMemoryBackend) applyParameterOverrides(
ctx context.Context, p *Pipeline, execID string, action Action, raw string, req *StackRequest,
) error {
var overrides map[string]any
- if err := json.Unmarshal([]byte(raw), &overrides); err != nil {
+ if err := json.Unmarshal([]byte(raw), &overrides, jsonx.V1()); err != nil {
return fmt.Errorf("ParameterOverrides is not a valid JSON object: %w", err)
}
@@ -506,7 +509,7 @@ func (b *InMemoryBackend) resolveParameterFunction(
}
var doc map[string]any
- if err = json.Unmarshal(body, &doc); err != nil {
+ if err = json.Unmarshal(body, &doc, jsonx.V1()); err != nil {
return "", fmt.Errorf("%s is not a JSON object: %w", scalarString(args[1]), err)
}
diff --git a/services/codepipeline/deploy_ecs.go b/services/codepipeline/deploy_ecs.go
index 3c49a802fd..d6ea044324 100644
--- a/services/codepipeline/deploy_ecs.go
+++ b/services/codepipeline/deploy_ecs.go
@@ -2,10 +2,12 @@ package codepipeline
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strconv"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -97,7 +99,7 @@ func (b *InMemoryBackend) ecsStartDeployment(
}
var defs []imageDefinition
- if err = json.Unmarshal(body, &defs); err != nil || len(defs) == 0 {
+ if err = json.Unmarshal(body, &defs, jsonx.V1()); err != nil || len(defs) == 0 {
return "", fmt.Errorf(
"%w: image definitions file %q must be a non-empty JSON array of name/imageUri",
ErrValidation,
diff --git a/services/codepipeline/handler.go b/services/codepipeline/handler.go
index d2c660af97..03a8c42efe 100644
--- a/services/codepipeline/handler.go
+++ b/services/codepipeline/handler.go
@@ -2,7 +2,7 @@ package codepipeline
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -12,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -294,7 +295,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
@@ -346,10 +347,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
}
}
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
- if errors.As(err, &syntaxErr) || errors.As(err, &typeErr) {
+ if jsonx.IsSyntaxError(err) || jsonx.IsTypeError(err) {
return errorBlob(c, http.StatusBadRequest, errTypeValidation, err)
}
@@ -372,7 +370,7 @@ func errorBlob(c *echo.Context, status int, errType string, err error) error {
payload, _ := json.Marshal(service.JSONErrorResponse{
Type: errType,
Message: stripExceptionPrefixes(err.Error()),
- })
+ }, jsonx.V1())
return c.JSONBlob(status, payload)
}
diff --git a/services/codepipeline/handler_test.go b/services/codepipeline/handler_test.go
index e40334a94e..8489b9042c 100644
--- a/services/codepipeline/handler_test.go
+++ b/services/codepipeline/handler_test.go
@@ -3,7 +3,7 @@ package codepipeline_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/codepipeline"
)
@@ -28,7 +29,7 @@ func doRequest(t *testing.T, h *codepipeline.Handler, action string, body any) *
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -174,7 +175,7 @@ func decodeBody(t *testing.T, data []byte) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(data, &m))
+ require.NoError(t, json.Unmarshal(data, &m, jsonx.V1()))
return m
}
@@ -343,7 +344,7 @@ func TestHandler_UnknownAction(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "InvalidActionException", out["__type"])
}
@@ -422,7 +423,7 @@ func TestHandler_ErrorEnvelopes(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out["__type"])
})
}
@@ -445,7 +446,7 @@ func TestErrValidationMapping(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "ValidationException", out["__type"])
}
diff --git a/services/codepipeline/jobs_test.go b/services/codepipeline/jobs_test.go
index ebb2142fc2..2e5918725c 100644
--- a/services/codepipeline/jobs_test.go
+++ b/services/codepipeline/jobs_test.go
@@ -2,13 +2,14 @@ package codepipeline_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codepipeline"
)
@@ -78,7 +79,7 @@ func TestHandler_AcknowledgeJob(t *testing.T) {
if !tt.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantStatus, out["status"])
}
})
@@ -133,7 +134,7 @@ func TestHandler_GetJobDetails(t *testing.T) {
if !tt.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
details, ok := out["jobDetails"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "jd-1", details["id"])
@@ -176,7 +177,7 @@ func TestGetJobDetails_DataPopulated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
details, ok := out["jobDetails"].(map[string]any)
require.True(t, ok, "jobDetails must be present")
@@ -284,7 +285,7 @@ func TestHandler_PollForJobs_Filter(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
jobs, _ := out["jobs"].([]any)
gotIDs := make([]string, len(jobs))
@@ -379,7 +380,7 @@ func TestHandler_PollForJobs_MaxBatchSize(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
jobs, _ := out["jobs"].([]any)
assert.Len(t, jobs, tt.wantCount)
diff --git a/services/codepipeline/persistence.go b/services/codepipeline/persistence.go
index 8d53cd24c1..54c504bf97 100644
--- a/services/codepipeline/persistence.go
+++ b/services/codepipeline/persistence.go
@@ -2,7 +2,7 @@ package codepipeline
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -96,14 +96,14 @@ type conditionRunEntry struct {
// incompatible (older or newer) build of this backend as though it were the
// current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Executions []executionEntry `json:"executions"`
- ActionExecutions []actionExecutionEntry `json:"actionExecutions"`
- ActionRevisions []actionRevisionEntry `json:"actionRevisions"`
- ConditionRuns []conditionRunEntry `json:"conditionRuns,omitempty"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Executions []executionEntry `json:"executions"`
+ ActionExecutions []actionExecutionEntry `json:"actionExecutions"`
+ ActionRevisions []actionRevisionEntry `json:"actionRevisions"`
+ ConditionRuns []conditionRunEntry `json:"conditionRuns,omitempty"`
+ Version int `json:"version"`
}
// customActionTypeKey.String returns a unique string for use as the DTO id
@@ -352,7 +352,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreResourceTables rebuilds the five resource store.Table values from
// snap's per-table JSON, factored out of Restore to keep Restore's own
// cognitive complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
dtoReg, pipelineDTOs, catDTOs, jobDTOs, webhookDTOs, transitionDTOs := buildPersistenceDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
diff --git a/services/codepipeline/pipeline_declaration_test.go b/services/codepipeline/pipeline_declaration_test.go
index e495533bbf..01ea61a3ca 100644
--- a/services/codepipeline/pipeline_declaration_test.go
+++ b/services/codepipeline/pipeline_declaration_test.go
@@ -1,13 +1,14 @@
package codepipeline_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codepipeline"
)
@@ -95,7 +96,7 @@ func TestHandler_PipelineDeclaration_V2Fields(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pl, _ := out["pipeline"].(map[string]any)
require.NotNil(t, pl)
@@ -219,7 +220,7 @@ func TestHandler_Action_ExtendedFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
pl := out["pipeline"].(map[string]any)
stages := pl["stages"].([]any)
diff --git a/services/codepipeline/pipeline_executions_test.go b/services/codepipeline/pipeline_executions_test.go
index 6443c16ef8..000994f6d9 100644
--- a/services/codepipeline/pipeline_executions_test.go
+++ b/services/codepipeline/pipeline_executions_test.go
@@ -2,7 +2,8 @@ package codepipeline_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codepipeline"
)
@@ -100,7 +102,7 @@ func TestHandler_PipelineExecution_TerminalStatus(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
execID, _ := startOut["pipelineExecutionId"].(string)
require.NotEmpty(t, execID)
@@ -108,7 +110,7 @@ func TestHandler_PipelineExecution_TerminalStatus(t *testing.T) {
require.Equal(t, tt.httpStatus, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tt.wantType != "" {
assert.Equal(t, tt.wantType, out["__type"])
@@ -247,7 +249,7 @@ func TestHandler_ListPipelineExecutions_StoresAndReturns(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries, _ := out["pipelineExecutionSummaries"].([]any)
assert.Len(t, summaries, tt.wantCount)
}
@@ -293,7 +295,7 @@ func TestListPipelineExecutions_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp listResp
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.LessOrEqual(t, len(resp.PipelineExecutionSummaries), 2, "page exceeds maxResults")
for _, s := range resp.PipelineExecutionSummaries {
@@ -392,7 +394,7 @@ func TestCPPagination_ListPipelineExecutions(t *testing.T) {
NextToken string `json:"nextToken"`
PipelineExecutionSummaries []map[string]any `json:"pipelineExecutionSummaries"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
total += len(out.PipelineExecutionSummaries)
pages++
@@ -432,7 +434,7 @@ func TestListPipelineExecutions_TriggerObject(t *testing.T) {
var out struct {
PipelineExecutionSummaries []map[string]any `json:"pipelineExecutionSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.PipelineExecutionSummaries, 2)
for _, exec := range out.PipelineExecutionSummaries {
@@ -598,7 +600,7 @@ func TestCPPagination_ListActionExecutions(t *testing.T) {
NextToken string `json:"nextToken"`
ActionExecutionDetails []map[string]any `json:"actionExecutionDetails"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
total += len(out.ActionExecutionDetails)
pages++
diff --git a/services/codepipeline/pipeline_state_test.go b/services/codepipeline/pipeline_state_test.go
index 1921f90070..15fcb2d71c 100644
--- a/services/codepipeline/pipeline_state_test.go
+++ b/services/codepipeline/pipeline_state_test.go
@@ -2,13 +2,14 @@ package codepipeline_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codepipeline"
)
@@ -321,7 +322,7 @@ func TestHandler_DisableStageTransition_StageValidation(t *testing.T) {
if tt.wantType != "" {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out["__type"])
}
})
@@ -524,7 +525,7 @@ func TestHandler_GetPipelineState_ActionStates(t *testing.T) {
if tt.checkFn != nil {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tt.checkFn(t, out)
}
})
@@ -611,7 +612,7 @@ func TestHandler_GetPipelineState_LatestExecution(t *testing.T) {
if tt.checkFn != nil {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tt.checkFn(t, out)
}
})
@@ -655,7 +656,7 @@ func TestGetPipelineState_PipelineVersion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.InDelta(t, tt.wantVersion, out["pipelineVersion"], 0, "pipelineVersion mismatch")
})
}
diff --git a/services/codepipeline/pipeline_state_wire_test.go b/services/codepipeline/pipeline_state_wire_test.go
index 8249eade4e..334510079c 100644
--- a/services/codepipeline/pipeline_state_wire_test.go
+++ b/services/codepipeline/pipeline_state_wire_test.go
@@ -1,7 +1,7 @@
package codepipeline_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codepipeline"
)
@@ -134,7 +135,7 @@ func TestGetPipelineState_OmitsOutboundTransitionState(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
stageStates := out["stageStates"].([]any)
require.Len(t, stageStates, 1)
diff --git a/services/codepipeline/pipelines_test.go b/services/codepipeline/pipelines_test.go
index 59c84f843d..021790e452 100644
--- a/services/codepipeline/pipelines_test.go
+++ b/services/codepipeline/pipelines_test.go
@@ -2,13 +2,14 @@ package codepipeline_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codepipeline"
)
@@ -72,7 +73,7 @@ func TestHandler_CreatePipeline(t *testing.T) {
if !tt.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pipeline, ok := out["pipeline"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantName, pipeline["name"])
@@ -131,8 +132,8 @@ func TestHandler_CreatePipeline_RoleArnRequired(t *testing.T) {
input := tt.pipeline
if input == nil {
// marshal samplePipeline to map
- b, _ := json.Marshal(samplePipeline("role-test"))
- require.NoError(t, json.Unmarshal(b, &input))
+ b, _ := json.Marshal(samplePipeline("role-test"), jsonx.V1())
+ require.NoError(t, json.Unmarshal(b, &input, jsonx.V1()))
}
rec := doRequest(t, h, "CreatePipeline", map[string]any{"pipeline": input})
@@ -140,7 +141,7 @@ func TestHandler_CreatePipeline_RoleArnRequired(t *testing.T) {
if tt.wantType != "" {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out["__type"])
}
})
@@ -177,7 +178,7 @@ func TestCreatePipeline_DuplicateName_PipelineNameInUseException(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec2.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &body, jsonx.V1()))
errType, ok := body["__type"].(string)
require.True(t, ok, "response must have __type field")
@@ -200,7 +201,7 @@ func TestCreatePipeline_DuplicateName_NotInvalidStructureException(t *testing.T)
require.Equal(t, http.StatusBadRequest, rec2.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &body, jsonx.V1()))
errType, _ := body["__type"].(string)
assert.NotEqual(t, "InvalidStructureException", errType,
@@ -329,7 +330,7 @@ func TestHandler_GetPipeline_VersionHandling(t *testing.T) {
if tt.wantType != "" {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out["__type"])
}
})
@@ -449,7 +450,7 @@ func TestHandler_UpdatePipeline_VersionIsIgnored(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pipeline, _ := out["pipeline"].(map[string]any)
assert.InDelta(
t,
@@ -585,7 +586,7 @@ func TestInMemoryBackend_DeletePipeline_ClearsActionExecutions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var before map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &before))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &before, jsonx.V1()))
beforeDetails, _ := before["actionExecutionDetails"].([]any)
require.NotEmpty(t, beforeDetails, "action executions must be recorded before delete")
@@ -600,7 +601,7 @@ func TestInMemoryBackend_DeletePipeline_ClearsActionExecutions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var after map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &after))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &after, jsonx.V1()))
afterDetails, _ := after["actionExecutionDetails"].([]any)
assert.Empty(
t,
@@ -683,7 +684,7 @@ func TestDeletePipeline_CascadeActionRevisions(t *testing.T) {
require.Equal(t, http.StatusOK, stateRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(stateRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(stateRec.Body.Bytes(), &out, jsonx.V1()))
stageStates, _ := out["stageStates"].([]any)
require.Len(t, stageStates, 1)
stage, _ := stageStates[0].(map[string]any)
@@ -738,7 +739,7 @@ func TestHandler_ListPipelines(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pipelines, _ := out["pipelines"].([]any)
assert.Len(t, pipelines, tt.wantCount)
})
@@ -783,7 +784,7 @@ func TestHandler_ListPipelines_IncludesPipelineType(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pipelines, _ := out["pipelines"].([]any)
require.Len(t, pipelines, 1)
@@ -812,7 +813,7 @@ func TestSortedListPipelines(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
list, ok := out["pipelines"].([]any)
require.True(t, ok)
@@ -836,7 +837,7 @@ func TestListPipelines_NonNilWhenEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// pipelines key must exist and be an array, not null.
list, ok := out["pipelines"]
@@ -861,7 +862,7 @@ func TestListPipelines_OmitsPipelineArn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
list := out["pipelines"].([]any)
item := list[0].(map[string]any)
@@ -871,7 +872,7 @@ func TestListPipelines_OmitsPipelineArn(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
metadata := getOut["metadata"].(map[string]any)
arn, ok := metadata["pipelineArn"].(string)
@@ -932,7 +933,7 @@ func TestListPipelines_Pagination(t *testing.T) {
NextToken string `json:"nextToken"`
Pipelines []map[string]any `json:"pipelines"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Pipelines, tt.wantLen)
if tt.wantNextToken {
assert.NotEmpty(t, out.NextToken)
@@ -976,7 +977,7 @@ func TestListPipelines_FullPagination(t *testing.T) {
NextToken string `json:"nextToken"`
Pipelines []map[string]any `json:"pipelines"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.LessOrEqual(t, len(out.Pipelines), 2)
for _, p := range out.Pipelines {
diff --git a/services/codepipeline/rules_test.go b/services/codepipeline/rules_test.go
index b32ae50801..4c963e138d 100644
--- a/services/codepipeline/rules_test.go
+++ b/services/codepipeline/rules_test.go
@@ -2,13 +2,15 @@ package codepipeline_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codepipeline"
)
@@ -110,7 +112,7 @@ func TestHandler_ListExecutionsAndRules(t *testing.T) {
ActionExecutionID string `json:"actionExecutionId"`
} `json:"actionExecutionDetails"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&actionExecs))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &actionExecs, jsonx.V1()))
require.NotEmpty(t, actionExecs.ActionExecutionDetails)
actionExecutionID := actionExecs.ActionExecutionDetails[0].ActionExecutionID
require.NotEmpty(t, actionExecutionID)
diff --git a/services/codepipeline/source_actions.go b/services/codepipeline/source_actions.go
index db50e1ea98..f9f3c9dfb9 100644
--- a/services/codepipeline/source_actions.go
+++ b/services/codepipeline/source_actions.go
@@ -2,11 +2,12 @@ package codepipeline
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"strconv"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -65,7 +66,7 @@ func (b *InMemoryBackend) runECRSourceAction(
RepositoryName: img.RepositoryName,
ImageURI: uri,
ImageTags: img.Tags,
- })
+ }, jsonx.V1())
if err == nil {
err = b.putOutputFiles(ctx, p, execID, action, map[string][]byte{imageDetailFile: detail})
}
diff --git a/services/codepipeline/tags_test.go b/services/codepipeline/tags_test.go
index 6a210bcdc2..f2f6298783 100644
--- a/services/codepipeline/tags_test.go
+++ b/services/codepipeline/tags_test.go
@@ -2,13 +2,14 @@ package codepipeline_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codepipeline"
)
@@ -173,7 +174,7 @@ func TestHandler_ListTagsForResource_WebhookARN(t *testing.T) {
if tt.wantType != "" {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out["__type"])
}
})
@@ -201,7 +202,7 @@ func TestSortedListTagsForResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tags, ok := out["tags"].([]any)
require.True(t, ok)
@@ -228,7 +229,7 @@ func TestListTagsForResource_EmptySlice(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tags := out["tags"]
assert.NotNil(t, tags)
@@ -280,7 +281,7 @@ func TestWebhookTagging(t *testing.T) {
// Get the webhook ARN.
var putOut map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut, jsonx.V1()))
webhookEntry := putOut["webhook"].(map[string]any)
arn := webhookEntry["arn"].(string)
require.NotEmpty(t, arn)
@@ -308,7 +309,7 @@ func TestWebhookTagging(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
rawTags, _ := listOut["tags"].([]any)
gotKeys := make([]string, 0, len(rawTags))
diff --git a/services/codepipeline/third_party_jobs_test.go b/services/codepipeline/third_party_jobs_test.go
index ae993d186d..da89b3e178 100644
--- a/services/codepipeline/third_party_jobs_test.go
+++ b/services/codepipeline/third_party_jobs_test.go
@@ -1,13 +1,14 @@
package codepipeline_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codepipeline"
)
@@ -67,7 +68,7 @@ func TestHandler_AcknowledgeThirdPartyJob(t *testing.T) {
if !tt.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantStatus, out["status"])
}
})
@@ -166,7 +167,7 @@ func TestHandler_PollForThirdPartyJobs_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
jobs, _ := out["jobs"].([]any)
require.Len(t, jobs, 1)
@@ -218,7 +219,7 @@ func TestGetThirdPartyJobDetails_DataPopulated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
details, ok := out["jobDetails"].(map[string]any)
require.True(t, ok, "jobDetails must be present")
diff --git a/services/codepipeline/webhooks_test.go b/services/codepipeline/webhooks_test.go
index 9ed32cbf20..95308746fe 100644
--- a/services/codepipeline/webhooks_test.go
+++ b/services/codepipeline/webhooks_test.go
@@ -1,7 +1,8 @@
package codepipeline_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codepipeline"
)
@@ -107,7 +109,7 @@ func TestHandler_DeregisterWebhookWithThirdParty(t *testing.T) {
if tt.httpStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out)
}
})
@@ -248,7 +250,7 @@ func TestHandler_Webhook_FullModel(t *testing.T) {
if tt.checkFn != nil && tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tt.checkFn(t, out)
}
})
@@ -329,7 +331,7 @@ func TestHandler_ListWebhooks_DefinitionWrapper(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
webhooks, _ := out["webhooks"].([]any)
assert.Len(t, webhooks, tt.wantCount)
@@ -499,7 +501,7 @@ func TestCPPagination_ListWebhooks(t *testing.T) {
NextToken string `json:"NextToken"`
Webhooks []map[string]any `json:"webhooks"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
total += len(out.Webhooks)
pages++
@@ -561,7 +563,7 @@ func TestPutWebhook_Filters_EmptyNotAbsent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
webhook, ok := raw["webhook"].(map[string]any)
require.True(t, ok, "webhook must be an object")
@@ -604,7 +606,7 @@ func TestListWebhooks_Filters_EmptyNotAbsent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
webhooks, ok := raw["webhooks"].([]any)
require.True(t, ok, "webhooks must be an array")
@@ -651,7 +653,7 @@ func TestListWebhooks_Filters_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
webhooks, ok := raw["webhooks"].([]any)
require.True(t, ok)
diff --git a/services/codestarconnections/handler.go b/services/codestarconnections/handler.go
index 7e6c704228..ac1cb18e65 100644
--- a/services/codestarconnections/handler.go
+++ b/services/codestarconnections/handler.go
@@ -2,7 +2,7 @@ package codestarconnections
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -183,7 +184,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
ResourceName string `json:"ResourceName"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
switch {
case req.ConnectionArn != "":
@@ -229,15 +230,12 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
const codestarContentType = "application/x-amz-json-1.0"
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
var errType string
var statusCode int
@@ -256,7 +254,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
errType, statusCode = "LimitExceededException", http.StatusBadRequest
case errors.Is(err, ErrValidation),
errors.Is(err, errInvalidRequest), errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
errType, statusCode = errTypeInvalidInput, http.StatusBadRequest
default:
errType, statusCode = "InternalFailure", http.StatusInternalServerError
@@ -265,7 +263,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
payload, marshalErr := json.Marshal(service.JSONErrorResponse{
Type: errType,
Message: wireMessage(err, errType),
- })
+ }, jsonx.V1())
if marshalErr != nil {
return c.String(http.StatusInternalServerError, "internal server error")
}
diff --git a/services/codestarconnections/handler_connections_test.go b/services/codestarconnections/handler_connections_test.go
index 6aff1d4816..0e0c485444 100644
--- a/services/codestarconnections/handler_connections_test.go
+++ b/services/codestarconnections/handler_connections_test.go
@@ -2,13 +2,14 @@ package codestarconnections_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codestarconnections"
)
@@ -50,7 +51,7 @@ func TestHandler_CreateConnection(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out[tt.wantKey])
})
}
@@ -119,7 +120,7 @@ func TestHandler_GetConnection(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
conn, ok := out["Connection"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "test-conn", conn["ConnectionName"])
@@ -209,7 +210,7 @@ func TestHandler_ListConnections(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
conns, ok := out["Connections"].([]any)
require.True(t, ok)
assert.Len(t, conns, tt.wantCount)
@@ -339,7 +340,7 @@ func TestSortedListConnections(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
conns := out["Connections"].([]any)
require.Len(t, conns, 3)
@@ -372,7 +373,7 @@ func TestGetConnectionIncludesTags(t *testing.T) {
require.Equal(t, http.StatusOK, recGet.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out, jsonx.V1()))
conn := out["Connection"].(map[string]any)
_, hasTags := conn["Tags"]
assert.False(t, hasTags, "GetConnection should not include Tags in response")
@@ -381,7 +382,7 @@ func TestGetConnectionIncludesTags(t *testing.T) {
require.Equal(t, http.StatusOK, recTags.Code)
var tagsOut map[string]any
- require.NoError(t, json.Unmarshal(recTags.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(recTags.Body.Bytes(), &tagsOut, jsonx.V1()))
tags := tagsOut["Tags"].([]any)
require.Len(t, tags, 1)
assert.Equal(t, "env", tags[0].(map[string]any)["Key"].(string))
diff --git a/services/codestarconnections/handler_hosts_test.go b/services/codestarconnections/handler_hosts_test.go
index 09decc3dc1..8a6670255e 100644
--- a/services/codestarconnections/handler_hosts_test.go
+++ b/services/codestarconnections/handler_hosts_test.go
@@ -2,13 +2,14 @@ package codestarconnections_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codestarconnections"
)
@@ -52,7 +53,7 @@ func TestHandler_CreateHost(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["HostArn"])
})
}
@@ -132,7 +133,7 @@ func TestHandler_GetHost(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "test-host", out["Name"])
assert.Equal(t, "GitHubEnterpriseServer", out["ProviderType"])
assert.Equal(t, "https://example.com", out["ProviderEndpoint"])
@@ -152,7 +153,7 @@ func TestHandler_ListHosts(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
hosts, ok := out["Hosts"].([]any)
require.True(t, ok)
assert.Len(t, hosts, 2)
@@ -346,7 +347,7 @@ func TestSortedListHosts(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
hosts := out["Hosts"].([]any)
require.Len(t, hosts, 3)
@@ -380,7 +381,7 @@ func TestGetHostIncludesTags(t *testing.T) {
require.Equal(t, http.StatusOK, recGet.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &out, jsonx.V1()))
_, hasTags := out["Tags"]
assert.False(t, hasTags, "GetHost should not include Tags in response")
@@ -388,7 +389,7 @@ func TestGetHostIncludesTags(t *testing.T) {
require.Equal(t, http.StatusOK, recTags.Code)
var tagsOut map[string]any
- require.NoError(t, json.Unmarshal(recTags.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(recTags.Body.Bytes(), &tagsOut, jsonx.V1()))
tags := tagsOut["Tags"].([]any)
require.Len(t, tags, 1)
assert.Equal(t, "platform", tags[0].(map[string]any)["Value"].(string))
diff --git a/services/codestarconnections/handler_repository_links_test.go b/services/codestarconnections/handler_repository_links_test.go
index b8d700cd4b..c897b743d7 100644
--- a/services/codestarconnections/handler_repository_links_test.go
+++ b/services/codestarconnections/handler_repository_links_test.go
@@ -2,7 +2,7 @@ package codestarconnections_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codestarconnections"
)
@@ -69,7 +70,7 @@ func TestHandler_CreateRepositoryLink(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
info, ok := out["RepositoryLinkInfo"].(map[string]any)
require.True(t, ok)
assert.NotEmpty(t, info["RepositoryLinkId"])
@@ -138,7 +139,7 @@ func TestHandler_GetRepositoryLink(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
info, ok := out["RepositoryLinkInfo"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "my-owner", info["OwnerId"])
@@ -226,7 +227,7 @@ func TestHandler_ListRepositoryLinks(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
links, ok := out["RepositoryLinks"].([]any)
require.True(t, ok)
assert.Len(t, links, 2)
diff --git a/services/codestarconnections/handler_sync_blockers_test.go b/services/codestarconnections/handler_sync_blockers_test.go
index dfa7b2540d..c10c9f0230 100644
--- a/services/codestarconnections/handler_sync_blockers_test.go
+++ b/services/codestarconnections/handler_sync_blockers_test.go
@@ -2,13 +2,14 @@ package codestarconnections_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codestarconnections"
)
@@ -71,7 +72,7 @@ func TestHandler_GetSyncBlockerSummary(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summary, ok := out["SyncBlockerSummary"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "blocker-resource", summary["ResourceName"])
diff --git a/services/codestarconnections/handler_sync_configurations_test.go b/services/codestarconnections/handler_sync_configurations_test.go
index 0ffea88f89..dcd64e375e 100644
--- a/services/codestarconnections/handler_sync_configurations_test.go
+++ b/services/codestarconnections/handler_sync_configurations_test.go
@@ -2,13 +2,14 @@ package codestarconnections_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codestarconnections"
)
@@ -84,7 +85,7 @@ func TestHandler_CreateSyncConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
cfg, ok := out["SyncConfiguration"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "main", cfg["Branch"])
@@ -147,7 +148,7 @@ func TestHandler_GetSyncConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
cfg, ok := out["SyncConfiguration"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "my-stack", cfg["ResourceName"])
@@ -225,7 +226,7 @@ func TestHandler_RepositoryLink_SyncConfiguration_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, recLink.Code)
var linkOut map[string]any
- require.NoError(t, json.Unmarshal(recLink.Body.Bytes(), &linkOut))
+ require.NoError(t, json.Unmarshal(recLink.Body.Bytes(), &linkOut, jsonx.V1()))
linkInfo := linkOut["RepositoryLinkInfo"].(map[string]any)
linkID := linkInfo["RepositoryLinkId"].(string)
require.NotEmpty(t, linkID)
@@ -242,7 +243,7 @@ func TestHandler_RepositoryLink_SyncConfiguration_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, recSync.Code)
var syncOut map[string]any
- require.NoError(t, json.Unmarshal(recSync.Body.Bytes(), &syncOut))
+ require.NoError(t, json.Unmarshal(recSync.Body.Bytes(), &syncOut, jsonx.V1()))
syncCfg := syncOut["SyncConfiguration"].(map[string]any)
assert.Equal(t, "my-repo", syncCfg["RepositoryName"])
assert.Equal(t, "GitHub", syncCfg["ProviderType"])
@@ -267,7 +268,7 @@ func TestHandler_RepositoryLink_SyncConfiguration_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
links, ok := listOut["RepositoryLinks"].([]any)
require.True(t, ok)
assert.Len(t, links, 1)
diff --git a/services/codestarconnections/handler_sync_statuses_test.go b/services/codestarconnections/handler_sync_statuses_test.go
index bd72b5dfe7..7fe4f1e04b 100644
--- a/services/codestarconnections/handler_sync_statuses_test.go
+++ b/services/codestarconnections/handler_sync_statuses_test.go
@@ -2,13 +2,14 @@ package codestarconnections_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codestarconnections"
)
@@ -74,7 +75,7 @@ func TestHandler_GetRepositorySyncStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
latest, ok := out["LatestSync"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "SUCCEEDED", latest["Status"])
@@ -136,7 +137,7 @@ func TestHandler_GetResourceSyncStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
latest, ok := out["LatestSync"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "SUCCEEDED", latest["Status"])
diff --git a/services/codestarconnections/handler_tags_test.go b/services/codestarconnections/handler_tags_test.go
index ea5663db45..6cd379bcb9 100644
--- a/services/codestarconnections/handler_tags_test.go
+++ b/services/codestarconnections/handler_tags_test.go
@@ -2,13 +2,14 @@ package codestarconnections_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/codestarconnections"
)
@@ -31,7 +32,7 @@ func TestHandler_TagResource_Connection(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
tags, ok := out["Tags"].([]any)
require.True(t, ok)
require.Len(t, tags, 1)
@@ -93,7 +94,7 @@ func TestHandler_ListTagsForResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tags, ok := out["Tags"].([]any)
require.True(t, ok)
require.Len(t, tags, 1)
@@ -125,7 +126,7 @@ func TestSortedTags(t *testing.T) {
require.Equal(t, http.StatusOK, recTags.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recTags.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recTags.Body.Bytes(), &out, jsonx.V1()))
tags := out["Tags"].([]any)
keys := make([]string, len(tags))
@@ -178,7 +179,7 @@ func TestTagResourceOnHost(t *testing.T) {
require.Equal(t, http.StatusOK, recList.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &out, jsonx.V1()))
tags := out["Tags"].([]any)
require.Len(t, tags, 1)
assert.Equal(t, "ci", tags[0].(map[string]any)["Value"].(string))
@@ -211,7 +212,7 @@ func TestUntagResourceRemovesKeys(t *testing.T) {
require.Equal(t, http.StatusOK, recList.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &out, jsonx.V1()))
tags := out["Tags"].([]any)
require.Len(t, tags, 1)
assert.Equal(t, "keep", tags[0].(map[string]any)["Key"].(string))
diff --git a/services/codestarconnections/handler_test.go b/services/codestarconnections/handler_test.go
index a79bfaec9c..d2e7a9ee36 100644
--- a/services/codestarconnections/handler_test.go
+++ b/services/codestarconnections/handler_test.go
@@ -3,7 +3,7 @@ package codestarconnections_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/codestarconnections"
)
@@ -28,7 +29,7 @@ func doRequest(t *testing.T, h *codestarconnections.Handler, action string, body
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -52,7 +53,7 @@ func parseResp(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
@@ -527,7 +528,7 @@ func TestErrValidationMapping(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidInputException", resp["__type"])
}
@@ -670,7 +671,7 @@ func TestExtractResource(t *testing.T) {
h := newTestHandler(t)
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
diff --git a/services/codestarconnections/persistence.go b/services/codestarconnections/persistence.go
index cd78ca1a20..b224bb20c7 100644
--- a/services/codestarconnections/persistence.go
+++ b/services/codestarconnections/persistence.go
@@ -2,7 +2,7 @@ package codestarconnections
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -87,10 +87,10 @@ func resourceSyncStatusDTOKeyFn(d *resourceSyncStatusDTO) string {
// an incompatible (older or newer) build of this backend as though it were
// the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// persistenceDTOTables groups every ephemeral DTO table Snapshot/Restore
@@ -246,7 +246,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// syncBlockers; see store_setup.go's registerAllTables doc) from tables via
// the ephemeral DTO registry, factored out of Restore to keep Restore's own
// cognitive complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreDirtyTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTables(tables map[string]jsontext.Value) error {
dtos := buildPersistenceDTORegistry()
if err := dtos.registry.RestoreAll(tables); err != nil {
return fmt.Errorf("codestarconnections: restore snapshot DTO tables: %w", err)
diff --git a/services/cognitoidentity/handler.go b/services/cognitoidentity/handler.go
index b3c47be5d5..1a1d476a81 100644
--- a/services/cognitoidentity/handler.go
+++ b/services/cognitoidentity/handler.go
@@ -3,7 +3,7 @@ package cognitoidentity
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -119,7 +120,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
IdentityID string `json:"IdentityId"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if req.IdentityPoolID != "" {
return req.IdentityPoolID
@@ -186,7 +187,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
@@ -252,11 +253,11 @@ func resolveErrorType(err error) (string, int) {
}
}
- if _, ok := errors.AsType[*json.SyntaxError](err); ok {
+ if _, ok := jsonx.SyntaxError(err); ok {
return ErrInvalidParameter.Error(), http.StatusBadRequest
}
- if _, ok := errors.AsType[*json.UnmarshalTypeError](err); ok {
+ if _, ok := jsonx.TypeError(err); ok {
return ErrInvalidParameter.Error(), http.StatusBadRequest
}
diff --git a/services/cognitoidentity/handler_credentials_test.go b/services/cognitoidentity/handler_credentials_test.go
index 4afb195016..62d5bc9e2f 100644
--- a/services/cognitoidentity/handler_credentials_test.go
+++ b/services/cognitoidentity/handler_credentials_test.go
@@ -1,13 +1,15 @@
package cognitoidentity_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// aws-sdk-go-v2's cognitoidentity deserializer parses Credentials.Expiration with
@@ -27,7 +29,7 @@ func TestGetCredentialsForIdentity_ExpirationIsEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
rolesRec := doCognitoIdentityRequest(t, h, "SetIdentityPoolRoles", map[string]any{
"IdentityPoolId": created["IdentityPoolId"],
@@ -44,7 +46,7 @@ func TestGetCredentialsForIdentity_ExpirationIsEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, idRec.Code)
var idOut map[string]any
- require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut))
+ require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut, jsonx.V1()))
rec := doCognitoIdentityRequest(t, h, "GetCredentialsForIdentity", map[string]any{
"IdentityId": idOut["IdentityId"],
@@ -52,7 +54,7 @@ func TestGetCredentialsForIdentity_ExpirationIsEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
creds := out["Credentials"].(map[string]any)
expiration, ok := creds["Expiration"].(float64)
@@ -118,7 +120,7 @@ func TestGetCredentialsForIdentity_CustomRoleArn(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
rolesRec := doCognitoIdentityRequest(t, h, "SetIdentityPoolRoles", map[string]any{
"IdentityPoolId": created["IdentityPoolId"],
@@ -135,7 +137,7 @@ func TestGetCredentialsForIdentity_CustomRoleArn(t *testing.T) {
require.Equal(t, http.StatusOK, idRec.Code)
var idOut map[string]any
- require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut))
+ require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut, jsonx.V1()))
req := map[string]any{"IdentityId": idOut["IdentityId"]}
if tt.customRoleArn != "" {
@@ -166,7 +168,7 @@ func TestHandler_GetCredentialsForIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
rolesRec := doCognitoIdentityRequest(t, h, "SetIdentityPoolRoles", map[string]any{
"IdentityPoolId": created["IdentityPoolId"],
@@ -183,7 +185,7 @@ func TestHandler_GetCredentialsForIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, idRec.Code)
var idOut map[string]any
- require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut))
+ require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut, jsonx.V1()))
rec := doCognitoIdentityRequest(t, h, "GetCredentialsForIdentity", map[string]any{
"IdentityId": idOut["IdentityId"],
@@ -191,7 +193,7 @@ func TestHandler_GetCredentialsForIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["IdentityId"])
creds, ok := out["Credentials"].(map[string]any)
@@ -213,7 +215,7 @@ func TestHandler_GetOpenIDToken(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
idRec := doCognitoIdentityRequest(t, h, "GetId", map[string]any{
"AccountId": "000000000000",
@@ -222,7 +224,7 @@ func TestHandler_GetOpenIDToken(t *testing.T) {
require.Equal(t, http.StatusOK, idRec.Code)
var idOut map[string]any
- require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut))
+ require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut, jsonx.V1()))
rec := doCognitoIdentityRequest(t, h, "GetOpenIdToken", map[string]any{
"IdentityId": idOut["IdentityId"],
@@ -230,7 +232,7 @@ func TestHandler_GetOpenIDToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["Token"])
assert.Equal(t, idOut["IdentityId"], out["IdentityId"])
}
@@ -294,7 +296,7 @@ func TestHandler_GetOpenIdTokenForDeveloperIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
body = map[string]any{
"IdentityPoolId": created["IdentityPoolId"],
@@ -310,7 +312,7 @@ func TestHandler_GetOpenIdTokenForDeveloperIdentity(t *testing.T) {
if tt.wantCode == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["IdentityId"])
assert.NotEmpty(t, out["Token"])
}
@@ -331,7 +333,7 @@ func TestHandler_GetOpenIdTokenForDeveloperIdentity_ExistingIdentity(t *testing.
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
// First call creates a new identity.
@@ -344,7 +346,7 @@ func TestHandler_GetOpenIdTokenForDeveloperIdentity_ExistingIdentity(t *testing.
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
identityID1 := out1["IdentityId"].(string)
// Second call with same logins should re-use the same identity.
@@ -357,7 +359,7 @@ func TestHandler_GetOpenIdTokenForDeveloperIdentity_ExistingIdentity(t *testing.
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
identityID2 := out2["IdentityId"].(string)
assert.Equal(t, identityID1, identityID2)
@@ -375,7 +377,7 @@ func TestHandler_GetOpenIdTokenForDeveloperIdentity_InvalidDuration(t *testing.T
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
rec := doCognitoIdentityRequest(t, h, "GetOpenIdTokenForDeveloperIdentity", map[string]any{
"IdentityPoolId": created["IdentityPoolId"],
diff --git a/services/cognitoidentity/handler_developer_identities_test.go b/services/cognitoidentity/handler_developer_identities_test.go
index bdb1316853..d21aaab442 100644
--- a/services/cognitoidentity/handler_developer_identities_test.go
+++ b/services/cognitoidentity/handler_developer_identities_test.go
@@ -1,12 +1,14 @@
package cognitoidentity_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_LookupDeveloperIdentity(t *testing.T) {
@@ -52,7 +54,7 @@ func TestHandler_LookupDeveloperIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID = created["IdentityPoolId"].(string)
devRec := doCognitoIdentityRequest(t, h, "GetOpenIdTokenForDeveloperIdentity", map[string]any{
@@ -64,7 +66,7 @@ func TestHandler_LookupDeveloperIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, devRec.Code)
var devOut map[string]any
- require.NoError(t, json.Unmarshal(devRec.Body.Bytes(), &devOut))
+ require.NoError(t, json.Unmarshal(devRec.Body.Bytes(), &devOut, jsonx.V1()))
identityID = devOut["IdentityId"].(string)
}
@@ -89,7 +91,7 @@ func TestHandler_LookupDeveloperIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
body = map[string]any{
"IdentityPoolId": created["IdentityPoolId"],
@@ -101,7 +103,7 @@ func TestHandler_LookupDeveloperIdentity(t *testing.T) {
if tt.wantCode == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["IdentityId"])
}
})
@@ -145,7 +147,7 @@ func TestHandler_MergeDeveloperIdentities(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
for _, userID := range []string{"user-src", "user-dst"} {
@@ -171,7 +173,7 @@ func TestHandler_MergeDeveloperIdentities(t *testing.T) {
if tt.wantCode == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["IdentityId"])
}
})
@@ -226,7 +228,7 @@ func TestHandler_UnlinkIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
getIDRec := doCognitoIdentityRequest(t, h, "GetId", map[string]any{
@@ -239,7 +241,7 @@ func TestHandler_UnlinkIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, getIDRec.Code)
var getIDOut map[string]any
- require.NoError(t, json.Unmarshal(getIDRec.Body.Bytes(), &getIDOut))
+ require.NoError(t, json.Unmarshal(getIDRec.Body.Bytes(), &getIDOut, jsonx.V1()))
identityID = getIDOut["IdentityId"].(string)
body = map[string]any{
@@ -261,7 +263,7 @@ func TestHandler_UnlinkIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
logins, ok := descOut["Logins"].([]any)
require.True(t, ok)
assert.Equal(t, []any{"graph.facebook.com"}, logins)
@@ -308,7 +310,7 @@ func TestHandler_UnlinkDeveloperIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID = created["IdentityPoolId"].(string)
tokenRec := doCognitoIdentityRequest(t, h, "GetOpenIdTokenForDeveloperIdentity", map[string]any{
@@ -320,7 +322,7 @@ func TestHandler_UnlinkDeveloperIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, tokenRec.Code)
var tokenOut map[string]any
- require.NoError(t, json.Unmarshal(tokenRec.Body.Bytes(), &tokenOut))
+ require.NoError(t, json.Unmarshal(tokenRec.Body.Bytes(), &tokenOut, jsonx.V1()))
identityID = tokenOut["IdentityId"].(string)
body = map[string]any{
@@ -343,7 +345,7 @@ func TestHandler_UnlinkDeveloperIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, lookupRec.Code)
var lookupOut map[string]any
- require.NoError(t, json.Unmarshal(lookupRec.Body.Bytes(), &lookupOut))
+ require.NoError(t, json.Unmarshal(lookupRec.Body.Bytes(), &lookupOut, jsonx.V1()))
ids, ok := lookupOut["DeveloperUserIdentifierList"].([]any)
require.True(t, ok)
assert.Empty(t, ids)
@@ -364,7 +366,7 @@ func TestHandler_UnlinkIdentity_TokenMismatch_Returns403(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
idRec := doCognitoIdentityRequest(t, h, "GetId", map[string]any{
"AccountId": "000000000000",
@@ -376,7 +378,7 @@ func TestHandler_UnlinkIdentity_TokenMismatch_Returns403(t *testing.T) {
require.Equal(t, http.StatusOK, idRec.Code)
var idOut map[string]any
- require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut))
+ require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut, jsonx.V1()))
rec := doCognitoIdentityRequest(t, h, "UnlinkIdentity", map[string]any{
"IdentityId": idOut["IdentityId"],
@@ -402,7 +404,7 @@ func TestHandler_UnlinkDeveloperIdentity_WrongUser_Returns403(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
devRec := doCognitoIdentityRequest(t, h, "GetOpenIdTokenForDeveloperIdentity", map[string]any{
@@ -414,7 +416,7 @@ func TestHandler_UnlinkDeveloperIdentity_WrongUser_Returns403(t *testing.T) {
require.Equal(t, http.StatusOK, devRec.Code)
var devOut map[string]any
- require.NoError(t, json.Unmarshal(devRec.Body.Bytes(), &devOut))
+ require.NoError(t, json.Unmarshal(devRec.Body.Bytes(), &devOut, jsonx.V1()))
rec := doCognitoIdentityRequest(t, h, "UnlinkDeveloperIdentity", map[string]any{
"IdentityId": devOut["IdentityId"],
diff --git a/services/cognitoidentity/handler_identities_test.go b/services/cognitoidentity/handler_identities_test.go
index 24ee102ae1..d8d8bb5200 100644
--- a/services/cognitoidentity/handler_identities_test.go
+++ b/services/cognitoidentity/handler_identities_test.go
@@ -1,13 +1,16 @@
package cognitoidentity_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_GetID_UnauthDisabled_EmptyLogins(t *testing.T) {
@@ -22,7 +25,7 @@ func TestHandler_GetID_UnauthDisabled_EmptyLogins(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
rec := doCognitoIdentityRequest(t, h, "GetId", map[string]any{
"AccountId": "000000000000",
@@ -43,7 +46,7 @@ func TestHandler_ListIdentities_MaxResultsZero(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
rec := doCognitoIdentityRequest(t, h, "ListIdentities", map[string]any{
"IdentityPoolId": created["IdentityPoolId"],
@@ -64,7 +67,7 @@ func TestHandler_GetID(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
rec := doCognitoIdentityRequest(t, h, "GetId", map[string]any{
"AccountId": "000000000000",
@@ -74,7 +77,7 @@ func TestHandler_GetID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["IdentityId"])
}
@@ -139,7 +142,7 @@ func TestHandler_DeleteIdentities(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
idRec := doCognitoIdentityRequest(t, h, "GetId", map[string]any{
"AccountId": "000000000000",
@@ -148,7 +151,7 @@ func TestHandler_DeleteIdentities(t *testing.T) {
require.Equal(t, http.StatusOK, idRec.Code)
var idOut map[string]any
- require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut))
+ require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut, jsonx.V1()))
body = map[string]any{
"IdentityIdsToDelete": []string{idOut["IdentityId"].(string)},
@@ -160,7 +163,7 @@ func TestHandler_DeleteIdentities(t *testing.T) {
if tt.wantCode == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
unprocessed, _ := out["UnprocessedIdentityIds"].([]any)
assert.Len(t, unprocessed, tt.wantLen)
}
@@ -195,7 +198,7 @@ func TestHandler_DescribeIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
idRec := doCognitoIdentityRequest(t, h, "GetId", map[string]any{
"AccountId": "000000000000",
@@ -207,7 +210,7 @@ func TestHandler_DescribeIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, idRec.Code)
var idOut map[string]any
- require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut))
+ require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut, jsonx.V1()))
identityID = idOut["IdentityId"].(string)
}
@@ -219,7 +222,7 @@ func TestHandler_DescribeIdentity(t *testing.T) {
if tt.wantCode == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, identityID, out["IdentityId"])
assert.NotEmpty(t, out["CreationDate"])
logins, _ := out["Logins"].([]any)
@@ -257,7 +260,7 @@ func TestHandler_ListIdentities(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID = created["IdentityPoolId"].(string)
for _, login := range []map[string]string{
@@ -282,7 +285,7 @@ func TestHandler_ListIdentities(t *testing.T) {
if tt.wantCode == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, poolID, out["IdentityPoolId"])
identities, _ := out["Identities"].([]any)
@@ -304,7 +307,7 @@ func TestSortedListIdentities(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
for _, login := range []map[string]string{
@@ -327,7 +330,7 @@ func TestSortedListIdentities(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
identities, _ := listOut["Identities"].([]any)
require.Len(t, identities, 3)
@@ -389,7 +392,7 @@ func TestListIdentities_MaxResultsValidation(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
rec := doCognitoIdentityRequest(t, h, "ListIdentities", map[string]any{
"IdentityPoolId": created["IdentityPoolId"],
@@ -425,7 +428,7 @@ func TestSortedLogins(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
idRec := doCognitoIdentityRequest(t, h, "GetId", map[string]any{
@@ -440,7 +443,7 @@ func TestSortedLogins(t *testing.T) {
require.Equal(t, http.StatusOK, idRec.Code)
var idOut map[string]any
- require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut))
+ require.NoError(t, json.Unmarshal(idRec.Body.Bytes(), &idOut, jsonx.V1()))
identityID := idOut["IdentityId"].(string)
rec := doCognitoIdentityRequest(t, h, "DescribeIdentity", map[string]any{
@@ -449,7 +452,7 @@ func TestSortedLogins(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut, jsonx.V1()))
logins, _ := descOut["Logins"].([]any)
require.Len(t, logins, 3)
@@ -498,7 +501,7 @@ func TestHandler_GetID_MergesLoginProviders(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
poolID := createOut["IdentityPoolId"].(string)
// First call: only google.
@@ -509,7 +512,7 @@ func TestHandler_GetID_MergesLoginProviders(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.NewDecoder(rec1.Body).Decode(&out1))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec1.Body), &out1, jsonx.V1()))
id1 := out1["IdentityId"].(string)
// Second call: same google + new facebook → must return same ID.
@@ -523,7 +526,7 @@ func TestHandler_GetID_MergesLoginProviders(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&out2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &out2, jsonx.V1()))
id2 := out2["IdentityId"].(string)
assert.Equal(t, id1, id2, "GetId must return the same identity when any login provider matches")
@@ -541,7 +544,7 @@ func TestHandler_ListIdentities_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
poolID := createOut["IdentityPoolId"].(string)
for i := range 3 {
@@ -559,7 +562,7 @@ func TestHandler_ListIdentities_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.NewDecoder(rec1.Body).Decode(&out1))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec1.Body), &out1, jsonx.V1()))
assert.Len(t, out1["Identities"], 2)
nextToken, _ := out1["NextToken"].(string)
assert.NotEmpty(t, nextToken)
@@ -573,6 +576,6 @@ func TestHandler_ListIdentities_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&out2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &out2, jsonx.V1()))
assert.Len(t, out2["Identities"], 1)
}
diff --git a/services/cognitoidentity/handler_identity_pool_roles_test.go b/services/cognitoidentity/handler_identity_pool_roles_test.go
index ffc2fb37b5..602601f6e8 100644
--- a/services/cognitoidentity/handler_identity_pool_roles_test.go
+++ b/services/cognitoidentity/handler_identity_pool_roles_test.go
@@ -1,12 +1,15 @@
package cognitoidentity_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_SetGetIdentityPoolRoles_WithRoleMappings(t *testing.T) {
@@ -21,7 +24,7 @@ func TestHandler_SetGetIdentityPoolRoles_WithRoleMappings(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
setRec := doCognitoIdentityRequest(t, h, "SetIdentityPoolRoles", map[string]any{
@@ -45,7 +48,7 @@ func TestHandler_SetGetIdentityPoolRoles_WithRoleMappings(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
roles, ok := out["Roles"].(map[string]any)
require.True(t, ok)
@@ -73,7 +76,7 @@ func TestHandler_SetGetIdentityPoolRoles(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
setRec := doCognitoIdentityRequest(t, h, "SetIdentityPoolRoles", map[string]any{
"IdentityPoolId": created["IdentityPoolId"],
@@ -90,7 +93,7 @@ func TestHandler_SetGetIdentityPoolRoles(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
roles, ok := out["Roles"].(map[string]any)
require.True(t, ok)
@@ -186,7 +189,7 @@ func TestHandler_GetPrincipalTagAttributeMap(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID = created["IdentityPoolId"].(string)
}
@@ -199,7 +202,7 @@ func TestHandler_GetPrincipalTagAttributeMap(t *testing.T) {
if tt.wantCode == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantDefaults, out["UseDefaults"])
}
})
@@ -244,7 +247,7 @@ func TestHandler_SetPrincipalTagAttributeMap(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
body = map[string]any{
@@ -262,7 +265,7 @@ func TestHandler_SetPrincipalTagAttributeMap(t *testing.T) {
if tt.wantCode == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, false, out["UseDefaults"])
tags, _ := out["PrincipalTags"].(map[string]any)
@@ -284,7 +287,7 @@ func TestHandler_SetGetPrincipalTagAttributeMap_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
providerName := "cognito-idp.us-east-1.amazonaws.com/us-east-1_Pool1"
@@ -307,7 +310,7 @@ func TestHandler_SetGetPrincipalTagAttributeMap_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, poolID, out["IdentityPoolId"])
assert.Equal(t, providerName, out["IdentityProviderName"])
assert.Equal(t, false, out["UseDefaults"])
@@ -329,7 +332,7 @@ func TestHandler_SetIdentityPoolRoles_SingleRole(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
rec := doCognitoIdentityRequest(t, h, "SetIdentityPoolRoles", map[string]any{
"IdentityPoolId": created["IdentityPoolId"],
@@ -352,7 +355,7 @@ func TestHandler_GetPrincipalTagAttributeMap_EmptyProvider_Returns400(t *testing
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
rec := doCognitoIdentityRequest(t, h, "GetPrincipalTagAttributeMap", map[string]any{
"IdentityPoolId": created["IdentityPoolId"],
@@ -384,7 +387,7 @@ func TestHandler_GetIdentityPoolRoles_OmitsEmptyRoles(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
poolID := createOut["IdentityPoolId"].(string)
setRec := doCognitoIdentityRequest(t, h, "SetIdentityPoolRoles", map[string]any{
@@ -401,7 +404,7 @@ func TestHandler_GetIdentityPoolRoles_OmitsEmptyRoles(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &out, jsonx.V1()))
roles := out["Roles"].(map[string]any)
assert.Contains(t, roles, "authenticated")
@@ -420,7 +423,7 @@ func TestHandler_SetIdentityPoolRoles_PreservesExistingRole(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
poolID := createOut["IdentityPoolId"].(string)
// Set both roles.
@@ -446,7 +449,7 @@ func TestHandler_SetIdentityPoolRoles_PreservesExistingRole(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &out, jsonx.V1()))
roles := out["Roles"].(map[string]any)
assert.Equal(t, "arn:aws:iam::000000000000:role/AuthV2", roles["authenticated"])
diff --git a/services/cognitoidentity/handler_identity_pools_test.go b/services/cognitoidentity/handler_identity_pools_test.go
index c2e85bc350..b88d6e73b1 100644
--- a/services/cognitoidentity/handler_identity_pools_test.go
+++ b/services/cognitoidentity/handler_identity_pools_test.go
@@ -1,12 +1,15 @@
package cognitoidentity_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCreateIdentityPool_OIDCProviderARNs(t *testing.T) {
@@ -54,12 +57,12 @@ func TestCreateIdentityPool_OIDCProviderARNs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "create: %s", rec.Body.String())
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
// Verify ARNs in CreateIdentityPool response.
- raw, _ := json.Marshal(created["OpenIdConnectProviderARNs"])
+ raw, _ := json.Marshal(created["OpenIdConnectProviderARNs"], jsonx.V1())
var gotARNs []string
- _ = json.Unmarshal(raw, &gotARNs)
+ _ = json.Unmarshal(raw, &gotARNs, jsonx.V1())
assert.Equal(t, tt.wantARNs, gotARNs, "create response OpenIdConnectProviderARNs mismatch")
poolID, _ := created["IdentityPoolId"].(string)
@@ -72,11 +75,11 @@ func TestCreateIdentityPool_OIDCProviderARNs(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var desc map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc, jsonx.V1()))
- raw, _ = json.Marshal(desc["OpenIdConnectProviderARNs"])
+ raw, _ = json.Marshal(desc["OpenIdConnectProviderARNs"], jsonx.V1())
var descARNs []string
- _ = json.Unmarshal(raw, &descARNs)
+ _ = json.Unmarshal(raw, &descARNs, jsonx.V1())
assert.Equal(t, tt.wantARNs, descARNs, "describe response OpenIdConnectProviderARNs mismatch")
})
}
@@ -118,11 +121,11 @@ func TestCreateIdentityPool_SAMLProviderARNs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
- raw, _ := json.Marshal(created["SamlProviderARNs"])
+ raw, _ := json.Marshal(created["SamlProviderARNs"], jsonx.V1())
var gotARNs []string
- _ = json.Unmarshal(raw, &gotARNs)
+ _ = json.Unmarshal(raw, &gotARNs, jsonx.V1())
assert.Equal(t, tt.wantARNs, gotARNs, "SamlProviderARNs mismatch in create response")
})
}
@@ -177,7 +180,7 @@ func TestUpdateIdentityPool_OIDCAndSAMLARNs(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID, _ := created["IdentityPoolId"].(string)
require.NotEmpty(t, poolID)
@@ -191,16 +194,16 @@ func TestUpdateIdentityPool_OIDCAndSAMLARNs(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code, "update: %s", updRec.Body.String())
var updated map[string]any
- require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updated, jsonx.V1()))
- raw, _ := json.Marshal(updated["OpenIdConnectProviderARNs"])
+ raw, _ := json.Marshal(updated["OpenIdConnectProviderARNs"], jsonx.V1())
var gotOIDC []string
- _ = json.Unmarshal(raw, &gotOIDC)
+ _ = json.Unmarshal(raw, &gotOIDC, jsonx.V1())
assert.Equal(t, tt.wantOIDC, gotOIDC, "OIDC ARNs after update")
- raw, _ = json.Marshal(updated["SamlProviderARNs"])
+ raw, _ = json.Marshal(updated["SamlProviderARNs"], jsonx.V1())
var gotSAML []string
- _ = json.Unmarshal(raw, &gotSAML)
+ _ = json.Unmarshal(raw, &gotSAML, jsonx.V1())
assert.Equal(t, tt.wantSAML, gotSAML, "SAML ARNs after update")
})
}
@@ -267,7 +270,7 @@ func TestListIdentityPools_Pagination(t *testing.T) {
IdentityPools []any `json:"IdentityPools"`
NextToken string `json:"NextToken"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pageCounts = append(pageCounts, len(out.IdentityPools))
token = out.NextToken
@@ -304,7 +307,7 @@ func TestListIdentityPools_TokenPastEnd(t *testing.T) {
IdentityPools []any `json:"IdentityPools"`
NextToken string `json:"NextToken"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.IdentityPools, "token past last pool must return empty list, not first page")
assert.Empty(t, out.NextToken)
@@ -344,7 +347,7 @@ func TestHandler_CreateIdentityPool(t *testing.T) {
if tt.wantCode == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.poolName, out["IdentityPoolName"])
assert.NotEmpty(t, out["IdentityPoolId"])
}
@@ -380,7 +383,7 @@ func TestHandler_DeleteIdentityPool(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
poolID = out["IdentityPoolId"].(string)
} else {
poolID = "us-east-1:nonexistent"
@@ -407,7 +410,7 @@ func TestHandler_DescribeIdentityPool(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
rec := doCognitoIdentityRequest(t, h, "DescribeIdentityPool", map[string]any{
"IdentityPoolId": created["IdentityPoolId"],
@@ -415,7 +418,7 @@ func TestHandler_DescribeIdentityPool(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "desc-pool", out["IdentityPoolName"])
}
@@ -438,7 +441,7 @@ func TestHandler_ListIdentityPools(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pools, ok := out["IdentityPools"].([]any)
require.True(t, ok)
@@ -457,7 +460,7 @@ func TestHandler_UpdateIdentityPool(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
rec := doCognitoIdentityRequest(t, h, "UpdateIdentityPool", map[string]any{
"IdentityPoolId": created["IdentityPoolId"],
@@ -467,7 +470,7 @@ func TestHandler_UpdateIdentityPool(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, false, out["AllowUnauthenticatedIdentities"])
}
@@ -514,7 +517,7 @@ func TestHandler_WithProviders(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "provider-pool", out["IdentityPoolName"])
providers, ok := out["CognitoIdentityProviders"].([]any)
@@ -541,7 +544,7 @@ func TestSortedListIdentityPools(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pools, _ := out["IdentityPools"].([]any)
require.Len(t, pools, 3)
@@ -571,7 +574,7 @@ func TestTagsInIdentityPoolOutput(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
// Tags should be in the CreateIdentityPool response.
tags, _ := created["IdentityPoolTags"].(map[string]any)
@@ -585,7 +588,7 @@ func TestTagsInIdentityPoolOutput(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
descTags, _ := descOut["IdentityPoolTags"].(map[string]any)
assert.Equal(t, "test", descTags["env"])
@@ -603,7 +606,7 @@ func TestDeleteIdentityPool_CleansTagsAndPrincipalTags(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
arn := "arn:aws:cognito-identity:us-east-1:000000000000:identitypool/" + poolID
@@ -648,7 +651,7 @@ func TestHandler_DeveloperProviderName_InCreateAndDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
assert.Equal(t, "developer.myapp.com", created["DeveloperProviderName"])
descRec := doCognitoIdentityRequest(t, h, "DescribeIdentityPool", map[string]any{
@@ -657,7 +660,7 @@ func TestHandler_DeveloperProviderName_InCreateAndDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, "developer.myapp.com", descOut["DeveloperProviderName"])
}
@@ -674,7 +677,7 @@ func TestHandler_UpdateIdentityPool_WithTags(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
updateRec := doCognitoIdentityRequest(t, h, "UpdateIdentityPool", map[string]any{
@@ -686,7 +689,7 @@ func TestHandler_UpdateIdentityPool_WithTags(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updated map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updated, jsonx.V1()))
tags, _ := updated["IdentityPoolTags"].(map[string]any)
assert.Equal(t, "prod", tags["env"])
assert.Equal(t, "backend", tags["team"])
@@ -744,7 +747,7 @@ func TestHandler_ListIdentityPools_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.NewDecoder(rec1.Body).Decode(&out1))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec1.Body), &out1, jsonx.V1()))
assert.Len(t, out1["IdentityPools"], 2)
nextToken, _ := out1["NextToken"].(string)
assert.NotEmpty(t, nextToken)
@@ -757,6 +760,6 @@ func TestHandler_ListIdentityPools_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&out2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &out2, jsonx.V1()))
assert.Len(t, out2["IdentityPools"], 1)
}
diff --git a/services/cognitoidentity/handler_tags_test.go b/services/cognitoidentity/handler_tags_test.go
index e122f31ddc..2b2689a511 100644
--- a/services/cognitoidentity/handler_tags_test.go
+++ b/services/cognitoidentity/handler_tags_test.go
@@ -1,12 +1,14 @@
package cognitoidentity_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_ListTagsForResource(t *testing.T) {
@@ -43,7 +45,7 @@ func TestHandler_ListTagsForResource(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
arn = "arn:aws:cognito-identity:us-east-1:000000000000:identitypool/" + poolID
}
@@ -56,7 +58,7 @@ func TestHandler_ListTagsForResource(t *testing.T) {
if tt.wantCode == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tags, _ := out["Tags"].(map[string]any)
assert.Equal(t, "test", tags["env"])
}
@@ -76,7 +78,7 @@ func TestHandler_TagResource_UntagResource(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
arn := "arn:aws:cognito-identity:us-east-1:000000000000:identitypool/" + poolID
@@ -97,7 +99,7 @@ func TestHandler_TagResource_UntagResource(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
tags, _ := listOut["Tags"].(map[string]any)
assert.Equal(t, "backend", tags["team"])
assert.Equal(t, "alice", tags["owner"])
@@ -116,7 +118,7 @@ func TestHandler_TagResource_UntagResource(t *testing.T) {
require.Equal(t, http.StatusOK, listRec2.Code)
var listOut2 map[string]any
- require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listOut2))
+ require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listOut2, jsonx.V1()))
tags2, _ := listOut2["Tags"].(map[string]any)
assert.Equal(t, "backend", tags2["team"])
assert.NotContains(t, tags2, "owner")
@@ -160,7 +162,7 @@ func TestListTagsForResource_EmptyTags(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
arn := "arn:aws:cognito-identity:us-east-1:000000000000:identitypool/" + poolID
@@ -171,7 +173,7 @@ func TestListTagsForResource_EmptyTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// Tags should always be present (even if empty), not omitted.
tags, hasKey := out["Tags"]
diff --git a/services/cognitoidentity/handler_test.go b/services/cognitoidentity/handler_test.go
index d9c02b54f2..c644d91277 100644
--- a/services/cognitoidentity/handler_test.go
+++ b/services/cognitoidentity/handler_test.go
@@ -3,7 +3,7 @@ package cognitoidentity_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidentity"
)
@@ -62,7 +63,7 @@ func doCognitoIdentityRequest(
) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -338,7 +339,7 @@ func TestNonNilSlices(t *testing.T) {
require.Equal(t, http.StatusOK, listPoolsRec.Code)
var listPoolsOut map[string]any
- require.NoError(t, json.Unmarshal(listPoolsRec.Body.Bytes(), &listPoolsOut))
+ require.NoError(t, json.Unmarshal(listPoolsRec.Body.Bytes(), &listPoolsOut, jsonx.V1()))
pools, poolsOK := listPoolsOut["IdentityPools"].([]any)
require.True(t, poolsOK, "IdentityPools should be a non-null array")
assert.Empty(t, pools)
@@ -351,7 +352,7 @@ func TestNonNilSlices(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
listIDsRec := doCognitoIdentityRequest(t, h, "ListIdentities", map[string]any{
"IdentityPoolId": created["IdentityPoolId"],
@@ -360,7 +361,7 @@ func TestNonNilSlices(t *testing.T) {
require.Equal(t, http.StatusOK, listIDsRec.Code)
var listIDsOut map[string]any
- require.NoError(t, json.Unmarshal(listIDsRec.Body.Bytes(), &listIDsOut))
+ require.NoError(t, json.Unmarshal(listIDsRec.Body.Bytes(), &listIDsOut, jsonx.V1()))
identities, idsOK := listIDsOut["Identities"].([]any)
require.True(t, idsOK, "Identities should be a non-null array")
assert.Empty(t, identities)
@@ -389,7 +390,7 @@ func TestSeedHelpers(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, "seeded-pool", descOut["IdentityPoolName"])
// Seed an identity.
@@ -420,7 +421,7 @@ func TestCountHelpers(t *testing.T) {
assert.Equal(t, 1, h.Backend.PoolCount())
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
doCognitoIdentityRequest(t, h, "GetId", map[string]any{
@@ -470,7 +471,7 @@ func TestPersistenceRoundTripWithPrincipalTags(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
poolID := created["IdentityPoolId"].(string)
setRec := doCognitoIdentityRequest(t, h, "SetPrincipalTagAttributeMap", map[string]any{
@@ -497,7 +498,7 @@ func TestPersistenceRoundTripWithPrincipalTags(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
tags, _ := getOut["PrincipalTags"].(map[string]any)
assert.Equal(t, "user_id", tags["sub"])
diff --git a/services/cognitoidentity/login_tokens.go b/services/cognitoidentity/login_tokens.go
index 2e2508b215..ace6363d06 100644
--- a/services/cognitoidentity/login_tokens.go
+++ b/services/cognitoidentity/login_tokens.go
@@ -5,10 +5,12 @@ import (
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const jwtSegments = 3
@@ -28,7 +30,7 @@ func parseLoginClaims(token string) (map[string]any, bool) {
}
var claims map[string]any
- if json.Unmarshal(raw, &claims) != nil {
+ if json.Unmarshal(raw, &claims, jsonx.V1()) != nil {
return nil, false
}
@@ -123,7 +125,7 @@ func (b *InMemoryBackend) verifyLoginSignature(token string, claims map[string]a
Kid string `json:"kid"`
}
- if json.Unmarshal(rawHdr, &hdr) != nil {
+ if json.Unmarshal(rawHdr, &hdr, jsonx.V1()) != nil {
return errBadSignature
}
diff --git a/services/cognitoidentity/open_id_token.go b/services/cognitoidentity/open_id_token.go
index 0c3a422439..8fabc51b7b 100644
--- a/services/cognitoidentity/open_id_token.go
+++ b/services/cognitoidentity/open_id_token.go
@@ -2,9 +2,11 @@ package cognitoidentity
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -50,12 +52,12 @@ func issueOpenIDToken(
claims[openIDTagsClaim] = tags
}
- header, err := json.Marshal(map[string]string{"alg": "RS256", "typ": "JWT"})
+ header, err := json.Marshal(map[string]string{"alg": "RS256", "typ": "JWT"}, jsonx.V1())
if err != nil {
return "", fmt.Errorf("encode token header: %w", err)
}
- payload, err := json.Marshal(claims)
+ payload, err := json.Marshal(claims, jsonx.V1())
if err != nil {
return "", fmt.Errorf("encode token claims: %w", err)
}
diff --git a/services/cognitoidentity/open_id_token_test.go b/services/cognitoidentity/open_id_token_test.go
index c58450d76f..0acec5afd5 100644
--- a/services/cognitoidentity/open_id_token_test.go
+++ b/services/cognitoidentity/open_id_token_test.go
@@ -3,13 +3,14 @@ package cognitoidentity_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidentity"
)
@@ -23,7 +24,7 @@ func tokenClaims(t *testing.T, token string) map[string]any {
require.NoError(t, err)
var claims map[string]any
- require.NoError(t, json.Unmarshal(raw, &claims))
+ require.NoError(t, json.Unmarshal(raw, &claims, jsonx.V1()))
return claims
}
diff --git a/services/cognitoidentity/persistence.go b/services/cognitoidentity/persistence.go
index 77baf9ce8b..edab853440 100644
--- a/services/cognitoidentity/persistence.go
+++ b/services/cognitoidentity/persistence.go
@@ -2,7 +2,7 @@ package cognitoidentity
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -60,10 +60,10 @@ func principalTagDTOKeyFn(d *principalTagDTO) string {
// decoding a snapshot from an incompatible (older or newer) build of this backend as
// though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// persistenceDTOTables groups every ephemeral DTO table buildPersistenceDTORegistry
@@ -225,7 +225,7 @@ func unwrapPrincipalTagDTOs(dtos *store.Table[principalTagDTO]) []*PrincipalTagM
// restoreResourceTables rebuilds every store.Table on b from snap's per-table JSON,
// factored out of Restore to keep Restore's own cognitive complexity low. Callers must
// hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
dtos := buildPersistenceDTORegistry()
if err := dtos.registry.RestoreAll(tables); err != nil {
diff --git a/services/cognitoidentity/persistence_test.go b/services/cognitoidentity/persistence_test.go
index 03e51cd828..2db79aa3a9 100644
--- a/services/cognitoidentity/persistence_test.go
+++ b/services/cognitoidentity/persistence_test.go
@@ -5,11 +5,13 @@ package cognitoidentity //nolint:testpackage // see comment above.
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// testRegionEast and testRegionWest are the two AWS regions exercised by the
@@ -220,11 +222,11 @@ func tamperSnapshotVersion(t *testing.T, data []byte, version int) []byte {
t.Helper()
var snap backendSnapshot
- require.NoError(t, json.Unmarshal(data, &snap))
+ require.NoError(t, json.Unmarshal(data, &snap, jsonx.V1()))
snap.Version = version
- out, err := json.Marshal(snap)
+ out, err := json.Marshal(snap, jsonx.V1())
require.NoError(t, err)
return out
diff --git a/services/cognitoidentity/role_selection_test.go b/services/cognitoidentity/role_selection_test.go
index da45ff0814..d680153e95 100644
--- a/services/cognitoidentity/role_selection_test.go
+++ b/services/cognitoidentity/role_selection_test.go
@@ -7,7 +7,7 @@ import (
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"maps"
"testing"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidentity"
)
@@ -302,10 +303,10 @@ func signedLogin(t *testing.T, key *rsa.PrivateKey, claims map[string]any) strin
t.Helper()
enc := base64.RawURLEncoding
- hdr, err := json.Marshal(map[string]string{"alg": "RS256", "kid": "k1"})
+ hdr, err := json.Marshal(map[string]string{"alg": "RS256", "kid": "k1"}, jsonx.V1())
require.NoError(t, err)
- body, err := json.Marshal(claims)
+ body, err := json.Marshal(claims, jsonx.V1())
require.NoError(t, err)
signing := enc.EncodeToString(hdr) + "." + enc.EncodeToString(body)
diff --git a/services/cognitoidentity/sdk_login_token_flow_test.go b/services/cognitoidentity/sdk_login_token_flow_test.go
index 5bfd506f30..4b311b909b 100644
--- a/services/cognitoidentity/sdk_login_token_flow_test.go
+++ b/services/cognitoidentity/sdk_login_token_flow_test.go
@@ -2,7 +2,7 @@ package cognitoidentity_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
@@ -11,16 +11,18 @@ import (
sdktypes "github.com/aws/aws-sdk-go-v2/service/cognitoidentity/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func loginJWT(t *testing.T, claims map[string]any) string {
t.Helper()
enc := base64.RawURLEncoding
- hdr, err := json.Marshal(map[string]string{"alg": "RS256"})
+ hdr, err := json.Marshal(map[string]string{"alg": "RS256"}, jsonx.V1())
require.NoError(t, err)
- body, err := json.Marshal(claims)
+ body, err := json.Marshal(claims, jsonx.V1())
require.NoError(t, err)
return enc.EncodeToString(hdr) + "." + enc.EncodeToString(body) + ".sig"
diff --git a/services/cognitoidp/attribute_custom_message_test.go b/services/cognitoidp/attribute_custom_message_test.go
index 2995a2693e..ebdc03c0a1 100644
--- a/services/cognitoidp/attribute_custom_message_test.go
+++ b/services/cognitoidp/attribute_custom_message_test.go
@@ -1,7 +1,7 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -134,7 +135,7 @@ func TestUpdateUserAttributes_CustomMessageAndVerification(t *testing.T) {
var resp struct {
CodeDeliveryDetailsList []map[string]string `json:"CodeDeliveryDetailsList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.CodeDeliveryDetailsList, 1)
d := resp.CodeDeliveryDetailsList[0]
@@ -216,7 +217,7 @@ func TestGetUserAttributeVerificationCode_FiresCustomMessage(t *testing.T) {
var resp struct {
CodeDeliveryDetails map[string]string `json:"CodeDeliveryDetails"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "subj", resp.CodeDeliveryDetails["CustomMessageSubject"])
assert.Equal(t, "v", metadataOf(t, f.inv, "CustomMessage_VerifyUserAttribute", "clientMetadata")["k"])
}
diff --git a/services/cognitoidp/attributes_management_test.go b/services/cognitoidp/attributes_management_test.go
index 2e1e66fffb..427f854258 100644
--- a/services/cognitoidp/attributes_management_test.go
+++ b/services/cognitoidp/attributes_management_test.go
@@ -1,13 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -133,7 +134,7 @@ func TestHandler_UpdateUserAttributes(t *testing.T) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "attr-pool"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolResp["UserPool"]["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -141,7 +142,7 @@ func TestHandler_UpdateUserAttributes(t *testing.T) {
"ClientName": "c",
})
var clientResp map[string]map[string]any
- _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp)
+ _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1())
clientID := clientResp["UserPoolClient"]["ClientId"].(string)
doCognitoRequest(t, h, "AdminCreateUser", map[string]any{
@@ -168,7 +169,7 @@ func TestHandler_UpdateUserAttributes(t *testing.T) {
require.Equal(t, http.StatusOK, authRec.Code)
var authData map[string]map[string]any
- require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authData))
+ require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authData, jsonx.V1()))
accessToken := authData["AuthenticationResult"]["AccessToken"].(string)
if tt.badToken {
@@ -221,7 +222,7 @@ func TestHandler_AdminUpdateUserAttributes(t *testing.T) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "admin-attr-pool"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolResp["UserPool"]["Id"].(string)
doCognitoRequest(t, h, "AdminCreateUser", map[string]any{
@@ -256,7 +257,7 @@ func TestHandler_AddCustomAttributes(t *testing.T) {
setup: func(h *cognitoidp.Handler) string {
rec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "test-pool"})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["UserPool"].(map[string]any)["Id"].(string)
},
@@ -334,7 +335,7 @@ func TestHandler_AdminDeleteUserAttributes(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "attr-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
username := "deleteattr-user"
@@ -393,7 +394,7 @@ func TestAddCustomAttributes_RequiresCustomPrefix(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "prefix-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
rec := doCognitoRequest(t, h, "AddCustomAttributes", map[string]any{
@@ -413,7 +414,7 @@ func TestSortedAttributes(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "sorted-attrs-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
doCognitoRequest(t, h, "AdminCreateUser", map[string]any{
@@ -434,7 +435,7 @@ func TestSortedAttributes(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
attrs := resp["UserAttributes"].([]any)
require.GreaterOrEqual(t, len(attrs), 3)
@@ -462,7 +463,7 @@ func TestIDTokenUserAttributeClaims(t *testing.T) {
require.Equal(t, http.StatusOK, poolRec.Code)
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -476,7 +477,7 @@ func TestIDTokenUserAttributeClaims(t *testing.T) {
require.Equal(t, http.StatusOK, clientRec.Code)
var clientResp map[string]any
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
clientID := clientResp["UserPoolClient"].(map[string]any)["ClientId"].(string)
signupRec := doCognitoRequest(t, h, "SignUp", map[string]any{
@@ -490,7 +491,7 @@ func TestIDTokenUserAttributeClaims(t *testing.T) {
require.Equal(t, http.StatusOK, signupRec.Code)
var signupResp map[string]any
- require.NoError(t, json.Unmarshal(signupRec.Body.Bytes(), &signupResp))
+ require.NoError(t, json.Unmarshal(signupRec.Body.Bytes(), &signupResp, jsonx.V1()))
// AutoVerifiedAttributes only selects which channel receives the confirmation
// code; it does not skip confirmation itself (see TestSignUpWithValidation_AutoVerify).
@@ -513,7 +514,7 @@ func TestIDTokenUserAttributeClaims(t *testing.T) {
require.Equal(t, http.StatusOK, authRec.Code)
var authResp map[string]any
- require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp))
+ require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp, jsonx.V1()))
idToken := authResp["AuthenticationResult"].(map[string]any)["IdToken"].(string)
claims := decodeJWTPayload(t, idToken)
@@ -550,7 +551,7 @@ func TestAdminCreateUserSubInAttributes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
user := resp["User"].(map[string]any)
attrs := user["Attributes"].([]any)
diff --git a/services/cognitoidp/attributes_test.go b/services/cognitoidp/attributes_test.go
index 6fbe0dd2c6..1d95583949 100644
--- a/services/cognitoidp/attributes_test.go
+++ b/services/cognitoidp/attributes_test.go
@@ -1,13 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -50,7 +51,7 @@ func TestVerifyUserAttribute_RealCodeValidation(t *testing.T) {
ID string `json:"Id"`
} `json:"UserPool"`
}
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp.UserPool.ID
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -63,7 +64,7 @@ func TestVerifyUserAttribute_RealCodeValidation(t *testing.T) {
ClientID string `json:"ClientId"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
clientID := clientResp.UserPoolClient.ClientID
// Sign up and confirm a user with email attribute.
@@ -117,7 +118,7 @@ func TestVerifyUserAttribute_RealCodeValidation(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, errResp.Type)
}
})
@@ -279,7 +280,7 @@ func TestAttrVerification_Handler_GetAndVerify(t *testing.T) {
CodeDeliveryDetails map[string]string `json:"CodeDeliveryDetails,omitempty"`
UserConfirmed bool `json:"UserConfirmed,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &signUpResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &signUpResp, jsonx.V1()))
if !signUpResp.UserConfirmed {
confirmRec := doCognitoRequest(t, h, "ConfirmSignUp", map[string]any{
@@ -302,7 +303,7 @@ func TestAttrVerification_Handler_GetAndVerify(t *testing.T) {
var codeOut struct {
CodeDeliveryDetails map[string]string `json:"CodeDeliveryDetails,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &codeOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &codeOut, jsonx.V1()))
assert.Equal(t, "EMAIL", codeOut.CodeDeliveryDetails["DeliveryMedium"])
assert.NotEmpty(t, codeOut.CodeDeliveryDetails["Destination"])
@@ -449,7 +450,7 @@ func TestHandler_VerifyUserAttribute(t *testing.T) {
CodeDeliveryDetails map[string]string `json:"CodeDeliveryDetails,omitempty"`
UserConfirmed bool `json:"UserConfirmed,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &signUpResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &signUpResp, jsonx.V1()))
if !signUpResp.UserConfirmed {
confirmRec := doCognitoRequest(t, h, "ConfirmSignUp", map[string]any{
diff --git a/services/cognitoidp/auth_events_test.go b/services/cognitoidp/auth_events_test.go
index 542559a798..572835285a 100644
--- a/services/cognitoidp/auth_events_test.go
+++ b/services/cognitoidp/auth_events_test.go
@@ -1,12 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestAdminListUserAuthEvents_EmptyButValid(t *testing.T) {
@@ -26,7 +28,7 @@ func TestAdminListUserAuthEvents_EmptyButValid(t *testing.T) {
NextToken string `json:"NextToken,omitempty"`
AuthEvents []map[string]any `json:"AuthEvents,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp.AuthEvents)
assert.Empty(t, resp.NextToken)
@@ -122,7 +124,7 @@ func TestHandler_AdminListUserAuthEvents_Validation(t *testing.T) {
var resp struct {
AuthEvents []map[string]any `json:"AuthEvents"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp.AuthEvents)
})
diff --git a/services/cognitoidp/auth_tokens.go b/services/cognitoidp/auth_tokens.go
index e6e1fe8a41..205fddc51e 100644
--- a/services/cognitoidp/auth_tokens.go
+++ b/services/cognitoidp/auth_tokens.go
@@ -4,7 +4,7 @@ import (
"crypto/rsa"
"crypto/subtle"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -12,6 +12,8 @@ import (
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// findUserByAccessTokenLocked finds the live *User for a given access token.
@@ -104,7 +106,7 @@ func tokenHeaderKID(token string) string {
var hdr struct {
Kid string `json:"kid"`
}
- if json.Unmarshal(raw, &hdr) != nil {
+ if json.Unmarshal(raw, &hdr, jsonx.V1()) != nil {
return ""
}
diff --git a/services/cognitoidp/auth_tokens_test.go b/services/cognitoidp/auth_tokens_test.go
index 7cdb43f65c..87595510c2 100644
--- a/services/cognitoidp/auth_tokens_test.go
+++ b/services/cognitoidp/auth_tokens_test.go
@@ -2,7 +2,7 @@ package cognitoidp_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -121,7 +122,7 @@ func initiateAuth(t *testing.T, h *cognitoidp.Handler, clientID, username string
require.Equal(t, http.StatusOK, rec.Code, "InitiateAuth: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
@@ -137,7 +138,7 @@ func jwtClaims(t *testing.T, token string) map[string]any {
require.NoError(t, err, "base64 decode JWT payload")
var claims map[string]any
- require.NoError(t, json.Unmarshal(payload, &claims))
+ require.NoError(t, json.Unmarshal(payload, &claims, jsonx.V1()))
return claims
}
@@ -217,7 +218,7 @@ func TestTokenValidity_StoredAndReturned(t *testing.T) {
RefreshTokenValidity int32 `json:"RefreshTokenValidity"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Equal(t, tt.accessTokenValidity, createResp.UserPoolClient.AccessTokenValidity)
assert.Equal(t, tt.idTokenValidity, createResp.UserPoolClient.IDTokenValidity)
assert.Equal(t, wantRefresh, createResp.UserPoolClient.RefreshTokenValidity)
@@ -242,7 +243,7 @@ func TestTokenValidity_StoredAndReturned(t *testing.T) {
RefreshTokenValidity int32 `json:"RefreshTokenValidity"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, tt.accessTokenValidity, descResp.UserPoolClient.AccessTokenValidity)
assert.Equal(t, tt.idTokenValidity, descResp.UserPoolClient.IDTokenValidity)
assert.Equal(t, wantRefresh, descResp.UserPoolClient.RefreshTokenValidity)
@@ -268,7 +269,7 @@ func TestTokenValidity_UpdateClient(t *testing.T) {
ClientID string `json:"ClientId"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
clientID := createResp.UserPoolClient.ClientID
// Update with validity settings.
@@ -291,7 +292,7 @@ func TestTokenValidity_UpdateClient(t *testing.T) {
IDTokenValidity int32 `json:"IdTokenValidity"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updResp))
+ require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updResp, jsonx.V1()))
assert.Equal(t, int32(5), updResp.UserPoolClient.AccessTokenValidity)
assert.Equal(t, int32(5), updResp.UserPoolClient.IDTokenValidity)
assert.Equal(t, "minutes", updResp.UserPoolClient.TokenValidityUnits["AccessToken"])
@@ -349,7 +350,7 @@ func TestTokenValidity_HonoredInJWT(t *testing.T) {
ClientID string `json:"ClientId"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
clientID := createResp.UserPoolClient.ClientID
// Create and confirm a user, then authenticate.
@@ -374,7 +375,7 @@ func TestTokenValidity_HonoredInJWT(t *testing.T) {
ClientID string `json:"ClientId"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &cr, jsonx.V1()))
validClientID := cr.UserPoolClient.ClientID
// Sign up user with the validity client.
@@ -409,7 +410,7 @@ func TestTokenValidity_HonoredInJWT(t *testing.T) {
ExpiresIn float64 `json:"ExpiresIn"`
} `json:"AuthenticationResult"`
}
- require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp))
+ require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp, jsonx.V1()))
assert.InDelta(t, tt.wantExpiresIn, authResp.AuthenticationResult.ExpiresIn, 1, "ExpiresIn mismatch")
@@ -464,7 +465,7 @@ func TestAuthFlow_RefreshTokenAuth(t *testing.T) {
require.Equal(t, http.StatusOK, refreshRec.Code, refreshRec.Body.String())
var refreshResp map[string]any
- require.NoError(t, json.Unmarshal(refreshRec.Body.Bytes(), &refreshResp))
+ require.NoError(t, json.Unmarshal(refreshRec.Body.Bytes(), &refreshResp, jsonx.V1()))
result, ok := refreshResp["AuthenticationResult"].(map[string]any)
require.True(t, ok)
assert.NotEmpty(t, result["AccessToken"])
@@ -587,7 +588,7 @@ func TestGetTokensFromRefreshToken(t *testing.T) {
RefreshToken string `json:"RefreshToken,omitempty"`
} `json:"AuthenticationResult"`
}
- require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp))
+ require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp, jsonx.V1()))
refreshToken := authResp.AuthenticationResult.RefreshToken
require.NotEmpty(t, refreshToken)
@@ -603,7 +604,7 @@ func TestGetTokensFromRefreshToken(t *testing.T) {
AccessToken string `json:"AccessToken,omitempty"`
} `json:"AuthenticationResult"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tokenResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tokenResp, jsonx.V1()))
assert.NotEmpty(t, tokenResp.AuthenticationResult.AccessToken)
}
@@ -702,7 +703,7 @@ func TestHandler_RefreshTokenAuth(t *testing.T) {
setup: func(h *cognitoidp.Handler) (string, string) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolResp["UserPool"]["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -710,7 +711,7 @@ func TestHandler_RefreshTokenAuth(t *testing.T) {
"ClientName": "c",
})
var clientResp map[string]map[string]any
- _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp)
+ _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1())
clientID := clientResp["UserPoolClient"]["ClientId"].(string)
// Admin-create a confirmed user.
@@ -737,7 +738,7 @@ func TestHandler_RefreshTokenAuth(t *testing.T) {
},
})
var authResp map[string]map[string]any
- _ = json.Unmarshal(authRec.Body.Bytes(), &authResp)
+ _ = json.Unmarshal(authRec.Body.Bytes(), &authResp, jsonx.V1())
rt := authResp["AuthenticationResult"]["RefreshToken"].(string)
return clientID, rt
@@ -749,7 +750,7 @@ func TestHandler_RefreshTokenAuth(t *testing.T) {
setup: func(h *cognitoidp.Handler) (string, string) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p2"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolResp["UserPool"]["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -757,7 +758,7 @@ func TestHandler_RefreshTokenAuth(t *testing.T) {
"ClientName": "c2",
})
var clientResp map[string]map[string]any
- _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp)
+ _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1())
clientID := clientResp["UserPoolClient"]["ClientId"].(string)
return clientID, "totally-invalid-refresh-token"
@@ -784,7 +785,7 @@ func TestHandler_RefreshTokenAuth(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["AuthenticationResult"]["AccessToken"])
assert.NotEmpty(t, resp["AuthenticationResult"]["IdToken"])
// After rotation the old token is gone; new one must differ.
diff --git a/services/cognitoidp/branding_test.go b/services/cognitoidp/branding_test.go
index b9068587fa..fc1fd999b0 100644
--- a/services/cognitoidp/branding_test.go
+++ b/services/cognitoidp/branding_test.go
@@ -1,12 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestUICustomization_SetGet_WithImageFile(t *testing.T) {
@@ -33,7 +35,7 @@ func TestUICustomization_SetGet_WithImageFile(t *testing.T) {
LastModifiedDate float64 `json:"LastModifiedDate,omitempty"`
} `json:"UICustomization"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &setOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &setOut, jsonx.V1()))
require.NotNil(t, setOut.UICustomization)
assert.Equal(t, poolID, setOut.UICustomization.UserPoolID)
assert.Equal(t, clientID, setOut.UICustomization.ClientID)
@@ -56,7 +58,7 @@ func TestUICustomization_SetGet_WithImageFile(t *testing.T) {
LastModifiedDate float64 `json:"LastModifiedDate,omitempty"`
} `json:"UICustomization"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut, jsonx.V1()))
require.NotNil(t, getOut.UICustomization)
assert.Equal(t, ".banner { background: blue; }", getOut.UICustomization.CSS)
assert.Empty(t, getOut.UICustomization.ImageURL)
@@ -80,7 +82,7 @@ func TestUICustomization_Get_Empty(t *testing.T) {
CSS string `json:"CSS,omitempty"`
} `json:"UICustomization"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.UICustomization)
assert.Empty(t, out.UICustomization.CSS)
}
@@ -155,7 +157,7 @@ func TestUICustomization(t *testing.T) {
CSS string `json:"CSS,omitempty"`
} `json:"UICustomization"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, ".logo { color: red; }", resp.UICustomization.CSS)
}
@@ -177,7 +179,7 @@ func TestManagedLoginBranding_CRUD(t *testing.T) {
ManagedLoginBrandingID string `json:"ManagedLoginBrandingId,omitempty"`
} `json:"ManagedLoginBranding"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
brandingID := createResp.ManagedLoginBranding.ManagedLoginBrandingID
require.NotEmpty(t, brandingID)
@@ -252,7 +254,7 @@ func TestManagedLoginBranding_SettingsAndAssets_AcceptedAndEchoed(t *testing.T)
var createResp struct {
ManagedLoginBranding managedLoginBrandingWire `json:"ManagedLoginBranding"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
componentClasses, ok := createResp.ManagedLoginBranding.Settings["componentClasses"].(map[string]any)
require.True(t, ok)
primaryButton, ok := componentClasses["primaryButton"].(map[string]any)
@@ -277,7 +279,7 @@ func TestManagedLoginBranding_SettingsAndAssets_AcceptedAndEchoed(t *testing.T)
var updResp struct {
ManagedLoginBranding managedLoginBrandingWire `json:"ManagedLoginBranding"`
}
- require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updResp))
+ require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updResp, jsonx.V1()))
assert.True(t, updResp.ManagedLoginBranding.UseCognitoProvidedValues)
assert.NotEmpty(t, updResp.ManagedLoginBranding.Settings, "omitting Settings on update must not clear it")
}
diff --git a/services/cognitoidp/delivery_test.go b/services/cognitoidp/delivery_test.go
index c675ba3506..4e71afa573 100644
--- a/services/cognitoidp/delivery_test.go
+++ b/services/cognitoidp/delivery_test.go
@@ -2,7 +2,7 @@ package cognitoidp_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"maps"
"net/http"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -76,7 +77,7 @@ func newDeliveryEnv(t *testing.T, poolExtra map[string]any) *deliveryEnv {
} `json:"UserPool"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pool))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pool, jsonx.V1()))
rec = doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
"UserPoolId": pool.UserPool.ID, "ClientName": "app",
@@ -89,7 +90,7 @@ func newDeliveryEnv(t *testing.T, poolExtra map[string]any) *deliveryEnv {
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &client))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &client, jsonx.V1()))
return &deliveryEnv{h: h, snd: snd, poolID: pool.UserPool.ID, clientID: client.UserPoolClient.ClientID}
}
@@ -102,7 +103,7 @@ func (e *deliveryEnv) call(t *testing.T, action string, body map[string]any) map
var out map[string]any
if rec.Body.Len() > 0 {
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
}
return out
diff --git a/services/cognitoidp/devices_test.go b/services/cognitoidp/devices_test.go
index d4c2b484e2..662d044d2d 100644
--- a/services/cognitoidp/devices_test.go
+++ b/services/cognitoidp/devices_test.go
@@ -1,13 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -33,7 +34,7 @@ func TestDevices_CRUD(t *testing.T) {
var confirmResp struct {
UserConfirmationNecessary bool `json:"UserConfirmationNecessary,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &confirmResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &confirmResp, jsonx.V1()))
assert.False(t, confirmResp.UserConfirmationNecessary)
// ListDevices — discover the generated device key.
@@ -43,7 +44,7 @@ func TestDevices_CRUD(t *testing.T) {
var listResp struct {
Devices []deviceWire `json:"Devices,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Devices, 1)
assert.NotContains(t, rec.Body.String(), "DeviceStatus")
deviceKey := listResp.Devices[0].DeviceKey
@@ -59,7 +60,7 @@ func TestDevices_CRUD(t *testing.T) {
var getResp struct {
Device deviceWire `json:"Device"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, deviceKey, getResp.Device.DeviceKey)
// UpdateDeviceStatus -> remembered
@@ -75,7 +76,7 @@ func TestDevices_CRUD(t *testing.T) {
"DeviceKey": deviceKey,
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.NotContains(t, rec.Body.String(), "DeviceStatus")
// ForgetDevice
@@ -101,7 +102,7 @@ func TestDevices_CRUD(t *testing.T) {
var listResp2 struct {
Devices []deviceWire `json:"Devices,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp2, jsonx.V1()))
assert.Empty(t, listResp2.Devices)
}
@@ -144,7 +145,7 @@ func TestDevices_NotFoundAndValidation(t *testing.T) {
var listResp struct {
Devices []deviceWire `json:"Devices,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Devices, 1)
rec = doCognitoRequest(t, h, "UpdateDeviceStatus", map[string]any{
@@ -179,7 +180,7 @@ func TestAdminDevices_CRUD(t *testing.T) {
var listResp struct {
Devices []deviceWire `json:"Devices,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Devices, 1)
assert.Equal(t, "device-abc", listResp.Devices[0].DeviceKey)
@@ -210,7 +211,7 @@ func TestAdminDevices_CRUD(t *testing.T) {
var getResp struct {
Device deviceWire `json:"Device"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.NotContains(t, rec.Body.String(), "DeviceStatus")
// AdminForgetDevice — real deletion when the device exists.
@@ -395,7 +396,7 @@ func TestHandler_AdminForgetDevice(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "device-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
username := "device-user"
diff --git a/services/cognitoidp/domains_test.go b/services/cognitoidp/domains_test.go
index 8bf070a1a2..017b878e72 100644
--- a/services/cognitoidp/domains_test.go
+++ b/services/cognitoidp/domains_test.go
@@ -1,7 +1,7 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -79,7 +80,7 @@ func TestCreateUserPoolDomain_ManagedDomain_EmptyCloudFrontDomain(t *testing.T)
var out struct {
CloudFrontDomain string `json:"CloudFrontDomain,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tt.wantCF {
assert.Contains(
@@ -114,7 +115,7 @@ func TestUserPoolDomain_Managed(t *testing.T) {
var out struct {
CloudFrontDomain string `json:"CloudFrontDomain,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// Managed domains: AWS returns empty CloudFrontDomain (no CloudFront distribution).
assert.Empty(t, out.CloudFrontDomain)
}
@@ -139,7 +140,7 @@ func TestUserPoolDomain_Custom_WithCertArn(t *testing.T) {
var createOut struct {
CloudFrontDomain string `json:"CloudFrontDomain,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
assert.Contains(t, createOut.CloudFrontDomain, "cloudfront.net")
// Describe should return the domain with status.
@@ -159,7 +160,7 @@ func TestUserPoolDomain_Custom_WithCertArn(t *testing.T) {
CloudFrontDistribution string `json:"CloudFrontDistribution,omitempty"`
} `json:"DomainDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut, jsonx.V1()))
require.NotNil(t, descOut.DomainDescription)
assert.Equal(t, "auth.mycompany.com", descOut.DomainDescription.Domain)
assert.Equal(t, "ACTIVE", descOut.DomainDescription.Status)
@@ -197,7 +198,7 @@ func TestUserPoolDomain_Managed_NoCustomDomainConfig(t *testing.T) {
} `json:"CustomDomainConfig,omitempty"`
} `json:"DomainDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut, jsonx.V1()))
require.NotNil(t, descOut.DomainDescription)
assert.Nil(t, descOut.DomainDescription.CustomDomainConfig)
}
@@ -231,7 +232,7 @@ func TestUserPoolDomain_Update_WithCertArn(t *testing.T) {
var out struct {
CloudFrontDomain string `json:"CloudFrontDomain,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out.CloudFrontDomain, "cloudfront.net")
}
@@ -282,7 +283,7 @@ func TestUserPoolDomain_Describe_AWSAccountIdManagedLoginVersionS3Bucket(t *test
var createOut struct {
ManagedLoginVersion *int32 `json:"ManagedLoginVersion,omitempty"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
require.NotNil(t, createOut.ManagedLoginVersion, "CreateUserPoolDomain must echo ManagedLoginVersion")
assert.Equal(t, tt.wantMLV, *createOut.ManagedLoginVersion)
@@ -290,7 +291,7 @@ func TestUserPoolDomain_Describe_AWSAccountIdManagedLoginVersionS3Bucket(t *test
require.Equal(t, http.StatusOK, descRec.Code, descRec.Body.String())
var out domainDescriptionWire
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.DomainDescription)
assert.NotEmpty(t, out.DomainDescription.AWSAccountID)
@@ -323,7 +324,7 @@ func TestUserPoolDomain_UpdateManagedLoginVersion_OmittedRetainsPrior(t *testing
var updateOut struct {
ManagedLoginVersion *int32 `json:"ManagedLoginVersion,omitempty"`
}
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut, jsonx.V1()))
require.NotNil(t, updateOut.ManagedLoginVersion, "UpdateUserPoolDomain must echo ManagedLoginVersion")
assert.EqualValues(t, 2, *updateOut.ManagedLoginVersion, "omitting the field on update must not reset it")
}
@@ -429,7 +430,7 @@ func TestDomain_FullCRUDLifecycle(t *testing.T) {
var createResp struct {
CloudFrontDomain string `json:"CloudFrontDomain,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
// Managed domains: AWS returns empty CloudFrontDomain in the create response.
assert.Empty(t, createResp.CloudFrontDomain)
@@ -445,7 +446,7 @@ func TestDomain_FullCRUDLifecycle(t *testing.T) {
Status string `json:"Status,omitempty"`
} `json:"DomainDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "myapp", descResp.DomainDescription.Domain)
assert.Equal(t, "ACTIVE", descResp.DomainDescription.Status)
@@ -475,6 +476,6 @@ func TestDomain_FullCRUDLifecycle(t *testing.T) {
Status string `json:"Status,omitempty"`
} `json:"DomainDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &afterDeleteResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &afterDeleteResp, jsonx.V1()))
assert.Empty(t, afterDeleteResp.DomainDescription.Domain)
}
diff --git a/services/cognitoidp/federation_providers_test.go b/services/cognitoidp/federation_providers_test.go
index 70be4d4e4c..2ae4ee5831 100644
--- a/services/cognitoidp/federation_providers_test.go
+++ b/services/cognitoidp/federation_providers_test.go
@@ -5,7 +5,8 @@ import (
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"encoding/pem"
"net/http"
"net/http/httptest"
@@ -16,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -48,7 +50,10 @@ func newOAuth2OnlyIDP(t *testing.T, profile map[string]any) *oauth2OnlyIDP {
return
}
- _ = json.NewEncoder(w).Encode(map[string]string{"access_token": "opaque-token", "token_type": "bearer"})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]string{"access_token": "opaque-token", "token_type": "bearer"},
+ )
case "/me":
if r.Header.Get("Authorization") != "Bearer opaque-token" {
http.Error(w, "unauthorized", http.StatusUnauthorized)
@@ -56,7 +61,7 @@ func newOAuth2OnlyIDP(t *testing.T, profile map[string]any) *oauth2OnlyIDP {
return
}
- _ = json.NewEncoder(w).Encode(idp.profile)
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), idp.profile)
default:
http.NotFound(w, r)
}
diff --git a/services/cognitoidp/groups_membership_test.go b/services/cognitoidp/groups_membership_test.go
index 25c01a9d76..47843fd197 100644
--- a/services/cognitoidp/groups_membership_test.go
+++ b/services/cognitoidp/groups_membership_test.go
@@ -1,13 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -236,7 +237,7 @@ func TestHandler_Groups_CRUD(t *testing.T) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "grp-pool"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolResp["UserPool"]["Id"].(string)
// Create a group.
@@ -283,7 +284,7 @@ func TestHandler_Groups_CRUD(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec2.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp, jsonx.V1()))
groups := listResp["Groups"].([]any)
assert.Empty(t, groups)
}
@@ -334,7 +335,7 @@ func TestHandler_AdminGroupMembership(t *testing.T) {
// Set up pool, user, and group.
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "grp-pool"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolResp["UserPool"]["Id"].(string)
doCognitoRequest(t, h, "AdminCreateUser", map[string]any{
@@ -405,7 +406,7 @@ func TestHandler_AdminGroupMembership(t *testing.T) {
})
assert.Equal(t, http.StatusOK, listRec2.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp, jsonx.V1()))
groups := listResp["Groups"].([]any)
assert.Empty(t, groups)
}
@@ -419,7 +420,7 @@ func TestSortedListGroups(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "sorted-groups-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
for _, name := range []string{"zeta", "admin", "moderator"} {
@@ -433,7 +434,7 @@ func TestSortedListGroups(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
groups := listResp["Groups"].([]any)
require.Len(t, groups, 3)
assert.Equal(t, "admin", groups[0].(map[string]any)["GroupName"])
@@ -447,7 +448,7 @@ func TestSortedAdminListGroupsForUser(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "algfu-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
for _, g := range []string{"zeta", "alpha", "beta"} {
@@ -478,7 +479,7 @@ func TestSortedAdminListGroupsForUser(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups := resp["Groups"].([]any)
require.Len(t, groups, 3)
assert.Equal(t, "alpha", groups[0].(map[string]any)["GroupName"])
@@ -537,7 +538,7 @@ func TestAccessTokenGroupsClaim(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
authResult := resp["AuthenticationResult"].(map[string]any)
accessToken := authResult["AccessToken"].(string)
@@ -593,7 +594,7 @@ func TestAdminListGroupsForUser_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code, "body: %s", rec1.Body)
var page1 listOut
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1, jsonx.V1()))
require.Len(t, page1.Groups, 2)
require.NotEmpty(t, page1.NextToken, "first page must return a cursor when more groups remain")
@@ -606,7 +607,7 @@ func TestAdminListGroupsForUser_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code, "body: %s", rec2.Body)
var page2 listOut
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
require.Len(t, page2.Groups, 1)
require.Empty(t, page2.NextToken)
diff --git a/services/cognitoidp/groups_test.go b/services/cognitoidp/groups_test.go
index 90926953b9..bb35148c7f 100644
--- a/services/cognitoidp/groups_test.go
+++ b/services/cognitoidp/groups_test.go
@@ -1,7 +1,7 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -186,7 +187,7 @@ func TestGroup_LastModifiedDate_Present(t *testing.T) {
LastModifiedDate float64 `json:"LastModifiedDate,omitempty"`
} `json:"Group"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.Group)
assert.Greater(t, out.Group.LastModifiedDate, float64(0), "CreateGroup must include LastModifiedDate")
@@ -202,7 +203,7 @@ func TestGroup_LastModifiedDate_Present(t *testing.T) {
LastModifiedDate float64 `json:"LastModifiedDate,omitempty"`
} `json:"Group"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.Group)
assert.Greater(t, out.Group.LastModifiedDate, float64(0), "GetGroup must include LastModifiedDate")
@@ -219,7 +220,7 @@ func TestGroup_LastModifiedDate_Present(t *testing.T) {
LastModifiedDate float64 `json:"LastModifiedDate,omitempty"`
} `json:"Group"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.Group)
assert.Greater(t, out.Group.LastModifiedDate, float64(0), "UpdateGroup must include LastModifiedDate")
@@ -235,7 +236,7 @@ func TestGroup_LastModifiedDate_Present(t *testing.T) {
LastModifiedDate float64 `json:"LastModifiedDate,omitempty"`
} `json:"Groups"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Groups, 1)
assert.Greater(
t,
@@ -273,7 +274,7 @@ func TestGroup_Create_WithRoleArn(t *testing.T) {
CreationDate float64 `json:"CreationDate,omitempty"`
} `json:"Group"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.Group)
assert.Equal(t, "admins", out.Group.GroupName)
assert.Equal(t, roleArn, out.Group.RoleArn)
@@ -310,7 +311,7 @@ func TestGroup_Update_WithRoleArn(t *testing.T) {
Precedence int32 `json:"Precedence,omitempty"`
} `json:"Group"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.Group)
assert.Equal(t, roleArn, out.Group.RoleArn)
assert.Equal(t, int32(10), out.Group.Precedence)
@@ -344,7 +345,7 @@ func TestGroup_GetGroup_WithRoleArn(t *testing.T) {
Precedence int32 `json:"Precedence,omitempty"`
} `json:"Group"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.Group)
assert.Equal(t, "viewers", out.Group.GroupName)
assert.Equal(t, roleArn, out.Group.RoleArn)
@@ -378,7 +379,7 @@ func TestGroup_ListGroups_Pagination(t *testing.T) {
GroupName string `json:"GroupName,omitempty"`
} `json:"Groups"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
assert.Len(t, page1.Groups, 3)
assert.NotEmpty(t, page1.NextToken)
@@ -396,7 +397,7 @@ func TestGroup_ListGroups_Pagination(t *testing.T) {
GroupName string `json:"GroupName,omitempty"`
} `json:"Groups"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
assert.Len(t, page2.Groups, 2)
assert.Empty(t, page2.NextToken)
}
@@ -439,7 +440,7 @@ func TestGroup_ListUsersInGroup_Pagination(t *testing.T) {
Username string `json:"Username,omitempty"`
} `json:"Users"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
assert.Len(t, page1.Users, 2)
assert.NotEmpty(t, page1.NextToken)
@@ -458,7 +459,7 @@ func TestGroup_ListUsersInGroup_Pagination(t *testing.T) {
Username string `json:"Username,omitempty"`
} `json:"Users"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
assert.Len(t, page2.Users, 2)
assert.Empty(t, page2.NextToken)
}
diff --git a/services/cognitoidp/handler.go b/services/cognitoidp/handler.go
index 6a8436f438..9a42222061 100644
--- a/services/cognitoidp/handler.go
+++ b/services/cognitoidp/handler.go
@@ -2,7 +2,7 @@ package cognitoidp
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -13,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -321,7 +322,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
Username string `json:"Username,omitempty"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if req.UserPoolID != "" {
return req.UserPoolID
@@ -421,7 +422,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
@@ -483,11 +484,11 @@ func resolveErrorType(err error) (string, int) {
}
}
- if _, ok := errors.AsType[*json.SyntaxError](err); ok {
+ if _, ok := jsonx.SyntaxError(err); ok {
return errInvalidParameterException, http.StatusBadRequest
}
- if _, ok := errors.AsType[*json.UnmarshalTypeError](err); ok {
+ if _, ok := jsonx.TypeError(err); ok {
return errInvalidParameterException, http.StatusBadRequest
}
diff --git a/services/cognitoidp/handler_auth_challenges_test.go b/services/cognitoidp/handler_auth_challenges_test.go
index 9f4e911a55..7397225c0a 100644
--- a/services/cognitoidp/handler_auth_challenges_test.go
+++ b/services/cognitoidp/handler_auth_challenges_test.go
@@ -1,13 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -153,7 +154,7 @@ func TestHandler_AdminRespondToAuthChallenge(t *testing.T) {
var authResp struct {
Session string `json:"Session,omitempty"`
}
- require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp))
+ require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp, jsonx.V1()))
session = authResp.Session
}
@@ -234,7 +235,7 @@ func TestHandler_RespondToAuthChallenge_NewPassword(t *testing.T) {
Session string `json:"Session,omitempty"`
}
- require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp))
+ require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp, jsonx.V1()))
session = authResp.Session
}
@@ -259,7 +260,7 @@ func TestHandler_ForgotPasswordConfirmForgotPassword(t *testing.T) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "fp-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -267,7 +268,7 @@ func TestHandler_ForgotPasswordConfirmForgotPassword(t *testing.T) {
"ClientName": "fp-client",
})
var clientResp map[string]any
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
clientID := clientResp["UserPoolClient"].(map[string]any)["ClientId"].(string)
doCognitoRequest(t, h, "SignUp", map[string]any{
@@ -288,7 +289,7 @@ func TestHandler_ForgotPasswordConfirmForgotPassword(t *testing.T) {
assert.Equal(t, http.StatusOK, fpRec.Code)
var fpResp map[string]any
- require.NoError(t, json.Unmarshal(fpRec.Body.Bytes(), &fpResp))
+ require.NoError(t, json.Unmarshal(fpRec.Body.Bytes(), &fpResp, jsonx.V1()))
details := fpResp["CodeDeliveryDetails"].(map[string]any)
code := details["ConfirmationCode"].(string)
require.NotEmpty(t, code)
@@ -330,7 +331,7 @@ func TestHandler_GetUser_ChangePassword(t *testing.T) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "gu-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -338,7 +339,7 @@ func TestHandler_GetUser_ChangePassword(t *testing.T) {
"ClientName": "gu-client",
})
var clientResp map[string]any
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
clientID := clientResp["UserPoolClient"].(map[string]any)["ClientId"].(string)
doCognitoRequest(t, h, "SignUp", map[string]any{
@@ -366,7 +367,7 @@ func TestHandler_GetUser_ChangePassword(t *testing.T) {
assert.Equal(t, http.StatusOK, authRec.Code)
var authResp map[string]any
- require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp))
+ require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp, jsonx.V1()))
authResult := authResp["AuthenticationResult"].(map[string]any)
accessToken := authResult["AccessToken"].(string)
@@ -377,7 +378,7 @@ func TestHandler_GetUser_ChangePassword(t *testing.T) {
assert.Equal(t, http.StatusOK, guRec.Code)
var guResp map[string]any
- require.NoError(t, json.Unmarshal(guRec.Body.Bytes(), &guResp))
+ require.NoError(t, json.Unmarshal(guRec.Body.Bytes(), &guResp, jsonx.V1()))
assert.Equal(t, "guuser", guResp["Username"])
// ChangePassword with wrong old password must fail.
@@ -497,7 +498,7 @@ func TestForgotPassword_UserStateValidation(t *testing.T) {
if tt.wantType != "" {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
}
})
@@ -558,7 +559,7 @@ func TestResendConfirmationCodeAlreadyConfirmed(t *testing.T) {
if tt.wantType != "" {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
}
})
@@ -607,7 +608,7 @@ func TestConfirmForgotPasswordExpiryCheckedFirst(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var fpResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fpResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fpResp, jsonx.V1()))
code := tt.useCode
if code == "" {
@@ -626,7 +627,7 @@ func TestConfirmForgotPasswordExpiryCheckedFirst(t *testing.T) {
if tt.wantType != "" {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(confirmRec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(confirmRec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
}
})
diff --git a/services/cognitoidp/handler_auth_flows_test.go b/services/cognitoidp/handler_auth_flows_test.go
index 3b46f04439..8e717ea352 100644
--- a/services/cognitoidp/handler_auth_flows_test.go
+++ b/services/cognitoidp/handler_auth_flows_test.go
@@ -1,13 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -32,7 +33,7 @@ func TestHandler_SignUp_PolicyEnforced(t *testing.T) {
ID string `json:"Id,omitempty"`
} `json:"UserPool"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &poolResp, jsonx.V1()))
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
"UserPoolId": poolResp.UserPool.ID,
@@ -45,7 +46,7 @@ func TestHandler_SignUp_PolicyEnforced(t *testing.T) {
ClientID string `json:"ClientId,omitempty"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
clientID := clientResp.UserPoolClient.ClientID
rec := doCognitoRequest(t, h, "SignUp", map[string]any{
@@ -100,7 +101,7 @@ func TestSignUp_ConfirmSignUp(t *testing.T) {
ID string `json:"Id"`
} `json:"UserPool"`
}
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp.UserPool.ID
if tt.policy != nil {
@@ -121,7 +122,7 @@ func TestSignUp_ConfirmSignUp(t *testing.T) {
ClientID string `json:"ClientId"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
clientID := clientResp.UserPoolClient.ClientID
rec := doCognitoRequest(t, h, "SignUp", map[string]any{
@@ -153,7 +154,7 @@ func TestHandler_SignUp(t *testing.T) {
setup: func(h *cognitoidp.Handler) string {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolResp["UserPool"]["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -161,7 +162,7 @@ func TestHandler_SignUp(t *testing.T) {
"ClientName": "c",
})
var clientResp map[string]map[string]any
- _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp)
+ _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1())
return clientResp["UserPoolClient"]["ClientId"].(string)
},
@@ -225,7 +226,7 @@ func TestHandler_ConfirmSignUp(t *testing.T) {
setup: func(h *cognitoidp.Handler) (string, string, string) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolResp["UserPool"]["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -233,7 +234,7 @@ func TestHandler_ConfirmSignUp(t *testing.T) {
"ClientName": "c",
})
var clientResp map[string]map[string]any
- _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp)
+ _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1())
clientID := clientResp["UserPoolClient"]["ClientId"].(string)
signupRec := doCognitoRequest(t, h, "SignUp", map[string]any{
@@ -245,7 +246,7 @@ func TestHandler_ConfirmSignUp(t *testing.T) {
// Extract confirm code from the CodeDeliveryDetails in the SignUp response.
var signupResp map[string]any
- _ = json.Unmarshal(signupRec.Body.Bytes(), &signupResp)
+ _ = json.Unmarshal(signupRec.Body.Bytes(), &signupResp, jsonx.V1())
code := ""
if details, ok := signupResp["CodeDeliveryDetails"].(map[string]any); ok {
code, _ = details["ConfirmationCode"].(string)
@@ -260,7 +261,7 @@ func TestHandler_ConfirmSignUp(t *testing.T) {
setup: func(h *cognitoidp.Handler) (string, string, string) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolResp["UserPool"]["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -268,7 +269,7 @@ func TestHandler_ConfirmSignUp(t *testing.T) {
"ClientName": "c",
})
var clientResp map[string]map[string]any
- _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp)
+ _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1())
return clientResp["UserPoolClient"]["ClientId"].(string), "nobody", "123456"
},
@@ -308,7 +309,7 @@ func TestHandler_InitiateAuth(t *testing.T) {
setup: func(h *cognitoidp.Handler) (string, string) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolResp["UserPool"]["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -316,7 +317,7 @@ func TestHandler_InitiateAuth(t *testing.T) {
"ClientName": "c",
})
var clientResp map[string]map[string]any
- _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp)
+ _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1())
clientID := clientResp["UserPoolClient"]["ClientId"].(string)
signupRec := doCognitoRequest(t, h, "SignUp", map[string]any{
@@ -328,7 +329,7 @@ func TestHandler_InitiateAuth(t *testing.T) {
// Extract the confirmation code from the SignUp response.
var signupResp map[string]any
- _ = json.Unmarshal(signupRec.Body.Bytes(), &signupResp)
+ _ = json.Unmarshal(signupRec.Body.Bytes(), &signupResp, jsonx.V1())
code := ""
if details, ok := signupResp["CodeDeliveryDetails"].(map[string]any); ok {
code, _ = details["ConfirmationCode"].(string)
@@ -352,7 +353,7 @@ func TestHandler_InitiateAuth(t *testing.T) {
setup: func(h *cognitoidp.Handler) (string, string) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolResp["UserPool"]["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -360,7 +361,7 @@ func TestHandler_InitiateAuth(t *testing.T) {
"ClientName": "c",
})
var clientResp map[string]map[string]any
- _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp)
+ _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1())
clientID := clientResp["UserPoolClient"]["ClientId"].(string)
signupRec := doCognitoRequest(t, h, "SignUp", map[string]any{
@@ -372,7 +373,7 @@ func TestHandler_InitiateAuth(t *testing.T) {
// Extract the confirmation code from the SignUp response.
var signupResp map[string]any
- _ = json.Unmarshal(signupRec.Body.Bytes(), &signupResp)
+ _ = json.Unmarshal(signupRec.Body.Bytes(), &signupResp, jsonx.V1())
code := ""
if details, ok := signupResp["CodeDeliveryDetails"].(map[string]any); ok {
code, _ = details["ConfirmationCode"].(string)
@@ -431,7 +432,7 @@ func TestHandler_AdminInitiateAuth(t *testing.T) {
setup: func(h *cognitoidp.Handler) (string, string, string) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolResp["UserPool"]["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -439,7 +440,7 @@ func TestHandler_AdminInitiateAuth(t *testing.T) {
"ClientName": "c",
})
var clientResp map[string]map[string]any
- _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp)
+ _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1())
clientID := clientResp["UserPoolClient"]["ClientId"].(string)
doCognitoRequest(t, h, "AdminCreateUser", map[string]any{
@@ -495,7 +496,7 @@ func TestHandler_AdminConfirmSignUp(t *testing.T) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "admin-confirm-pool"})
var poolData map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolData)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolData, jsonx.V1())
poolID := poolData["UserPool"]["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -503,7 +504,7 @@ func TestHandler_AdminConfirmSignUp(t *testing.T) {
"ClientName": "c",
})
var clientData map[string]map[string]any
- _ = json.Unmarshal(clientRec.Body.Bytes(), &clientData)
+ _ = json.Unmarshal(clientRec.Body.Bytes(), &clientData, jsonx.V1())
clientID := clientData["UserPoolClient"]["ClientId"].(string)
signupRec := doCognitoRequest(t, h, "SignUp", map[string]any{
@@ -552,7 +553,7 @@ func TestAdminNoSRPAuth(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code, "ADMIN_NO_SRP_AUTH should succeed")
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasAuth := resp["AuthenticationResult"]
assert.True(t, hasAuth, "must have AuthenticationResult")
}
diff --git a/services/cognitoidp/handler_auth_tokens_test.go b/services/cognitoidp/handler_auth_tokens_test.go
index b0d1bc8e1e..30aa011e0c 100644
--- a/services/cognitoidp/handler_auth_tokens_test.go
+++ b/services/cognitoidp/handler_auth_tokens_test.go
@@ -1,12 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_SecretHash_InitiateAuth_Via_HTTP(t *testing.T) {
@@ -23,7 +25,7 @@ func TestHandler_SecretHash_InitiateAuth_Via_HTTP(t *testing.T) {
ID string `json:"Id,omitempty"`
} `json:"UserPool"`
}
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp.UserPool.ID
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -39,7 +41,7 @@ func TestHandler_SecretHash_InitiateAuth_Via_HTTP(t *testing.T) {
ClientSecret string `json:"ClientSecret,omitempty"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
clientID := clientResp.UserPoolClient.ClientID
secret := clientResp.UserPoolClient.ClientSecret
require.NotEmpty(t, secret)
@@ -56,7 +58,7 @@ func TestHandler_SecretHash_InitiateAuth_Via_HTTP(t *testing.T) {
var signUpResp struct {
CodeDeliveryDetails map[string]string `json:"CodeDeliveryDetails,omitempty"`
}
- require.NoError(t, json.Unmarshal(signUpRec.Body.Bytes(), &signUpResp))
+ require.NoError(t, json.Unmarshal(signUpRec.Body.Bytes(), &signUpResp, jsonx.V1()))
code := signUpResp.CodeDeliveryDetails["ConfirmationCode"]
confirmRec := doCognitoRequest(t, h, "ConfirmSignUp", map[string]any{
@@ -147,7 +149,7 @@ func TestTokenExpiryFor_Backend(t *testing.T) {
RefreshTokenValidity int32 `json:"RefreshTokenValidity"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Equal(t, tt.validity, createResp.UserPoolClient.AccessTokenValidity)
assert.Equal(t, tt.unit, createResp.UserPoolClient.TokenValidityUnits[unitKey])
})
@@ -251,12 +253,12 @@ func TestHandler_RevokeToken(t *testing.T) {
// Set up pool, client and confirmed user.
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "revoke-pool"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolRec.Body.String()
_ = poolID
var poolData map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolData)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolData, jsonx.V1())
pID := poolData["UserPool"]["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -264,7 +266,7 @@ func TestHandler_RevokeToken(t *testing.T) {
"ClientName": "revoke-client",
})
var clientData map[string]map[string]any
- _ = json.Unmarshal(clientRec.Body.Bytes(), &clientData)
+ _ = json.Unmarshal(clientRec.Body.Bytes(), &clientData, jsonx.V1())
clientID := clientData["UserPoolClient"]["ClientId"].(string)
doCognitoRequest(t, h, "AdminCreateUser", map[string]any{
@@ -292,7 +294,7 @@ func TestHandler_RevokeToken(t *testing.T) {
require.Equal(t, http.StatusOK, authRec.Code)
var authData map[string]map[string]any
- require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authData))
+ require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authData, jsonx.V1()))
refreshToken := authData["AuthenticationResult"]["RefreshToken"].(string)
// RevokeToken should succeed (200).
diff --git a/services/cognitoidp/handler_test.go b/services/cognitoidp/handler_test.go
index a2098b0f0a..6410a5cd4e 100644
--- a/services/cognitoidp/handler_test.go
+++ b/services/cognitoidp/handler_test.go
@@ -2,7 +2,7 @@ package cognitoidp_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -191,7 +192,7 @@ func TestHandler_JWKS(t *testing.T) {
setup: func(h *cognitoidp.Handler) string {
rec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var resp map[string]map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
return resp["UserPool"]["Id"].(string)
},
@@ -354,7 +355,7 @@ func TestReset(t *testing.T) {
ID string `json:"Id,omitempty"`
} `json:"UserPool"`
}
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp.UserPool.ID
assert.Equal(t, 1, b.UserPoolCount())
@@ -423,7 +424,7 @@ func TestNonNilSlices(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "nil-slices-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
// Empty ListUsers must return [] not null.
@@ -484,7 +485,7 @@ func TestExportCountHelpers(t *testing.T) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "count-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
assert.Equal(t, 1, h.Backend.UserPoolCount())
@@ -593,7 +594,7 @@ func TestHandler_ProvisionedLimits(t *testing.T) {
}
var resp provisionedLimitResp
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "API_CATEGORY", resp.Limit.LimitDefinition.LimitClass)
assert.EqualValues(t, 120, resp.Limit.FreeLimitValue)
@@ -627,7 +628,7 @@ func TestHandler_ProvisionedLimits_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp provisionedLimitResp
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
assert.EqualValues(t, 200, resp.Limit.ProvisionedLimitValue)
assert.EqualValues(t, 50, resp.Limit.FreeLimitValue)
}
diff --git a/services/cognitoidp/handler_users_lifecycle_test.go b/services/cognitoidp/handler_users_lifecycle_test.go
index 45ee339057..c330269275 100644
--- a/services/cognitoidp/handler_users_lifecycle_test.go
+++ b/services/cognitoidp/handler_users_lifecycle_test.go
@@ -1,7 +1,7 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -25,7 +26,7 @@ func TestHandler_AdminGetUser(t *testing.T) {
setup: func(h *cognitoidp.Handler) (string, string) {
rec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var resp map[string]map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
poolID := resp["UserPool"]["Id"].(string)
doCognitoRequest(t, h, "AdminCreateUser", map[string]any{
@@ -43,7 +44,7 @@ func TestHandler_AdminGetUser(t *testing.T) {
setup: func(h *cognitoidp.Handler) (string, string) {
rec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var resp map[string]map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
return resp["UserPool"]["Id"].(string), "nobody"
},
@@ -95,7 +96,7 @@ func TestHandler_ListUsers(t *testing.T) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "test-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -103,7 +104,7 @@ func TestHandler_ListUsers(t *testing.T) {
"ClientName": "test-client",
})
var clientResp map[string]any
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
clientID := clientResp["UserPoolClient"].(map[string]any)["ClientId"].(string)
if tt.wantCount > 0 {
@@ -120,7 +121,7 @@ func TestHandler_ListUsers(t *testing.T) {
assert.Equal(t, tt.wantHTTP, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
users := resp["Users"].([]any)
assert.Len(t, users, tt.wantCount)
})
@@ -155,7 +156,7 @@ func TestHandler_AdminDeleteUser(t *testing.T) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "test-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -163,7 +164,7 @@ func TestHandler_AdminDeleteUser(t *testing.T) {
"ClientName": "test-client",
})
var clientResp map[string]any
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
clientID := clientResp["UserPoolClient"].(map[string]any)["ClientId"].(string)
// Create the user to delete in the first case.
@@ -224,7 +225,7 @@ func TestHandler_AdminDisableEnableUser(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "dis-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
username := "dis-user"
@@ -252,7 +253,7 @@ func TestHandler_AdminDisableUser_BlocksAuth(t *testing.T) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "block-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -260,7 +261,7 @@ func TestHandler_AdminDisableUser_BlocksAuth(t *testing.T) {
"ClientName": "block-client",
})
var clientResp map[string]any
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
clientID := clientResp["UserPoolClient"].(map[string]any)["ClientId"].(string)
doCognitoRequest(t, h, "AdminCreateUser", map[string]any{
@@ -341,7 +342,7 @@ func TestHandler_ListUsers_ReturnsCorrectEnabledState(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "enabled-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
const username = "state-user"
@@ -362,7 +363,7 @@ func TestHandler_ListUsers_ReturnsCorrectEnabledState(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
users := listResp["Users"].([]any)
require.Len(t, users, 1)
assert.Equal(t, tt.wantEnabled, users[0].(map[string]any)["Enabled"].(bool))
@@ -376,7 +377,7 @@ func TestSortedListUsers(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "sorted-users-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
for _, username := range []string{"zeus", "alice", "bob"} {
@@ -391,7 +392,7 @@ func TestSortedListUsers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
users := listResp["Users"].([]any)
require.Len(t, users, 3)
assert.Equal(t, "alice", users[0].(map[string]any)["Username"])
@@ -405,7 +406,7 @@ func TestAdminGetUser_IncludesEnabledAndModifiedDate(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "agu-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
doCognitoRequest(t, h, "AdminCreateUser", map[string]any{
@@ -421,7 +422,7 @@ func TestAdminGetUser_IncludesEnabledAndModifiedDate(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.True(t, resp["Enabled"].(bool))
assert.NotZero(t, resp["UserCreateDate"])
assert.NotZero(t, resp["UserLastModifiedDate"])
@@ -433,7 +434,7 @@ func TestAdminCreateUser_IncludesEnabled(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "acu-enabled-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
rec := doCognitoRequest(t, h, "AdminCreateUser", map[string]any{
@@ -444,7 +445,7 @@ func TestAdminCreateUser_IncludesEnabled(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.True(t, resp["User"].(map[string]any)["Enabled"].(bool))
}
@@ -454,7 +455,7 @@ func TestRefreshToken_DisabledUserBlocked(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "rt-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -462,7 +463,7 @@ func TestRefreshToken_DisabledUserBlocked(t *testing.T) {
"ClientName": "rt-client",
})
var clientResp map[string]any
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
clientID := clientResp["UserPoolClient"].(map[string]any)["ClientId"].(string)
doCognitoRequest(t, h, "AdminCreateUser", map[string]any{
@@ -488,7 +489,7 @@ func TestRefreshToken_DisabledUserBlocked(t *testing.T) {
require.Equal(t, http.StatusOK, authRec.Code)
var authResp map[string]any
- require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp))
+ require.NoError(t, json.Unmarshal(authRec.Body.Bytes(), &authResp, jsonx.V1()))
refreshToken := authResp["AuthenticationResult"].(map[string]any)["RefreshToken"].(string)
// Disable the user.
@@ -525,7 +526,7 @@ func TestListUsers_Pagination(t *testing.T) {
ID string `json:"Id"`
} `json:"UserPool"`
}
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp.UserPool.ID
for i := range 5 {
@@ -555,7 +556,7 @@ func TestListUsers_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp listResp
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.LessOrEqual(t, len(resp.Users), 2)
for _, u := range resp.Users {
diff --git a/services/cognitoidp/handler_users_test.go b/services/cognitoidp/handler_users_test.go
index 51b71a4727..755627cd76 100644
--- a/services/cognitoidp/handler_users_test.go
+++ b/services/cognitoidp/handler_users_test.go
@@ -1,13 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -36,7 +37,7 @@ func TestHandler_UserSRPAuth_ViaHTTP(t *testing.T) {
Session *string `json:"Session,omitempty"`
ChallengeParameters map[string]string `json:"ChallengeParameters,omitempty"`
}
- require.NoError(t, json.Unmarshal(initRec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(initRec.Body.Bytes(), &initResp, jsonx.V1()))
require.NotNil(t, initResp.ChallengeName)
assert.Equal(t, "PASSWORD_VERIFIER", *initResp.ChallengeName)
@@ -55,7 +56,7 @@ func TestHandler_UserSRPAuth_ViaHTTP(t *testing.T) {
AccessToken string `json:"AccessToken,omitempty"`
} `json:"AuthenticationResult"`
}
- require.NoError(t, json.Unmarshal(respRec.Body.Bytes(), &tokenResp))
+ require.NoError(t, json.Unmarshal(respRec.Body.Bytes(), &tokenResp, jsonx.V1()))
require.NotNil(t, tokenResp.AuthenticationResult)
assert.NotEmpty(t, tokenResp.AuthenticationResult.AccessToken)
}
@@ -81,7 +82,7 @@ func TestHandler_AdminCreateUser_PolicyEnforced(t *testing.T) {
ID string `json:"Id,omitempty"`
} `json:"UserPool"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp.UserPool.ID
rec := doCognitoRequest(t, h, "AdminCreateUser", map[string]any{
@@ -144,7 +145,7 @@ func TestAuthFlow_UserPasswordAuth(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
authResult, ok := resp["AuthenticationResult"].(map[string]any)
require.True(t, ok)
assert.NotEmpty(t, authResult["AccessToken"])
@@ -173,7 +174,7 @@ func TestAdminCreateUser(t *testing.T) {
Username string `json:"Username"`
} `json:"User"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "adminuser", resp.User.Username)
}
@@ -200,7 +201,7 @@ func TestAdminCreateUser_Suppress(t *testing.T) {
UserStatus string `json:"UserStatus,omitempty"`
} `json:"User"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.User)
assert.Equal(t, "suppressed-user", out.User.Username)
assert.Equal(t, "FORCE_CHANGE_PASSWORD", out.User.UserStatus)
@@ -234,7 +235,7 @@ func TestAdminCreateUser_Resend(t *testing.T) {
UserStatus string `json:"UserStatus,omitempty"`
} `json:"User"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.User)
assert.Equal(t, "resend-user", out.User.Username)
}
@@ -261,7 +262,7 @@ func TestAdminCreateUser_DeliveryMediums(t *testing.T) {
Username string `json:"Username,omitempty"`
} `json:"User"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.User)
assert.Equal(t, "delivery-user", out.User.Username)
}
@@ -299,7 +300,7 @@ func TestAdminSetUserPassword_Permanent(t *testing.T) {
var out struct {
UserStatus string `json:"UserStatus,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "CONFIRMED", out.UserStatus)
}
@@ -335,7 +336,7 @@ func TestAdminSetUserPassword_Temporary(t *testing.T) {
var out struct {
UserStatus string `json:"UserStatus,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "FORCE_CHANGE_PASSWORD", out.UserStatus)
}
@@ -355,7 +356,7 @@ func TestUserSettings_And_AuthFactors(t *testing.T) {
Username string `json:"Username,omitempty"`
ConfiguredUserAuthFactors []string `json:"ConfiguredUserAuthFactors,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &factorsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &factorsResp, jsonx.V1()))
assert.Equal(t, "settings-user", factorsResp.Username)
assert.Contains(t, factorsResp.ConfiguredUserAuthFactors, "PASSWORD")
assert.NotContains(t, factorsResp.ConfiguredUserAuthFactors, "SMS_OTP")
@@ -372,7 +373,7 @@ func TestUserSettings_And_AuthFactors(t *testing.T) {
rec = doCognitoRequest(t, h, "GetUserAuthFactors", map[string]any{"AccessToken": accessToken})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &factorsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &factorsResp, jsonx.V1()))
assert.Contains(t, factorsResp.ConfiguredUserAuthFactors, "SMS_OTP")
// Register a WebAuthn credential -> WEB_AUTHN factor appears too.
@@ -384,7 +385,7 @@ func TestUserSettings_And_AuthFactors(t *testing.T) {
rec = doCognitoRequest(t, h, "GetUserAuthFactors", map[string]any{"AccessToken": accessToken})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &factorsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &factorsResp, jsonx.V1()))
assert.Contains(t, factorsResp.ConfiguredUserAuthFactors, "WEB_AUTHN")
// AdminSetUserSettings — admin variant, same persisted state.
@@ -486,7 +487,7 @@ func TestHandler_AdminGetUserAuthFactors(t *testing.T) {
PreferredMfaSetting string `json:"PreferredMfaSetting,omitempty"`
UserMFASettingList []string `json:"UserMFASettingList,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, username, resp.Username)
assert.Contains(t, resp.ConfiguredUserAuthFactors, "PASSWORD")
assert.Contains(t, resp.ConfiguredUserAuthFactors, "SMS_OTP")
@@ -570,7 +571,7 @@ func TestHandler_AdminCreateUser(t *testing.T) {
setup: func(h *cognitoidp.Handler) string {
rec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var resp map[string]map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
return resp["UserPool"]["Id"].(string)
},
@@ -622,7 +623,7 @@ func TestHandler_AdminSetUserPassword(t *testing.T) {
setup: func(h *cognitoidp.Handler) (string, string) {
rec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var resp map[string]map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
poolID := resp["UserPool"]["Id"].(string)
doCognitoRequest(t, h, "AdminCreateUser", map[string]any{
diff --git a/services/cognitoidp/identity_providers_test.go b/services/cognitoidp/identity_providers_test.go
index bc4abaed74..d47f168706 100644
--- a/services/cognitoidp/identity_providers_test.go
+++ b/services/cognitoidp/identity_providers_test.go
@@ -1,13 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -48,7 +49,7 @@ func TestCreateIdentityProvider_Duplicate_ReturnsDuplicateProviderException(t *t
var errResp struct {
Code string `json:"__type,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "DuplicateProviderException", errResp.Code)
} else {
b := newTestBackend()
@@ -123,7 +124,7 @@ func TestUpdateIdentityProvider_ReplacesProviderDetails(t *testing.T) {
ProviderDetails map[string]string `json:"ProviderDetails,omitempty"`
} `json:"IdentityProvider"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.IdentityProvider)
assert.Equal(t, "https://new.example.com/metadata", out.IdentityProvider.ProviderDetails["MetadataURL"])
@@ -195,7 +196,7 @@ func TestIdentityProvider_SAML_WithAttributeMapping(t *testing.T) {
CreationDate float64 `json:"CreationDate,omitempty"`
} `json:"IdentityProvider"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
require.NotNil(t, createOut.IdentityProvider)
assert.Equal(t, "MySAML", createOut.IdentityProvider.ProviderName)
assert.Equal(t, "SAML", createOut.IdentityProvider.ProviderType)
@@ -235,7 +236,7 @@ func TestIdentityProvider_OIDC_WithScopes(t *testing.T) {
ProviderType string `json:"ProviderType,omitempty"`
} `json:"IdentityProvider"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.IdentityProvider)
assert.Equal(t, "MyOIDC", out.IdentityProvider.ProviderName)
assert.Equal(t, "email", out.IdentityProvider.AttributeMapping["email"])
@@ -280,7 +281,7 @@ func TestIdentityProvider_Social_Google(t *testing.T) {
ProviderDetails map[string]string `json:"ProviderDetails,omitempty"`
} `json:"IdentityProvider"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut, jsonx.V1()))
require.NotNil(t, descOut.IdentityProvider)
assert.Equal(t, "email", descOut.IdentityProvider.AttributeMapping["email"])
assert.Equal(t, "google-app-id", descOut.IdentityProvider.ProviderDetails["client_id"])
@@ -324,7 +325,7 @@ func TestIdentityProvider_Update_AttributeMapping(t *testing.T) {
IdpIdentifiers []string `json:"IdpIdentifiers,omitempty"`
} `json:"IdentityProvider"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updOut, jsonx.V1()))
require.NotNil(t, updOut.IdentityProvider)
assert.Equal(t, "id", updOut.IdentityProvider.AttributeMapping["username"])
assert.Contains(t, updOut.IdentityProvider.IdpIdentifiers, "facebook.com")
@@ -364,7 +365,7 @@ func TestIdentityProvider_GetByIdentifier(t *testing.T) {
CreationDate float64 `json:"CreationDate,omitempty"`
} `json:"IdentityProvider"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.IdentityProvider)
assert.Equal(t, "LoginWithAmazon", out.IdentityProvider.ProviderName)
// AttributeMapping/IdpIdentifiers/CreationDate only appear on the "Full"
@@ -414,7 +415,7 @@ func TestIdentityProvider_List_WithTimestamps(t *testing.T) {
LastModifiedDate float64 `json:"LastModifiedDate,omitempty"`
} `json:"Providers"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Providers, 3)
for _, p := range out.Providers {
@@ -544,7 +545,7 @@ func TestIdentityProvider_CRUD(t *testing.T) {
ProviderType string `json:"ProviderType,omitempty"`
} `json:"IdentityProvider"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Equal(t, "Google", createResp.IdentityProvider.ProviderName)
assert.Equal(t, "Google", createResp.IdentityProvider.ProviderType)
@@ -573,7 +574,7 @@ func TestIdentityProvider_CRUD(t *testing.T) {
ProviderName string `json:"ProviderName,omitempty"`
} `json:"Providers"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Providers, 1)
assert.Equal(t, "Google", listResp.Providers[0].ProviderName)
@@ -597,7 +598,7 @@ func TestIdentityProvider_CRUD(t *testing.T) {
"UserPoolId": poolID,
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Empty(t, listResp.Providers)
}
@@ -689,7 +690,7 @@ func TestHandler_AdminDisableProviderForUser(t *testing.T) {
if poolID == "" {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "provider-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID = poolResp["UserPool"].(map[string]any)["Id"].(string)
createProviderViaHandler(t, h, poolID, "Google", "Google")
_, err := h.Backend.CreateFederatedUserForTest(poolID, "Google", "google-123")
@@ -743,7 +744,7 @@ func TestListIdentityProviders_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code, "body: %s", rec1.Body)
var page1 listOut
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1, jsonx.V1()))
require.Len(t, page1.Providers, 2)
require.NotEmpty(t, page1.NextToken, "first page must return a cursor when more providers remain")
@@ -755,7 +756,7 @@ func TestListIdentityProviders_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code, "body: %s", rec2.Body)
var page2 listOut
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
require.Len(t, page2.Providers, 1)
require.Empty(t, page2.NextToken)
diff --git a/services/cognitoidp/input_validation.go b/services/cognitoidp/input_validation.go
index 161b59f11b..dd96df3a15 100644
--- a/services/cognitoidp/input_validation.go
+++ b/services/cognitoidp/input_validation.go
@@ -1,9 +1,11 @@
package cognitoidp
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -24,7 +26,7 @@ type commonIdentifiers struct {
// validateCommonIdentifiers applies the SDK's userPoolId/clientId pattern constraints before dispatch.
func validateCommonIdentifiers(body []byte) error {
var ids commonIdentifiers
- if err := json.Unmarshal(body, &ids); err != nil {
+ if err := json.Unmarshal(body, &ids, jsonx.V1()); err != nil {
return nil //nolint:nilerr // malformed bodies are reported by the op's own decoder
}
diff --git a/services/cognitoidp/input_validation_test.go b/services/cognitoidp/input_validation_test.go
index 213515aee1..275586dc55 100644
--- a/services/cognitoidp/input_validation_test.go
+++ b/services/cognitoidp/input_validation_test.go
@@ -1,12 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestInputValidation_ErrorTypes(t *testing.T) {
@@ -80,7 +82,7 @@ func TestInputValidation_ErrorTypes(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tc.wantType, errResp.Type)
})
}
diff --git a/services/cognitoidp/mfa_test.go b/services/cognitoidp/mfa_test.go
index db8ebff292..297b31e365 100644
--- a/services/cognitoidp/mfa_test.go
+++ b/services/cognitoidp/mfa_test.go
@@ -1,7 +1,7 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -116,7 +117,7 @@ func TestHandler_GetUser_WithMFAFields(t *testing.T) {
PreferredMfaSetting string `json:"PreferredMfaSetting,omitempty"`
UserMFASettingList []string `json:"UserMFASettingList,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, []string{"SOFTWARE_TOKEN_MFA"}, resp.UserMFASettingList)
assert.Equal(t, "SOFTWARE_TOKEN_MFA", resp.PreferredMfaSetting)
}
@@ -140,7 +141,7 @@ func TestHandler_RespondToAuthChallenge_SMSMFAFlow(t *testing.T) {
var assocResp struct {
SecretCode string `json:"SecretCode,omitempty"`
}
- require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp))
+ require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp, jsonx.V1()))
require.NotEmpty(t, assocResp.SecretCode)
setupCode, err := cognitoidp.GenerateTOTPCode(assocResp.SecretCode, time.Now())
@@ -173,7 +174,7 @@ func TestHandler_RespondToAuthChallenge_SMSMFAFlow(t *testing.T) {
ChallengeName *string `json:"ChallengeName,omitempty"`
Session *string `json:"Session,omitempty"`
}
- require.NoError(t, json.Unmarshal(setMFARec.Body.Bytes(), &mfaInitResp))
+ require.NoError(t, json.Unmarshal(setMFARec.Body.Bytes(), &mfaInitResp, jsonx.V1()))
require.NotNil(t, mfaInitResp.ChallengeName)
require.Equal(t, "SOFTWARE_TOKEN_MFA", *mfaInitResp.ChallengeName)
@@ -197,7 +198,7 @@ func TestHandler_RespondToAuthChallenge_SMSMFAFlow(t *testing.T) {
AccessToken string `json:"AccessToken,omitempty"`
} `json:"AuthenticationResult"`
}
- require.NoError(t, json.Unmarshal(respondRec.Body.Bytes(), &tokenResp))
+ require.NoError(t, json.Unmarshal(respondRec.Body.Bytes(), &tokenResp, jsonx.V1()))
require.NotNil(t, tokenResp.AuthenticationResult)
assert.NotEmpty(t, tokenResp.AuthenticationResult.AccessToken)
}
@@ -217,7 +218,7 @@ func TestHandler_AssociateSoftwareToken_Accurate(t *testing.T) {
var resp struct {
SecretCode string `json:"SecretCode,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.SecretCode)
assert.Greater(t, len(resp.SecretCode), 10)
}
@@ -266,7 +267,7 @@ func TestHandler_VerifySoftwareToken_Accurate(t *testing.T) {
var assocResp struct {
SecretCode string `json:"SecretCode,omitempty"`
}
- require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp))
+ require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp, jsonx.V1()))
require.NotEmpty(t, assocResp.SecretCode)
code, err := cognitoidp.GenerateTOTPCode(assocResp.SecretCode, time.Now())
@@ -281,7 +282,7 @@ func TestHandler_VerifySoftwareToken_Accurate(t *testing.T) {
var resp struct {
Status string `json:"Status,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "SUCCESS", resp.Status)
}
@@ -384,7 +385,7 @@ func TestSetGetMfaConfig_SmsMfaConfiguration(t *testing.T) {
} `json:"SmsMfaConfiguration"`
MfaConfiguration string `json:"MfaConfiguration,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &setOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &setOut, jsonx.V1()))
assert.Equal(t, "ON", setOut.MfaConfiguration)
require.NotNil(t, setOut.SmsMfaConfiguration)
assert.Equal(t, "Your code is {####}", setOut.SmsMfaConfiguration.SmsAuthenticationMessage)
@@ -403,7 +404,7 @@ func TestSetGetMfaConfig_SmsMfaConfiguration(t *testing.T) {
} `json:"SmsMfaConfiguration"`
MfaConfiguration string `json:"MfaConfiguration,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "ON", getOut.MfaConfiguration)
require.NotNil(t, getOut.SmsMfaConfiguration)
assert.Equal(t, "Your code is {####}", getOut.SmsMfaConfiguration.SmsAuthenticationMessage)
@@ -430,7 +431,7 @@ func TestSetGetMfaConfig_SoftwareTokenMfa(t *testing.T) {
} `json:"SoftwareTokenMfaConfiguration"`
MfaConfiguration string `json:"MfaConfiguration,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &setOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &setOut, jsonx.V1()))
assert.Equal(t, "OPTIONAL", setOut.MfaConfiguration)
require.NotNil(t, setOut.SoftwareTokenMfaConfiguration)
assert.True(t, setOut.SoftwareTokenMfaConfiguration.Enabled)
@@ -446,7 +447,7 @@ func TestSetGetMfaConfig_SoftwareTokenMfa(t *testing.T) {
Enabled bool `json:"Enabled,omitempty"`
} `json:"SoftwareTokenMfaConfiguration"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut, jsonx.V1()))
require.NotNil(t, getOut.SoftwareTokenMfaConfiguration)
assert.True(t, getOut.SoftwareTokenMfaConfiguration.Enabled)
}
@@ -473,7 +474,7 @@ func TestSetGetMfaConfig_EmailMfa(t *testing.T) {
Subject string `json:"Subject,omitempty"`
} `json:"EmailMfaConfiguration"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &setOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &setOut, jsonx.V1()))
require.NotNil(t, setOut.EmailMfaConfiguration)
assert.Equal(t, "Your login code is {####}", setOut.EmailMfaConfiguration.Message)
assert.Equal(t, "Login verification", setOut.EmailMfaConfiguration.Subject)
@@ -701,7 +702,7 @@ func TestAdminGetUserMFAFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// Fields should be present (even if empty/nil for non-MFA user).
// The response JSON should decode without error and have the base fields.
diff --git a/services/cognitoidp/multi_region_test.go b/services/cognitoidp/multi_region_test.go
index 4188d176b6..fa12ceb066 100644
--- a/services/cognitoidp/multi_region_test.go
+++ b/services/cognitoidp/multi_region_test.go
@@ -2,7 +2,7 @@ package cognitoidp_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -54,7 +55,7 @@ func idpCall(t *testing.T, h *cognitoidp.Handler, region, op, body string) map[s
out := map[string]any{}
if rec.Body.Len() > 0 {
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
}
return out
diff --git a/services/cognitoidp/oauth_federation_backend.go b/services/cognitoidp/oauth_federation_backend.go
index b0060234ba..6aeb0115f1 100644
--- a/services/cognitoidp/oauth_federation_backend.go
+++ b/services/cognitoidp/oauth_federation_backend.go
@@ -3,7 +3,7 @@ package cognitoidp
import (
"crypto/rand"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
@@ -13,6 +13,7 @@ import (
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -308,7 +309,7 @@ func addIdentity(existing string, idp *IdentityProvider, subject, issuer string)
func parseIdentities(raw string) []identityRecord {
records := make([]identityRecord, 0, 1)
if raw != "" {
- _ = json.Unmarshal([]byte(raw), &records)
+ _ = json.Unmarshal([]byte(raw), &records, jsonx.V1())
}
return records
@@ -328,7 +329,7 @@ func appendIdentity(existing string, rec identityRecord) string {
rec.Primary = strconv.FormatBool(len(records) == 0)
}
- out, err := json.Marshal(append(records, rec))
+ out, err := json.Marshal(append(records, rec), jsonx.V1())
if err != nil {
return existing
}
@@ -345,7 +346,7 @@ func removeIdentity(existing, providerName, userID string) string {
return ""
}
- out, err := json.Marshal(records)
+ out, err := json.Marshal(records, jsonx.V1())
if err != nil {
return existing
}
diff --git a/services/cognitoidp/oauth_federation_helpers_test.go b/services/cognitoidp/oauth_federation_helpers_test.go
index 6a0912758f..697ee6539b 100644
--- a/services/cognitoidp/oauth_federation_helpers_test.go
+++ b/services/cognitoidp/oauth_federation_helpers_test.go
@@ -4,7 +4,8 @@ import (
"crypto/rand"
"crypto/rsa"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"math/big"
"net/http"
@@ -18,6 +19,7 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -62,7 +64,7 @@ func newFakeIDP(t *testing.T) *fakeIDP {
func (f *fakeIDP) serve(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/.well-known/openid-configuration":
- _ = json.NewEncoder(w).Encode(map[string]string{
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), map[string]string{
"issuer": f.srv.URL, "authorization_endpoint": f.srv.URL + "/authorize",
"token_endpoint": f.srv.URL + "/token", "jwks_uri": f.srv.URL + "/jwks",
})
@@ -72,7 +74,7 @@ func (f *fakeIDP) serve(w http.ResponseWriter, r *http.Request) {
f.token(w, r)
case "/jwks":
pub := f.key.PublicKey
- _ = json.NewEncoder(w).Encode(map[string]any{"keys": []map[string]string{{
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), map[string]any{"keys": []map[string]string{{
"kty": "RSA", "kid": fedKID, "alg": "RS256", "use": "sig",
"n": base64.RawURLEncoding.EncodeToString(pub.N.Bytes()),
"e": base64.RawURLEncoding.EncodeToString(big.NewInt(int64(pub.E)).Bytes()),
@@ -150,7 +152,7 @@ func (f *fakeIDP) token(w http.ResponseWriter, r *http.Request) {
return
}
- _ = json.NewEncoder(w).Encode(map[string]string{
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), map[string]string{
"id_token": signed, "access_token": "idp-access", "token_type": "Bearer",
})
}
diff --git a/services/cognitoidp/oauth_federation_idp.go b/services/cognitoidp/oauth_federation_idp.go
index 26d0e580a3..add496e292 100644
--- a/services/cognitoidp/oauth_federation_idp.go
+++ b/services/cognitoidp/oauth_federation_idp.go
@@ -5,7 +5,8 @@ import (
"crypto/rsa"
"crypto/subtle"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -19,6 +20,7 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -201,7 +203,11 @@ func idpFetchJSON(
return idpErrorf("%s returned status %d", req.URL.Host, resp.StatusCode)
}
- if err = json.NewDecoder(io.LimitReader(resp.Body, maxIDPDocBytes)).Decode(out); err != nil {
+ if err = json.UnmarshalDecode(
+ jsontext.NewDecoder(io.LimitReader(resp.Body, maxIDPDocBytes)),
+ out,
+ jsonx.V1(),
+ ); err != nil {
return idpErrorf("invalid JSON from %s: %v", req.URL.Host, err)
}
diff --git a/services/cognitoidp/oauth_federation_test.go b/services/cognitoidp/oauth_federation_test.go
index c6f9ac278b..b016bbe2ac 100644
--- a/services/cognitoidp/oauth_federation_test.go
+++ b/services/cognitoidp/oauth_federation_test.go
@@ -3,7 +3,7 @@ package cognitoidp_test
import (
"crypto/rand"
"crypto/rsa"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -77,7 +78,7 @@ func TestFederatedOIDCCodeFlow(t *testing.T) {
assert.Equal(t, "true", user.Attributes["email_verified"])
var stored []map[string]string
- require.NoError(t, json.Unmarshal([]byte(user.Attributes["identities"]), &stored))
+ require.NoError(t, json.Unmarshal([]byte(user.Attributes["identities"]), &stored, jsonx.V1()))
assert.Equal(t, fedSubject, stored[0]["userId"])
})
}
diff --git a/services/cognitoidp/oauth_helpers_test.go b/services/cognitoidp/oauth_helpers_test.go
index 759a5861e3..4189694c51 100644
--- a/services/cognitoidp/oauth_helpers_test.go
+++ b/services/cognitoidp/oauth_helpers_test.go
@@ -4,7 +4,7 @@ import (
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"math/big"
"net/http"
@@ -18,6 +18,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -259,7 +260,7 @@ func jsonMap(t *testing.T, body []byte) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(body, &m), string(body))
+ require.NoError(t, json.Unmarshal(body, &m, jsonx.V1()), string(body))
return m
}
@@ -277,7 +278,7 @@ func (e *oauthEnv) verifyAgainstJWKS(t *testing.T, tok string) jwt.MapClaims {
E string `json:"e"`
} `json:"keys"`
}
- require.NoError(t, json.Unmarshal(body, &set))
+ require.NoError(t, json.Unmarshal(body, &set, jsonx.V1()))
require.NotEmpty(t, set.Keys)
parsed, err := jwt.Parse(tok, func(tk *jwt.Token) (any, error) {
diff --git a/services/cognitoidp/oauth_token.go b/services/cognitoidp/oauth_token.go
index ee9aed4249..a03abeda56 100644
--- a/services/cognitoidp/oauth_token.go
+++ b/services/cognitoidp/oauth_token.go
@@ -4,7 +4,7 @@ import (
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
@@ -12,6 +12,8 @@ import (
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -339,7 +341,7 @@ func clientMetadataParam(form url.Values) (map[string]string, *oauthError) {
}
var metadata map[string]string
- if err := json.Unmarshal([]byte(raw), &metadata); err != nil {
+ if err := json.Unmarshal([]byte(raw), &metadata, jsonx.V1()); err != nil {
return nil, newOAuthError(errInvalidRequest, "aws_client_metadata must be a JSON object of strings")
}
diff --git a/services/cognitoidp/persistence.go b/services/cognitoidp/persistence.go
index 8ed0530171..3d48336470 100644
--- a/services/cognitoidp/persistence.go
+++ b/services/cognitoidp/persistence.go
@@ -4,13 +4,15 @@ import (
"context"
"crypto/rsa"
"crypto/x509"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"encoding/pem"
"errors"
"fmt"
"slices"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -133,7 +135,7 @@ type backendSnapshot struct {
TokenRevokedBeforeSeq map[string]int64 `json:"tokenRevokedBeforeSeq,omitempty"`
TokenRevokedBefore map[string]time.Time `json:"tokenRevokedBefore,omitempty"`
RevokedChains map[string]time.Time `json:"revokedChains,omitempty"`
- Tables map[string]json.RawMessage `json:"tables,omitempty"`
+ Tables map[string]jsontext.Value `json:"tables,omitempty"`
ResourceTags map[string]map[string]string `json:"resourceTags,omitempty"`
RiskConfigurations map[string]*RiskConfiguration `json:"riskConfigurations,omitempty"`
LogDeliveryConfigs map[string]*LogDeliveryConfig `json:"logDeliveryConfigs,omitempty"`
@@ -345,7 +347,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Endpoint: b.endpoint,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "cognitoidp: failed to marshal backend snapshot", "error", err)
@@ -425,7 +427,7 @@ func (b *InMemoryBackend) resetForIncompatibleSnapshotLocked() {
// every secondary index registered on it, e.g. clientsByPool/groupsByPool, so
// no separate index rebuild step is needed for these). Caller must hold b.mu
// in write mode.
-func (b *InMemoryBackend) restoreTablesLocked(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreTablesLocked(tables map[string]jsontext.Value) error {
dtoReg := store.NewRegistry()
poolDTOs := store.Register(dtoReg, "pools", store.New(poolSnapshotKeyFn))
@@ -468,7 +470,7 @@ func (b *InMemoryBackend) restoreTablesLocked(tables map[string]json.RawMessage)
// a shape from some other future change) is likewise dropped rather than
// failing the whole Restore, unlike dtoReg.RestoreAll's other tables. Caller
// must hold b.mu in write mode.
-func (b *InMemoryBackend) restoreTermsLocked(ctx context.Context, raw json.RawMessage) {
+func (b *InMemoryBackend) restoreTermsLocked(ctx context.Context, raw jsontext.Value) {
if len(raw) == 0 {
b.terms.Restore(nil)
@@ -477,7 +479,7 @@ func (b *InMemoryBackend) restoreTermsLocked(ctx context.Context, raw json.RawMe
var items []*Terms
- if err := json.Unmarshal(raw, &items); err != nil {
+ if err := json.Unmarshal(raw, &items, jsonx.V1()); err != nil {
logger.Load(ctx).WarnContext(ctx, "cognitoidp: dropping unparseable terms snapshot", "error", err)
b.terms.Restore(nil)
diff --git a/services/cognitoidp/persistence_internal_test.go b/services/cognitoidp/persistence_internal_test.go
index 3b1410442f..f0ac8b87d1 100644
--- a/services/cognitoidp/persistence_internal_test.go
+++ b/services/cognitoidp/persistence_internal_test.go
@@ -1,12 +1,15 @@
package cognitoidp
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestSnapshotRoundTrip_NonUTCZone guards against the bare-'Z' layout bug: Format's
@@ -128,19 +131,19 @@ func TestGlobalSignOut_RestoreFromV2Snapshot(t *testing.T) {
data := b.Snapshot(t.Context())
require.NotEmpty(t, data)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(data, &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(data, &raw, jsonx.V1()))
delete(raw, "tokenSeq")
delete(raw, "tokenRevokedBeforeSeq")
key := pool.ID + ":" + user.Username
- revoked, err := json.Marshal(map[string]time.Time{key: baseTime})
+ revoked, err := json.Marshal(map[string]time.Time{key: baseTime}, jsonx.V1())
require.NoError(t, err)
raw["tokenRevokedBefore"] = revoked
- raw["version"] = json.RawMessage(`2`)
+ raw["version"] = jsontext.Value(`2`)
- patched, err := json.Marshal(raw)
+ patched, err := json.Marshal(raw, jsonx.V1())
require.NoError(t, err)
b2 := NewInMemoryBackend("000000000000", "us-east-1", "http://localhost:8000")
diff --git a/services/cognitoidp/persistence_test.go b/services/cognitoidp/persistence_test.go
index 126dfcbab2..4e74060591 100644
--- a/services/cognitoidp/persistence_test.go
+++ b/services/cognitoidp/persistence_test.go
@@ -2,13 +2,14 @@ package cognitoidp_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -391,7 +392,7 @@ func TestPersistenceRoundTrip(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "persist-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -399,7 +400,7 @@ func TestPersistenceRoundTrip(t *testing.T) {
"ClientName": "persist-client",
})
var clientResp map[string]any
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
doCognitoRequest(t, h, "AdminCreateUser", map[string]any{
"UserPoolId": poolID,
@@ -427,7 +428,7 @@ func TestPersistenceRoundTrip(t *testing.T) {
"Username": "persist-user",
})
var getUserResp map[string]any
- require.NoError(t, json.Unmarshal(getUserRec.Body.Bytes(), &getUserResp))
+ require.NoError(t, json.Unmarshal(getUserRec.Body.Bytes(), &getUserResp, jsonx.V1()))
assert.False(t, getUserResp["Enabled"].(bool))
}
@@ -542,7 +543,7 @@ func TestInMemoryBackend_RestoreDropsPreRedesignTerms(t *testing.T) {
require.NotEmpty(t, data)
var raw map[string]any
- require.NoError(t, json.Unmarshal(data, &raw))
+ require.NoError(t, json.Unmarshal(data, &raw, jsonx.V1()))
tables, ok := raw["tables"].(map[string]any)
require.True(t, ok)
@@ -557,7 +558,7 @@ func TestInMemoryBackend_RestoreDropsPreRedesignTerms(t *testing.T) {
{"userPoolID": pool.ID, "text": "accept these terms"},
}
- spliced, err := json.Marshal(raw)
+ spliced, err := json.Marshal(raw, jsonx.V1())
require.NoError(t, err)
restored := newTestBackend()
diff --git a/services/cognitoidp/realclient_managed_login_and_provisioning_test.go b/services/cognitoidp/realclient_managed_login_and_provisioning_test.go
index 9a141afaa0..9878dada6c 100644
--- a/services/cognitoidp/realclient_managed_login_and_provisioning_test.go
+++ b/services/cognitoidp/realclient_managed_login_and_provisioning_test.go
@@ -1,7 +1,7 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -54,7 +55,7 @@ func decodeIdpDocument(t *testing.T, doc interface{ UnmarshalSmithyDocument(v an
var v any
require.NoError(t, doc.UnmarshalSmithyDocument(&v))
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
return string(b)
diff --git a/services/cognitoidp/regions.go b/services/cognitoidp/regions.go
index b7002fc752..4022a6fcd0 100644
--- a/services/cognitoidp/regions.go
+++ b/services/cognitoidp/regions.go
@@ -3,7 +3,7 @@ package cognitoidp
import (
"context"
"crypto/rsa"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
)
@@ -265,7 +266,7 @@ func targetHints(r *http.Request) ownerHints {
Session string `json:"Session"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return ownerHints{
poolID: req.UserPoolID, arn: req.ResourceArn, clientID: req.ClientID,
diff --git a/services/cognitoidp/resource_servers_test.go b/services/cognitoidp/resource_servers_test.go
index 2962326c0d..8be9eb75ff 100644
--- a/services/cognitoidp/resource_servers_test.go
+++ b/services/cognitoidp/resource_servers_test.go
@@ -1,13 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -66,7 +67,7 @@ func TestHandler_ResourceServers_Accurate(t *testing.T) {
} `json:"Scopes"`
} `json:"ResourceServer"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "My API", resp.ResourceServer.Name)
assert.Len(t, resp.ResourceServer.Scopes, 1)
assert.Equal(t, "read", resp.ResourceServer.Scopes[0].ScopeName)
@@ -98,7 +99,7 @@ func TestResourceServer_CRUD(t *testing.T) {
Identifier string `json:"Identifier"`
} `json:"ResourceServers"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.ResourceServers, 1)
assert.Equal(t, "https://api.example.com", listResp.ResourceServers[0].Identifier)
}
@@ -279,7 +280,7 @@ func TestHandler_ListResourceServers(t *testing.T) {
var resp struct {
ResourceServers []map[string]any `json:"ResourceServers,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.ResourceServers, tt.count)
})
}
@@ -399,7 +400,7 @@ func TestListResourceServers_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code, "body: %s", rec1.Body)
var page1 listOut
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1, jsonx.V1()))
require.Len(t, page1.ResourceServers, 2)
require.NotEmpty(t, page1.NextToken, "first page must return a cursor when more resource servers remain")
@@ -411,7 +412,7 @@ func TestListResourceServers_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code, "body: %s", rec2.Body)
var page2 listOut
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
require.Len(t, page2.ResourceServers, 1)
require.Empty(t, page2.NextToken)
diff --git a/services/cognitoidp/saml_federation_test.go b/services/cognitoidp/saml_federation_test.go
index 2d939a6e88..9b74116cda 100644
--- a/services/cognitoidp/saml_federation_test.go
+++ b/services/cognitoidp/saml_federation_test.go
@@ -10,7 +10,7 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"maps"
"math/big"
@@ -25,6 +25,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -481,7 +482,7 @@ func TestSAMLFederation(t *testing.T) {
require.Equal(t, http.StatusOK, form.StatusCode, string(body))
var tok map[string]any
- require.NoError(t, json.Unmarshal(body, &tok))
+ require.NoError(t, json.Unmarshal(body, &tok, jsonx.V1()))
claims := env.verifyAgainstJWKS(t, tok["id_token"].(string))
assert.Equal(t, fedProvider+"_"+samlNameID, claims["cognito:username"])
@@ -527,7 +528,7 @@ func TestSAMLMultiValuedAttributes(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode, string(body))
var tok map[string]any
- require.NoError(t, json.Unmarshal(body, &tok))
+ require.NoError(t, json.Unmarshal(body, &tok, jsonx.V1()))
claims := env.verifyAgainstJWKS(t, tok["id_token"].(string))
assert.Equal(t, tt.want, claims["email"])
diff --git a/services/cognitoidp/saml_slo_test.go b/services/cognitoidp/saml_slo_test.go
index 77adbc26dc..99e5094015 100644
--- a/services/cognitoidp/saml_slo_test.go
+++ b/services/cognitoidp/saml_slo_test.go
@@ -8,7 +8,7 @@ import (
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"regexp"
@@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -53,7 +54,7 @@ func (e *samlEnv) loginSAML(t *testing.T) string {
require.Equal(t, http.StatusOK, resp.StatusCode, string(body))
var tok map[string]any
- require.NoError(t, json.Unmarshal(body, &tok))
+ require.NoError(t, json.Unmarshal(body, &tok, jsonx.V1()))
return tok["access_token"].(string)
}
diff --git a/services/cognitoidp/security_config_test.go b/services/cognitoidp/security_config_test.go
index 74bf1951ce..8a7d338a2c 100644
--- a/services/cognitoidp/security_config_test.go
+++ b/services/cognitoidp/security_config_test.go
@@ -1,13 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -60,7 +61,7 @@ func TestSetRiskConfiguration_PersistsRiskExceptionConfiguration(t *testing.T) {
} `json:"RiskExceptionConfiguration"`
} `json:"RiskConfiguration"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &setOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &setOut, jsonx.V1()))
require.NotNil(t, setOut.RiskConfiguration)
require.NotNil(
t,
@@ -100,7 +101,7 @@ func TestSetRiskConfiguration_PersistsRiskExceptionConfiguration(t *testing.T) {
} `json:"RiskExceptionConfiguration"`
} `json:"RiskConfiguration"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut, jsonx.V1()))
require.NotNil(t, descOut.RiskConfiguration)
require.NotNil(
t,
@@ -138,7 +139,7 @@ func TestRiskConfiguration_CompromisedCredentials(t *testing.T) {
} `json:"CompromisedCredentialsRiskConfiguration"`
} `json:"RiskConfiguration"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &setOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &setOut, jsonx.V1()))
require.NotNil(t, setOut.RiskConfiguration)
require.NotNil(t, setOut.RiskConfiguration.CompromisedCredentialsRiskConfiguration)
assert.Contains(t, setOut.RiskConfiguration.CompromisedCredentialsRiskConfiguration.EventFilter, "SIGN_IN")
@@ -160,7 +161,7 @@ func TestRiskConfiguration_CompromisedCredentials(t *testing.T) {
} `json:"CompromisedCredentialsRiskConfiguration"`
} `json:"RiskConfiguration"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut, jsonx.V1()))
require.NotNil(t, descOut.RiskConfiguration)
require.NotNil(t, descOut.RiskConfiguration.CompromisedCredentialsRiskConfiguration)
assert.Equal(t, "BLOCK", descOut.RiskConfiguration.CompromisedCredentialsRiskConfiguration.Actions.EventAction)
@@ -226,7 +227,7 @@ func TestRiskConfiguration_AccountTakeover(t *testing.T) {
} `json:"AccountTakeoverRiskConfiguration"`
} `json:"RiskConfiguration"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.RiskConfiguration)
require.NotNil(t, out.RiskConfiguration.AccountTakeoverRiskConfiguration)
require.NotNil(t, out.RiskConfiguration.AccountTakeoverRiskConfiguration.Actions)
@@ -274,7 +275,7 @@ func TestRiskConfiguration_PerClient(t *testing.T) {
CompromisedCredentialsRiskConfiguration *struct{} `json:"CompromisedCredentialsRiskConfiguration,omitempty"`
} `json:"RiskConfiguration"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &poolOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &poolOut, jsonx.V1()))
assert.Nil(t, poolOut.RiskConfiguration.CompromisedCredentialsRiskConfiguration)
// Client-level should be set.
@@ -293,7 +294,7 @@ func TestRiskConfiguration_PerClient(t *testing.T) {
} `json:"CompromisedCredentialsRiskConfiguration"`
} `json:"RiskConfiguration"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &clientOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &clientOut, jsonx.V1()))
require.NotNil(t, clientOut.RiskConfiguration.CompromisedCredentialsRiskConfiguration)
assert.Equal(
t,
@@ -414,7 +415,7 @@ func TestSetLogDeliveryConfiguration_LogConfigurationsPersistAndEcho(t *testing.
LogConfigurations []map[string]any `json:"LogConfigurations,omitempty"`
} `json:"LogDeliveryConfiguration"`
}
- require.NoError(t, json.Unmarshal(setRec.Body.Bytes(), &setOut))
+ require.NoError(t, json.Unmarshal(setRec.Body.Bytes(), &setOut, jsonx.V1()))
require.Len(t, setOut.LogDeliveryConfiguration.LogConfigurations, 1,
"LogConfigurations is a required accepted field and must not be silently dropped",
)
@@ -428,7 +429,7 @@ func TestSetLogDeliveryConfiguration_LogConfigurationsPersistAndEcho(t *testing.
LogConfigurations []map[string]any `json:"LogConfigurations,omitempty"`
} `json:"LogDeliveryConfiguration"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
require.Len(t,
getOut.LogDeliveryConfiguration.LogConfigurations, 1, "Set must actually persist for Get to read back",
)
diff --git a/services/cognitoidp/tags_test.go b/services/cognitoidp/tags_test.go
index 4508ddee48..0edf0654ec 100644
--- a/services/cognitoidp/tags_test.go
+++ b/services/cognitoidp/tags_test.go
@@ -1,12 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestTags(t *testing.T) {
@@ -31,7 +33,7 @@ func TestTags(t *testing.T) {
var listResp struct {
Tags map[string]string `json:"Tags,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Equal(t, "prod", listResp.Tags["env"])
assert.Equal(t, "platform", listResp.Tags["team"])
@@ -49,7 +51,7 @@ func TestTags(t *testing.T) {
var afterUntagResp struct {
Tags map[string]string `json:"Tags,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &afterUntagResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &afterUntagResp, jsonx.V1()))
assert.NotContains(t, afterUntagResp.Tags, "env")
assert.Equal(t, "platform", afterUntagResp.Tags["team"])
}
diff --git a/services/cognitoidp/temp_password_sdk_test.go b/services/cognitoidp/temp_password_sdk_test.go
index d46ef744bf..e766608687 100644
--- a/services/cognitoidp/temp_password_sdk_test.go
+++ b/services/cognitoidp/temp_password_sdk_test.go
@@ -1,7 +1,8 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -9,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const tempPwd = "Tmp-Secret-9876!x"
@@ -108,7 +111,7 @@ func TestTemporaryPassword_NotReturnedByUserOps(t *testing.T) {
require.NoError(t, err)
attrs := tt.op(t, c, poolID)
- raw, err := json.Marshal(attrs)
+ raw, err := json.Marshal(attrs, jsonx.V1())
require.NoError(t, err)
assert.NotContains(t, string(raw), tempPwd)
assert.NotContains(t, attrNames(attrs), "custom:temporaryPassword")
@@ -196,25 +199,25 @@ func TestTemporaryPassword_LegacySnapshotMigrates(t *testing.T) {
})
require.NoError(t, err)
- var snap map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(h.Snapshot(t.Context()), &snap))
+ var snap map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(h.Snapshot(t.Context()), &snap, jsonx.V1()))
- var tables map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap["tables"], &tables))
+ var tables map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap["tables"], &tables, jsonx.V1()))
var users []map[string]any
- require.NoError(t, json.Unmarshal(tables["users"], &users))
+ require.NoError(t, json.Unmarshal(tables["users"], &users, jsonx.V1()))
require.Len(t, users, 1)
delete(users[0], "temporaryPassword")
users[0]["attributes"].(map[string]any)["custom:temporaryPassword"] = tempPwd
- tables["users"], err = json.Marshal(users)
+ tables["users"], err = json.Marshal(users, jsonx.V1())
require.NoError(t, err)
- snap["tables"], err = json.Marshal(tables)
+ snap["tables"], err = json.Marshal(tables, jsonx.V1())
require.NoError(t, err)
- old, err := json.Marshal(snap)
+ old, err := json.Marshal(snap, jsonx.V1())
require.NoError(t, err)
fresh := newTestHandler(t)
diff --git a/services/cognitoidp/terms_test.go b/services/cognitoidp/terms_test.go
index 95edf1255f..aca924818e 100644
--- a/services/cognitoidp/terms_test.go
+++ b/services/cognitoidp/terms_test.go
@@ -1,12 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func validTermsBody(poolID, clientID string) map[string]any {
@@ -51,7 +53,7 @@ func TestCreateTerms_Validation(t *testing.T) {
var errResp struct {
Type string `json:"__type,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp.Type)
})
}
@@ -69,7 +71,7 @@ func TestCreateTerms_UnknownClient(t *testing.T) {
var errResp struct {
Type string `json:"__type,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp.Type)
}
@@ -88,7 +90,7 @@ func TestCreateTerms_Duplicate(t *testing.T) {
var errResp struct {
Type string `json:"__type,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "TermsExistsException", errResp.Type)
}
@@ -109,7 +111,7 @@ func TestTerms_CRUD(t *testing.T) {
ClientID string `json:"ClientId,omitempty"`
} `json:"Terms"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Empty(t, listResp.Terms)
rec = doCognitoRequest(t, h, "CreateTerms", validTermsBody(poolID, clientID))
@@ -128,7 +130,7 @@ func TestTerms_CRUD(t *testing.T) {
LastModifiedDate float64 `json:"LastModifiedDate,omitempty"`
} `json:"Terms"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
require.NotEmpty(t, createResp.Terms.TermsID)
assert.Equal(t, clientID, createResp.Terms.ClientID)
assert.Equal(t, poolID, createResp.Terms.UserPoolID)
@@ -144,7 +146,7 @@ func TestTerms_CRUD(t *testing.T) {
// List returns TermsDescriptionType, which has no ClientId field on the real wire.
rec = doCognitoRequest(t, h, "ListTerms", map[string]any{"UserPoolId": poolID})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Terms, 1)
assert.Equal(t, termsID, listResp.Terms[0].TermsID)
assert.Empty(t, listResp.Terms[0].ClientID, "TermsDescriptionType has no ClientId field")
@@ -155,7 +157,7 @@ func TestTerms_CRUD(t *testing.T) {
"TermsName": "terms-of-use-v2",
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Equal(t, "terms-of-use-v2", createResp.Terms.TermsName)
assert.Equal(t, "NONE", createResp.Terms.Enforcement, "unset fields on update are left unchanged")
@@ -163,7 +165,7 @@ func TestTerms_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
rec = doCognitoRequest(t, h, "ListTerms", map[string]any{"UserPoolId": poolID})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Empty(t, listResp.Terms)
}
@@ -205,7 +207,7 @@ func TestTerms_NotFound(t *testing.T) {
var errResp struct {
Type string `json:"__type,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp.Type)
})
}
diff --git a/services/cognitoidp/testhelpers_test.go b/services/cognitoidp/testhelpers_test.go
index 3365f18849..a1487da2c7 100644
--- a/services/cognitoidp/testhelpers_test.go
+++ b/services/cognitoidp/testhelpers_test.go
@@ -5,7 +5,7 @@ import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -14,6 +14,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -28,7 +29,7 @@ func decodeJWTPayload(t *testing.T, token string) map[string]any {
require.NoError(t, err)
var claims map[string]any
- require.NoError(t, json.Unmarshal(payload, &claims))
+ require.NoError(t, json.Unmarshal(payload, &claims, jsonx.V1()))
return claims
}
@@ -79,7 +80,7 @@ func setupHandlerPoolAndClient(t *testing.T, h *cognitoidp.Handler, poolName str
ID string `json:"Id,omitempty"`
} `json:"UserPool"`
}
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp.UserPool.ID
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -93,7 +94,7 @@ func setupHandlerPoolAndClient(t *testing.T, h *cognitoidp.Handler, poolName str
ClientID string `json:"ClientId,omitempty"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
return poolID, clientResp.UserPoolClient.ClientID
}
@@ -114,7 +115,7 @@ func signUpAndConfirmViaHandler(t *testing.T, h *cognitoidp.Handler, clientID, u
CodeDeliveryDetails map[string]string `json:"CodeDeliveryDetails,omitempty"`
UserConfirmed bool `json:"UserConfirmed,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &signUpResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &signUpResp, jsonx.V1()))
if signUpResp.UserConfirmed {
return
@@ -147,7 +148,7 @@ func loginViaHandler(t *testing.T, h *cognitoidp.Handler, clientID, username str
AccessToken string `json:"AccessToken,omitempty"`
} `json:"AuthenticationResult"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotNil(t, resp.AuthenticationResult)
return resp.AuthenticationResult.AccessToken
@@ -175,7 +176,7 @@ func newTestHandler(t *testing.T) *cognitoidp.Handler {
func doCognitoRequest(t *testing.T, h *cognitoidp.Handler, action string, body any) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -214,7 +215,7 @@ func setupPoolAndClientNamed(t *testing.T, h *cognitoidp.Handler, poolName, clie
require.Equal(t, http.StatusOK, poolRec.Code)
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -224,7 +225,7 @@ func setupPoolAndClientNamed(t *testing.T, h *cognitoidp.Handler, poolName, clie
require.Equal(t, http.StatusOK, clientRec.Code)
var clientResp map[string]any
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
clientID := clientResp["UserPoolClient"].(map[string]any)["ClientId"].(string)
return poolID, clientID
diff --git a/services/cognitoidp/tokens.go b/services/cognitoidp/tokens.go
index fb1139d59a..0616fc0669 100644
--- a/services/cognitoidp/tokens.go
+++ b/services/cognitoidp/tokens.go
@@ -7,7 +7,7 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"fmt"
"math/big"
@@ -19,6 +19,8 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// rsaKeyBits is the size of the RSA key generated for JWT signing.
@@ -373,7 +375,7 @@ func (t *tokenIssuer) signIDToken(p TokenParams, now time.Time, idExpiry time.Du
if raw := p.Attributes[attrIdentities]; raw != "" {
var identities []any
- if json.Unmarshal([]byte(raw), &identities) == nil {
+ if json.Unmarshal([]byte(raw), &identities, jsonx.V1()) == nil {
idClaims[attrIdentities] = identities
}
}
@@ -547,5 +549,5 @@ func (t *tokenIssuer) ParseAccessToken(tokenString string) (jwt.MapClaims, error
// jwksResponseJSON serializes JWKSResponse as JSON bytes.
func jwksResponseJSON(r JWKSResponse) ([]byte, error) {
- return json.Marshal(r)
+ return json.Marshal(r, jsonx.V1())
}
diff --git a/services/cognitoidp/user_import_test.go b/services/cognitoidp/user_import_test.go
index eb67177334..ff5d778d40 100644
--- a/services/cognitoidp/user_import_test.go
+++ b/services/cognitoidp/user_import_test.go
@@ -1,7 +1,7 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -32,7 +33,7 @@ func TestUserImportJob_CRUD(t *testing.T) {
Status string `json:"Status,omitempty"`
} `json:"UserImportJob"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
jobID := createResp.UserImportJob.JobID
require.NotEmpty(t, jobID)
assert.Equal(t, "my-import", createResp.UserImportJob.JobName)
@@ -55,7 +56,7 @@ func TestUserImportJob_CRUD(t *testing.T) {
JobID string `json:"JobId,omitempty"`
} `json:"UserImportJobs"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.UserImportJobs, 1)
// GetCSVHeader
@@ -73,7 +74,7 @@ func TestUserImportJob_CRUD(t *testing.T) {
Status string `json:"Status,omitempty"`
} `json:"UserImportJob"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
assert.Equal(t, "InProgress", startResp.UserImportJob.Status)
// Stop
@@ -87,7 +88,7 @@ func TestUserImportJob_CRUD(t *testing.T) {
Status string `json:"Status,omitempty"`
} `json:"UserImportJob"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stopResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stopResp, jsonx.V1()))
assert.Equal(t, "Stopped", stopResp.UserImportJob.Status)
// Start again on stopped job — error
@@ -125,7 +126,7 @@ func TestUserImportJob_SelfCompletesToSucceeded(t *testing.T) {
JobID string `json:"JobId,omitempty"`
} `json:"UserImportJob"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
jobID := createResp.UserImportJob.JobID
require.NotEmpty(t, jobID)
@@ -153,7 +154,7 @@ func TestUserImportJob_SelfCompletesToSucceeded(t *testing.T) {
Status string `json:"Status,omitempty"`
} `json:"UserImportJob"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc, jsonx.V1()))
return desc.UserImportJob.Status == "Succeeded"
}, 2*time.Second, 10*time.Millisecond,
@@ -193,7 +194,7 @@ func TestUserImportJob_EchoesCloudWatchLogsRoleArnAndPasswordHashingAlgorithm(t
var createResp struct {
UserImportJob userImportJobWire `json:"UserImportJob"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Equal(t,
"arn:aws:iam::000000000000:role/CognitoImportRole", createResp.UserImportJob.CloudWatchLogsRoleArn,
@@ -213,7 +214,7 @@ func TestUserImportJob_EchoesCloudWatchLogsRoleArnAndPasswordHashingAlgorithm(t
var startResp struct {
UserImportJob userImportJobWire `json:"UserImportJob"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
assert.NotZero(t, startResp.UserImportJob.StartDate)
assert.Zero(t, startResp.UserImportJob.CompletionDate)
@@ -223,7 +224,7 @@ func TestUserImportJob_EchoesCloudWatchLogsRoleArnAndPasswordHashingAlgorithm(t
var stopResp struct {
UserImportJob userImportJobWire `json:"UserImportJob"`
}
- require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopResp))
+ require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopResp, jsonx.V1()))
assert.NotZero(t, stopResp.UserImportJob.CompletionDate)
}
@@ -257,7 +258,7 @@ func TestListUserImportJobs_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code, "body: %s", rec1.Body)
var page1 listOut
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1, jsonx.V1()))
require.Len(t, page1.UserImportJobs, 2)
require.NotEmpty(t, page1.PaginationToken, "first page must return a cursor when more import jobs remain")
@@ -269,7 +270,7 @@ func TestListUserImportJobs_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code, "body: %s", rec2.Body)
var page2 listOut
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
require.Len(t, page2.UserImportJobs, 1)
require.Empty(t, page2.PaginationToken)
diff --git a/services/cognitoidp/user_pool_clients_handler_test.go b/services/cognitoidp/user_pool_clients_handler_test.go
index 17f5975af8..c62eae2c9c 100644
--- a/services/cognitoidp/user_pool_clients_handler_test.go
+++ b/services/cognitoidp/user_pool_clients_handler_test.go
@@ -1,7 +1,7 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -56,7 +57,7 @@ func TestHandler_UpdateUserPoolClient_WithOpts(t *testing.T) {
AllowedOAuthScopes []string `json:"AllowedOAuthScopes,omitempty"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "renamed-client", resp.UserPoolClient.ClientName)
})
}
@@ -75,7 +76,7 @@ func TestHandler_CreateUserPoolClient(t *testing.T) {
setup: func(h *cognitoidp.Handler) string {
rec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var resp map[string]map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
return resp["UserPool"]["Id"].(string)
},
@@ -119,7 +120,7 @@ func TestHandler_DescribeUserPoolClient(t *testing.T) {
setup: func(h *cognitoidp.Handler) (string, string) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolResp["UserPool"]["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -127,7 +128,7 @@ func TestHandler_DescribeUserPoolClient(t *testing.T) {
"ClientName": "c",
})
var clientResp map[string]map[string]any
- _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp)
+ _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1())
return poolID, clientResp["UserPoolClient"]["ClientId"].(string)
},
@@ -138,7 +139,7 @@ func TestHandler_DescribeUserPoolClient(t *testing.T) {
setup: func(h *cognitoidp.Handler) (string, string) {
rec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var resp map[string]map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
return resp["UserPool"]["Id"].(string), "nonexistent-client"
},
@@ -204,7 +205,7 @@ func TestHandler_DeleteUserPoolClient(t *testing.T) {
setup: func(h *cognitoidp.Handler) (string, string) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolResp["UserPool"]["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -212,7 +213,7 @@ func TestHandler_DeleteUserPoolClient(t *testing.T) {
"ClientName": "c",
})
var clientResp map[string]map[string]any
- _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp)
+ _ = json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1())
return poolID, clientResp["UserPoolClient"]["ClientId"].(string)
},
@@ -223,7 +224,7 @@ func TestHandler_DeleteUserPoolClient(t *testing.T) {
setup: func(h *cognitoidp.Handler) (string, string) {
rec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var resp map[string]map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
return resp["UserPool"]["Id"].(string), "nonexistent-client"
},
@@ -275,7 +276,7 @@ func TestHandler_ListUserPoolClients(t *testing.T) {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var poolResp map[string]map[string]any
- _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp)
+ _ = json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1())
poolID := poolResp["UserPool"]["Id"].(string)
for i := range tt.numClients {
@@ -290,7 +291,7 @@ func TestHandler_ListUserPoolClients(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clients := resp["UserPoolClients"].([]any)
assert.Len(t, clients, tt.numClients)
})
@@ -337,7 +338,7 @@ func TestHandler_AddUserPoolClientSecret(t *testing.T) {
if tt.poolExists {
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "sec-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID = poolResp["UserPool"].(map[string]any)["Id"].(string)
}
@@ -347,7 +348,7 @@ func TestHandler_AddUserPoolClientSecret(t *testing.T) {
"ClientName": "test-client",
})
var clientResp map[string]any
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
clientID = clientResp["UserPoolClient"].(map[string]any)["ClientId"].(string)
}
@@ -369,7 +370,7 @@ func TestSortedListUserPoolClients(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "sorted-clients-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
for _, name := range []string{"web-client", "android-client", "ios-client"} {
@@ -383,7 +384,7 @@ func TestSortedListUserPoolClients(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
clients := listResp["UserPoolClients"].([]any)
require.Len(t, clients, 3)
assert.Equal(t, "android-client", clients[0].(map[string]any)["ClientName"])
@@ -397,7 +398,7 @@ func TestDescribeUserPoolClient_IncludesClientSecret(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "secret-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
clientRec := doCognitoRequest(t, h, "CreateUserPoolClient", map[string]any{
@@ -405,7 +406,7 @@ func TestDescribeUserPoolClient_IncludesClientSecret(t *testing.T) {
"ClientName": "sec-client",
})
var clientResp map[string]any
- require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp))
+ require.NoError(t, json.Unmarshal(clientRec.Body.Bytes(), &clientResp, jsonx.V1()))
clientID := clientResp["UserPoolClient"].(map[string]any)["ClientId"].(string)
// No secret yet.
@@ -414,7 +415,7 @@ func TestDescribeUserPoolClient_IncludesClientSecret(t *testing.T) {
"ClientId": clientID,
})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Empty(t, descResp["UserPoolClient"].(map[string]any)["ClientSecret"])
// AddUserPoolClientSecret creates an independently ClientSecretId-keyed
@@ -427,7 +428,7 @@ func TestDescribeUserPoolClient_IncludesClientSecret(t *testing.T) {
"ClientId": clientID,
})
var addResp map[string]any
- require.NoError(t, json.Unmarshal(addRec.Body.Bytes(), &addResp))
+ require.NoError(t, json.Unmarshal(addRec.Body.Bytes(), &addResp, jsonx.V1()))
descriptor, ok := addResp["ClientSecretDescriptor"].(map[string]any)
require.True(t, ok)
assert.NotEmpty(t, descriptor["ClientSecretId"])
@@ -438,7 +439,7 @@ func TestDescribeUserPoolClient_IncludesClientSecret(t *testing.T) {
"ClientId": clientID,
})
var descResp2 map[string]any
- require.NoError(t, json.Unmarshal(descRec2.Body.Bytes(), &descResp2))
+ require.NoError(t, json.Unmarshal(descRec2.Body.Bytes(), &descResp2, jsonx.V1()))
assert.Empty(t, descResp2["UserPoolClient"].(map[string]any)["ClientSecret"])
listRec := doCognitoRequest(t, h, "ListUserPoolClientSecrets", map[string]any{
@@ -446,7 +447,7 @@ func TestDescribeUserPoolClient_IncludesClientSecret(t *testing.T) {
"ClientId": clientID,
})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
secrets, ok := listResp["ClientSecrets"].([]any)
require.True(t, ok)
require.Len(t, secrets, 1)
@@ -460,14 +461,14 @@ func TestListUserPoolClients_NonNilWhenEmpty(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "empty-clients-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
rec := doCognitoRequest(t, h, "ListUserPoolClients", map[string]any{"UserPoolId": poolID})
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
clients := listResp["UserPoolClients"].([]any)
assert.Empty(t, clients)
}
@@ -486,7 +487,7 @@ func TestClientLastModifiedDate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
client := descResp["UserPoolClient"].(map[string]any)
creationDate := client["CreationDate"].(float64)
lastModBefore := client["LastModifiedDate"].(float64)
@@ -501,7 +502,7 @@ func TestClientLastModifiedDate(t *testing.T) {
require.Equal(t, http.StatusOK, upRec.Code)
var upResp map[string]any
- require.NoError(t, json.Unmarshal(upRec.Body.Bytes(), &upResp))
+ require.NoError(t, json.Unmarshal(upRec.Body.Bytes(), &upResp, jsonx.V1()))
updatedClient := upResp["UserPoolClient"].(map[string]any)
lastModAfter := updatedClient["LastModifiedDate"].(float64)
assert.GreaterOrEqual(t, lastModAfter, creationDate, "LastModifiedDate must be >= CreationDate after update")
@@ -526,7 +527,7 @@ func TestClientOAuthRedirectFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
client := resp["UserPoolClient"].(map[string]any)
callbackURLs := client["CallbackURLs"].([]any)
diff --git a/services/cognitoidp/user_pool_clients_test.go b/services/cognitoidp/user_pool_clients_test.go
index ccf9ee1e21..ddd0f02824 100644
--- a/services/cognitoidp/user_pool_clients_test.go
+++ b/services/cognitoidp/user_pool_clients_test.go
@@ -1,13 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -55,7 +56,7 @@ func TestHandler_CreateUserPoolClient_WithOAuthFields(t *testing.T) {
ExplicitAuthFlows []string `json:"ExplicitAuthFlows,omitempty"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, []string{"code"}, resp.UserPoolClient.AllowedOAuthFlows)
assert.ElementsMatch(t, []string{"email", "openid"}, resp.UserPoolClient.AllowedOAuthScopes)
assert.Equal(t, []string{"ALLOW_USER_PASSWORD_AUTH"}, resp.UserPoolClient.ExplicitAuthFlows)
@@ -81,7 +82,7 @@ func TestHandler_DescribeUserPoolClient_IncludesOAuthFields(t *testing.T) {
ClientID string `json:"ClientId,omitempty"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
rec := doCognitoRequest(t, h, "DescribeUserPoolClient", map[string]any{
"UserPoolId": poolID,
@@ -96,7 +97,7 @@ func TestHandler_DescribeUserPoolClient_IncludesOAuthFields(t *testing.T) {
ExplicitAuthFlows []string `json:"ExplicitAuthFlows,omitempty"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, []string{"code"}, resp.UserPoolClient.AllowedOAuthFlows)
assert.Equal(t, []string{"openid"}, resp.UserPoolClient.AllowedOAuthScopes)
assert.Equal(t, []string{"ALLOW_USER_PASSWORD_AUTH"}, resp.UserPoolClient.ExplicitAuthFlows)
@@ -121,7 +122,7 @@ func TestAppClient_CRUD(t *testing.T) {
ClientName string `json:"ClientName"`
} `json:"UserPoolClient"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Equal(t, "my-app-client", createResp.UserPoolClient.ClientName)
clientID := createResp.UserPoolClient.ClientID
assert.NotEmpty(t, clientID)
@@ -135,7 +136,7 @@ func TestAppClient_CRUD(t *testing.T) {
ClientID string `json:"ClientId"`
} `json:"UserPoolClients"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
var found bool
for _, c := range listResp.UserPoolClients {
if c.ClientID == clientID {
@@ -418,7 +419,7 @@ func TestClientSecrets(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp clientSecretsListResp
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Empty(t, listResp.ClientSecrets)
// Add a secret; its ClientSecretId is required to delete it later
@@ -436,7 +437,7 @@ func TestClientSecrets(t *testing.T) {
ClientSecretValue string `json:"ClientSecretValue"`
} `json:"ClientSecretDescriptor"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &addResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &addResp, jsonx.V1()))
secretID := addResp.ClientSecretDescriptor.ClientSecretID
require.NotEmpty(t, secretID)
require.NotEmpty(t, addResp.ClientSecretDescriptor.ClientSecretValue)
@@ -446,7 +447,7 @@ func TestClientSecrets(t *testing.T) {
"UserPoolId": poolID,
"ClientId": clientID,
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.ClientSecrets, 1)
assert.Equal(t, secretID, listResp.ClientSecrets[0].ClientSecretID)
assert.Empty(t, listResp.ClientSecrets[0].ClientSecretValue)
@@ -472,7 +473,7 @@ func TestClientSecrets(t *testing.T) {
"UserPoolId": poolID,
"ClientId": clientID,
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Empty(t, listResp.ClientSecrets)
}
@@ -592,7 +593,7 @@ func TestListUserPoolClients_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code, "body: %s", rec1.Body)
var page1 listOut
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1, jsonx.V1()))
require.Len(t, page1.UserPoolClients, 2)
require.NotEmpty(t, page1.NextToken, "first page must return a cursor when more clients remain")
@@ -604,7 +605,7 @@ func TestListUserPoolClients_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code, "body: %s", rec2.Body)
var page2 listOut
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
require.Len(t, page2.UserPoolClients, 2)
require.Empty(t, page2.NextToken)
diff --git a/services/cognitoidp/user_pools_config_test.go b/services/cognitoidp/user_pools_config_test.go
index 33940ea1d8..24caabc970 100644
--- a/services/cognitoidp/user_pools_config_test.go
+++ b/services/cognitoidp/user_pools_config_test.go
@@ -1,7 +1,7 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"net/http"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -172,7 +173,7 @@ func TestHandler_ListUserPools(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pools, ok := resp["UserPools"].([]any)
require.True(t, ok)
@@ -194,7 +195,7 @@ func TestHandler_DescribeUserPool(t *testing.T) {
setup: func(h *cognitoidp.Handler) string {
rec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "p"})
var resp map[string]map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
return resp["UserPool"]["Id"].(string)
},
@@ -266,7 +267,7 @@ func TestHandler_DeleteUserPool(t *testing.T) {
setup: func(h *cognitoidp.Handler) string {
rec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "del-pool"})
var resp map[string]map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
return resp["UserPool"]["Id"].(string)
},
@@ -307,7 +308,7 @@ func TestHandler_GetUserPoolMfaConfig(t *testing.T) {
setup: func(h *cognitoidp.Handler) string {
rec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "mfa-pool"})
var resp map[string]map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
return resp["UserPool"]["Id"].(string)
},
@@ -351,7 +352,7 @@ func TestSortedListUserPools(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pools := resp["UserPools"].([]any)
require.Len(t, pools, 3)
@@ -366,7 +367,7 @@ func TestDescribeUserPool_IncludesSchemaAttributes(t *testing.T) {
h := newTestHandler(t)
poolRec := doCognitoRequest(t, h, "CreateUserPool", map[string]any{"PoolName": "schema-pool"})
var poolResp map[string]any
- require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp))
+ require.NoError(t, json.Unmarshal(poolRec.Body.Bytes(), &poolResp, jsonx.V1()))
poolID := poolResp["UserPool"].(map[string]any)["Id"].(string)
doCognitoRequest(t, h, "AddCustomAttributes", map[string]any{
@@ -380,7 +381,7 @@ func TestDescribeUserPool_IncludesSchemaAttributes(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
schema := resp["UserPool"].(map[string]any)["SchemaAttributes"]
assert.NotNil(t, schema)
schemaList := schema.([]any)
@@ -396,7 +397,7 @@ func TestListUserPools_NonNilWhenEmpty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
pools, ok := listResp["UserPools"].([]any)
assert.True(t, ok, "UserPools should be an array, not nil")
assert.Empty(t, pools)
@@ -436,7 +437,7 @@ func TestPasswordPolicyDefaults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pool := resp["UserPool"].(map[string]any)
policies := pool["Policies"].(map[string]any)
@@ -457,7 +458,7 @@ func TestUserPoolLastModifiedDate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
pool := createResp["UserPool"].(map[string]any)
poolID := pool["Id"].(string)
createDate := pool["CreationDate"].(float64)
@@ -478,7 +479,7 @@ func TestUserPoolLastModifiedDate(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
lastModAfter := descResp["UserPool"].(map[string]any)["LastModifiedDate"].(float64)
assert.GreaterOrEqual(t, lastModAfter, createDate, "LastModifiedDate must be >= CreationDate after update")
}
@@ -493,7 +494,7 @@ func TestUpdateUserPoolLambdaConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
poolID := createResp["UserPool"].(map[string]any)["Id"].(string)
lambdaConfig := map[string]any{
@@ -510,7 +511,7 @@ func TestUpdateUserPoolLambdaConfig(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
returnedPool := descResp["UserPool"].(map[string]any)
returnedLambda := returnedPool["LambdaConfig"].(map[string]any)
assert.Equal(t, "arn:aws:lambda:us-east-1:000000000000:function:pre-signup", returnedLambda["PreSignUp"])
@@ -545,7 +546,7 @@ func TestListUserPools_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp listResp
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.LessOrEqual(t, len(resp.UserPools), 2, "page must not exceed MaxResults")
for _, p := range resp.UserPools {
diff --git a/services/cognitoidp/user_pools_test.go b/services/cognitoidp/user_pools_test.go
index b7a41fd261..7c00f5ce25 100644
--- a/services/cognitoidp/user_pools_test.go
+++ b/services/cognitoidp/user_pools_test.go
@@ -1,7 +1,7 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -179,7 +180,7 @@ func TestHandler_CreateUserPool_WithPasswordPolicy(t *testing.T) {
AutoVerifiedAttributes []string `json:"AutoVerifiedAttributes,omitempty"`
} `json:"UserPool"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotNil(t, resp.UserPool.Policies)
require.NotNil(t, resp.UserPool.Policies.PasswordPolicy)
assert.Equal(t, 10, resp.UserPool.Policies.PasswordPolicy.MinimumLength)
@@ -205,7 +206,7 @@ func TestHandler_DescribeUserPool_IncludesPolicy(t *testing.T) {
ID string `json:"Id,omitempty"`
} `json:"UserPool"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
rec := doCognitoRequest(t, h, "DescribeUserPool", map[string]any{"UserPoolId": createResp.UserPool.ID})
require.Equal(t, http.StatusOK, rec.Code)
@@ -219,7 +220,7 @@ func TestHandler_DescribeUserPool_IncludesPolicy(t *testing.T) {
} `json:"Policies"`
} `json:"UserPool"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotNil(t, resp.UserPool.Policies)
require.NotNil(t, resp.UserPool.Policies.PasswordPolicy)
assert.Equal(t, 12, resp.UserPool.Policies.PasswordPolicy.MinimumLength)
@@ -251,7 +252,7 @@ func TestUserPool_CRUD(t *testing.T) {
Name string `json:"Name"`
} `json:"UserPool"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
poolID := createResp.UserPool.ID
assert.Equal(t, tt.poolName, createResp.UserPool.Name)
assert.NotEmpty(t, poolID)
@@ -265,7 +266,7 @@ func TestUserPool_CRUD(t *testing.T) {
ID string `json:"Id"`
} `json:"UserPool"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, poolID, descResp.UserPool.ID)
// Delete.
@@ -588,7 +589,7 @@ func TestHandler_CreateUserPool_MfaConfiguration(t *testing.T) {
Id string `json:"Id"` //nolint:revive,staticcheck // matches wire field name.
} `json:"UserPool"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
rec = doCognitoRequest(t, h, "GetUserPoolMfaConfig", map[string]any{
"UserPoolId": createResp.UserPool.Id,
@@ -598,7 +599,7 @@ func TestHandler_CreateUserPool_MfaConfiguration(t *testing.T) {
var mfaResp struct {
MfaConfiguration string `json:"MfaConfiguration,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mfaResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mfaResp, jsonx.V1()))
assert.Equal(t, "ON", mfaResp.MfaConfiguration)
}
@@ -616,7 +617,7 @@ func TestGetUserPoolMfaConfig_DefaultsToOFF(t *testing.T) {
var out struct {
MfaConfiguration string `json:"MfaConfiguration,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "OFF", out.MfaConfiguration)
}
@@ -844,7 +845,7 @@ func TestHandler_UserPoolReplicas(t *testing.T) {
UserPoolArn string `json:"UserPoolArn,omitempty"`
} `json:"UserPoolReplica"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Equal(t, tt.regionName, createResp.UserPoolReplica.RegionName)
assert.Equal(t, "SECONDARY", createResp.UserPoolReplica.Role)
assert.Equal(t, "INACTIVE", createResp.UserPoolReplica.Status)
@@ -860,7 +861,7 @@ func TestHandler_UserPoolReplicas(t *testing.T) {
RegionName string `json:"RegionName,omitempty"`
} `json:"UserPoolReplicas,omitempty"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.UserPoolReplicas, 1)
assert.Equal(t, tt.regionName, listResp.UserPoolReplicas[0].RegionName)
@@ -876,7 +877,7 @@ func TestHandler_UserPoolReplicas(t *testing.T) {
Status string `json:"Status,omitempty"`
} `json:"UserPoolReplica"`
}
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Equal(t, "ACTIVE", updateResp.UserPoolReplica.Status)
deleteRec := doCognitoRequest(t, h, "DeleteUserPoolReplica", map[string]any{
@@ -890,7 +891,7 @@ func TestHandler_UserPoolReplicas(t *testing.T) {
Status string `json:"Status,omitempty"`
} `json:"UserPoolReplica"`
}
- require.NoError(t, json.Unmarshal(deleteRec.Body.Bytes(), &deleteResp))
+ require.NoError(t, json.Unmarshal(deleteRec.Body.Bytes(), &deleteResp, jsonx.V1()))
assert.Equal(t, "DELETING", deleteResp.UserPoolReplica.Status)
})
}
diff --git a/services/cognitoidp/users_test.go b/services/cognitoidp/users_test.go
index 860bb98eb9..521fb193d1 100644
--- a/services/cognitoidp/users_test.go
+++ b/services/cognitoidp/users_test.go
@@ -1,7 +1,7 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http/httptest"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cognitoidp"
)
@@ -866,7 +867,7 @@ func TestDeleteUser_ClearsGroupAndWebAuthnStateOnRecreate(t *testing.T) {
func unmarshalBody(t *testing.T, rec *httptest.ResponseRecorder, v any) error {
t.Helper()
- return json.Unmarshal(rec.Body.Bytes(), v)
+ return json.Unmarshal(rec.Body.Bytes(), v, jsonx.V1())
}
func TestAdminSetUserPassword_PolicyEnforced(t *testing.T) {
diff --git a/services/cognitoidp/webauthn_test.go b/services/cognitoidp/webauthn_test.go
index e7b7476b4e..2417ec442f 100644
--- a/services/cognitoidp/webauthn_test.go
+++ b/services/cognitoidp/webauthn_test.go
@@ -1,12 +1,14 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestWebAuthn_CRUD(t *testing.T) {
@@ -24,7 +26,7 @@ func TestWebAuthn_CRUD(t *testing.T) {
var startResp struct {
CredentialCreationOptions map[string]any `json:"CredentialCreationOptions,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
require.NotEmpty(t, startResp.CredentialCreationOptions)
assert.NotEmpty(t, startResp.CredentialCreationOptions["challenge"])
rp, ok := startResp.CredentialCreationOptions["rp"].(map[string]any)
@@ -59,7 +61,7 @@ func TestWebAuthn_CRUD(t *testing.T) {
AuthenticatorTransports []string `json:"AuthenticatorTransports"`
} `json:"Credentials,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Credentials, 1)
assert.Equal(t, "cred-id-1", listResp.Credentials[0].CredentialID)
assert.Equal(t, "platform", listResp.Credentials[0].AuthenticatorAttachment)
@@ -88,7 +90,7 @@ func TestWebAuthn_CRUD(t *testing.T) {
CredentialID string `json:"CredentialId,omitempty"`
} `json:"Credentials,omitempty"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp2, jsonx.V1()))
assert.Empty(t, listResp2.Credentials)
// Delete again — not found.
@@ -139,7 +141,7 @@ func TestHandler_CompleteWebAuthnRegistration_Validation(t *testing.T) {
AccessToken string `json:"AccessToken,omitempty"`
} `json:"AuthenticationResult"`
}
- require.NoError(t, json.Unmarshal(initRec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(initRec.Body.Bytes(), &initResp, jsonx.V1()))
require.NotNil(t, initResp.AuthenticationResult)
accessToken := initResp.AuthenticationResult.AccessToken
require.NotEmpty(t, accessToken)
diff --git a/services/cognitoidp/wire_field_fixes_test.go b/services/cognitoidp/wire_field_fixes_test.go
index 6c5ecd8320..ba6d1c424a 100644
--- a/services/cognitoidp/wire_field_fixes_test.go
+++ b/services/cognitoidp/wire_field_fixes_test.go
@@ -1,7 +1,7 @@
package cognitoidp_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
@@ -10,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestListUserPoolClients_SummaryShape proves ListUserPoolClients returns the
@@ -56,7 +58,7 @@ func TestListUserPoolClients_SummaryShape(t *testing.T) {
// assert directly on the wire body that no such key is emitted.
rec := doCognitoRequest(t, h, "ListUserPoolClients", map[string]any{"UserPoolId": poolID})
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
rawClients, ok := raw["UserPoolClients"].([]any)
require.True(t, ok)
require.Len(t, rawClients, 1)
diff --git a/services/comprehend/handler.go b/services/comprehend/handler.go
index 8ddfaf6662..ab2292b253 100644
--- a/services/comprehend/handler.go
+++ b/services/comprehend/handler.go
@@ -2,7 +2,7 @@ package comprehend
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -155,7 +156,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
return ""
}
var values map[string]any
- if unmarshalErr := json.Unmarshal(body, &values); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &values, jsonx.V1()); unmarshalErr != nil {
return ""
}
for _, key := range []string{
@@ -202,7 +203,7 @@ func (h *Handler) dispatch(_ context.Context, action string, body []byte) ([]byt
var input map[string]any
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
}
@@ -214,7 +215,7 @@ func (h *Handler) dispatch(_ context.Context, action string, body []byte) ([]byt
return nil, err
}
- return json.Marshal(output)
+ return json.Marshal(output, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
@@ -241,7 +242,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
code, status = "KmsKeyValidationException", http.StatusBadRequest
}
- payload, marshalErr := json.Marshal(service.JSONErrorResponse{Type: code, Message: errMessage(err)})
+ payload, marshalErr := json.Marshal(service.JSONErrorResponse{Type: code, Message: errMessage(err)}, jsonx.V1())
if marshalErr != nil {
return c.String(http.StatusInternalServerError, "internal server error")
}
diff --git a/services/comprehend/handler_test.go b/services/comprehend/handler_test.go
index e6fdcc10ca..c49c2e5b2d 100644
--- a/services/comprehend/handler_test.go
+++ b/services/comprehend/handler_test.go
@@ -2,7 +2,7 @@ package comprehend_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/comprehend"
)
@@ -67,7 +68,7 @@ func mergedBody(base, extra map[string]any) map[string]any {
func request(t *testing.T, handler *comprehend.Handler, operation string, input map[string]any) map[string]any {
t.Helper()
- payload, err := json.Marshal(input)
+ payload, err := json.Marshal(input, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(payload))
req.Header.Set("Content-Type", "application/x-amz-json-1.1")
@@ -78,7 +79,7 @@ func request(t *testing.T, handler *comprehend.Handler, operation string, input
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var output map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &output))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &output, jsonx.V1()))
return output
}
@@ -107,7 +108,7 @@ func decodeBody(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
@@ -118,7 +119,7 @@ func decodeBody(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
func toJSON(t *testing.T, input map[string]any) string {
t.Helper()
- payload, err := json.Marshal(input)
+ payload, err := json.Marshal(input, jsonx.V1())
require.NoError(t, err)
return string(payload)
@@ -892,7 +893,7 @@ func TestProtocolOpList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var ops []string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops, jsonx.V1()))
assert.NotEmpty(t, ops, "GET / must return non-empty operation list")
assert.Contains(t, ops, "DetectSentiment")
assert.Contains(t, ops, "DetectEntities")
diff --git a/services/comprehend/persistence.go b/services/comprehend/persistence.go
index 4555857377..ab0a89612c 100644
--- a/services/comprehend/persistence.go
+++ b/services/comprehend/persistence.go
@@ -2,7 +2,7 @@ package comprehend
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -41,7 +41,7 @@ const comprehendSnapshotVersion = 1
// nothing for store.Table to key on (see store_setup.go's file doc comment).
// They are persisted directly here.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
Policies map[string]string `json:"policies"`
PolicyRevisions map[string]string `json:"policyRevisions"`
diff --git a/services/comprehend/s3_output.go b/services/comprehend/s3_output.go
index 1a0ec15aa7..44ba63abfb 100644
--- a/services/comprehend/s3_output.go
+++ b/services/comprehend/s3_output.go
@@ -5,7 +5,7 @@ import (
"bytes"
"compress/gzip"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"path"
"slices"
@@ -14,6 +14,7 @@ import (
"github.com/aws/aws-sdk-go-v2/aws"
awss3 "github.com/aws/aws-sdk-go-v2/service/s3"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
s3backend "github.com/blackbirdworks/gopherstack/services/s3"
)
@@ -172,12 +173,12 @@ func outputFiles(job *Job) map[string][]byte {
case jobTypeClassification:
line, _ := json.Marshal(map[string]any{
"File": input, "Classes": []map[string]any{{"Name": "DEFAULT", "Score": defaultScore}},
- })
+ }, jsonx.V1())
return map[string][]byte{"predictions.jsonl": append(line, '\n')}
}
- line, _ := json.Marshal(jobResultLine(job, input))
+ line, _ := json.Marshal(jobResultLine(job, input), jsonx.V1())
return map[string][]byte{"output": append(line, '\n')}
}
diff --git a/services/connect/contacts.go b/services/connect/contacts.go
index eebb94684c..fb8aa74503 100644
--- a/services/connect/contacts.go
+++ b/services/connect/contacts.go
@@ -2,7 +2,7 @@ package connect
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"maps"
"slices"
"strings"
@@ -40,8 +40,8 @@ type StartOutboundVoiceContactInput struct {
Description string
RelatedContactID string `json:"RelatedContactId"`
ClientToken string
- OutboundStrategy json.RawMessage
- References json.RawMessage
+ OutboundStrategy jsontext.Value
+ References jsontext.Value
}
// ParticipantDetails names a chat participant.
@@ -73,7 +73,7 @@ type StartChatContactInput struct {
CustomerID string `json:"CustomerId"`
RelatedContactID string `json:"RelatedContactId"`
ClientToken string
- SegmentAttributes json.RawMessage
+ SegmentAttributes jsontext.Value
}
// StartTaskContactInput is the StartTaskContact request.
@@ -89,9 +89,9 @@ type StartTaskContactInput struct {
QuickConnectID string `json:"QuickConnectId"`
TaskTemplateID string `json:"TaskTemplateId"`
ClientToken string
- SegmentAttributes json.RawMessage
- Attachments json.RawMessage
- References json.RawMessage
+ SegmentAttributes jsontext.Value
+ Attachments jsontext.Value
+ References jsontext.Value
}
// ContactRefInput addresses one contact in body-addressed contact operations.
@@ -114,8 +114,8 @@ type ContactRefInput struct {
ContactID string `json:"ContactId" path:"ContactId"`
InstanceID string `json:"InstanceId" path:"InstanceId"`
InitialContactID string `json:"InitialContactId"`
- SegmentAttributes json.RawMessage
- References json.RawMessage
+ SegmentAttributes jsontext.Value
+ References jsontext.Value
}
// ContactPathInput addresses one contact by path.
diff --git a/services/connect/datatables.go b/services/connect/datatables.go
index ba40dfa647..1157cacb49 100644
--- a/services/connect/datatables.go
+++ b/services/connect/datatables.go
@@ -2,7 +2,8 @@ package connect
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"math"
"net/http"
@@ -11,6 +12,7 @@ import (
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -211,11 +213,11 @@ func (t *dataTableRec) primaryAttrs() []*DataTableAttribute {
}
func (t *dataTableRec) clone() *dataTableRec {
- raw, _ := json.Marshal(t)
+ raw, _ := json.Marshal(t, jsonx.V1())
var out dataTableRec
- _ = json.Unmarshal(raw, &out)
+ _ = json.Unmarshal(raw, &out, jsonx.V1())
return &out
}
@@ -779,8 +781,8 @@ func checkDTText(v *DataTableValidation, s string) error {
}
func checkDTList(a *DataTableAttribute, s string) error {
- var items []json.RawMessage
- if err := json.Unmarshal([]byte(s), &items); err != nil {
+ var items []jsontext.Value
+ if err := json.Unmarshal([]byte(s), &items, jsonx.V1()); err != nil {
return valueErr("value must be a JSON array")
}
@@ -798,7 +800,7 @@ func checkDTList(a *DataTableAttribute, s string) error {
for _, raw := range items {
if a.ValueType == dtTypeNumberList {
var f float64
- if json.Unmarshal(raw, &f) != nil {
+ if json.Unmarshal(raw, &f, jsonx.V1()) != nil {
return valueErr("list item %s is not a number", raw)
}
@@ -810,7 +812,7 @@ func checkDTList(a *DataTableAttribute, s string) error {
}
var str string
- if json.Unmarshal(raw, &str) != nil {
+ if json.Unmarshal(raw, &str, jsonx.V1()) != nil {
return valueErr("list item %s is not text", raw)
}
diff --git a/services/connect/evaluationforms.go b/services/connect/evaluationforms.go
index 1ae1c508dc..a6dce6a97c 100644
--- a/services/connect/evaluationforms.go
+++ b/services/connect/evaluationforms.go
@@ -2,11 +2,13 @@ package connect
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"slices"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -54,15 +56,15 @@ type evalFormVersion struct {
}
type formContent struct {
- ScoringStrategy json.RawMessage `json:"ScoringStrategy,omitempty"`
- AutoEvaluationConfiguration json.RawMessage `json:"AutoEvaluationConfiguration,omitempty"`
- ReviewConfiguration json.RawMessage `json:"ReviewConfiguration,omitempty"`
- TargetConfiguration json.RawMessage `json:"TargetConfiguration,omitempty"`
- LanguageConfiguration json.RawMessage `json:"LanguageConfiguration,omitempty"`
- Title string `json:"Title"`
- Description string `json:"Description,omitempty"`
- AIVersion string `json:"AIVersion,omitempty"`
- Items []json.RawMessage `json:"Items"`
+ ScoringStrategy jsontext.Value `json:"ScoringStrategy,omitempty"`
+ AutoEvaluationConfiguration jsontext.Value `json:"AutoEvaluationConfiguration,omitempty"`
+ ReviewConfiguration jsontext.Value `json:"ReviewConfiguration,omitempty"`
+ TargetConfiguration jsontext.Value `json:"TargetConfiguration,omitempty"`
+ LanguageConfiguration jsontext.Value `json:"LanguageConfiguration,omitempty"`
+ Title string `json:"Title"`
+ Description string `json:"Description,omitempty"`
+ AIVersion string `json:"AIVersion,omitempty"`
+ Items []jsontext.Value `json:"Items"`
}
type evalFormRec struct {
@@ -212,9 +214,9 @@ func (w *formWalk) problem(sev, code, desc string) {
w.findings = append(w.findings, FormFinding{Description: desc, IssueCode: code, Severity: sev})
}
-func (w *formWalk) item(raw json.RawMessage, depth int) {
- var u map[string]json.RawMessage
- if err := json.Unmarshal(raw, &u); err != nil || len(u) != 1 {
+func (w *formWalk) item(raw jsontext.Value, depth int) {
+ var u map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &u, jsonx.V1()); err != nil || len(u) != 1 {
w.problem(findingError, "InvalidItem", "an item must be an object with exactly one of Section or Question")
return
@@ -248,14 +250,14 @@ func (w *formWalk) ref(ref string) {
}
}
-func (w *formWalk) section(raw json.RawMessage, depth int) {
+func (w *formWalk) section(raw jsontext.Value, depth int) {
var s struct {
- RefID string `json:"RefId"`
- Title string `json:"Title"`
- Items []json.RawMessage `json:"Items"`
+ RefID string `json:"RefId"`
+ Title string `json:"Title"`
+ Items []jsontext.Value `json:"Items"`
}
- if json.Unmarshal(raw, &s) != nil {
+ if json.Unmarshal(raw, &s, jsonx.V1()) != nil {
w.problem(findingError, "InvalidSection", "a Section is not valid")
return
@@ -278,15 +280,15 @@ func (w *formWalk) section(raw json.RawMessage, depth int) {
}
}
-func (w *formWalk) question(raw json.RawMessage) {
+func (w *formWalk) question(raw jsontext.Value) {
var q struct {
- Props map[string]json.RawMessage `json:"QuestionTypeProperties"`
- RefID string `json:"RefId"`
- Title string `json:"Title"`
- QuestionType string `json:"QuestionType"`
+ Props map[string]jsontext.Value `json:"QuestionTypeProperties"`
+ RefID string `json:"RefId"`
+ Title string `json:"Title"`
+ QuestionType string `json:"QuestionType"`
}
- if json.Unmarshal(raw, &q) != nil {
+ if json.Unmarshal(raw, &q, jsonx.V1()) != nil {
w.problem(findingError, "InvalidQuestion", "a Question is not valid")
return
@@ -313,7 +315,7 @@ func (w *formWalk) question(raw json.RawMessage) {
}
// inspectForm walks the items and reports structural problems and warnings.
-func inspectForm(items []json.RawMessage) *formWalk {
+func inspectForm(items []jsontext.Value) *formWalk {
w := &formWalk{refs: map[string]bool{}}
for _, it := range items {
w.item(it, 0)
@@ -346,7 +348,7 @@ func checkFormContent(c *formContent) error {
Mode string `json:"Mode"`
Status string `json:"Status"`
}
- if json.Unmarshal(c.ScoringStrategy, &s) != nil {
+ if json.Unmarshal(c.ScoringStrategy, &s, jsonx.V1()) != nil {
return invalidParamErr("ScoringStrategy is not valid")
}
@@ -730,13 +732,13 @@ func (b *Backend) SearchEvaluationForms(in *SearchRequest) (*SearchResult, error
}
// rejectCriteria refuses criteria members the emulator does not evaluate, at any nesting level.
-func rejectCriteria(raw json.RawMessage, members ...string) error {
+func rejectCriteria(raw jsontext.Value, members ...string) error {
if len(raw) == 0 {
return nil
}
- var node map[string]json.RawMessage
- if json.Unmarshal(raw, &node) != nil {
+ var node map[string]jsontext.Value
+ if json.Unmarshal(raw, &node, jsonx.V1()) != nil {
return invalidParamErr("SearchCriteria is not valid")
}
@@ -747,8 +749,8 @@ func rejectCriteria(raw json.RawMessage, members ...string) error {
}
for _, key := range []string{"AndConditions", "OrConditions"} {
- var subs []json.RawMessage
- if json.Unmarshal(node[key], &subs) != nil {
+ var subs []jsontext.Value
+ if json.Unmarshal(node[key], &subs, jsonx.V1()) != nil {
continue
}
diff --git a/services/connect/flows.go b/services/connect/flows.go
index d77980fb5f..fb1bc4837b 100644
--- a/services/connect/flows.go
+++ b/services/connect/flows.go
@@ -4,12 +4,15 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"regexp"
"slices"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -57,7 +60,7 @@ func validateFlowContent(code, content string) error {
}
var doc flowDoc
- if err := json.Unmarshal([]byte(content), &doc); err != nil {
+ if err := json.Unmarshal([]byte(content), &doc, jsonx.V1()); err != nil {
return invalidFlowErr(code, "content is not valid flow language JSON: "+err.Error())
}
@@ -402,7 +405,7 @@ type CreateContactFlowModuleInput struct {
Settings string
ClientToken string
instPath
- ExternalInvocationConfiguration json.RawMessage
+ ExternalInvocationConfiguration jsontext.Value
}
func validateModule(content, settings string) error {
@@ -410,7 +413,7 @@ func validateModule(content, settings string) error {
return err
}
- if settings != "" && !json.Valid([]byte(settings)) {
+ if settings != "" && !jsontext.Value([]byte(settings)).IsValid(jsonx.V1()) {
return invalidFlowErr("InvalidContactFlowModuleException", "Settings is not valid JSON")
}
diff --git a/services/connect/metrics.go b/services/connect/metrics.go
index e190e792b2..4db70874da 100644
--- a/services/connect/metrics.go
+++ b/services/connect/metrics.go
@@ -1,7 +1,7 @@
package connect
import (
- "encoding/json"
+ "encoding/json/jsontext"
"slices"
"strings"
"time"
@@ -572,10 +572,10 @@ func sortMetricResults(results []CurrentMetricResult, criteria []CurrentMetricSo
// HistoricalMetric names one historical metric and statistic.
type HistoricalMetric struct {
- Name string `json:"Name"`
- Statistic string `json:"Statistic"`
- Unit string `json:"Unit"`
- Threshold json.RawMessage `json:"Threshold,omitempty"`
+ Name string `json:"Name"`
+ Statistic string `json:"Statistic"`
+ Unit string `json:"Unit"`
+ Threshold jsontext.Value `json:"Threshold,omitempty"`
}
// GetMetricDataInput is the GetMetricData request.
diff --git a/services/connect/models.go b/services/connect/models.go
index 72b4359acc..725ae00a74 100644
--- a/services/connect/models.go
+++ b/services/connect/models.go
@@ -1,7 +1,7 @@
package connect
import (
- "encoding/json"
+ "encoding/json/jsontext"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
)
@@ -167,11 +167,11 @@ type User struct {
RoutingProfileID string `json:"RoutingProfileId"`
Username string `json:"Username"`
SecurityProfileIDs []string `json:"SecurityProfileIds"`
- AfterContactWorkConfigs json.RawMessage `json:"AfterContactWorkConfigs,omitempty"`
- AutoAcceptConfigs json.RawMessage `json:"AutoAcceptConfigs,omitempty"`
- PersistentConnectionConfigs json.RawMessage `json:"PersistentConnectionConfigs,omitempty"`
- PhoneNumberConfigs json.RawMessage `json:"PhoneNumberConfigs,omitempty"`
- VoiceEnhancementConfigs json.RawMessage `json:"VoiceEnhancementConfigs,omitempty"`
+ AfterContactWorkConfigs jsontext.Value `json:"AfterContactWorkConfigs,omitempty"`
+ AutoAcceptConfigs jsontext.Value `json:"AutoAcceptConfigs,omitempty"`
+ PersistentConnectionConfigs jsontext.Value `json:"PersistentConnectionConfigs,omitempty"`
+ PhoneNumberConfigs jsontext.Value `json:"PhoneNumberConfigs,omitempty"`
+ VoiceEnhancementConfigs jsontext.Value `json:"VoiceEnhancementConfigs,omitempty"`
}
// Summary is the list shape shared by most resources.
@@ -192,10 +192,10 @@ type UserSummary struct {
// MediaConcurrency limits concurrent contacts of one channel.
type MediaConcurrency struct {
- Channel string `json:"Channel"`
- CrossChannelBehavior json.RawMessage `json:"CrossChannelBehavior,omitempty"`
- WorkloadTypeConcurrencies json.RawMessage `json:"WorkloadTypeConcurrencies,omitempty"`
- Concurrency int32 `json:"Concurrency"`
+ Channel string `json:"Channel"`
+ CrossChannelBehavior jsontext.Value `json:"CrossChannelBehavior,omitempty"`
+ WorkloadTypeConcurrencies jsontext.Value `json:"WorkloadTypeConcurrencies,omitempty"`
+ Concurrency int32 `json:"Concurrency"`
}
// QueueReference names a queue and channel in a routing profile.
@@ -246,7 +246,7 @@ type SecurityProfile struct {
Stamp
AllowedAccessControlTags map[string]string `json:"AllowedAccessControlTags,omitempty"`
Tags map[string]string `json:"Tags,omitempty"`
- GranularAccessControlConfiguration json.RawMessage `json:"GranularAccessControlConfiguration,omitempty"`
+ GranularAccessControlConfiguration jsontext.Value `json:"GranularAccessControlConfiguration,omitempty"`
AllowedAccessControlHierarchyGroupID string `json:"AllowedAccessControlHierarchyGroupId,omitempty"`
Arn string `json:"Arn"`
Description string `json:"Description,omitempty"`
@@ -385,8 +385,8 @@ type QuickConnectConfig struct {
ContactFlowID string `json:"ContactFlowId"`
UserID string `json:"UserId"`
} `json:"UserConfig,omitempty"`
- QuickConnectType string `json:"QuickConnectType"`
- FlowConfig json.RawMessage `json:"FlowConfig,omitempty"`
+ QuickConnectType string `json:"QuickConnectType"`
+ FlowConfig jsontext.Value `json:"FlowConfig,omitempty"`
}
// QuickConnect is a saved transfer destination.
@@ -441,7 +441,7 @@ type ContactFlowModule struct {
State string `json:"State"`
Status string `json:"Status"`
VersionDescription string `json:"VersionDescription,omitempty"`
- ExternalInvocationConfiguration json.RawMessage `json:"ExternalInvocationConfiguration,omitempty"`
+ ExternalInvocationConfiguration jsontext.Value `json:"ExternalInvocationConfiguration,omitempty"`
}
// ContactFlowModuleSummary is the list shape of a ContactFlowModule.
@@ -580,6 +580,6 @@ type Contact struct {
Name string `json:"Name,omitempty"`
PreviousContactID string `json:"PreviousContactId,omitempty"`
RelatedContactID string `json:"RelatedContactId,omitempty"`
- OutboundStrategy json.RawMessage `json:"OutboundStrategy,omitempty"`
- SegmentAttributes json.RawMessage `json:"SegmentAttributes,omitempty"`
+ OutboundStrategy jsontext.Value `json:"OutboundStrategy,omitempty"`
+ SegmentAttributes jsontext.Value `json:"SegmentAttributes,omitempty"`
}
diff --git a/services/connect/persistence.go b/services/connect/persistence.go
index 5536f01256..66da04122e 100644
--- a/services/connect/persistence.go
+++ b/services/connect/persistence.go
@@ -2,7 +2,7 @@ package connect
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,10 +12,10 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/connect/proficiencies.go b/services/connect/proficiencies.go
index e3fcf04154..2f63a96f22 100644
--- a/services/connect/proficiencies.go
+++ b/services/connect/proficiencies.go
@@ -2,11 +2,13 @@ package connect
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"math"
"slices"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -210,17 +212,17 @@ func (b *Backend) ListUserProficiencies(in *ProficienciesInput) (*ProficienciesO
type UpdateUserConfigInput struct {
UserID string `json:"-" path:"UserId"`
instPath
- AfterContactWorkConfigs json.RawMessage
- AutoAcceptConfigs json.RawMessage
- PersistentConnectionConfigs json.RawMessage
- PhoneNumberConfigs json.RawMessage
- VoiceEnhancementConfigs json.RawMessage
+ AfterContactWorkConfigs jsontext.Value
+ AutoAcceptConfigs jsontext.Value
+ PersistentConnectionConfigs jsontext.Value
+ PhoneNumberConfigs jsontext.Value
+ VoiceEnhancementConfigs jsontext.Value
}
var userChannels = []string{"VOICE", "CHAT", "TASK", emailValue} //nolint:gochecknoglobals // immutable lookup table
// checkChannelConfigs validates that every entry names a known channel and returns the normalised list.
-func checkChannelConfigs(field string, raw json.RawMessage) (json.RawMessage, error) {
+func checkChannelConfigs(field string, raw jsontext.Value) (jsontext.Value, error) {
if len(raw) == 0 || string(raw) == jsonNull {
return nil, nil
}
@@ -229,7 +231,7 @@ func checkChannelConfigs(field string, raw json.RawMessage) (json.RawMessage, er
Channel string `json:"Channel"`
}
- if err := json.Unmarshal(raw, &items); err != nil {
+ if err := json.Unmarshal(raw, &items, jsonx.V1()); err != nil {
return nil, invalidParamErr("%s is not valid", field)
}
@@ -251,30 +253,30 @@ func checkChannelConfigs(field string, raw json.RawMessage) (json.RawMessage, er
}
type userConfigField struct {
- dst func(*User) *json.RawMessage
+ dst func(*User) *jsontext.Value
name string
- raw json.RawMessage
+ raw jsontext.Value
}
func userConfigFields(in *UpdateUserConfigInput) []userConfigField {
return []userConfigField{
{name: "AfterContactWorkConfigs", raw: in.AfterContactWorkConfigs,
- dst: func(u *User) *json.RawMessage { return &u.AfterContactWorkConfigs }},
+ dst: func(u *User) *jsontext.Value { return &u.AfterContactWorkConfigs }},
{name: "AutoAcceptConfigs", raw: in.AutoAcceptConfigs,
- dst: func(u *User) *json.RawMessage { return &u.AutoAcceptConfigs }},
+ dst: func(u *User) *jsontext.Value { return &u.AutoAcceptConfigs }},
{name: "PersistentConnectionConfigs", raw: in.PersistentConnectionConfigs,
- dst: func(u *User) *json.RawMessage { return &u.PersistentConnectionConfigs }},
+ dst: func(u *User) *jsontext.Value { return &u.PersistentConnectionConfigs }},
{name: "PhoneNumberConfigs", raw: in.PhoneNumberConfigs,
- dst: func(u *User) *json.RawMessage { return &u.PhoneNumberConfigs }},
+ dst: func(u *User) *jsontext.Value { return &u.PhoneNumberConfigs }},
{name: "VoiceEnhancementConfigs", raw: in.VoiceEnhancementConfigs,
- dst: func(u *User) *json.RawMessage { return &u.VoiceEnhancementConfigs }},
+ dst: func(u *User) *jsontext.Value { return &u.VoiceEnhancementConfigs }},
}
}
// UpdateUserConfig replaces the supplied per-channel settings of a user.
func (b *Backend) UpdateUserConfig(ctx context.Context, in *UpdateUserConfigInput) (*empty, error) {
fields := userConfigFields(in)
- checked := make([]json.RawMessage, len(fields))
+ checked := make([]jsontext.Value, len(fields))
for i, f := range fields {
raw, err := checkChannelConfigs(f.name, f.raw)
diff --git a/services/connect/records.go b/services/connect/records.go
index 6eb4a53ce8..4e412b527d 100644
--- a/services/connect/records.go
+++ b/services/connect/records.go
@@ -1,7 +1,7 @@
package connect
import (
- "encoding/json"
+ "encoding/json/jsontext"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -90,13 +90,13 @@ type promptRec struct {
}
type contactRec struct {
- InstanceID string `json:"instanceId"`
- ParticipantID string `json:"participantId,omitempty"`
- ParticipantToken string `json:"participantToken,omitempty"`
- FlowID string `json:"flowId,omitempty"`
- TaskTemplateID string `json:"taskTemplateId,omitempty"`
- Contact Contact `json:"contact"`
- References json.RawMessage `json:"references,omitempty"`
+ InstanceID string `json:"instanceId"`
+ ParticipantID string `json:"participantId,omitempty"`
+ ParticipantToken string `json:"participantToken,omitempty"`
+ FlowID string `json:"flowId,omitempty"`
+ TaskTemplateID string `json:"taskTemplateId,omitempty"`
+ Contact Contact `json:"contact"`
+ References jsontext.Value `json:"references,omitempty"`
}
type integrationRec struct {
diff --git a/services/connect/rules.go b/services/connect/rules.go
index b4ae08aaa6..9a20d281d2 100644
--- a/services/connect/rules.go
+++ b/services/connect/rules.go
@@ -2,10 +2,12 @@ package connect
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"slices"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -57,8 +59,8 @@ type Rule struct {
PublishStatus string `json:"PublishStatus"`
RuleArn string `json:"RuleArn"`
RuleID string `json:"RuleId"`
- Actions []json.RawMessage `json:"Actions"`
- PreEvaluationFilters json.RawMessage `json:"PreEvaluationFilters,omitempty"`
+ Actions []jsontext.Value `json:"Actions"`
+ PreEvaluationFilters jsontext.Value `json:"PreEvaluationFilters,omitempty"`
}
// ActionSummary is the list shape of a rule action.
@@ -92,8 +94,8 @@ type CreateRuleInput struct {
PublishStatus string
ClientToken string
instPath
- PreEvaluationFilters json.RawMessage
- Actions []json.RawMessage
+ PreEvaluationFilters jsontext.Value
+ Actions []jsontext.Value
}
// UpdateRuleInput is the UpdateRule request.
@@ -103,8 +105,8 @@ type UpdateRuleInput struct {
PublishStatus string
RuleID string `json:"-" path:"RuleId"`
instPath
- PreEvaluationFilters json.RawMessage
- Actions []json.RawMessage
+ PreEvaluationFilters jsontext.Value
+ Actions []jsontext.Value
}
// RuleOut is the wire shape of a rule.
@@ -113,14 +115,14 @@ type RuleOut struct {
RuleID string `json:"RuleId"`
}
-func ruleActionType(raw json.RawMessage) (string, error) {
- var a map[string]json.RawMessage
- if err := json.Unmarshal(raw, &a); err != nil {
+func ruleActionType(raw jsontext.Value) (string, error) {
+ var a map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &a, jsonx.V1()); err != nil {
return "", invalidParamErr("Actions contains an invalid action")
}
var t string
- if err := json.Unmarshal(a["ActionType"], &t); err != nil {
+ if err := json.Unmarshal(a["ActionType"], &t, jsonx.V1()); err != nil {
return "", invalidParamErr("every action needs an ActionType")
}
@@ -136,7 +138,7 @@ func ruleActionType(raw json.RawMessage) (string, error) {
return t, nil
}
-func (b *Backend) checkRuleActions(inst string, actions []json.RawMessage) error {
+func (b *Backend) checkRuleActions(inst string, actions []jsontext.Value) error {
if len(actions) == 0 || len(actions) > maxRuleActions {
return invalidParamErr("Actions must contain 1-%d actions", maxRuleActions)
}
@@ -157,14 +159,14 @@ func (b *Backend) checkRuleActions(inst string, actions []json.RawMessage) error
return nil
}
-func (b *Backend) checkTaskActionFlow(inst string, raw json.RawMessage) error {
+func (b *Backend) checkTaskActionFlow(inst string, raw jsontext.Value) error {
var a struct {
TaskAction struct {
ContactFlowID string `json:"ContactFlowId"`
} `json:"TaskAction"`
}
- if err := json.Unmarshal(raw, &a); err != nil {
+ if err := json.Unmarshal(raw, &a, jsonx.V1()); err != nil {
return invalidParamErr("TaskAction is not valid")
}
diff --git a/services/connect/search.go b/services/connect/search.go
index d45f34779e..755290af2b 100644
--- a/services/connect/search.go
+++ b/services/connect/search.go
@@ -1,11 +1,13 @@
package connect
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"slices"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TimeRange bounds a contact search.
@@ -70,23 +72,23 @@ type SearchContactsInput struct {
TimeRange TimeRange
InstanceID string `json:"InstanceId"`
NextToken string
- SearchCriteria json.RawMessage
+ SearchCriteria jsontext.Value
MaxResults int32
}
-func parseCriteria(raw json.RawMessage) (*ContactSearchCriteria, error) {
+func parseCriteria(raw jsontext.Value) (*ContactSearchCriteria, error) {
var crit ContactSearchCriteria
if len(raw) == 0 {
return &crit, nil
}
- if err := json.Unmarshal(raw, &crit); err != nil {
+ if err := json.Unmarshal(raw, &crit, jsonx.V1()); err != nil {
return nil, invalidParamErr("SearchCriteria is not valid")
}
- var members map[string]json.RawMessage
- if err := json.Unmarshal(raw, &members); err != nil {
+ var members map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &members, jsonx.V1()); err != nil {
return nil, invalidParamErr("SearchCriteria is not valid")
}
@@ -291,7 +293,7 @@ type ContactSearchSummary struct {
InitiationMethod string `json:"InitiationMethod"`
Name string `json:"Name,omitempty"`
PreviousContactID string `json:"PreviousContactId,omitempty"`
- SegmentAttributes json.RawMessage `json:"SegmentAttributes,omitempty"`
+ SegmentAttributes jsontext.Value `json:"SegmentAttributes,omitempty"`
}
func summarizeContact(c *contactRec) ContactSearchSummary {
diff --git a/services/connect/searchengine.go b/services/connect/searchengine.go
index 63d391048b..e27096ffb2 100644
--- a/services/connect/searchengine.go
+++ b/services/connect/searchengine.go
@@ -1,10 +1,12 @@
package connect
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"slices"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -124,17 +126,17 @@ type searchSpec struct {
fields []string
}
-func parseSearch(rawCriteria, rawFilter json.RawMessage, spec searchSpec) (*searchCriteria, *searchFilter, error) {
+func parseSearch(rawCriteria, rawFilter jsontext.Value, spec searchSpec) (*searchCriteria, *searchFilter, error) {
var c searchCriteria
var f searchFilter
- for raw, dst := range map[*json.RawMessage]any{&rawCriteria: &c, &rawFilter: &f} {
+ for raw, dst := range map[*jsontext.Value]any{&rawCriteria: &c, &rawFilter: &f} {
if len(*raw) == 0 || string(*raw) == jsonNull {
continue
}
- if err := json.Unmarshal(*raw, dst); err != nil {
+ if err := json.Unmarshal(*raw, dst, jsonx.V1()); err != nil {
return nil, nil, invalidParamErr("SearchCriteria or SearchFilter is not valid")
}
}
diff --git a/services/connect/searches.go b/services/connect/searches.go
index d628b95bee..5ad78bcbc6 100644
--- a/services/connect/searches.go
+++ b/services/connect/searches.go
@@ -2,18 +2,21 @@ package connect
import (
"cmp"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// SearchRequest is the body shared by the Search* operations.
type SearchRequest struct {
InstanceID string `json:"InstanceId"`
NextToken string
- SearchCriteria json.RawMessage
- SearchFilter json.RawMessage
+ SearchCriteria jsontext.Value
+ SearchFilter jsontext.Value
ResourceTypes []string
MaxResults int32
}
@@ -435,10 +438,10 @@ type TagSet struct {
Value string `json:"value"`
}
-func parseTagSearch(raw json.RawMessage) (func(k, v string) bool, error) {
+func parseTagSearch(raw jsontext.Value) (func(k, v string) bool, error) {
var c resourceTagsCriteria
if len(raw) > 0 && string(raw) != jsonNull {
- if err := json.Unmarshal(raw, &c); err != nil {
+ if err := json.Unmarshal(raw, &c, jsonx.V1()); err != nil {
return nil, invalidParamErr("SearchCriteria is not valid")
}
}
diff --git a/services/connect/security.go b/services/connect/security.go
index 4809c00633..c729d59969 100644
--- a/services/connect/security.go
+++ b/services/connect/security.go
@@ -2,7 +2,7 @@ package connect
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"slices"
"github.com/google/uuid"
@@ -18,13 +18,13 @@ type CreateSecurityProfileInput struct {
instPath
AllowedAccessControlHierarchyGroupID string `json:"AllowedAccessControlHierarchyGroupId"`
Description string
- AllowedAIAgents json.RawMessage
- Applications json.RawMessage
- AllowedFlowModules json.RawMessage
+ AllowedAIAgents jsontext.Value
+ Applications jsontext.Value
+ AllowedFlowModules jsontext.Value
HierarchyRestrictedResources []string
Permissions []string
TagRestrictedResources []string
- GranularAccessControlConfiguration json.RawMessage
+ GranularAccessControlConfiguration jsontext.Value
}
// UpdateSecurityProfileInput is the UpdateSecurityProfile request.
@@ -34,10 +34,10 @@ type UpdateSecurityProfileInput struct {
AllowedAccessControlHierarchyGroupID *string `json:"AllowedAccessControlHierarchyGroupId"`
ProfileID string `json:"-" path:"SecurityProfileId"`
instPath
- GranularAccessControlConfiguration json.RawMessage
- AllowedAIAgents json.RawMessage
- AllowedFlowModules json.RawMessage
- Applications json.RawMessage
+ GranularAccessControlConfiguration jsontext.Value
+ AllowedAIAgents jsontext.Value
+ AllowedFlowModules jsontext.Value
+ Applications jsontext.Value
HierarchyRestrictedResources []string
Permissions []string
TagRestrictedResources []string
diff --git a/services/connect/securityextras.go b/services/connect/securityextras.go
index f3189ef3a7..0c43c45535 100644
--- a/services/connect/securityextras.go
+++ b/services/connect/securityextras.go
@@ -2,18 +2,21 @@ package connect
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const maxSecurityExtras = 100
// securityExtras are the Applications, AllowedFlowModules and AllowedAIAgents of a security profile.
type securityExtras struct {
- AIAgents json.RawMessage `json:"aiAgents,omitempty"`
- Applications json.RawMessage `json:"applications,omitempty"`
- FlowModules json.RawMessage `json:"flowModules,omitempty"`
+ AIAgents jsontext.Value `json:"aiAgents,omitempty"`
+ Applications jsontext.Value `json:"applications,omitempty"`
+ FlowModules jsontext.Value `json:"flowModules,omitempty"`
}
type entityProfilesRec struct {
@@ -23,7 +26,7 @@ type entityProfilesRec struct {
}
// checkSecurityExtras validates the three lists and returns them ready to store; nil inputs stay nil.
-func (b *Backend) checkSecurityExtras(inst string, aiAgents, apps, modules json.RawMessage) (securityExtras, error) {
+func (b *Backend) checkSecurityExtras(inst string, aiAgents, apps, modules jsontext.Value) (securityExtras, error) {
var out securityExtras
if present(aiAgents) {
@@ -53,9 +56,9 @@ func (b *Backend) checkSecurityExtras(inst string, aiAgents, apps, modules json.
return out, nil
}
-func present(raw json.RawMessage) bool { return len(raw) > 0 && string(raw) != jsonNull }
+func present(raw jsontext.Value) bool { return len(raw) > 0 && string(raw) != jsonNull }
-func checkAIAgents(raw json.RawMessage) error {
+func checkAIAgents(raw jsontext.Value) error {
var items []struct {
Arn string `json:"Arn"`
Type string `json:"Type"`
@@ -77,7 +80,7 @@ func checkAIAgents(raw json.RawMessage) error {
return nil
}
-func checkApplications(raw json.RawMessage) error {
+func checkApplications(raw jsontext.Value) error {
var items []struct {
Namespace string `json:"Namespace"`
Type string `json:"Type"`
@@ -100,7 +103,7 @@ func checkApplications(raw json.RawMessage) error {
return nil
}
-func (b *Backend) checkFlowModules(inst string, raw json.RawMessage) error {
+func (b *Backend) checkFlowModules(inst string, raw jsontext.Value) error {
var items []struct {
FlowModuleID string `json:"FlowModuleId"`
Type string `json:"Type"`
@@ -123,13 +126,13 @@ func (b *Backend) checkFlowModules(inst string, raw json.RawMessage) error {
return nil
}
-func decodeBounded(raw json.RawMessage, dst any, field string) error {
- if err := json.Unmarshal(raw, dst); err != nil {
+func decodeBounded(raw jsontext.Value, dst any, field string) error {
+ if err := json.Unmarshal(raw, dst, jsonx.V1()); err != nil {
return invalidParamErr("%s is not valid", field)
}
- var list []json.RawMessage
- if json.Unmarshal(raw, &list) == nil && len(list) > maxSecurityExtras {
+ var list []jsontext.Value
+ if json.Unmarshal(raw, &list, jsonx.V1()) == nil && len(list) > maxSecurityExtras {
return invalidParamErr("%s can hold at most %d entries", field, maxSecurityExtras)
}
@@ -172,10 +175,10 @@ type SecurityListInput struct {
type SecurityListOut map[string]any
func (b *Backend) listSecurityExtra(
- op, key string, in *SecurityListInput, pick func(*securityExtras) json.RawMessage,
+ op, key string, in *SecurityListInput, pick func(*securityExtras) jsontext.Value,
) (*SecurityListOut, error) {
var (
- items []json.RawMessage
+ items []jsontext.Value
stamp Stamp
)
@@ -188,7 +191,7 @@ func (b *Backend) listSecurityExtra(
stamp = p.Profile.Stamp
if raw := pick(&p.Extras); raw != nil {
- err = json.Unmarshal(raw, &items)
+ err = json.Unmarshal(raw, &items, jsonx.V1())
}
return &empty{}, err
@@ -216,19 +219,19 @@ func (b *Backend) listSecurityExtra(
// ListSecurityProfileApplications lists the applications a security profile grants.
func (b *Backend) ListSecurityProfileApplications(in *SecurityListInput) (*SecurityListOut, error) {
return b.listSecurityExtra("ListSecurityProfileApplications", "Applications", in,
- func(e *securityExtras) json.RawMessage { return e.Applications })
+ func(e *securityExtras) jsontext.Value { return e.Applications })
}
// ListSecurityProfileFlowModules lists the flow modules a security profile allows.
func (b *Backend) ListSecurityProfileFlowModules(in *SecurityListInput) (*SecurityListOut, error) {
return b.listSecurityExtra("ListSecurityProfileFlowModules", "AllowedFlowModules", in,
- func(e *securityExtras) json.RawMessage { return e.FlowModules })
+ func(e *securityExtras) jsontext.Value { return e.FlowModules })
}
// ListSecurityProfileAIAgents lists the AI agents a security profile allows.
func (b *Backend) ListSecurityProfileAIAgents(in *SecurityListInput) (*SecurityListOut, error) {
return b.listSecurityExtra("ListSecurityProfileAIAgents", "AllowedAIAgents", in,
- func(e *securityExtras) json.RawMessage { return e.AIAgents })
+ func(e *securityExtras) jsontext.Value { return e.AIAgents })
}
// EntityProfilesInput is the Associate/Disassociate/ListEntitySecurityProfiles request.
diff --git a/services/connect/users.go b/services/connect/users.go
index 8fa30a0c57..1a080973ff 100644
--- a/services/connect/users.go
+++ b/services/connect/users.go
@@ -2,7 +2,7 @@ package connect
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"slices"
"unicode"
@@ -29,12 +29,12 @@ type CreateUserInput struct {
HierarchyGroupID string `json:"HierarchyGroupId"`
DirectoryUserID string `json:"DirectoryUserId"`
Username string
- AfterContactWorkConfigs json.RawMessage
- VoiceEnhancementConfigs json.RawMessage
- PhoneNumberConfigs json.RawMessage
- PersistentConnectionConfigs json.RawMessage
+ AfterContactWorkConfigs jsontext.Value
+ VoiceEnhancementConfigs jsontext.Value
+ PhoneNumberConfigs jsontext.Value
+ PersistentConnectionConfigs jsontext.Value
SecurityProfileIDs []string `json:"SecurityProfileIds"`
- AutoAcceptConfigs json.RawMessage
+ AutoAcceptConfigs jsontext.Value
}
// UpdateUserInput carries the user update requests; each op reads only its own members.
diff --git a/services/connect/views.go b/services/connect/views.go
index 5bd749287a..d082f35405 100644
--- a/services/connect/views.go
+++ b/services/connect/views.go
@@ -2,13 +2,15 @@ package connect
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"regexp"
"slices"
"strconv"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -113,7 +115,7 @@ func checkViewContent(c *ViewInputContent) (*ViewContent, error) {
return nil, invalidParamErr("Content.Template is required")
}
- if !json.Valid([]byte(c.Template)) {
+ if !jsontext.Value([]byte(c.Template)).IsValid(jsonx.V1()) {
return nil, invalidParamErr("Content.Template must be valid JSON")
}
@@ -137,7 +139,7 @@ func viewSchema(template string) string {
raw, _ := json.Marshal(map[string]any{
"$schema": "http://json-schema.org/draft-07/schema#", fieldType: "object", "properties": props,
- })
+ }, jsonx.V1())
return string(raw)
}
diff --git a/services/connect/workspaces.go b/services/connect/workspaces.go
index 1f08156e35..cb02205d87 100644
--- a/services/connect/workspaces.go
+++ b/services/connect/workspaces.go
@@ -3,11 +3,13 @@ package connect
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"slices"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -59,13 +61,13 @@ type MediaItem struct {
type Workspace struct {
Tags map[string]string `json:"Tags,omitempty"`
Stamp
- Arn string `json:"Arn"`
- ID string `json:"Id"`
- Name string `json:"Name"`
- Description string `json:"Description,omitempty"`
- Title string `json:"Title,omitempty"`
- Visibility string `json:"Visibility"`
- Theme json.RawMessage `json:"Theme,omitempty"`
+ Arn string `json:"Arn"`
+ ID string `json:"Id"`
+ Name string `json:"Name"`
+ Description string `json:"Description,omitempty"`
+ Title string `json:"Title,omitempty"`
+ Visibility string `json:"Visibility"`
+ Theme jsontext.Value `json:"Theme,omitempty"`
}
// WorkspaceSummary is the list shape of a workspace.
@@ -112,19 +114,19 @@ type wsRef struct {
instPath
}
-func checkTheme(raw json.RawMessage) error {
+func checkTheme(raw jsontext.Value) error {
if len(raw) == 0 || string(raw) == jsonNull {
return nil
}
- var modes map[string]json.RawMessage
- if err := json.Unmarshal(raw, &modes); err != nil {
+ var modes map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &modes, jsonx.V1()); err != nil {
return invalidParamErr("Theme must be an object")
}
for k, v := range modes {
- var cfg map[string]json.RawMessage
- if k != "Light" && k != "Dark" || json.Unmarshal(v, &cfg) != nil {
+ var cfg map[string]jsontext.Value
+ if k != "Light" && k != "Dark" || json.Unmarshal(v, &cfg, jsonx.V1()) != nil {
return invalidParamErr("Theme may only hold Light and Dark theme configurations")
}
}
@@ -151,7 +153,7 @@ type CreateWorkspaceInput struct {
Description string
Title string
instPath
- Theme json.RawMessage
+ Theme jsontext.Value
}
type createWorkspaceOut struct {
@@ -261,7 +263,7 @@ func (b *Backend) UpdateWorkspaceMetadata(ctx context.Context, in *UpdateWorkspa
// UpdateWorkspaceThemeInput is the UpdateWorkspaceTheme request.
type UpdateWorkspaceThemeInput struct {
wsRef
- Theme json.RawMessage
+ Theme jsontext.Value
}
// UpdateWorkspaceTheme replaces the theme of a workspace.
@@ -371,8 +373,8 @@ func (b *Backend) checkPageFields(inst string, in *pageFields) error {
}
if in.InputData != nil {
- var obj map[string]json.RawMessage
- if json.Unmarshal([]byte(*in.InputData), &obj) != nil {
+ var obj map[string]jsontext.Value
+ if json.Unmarshal([]byte(*in.InputData), &obj, jsonx.V1()) != nil {
return invalidParamErr("InputData must be a JSON object")
}
}
diff --git a/services/cosmosdb/account_ops_test.go b/services/cosmosdb/account_ops_test.go
index e651ad8e5b..93126cb2bb 100644
--- a/services/cosmosdb/account_ops_test.go
+++ b/services/cosmosdb/account_ops_test.go
@@ -1,7 +1,7 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -9,6 +9,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_AccountRoot covers the database-account root resource ("" or
@@ -72,7 +74,7 @@ func TestHandler_AccountRoot_EndpointEchoesRequestHost(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
const wantEndpoint = "http://localhost:32771/"
diff --git a/services/cosmosdb/batch.go b/services/cosmosdb/batch.go
index a3810d3704..578977b7ca 100644
--- a/services/cosmosdb/batch.go
+++ b/services/cosmosdb/batch.go
@@ -2,7 +2,8 @@ package cosmosdb
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -11,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// maxBatchOperations is the Cosmos limit on operations in one transactional batch.
@@ -33,10 +35,10 @@ func isBatchRequest(r *http.Request) bool {
}
type batchOpWire struct {
- OperationType string `json:"operationType"`
- ID string `json:"id"`
- IfMatch string `json:"ifMatch"`
- ResourceBody json.RawMessage `json:"resourceBody"`
+ OperationType string `json:"operationType"`
+ ID string `json:"id"`
+ IfMatch string `json:"ifMatch"`
+ ResourceBody jsontext.Value `json:"resourceBody"`
}
// batchOp is a decoded batch operation.
@@ -59,8 +61,8 @@ type BatchResult struct {
func parseBatchOps(raw []byte) ([]batchOp, error) {
var wire []batchOpWire
- dec := json.NewDecoder(bytes.NewReader(raw))
- if err := dec.Decode(&wire); err != nil || rejectTrailingJSON(dec) != nil {
+ dec := jsontext.NewDecoder(bytes.NewReader(raw), jsonx.V1())
+ if err := json.UnmarshalDecode(dec, &wire, jsonx.V1()); err != nil || rejectTrailingJSON(dec) != nil {
return nil, fmt.Errorf("%w: the batch body is not a JSON array of operations", ErrInvalidDocument)
}
@@ -190,7 +192,7 @@ func (tx *containerTx) runBatchOp(pk string, op batchOp) (BatchResult, error) {
}
body := documentAsMap(info)
- size, _ := json.Marshal(body)
+ size, _ := json.Marshal(body, jsonx.V1())
return BatchResult{
StatusCode: status, ETag: info.ETag, Body: body,
diff --git a/services/cosmosdb/changelog.go b/services/cosmosdb/changelog.go
index b26d14e64f..64be63997b 100644
--- a/services/cosmosdb/changelog.go
+++ b/services/cosmosdb/changelog.go
@@ -1,9 +1,11 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -125,10 +127,10 @@ func partitionKeyObject(paths []string, pkJSON string) map[string]any {
var vals []any
if len(paths) == 1 {
var v any
- if json.Unmarshal([]byte(pkJSON), &v) == nil {
+ if json.Unmarshal([]byte(pkJSON), &v, jsonx.V1()) == nil {
vals = []any{v}
}
- } else if json.Unmarshal([]byte(pkJSON), &vals) != nil {
+ } else if json.Unmarshal([]byte(pkJSON), &vals, jsonx.V1()) != nil {
vals = nil
}
diff --git a/services/cosmosdb/computed_properties_test.go b/services/cosmosdb/computed_properties_test.go
index a6aeefaad9..167ed15110 100644
--- a/services/cosmosdb/computed_properties_test.go
+++ b/services/cosmosdb/computed_properties_test.go
@@ -1,7 +1,8 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -20,10 +22,10 @@ func computedBackend(t *testing.T, indexing string, props string) *cosmosdb.InMe
require.NoError(t, err)
spec := cosmosdb.ContainerSpec{
- ID: "c", PartitionKeyPath: "/pk", ComputedProperties: json.RawMessage(props),
+ ID: "c", PartitionKeyPath: "/pk", ComputedProperties: jsontext.Value(props),
}
if indexing != "" {
- spec.IndexingPolicy = json.RawMessage(indexing)
+ spec.IndexingPolicy = jsontext.Value(indexing)
}
_, err = b.CreateContainer("d", spec)
@@ -77,7 +79,7 @@ func TestComputedProperties_IndexSelection(t *testing.T) {
name: "group by a computed property",
indexing: defaultPolicy,
query: `SELECT VALUE COUNT(1) FROM c GROUP BY c.cp_lower`,
- want: []any{json.Number("1"), json.Number("1"), json.Number("1")},
+ want: []any{jsonx.Number("1"), jsonx.Number("1"), jsonx.Number("1")},
},
{
name: "select star leaves computed properties out", indexing: defaultPolicy,
@@ -207,7 +209,7 @@ func TestComputedProperties_DefinitionRules(t *testing.T) {
props = append(props, map[string]string{"name": fmt.Sprintf("p%d", i), "query": "SELECT VALUE 1 FROM c"})
}
- raw, err := json.Marshal(props)
+ raw, err := json.Marshal(props, jsonx.V1())
require.NoError(t, err)
s := newLiveServer(t)
diff --git a/services/cosmosdb/computed_rules.go b/services/cosmosdb/computed_rules.go
index d60e14b0ea..e39a932da9 100644
--- a/services/cosmosdb/computed_rules.go
+++ b/services/cosmosdb/computed_rules.go
@@ -1,7 +1,7 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"strings"
)
@@ -133,7 +133,7 @@ func (p indexingPolicy) isComputed(path string) bool {
}
// checkComputedIndexes rejects a spatial index on a computed property.
-func checkComputedIndexes(pol indexingPolicy, raw json.RawMessage) error {
+func checkComputedIndexes(pol indexingPolicy, raw jsontext.Value) error {
props, err := parseComputedProperties(raw)
if err != nil || len(props) == 0 {
return err
diff --git a/services/cosmosdb/conflicts.go b/services/cosmosdb/conflicts.go
index 848c2c9c28..bd0c86ff7a 100644
--- a/services/cosmosdb/conflicts.go
+++ b/services/cosmosdb/conflicts.go
@@ -1,11 +1,14 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Conflict resolution modes (azcosmos ConflictResolutionMode) and the default last-writer-wins path.
@@ -24,11 +27,11 @@ type conflictPolicy struct {
Procedure string `json:"conflictResolutionProcedure"`
}
-func parseConflictPolicy(raw json.RawMessage) conflictPolicy {
+func parseConflictPolicy(raw jsontext.Value) conflictPolicy {
p := conflictPolicy{Mode: conflictModeLWW, Path: conflictPathTS}
if len(raw) > 0 {
- _ = json.Unmarshal(raw, &p)
+ _ = json.Unmarshal(raw, &p, jsonx.V1())
}
if p.Mode == "" {
@@ -76,7 +79,7 @@ func conflictValue(d *storedDocument, path string) float64 {
}
switch n := cur.(type) {
- case json.Number:
+ case jsonx.Number:
if f, err := n.Float64(); err == nil {
return f
}
@@ -279,13 +282,13 @@ func (b *InMemoryBackend) resolveRegion(region string) string {
// validateConflictPolicy checks a container's conflictResolutionPolicy: mode LastWriterWins or Custom, a path that
// starts with /.
-func validateConflictPolicy(raw json.RawMessage) error {
+func validateConflictPolicy(raw jsontext.Value) error {
if len(raw) == 0 || isJSONNull(raw) {
return nil
}
var p conflictPolicy
- if err := json.Unmarshal(raw, &p); err != nil {
+ if err := json.Unmarshal(raw, &p, jsonx.V1()); err != nil {
return fmt.Errorf("%w: conflictResolutionPolicy is malformed", ErrInvalidContainerPolicy)
}
@@ -304,7 +307,7 @@ func validateConflictPolicy(raw json.RawMessage) error {
// conflictPolicyChanged reports whether a replace request names a policy different from the stored one; the policy
// can only be set at container creation (Learn, "Manage conflicts between regions").
-func conflictPolicyChanged(stored, requested json.RawMessage) bool {
+func conflictPolicyChanged(stored, requested jsontext.Value) bool {
return len(requested) > 0 && !isJSONNull(requested) &&
parseConflictPolicy(stored) != parseConflictPolicy(requested)
}
diff --git a/services/cosmosdb/container_ops.go b/services/cosmosdb/container_ops.go
index b196eb69cc..aa7325ce77 100644
--- a/services/cosmosdb/container_ops.go
+++ b/services/cosmosdb/container_ops.go
@@ -1,7 +1,8 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -10,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// partitionKeyDefSpec is the "partitionKey" sub-object of a container definition:
@@ -24,9 +26,9 @@ type containerBodyRequest struct {
DefaultTTL *int `json:"defaultTtl"`
ID string `json:"id"`
PartitionKey partitionKeyDefSpec `json:"partitionKey"`
- IndexingPolicy json.RawMessage `json:"indexingPolicy"`
+ IndexingPolicy jsontext.Value `json:"indexingPolicy"`
- ConflictResolutionPolicy json.RawMessage `json:"conflictResolutionPolicy"`
+ ConflictResolutionPolicy jsontext.Value `json:"conflictResolutionPolicy"`
ContainerPolicies
}
@@ -111,7 +113,7 @@ func (h *Handler) readContainerRequest(c *echo.Context) (containerBodyRequest, b
return req, false
}
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
_ = h.writeError(c, http.StatusBadRequest, "BadRequest", "The input is not valid JSON.")
return req, false
@@ -288,12 +290,12 @@ func resourceTS(modified time.Time) int64 {
}
// echoOrDefault returns raw, or def when raw is unset.
-func echoOrDefault(raw json.RawMessage, def string) json.RawMessage {
+func echoOrDefault(raw jsontext.Value, def string) jsontext.Value {
if len(raw) > 0 {
return raw
}
- return json.RawMessage(def)
+ return jsontext.Value(def)
}
// addPolicyFields echoes the container policies; unset unique-key and geospatial settings show their defaults.
@@ -301,7 +303,7 @@ func addPolicyFields(body map[string]any, p ContainerPolicies) {
body["uniqueKeyPolicy"] = echoOrDefault(p.UniqueKeyPolicy, defaultUniqueKeyPol)
body["geospatialConfig"] = echoOrDefault(p.GeospatialConfig, defaultGeoConfig)
- for key, raw := range map[string]json.RawMessage{
+ for key, raw := range map[string]jsontext.Value{
"vectorEmbeddingPolicy": p.VectorEmbeddingPolicy, "fullTextPolicy": p.FullTextPolicy,
"computedProperties": p.ComputedProperties,
} {
@@ -331,7 +333,7 @@ func containerBody(info ContainerInfo) map[string]any {
kind = partitionKindMultiHash
}
- indexing := json.RawMessage(defaultIndexingPolicy)
+ indexing := jsontext.Value(defaultIndexingPolicy)
if len(info.IndexingPolicy) > 0 {
indexing = info.IndexingPolicy
}
@@ -357,7 +359,7 @@ func containerBody(info ContainerInfo) map[string]any {
addPolicyFields(body, info.ContainerPolicies)
- body["conflictResolutionPolicy"] = json.RawMessage(defaultConflictResolutionPolicy)
+ body["conflictResolutionPolicy"] = jsontext.Value(defaultConflictResolutionPolicy)
if len(info.ConflictResolutionPolicy) > 0 {
body["conflictResolutionPolicy"] = info.ConflictResolutionPolicy
}
diff --git a/services/cosmosdb/container_policy.go b/services/cosmosdb/container_policy.go
index 299d4d190b..b27e0708e3 100644
--- a/services/cosmosdb/container_policy.go
+++ b/services/cosmosdb/container_policy.go
@@ -2,11 +2,14 @@ package cosmosdb
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"reflect"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -22,11 +25,11 @@ const (
// ContainerPolicies are the optional container settings that are stored and echoed verbatim; the unique-key
// policy is enforced on writes and computed properties are evaluated by queries.
type ContainerPolicies struct {
- UniqueKeyPolicy json.RawMessage `json:"uniqueKeyPolicy"`
- GeospatialConfig json.RawMessage `json:"geospatialConfig"`
- VectorEmbeddingPolicy json.RawMessage `json:"vectorEmbeddingPolicy"`
- FullTextPolicy json.RawMessage `json:"fullTextPolicy"`
- ComputedProperties json.RawMessage `json:"computedProperties"`
+ UniqueKeyPolicy jsontext.Value `json:"uniqueKeyPolicy"`
+ GeospatialConfig jsontext.Value `json:"geospatialConfig"`
+ VectorEmbeddingPolicy jsontext.Value `json:"vectorEmbeddingPolicy"`
+ FullTextPolicy jsontext.Value `json:"fullTextPolicy"`
+ ComputedProperties jsontext.Value `json:"computedProperties"`
}
type uniqueKeyPolicy struct {
@@ -40,14 +43,14 @@ type computedProperty struct {
Query string `json:"query"`
}
-func isJSONNull(raw json.RawMessage) bool {
+func isJSONNull(raw jsontext.Value) bool {
t := bytes.TrimSpace(raw)
return len(t) == 0 || bytes.Equal(t, []byte(nullText))
}
func (p ContainerPolicies) stored() ContainerPolicies {
- for _, f := range []*json.RawMessage{
+ for _, f := range []*jsontext.Value{
&p.UniqueKeyPolicy, &p.GeospatialConfig, &p.VectorEmbeddingPolicy, &p.FullTextPolicy, &p.ComputedProperties,
} {
if isJSONNull(*f) {
@@ -81,13 +84,13 @@ func (p ContainerPolicies) validate() error {
// validateSearchIndexes checks the indexing policy's vector and full-text indexes against the policies they
// refer to.
-func validateSearchIndexes(indexing json.RawMessage, p ContainerPolicies) error {
+func validateSearchIndexes(indexing jsontext.Value, p ContainerPolicies) error {
if len(indexing) == 0 {
return nil
}
var pol indexingPolicy
- if err := json.Unmarshal(indexing, &pol); err != nil {
+ if err := json.Unmarshal(indexing, &pol, jsonx.V1()); err != nil {
return fmt.Errorf("%w: indexingPolicy is malformed", ErrInvalidContainerPolicy)
}
@@ -148,26 +151,27 @@ func (c *storedContainer) checkReplaceIndexes(spec ContainerSpec) error {
}
// sameJSON reports whether two raw JSON values are semantically equal.
-func sameJSON(a, b json.RawMessage) bool {
+func sameJSON(a, b jsontext.Value) bool {
var va, vb any
- return json.Unmarshal(a, &va) == nil && json.Unmarshal(b, &vb) == nil && reflect.DeepEqual(va, vb)
+ return json.Unmarshal(a, &va, jsonx.V1()) == nil && json.Unmarshal(b, &vb, jsonx.V1()) == nil &&
+ reflect.DeepEqual(va, vb)
}
-func parseUniqueKeyPolicy(raw json.RawMessage) (uniqueKeyPolicy, error) {
+func parseUniqueKeyPolicy(raw jsontext.Value) (uniqueKeyPolicy, error) {
var pol uniqueKeyPolicy
if isJSONNull(raw) {
return pol, nil
}
- if err := json.Unmarshal(raw, &pol); err != nil {
+ if err := json.Unmarshal(raw, &pol, jsonx.V1()); err != nil {
return pol, fmt.Errorf("%w: uniqueKeyPolicy is malformed", ErrInvalidContainerPolicy)
}
return pol, nil
}
-func validateUniqueKeyPolicy(raw json.RawMessage) error {
+func validateUniqueKeyPolicy(raw jsontext.Value) error {
pol, err := parseUniqueKeyPolicy(raw)
if err != nil {
return err
@@ -192,7 +196,7 @@ func validateUniqueKeyPolicy(raw json.RawMessage) error {
return nil
}
-func validateGeospatialConfig(raw json.RawMessage) error {
+func validateGeospatialConfig(raw jsontext.Value) error {
if isJSONNull(raw) {
return nil
}
@@ -201,7 +205,12 @@ func validateGeospatialConfig(raw json.RawMessage) error {
Type string `json:"type"`
}
- if err := json.Unmarshal(raw, &cfg); err != nil || (cfg.Type != geoTypeGeography && cfg.Type != geoTypeGeometry) {
+ if err := json.Unmarshal(
+ raw,
+ &cfg,
+ jsonx.V1(),
+ ); err != nil ||
+ (cfg.Type != geoTypeGeography && cfg.Type != geoTypeGeometry) {
return fmt.Errorf("%w: geospatialConfig.type must be %s or %s",
ErrInvalidContainerPolicy, geoTypeGeography, geoTypeGeometry)
}
@@ -211,13 +220,13 @@ func validateGeospatialConfig(raw json.RawMessage) error {
// parseComputedProperties decodes and validates computedProperties: each query must be
// SELECT VALUE FROM with nothing else.
-func parseComputedProperties(raw json.RawMessage) ([]computedProperty, error) {
+func parseComputedProperties(raw jsontext.Value) ([]computedProperty, error) {
if isJSONNull(raw) {
return nil, nil
}
var props []computedProperty
- if err := json.Unmarshal(raw, &props); err != nil {
+ if err := json.Unmarshal(raw, &props, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: computedProperties is malformed", ErrInvalidContainerPolicy)
}
@@ -273,7 +282,7 @@ func isBareSelectValue(stmt *sqlSelectStmt) bool {
}
// policiesEqualUnique reports whether two raw unique-key policies are semantically identical.
-func policiesEqualUnique(a, b json.RawMessage) bool {
+func policiesEqualUnique(a, b jsontext.Value) bool {
pa, errA := parseUniqueKeyPolicy(a)
pb, errB := parseUniqueKeyPolicy(b)
@@ -309,7 +318,7 @@ func (c *storedContainer) applyPolicies(p ContainerPolicies, replacing bool) err
c.UniqueKeyPolicy = p.UniqueKeyPolicy
}
- for _, f := range []struct{ dst, src *json.RawMessage }{
+ for _, f := range []struct{ dst, src *jsontext.Value }{
{&c.GeospatialConfig, &p.GeospatialConfig},
{&c.VectorEmbeddingPolicy, &p.VectorEmbeddingPolicy},
{&c.FullTextPolicy, &p.FullTextPolicy},
@@ -324,7 +333,7 @@ func (c *storedContainer) applyPolicies(p ContainerPolicies, replacing bool) err
}
// policyChanged reports whether a replace request carries a setting different from the stored one.
-func policyChanged(stored, requested json.RawMessage) bool {
+func policyChanged(stored, requested jsontext.Value) bool {
return requested != nil && !sameJSON(stored, requested)
}
diff --git a/services/cosmosdb/container_policy_test.go b/services/cosmosdb/container_policy_test.go
index 2eb021bf59..58e9b79231 100644
--- a/services/cosmosdb/container_policy_test.go
+++ b/services/cosmosdb/container_policy_test.go
@@ -1,7 +1,7 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -357,7 +358,7 @@ func TestComputedProperties_UsableInQueries(t *testing.T) {
func mustJSON(t *testing.T, v any) []byte {
t.Helper()
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
return out
diff --git a/services/cosmosdb/database_ops.go b/services/cosmosdb/database_ops.go
index 950b06c581..e6bd97a137 100644
--- a/services/cosmosdb/database_ops.go
+++ b/services/cosmosdb/database_ops.go
@@ -1,13 +1,14 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// createDatabaseBody is the request body shape for POST /dbs.
@@ -58,7 +59,7 @@ func (h *Handler) createDatabase(c *echo.Context) error {
}
var req createDatabaseBody
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "BadRequest", "The input is not valid JSON.")
}
diff --git a/services/cosmosdb/document_ops.go b/services/cosmosdb/document_ops.go
index d82601a695..6ec30ce5ef 100644
--- a/services/cosmosdb/document_ops.go
+++ b/services/cosmosdb/document_ops.go
@@ -2,7 +2,8 @@ package cosmosdb
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -11,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -75,11 +77,10 @@ func partitionKeyFromHeader(r *http.Request) (string, error) {
}
func parsePartitionKeyHeader(raw string) (string, error) {
- dec := json.NewDecoder(bytes.NewReader([]byte(raw)))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader([]byte(raw)), jsonx.V1())
var arr []any
- if err := dec.Decode(&arr); err != nil {
+ if err := json.UnmarshalDecode(dec, &arr, jsonx.V1(), jsonx.UseNumber()); err != nil {
return "", fmt.Errorf("%w: malformed %s header: %w", ErrInvalidDocument, headerPartitionKey, err)
}
@@ -106,7 +107,7 @@ func parsePartitionKeyHeader(raw string) (string, error) {
return canonicalPartitionKeyJSON(arr[0])
}
- data, err := json.Marshal(arr)
+ data, err := json.Marshal(arr, jsonx.V1())
if err != nil {
return "", fmt.Errorf("cosmosdb: encode partition key: %w", err)
}
@@ -370,10 +371,15 @@ func (h *Handler) queryDocuments(c *echo.Context, dbID, collID string) error {
var req queryRequestBody
- dec := json.NewDecoder(bytes.NewReader(body))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(body), jsonx.V1())
- if decErr := dec.Decode(&req); decErr != nil || rejectTrailingJSON(dec) != nil {
+ if decErr := json.UnmarshalDecode(
+ dec,
+ &req,
+ jsonx.V1(),
+ jsonx.UseNumber(),
+ ); decErr != nil ||
+ rejectTrailingJSON(dec) != nil {
return h.writeError(c, http.StatusBadRequest, "BadRequest", "The input is not valid JSON.")
}
diff --git a/services/cosmosdb/fulltext.go b/services/cosmosdb/fulltext.go
index aa241c758c..24c3288529 100644
--- a/services/cosmosdb/fulltext.go
+++ b/services/cosmosdb/fulltext.go
@@ -1,12 +1,15 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"math"
"regexp"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -32,20 +35,20 @@ type fullTextPolicy struct {
FullTextPaths []fullTextPath `json:"fullTextPaths"`
}
-func parseFullTextPolicy(raw json.RawMessage) (fullTextPolicy, error) {
+func parseFullTextPolicy(raw jsontext.Value) (fullTextPolicy, error) {
var pol fullTextPolicy
if isJSONNull(raw) {
return pol, nil
}
- if err := json.Unmarshal(raw, &pol); err != nil {
+ if err := json.Unmarshal(raw, &pol, jsonx.V1()); err != nil {
return pol, fmt.Errorf("%w: fullTextPolicy is malformed", ErrInvalidContainerPolicy)
}
return pol, nil
}
-func validateFullTextPolicy(raw json.RawMessage) error {
+func validateFullTextPolicy(raw jsontext.Value) error {
pol, err := parseFullTextPolicy(raw)
if err != nil {
return err
diff --git a/services/cosmosdb/fulltext_analysis_test.go b/services/cosmosdb/fulltext_analysis_test.go
index d9ad640a26..ff6fa5a61c 100644
--- a/services/cosmosdb/fulltext_analysis_test.go
+++ b/services/cosmosdb/fulltext_analysis_test.go
@@ -1,7 +1,7 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"testing"
@@ -21,10 +21,10 @@ func ftBackend(t *testing.T, lang string, texts ...string) *cosmosdb.InMemoryBac
policy := fmt.Sprintf(`{"defaultLanguage":%q,"fullTextPaths":[{"path":"/text","language":%q}]}`, lang, lang)
_, err = b.CreateContainer("d", cosmosdb.ContainerSpec{
ID: "c", PartitionKeyPath: "/pk",
- IndexingPolicy: json.RawMessage(
+ IndexingPolicy: jsontext.Value(
`{"indexingMode":"consistent","includedPaths":[{"path":"/*"}],"excludedPaths":[],` +
`"fullTextIndexes":[{"path":"/text"}]}`),
- FullTextPolicy: json.RawMessage(policy),
+ FullTextPolicy: jsontext.Value(policy),
})
require.NoError(t, err)
diff --git a/services/cosmosdb/handler.go b/services/cosmosdb/handler.go
index 33d7407a26..adf55f58c0 100644
--- a/services/cosmosdb/handler.go
+++ b/services/cosmosdb/handler.go
@@ -4,7 +4,7 @@ import (
"context"
"crypto/rand"
"crypto/tls"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net"
@@ -15,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/devtls"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/odatatable"
@@ -827,7 +828,7 @@ func (h *Handler) writeJSON(c *echo.Context, status int, v any) error {
return c.Blob(status, "application/json", body)
}
-func jsonMarshal(v any) ([]byte, error) { return json.Marshal(v) }
+func jsonMarshal(v any) ([]byte, error) { return json.Marshal(v, jsonx.V1()) }
// cosmosErrorBody is Cosmos DB's JSON error envelope shape:
// {"code":"NotFound","message":"..."}.
@@ -846,7 +847,7 @@ func (h *Handler) writeError(c *echo.Context, status int, code, message string)
// itself somehow failed to marshal, though it never does in practice since
// it's built from plain strings).
func (h *Handler) writeErrorNoRecurse(c *echo.Context, status int, code, message string) error {
- body, _ := json.Marshal(cosmosErrorBody{Code: code, Message: message})
+ body, _ := json.Marshal(cosmosErrorBody{Code: code, Message: message}, jsonx.V1())
return c.Blob(status, "application/json", body)
}
diff --git a/services/cosmosdb/handler_test.go b/services/cosmosdb/handler_test.go
index e6237d9eda..841c6a8257 100644
--- a/services/cosmosdb/handler_test.go
+++ b/services/cosmosdb/handler_test.go
@@ -2,7 +2,7 @@ package cosmosdb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -54,7 +55,7 @@ func decodeBody(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
@@ -107,7 +108,7 @@ func TestHandler_DatabaseLifecycle(t *testing.T) {
h := newTestHandler(t)
- createBody, err := json.Marshal(map[string]string{"id": "mydb"})
+ createBody, err := json.Marshal(map[string]string{"id": "mydb"}, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/dbs", nil, createBody)
@@ -176,7 +177,7 @@ func TestHandler_CreateDatabase_InvalidInput(t *testing.T) {
func createTestDatabase(t *testing.T, h *cosmosdb.Handler) {
t.Helper()
- body, err := json.Marshal(map[string]string{"id": "mydb"})
+ body, err := json.Marshal(map[string]string{"id": "mydb"}, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/dbs", nil, body)
@@ -189,7 +190,7 @@ func createTestContainer(t *testing.T, h *cosmosdb.Handler) {
body, err := json.Marshal(map[string]any{
"id": "mycoll",
"partitionKey": map[string]any{"paths": []string{"/pk"}, "kind": "Hash"},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/dbs/mydb/colls", nil, body)
@@ -205,7 +206,7 @@ func TestHandler_ContainerLifecycle(t *testing.T) {
body, err := json.Marshal(map[string]any{
"id": "mycoll",
"partitionKey": map[string]any{"paths": []string{"/pk"}, "kind": "Hash"},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/dbs/mydb/colls", nil, body)
@@ -255,7 +256,7 @@ func TestHandler_CreateContainer_InvalidPartitionKey(t *testing.T) {
body, err := json.Marshal(map[string]any{
"id": "mycoll",
"partitionKey": map[string]any{"paths": []string{"/a", "/b"}, "kind": "Hash"},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/dbs/mydb/colls", nil, body)
@@ -276,7 +277,7 @@ func TestHandler_CreateContainer_EmptyPartitionKeyPath(t *testing.T) {
body, err := json.Marshal(map[string]any{
"id": "mycoll",
"partitionKey": map[string]any{"paths": []string{""}, "kind": "Hash"},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/dbs/mydb/colls", nil, body)
@@ -309,7 +310,7 @@ func TestHandler_DocumentLifecycle(t *testing.T) {
createTestDatabase(t, h)
createTestContainer(t, h)
- docBody, err := json.Marshal(map[string]any{"id": "doc1", "pk": "partA", "value": 42})
+ docBody, err := json.Marshal(map[string]any{"id": "doc1", "pk": "partA", "value": 42}, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/dbs/mydb/colls/mycoll/docs", nil, docBody)
@@ -349,7 +350,7 @@ func TestHandler_DocumentLifecycle(t *testing.T) {
assert.Equal(t, http.StatusNotFound, rec.Code)
// Replace with wrong If-Match -> 412.
- replaceBody, err := json.Marshal(map[string]any{"pk": "partA", "value": 100})
+ replaceBody, err := json.Marshal(map[string]any{"pk": "partA", "value": 100}, jsonx.V1())
require.NoError(t, err)
wrongEtagHeaders := map[string]string{
@@ -436,7 +437,7 @@ func TestHandler_ReadFeed(t *testing.T) {
createTestContainer(t, h)
for _, id := range []string{"a", "b"} {
- body, err := json.Marshal(map[string]any{"id": id, "pk": "x"})
+ body, err := json.Marshal(map[string]any{"id": id, "pk": "x"}, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/dbs/mydb/colls/mycoll/docs", nil, body)
@@ -458,14 +459,14 @@ func TestHandler_Query(t *testing.T) {
createTestContainer(t, h)
for i, name := range []string{"alice", "bob"} {
- body, err := json.Marshal(map[string]any{"id": name, "pk": "x", "n": i})
+ body, err := json.Marshal(map[string]any{"id": name, "pk": "x", "n": i}, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/dbs/mydb/colls/mycoll/docs", nil, body)
require.Equal(t, http.StatusCreated, rec.Code)
}
- queryBody, err := json.Marshal(map[string]any{"query": "SELECT * FROM c WHERE c.id = 'alice'"})
+ queryBody, err := json.Marshal(map[string]any{"query": "SELECT * FROM c WHERE c.id = 'alice'"}, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, http.MethodPost, "/dbs/mydb/colls/mycoll/docs",
@@ -476,7 +477,7 @@ func TestHandler_Query(t *testing.T) {
assert.InDelta(t, 1, result["_count"], 0)
// A malformed query yields 400, never a panic.
- badBody, err := json.Marshal(map[string]any{"query": "NOT VALID SQL AT ALL ((("})
+ badBody, err := json.Marshal(map[string]any{"query": "NOT VALID SQL AT ALL ((("}, jsonx.V1())
require.NoError(t, err)
rec = doRequest(t, h, http.MethodPost, "/dbs/mydb/colls/mycoll/docs",
diff --git a/services/cosmosdb/helpers_test.go b/services/cosmosdb/helpers_test.go
index 36e43d88b6..d4a6c310ef 100644
--- a/services/cosmosdb/helpers_test.go
+++ b/services/cosmosdb/helpers_test.go
@@ -2,13 +2,16 @@ package cosmosdb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// newEchoContextForRequest wraps req in an *echo.Context for tests that call
@@ -32,12 +35,11 @@ func newEchoContextForRequest(t *testing.T, req *http.Request) *echo.Context {
func decodeJSONBody(t *testing.T, raw string) map[string]any {
t.Helper()
- dec := json.NewDecoder(bytes.NewReader([]byte(raw)))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader([]byte(raw)))
var m map[string]any
- require.NoError(t, dec.Decode(&m))
+ require.NoError(t, json.UnmarshalDecode(dec, &m, jsonx.V1(), jsonx.UseNumber()))
return m
}
diff --git a/services/cosmosdb/indexing.go b/services/cosmosdb/indexing.go
index 66e034d22a..ace0946244 100644
--- a/services/cosmosdb/indexing.go
+++ b/services/cosmosdb/indexing.go
@@ -1,9 +1,12 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -48,14 +51,14 @@ type indexingPolicy struct {
}
// parseIndexingPolicy decodes the stored policy; an unset or undecodable one yields the default (index all).
-func parseIndexingPolicy(raw json.RawMessage) indexingPolicy {
+func parseIndexingPolicy(raw jsontext.Value) indexingPolicy {
if len(raw) == 0 {
- raw = json.RawMessage(defaultIndexingPolicy)
+ raw = jsontext.Value(defaultIndexingPolicy)
}
var pol indexingPolicy
- if err := json.Unmarshal(raw, &pol); err != nil {
- _ = json.Unmarshal([]byte(defaultIndexingPolicy), &pol)
+ if err := json.Unmarshal(raw, &pol, jsonx.V1()); err != nil {
+ _ = json.Unmarshal([]byte(defaultIndexingPolicy), &pol, jsonx.V1())
}
return pol
@@ -64,7 +67,7 @@ func parseIndexingPolicy(raw json.RawMessage) indexingPolicy {
// checkQueryIndexing applies the indexing policy to stmt: multi-property ORDER BY needs a composite index, and a
// query whose filter cannot use the index needs the scan switch.
func checkQueryIndexing(
- stmt *sqlSelectStmt, raw json.RawMessage, opts QueryOptions, computed map[string]sqlExpr,
+ stmt *sqlSelectStmt, raw jsontext.Value, opts QueryOptions, computed map[string]sqlExpr,
) error {
pol := parseIndexingPolicy(raw)
pol.computed = make(map[string]struct{}, len(computed))
diff --git a/services/cosmosdb/live_server_test.go b/services/cosmosdb/live_server_test.go
index 056b26c1a9..23c5be7917 100644
--- a/services/cosmosdb/live_server_test.go
+++ b/services/cosmosdb/live_server_test.go
@@ -1,7 +1,7 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -35,7 +36,7 @@ func (r rawResponse) object(t *testing.T) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(r.body, &m), string(r.body))
+ require.NoError(t, json.Unmarshal(r.body, &m, jsonx.V1()), string(r.body))
return m
}
@@ -134,13 +135,13 @@ func stripSystem[T ~string | ~[]byte](t *testing.T, raw T) string {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal([]byte(raw), &m))
+ require.NoError(t, json.Unmarshal([]byte(raw), &m, jsonx.V1()))
for _, k := range []string{"_rid", "_self", "_etag", "_ts", "_attachments", "_lsn"} {
delete(m, k)
}
- out, err := json.Marshal(m)
+ out, err := json.Marshal(m, jsonx.V1())
require.NoError(t, err)
return string(out)
diff --git a/services/cosmosdb/models.go b/services/cosmosdb/models.go
index 0650f84281..dd5a37f60c 100644
--- a/services/cosmosdb/models.go
+++ b/services/cosmosdb/models.go
@@ -4,7 +4,8 @@ import (
"bytes"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -12,6 +13,8 @@ import (
"strconv"
"sync/atomic"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// System property names auto-populated on every Cosmos resource. Named
@@ -108,9 +111,9 @@ type ContainerSpec struct {
ID string
PartitionKeyPath string
PartitionKeyPaths []string
- IndexingPolicy json.RawMessage
+ IndexingPolicy jsontext.Value
- ConflictResolutionPolicy json.RawMessage
+ ConflictResolutionPolicy jsontext.Value
ContainerPolicies
}
@@ -122,10 +125,10 @@ type ContainerInfo struct {
RID string
PartitionKeyPath string
PartitionKeyPaths []string
- IndexingPolicy json.RawMessage
+ IndexingPolicy jsontext.Value
Modified time.Time
- ConflictResolutionPolicy json.RawMessage
+ ConflictResolutionPolicy jsontext.Value
ContainerPolicies
}
@@ -168,16 +171,16 @@ type storedContainer struct {
Sprocs map[string]*storedScript `json:"Sprocs,omitempty"`
PartitionKeyPath string
ID string
- GeospatialConfig json.RawMessage `json:"GeospatialConfig,omitempty"`
- UniqueKeyPolicy json.RawMessage `json:"UniqueKeyPolicy,omitempty"`
- ConflictResolutionPolicy json.RawMessage `json:"ConflictResolutionPolicy,omitempty"`
+ GeospatialConfig jsontext.Value `json:"GeospatialConfig,omitempty"`
+ UniqueKeyPolicy jsontext.Value `json:"UniqueKeyPolicy,omitempty"`
+ ConflictResolutionPolicy jsontext.Value `json:"ConflictResolutionPolicy,omitempty"`
Conflicts []*conflictRecord `json:"Conflicts,omitempty"`
PartitionKeyPaths []string `json:"PartitionKeyPaths,omitempty"`
- VectorEmbeddingPolicy json.RawMessage `json:"VectorEmbeddingPolicy,omitempty"`
- FullTextPolicy json.RawMessage `json:"FullTextPolicy,omitempty"`
- ComputedProperties json.RawMessage `json:"ComputedProperties,omitempty"`
+ VectorEmbeddingPolicy jsontext.Value `json:"VectorEmbeddingPolicy,omitempty"`
+ FullTextPolicy jsontext.Value `json:"FullTextPolicy,omitempty"`
+ ComputedProperties jsontext.Value `json:"ComputedProperties,omitempty"`
ChangeLog []*changeRecord `json:"ChangeLog,omitempty"`
- IndexingPolicy json.RawMessage `json:"IndexingPolicy,omitempty"`
+ IndexingPolicy jsontext.Value `json:"IndexingPolicy,omitempty"`
keep time.Duration
ChangeFloor int64 `json:"ChangeFloor,omitempty"`
ChangeSeq int64 `json:"ChangeSeq,omitempty"`
@@ -203,18 +206,18 @@ type storedDocument struct {
// snapshot/restore cycle -- exactly the bug class AZURE.md's process rules
// call out.
type storedDocumentWire struct {
- Timestamp time.Time `json:"Timestamp"`
- ID string `json:"ID"`
- PartitionKeyJSON string `json:"PartitionKeyJSON"`
- Origin string `json:"Origin,omitempty"`
- Body json.RawMessage `json:"Body"`
- Seq int64 `json:"Seq,omitempty"`
- GLSN int64 `json:"GLSN,omitempty"`
+ Timestamp time.Time `json:"Timestamp"`
+ ID string `json:"ID"`
+ PartitionKeyJSON string `json:"PartitionKeyJSON"`
+ Origin string `json:"Origin,omitempty"`
+ Body jsontext.Value `json:"Body"`
+ Seq int64 `json:"Seq,omitempty"`
+ GLSN int64 `json:"GLSN,omitempty"`
}
// MarshalJSON implements json.Marshaler for persistence snapshots.
func (d *storedDocument) MarshalJSON() ([]byte, error) {
- bodyBytes, err := json.Marshal(d.Body)
+ bodyBytes, err := json.Marshal(d.Body, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("cosmosdb: marshal stored document body: %w", err)
}
@@ -222,7 +225,7 @@ func (d *storedDocument) MarshalJSON() ([]byte, error) {
return json.Marshal(storedDocumentWire{
Body: bodyBytes, Timestamp: d.Timestamp, ID: d.ID, PartitionKeyJSON: d.PartitionKeyJSON, Seq: d.Seq,
GLSN: d.GLSN, Origin: d.Origin,
- })
+ }, jsonx.V1())
}
// UnmarshalJSON implements json.Unmarshaler for persistence snapshots. See
@@ -230,7 +233,7 @@ func (d *storedDocument) MarshalJSON() ([]byte, error) {
func (d *storedDocument) UnmarshalJSON(data []byte) error {
var wire storedDocumentWire
- if err := json.Unmarshal(data, &wire); err != nil {
+ if err := json.Unmarshal(data, &wire, jsonx.V1()); err != nil {
return fmt.Errorf("cosmosdb: unmarshal stored document: %w", err)
}
@@ -247,11 +250,10 @@ func (d *storedDocument) UnmarshalJSON(data []byte) error {
return fmt.Errorf("%w: id %q", ErrSnapshotDocumentNullBody, wire.ID)
}
- dec := json.NewDecoder(bytes.NewReader(wire.Body))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(wire.Body))
var body map[string]any
- if err := dec.Decode(&body); err != nil {
+ if err := json.UnmarshalDecode(dec, &body, jsonx.V1(), jsonx.UseNumber()); err != nil {
return fmt.Errorf("cosmosdb: unmarshal stored document body: %w", err)
}
@@ -282,7 +284,7 @@ type documentCompositeKey struct {
// rather than delimiter-joined, for the same collision-avoidance reason
// documented above and on services/azuretable's identical MarshalText.
func (k documentCompositeKey) MarshalText() ([]byte, error) {
- return json.Marshal([2]string{k.PartitionKeyJSON, k.ID})
+ return json.Marshal([2]string{k.PartitionKeyJSON, k.ID}, jsonx.V1())
}
// UnmarshalText implements encoding.TextUnmarshaler, the inverse of
@@ -290,7 +292,7 @@ func (k documentCompositeKey) MarshalText() ([]byte, error) {
func (k *documentCompositeKey) UnmarshalText(text []byte) error {
var pair [2]string
- if err := json.Unmarshal(text, &pair); err != nil {
+ if err := json.Unmarshal(text, &pair, jsonx.V1()); err != nil {
return fmt.Errorf("cosmosdb: malformed document composite key %q: %w", text, err)
}
@@ -318,7 +320,7 @@ func (k *documentCompositeKey) UnmarshalText(text []byte) error {
// data never intended to be a key at all.
func canonicalPartitionKeyJSON(v any) (string, error) {
switch v.(type) {
- case nil, string, bool, json.Number, float64, float32, int, int32, int64:
+ case nil, string, bool, jsonx.Number, float64, float32, int, int32, int64:
default:
return "", fmt.Errorf(
"%w: partition key value must be a scalar (string, number, bool, or null), got %T",
@@ -326,7 +328,7 @@ func canonicalPartitionKeyJSON(v any) (string, error) {
)
}
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return "", fmt.Errorf("cosmosdb: encode partition key value: %w", err)
}
@@ -342,11 +344,10 @@ func canonicalPartitionKeyJSON(v any) (string, error) {
// them. json.Number round-trips back out through json.Marshal as the exact
// original digit sequence.
func decodeJSONObject(body []byte) (map[string]any, error) {
- dec := json.NewDecoder(bytes.NewReader(body))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(body), jsonx.V1())
var m map[string]any
- if err := dec.Decode(&m); err != nil {
+ if err := json.UnmarshalDecode(dec, &m, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, fmt.Errorf("%w: %w", ErrInvalidDocument, err)
}
@@ -371,9 +372,9 @@ func decodeJSONObject(body []byte) (map[string]any, error) {
// *current* enclosing structure -- there isn't one here. Decoding a second
// value and requiring io.EOF instead makes the JSON scanner itself validate
// what remains, catching those unmatched-delimiter cases too.
-func rejectTrailingJSON(dec *json.Decoder) error {
+func rejectTrailingJSON(dec *jsontext.Decoder) error {
var extra any
- if err := dec.Decode(&extra); !errors.Is(err, io.EOF) {
+ if err := json.UnmarshalDecode(dec, &extra, jsonx.V1(), jsonx.UseNumber()); !errors.Is(err, io.EOF) {
if err == nil {
return ErrUnexpectedTrailingJSON
}
@@ -393,16 +394,15 @@ func rejectTrailingJSON(dec *json.Decoder) error {
// recursion, since it must handle arbitrarily nested maps/slices/scalars
// uniformly and preserve json.Number fidelity throughout.
func deepCopyJSONValue(v any) (any, error) {
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("cosmosdb: deep copy: marshal: %w", err)
}
- dec := json.NewDecoder(bytes.NewReader(data))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(data))
var out any
- if decodeErr := dec.Decode(&out); decodeErr != nil {
+ if decodeErr := json.UnmarshalDecode(dec, &out, jsonx.V1(), jsonx.UseNumber()); decodeErr != nil {
return nil, fmt.Errorf("cosmosdb: deep copy: unmarshal: %w", decodeErr)
}
@@ -464,7 +464,7 @@ func documentAsMap(info DocumentInfo) map[string]any {
// walk; anything else takes the JSON round-trip so results stay identical.
func copyStoredValue(v any) any {
switch t := v.(type) {
- case nil, string, bool, json.Number:
+ case nil, string, bool, jsonx.Number:
return t
case map[string]any:
out := make(map[string]any, len(t))
diff --git a/services/cosmosdb/offer_ops.go b/services/cosmosdb/offer_ops.go
index 5032d3b538..d2b9221e1c 100644
--- a/services/cosmosdb/offer_ops.go
+++ b/services/cosmosdb/offer_ops.go
@@ -1,7 +1,7 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -79,7 +80,7 @@ func specFromHeaders(r *http.Request) (ThroughputSpec, bool, error) {
}
var raw autopilotRaw
- if jsonErr := json.Unmarshal([]byte(auto), &raw); jsonErr != nil {
+ if jsonErr := json.Unmarshal([]byte(auto), &raw, jsonx.V1()); jsonErr != nil {
return ThroughputSpec{}, true, fmt.Errorf("%w: %w", ErrInvalidThroughput, jsonErr)
}
@@ -232,7 +233,7 @@ func (h *Handler) replaceOffer(c *echo.Context, tb ThroughputBackend, rid string
}
var wire offerWire
- if jsonErr := json.Unmarshal(body, &wire); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &wire, jsonx.V1()); jsonErr != nil {
return h.writeError(c, http.StatusBadRequest, "BadRequest", "The input is not valid JSON.")
}
diff --git a/services/cosmosdb/pagination_test.go b/services/cosmosdb/pagination_test.go
index 77c20ff74e..4e855e1efe 100644
--- a/services/cosmosdb/pagination_test.go
+++ b/services/cosmosdb/pagination_test.go
@@ -1,7 +1,7 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"net/http"
@@ -10,6 +10,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// pageIDs walks every page of a feed, returning each page's ids and asserting the continuation protocol.
@@ -149,7 +151,7 @@ func TestPagination_Feeds(t *testing.T) {
rec := doRequest(t, h, method, tt.path, headers, body)
var decoded map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &decoded)
+ _ = json.Unmarshal(rec.Body.Bytes(), &decoded, jsonx.V1())
return rec.Code, decoded, rec.Header()
}
@@ -221,7 +223,7 @@ func TestPagination_QueryContinuationIsStateless(t *testing.T) {
var body struct {
Documents []string `json:"Documents"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
return append(body.Documents, "next="+rec.Header().Get("X-Ms-Continuation"))
}
diff --git a/services/cosmosdb/partition_epk.go b/services/cosmosdb/partition_epk.go
index 287ed577d3..0324af6dd4 100644
--- a/services/cosmosdb/partition_epk.go
+++ b/services/cosmosdb/partition_epk.go
@@ -3,10 +3,13 @@ package cosmosdb
import (
"bytes"
"encoding/binary"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"math"
"math/big"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Effective partition key hashing for partition key version 2. Ported from the hashing azcosmos v1.5.0 uses to
@@ -157,7 +160,7 @@ func appendEPKComponent(buf []byte, v any) []byte {
}
return append(buf, epkComponentFalse)
- case json.Number:
+ case jsonx.Number:
f, _ := x.Float64()
return appendEPKNumber(buf, f)
@@ -193,11 +196,10 @@ func epkOfValues(vals []any) u128 {
// epkHash is the routing hash of a canonical partition key: a scalar hashes as a whole; a hierarchical key (a JSON
// array) routes by its first component, whose hash opens its effective partition key.
func epkHash(pkJSON string) u128 {
- dec := json.NewDecoder(bytes.NewReader([]byte(pkJSON)))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader([]byte(pkJSON)))
var v any
- if err := dec.Decode(&v); err != nil {
+ if err := json.UnmarshalDecode(dec, &v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return epkOfValues([]any{pkJSON})
}
diff --git a/services/cosmosdb/partitionkey.go b/services/cosmosdb/partitionkey.go
index 488b153d90..04d2846c04 100644
--- a/services/cosmosdb/partitionkey.go
+++ b/services/cosmosdb/partitionkey.go
@@ -1,9 +1,12 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// maxPartitionKeyPaths is the hierarchical (MultiHash) partition key depth limit.
@@ -66,7 +69,7 @@ func partitionKeyForBody(body map[string]any, paths []string) (string, error) {
vals[i] = v
}
- data, err := json.Marshal(vals)
+ data, err := json.Marshal(vals, jsonx.V1())
if err != nil {
return "", fmt.Errorf("cosmosdb: encode partition key: %w", err)
}
@@ -90,8 +93,8 @@ func partitionKeyArity(pkJSON string) int {
return 1
}
- var arr []json.RawMessage
- if err := json.Unmarshal([]byte(pkJSON), &arr); err != nil {
+ var arr []jsontext.Value
+ if err := json.Unmarshal([]byte(pkJSON), &arr, jsonx.V1()); err != nil {
return 0
}
diff --git a/services/cosmosdb/patch.go b/services/cosmosdb/patch.go
index e7bcd282e8..4f81e3afde 100644
--- a/services/cosmosdb/patch.go
+++ b/services/cosmosdb/patch.go
@@ -2,11 +2,14 @@ package cosmosdb
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"math"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// maxPatchOperations is the Cosmos limit on operations in one patch request.
@@ -24,10 +27,10 @@ const (
)
type patchOp struct {
- Op string `json:"op"`
- Path string `json:"path"`
- From string `json:"from,omitempty"`
- Value json.RawMessage `json:"value,omitempty"`
+ Op string `json:"op"`
+ Path string `json:"path"`
+ From string `json:"from,omitempty"`
+ Value jsontext.Value `json:"value,omitempty"`
}
type patchRequest struct {
@@ -43,8 +46,8 @@ func invalidPatchf(format string, args ...any) error {
func parsePatchRequest(raw []byte) (patchRequest, error) {
var req patchRequest
- dec := json.NewDecoder(bytes.NewReader(raw))
- if err := dec.Decode(&req); err != nil || rejectTrailingJSON(dec) != nil {
+ dec := jsontext.NewDecoder(bytes.NewReader(raw), jsonx.V1())
+ if err := json.UnmarshalDecode(dec, &req, jsonx.V1()); err != nil || rejectTrailingJSON(dec) != nil {
return patchRequest{}, invalidPatchf("the patch body is not valid JSON")
}
@@ -80,16 +83,15 @@ func patchPath(path string) ([]string, error) {
return segs, nil
}
-func decodePatchValue(raw json.RawMessage) (any, error) {
+func decodePatchValue(raw jsontext.Value) (any, error) {
if len(raw) == 0 {
return nil, invalidPatchf("the operation requires a value")
}
- dec := json.NewDecoder(bytes.NewReader(raw))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(raw))
var v any
- if err := dec.Decode(&v); err != nil {
+ if err := json.UnmarshalDecode(dec, &v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, invalidPatchf("invalid value: %v", err)
}
@@ -342,14 +344,14 @@ func incrValue(cur any, exists bool, delta any) (any, error) {
return addNumbers(c, d)
}
-func addNumbers(a, b json.Number) (json.Number, error) {
+func addNumbers(a, b jsonx.Number) (jsonx.Number, error) {
x, errX := a.Int64()
y, errY := b.Int64()
if errX == nil && errY == nil {
sum := x + y
if (x >= 0) != (y >= 0) || (sum >= 0) == (x >= 0) {
- return json.Number(strconv.FormatInt(sum, 10)), nil
+ return jsonx.Number(strconv.FormatInt(sum, 10)), nil
}
}
@@ -360,7 +362,7 @@ func addNumbers(a, b json.Number) (json.Number, error) {
return "", invalidPatchf("incr result is not a finite number")
}
- return json.Number(strconv.FormatFloat(fx+fy, 'g', -1, 64)), nil
+ return jsonx.Number(strconv.FormatFloat(fx+fy, 'g', -1, 64)), nil
}
// evalPatchCondition runs a "from c where ..." predicate against one document.
diff --git a/services/cosmosdb/permission_ops.go b/services/cosmosdb/permission_ops.go
index e53e86398c..0c760927fd 100644
--- a/services/cosmosdb/permission_ops.go
+++ b/services/cosmosdb/permission_ops.go
@@ -1,7 +1,7 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/jsontext"
"net/http"
"time"
@@ -10,10 +10,10 @@ import (
// permissionWire is the request body of a permission create or replace.
type permissionWire struct {
- ID string `json:"id"`
- Mode string `json:"permissionMode"`
- Resource string `json:"resource"`
- PartitionKey json.RawMessage `json:"resourcePartitionKey"`
+ ID string `json:"id"`
+ Mode string `json:"permissionMode"`
+ Resource string `json:"resource"`
+ PartitionKey jsontext.Value `json:"resourcePartitionKey"`
}
func (h *Handler) handlePermissions(c *echo.Context, dbID, userID string) error {
diff --git a/services/cosmosdb/persistence.go b/services/cosmosdb/persistence.go
index 749e1ee803..fa7f3d36d3 100644
--- a/services/cosmosdb/persistence.go
+++ b/services/cosmosdb/persistence.go
@@ -2,7 +2,7 @@ package cosmosdb
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"sort"
@@ -21,7 +21,7 @@ const cosmosdbSnapshotVersion = 1
type backendSnapshot struct {
Databases map[string]*storedDatabase `json:"databases"`
Regions *AccountRegions `json:"regions,omitempty"`
- TableAPI json.RawMessage `json:"tableApi,omitempty"`
+ TableAPI jsontext.Value `json:"tableApi,omitempty"`
Version int `json:"version"`
}
@@ -107,7 +107,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
}
// restoreTableAPI restores the attached Table API state; a snapshot without it empties the Table API.
-func (b *InMemoryBackend) restoreTableAPI(ctx context.Context, data json.RawMessage) error {
+func (b *InMemoryBackend) restoreTableAPI(ctx context.Context, data jsontext.Value) error {
if b.tableAPI == nil {
return nil
}
diff --git a/services/cosmosdb/persistence_test.go b/services/cosmosdb/persistence_test.go
index 4d86e4cdf1..91505a1b6f 100644
--- a/services/cosmosdb/persistence_test.go
+++ b/services/cosmosdb/persistence_test.go
@@ -1,12 +1,12 @@
package cosmosdb_test
import (
- "encoding/json"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -77,7 +77,7 @@ func TestBackend_SnapshotRestore_Int64PrecisionRoundTrip(t *testing.T) {
info, getErr := b2.GetDocument("mydb", "mycoll", pk, tt.name)
require.NoError(t, getErr, tt.name)
- gotNum, ok := info.Body["big"].(json.Number)
+ gotNum, ok := info.Body["big"].(jsonx.Number)
require.True(t, ok, tt.name)
assert.Equal(t, tt.raw, gotNum.String(), tt.name)
}
diff --git a/services/cosmosdb/query_resources.go b/services/cosmosdb/query_resources.go
index 85142c311c..baa2654fc4 100644
--- a/services/cosmosdb/query_resources.go
+++ b/services/cosmosdb/query_resources.go
@@ -2,12 +2,14 @@ package cosmosdb
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// queryResources runs the SQL query in the request body over resource bodies (databases or containers), as
@@ -25,10 +27,15 @@ func (h *Handler) queryResources(c *echo.Context, listKey string, bodies []map[s
var req queryRequestBody
- dec := json.NewDecoder(bytes.NewReader(raw))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(raw), jsonx.V1())
- if decErr := dec.Decode(&req); decErr != nil || rejectTrailingJSON(dec) != nil {
+ if decErr := json.UnmarshalDecode(
+ dec,
+ &req,
+ jsonx.V1(),
+ jsonx.UseNumber(),
+ ); decErr != nil ||
+ rejectTrailingJSON(dec) != nil {
return h.writeError(c, http.StatusBadRequest, "BadRequest", "The input is not valid JSON.")
}
diff --git a/services/cosmosdb/query_rows.go b/services/cosmosdb/query_rows.go
index cf294c6b9b..40b5cfa9eb 100644
--- a/services/cosmosdb/query_rows.go
+++ b/services/cosmosdb/query_rows.go
@@ -1,9 +1,11 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/jsontext"
"sort"
"strconv"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// maxParsedQueries bounds the parsed-statement cache.
@@ -32,7 +34,7 @@ func (r sqlRow) lookup(seg string) (any, bool) {
case "id":
return r.doc.ID, true
case sysPropTS:
- return json.Number(strconv.FormatInt(r.doc.Timestamp.Unix(), 10)), true
+ return jsonx.Number(strconv.FormatInt(r.doc.Timestamp.Unix(), 10)), true
case sysPropETag:
return r.src.b.etagFunc(r.doc.Timestamp), true
case sysPropAttachments:
@@ -56,7 +58,7 @@ func (r sqlRow) lookup(seg string) (any, bool) {
v, ok := r.m[seg]
if i, isInt := v.(int64); isInt {
- return json.Number(strconv.FormatInt(i, 10)), true
+ return jsonx.Number(strconv.FormatInt(i, 10)), true
}
return v, ok
@@ -221,7 +223,7 @@ func (b *InMemoryBackend) queryLocked(
// loadedCharge sums the read cost of the documents the query had to retrieve: those its filter kept when the
// indexing policy can serve the filter, every examined document otherwise (or when there is no filter).
-func loadedCharge(stmt *sqlSelectStmt, indexing json.RawMessage, res QueryResult) float64 {
+func loadedCharge(stmt *sqlSelectStmt, indexing jsontext.Value, res QueryResult) float64 {
docs := res.Matched
if stmt.where == nil || parseIndexingPolicy(indexing).needsScan(stmt.where) {
docs = res.Visited
diff --git a/services/cosmosdb/region_http.go b/services/cosmosdb/region_http.go
index 969094bded..dd1d4683fe 100644
--- a/services/cosmosdb/region_http.go
+++ b/services/cosmosdb/region_http.go
@@ -3,7 +3,7 @@ package cosmosdb
import (
"context"
"crypto/tls"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/telemetry"
)
@@ -398,7 +399,7 @@ func (h *Handler) putAdminTopology(c *echo.Context) error {
}
var req adminTopology
- if jsonErr := json.Unmarshal(raw, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(raw, &req, jsonx.V1()); jsonErr != nil {
return h.writeError(c, http.StatusBadRequest, "BadRequest", "The input is not valid JSON.")
}
diff --git a/services/cosmosdb/regions_conflicts_test.go b/services/cosmosdb/regions_conflicts_test.go
index f3d3503624..6f074dc0bc 100644
--- a/services/cosmosdb/regions_conflicts_test.go
+++ b/services/cosmosdb/regions_conflicts_test.go
@@ -1,7 +1,7 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -127,7 +128,7 @@ func jsonRank(t *testing.T, doc string) float64 {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal([]byte(doc), &m))
+ require.NoError(t, json.Unmarshal([]byte(doc), &m, jsonx.V1()))
r, _ := m["rank"].(float64)
diff --git a/services/cosmosdb/script_engine.go b/services/cosmosdb/script_engine.go
index 3ba73a7a62..c3a7a8a2f6 100644
--- a/services/cosmosdb/script_engine.go
+++ b/services/cosmosdb/script_engine.go
@@ -1,7 +1,6 @@
package cosmosdb
import (
- "encoding/json"
"errors"
"fmt"
"math"
@@ -10,6 +9,7 @@ import (
"github.com/dop251/goja"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/jsruntime"
)
@@ -90,7 +90,7 @@ func toJS(vm *goja.Runtime, v any) goja.Value {
return goja.Null()
case undef:
return goja.Undefined()
- case json.Number:
+ case jsonx.Number:
return numberToJS(vm, t)
case map[string]any:
o := vm.NewObject()
@@ -111,7 +111,7 @@ func toJS(vm *goja.Runtime, v any) goja.Value {
}
}
-func numberToJS(vm *goja.Runtime, n json.Number) goja.Value {
+func numberToJS(vm *goja.Runtime, n jsonx.Number) goja.Value {
if i, err := n.Int64(); err == nil {
return vm.ToValue(i)
}
@@ -137,15 +137,15 @@ func fromJS(v goja.Value) any {
func normalizeExport(v any) any {
switch t := v.(type) {
case int64:
- return json.Number(strconv.FormatInt(t, 10))
+ return jsonx.Number(strconv.FormatInt(t, 10))
case int:
- return json.Number(strconv.Itoa(t))
+ return jsonx.Number(strconv.Itoa(t))
case float64:
if math.IsNaN(t) || math.IsInf(t, 0) {
return nil
}
- return json.Number(strconv.FormatFloat(t, 'g', -1, 64))
+ return jsonx.Number(strconv.FormatFloat(t, 'g', -1, 64))
case map[string]any:
out := make(map[string]any, len(t))
for k, e := range t {
diff --git a/services/cosmosdb/script_ops.go b/services/cosmosdb/script_ops.go
index 06d18cb2d6..1a56c9b39e 100644
--- a/services/cosmosdb/script_ops.go
+++ b/services/cosmosdb/script_ops.go
@@ -2,7 +2,8 @@ package cosmosdb
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
@@ -11,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -224,7 +226,7 @@ func (h *Handler) writeScript(
}
var req scriptBody
- if jsonErr := json.Unmarshal(raw, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(raw, &req, jsonx.V1()); jsonErr != nil {
return h.writeError(c, http.StatusBadRequest, "BadRequest", "The input is not valid JSON.")
}
@@ -356,10 +358,15 @@ func sprocArgs(raw []byte) ([]any, error) {
var args []any
- dec := json.NewDecoder(bytes.NewReader(raw))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(raw), jsonx.V1())
- if err := dec.Decode(&args); err != nil || rejectTrailingJSON(dec) != nil {
+ if err := json.UnmarshalDecode(
+ dec,
+ &args,
+ jsonx.V1(),
+ jsonx.UseNumber(),
+ ); err != nil ||
+ rejectTrailingJSON(dec) != nil {
return nil, ErrInvalidDocument
}
diff --git a/services/cosmosdb/scripts_test.go b/services/cosmosdb/scripts_test.go
index 1264a03bf8..485ec9c77c 100644
--- a/services/cosmosdb/scripts_test.go
+++ b/services/cosmosdb/scripts_test.go
@@ -1,7 +1,7 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -22,7 +23,7 @@ const pkA = `["a"]`
func scriptJSON(t *testing.T, fields map[string]string) string {
t.Helper()
- raw, err := json.Marshal(fields)
+ raw, err := json.Marshal(fields, jsonx.V1())
require.NoError(t, err)
return string(raw)
@@ -562,7 +563,7 @@ func TestUDF_InQueries(t *testing.T) {
s.putScript("udfs", "boom", `function boom(){ throw new Error("x"); }`, nil).status,
)
- body, err := json.Marshal(map[string]any{"query": tt.query})
+ body, err := json.Marshal(map[string]any{"query": tt.query}, jsonx.V1())
require.NoError(t, err)
resp := s.do(http.MethodPost, "/dbs/d/colls/c/docs", map[string]string{
@@ -577,7 +578,7 @@ func TestUDF_InQueries(t *testing.T) {
require.Equal(t, want, resp.status, string(resp.body))
if tt.want != "" {
- docs, marshalErr := json.Marshal(resp.object(t)["Documents"])
+ docs, marshalErr := json.Marshal(resp.object(t)["Documents"], jsonx.V1())
require.NoError(t, marshalErr)
assert.JSONEq(t, tt.want, string(docs))
}
diff --git a/services/cosmosdb/sdk_test.go b/services/cosmosdb/sdk_test.go
index 9a85b0b09a..afffce5e87 100644
--- a/services/cosmosdb/sdk_test.go
+++ b/services/cosmosdb/sdk_test.go
@@ -1,7 +1,7 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -132,7 +133,7 @@ func TestSDK_QueriesPagingTTLAndHierarchicalKeys(t *testing.T) {
body, mErr := json.Marshal(map[string]any{
"id": fmt.Sprintf("i%d", i), "tenant": tenant, "user": "u", "n": i, "tags": []string{"x", strconv.Itoa(i)},
- })
+ }, jsonx.V1())
require.NoError(t, mErr)
_, err = c.CreateItem(ctx, azcosmos.NewPartitionKeyString(tenant).AppendString("u"), body, nil)
diff --git a/services/cosmosdb/search_funcs_test.go b/services/cosmosdb/search_funcs_test.go
index eee0f0b6e3..f447a35b75 100644
--- a/services/cosmosdb/search_funcs_test.go
+++ b/services/cosmosdb/search_funcs_test.go
@@ -1,12 +1,13 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -98,7 +99,7 @@ func TestSearchFunctions_ParseErrors(t *testing.T) {
func jsonString(t *testing.T, v any) string {
t.Helper()
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
return string(b)
diff --git a/services/cosmosdb/search_sdk_test.go b/services/cosmosdb/search_sdk_test.go
index ff4bd41892..f61a6d3313 100644
--- a/services/cosmosdb/search_sdk_test.go
+++ b/services/cosmosdb/search_sdk_test.go
@@ -1,7 +1,7 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -66,7 +67,7 @@ func seedSearchContainer(t *testing.T, props azcosmos.ContainerProperties) *azco
}
for _, d := range docs {
- body, mErr := json.Marshal(d)
+ body, mErr := json.Marshal(d, jsonx.V1())
require.NoError(t, mErr)
_, err = c.CreateItem(ctx, azcosmos.NewPartitionKeyString("p"), body, nil)
@@ -94,7 +95,7 @@ func queryIDs(t *testing.T, c *azcosmos.ContainerClient, query string) ([]string
ID string `json:"id"`
}
- require.NoError(t, json.Unmarshal(item, &v))
+ require.NoError(t, json.Unmarshal(item, &v, jsonx.V1()))
ids = append(ids, v.ID)
}
diff --git a/services/cosmosdb/spatial_index.go b/services/cosmosdb/spatial_index.go
index f8f0008ca6..1a6a282bae 100644
--- a/services/cosmosdb/spatial_index.go
+++ b/services/cosmosdb/spatial_index.go
@@ -1,10 +1,13 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Spatial index types (azcosmos SpatialType, azcosmos v1.5.0 spatial_type.go; Learn "Indexing policies").
@@ -30,12 +33,12 @@ func (b boundingBox) valid() bool {
}
// geospatialType is the container's geospatial data type; Geography unless the config says Geometry.
-func geospatialType(raw json.RawMessage) string {
+func geospatialType(raw jsontext.Value) string {
var cfg struct {
Type string `json:"type"`
}
- if json.Unmarshal(raw, &cfg) == nil && cfg.Type == geoTypeGeometry {
+ if json.Unmarshal(raw, &cfg, jsonx.V1()) == nil && cfg.Type == geoTypeGeometry {
return geoTypeGeometry
}
diff --git a/services/cosmosdb/sql_agg.go b/services/cosmosdb/sql_agg.go
index 63f0bc35d8..e0d3af152c 100644
--- a/services/cosmosdb/sql_agg.go
+++ b/services/cosmosdb/sql_agg.go
@@ -1,9 +1,10 @@
package cosmosdb
import (
- "encoding/json"
"math"
"strconv"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -52,7 +53,7 @@ func (a *aggAcc) add(v any) {
}
}
-func (a *aggAcc) addNumber(n json.Number) {
+func (a *aggAcc) addNumber(n jsonx.Number) {
a.numCount++
a.sumFloat += numFloat(n)
@@ -101,7 +102,7 @@ func (a *aggAcc) result() any {
case a.floatSum:
return floatResult(a.sumFloat)
default:
- return json.Number(strconv.FormatInt(a.sumInt, 10))
+ return jsonx.Number(strconv.FormatInt(a.sumInt, 10))
}
case aggAvg:
if a.numCount == 0 {
diff --git a/services/cosmosdb/sql_engine_test.go b/services/cosmosdb/sql_engine_test.go
index e80a085d70..cc6500322f 100644
--- a/services/cosmosdb/sql_engine_test.go
+++ b/services/cosmosdb/sql_engine_test.go
@@ -1,12 +1,13 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -137,8 +138,8 @@ func TestExecuteQuery_Engine(t *testing.T) {
name: "offset limit params",
query: "SELECT VALUE c.id FROM c ORDER BY c.age OFFSET @o LIMIT @l",
params: []cosmosdb.QueryParameter{
- {Name: "@o", Value: json.Number("0")},
- {Name: "@l", Value: json.Number("2")},
+ {Name: "@o", Value: jsonx.Number("0")},
+ {Name: "@l", Value: jsonx.Number("2")},
},
want: `["2","1"]`,
},
@@ -194,7 +195,7 @@ func TestExecuteQuery_Engine(t *testing.T) {
rows, err := cosmosdb.ExecuteQuery(tt.query, tt.params, docs)
require.NoError(t, err)
- got, err := json.Marshal(rows)
+ got, err := json.Marshal(rows, jsonx.V1())
require.NoError(t, err)
assert.JSONEq(t, tt.want, string(got))
diff --git a/services/cosmosdb/sql_eval.go b/services/cosmosdb/sql_eval.go
index dc9db26031..5ebe9cf108 100644
--- a/services/cosmosdb/sql_eval.go
+++ b/services/cosmosdb/sql_eval.go
@@ -1,7 +1,7 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"math"
"regexp"
@@ -9,6 +9,8 @@ import (
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// undef is the Cosmos SQL "undefined" value (a missing property, an unbound parameter, a failed operation).
@@ -247,7 +249,7 @@ func (ec *evalCtx) evalUnary(n *exprUnary, env []any) any {
return undef{}
}
- return numArith(sqlTokMinus, json.Number("0"), num)
+ return numArith(sqlTokMinus, jsonx.Number("0"), num)
default:
if num, ok := toNumber(v); ok {
return num
@@ -445,16 +447,16 @@ func (ec *evalCtx) evalSubquery(n *exprSubquery, env []any) any {
// --- numbers ---
-func toNumber(v any) (json.Number, bool) {
+func toNumber(v any) (jsonx.Number, bool) {
switch t := v.(type) {
- case json.Number:
+ case jsonx.Number:
return t, true
case int:
- return json.Number(strconv.Itoa(t)), true
+ return jsonx.Number(strconv.Itoa(t)), true
case int32:
- return json.Number(strconv.FormatInt(int64(t), 10)), true
+ return jsonx.Number(strconv.FormatInt(int64(t), 10)), true
case int64:
- return json.Number(strconv.FormatInt(t, 10)), true
+ return jsonx.Number(strconv.FormatInt(t, 10)), true
case float32:
return formatFloatNumber(float64(t))
case float64:
@@ -464,7 +466,7 @@ func toNumber(v any) (json.Number, bool) {
}
}
-func formatFloatNumber(f float64) (json.Number, bool) {
+func formatFloatNumber(f float64) (jsonx.Number, bool) {
if math.IsNaN(f) || math.IsInf(f, 0) {
return "", false
}
@@ -474,10 +476,10 @@ func formatFloatNumber(f float64) (json.Number, bool) {
format = 'e'
}
- return json.Number(strconv.FormatFloat(f, format, -1, 64)), true
+ return jsonx.Number(strconv.FormatFloat(f, format, -1, 64)), true
}
-func numFloat(n json.Number) float64 {
+func numFloat(n jsonx.Number) float64 {
f, _ := n.Float64()
return f
@@ -493,7 +495,7 @@ func floatResult(f float64) any {
}
// numArith applies + - * / % to a and b, staying in int64 when exact and falling back to float64.
-func numArith(op sqlTokenType, a, b json.Number) any {
+func numArith(op sqlTokenType, a, b jsonx.Number) any {
ai, aErr := a.Int64()
bi, bErr := b.Int64()
@@ -554,7 +556,7 @@ func intArith(op sqlTokenType, a, b int64) (any, bool) {
return nil, false
}
- return json.Number(strconv.FormatInt(v, 10)), true
+ return jsonx.Number(strconv.FormatInt(v, 10)), true
}
func intAdd(a, b int64) (int64, bool) {
@@ -720,7 +722,7 @@ func objectsEqual(a, b map[string]any) bool {
}
// compareJSONNumbers compares as int64 when both are exact integers, else as float64.
-func compareJSONNumbers(a, b json.Number) int {
+func compareJSONNumbers(a, b jsonx.Number) int {
ai, aErr := a.Int64()
bi, bErr := b.Int64()
@@ -856,7 +858,7 @@ func compareArrays(a, b []any) int {
}
func canonicalJSON(v any) string {
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Sprint(v)
}
diff --git a/services/cosmosdb/sql_exec_test.go b/services/cosmosdb/sql_exec_test.go
index e8986b8c3d..bfa2fcd003 100644
--- a/services/cosmosdb/sql_exec_test.go
+++ b/services/cosmosdb/sql_exec_test.go
@@ -1,7 +1,6 @@
package cosmosdb_test
import (
- "encoding/json"
"strings"
"testing"
"time"
@@ -9,6 +8,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -60,7 +60,7 @@ func TestExecuteQuery_ProjectionWithAlias(t *testing.T) {
require.NoError(t, err)
require.Len(t, rows, 1)
assert.Equal(t, "alice", rowObj(t, rows[0])["n"])
- assert.Equal(t, json.Number("30"), rowObj(t, rows[0])["age"])
+ assert.Equal(t, jsonx.Number("30"), rowObj(t, rows[0])["age"])
}
func TestExecuteQuery_WhereComparisons(t *testing.T) {
diff --git a/services/cosmosdb/sql_ext_test.go b/services/cosmosdb/sql_ext_test.go
index 4db3cfddbf..356d500548 100644
--- a/services/cosmosdb/sql_ext_test.go
+++ b/services/cosmosdb/sql_ext_test.go
@@ -1,12 +1,13 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -115,7 +116,7 @@ func TestExecuteQuery_SpatialAndConversionFunctions(t *testing.T) {
require.NoError(t, err)
require.Len(t, rows, 1)
- got, err := json.Marshal(rows[0])
+ got, err := json.Marshal(rows[0], jsonx.V1())
require.NoError(t, err)
assert.JSONEq(t, tt.want, string(got))
})
diff --git a/services/cosmosdb/sql_funcs.go b/services/cosmosdb/sql_funcs.go
index 7f33040387..574a4566cc 100644
--- a/services/cosmosdb/sql_funcs.go
+++ b/services/cosmosdb/sql_funcs.go
@@ -1,13 +1,14 @@
package cosmosdb
import (
- "encoding/json"
"math"
"regexp"
"strconv"
"strings"
"time"
"unicode/utf8"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -270,7 +271,7 @@ func intArg(v any) (int, bool) {
return int(i), true
}
-func intValue(i int) any { return json.Number(strconv.Itoa(i)) }
+func intValue(i int) any { return jsonx.Number(strconv.Itoa(i)) }
func boolArg(args []any, i int) bool {
if i >= len(args) {
@@ -459,7 +460,7 @@ func parseStringValue(name string, v any) any {
}
if i, iErr := strconv.ParseInt(s, 10, 64); iErr == nil {
- return json.Number(strconv.FormatInt(i, 10))
+ return jsonx.Number(strconv.FormatInt(i, 10))
}
return floatResult(f)
@@ -549,13 +550,13 @@ func callMath(name string, args []any) any {
}
}
-func mathRounding(name string, n json.Number) any {
+func mathRounding(name string, n jsonx.Number) any {
if i, err := n.Int64(); err == nil {
switch {
case name == biSign:
return intValue(cmpSign(i))
case name == biAbs && i < 0 && i != math.MinInt64:
- return json.Number(strconv.FormatInt(-i, 10))
+ return jsonx.Number(strconv.FormatInt(-i, 10))
default:
return n
}
@@ -590,7 +591,7 @@ func cmpSign(i int64) int {
}
}
-func mathLog(n json.Number, args []any) any {
+func mathLog(n jsonx.Number, args []any) any {
f := numFloat(n)
if f <= 0 {
return undef{}
@@ -823,9 +824,9 @@ func (ec *evalCtx) callDate(name string, args []any) any {
case biGetcurrentdatetime, biGetcurrentdatetimestatic:
return ec.now.Format(dateTimeLayout)
case biGetcurrenttimestamp, biGetcurrenttimestampstatic:
- return json.Number(strconv.FormatInt(ec.now.UnixMilli(), 10))
+ return jsonx.Number(strconv.FormatInt(ec.now.UnixMilli(), 10))
case biGetcurrentticks, biGetcurrentticksstatic:
- return json.Number(strconv.FormatInt(ec.now.UnixNano()/ticksPerNano, 10))
+ return jsonx.Number(strconv.FormatInt(ec.now.UnixNano()/ticksPerNano, 10))
case biTimestamptodatetime:
ms, ok := toNumber(args[0])
if !ok {
@@ -841,7 +842,7 @@ func (ec *evalCtx) callDate(name string, args []any) any {
return undef{}
}
- return json.Number(strconv.FormatInt(t.UnixMilli(), 10))
+ return jsonx.Number(strconv.FormatInt(t.UnixMilli(), 10))
default:
return dateTimeOp(name, args)
}
diff --git a/services/cosmosdb/sql_funcs_ext.go b/services/cosmosdb/sql_funcs_ext.go
index 8f850c67e6..b374ae7a66 100644
--- a/services/cosmosdb/sql_funcs_ext.go
+++ b/services/cosmosdb/sql_funcs_ext.go
@@ -2,10 +2,13 @@ package cosmosdb
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -18,11 +21,16 @@ const (
// parseJSONContainer implements StringToArray and StringToObject: the string must hold exactly that JSON kind.
func parseJSONContainer(name, s string) any {
- dec := json.NewDecoder(bytes.NewReader([]byte(s)))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader([]byte(s)), jsonx.V1())
var v any
- if err := dec.Decode(&v); err != nil || rejectTrailingJSON(dec) != nil {
+ if err := json.UnmarshalDecode(
+ dec,
+ &v,
+ jsonx.V1(),
+ jsonx.UseNumber(),
+ ); err != nil ||
+ rejectTrailingJSON(dec) != nil {
return undef{}
}
@@ -86,7 +94,7 @@ func dateTicksOp(name string, args []any) any {
return undef{}
}
- return json.Number(strconv.FormatInt(t.Unix()*ticksPerSecond+int64(t.Nanosecond())/ticksPerNano, 10))
+ return jsonx.Number(strconv.FormatInt(t.Unix()*ticksPerSecond+int64(t.Nanosecond())/ticksPerNano, 10))
case biTickstodatetime:
n, ok := toNumber(args[0])
if !ok {
diff --git a/services/cosmosdb/sql_funcs_more.go b/services/cosmosdb/sql_funcs_more.go
index 4b5bfdc0a5..864d9b83f5 100644
--- a/services/cosmosdb/sql_funcs_more.go
+++ b/services/cosmosdb/sql_funcs_more.go
@@ -3,7 +3,6 @@ package cosmosdb
import (
"crypto/rand"
"encoding/binary"
- "encoding/json"
"math"
"math/big"
"regexp"
@@ -12,6 +11,8 @@ import (
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Built-in functions of the Cosmos DB query language reference (Learn, "Functions") beyond the first set:
@@ -253,7 +254,7 @@ func trigonometry(name string, x float64) any {
// numberBin rounds a down to a multiple of the bin size (default 1): floor(a / size) * size, so a negative size
// bins upward as the documented example shows. A zero or non-numeric size has no result.
-func numberBin(a json.Number, args []any) any {
+func numberBin(a jsonx.Number, args []any) any {
size := 1.0
if len(args) > 1 {
@@ -277,7 +278,7 @@ func numberBin(a json.Number, args []any) any {
q--
}
- return json.Number(strconv.FormatInt(q*si, 10))
+ return jsonx.Number(strconv.FormatInt(q*si, 10))
}
return floatResult(math.Floor(numFloat(a)/size) * size)
@@ -311,7 +312,7 @@ func intMath(name string, args []any) any {
}
if name == biIntbitnot {
- return json.Number(strconv.FormatInt(^a, 10))
+ return jsonx.Number(strconv.FormatInt(^a, 10))
}
b, bok := int64Arg(args[1])
@@ -324,7 +325,7 @@ func intMath(name string, args []any) any {
return undef{}
}
- return json.Number(strconv.FormatInt(r, 10))
+ return jsonx.Number(strconv.FormatInt(r, 10))
}
func intBinary(name string, a, b int64) (int64, bool) {
@@ -406,8 +407,8 @@ func moreCollection(name string, args []any) any {
return args[arityPair]
case biChoose:
- i, ok := int64Arg(args[0])
- if !ok || i < 1 || int(i) >= len(args) {
+ i, ok := intArg(args[0])
+ if !ok || i < 1 || i >= len(args) {
return undef{}
}
@@ -605,12 +606,12 @@ func (ec *evalCtx) regexOp(name string, args []any) any {
group := 0
if len(args) > regexGroupArg {
- g, gok := int64Arg(args[regexGroupArg])
- if !gok || g < 0 || int(g) > re.NumSubexp() {
+ g, gok := intArg(args[regexGroupArg])
+ if !gok || g < 0 || g > re.NumSubexp() {
return undef{}
}
- group = int(g)
+ group = g
}
return extractRegex(re, subject, group, name == biRegexextractall)
@@ -810,7 +811,7 @@ func documentIDOf(n *exprCall, env []any) any {
return undef{}
}
- return json.Number(strconv.FormatInt(row.doc.Seq, 10))
+ return jsonx.Number(strconv.FormatInt(row.doc.Seq, 10))
}
// randomUnit is a uniform number in [0, 1).
diff --git a/services/cosmosdb/sql_functions_doc_test.go b/services/cosmosdb/sql_functions_doc_test.go
index 795506e23b..e08acff93e 100644
--- a/services/cosmosdb/sql_functions_doc_test.go
+++ b/services/cosmosdb/sql_functions_doc_test.go
@@ -1,12 +1,13 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -203,7 +204,7 @@ func TestSQLFunctions_DocumentedExamples(t *testing.T) {
require.NoError(t, err)
require.Len(t, rows, 1)
- got, err := json.Marshal(rows[0])
+ got, err := json.Marshal(rows[0], jsonx.V1())
require.NoError(t, err)
assert.JSONEq(t, tt.want, string(got))
})
@@ -343,7 +344,7 @@ func TestSQL_DocumentedStatementForms(t *testing.T) {
require.NoError(t, err)
- got, marshalErr := json.Marshal(rows)
+ got, marshalErr := json.Marshal(rows, jsonx.V1())
require.NoError(t, marshalErr)
assert.JSONEq(t, tt.want, string(got))
})
diff --git a/services/cosmosdb/sql_parser.go b/services/cosmosdb/sql_parser.go
index 6b8a6a6eda..80d2bce226 100644
--- a/services/cosmosdb/sql_parser.go
+++ b/services/cosmosdb/sql_parser.go
@@ -1,10 +1,11 @@
package cosmosdb
import (
- "encoding/json"
"fmt"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// sql_parser.go is a recursive-descent parser for Cosmos DB SQL.
@@ -267,7 +268,7 @@ func (p *sqlParser) parseCountOperand(what string) (sqlExpr, error) {
p.advance()
- return &exprLit{v: json.Number(strconv.Itoa(n))}, nil
+ return &exprLit{v: jsonx.Number(strconv.Itoa(n))}, nil
case sqlTokParam:
x := &exprParam{name: p.cur.lit}
@@ -883,7 +884,7 @@ func (p *sqlParser) parseUnary() (sqlExpr, error) {
p.advance()
- return &exprLit{v: json.Number(lit)}, nil
+ return &exprLit{v: jsonx.Number(lit)}, nil
}
inner, err := p.parseUnary()
@@ -937,7 +938,7 @@ func (p *sqlParser) parsePrimary() (sqlExpr, error) {
case sqlTokNumber:
p.advance()
- return &exprLit{v: json.Number(tok.lit)}, nil
+ return &exprLit{v: jsonx.Number(tok.lit)}, nil
case sqlTokString:
p.advance()
diff --git a/services/cosmosdb/sql_spatial_doc_test.go b/services/cosmosdb/sql_spatial_doc_test.go
index 6387ff30a5..15e021ede7 100644
--- a/services/cosmosdb/sql_spatial_doc_test.go
+++ b/services/cosmosdb/sql_spatial_doc_test.go
@@ -1,12 +1,14 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -33,7 +35,7 @@ func evalOne(t *testing.T, expr string) any {
func asFloat(t *testing.T, v any) float64 {
t.Helper()
- n, ok := v.(json.Number)
+ n, ok := v.(jsonx.Number)
require.True(t, ok, "%T", v)
f, err := n.Float64()
@@ -187,7 +189,7 @@ func TestSpatial_GeographySemantics(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- got, err := json.Marshal(evalOne(t, tt.expr))
+ got, err := json.Marshal(evalOne(t, tt.expr), jsonx.V1())
require.NoError(t, err)
assert.JSONEq(t, tt.want, string(got))
})
@@ -243,7 +245,7 @@ func TestSpatial_GeometryContainersUsePlanarMath(t *testing.T) {
_, err = b.CreateContainer("d", cosmosdb.ContainerSpec{
ID: "c",
PartitionKeyPath: "/pk",
- GeospatialConfig: json.RawMessage(`{"type":"` + tt.geo + `"}`),
+ GeospatialConfig: jsontext.Value(`{"type":"` + tt.geo + `"}`),
})
require.NoError(t, err)
_, err = b.CreateDocument("d", "c", map[string]any{"id": "1", "pk": "k"}, false)
@@ -253,7 +255,7 @@ func TestSpatial_GeometryContainersUsePlanarMath(t *testing.T) {
require.NoError(t, err)
require.Len(t, res.Rows, 1)
- got, err := json.Marshal(res.Rows[0])
+ got, err := json.Marshal(res.Rows[0], jsonx.V1())
require.NoError(t, err)
assert.JSONEq(t, tt.want, string(got))
})
diff --git a/services/cosmosdb/store_test.go b/services/cosmosdb/store_test.go
index 8bc610eb96..e1fffbda71 100644
--- a/services/cosmosdb/store_test.go
+++ b/services/cosmosdb/store_test.go
@@ -1,7 +1,6 @@
package cosmosdb_test
import (
- "encoding/json"
"math"
"testing"
"time"
@@ -9,6 +8,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -322,7 +322,7 @@ func TestInMemoryBackend_Int64PrecisionPreservedThroughStorage(t *testing.T) {
info, err := b.CreateDocument("mydb", "mycoll", body, false)
require.NoError(t, err)
- gotNum, ok := info.Body["big"].(json.Number)
+ gotNum, ok := info.Body["big"].(jsonx.Number)
require.True(t, ok, "big field must remain a json.Number, not a lossy float64")
assert.Equal(t, tt.raw, gotNum.String())
})
diff --git a/services/cosmosdb/table_api.go b/services/cosmosdb/table_api.go
index 95c390ac54..11ea881135 100644
--- a/services/cosmosdb/table_api.go
+++ b/services/cosmosdb/table_api.go
@@ -23,7 +23,7 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -31,6 +31,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/odatatable"
)
@@ -362,7 +363,7 @@ func (h *Handler) createTable(c *echo.Context) error {
}
var req createTableBody
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
return h.writeTableError(c, http.StatusBadRequest, "InvalidInput", "The input is not valid.")
}
@@ -514,7 +515,7 @@ type tableODataErrorEnvelope struct {
func (h *Handler) writeTableJSON(c *echo.Context, status int, v any) error {
level := tableODataLevelFromAccept(c.Request().Header.Get("Accept"))
- body, err := json.Marshal(v)
+ body, err := json.Marshal(v, jsonx.V1())
if err != nil {
return h.writeTableErrorNoRecurse(c, http.StatusInternalServerError, "InternalError",
"Failed to marshal response.")
@@ -545,7 +546,7 @@ func (h *Handler) writeTableErrorNoRecurse(c *echo.Context, status int, code, me
c.Response().Header().Set("X-Ms-Error-Code", code)
body, _ := json.Marshal(tableODataErrorEnvelope{
Error: tableODataErrorDetail{Code: code, Message: tableODataErrorMessage{Lang: tableErrorLang, Value: message}},
- })
+ }, jsonx.V1())
return c.Blob(status, "application/json;odata=minimalmetadata;streaming=true;charset=utf-8", body)
}
diff --git a/services/cosmosdb/throughput_test.go b/services/cosmosdb/throughput_test.go
index 75d432c8f6..5b8d1a2021 100644
--- a/services/cosmosdb/throughput_test.go
+++ b/services/cosmosdb/throughput_test.go
@@ -2,7 +2,7 @@ package cosmosdb_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -480,7 +481,7 @@ func TestThroughput_PersistenceRoundTrip(t *testing.T) {
snap := src.Snapshot(t.Context())
var raw map[string]any
- require.NoError(t, json.Unmarshal(snap, &raw))
+ require.NoError(t, json.Unmarshal(snap, &raw, jsonx.V1()))
dst := cosmosdb.NewInMemoryBackend()
require.NoError(t, dst.Restore(t.Context(), snap))
diff --git a/services/cosmosdb/token.go b/services/cosmosdb/token.go
index 92bf81bfd0..95f84a145e 100644
--- a/services/cosmosdb/token.go
+++ b/services/cosmosdb/token.go
@@ -4,13 +4,15 @@ import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -93,7 +95,7 @@ func signTokenPayload(masterKey string, payload []byte) []byte {
// issueResourceToken builds "type=resource&ver=1&sig=;", the value of a permission's _token.
func issueResourceToken(masterKey string, claims tokenClaims) (string, error) {
- payload, err := json.Marshal(claims)
+ payload, err := json.Marshal(claims, jsonx.V1())
if err != nil {
return "", fmt.Errorf("cosmosdb: marshal token claims: %w", err)
}
@@ -126,7 +128,7 @@ func parseResourceToken(masterKey, sig string) (tokenClaims, bool) {
}
var claims tokenClaims
- if json.Unmarshal(payload, &claims) != nil {
+ if json.Unmarshal(payload, &claims, jsonx.V1()) != nil {
return tokenClaims{}, false
}
diff --git a/services/cosmosdb/triggers.go b/services/cosmosdb/triggers.go
index 6b035b20ab..42bf0c680b 100644
--- a/services/cosmosdb/triggers.go
+++ b/services/cosmosdb/triggers.go
@@ -1,9 +1,11 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// docOp is a document write with the triggers requested for it.
@@ -226,7 +228,7 @@ func triggerNames(header string) []string {
// patchWithBody applies a patch whose request body (the operations, after any pre-trigger edit) is body.
func (tx *containerTx) patchWithBody(op docOp, body map[string]any) (DocumentInfo, error) {
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
if err != nil {
return DocumentInfo{}, invalidPatchf("the patch body is not valid JSON")
}
diff --git a/services/cosmosdb/ttl_test.go b/services/cosmosdb/ttl_test.go
index 5b450b5257..591c7284a5 100644
--- a/services/cosmosdb/ttl_test.go
+++ b/services/cosmosdb/ttl_test.go
@@ -1,7 +1,6 @@
package cosmosdb_test
import (
- "encoding/json"
"net/http"
"sync/atomic"
"testing"
@@ -10,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -143,7 +143,7 @@ func TestTTL_ExpiredReadsAsAbsentForWrites(t *testing.T) {
info, err := b.GetDocument("d", "c", `"a"`, "x")
require.NoError(t, err)
- assert.Equal(t, json.Number("2"), info.Body["v"])
+ assert.Equal(t, jsonx.Number("2"), info.Body["v"])
}
func TestTTL_SweepRemovesExpired(t *testing.T) {
diff --git a/services/cosmosdb/users.go b/services/cosmosdb/users.go
index 360b211e7c..cc4ab07d2a 100644
--- a/services/cosmosdb/users.go
+++ b/services/cosmosdb/users.go
@@ -1,7 +1,7 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
"sort"
@@ -46,7 +46,7 @@ type PermissionSpec struct {
ID string
Mode string
Resource string
- PartitionKey json.RawMessage
+ PartitionKey jsontext.Value
}
// PermissionInfo is a read-only snapshot of a permission. Link is the canonical id-based resource path.
@@ -60,7 +60,7 @@ type PermissionInfo struct {
Resource string
Link string
PartitionKeyJSON string
- PartitionKey json.RawMessage
+ PartitionKey jsontext.Value
}
type storedUser struct {
@@ -75,8 +75,8 @@ type storedPermission struct {
Mode string
Resource string
Link string
- PartitionKeyJSON string `json:"PartitionKeyJSON,omitempty"`
- PartitionKey json.RawMessage `json:"PartitionKey,omitempty"`
+ PartitionKeyJSON string `json:"PartitionKeyJSON,omitempty"`
+ PartitionKey jsontext.Value `json:"PartitionKey,omitempty"`
}
func userInfo(dbID string, u *storedUser) UserInfo {
diff --git a/services/cosmosdb/users_ops.go b/services/cosmosdb/users_ops.go
index d3cb7a433e..c472e1409a 100644
--- a/services/cosmosdb/users_ops.go
+++ b/services/cosmosdb/users_ops.go
@@ -1,13 +1,14 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -122,7 +123,7 @@ func (h *Handler) readJSONBody(c *echo.Context, dst any) bool {
return false
}
- if json.Unmarshal(raw, dst) != nil {
+ if json.Unmarshal(raw, dst, jsonx.V1()) != nil {
_ = h.writeError(c, http.StatusBadRequest, "BadRequest", "The input is not valid JSON.")
return false
diff --git a/services/cosmosdb/users_test.go b/services/cosmosdb/users_test.go
index 0e0637f369..132f42bc61 100644
--- a/services/cosmosdb/users_test.go
+++ b/services/cosmosdb/users_test.go
@@ -1,7 +1,7 @@
package cosmosdb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/cosmosdb"
)
@@ -65,7 +66,7 @@ func decodeMap(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
diff --git a/services/cosmosdb/vector.go b/services/cosmosdb/vector.go
index a4940dc3ed..bc93bdb11f 100644
--- a/services/cosmosdb/vector.go
+++ b/services/cosmosdb/vector.go
@@ -1,11 +1,14 @@
package cosmosdb
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"math"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -50,20 +53,20 @@ func validVectorIndexType(s string) bool {
return slices.Contains([]string{vectorIdxFlat, vectorIdxQuantizedFlat, vectorIdxDiskANN}, s)
}
-func parseVectorPolicy(raw json.RawMessage) (vectorEmbeddingPolicy, error) {
+func parseVectorPolicy(raw jsontext.Value) (vectorEmbeddingPolicy, error) {
var pol vectorEmbeddingPolicy
if isJSONNull(raw) {
return pol, nil
}
- if err := json.Unmarshal(raw, &pol); err != nil {
+ if err := json.Unmarshal(raw, &pol, jsonx.V1()); err != nil {
return pol, fmt.Errorf("%w: vectorEmbeddingPolicy is malformed", ErrInvalidContainerPolicy)
}
return pol, nil
}
-func validateVectorPolicy(raw json.RawMessage) error {
+func validateVectorPolicy(raw jsontext.Value) error {
pol, err := parseVectorPolicy(raw)
if err != nil {
return err
diff --git a/services/cur/delivery.go b/services/cur/delivery.go
index bca09016aa..a055f0a667 100644
--- a/services/cur/delivery.go
+++ b/services/cur/delivery.go
@@ -2,7 +2,7 @@ package cur
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"path"
"slices"
@@ -11,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
"github.com/blackbirdworks/gopherstack/pkgs/billingexport"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -224,7 +225,7 @@ func writeManifests(
periodDir, dataDir string,
athena bool,
) error {
- mb, err := json.Marshal(m)
+ mb, err := json.Marshal(m, jsonx.V1())
if err != nil {
return fmt.Errorf("encode manifest: %w", err)
}
@@ -303,7 +304,7 @@ func redshiftManifest(dataURI string) []byte {
"globalUploadSettings": map[string]string{
"format": "CSV", "delimiter": ",", "textqualifier": "\"", "containsHeader": "true",
},
- })
+ }, jsonx.V1())
return b
}
@@ -314,7 +315,7 @@ func quicksightManifest(dataURI string) []byte {
"globalUploadSettings": map[string]string{
"format": "CSV", "delimiter": ",", "textqualifier": "\"", "containsHeader": "true",
},
- })
+ }, jsonx.V1())
return b
}
diff --git a/services/cur/delivery_test.go b/services/cur/delivery_test.go
index e7f2c5dffe..16797c198c 100644
--- a/services/cur/delivery_test.go
+++ b/services/cur/delivery_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"compress/gzip"
"encoding/csv"
- "encoding/json"
+ "encoding/json/v2"
"io"
"slices"
"testing"
@@ -16,6 +16,8 @@ import (
sdk_s3 "github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (f *fixture) object(t *testing.T, key string) []byte {
@@ -109,7 +111,7 @@ func TestDelivery_CSVGzip(t *testing.T) {
}
var m map[string]any
- require.NoError(t, json.Unmarshal(f.object(t, manifestKey), &m))
+ require.NoError(t, json.Unmarshal(f.object(t, manifestKey), &m, jsonx.V1()))
assert.Equal(t, "daily", m["reportName"])
assert.Equal(t, "20260301T000000.000Z", m["billingPeriod"].(map[string]any)["start"])
assert.Equal(t, []any{dataKey}, m["reportKeys"])
diff --git a/services/cur/handler.go b/services/cur/handler.go
index fc7e4bd429..a9c3b53a95 100644
--- a/services/cur/handler.go
+++ b/services/cur/handler.go
@@ -2,7 +2,7 @@ package cur
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"slices"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/worker"
@@ -39,7 +40,7 @@ func bind[I, O any](fn func(*Handler, context.Context, *I) (*O, error)) opFunc {
var in I
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fail(ErrValidation, "malformed request body: %v", err)
}
}
diff --git a/services/cur/persistence.go b/services/cur/persistence.go
index e4b13d9968..dcf27103b0 100644
--- a/services/cur/persistence.go
+++ b/services/cur/persistence.go
@@ -2,7 +2,7 @@ package cur
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,10 +12,10 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/databrew/datasets_test.go b/services/databrew/datasets_test.go
index c9442e1a40..dd6a2986ea 100644
--- a/services/databrew/datasets_test.go
+++ b/services/databrew/datasets_test.go
@@ -2,13 +2,14 @@ package databrew_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/databrew"
)
@@ -298,7 +299,7 @@ func TestHandlerCreateDataset(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "my-ds", resp["Name"])
}
@@ -326,7 +327,7 @@ func TestHandlerDescribeDataset(t *testing.T) {
rec := databrewReq(t, h, http.MethodGet, "/databrew/v1/datasets/ds1", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var ds map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ds))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ds, jsonx.V1()))
assert.Equal(t, "ds1", ds["Name"])
}
@@ -347,7 +348,7 @@ func TestHandlerListDatasets(t *testing.T) {
rec := databrewReq(t, h, http.MethodGet, "/databrew/v1/datasets", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Datasets"])
}
@@ -453,7 +454,7 @@ func TestHandlerCreateDataset_PathOptions(t *testing.T) {
rec := databrewReq(t, h, http.MethodGet, "/databrew/v1/datasets/po-wire-ds", nil)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pathOptions, ok := resp["PathOptions"].(map[string]any)
require.True(t, ok, "PathOptions must round-trip as an object")
filesLimit, ok := pathOptions["FilesLimit"].(map[string]any)
diff --git a/services/databrew/handler.go b/services/databrew/handler.go
index 5eeb9c3ee4..1b01fea1ca 100644
--- a/services/databrew/handler.go
+++ b/services/databrew/handler.go
@@ -2,7 +2,8 @@ package databrew
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -12,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -377,18 +379,18 @@ var onceSimpleQueryParamToJSONKey = sync.OnceValue(func() map[string]string {
// mergeSimpleQueryParams copies every onceSimpleQueryParamToJSONKey entry
// present on the request into m.
-func mergeSimpleQueryParams(c *echo.Context, m map[string]json.RawMessage) {
+func mergeSimpleQueryParams(c *echo.Context, m map[string]jsontext.Value) {
for param, jsonKey := range onceSimpleQueryParamToJSONKey() {
if v := c.QueryParam(param); v != "" {
- m[jsonKey], _ = json.Marshal(v)
+ m[jsonKey], _ = json.Marshal(v, jsonx.V1())
}
}
}
func enrichDataBrewBody(c *echo.Context, _, name string, body []byte) ([]byte, error) {
- m := make(map[string]json.RawMessage)
+ m := make(map[string]jsontext.Value)
if len(body) > 0 {
- if err := json.Unmarshal(body, &m); err != nil {
+ if err := json.Unmarshal(body, &m, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -396,11 +398,11 @@ func enrichDataBrewBody(c *echo.Context, _, name string, body []byte) ([]byte, e
if name != "" {
if existingName, ok := m["Name"]; ok {
var parsedName string
- if err := json.Unmarshal(existingName, &parsedName); err == nil && parsedName != name {
+ if err := json.Unmarshal(existingName, &parsedName, jsonx.V1()); err == nil && parsedName != name {
return nil, fmt.Errorf("%w: name in body does not match path", errInvalidRequest)
}
}
- nameJSON, _ := json.Marshal(name)
+ nameJSON, _ := json.Marshal(name, jsonx.V1())
m["Name"] = nameJSON
}
@@ -413,7 +415,7 @@ func enrichDataBrewBody(c *echo.Context, _, name string, body []byte) ([]byte, e
// encoder.AddQuery("tagKeys") per key). Without this, UntagResource always
// silently no-ops.
if tagKeys := c.QueryParams()["tagKeys"]; len(tagKeys) > 0 {
- m["TagKeys"], _ = json.Marshal(tagKeys)
+ m["TagKeys"], _ = json.Marshal(tagKeys, jsonx.V1())
}
// ListRecipeVersions is GET /recipeVersions?name=... (top-level, no path
// segment for the recipe name); only set when name isn't already known
@@ -421,20 +423,20 @@ func enrichDataBrewBody(c *echo.Context, _, name string, body []byte) ([]byte, e
// alias already populated "Name" above).
if _, ok := m["Name"]; !ok {
if v := c.QueryParam("name"); v != "" {
- m["Name"], _ = json.Marshal(v)
+ m["Name"], _ = json.Marshal(v, jsonx.V1())
}
}
- result, _ := json.Marshal(m)
+ result, _ := json.Marshal(m, jsonx.V1())
return result, nil
}
func enrichDataBrewSubOpBody(path string, body []byte) []byte {
segments := strings.Split(path, "/")
- m := make(map[string]json.RawMessage)
+ m := make(map[string]jsontext.Value)
if len(body) > 0 {
- _ = json.Unmarshal(body, &m)
+ _ = json.Unmarshal(body, &m, jsonx.V1())
}
for i, seg := range segments {
@@ -442,26 +444,26 @@ func enrichDataBrewSubOpBody(path string, body []byte) []byte {
case "jobRun":
// e.g. /jobs/{Name}/jobRun/{RunId} — with or without /databrew/v1/ prefix
if i+1 < len(segments) {
- runIDJSON, _ := json.Marshal(segments[i+1])
+ runIDJSON, _ := json.Marshal(segments[i+1], jsonx.V1())
m["RunId"] = runIDJSON
}
case "recipeVersion":
// e.g. /recipes/{Name}/recipeVersion/{RecipeVersion}
if i+1 < len(segments) {
- versionJSON, _ := json.Marshal(segments[i+1])
+ versionJSON, _ := json.Marshal(segments[i+1], jsonx.V1())
m["RecipeVersion"] = versionJSON
}
case "tags":
// e.g. /databrew/v1/tags/{resourceArn} or /tags/{resourceArn}
if i+1 < len(segments) {
arn := strings.Join(segments[i+1:], "/")
- arnJSON, _ := json.Marshal(arn)
+ arnJSON, _ := json.Marshal(arn, jsonx.V1())
m["ResourceArn"] = arnJSON
}
}
}
- result, _ := json.Marshal(m)
+ result, _ := json.Marshal(m, jsonx.V1())
return result
}
diff --git a/services/databrew/handler_datasets.go b/services/databrew/handler_datasets.go
index f1c9512dae..9391c42334 100644
--- a/services/databrew/handler_datasets.go
+++ b/services/databrew/handler_datasets.go
@@ -2,9 +2,11 @@ package databrew
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseDatasetOp(method, name string) string {
@@ -72,7 +74,7 @@ func (h *Handler) handleCreateDataset(ctx context.Context, body []byte) ([]byte,
Name string `json:"Name"`
Format string `json:"Format"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
ds, err := h.Backend.CreateDataset(
@@ -82,14 +84,14 @@ func (h *Handler) handleCreateDataset(ctx context.Context, body []byte) ([]byte,
return nil, err
}
- return json.Marshal(map[string]string{keyName: ds.Name})
+ return json.Marshal(map[string]string{keyName: ds.Name}, jsonx.V1())
}
func (h *Handler) handleDescribeDataset(ctx context.Context, body []byte) ([]byte, error) {
var req struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
ds, err := h.Backend.DescribeDataset(ctx, req.Name)
@@ -100,7 +102,7 @@ func (h *Handler) handleDescribeDataset(ctx context.Context, body []byte) ([]byt
cp := *ds
cp.AccountID = ""
- return json.Marshal(cp)
+ return json.Marshal(cp, jsonx.V1())
}
func (h *Handler) handleListDatasets(ctx context.Context, body []byte) ([]byte, error) {
@@ -108,7 +110,7 @@ func (h *Handler) handleListDatasets(ctx context.Context, body []byte) ([]byte,
MaxResults string `json:"MaxResults"`
NextToken string `json:"NextToken"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
maxResults, token, err := listParams(req.MaxResults, req.NextToken)
if err != nil {
return nil, err
@@ -127,7 +129,7 @@ func (h *Handler) handleUpdateDataset(ctx context.Context, body []byte) ([]byte,
Name string `json:"Name"`
Format string `json:"Format"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := h.Backend.UpdateDataset(
@@ -136,19 +138,19 @@ func (h *Handler) handleUpdateDataset(ctx context.Context, body []byte) ([]byte,
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name})
+ return json.Marshal(map[string]string{keyName: req.Name}, jsonx.V1())
}
func (h *Handler) handleDeleteDataset(ctx context.Context, body []byte) ([]byte, error) {
var req struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := h.Backend.DeleteDataset(ctx, req.Name); err != nil {
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name})
+ return json.Marshal(map[string]string{keyName: req.Name}, jsonx.V1())
}
diff --git a/services/databrew/handler_jobs.go b/services/databrew/handler_jobs.go
index 13921b141d..152646c9b2 100644
--- a/services/databrew/handler_jobs.go
+++ b/services/databrew/handler_jobs.go
@@ -2,10 +2,12 @@ package databrew
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseProfileJobOp(method, name string) string {
@@ -143,7 +145,7 @@ func (h *Handler) handleCreateProfileJob(ctx context.Context, body []byte) ([]by
MaxRetries int `json:"MaxRetries"`
Timeout int `json:"Timeout"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
j, err := h.Backend.CreateJob(
@@ -172,7 +174,7 @@ func (h *Handler) handleCreateProfileJob(ctx context.Context, body []byte) ([]by
return nil, err
}
- return json.Marshal(map[string]string{keyName: j.Name})
+ return json.Marshal(map[string]string{keyName: j.Name}, jsonx.V1())
}
// outputLocationToOutputs converts a CreateProfileJobInput/UpdateProfileJobInput
@@ -204,7 +206,7 @@ func (h *Handler) handleCreateRecipeJob(ctx context.Context, body []byte) ([]byt
MaxRetries int `json:"MaxRetries"`
Timeout int `json:"Timeout"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
recipeName, recipeVersion := "", ""
@@ -238,14 +240,14 @@ func (h *Handler) handleCreateRecipeJob(ctx context.Context, body []byte) ([]byt
return nil, err
}
- return json.Marshal(map[string]string{keyName: j.Name})
+ return json.Marshal(map[string]string{keyName: j.Name}, jsonx.V1())
}
func (h *Handler) handleDescribeJob(ctx context.Context, body []byte) ([]byte, error) {
var req struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
j, err := h.Backend.DescribeJob(ctx, req.Name)
@@ -256,7 +258,7 @@ func (h *Handler) handleDescribeJob(ctx context.Context, body []byte) ([]byte, e
cp := *j
cp.AccountID = ""
- return json.Marshal(cp)
+ return json.Marshal(cp, jsonx.V1())
}
func (h *Handler) handleListJobs(ctx context.Context, body []byte) ([]byte, error) {
@@ -266,7 +268,7 @@ func (h *Handler) handleListJobs(ctx context.Context, body []byte) ([]byte, erro
DatasetName string `json:"DatasetName"`
ProjectName string `json:"ProjectName"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
maxResults, token, err := listParams(req.MaxResults, req.NextToken)
if err != nil {
return nil, err
@@ -295,7 +297,7 @@ func (h *Handler) handleUpdateProfileJob(ctx context.Context, body []byte) ([]by
MaxRetries int `json:"MaxRetries"`
Timeout int `json:"Timeout"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := h.Backend.UpdateJob(
@@ -313,7 +315,7 @@ func (h *Handler) handleUpdateProfileJob(ctx context.Context, body []byte) ([]by
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name})
+ return json.Marshal(map[string]string{keyName: req.Name}, jsonx.V1())
}
// handleUpdateRecipeJob handles UpdateRecipeJob, whose wire field for the
@@ -332,7 +334,7 @@ func (h *Handler) handleUpdateRecipeJob(ctx context.Context, body []byte) ([]byt
MaxRetries int `json:"MaxRetries"`
Timeout int `json:"Timeout"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := h.Backend.UpdateJob(
@@ -348,28 +350,28 @@ func (h *Handler) handleUpdateRecipeJob(ctx context.Context, body []byte) ([]byt
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name})
+ return json.Marshal(map[string]string{keyName: req.Name}, jsonx.V1())
}
func (h *Handler) handleDeleteJob(ctx context.Context, body []byte) ([]byte, error) {
var req struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := h.Backend.DeleteJob(ctx, req.Name); err != nil {
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name})
+ return json.Marshal(map[string]string{keyName: req.Name}, jsonx.V1())
}
func (h *Handler) handleStartJobRun(ctx context.Context, body []byte) ([]byte, error) {
var req struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
run, err := h.Backend.StartJobRun(ctx, req.Name)
@@ -377,7 +379,7 @@ func (h *Handler) handleStartJobRun(ctx context.Context, body []byte) ([]byte, e
return nil, err
}
- return json.Marshal(map[string]string{"RunId": run.RunID})
+ return json.Marshal(map[string]string{"RunId": run.RunID}, jsonx.V1())
}
func (h *Handler) handleListJobRuns(ctx context.Context, body []byte) ([]byte, error) {
@@ -386,7 +388,7 @@ func (h *Handler) handleListJobRuns(ctx context.Context, body []byte) ([]byte, e
MaxResults string `json:"MaxResults"`
NextToken string `json:"NextToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
maxResults, token, err := listParams(req.MaxResults, req.NextToken)
@@ -407,7 +409,7 @@ func (h *Handler) handleDescribeJobRun(ctx context.Context, body []byte) ([]byte
Name string `json:"Name"`
RunID string `json:"RunId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
run, err := h.Backend.DescribeJobRun(ctx, req.Name, req.RunID)
@@ -415,7 +417,7 @@ func (h *Handler) handleDescribeJobRun(ctx context.Context, body []byte) ([]byte
return nil, err
}
- return json.Marshal(run)
+ return json.Marshal(run, jsonx.V1())
}
func (h *Handler) handleStopJobRun(ctx context.Context, body []byte) ([]byte, error) {
@@ -423,7 +425,7 @@ func (h *Handler) handleStopJobRun(ctx context.Context, body []byte) ([]byte, er
Name string `json:"Name"`
RunID string `json:"RunId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
run, err := h.Backend.StopJobRun(ctx, req.Name, req.RunID)
@@ -431,5 +433,5 @@ func (h *Handler) handleStopJobRun(ctx context.Context, body []byte) ([]byte, er
return nil, err
}
- return json.Marshal(map[string]string{"RunId": run.RunID})
+ return json.Marshal(map[string]string{"RunId": run.RunID}, jsonx.V1())
}
diff --git a/services/databrew/handler_projects.go b/services/databrew/handler_projects.go
index cd55898518..e614ecc9f0 100644
--- a/services/databrew/handler_projects.go
+++ b/services/databrew/handler_projects.go
@@ -2,11 +2,13 @@ package databrew
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseProjectOp(method, name, subOp string) string {
@@ -88,7 +90,7 @@ func (h *Handler) handleCreateProject(ctx context.Context, body []byte) ([]byte,
RoleArn string `json:"RoleArn"`
Sample Sample `json:"Sample"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
p, err := h.Backend.CreateProject(
@@ -104,14 +106,14 @@ func (h *Handler) handleCreateProject(ctx context.Context, body []byte) ([]byte,
return nil, err
}
- return json.Marshal(map[string]string{keyName: p.Name})
+ return json.Marshal(map[string]string{keyName: p.Name}, jsonx.V1())
}
func (h *Handler) handleDescribeProject(ctx context.Context, body []byte) ([]byte, error) {
var req struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
p, err := h.Backend.DescribeProject(ctx, req.Name)
@@ -122,7 +124,7 @@ func (h *Handler) handleDescribeProject(ctx context.Context, body []byte) ([]byt
cp := *p
cp.AccountID = ""
- return json.Marshal(cp)
+ return json.Marshal(cp, jsonx.V1())
}
func (h *Handler) handleListProjects(ctx context.Context, body []byte) ([]byte, error) {
@@ -130,7 +132,7 @@ func (h *Handler) handleListProjects(ctx context.Context, body []byte) ([]byte,
MaxResults string `json:"MaxResults"`
NextToken string `json:"NextToken"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
maxResults, token, err := listParams(req.MaxResults, req.NextToken)
if err != nil {
return nil, err
@@ -149,28 +151,28 @@ func (h *Handler) handleUpdateProject(ctx context.Context, body []byte) ([]byte,
RoleArn string `json:"RoleArn"`
Sample Sample `json:"Sample"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := h.Backend.UpdateProject(ctx, req.Name, req.RoleArn, req.Sample); err != nil {
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name})
+ return json.Marshal(map[string]string{keyName: req.Name}, jsonx.V1())
}
func (h *Handler) handleDeleteProject(ctx context.Context, body []byte) ([]byte, error) {
var req struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := h.Backend.DeleteProject(ctx, req.Name); err != nil {
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name})
+ return json.Marshal(map[string]string{keyName: req.Name}, jsonx.V1())
}
// handleStartProjectSession handles StartProjectSession. It cannot emulate a
@@ -182,7 +184,7 @@ func (h *Handler) handleStartProjectSession(ctx context.Context, body []byte) ([
Name string `json:"Name"`
AssumeControl bool `json:"AssumeControl"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if _, err := h.Backend.OpenProjectSession(ctx, req.Name); err != nil {
@@ -192,7 +194,7 @@ func (h *Handler) handleStartProjectSession(ctx context.Context, body []byte) ([
return json.Marshal(map[string]string{
keyName: req.Name,
"ClientSessionId": uuid.New().String(),
- })
+ }, jsonx.V1())
}
// handleSendProjectSessionAction handles SendProjectSessionAction. Applying
@@ -203,12 +205,12 @@ func (h *Handler) handleSendProjectSessionAction(ctx context.Context, body []byt
var req struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if _, err := h.Backend.DescribeProject(ctx, req.Name); err != nil {
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name})
+ return json.Marshal(map[string]string{keyName: req.Name}, jsonx.V1())
}
diff --git a/services/databrew/handler_recipes.go b/services/databrew/handler_recipes.go
index c095156655..380ba1d65a 100644
--- a/services/databrew/handler_recipes.go
+++ b/services/databrew/handler_recipes.go
@@ -2,10 +2,12 @@ package databrew
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseRecipeSubOp(method, subOp string) string {
@@ -115,7 +117,7 @@ func (h *Handler) handleCreateRecipe(ctx context.Context, body []byte) ([]byte,
Name string `json:"Name"`
Description string `json:"Description"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -132,7 +134,7 @@ func (h *Handler) handleCreateRecipe(ctx context.Context, body []byte) ([]byte,
return nil, err
}
- return json.Marshal(map[string]string{keyName: r.Name})
+ return json.Marshal(map[string]string{keyName: r.Name}, jsonx.V1())
}
func (h *Handler) handleDescribeRecipe(ctx context.Context, body []byte) ([]byte, error) {
@@ -140,7 +142,7 @@ func (h *Handler) handleDescribeRecipe(ctx context.Context, body []byte) ([]byte
Name string `json:"Name"`
RecipeVersion string `json:"RecipeVersion"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
r, err := h.Backend.DescribeRecipe(ctx, req.Name, req.RecipeVersion)
@@ -148,7 +150,7 @@ func (h *Handler) handleDescribeRecipe(ctx context.Context, body []byte) ([]byte
return nil, err
}
- return json.Marshal(r)
+ return json.Marshal(r, jsonx.V1())
}
func (h *Handler) handleListRecipes(ctx context.Context, body []byte) ([]byte, error) {
@@ -157,7 +159,7 @@ func (h *Handler) handleListRecipes(ctx context.Context, body []byte) ([]byte, e
NextToken string `json:"NextToken"`
RecipeVersion string `json:"RecipeVersion"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
maxResults, token, err := listParams(req.MaxResults, req.NextToken)
if err != nil {
return nil, err
@@ -173,14 +175,14 @@ func (h *Handler) handlePublishRecipe(ctx context.Context, body []byte) ([]byte,
Name string `json:"Name"`
Description string `json:"Description"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := h.Backend.PublishRecipe(ctx, req.Name, req.Description); err != nil {
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name})
+ return json.Marshal(map[string]string{keyName: req.Name}, jsonx.V1())
}
func (h *Handler) handleUpdateRecipe(ctx context.Context, body []byte) ([]byte, error) {
@@ -189,7 +191,7 @@ func (h *Handler) handleUpdateRecipe(ctx context.Context, body []byte) ([]byte,
Description string `json:"Description"`
Steps []RecipeStep `json:"Steps"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := validateRecipeStepMembers(req.Steps); err != nil {
@@ -200,21 +202,21 @@ func (h *Handler) handleUpdateRecipe(ctx context.Context, body []byte) ([]byte,
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name})
+ return json.Marshal(map[string]string{keyName: req.Name}, jsonx.V1())
}
func (h *Handler) handleDeleteRecipe(ctx context.Context, body []byte) ([]byte, error) {
var req struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := h.Backend.DeleteRecipe(ctx, req.Name); err != nil {
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name})
+ return json.Marshal(map[string]string{keyName: req.Name}, jsonx.V1())
}
func (h *Handler) handleBatchDeleteRecipeVersion(ctx context.Context, body []byte) ([]byte, error) {
@@ -222,7 +224,7 @@ func (h *Handler) handleBatchDeleteRecipeVersion(ctx context.Context, body []byt
Name string `json:"Name"`
RecipeVersions []string `json:"RecipeVersions"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
errs, err := h.Backend.BatchDeleteRecipeVersion(ctx, req.Name, req.RecipeVersions)
@@ -230,7 +232,7 @@ func (h *Handler) handleBatchDeleteRecipeVersion(ctx context.Context, body []byt
return nil, err
}
- return json.Marshal(map[string]any{keyName: req.Name, "Errors": errs})
+ return json.Marshal(map[string]any{keyName: req.Name, "Errors": errs}, jsonx.V1())
}
func (h *Handler) handleDeleteRecipeVersion(ctx context.Context, body []byte) ([]byte, error) {
@@ -238,14 +240,14 @@ func (h *Handler) handleDeleteRecipeVersion(ctx context.Context, body []byte) ([
Name string `json:"Name"`
RecipeVersion string `json:"RecipeVersion"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := h.Backend.DeleteRecipeVersion(ctx, req.Name, req.RecipeVersion); err != nil {
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name, "RecipeVersion": req.RecipeVersion})
+ return json.Marshal(map[string]string{keyName: req.Name, "RecipeVersion": req.RecipeVersion}, jsonx.V1())
}
func (h *Handler) handleListRecipeVersions(ctx context.Context, body []byte) ([]byte, error) {
@@ -254,7 +256,7 @@ func (h *Handler) handleListRecipeVersions(ctx context.Context, body []byte) ([]
MaxResults string `json:"MaxResults"`
NextToken string `json:"NextToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
maxResults, token, err := listParams(req.MaxResults, req.NextToken)
diff --git a/services/databrew/handler_rulesets.go b/services/databrew/handler_rulesets.go
index 7628821e64..d72d0c4070 100644
--- a/services/databrew/handler_rulesets.go
+++ b/services/databrew/handler_rulesets.go
@@ -2,9 +2,11 @@ package databrew
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseRulesetOp(method, name string) string {
@@ -65,7 +67,7 @@ func (h *Handler) handleCreateRuleset(ctx context.Context, body []byte) ([]byte,
TargetArn string `json:"TargetArn"`
Rules []Rule `json:"Rules"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.TargetArn == "" || req.Rules == nil {
@@ -81,14 +83,14 @@ func (h *Handler) handleCreateRuleset(ctx context.Context, body []byte) ([]byte,
return nil, err
}
- return json.Marshal(map[string]string{keyName: rs.Name})
+ return json.Marshal(map[string]string{keyName: rs.Name}, jsonx.V1())
}
func (h *Handler) handleDescribeRuleset(ctx context.Context, body []byte) ([]byte, error) {
var req struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
rs, err := h.Backend.DescribeRuleset(ctx, req.Name)
@@ -96,7 +98,7 @@ func (h *Handler) handleDescribeRuleset(ctx context.Context, body []byte) ([]byt
return nil, err
}
- return json.Marshal(newRulesetDescribeView(rs))
+ return json.Marshal(newRulesetDescribeView(rs), jsonx.V1())
}
func (h *Handler) handleListRulesets(ctx context.Context, body []byte) ([]byte, error) {
@@ -105,7 +107,7 @@ func (h *Handler) handleListRulesets(ctx context.Context, body []byte) ([]byte,
NextToken string `json:"NextToken"`
TargetArn string `json:"TargetArn"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
maxResults, token, err := listParams(req.MaxResults, req.NextToken)
if err != nil {
return nil, err
@@ -126,7 +128,7 @@ func (h *Handler) handleUpdateRuleset(ctx context.Context, body []byte) ([]byte,
Description string `json:"Description"`
Rules []Rule `json:"Rules"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.Rules == nil {
@@ -141,19 +143,19 @@ func (h *Handler) handleUpdateRuleset(ctx context.Context, body []byte) ([]byte,
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name})
+ return json.Marshal(map[string]string{keyName: req.Name}, jsonx.V1())
}
func (h *Handler) handleDeleteRuleset(ctx context.Context, body []byte) ([]byte, error) {
var req struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := h.Backend.DeleteRuleset(ctx, req.Name); err != nil {
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name})
+ return json.Marshal(map[string]string{keyName: req.Name}, jsonx.V1())
}
diff --git a/services/databrew/handler_schedules.go b/services/databrew/handler_schedules.go
index 683225c9a0..f861ea9816 100644
--- a/services/databrew/handler_schedules.go
+++ b/services/databrew/handler_schedules.go
@@ -2,9 +2,11 @@ package databrew
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseScheduleOp(method, name string) string {
@@ -64,7 +66,7 @@ func (h *Handler) handleCreateSchedule(ctx context.Context, body []byte) ([]byte
CronExpression string `json:"CronExpression"`
JobNames []string `json:"JobNames"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
sc, err := h.Backend.CreateSchedule(ctx, req.Name, req.JobNames, req.CronExpression, req.Tags)
@@ -72,14 +74,14 @@ func (h *Handler) handleCreateSchedule(ctx context.Context, body []byte) ([]byte
return nil, err
}
- return json.Marshal(map[string]string{keyName: sc.Name})
+ return json.Marshal(map[string]string{keyName: sc.Name}, jsonx.V1())
}
func (h *Handler) handleDescribeSchedule(ctx context.Context, body []byte) ([]byte, error) {
var req struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
sc, err := h.Backend.DescribeSchedule(ctx, req.Name)
@@ -90,7 +92,7 @@ func (h *Handler) handleDescribeSchedule(ctx context.Context, body []byte) ([]by
cp := *sc
cp.AccountID = ""
- return json.Marshal(cp)
+ return json.Marshal(cp, jsonx.V1())
}
func (h *Handler) handleListSchedules(ctx context.Context, body []byte) ([]byte, error) {
@@ -99,7 +101,7 @@ func (h *Handler) handleListSchedules(ctx context.Context, body []byte) ([]byte,
NextToken string `json:"NextToken"`
JobName string `json:"JobName"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
maxResults, token, err := listParams(req.MaxResults, req.NextToken)
if err != nil {
return nil, err
@@ -116,26 +118,26 @@ func (h *Handler) handleUpdateSchedule(ctx context.Context, body []byte) ([]byte
CronExpression string `json:"CronExpression"`
JobNames []string `json:"JobNames"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := h.Backend.UpdateSchedule(ctx, req.Name, req.JobNames, req.CronExpression); err != nil {
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name})
+ return json.Marshal(map[string]string{keyName: req.Name}, jsonx.V1())
}
func (h *Handler) handleDeleteSchedule(ctx context.Context, body []byte) ([]byte, error) {
var req struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := h.Backend.DeleteSchedule(ctx, req.Name); err != nil {
return nil, err
}
- return json.Marshal(map[string]string{keyName: req.Name})
+ return json.Marshal(map[string]string{keyName: req.Name}, jsonx.V1())
}
diff --git a/services/databrew/handler_tags.go b/services/databrew/handler_tags.go
index e988ed5516..6b1321b3cb 100644
--- a/services/databrew/handler_tags.go
+++ b/services/databrew/handler_tags.go
@@ -2,9 +2,11 @@ package databrew
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseTagsOp(method, _ string) string {
@@ -47,14 +49,14 @@ func (h *Handler) handleTagResource(ctx context.Context, body []byte) ([]byte, e
Tags map[string]string `json:"Tags"`
ResourceArn string `json:"ResourceArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := h.Backend.UpdateTagsByArn(ctx, req.ResourceArn, req.Tags, nil); err != nil {
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
func (h *Handler) handleUntagResource(ctx context.Context, body []byte) ([]byte, error) {
@@ -62,21 +64,21 @@ func (h *Handler) handleUntagResource(ctx context.Context, body []byte) ([]byte,
ResourceArn string `json:"ResourceArn"`
TagKeys []string `json:"TagKeys"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if err := h.Backend.UpdateTagsByArn(ctx, req.ResourceArn, nil, req.TagKeys); err != nil {
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
func (h *Handler) handleListTagsForResource(ctx context.Context, body []byte) ([]byte, error) {
var req struct {
ResourceArn string `json:"ResourceArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
tags, err := h.Backend.FindTagsByArn(ctx, req.ResourceArn)
@@ -84,5 +86,5 @@ func (h *Handler) handleListTagsForResource(ctx context.Context, body []byte) ([
return nil, err
}
- return json.Marshal(map[string]any{"Tags": tags})
+ return json.Marshal(map[string]any{"Tags": tags}, jsonx.V1())
}
diff --git a/services/databrew/handler_test.go b/services/databrew/handler_test.go
index ac3cfd13d3..4b749e3455 100644
--- a/services/databrew/handler_test.go
+++ b/services/databrew/handler_test.go
@@ -1,7 +1,7 @@
package databrew_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -9,6 +9,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- Handler metadata ----
@@ -30,7 +32,7 @@ func TestHandlerReset(t *testing.T) {
rec := databrewReq(t, h, http.MethodGet, "/databrew/v1/datasets", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
datasets := resp["Datasets"].([]any)
assert.Empty(t, datasets)
}
diff --git a/services/databrew/jobs_test.go b/services/databrew/jobs_test.go
index cb673e183c..cf3c2ee8ac 100644
--- a/services/databrew/jobs_test.go
+++ b/services/databrew/jobs_test.go
@@ -2,7 +2,7 @@ package databrew_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/databrew"
)
@@ -738,7 +739,7 @@ func TestHandlerUpdateProfileJob(t *testing.T) {
)
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "upd-profile-j", resp["Name"])
}
@@ -811,7 +812,7 @@ func TestHandlerDescribeJobRun(t *testing.T) {
runRec := databrewReq(t, h, http.MethodPost, "/databrew/v1/jobs/djr-job/startJobRun", nil)
require.Equal(t, http.StatusOK, runRec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &startResp, jsonx.V1()))
runID, _ := startResp["RunId"].(string)
require.NotEmpty(t, runID)
@@ -832,7 +833,7 @@ func TestHandlerStopJobRun(t *testing.T) {
runRec := databrewReq(t, h, http.MethodPost, "/databrew/v1/jobs/sjr-job/startJobRun", nil)
require.Equal(t, http.StatusOK, runRec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &startResp, jsonx.V1()))
runID, _ := startResp["RunId"].(string)
require.NotEmpty(t, runID)
@@ -854,7 +855,7 @@ func TestStartJobRun_RunId(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["RunID"], "RunID (uppercase D) must not appear — AWS SDK uses RunId")
runID, ok := resp["RunId"].(string)
assert.True(t, ok, "response must have RunId string field")
@@ -935,7 +936,7 @@ func TestListJobs_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
jobs := resp["Jobs"].([]any)
assert.Len(t, jobs, tc.wantCount, "query=%v", tc.queryParams)
})
@@ -955,7 +956,7 @@ func TestJobRunIdField_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
runID, ok := startResp["RunId"].(string)
require.True(t, ok)
require.NotEmpty(t, runID)
@@ -967,13 +968,13 @@ func TestJobRunIdField_RoundTrip(t *testing.T) {
descRec := databrewReq(t, h, http.MethodGet, "/databrew/v1/jobs/rt-job/jobRun/"+runID, nil)
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, runID, descResp["RunId"])
stopRec := databrewReq(t, h, http.MethodPost, "/databrew/v1/jobs/rt-job/jobRun/"+runID, nil)
require.Equal(t, http.StatusOK, stopRec.Code)
var stopResp map[string]any
- require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopResp))
+ require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopResp, jsonx.V1()))
assert.Equal(t, runID, stopResp["RunId"])
})
}
@@ -1394,7 +1395,7 @@ func TestHandlerCreateProfileJob_Extras(t *testing.T) {
rec := databrewReq(t, h, http.MethodGet, "/databrew/v1/jobs/pj-extras", nil)
require.Equal(t, http.StatusOK, rec.Code)
var job map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &job))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &job, jsonx.V1()))
jobSample, ok := job["JobSample"].(map[string]any)
require.True(t, ok, "JobSample must round-trip as an object")
assert.Equal(t, "CUSTOM_ROWS", jobSample["Mode"])
@@ -1428,7 +1429,7 @@ func TestHandlerCreateRecipeJob_Extras(t *testing.T) {
rec := databrewReq(t, h, http.MethodGet, "/databrew/v1/jobs/rj-extras", nil)
require.Equal(t, http.StatusOK, rec.Code)
var job map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &job))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &job, jsonx.V1()))
assert.Equal(t, "SSE-KMS", job["EncryptionMode"])
assert.Equal(t, "ENABLE", job["LogSubscription"])
outputs, ok := job["DataCatalogOutputs"].([]any)
diff --git a/services/databrew/paginate_helper.go b/services/databrew/paginate_helper.go
index 762b051097..7781059dcd 100644
--- a/services/databrew/paginate_helper.go
+++ b/services/databrew/paginate_helper.go
@@ -2,10 +2,12 @@ package databrew
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func paginateKeys(keys []string, maxResults int, nextToken string) ([]string, string) {
@@ -69,5 +71,5 @@ func marshalPage(key string, items any, next string) ([]byte, error) {
out[nextTokenKey] = base64.RawURLEncoding.EncodeToString([]byte(pageTokenPrefix + next))
}
- return json.Marshal(out)
+ return json.Marshal(out, jsonx.V1())
}
diff --git a/services/databrew/persistence.go b/services/databrew/persistence.go
index 3205f05d08..45e09bcada 100644
--- a/services/databrew/persistence.go
+++ b/services/databrew/persistence.go
@@ -2,7 +2,7 @@ package databrew
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"strings"
@@ -43,7 +43,7 @@ const databrewSnapshotVersion = 1
// directly here, nested the same way it is held on InMemoryBackend
// (region -> job name -> run history).
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
JobRuns map[string]map[string][]*JobRun `json:"jobRuns"`
RecipeVersions map[string]map[string][]*Recipe `json:"recipeVersions"`
AccountID string `json:"accountID"`
@@ -90,7 +90,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
// "/" (see store_setup.go's *Table accessors);
// splitting on the first "/" recovers both parts (region names never
// contain "/").
-func (b *InMemoryBackend) preRegisterSnapshotTables(tables map[string]json.RawMessage) {
+func (b *InMemoryBackend) preRegisterSnapshotTables(tables map[string]jsontext.Value) {
for key := range tables {
name, region, found := strings.Cut(key, "/")
if !found {
@@ -144,7 +144,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
}
if snap.Tables == nil {
- snap.Tables = make(map[string]json.RawMessage)
+ snap.Tables = make(map[string]jsontext.Value)
}
b.preRegisterSnapshotTables(snap.Tables)
diff --git a/services/databrew/projects_test.go b/services/databrew/projects_test.go
index 255f6f9cec..5745c1dcfe 100644
--- a/services/databrew/projects_test.go
+++ b/services/databrew/projects_test.go
@@ -2,13 +2,14 @@ package databrew_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/databrew"
)
@@ -242,7 +243,7 @@ func TestHandlerUpdateProject(t *testing.T) {
desc := databrewReq(t, h, http.MethodGet, "/databrew/v1/projects/upd-proj", nil)
require.Equal(t, http.StatusOK, desc.Code)
var proj map[string]any
- require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &proj))
+ require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &proj, jsonx.V1()))
assert.Equal(t, "orig-ds", proj["DatasetName"], "DatasetName must be immutable via UpdateProject")
assert.Equal(t, "arn:aws:iam::123456789012:role/new", proj["RoleArn"])
}
@@ -276,7 +277,7 @@ func TestHandlerStartProjectSession(t *testing.T) {
)
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "sess-proj", resp["Name"])
}
@@ -297,7 +298,7 @@ func TestHandlerSendProjectSessionAction(t *testing.T) {
)
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "action-proj", resp["Name"])
}
@@ -314,7 +315,7 @@ func TestHandlerStartProjectSession_ClientSessionID(t *testing.T) {
rec := databrewReq(t, h, http.MethodPut, "/databrew/v1/projects/sess-id-proj/startProjectSession", nil)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["ClientSessionId"])
}
diff --git a/services/databrew/recipes_test.go b/services/databrew/recipes_test.go
index 6f0d069734..7602ebc4c5 100644
--- a/services/databrew/recipes_test.go
+++ b/services/databrew/recipes_test.go
@@ -2,13 +2,14 @@ package databrew_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/databrew"
)
@@ -193,7 +194,7 @@ func TestHandlerCreateRecipe(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "my-recipe", resp["Name"])
}
@@ -239,7 +240,7 @@ func TestHandlerListRecipes(t *testing.T) {
rec := databrewReq(t, h, http.MethodGet, "/databrew/v1/recipes", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Recipes"])
}
@@ -280,7 +281,7 @@ func TestHandlerListRecipeVersions(t *testing.T) {
rec := databrewReq(t, h, http.MethodGet, "/databrew/v1/recipes/ver-r/recipeVersions", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Recipes"])
}
@@ -293,7 +294,7 @@ func TestHandlerListRecipeVersions_UnknownRecipe(t *testing.T) {
rec := databrewReq(t, h, http.MethodGet, "/databrew/v1/recipes/no-such/recipeVersions", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["Recipes"])
}
@@ -356,7 +357,7 @@ func TestHandlerBatchDeleteRecipeVersion_LatestWorkingOnly(t *testing.T) {
var out struct {
Errors []map[string]string `json:"Errors"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.Errors, "deleting LATEST_WORKING as the only version must succeed with no partial failures")
desc := databrewReq(t, h, http.MethodGet, "/databrew/v1/recipes/bdrv-lw-only", nil)
@@ -394,7 +395,7 @@ func TestHandlerBatchDeleteRecipeVersion_LatestWorkingBlockedByPublished(t *test
ErrorCode string `json:"ErrorCode"`
} `json:"Errors"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Errors, 1)
assert.Equal(t, "LATEST_WORKING", out.Errors[0].RecipeVersion)
assert.Equal(t, "ValidationException", out.Errors[0].ErrorCode)
@@ -429,7 +430,7 @@ func TestRecipeReference(t *testing.T) {
require.Equal(t, http.StatusOK, desc.Code)
var job map[string]any
- require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &job))
+ require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &job, jsonx.V1()))
assert.Nil(t, job["RecipeName"], "RecipeName must not appear in JSON — AWS SDK uses RecipeReference")
ref, ok := job["RecipeReference"].(map[string]any)
@@ -463,7 +464,7 @@ func TestRecipeReference_HonorsNumericVersion(t *testing.T) {
require.Equal(t, http.StatusOK, desc.Code)
var job map[string]any
- require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &job))
+ require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &job, jsonx.V1()))
ref, ok := job["RecipeReference"].(map[string]any)
require.True(t, ok, "RecipeReference must be an object")
diff --git a/services/databrew/rulesets_test.go b/services/databrew/rulesets_test.go
index afcbe72660..83825b6371 100644
--- a/services/databrew/rulesets_test.go
+++ b/services/databrew/rulesets_test.go
@@ -2,13 +2,14 @@ package databrew_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/databrew"
)
@@ -177,7 +178,7 @@ func TestHandlerCreateRuleset(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "my-ruleset", resp["Name"])
}
@@ -204,7 +205,7 @@ func TestHandlerListRulesets(t *testing.T) {
rec := databrewReq(t, h, http.MethodGet, "/databrew/v1/rulesets", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Rulesets"])
}
@@ -283,7 +284,7 @@ func TestHandlerListRulesets_RuleCount(t *testing.T) {
var resp struct {
Rulesets []map[string]any `json:"Rulesets"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Rulesets, 1)
assert.InDelta(t, float64(1), resp.Rulesets[0]["RuleCount"], 0)
}
@@ -308,7 +309,7 @@ func TestHandlerListRulesets_NoRulesLeak(t *testing.T) {
var resp struct {
Rulesets []map[string]any `json:"Rulesets"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Rulesets, 1)
_, hasRules := resp.Rulesets[0]["Rules"]
assert.False(t, hasRules, "ListRulesets item leaked Rules; real types.RulesetItem has no Rules member")
@@ -331,7 +332,7 @@ func TestHandlerDescribeRuleset_NoAccountIDOrRuleCountLeak(t *testing.T) {
rec := databrewReq(t, h, http.MethodGet, "/databrew/v1/rulesets/no-leak-describe-rs", nil)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasAccountID := resp["AccountId"]
assert.False(
t,
diff --git a/services/databrew/schedules_test.go b/services/databrew/schedules_test.go
index ef5173790c..29870ec226 100644
--- a/services/databrew/schedules_test.go
+++ b/services/databrew/schedules_test.go
@@ -2,12 +2,14 @@ package databrew_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- Schedule backend ----
@@ -164,7 +166,7 @@ func TestHandlerCreateSchedule(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "my-schedule", resp["Name"])
}
@@ -191,7 +193,7 @@ func TestHandlerListSchedules(t *testing.T) {
rec := databrewReq(t, h, http.MethodGet, "/databrew/v1/schedules", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Schedules"])
}
diff --git a/services/databrew/store_test.go b/services/databrew/store_test.go
index e927d15c09..198896d43c 100644
--- a/services/databrew/store_test.go
+++ b/services/databrew/store_test.go
@@ -3,7 +3,8 @@ package databrew_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/databrew"
)
@@ -43,7 +45,7 @@ func databrewReq(t *testing.T, h *databrew.Handler, method, path string, body an
var buf bytes.Buffer
if body != nil {
- require.NoError(t, json.NewEncoder(&buf).Encode(body))
+ require.NoError(t, json.MarshalEncode(jsontext.NewEncoder(&buf, jsonx.V1()), body))
}
req := httptest.NewRequest(method, path, &buf)
@@ -198,7 +200,7 @@ func TestHandlerDescribe_NoAccountIDLeak(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasAccountID := resp["AccountId"]
assert.False(t, hasAccountID, "Describe leaked AccountId; the real Describe*Output has no AccountId member")
})
@@ -224,7 +226,7 @@ func TestHandlerDescribeProject_NoSessionStatusFabrication(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasSessionStatus := resp["SessionStatus"]
assert.False(t, hasSessionStatus, "DescribeProject fabricated SessionStatus; types.Project has no such member")
}
diff --git a/services/databrew/tags_test.go b/services/databrew/tags_test.go
index 5f77ef74eb..da8836c869 100644
--- a/services/databrew/tags_test.go
+++ b/services/databrew/tags_test.go
@@ -2,13 +2,14 @@ package databrew_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/databrew"
)
@@ -320,7 +321,7 @@ func TestHandlerListTagsForResource(t *testing.T) {
rec := databrewReq(t, h, http.MethodGet, "/databrew/v1/tags/"+ds.Arn, nil)
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Tags"])
}
@@ -375,7 +376,7 @@ func TestTagsOps(t *testing.T) {
listRec := databrewReq(t, h, http.MethodGet, prefix, nil)
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags := listResp["Tags"].(map[string]any)
assert.Equal(t, "prod", tags["env"])
diff --git a/services/datasync/handler.go b/services/datasync/handler.go
index 9cca34a962..15a61d4265 100644
--- a/services/datasync/handler.go
+++ b/services/datasync/handler.go
@@ -2,7 +2,7 @@ package datasync
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -226,13 +227,10 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, describeNotFound(err, body)
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
// ErrNotFound/ErrAlreadyExists deliberately map to the SAME wire code as
// ErrInvalidParameter below: every one of datasync's 53
@@ -249,8 +247,8 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
errors.Is(err, awserr.ErrInvalidParameter),
errors.Is(err, errInvalidRequest),
errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr),
- errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err),
+ jsonx.IsTypeError(err):
return c.JSON(http.StatusBadRequest, map[string]string{
keyType: "InvalidRequestException",
keyMessage: publicMessage(err),
diff --git a/services/datasync/handler_agents_test.go b/services/datasync/handler_agents_test.go
index 1f3b549478..53b280983f 100644
--- a/services/datasync/handler_agents_test.go
+++ b/services/datasync/handler_agents_test.go
@@ -1,13 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/datasync"
)
@@ -33,7 +34,7 @@ func TestDataSync_Agent(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp["AgentArn"], "arn:aws:datasync:us-east-1:000000000000:agent/")
},
},
@@ -54,7 +55,7 @@ func TestDataSync_Agent(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "ONLINE", resp["Status"])
},
},
@@ -93,7 +94,7 @@ func TestDataSync_Agent(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
agents, ok := resp["Agents"].([]any)
require.True(t, ok)
assert.Empty(t, agents)
@@ -138,7 +139,7 @@ func TestDataSync_AgentCRUD(t *testing.T) {
rec := doRequest(t, h, "DescribeAgent", map[string]any{"AgentArn": agentArn})
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "test-agent", descResp["Name"])
// Update
@@ -147,14 +148,14 @@ func TestDataSync_AgentCRUD(t *testing.T) {
// Verify update
rec = doRequest(t, h, "DescribeAgent", map[string]any{"AgentArn": agentArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "updated-agent", descResp["Name"])
// List
rec = doRequest(t, h, "ListAgents", map[string]any{})
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["Agents"], 1)
// Delete
diff --git a/services/datasync/handler_locations_agentarns_test.go b/services/datasync/handler_locations_agentarns_test.go
index 6c46772e1d..0929560627 100644
--- a/services/datasync/handler_locations_agentarns_test.go
+++ b/services/datasync/handler_locations_agentarns_test.go
@@ -1,12 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// agentArnsLocationCase covers one of the six location types that accept
@@ -179,7 +181,7 @@ func TestDataSync_AgentArns_RejectsPhantomAgent(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", resp["__type"])
})
}
@@ -206,7 +208,7 @@ func TestDataSync_AgentArns_UpdateRejectsPhantomAgent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "create: %s", rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn, ok := createResp["LocationArn"].(string)
require.True(t, ok)
@@ -214,7 +216,7 @@ func TestDataSync_AgentArns_UpdateRejectsPhantomAgent(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code, "update: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", resp["__type"])
// The rejected update must not have applied: AgentArns on the
@@ -223,7 +225,7 @@ func TestDataSync_AgentArns_UpdateRejectsPhantomAgent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.ElementsMatch(t, []any{realAgentArn}, tc.agentArns(t, descResp))
})
}
@@ -246,7 +248,7 @@ func TestDataSync_AgentArns_AcceptsRealAgent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn, ok := createResp["LocationArn"].(string)
require.True(t, ok)
@@ -254,7 +256,7 @@ func TestDataSync_AgentArns_AcceptsRealAgent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.ElementsMatch(t, []any{agentArn}, tc.agentArns(t, descResp))
})
}
diff --git a/services/datasync/handler_locations_azureblob_test.go b/services/datasync/handler_locations_azureblob_test.go
index 4d744fa50a..e52b021b7f 100644
--- a/services/datasync/handler_locations_azureblob_test.go
+++ b/services/datasync/handler_locations_azureblob_test.go
@@ -1,12 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDataSync_AzureBlob covers the AzureBlob location lifecycle.
@@ -31,7 +33,7 @@ func TestDataSync_AzureBlob(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn, ok := createResp["LocationArn"].(string)
require.True(t, ok)
require.NotEmpty(t, locArn)
@@ -41,7 +43,7 @@ func TestDataSync_AzureBlob(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, locArn, descResp["LocationArn"])
assert.Equal(t, "BLOCK", descResp["BlobType"])
assert.Equal(t, "HOT", descResp["AccessTier"])
@@ -62,7 +64,7 @@ func TestDataSync_AzureBlob(t *testing.T) {
// Describe after update
rec = doRequest(t, h, "DescribeLocationAzureBlob", map[string]any{"LocationArn": locArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "COOL", descResp["AccessTier"])
// Not found
diff --git a/services/datasync/handler_locations_efs_test.go b/services/datasync/handler_locations_efs_test.go
index 9c60568625..249b671ad2 100644
--- a/services/datasync/handler_locations_efs_test.go
+++ b/services/datasync/handler_locations_efs_test.go
@@ -1,12 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDataSync_Efs covers the EFS location lifecycle.
@@ -28,7 +30,7 @@ func TestDataSync_Efs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn := createResp["LocationArn"].(string)
// Describe
@@ -36,7 +38,7 @@ func TestDataSync_Efs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, locArn, descResp["LocationArn"])
assert.Equal(t, "TLS1_2", descResp["InTransitEncryption"])
assert.Contains(t, descResp["LocationUri"].(string), "efs://")
diff --git a/services/datasync/handler_locations_fsxlustre_test.go b/services/datasync/handler_locations_fsxlustre_test.go
index 6b9081886b..cdb33839bf 100644
--- a/services/datasync/handler_locations_fsxlustre_test.go
+++ b/services/datasync/handler_locations_fsxlustre_test.go
@@ -1,12 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDataSync_FsxLustre covers the FSx Lustre location lifecycle.
@@ -24,7 +26,7 @@ func TestDataSync_FsxLustre(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn := createResp["LocationArn"].(string)
// Describe
@@ -32,7 +34,7 @@ func TestDataSync_FsxLustre(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
// "fsxl://", not the bare "lustre://" real AWS's LocationUri pattern
// (`^(efs|nfs|s3|smb|hdfs|fsx[a-z0-9-]+)://...$`) rules out -- see
// fsxLustreURIScheme.
diff --git a/services/datasync/handler_locations_fsxontap_test.go b/services/datasync/handler_locations_fsxontap_test.go
index 92917d6b1d..91e5d06262 100644
--- a/services/datasync/handler_locations_fsxontap_test.go
+++ b/services/datasync/handler_locations_fsxontap_test.go
@@ -1,12 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDataSync_FsxOntap covers the FSx ONTAP location lifecycle.
@@ -29,7 +31,7 @@ func TestDataSync_FsxOntap(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn := createResp["LocationArn"].(string)
// Describe
@@ -37,7 +39,7 @@ func TestDataSync_FsxOntap(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
// NFS protocol -> "nfs://" scheme (ONTAP has no distinct "ontap://" or
// "fsxn://" scheme; DataSync reuses the underlying access protocol's own
// scheme, matching how FSx Windows reuses "smb://" -- see
@@ -57,7 +59,7 @@ func TestDataSync_FsxOntap(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
rec = doRequest(t, h, "DescribeLocationFsxOntap", map[string]any{"LocationArn": locArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Contains(t, descResp["LocationUri"].(string), "smb://")
// Missing StorageVirtualMachineArn
diff --git a/services/datasync/handler_locations_fsxopenzfs_test.go b/services/datasync/handler_locations_fsxopenzfs_test.go
index 8e8430085d..a0c2678ec2 100644
--- a/services/datasync/handler_locations_fsxopenzfs_test.go
+++ b/services/datasync/handler_locations_fsxopenzfs_test.go
@@ -1,12 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDataSync_FsxOpenZfs covers the FSx OpenZFS location lifecycle.
@@ -29,7 +31,7 @@ func TestDataSync_FsxOpenZfs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn := createResp["LocationArn"].(string)
// Describe
@@ -37,7 +39,7 @@ func TestDataSync_FsxOpenZfs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Contains(t, descResp["LocationUri"].(string), "fsxz://")
// The real DescribeLocationFsxOpenZfsOutput has no FsxFilesystemArn field.
assert.Nil(t, descResp["FsxFilesystemArn"])
diff --git a/services/datasync/handler_locations_fsxwindows_test.go b/services/datasync/handler_locations_fsxwindows_test.go
index 450a5feb79..dafa9f0059 100644
--- a/services/datasync/handler_locations_fsxwindows_test.go
+++ b/services/datasync/handler_locations_fsxwindows_test.go
@@ -1,12 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDataSync_FsxWindows covers the FSx Windows location lifecycle.
@@ -27,7 +29,7 @@ func TestDataSync_FsxWindows(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn := createResp["LocationArn"].(string)
// Describe
@@ -35,7 +37,7 @@ func TestDataSync_FsxWindows(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Contains(t, descResp["LocationUri"].(string), "smb://")
assert.Equal(t, "CORP", descResp["Domain"])
assert.Equal(t, "svcuser", descResp["User"])
@@ -56,7 +58,7 @@ func TestDataSync_FsxWindows(t *testing.T) {
// Verify update
rec = doRequest(t, h, "DescribeLocationFsxWindows", map[string]any{"LocationArn": locArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "NEWDOMAIN", descResp["Domain"])
// Missing FsxFilesystemArn
diff --git a/services/datasync/handler_locations_hdfs_test.go b/services/datasync/handler_locations_hdfs_test.go
index 5d95a0739b..8604ab7dc4 100644
--- a/services/datasync/handler_locations_hdfs_test.go
+++ b/services/datasync/handler_locations_hdfs_test.go
@@ -1,12 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDataSync_Hdfs covers the HDFS location lifecycle.
@@ -35,7 +37,7 @@ func TestDataSync_Hdfs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn := createResp["LocationArn"].(string)
// Describe
@@ -43,7 +45,7 @@ func TestDataSync_Hdfs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Contains(t, descResp["LocationUri"].(string), "hdfs://")
assert.Equal(t, "SIMPLE", descResp["AuthenticationType"])
assert.Equal(t, "hadoop", descResp["SimpleUser"])
@@ -62,7 +64,7 @@ func TestDataSync_Hdfs(t *testing.T) {
// Verify update
rec = doRequest(t, h, "DescribeLocationHdfs", map[string]any{"LocationArn": locArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "newuser", descResp["SimpleUser"])
nameNodes := descResp["NameNodes"].([]any)
require.Len(t, nameNodes, 1)
diff --git a/services/datasync/handler_locations_nfs_test.go b/services/datasync/handler_locations_nfs_test.go
index d757968c05..65d40f36ed 100644
--- a/services/datasync/handler_locations_nfs_test.go
+++ b/services/datasync/handler_locations_nfs_test.go
@@ -1,12 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDataSync_Nfs covers the NFS location lifecycle.
@@ -29,7 +31,7 @@ func TestDataSync_Nfs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn := createResp["LocationArn"].(string)
// Describe
@@ -37,7 +39,7 @@ func TestDataSync_Nfs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Contains(t, descResp["LocationUri"].(string), "nfs://")
assert.NotNil(t, descResp["MountOptions"])
assert.NotNil(t, descResp["OnPremConfig"])
@@ -58,7 +60,7 @@ func TestDataSync_Nfs(t *testing.T) {
// Verify update
rec = doRequest(t, h, "DescribeLocationNfs", map[string]any{"LocationArn": locArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
mo := descResp["MountOptions"].(map[string]any)
assert.Equal(t, "NFS4_1", mo["Version"])
@@ -120,7 +122,7 @@ func TestDataSync_UpdateLocationNfs_ServerHostname(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn, ok := createResp["LocationArn"].(string)
require.True(t, ok)
@@ -132,7 +134,7 @@ func TestDataSync_UpdateLocationNfs_ServerHostname(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, tt.wantURI, descResp["LocationUri"])
})
}
diff --git a/services/datasync/handler_locations_objectstorage_test.go b/services/datasync/handler_locations_objectstorage_test.go
index de2fe86594..3584f4adbd 100644
--- a/services/datasync/handler_locations_objectstorage_test.go
+++ b/services/datasync/handler_locations_objectstorage_test.go
@@ -1,12 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDataSync_ObjectStorage covers the ObjectStorage location lifecycle.
@@ -30,7 +32,7 @@ func TestDataSync_ObjectStorage(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn := createResp["LocationArn"].(string)
// Describe
@@ -38,7 +40,7 @@ func TestDataSync_ObjectStorage(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Contains(t, descResp["LocationUri"].(string), "object-storage://")
assert.Equal(t, "HTTPS", descResp["ServerProtocol"])
// SecretKey not returned
@@ -59,7 +61,7 @@ func TestDataSync_ObjectStorage(t *testing.T) {
// Verify update
rec = doRequest(t, h, "DescribeLocationObjectStorage", map[string]any{"LocationArn": locArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "HTTP", descResp["ServerProtocol"])
// Missing ServerHostname
@@ -127,7 +129,7 @@ func TestDataSync_UpdateLocationObjectStorage_ServerHostname(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn, ok := createResp["LocationArn"].(string)
require.True(t, ok)
@@ -139,7 +141,7 @@ func TestDataSync_UpdateLocationObjectStorage_ServerHostname(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, tt.wantURI, descResp["LocationUri"])
if proto, isSet := tt.update["ServerProtocol"]; isSet {
diff --git a/services/datasync/handler_locations_secretconfig_test.go b/services/datasync/handler_locations_secretconfig_test.go
index 659d399069..ee7201640a 100644
--- a/services/datasync/handler_locations_secretconfig_test.go
+++ b/services/datasync/handler_locations_secretconfig_test.go
@@ -1,7 +1,7 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/datasync"
)
@@ -126,7 +127,7 @@ func TestDataSync_SecretConfig_CmkRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "create: %s", rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn, ok := createResp["LocationArn"].(string)
require.True(t, ok)
@@ -134,7 +135,7 @@ func TestDataSync_SecretConfig_CmkRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "describe: %s", rec.Body.String())
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
cmk, ok := descResp["CmkSecretConfig"].(map[string]any)
require.True(t, ok, "CmkSecretConfig missing from describe response: %v", descResp)
@@ -166,7 +167,7 @@ func TestDataSync_SecretConfig_CustomRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "create: %s", rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn, ok := createResp["LocationArn"].(string)
require.True(t, ok)
@@ -174,7 +175,7 @@ func TestDataSync_SecretConfig_CustomRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "describe: %s", rec.Body.String())
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
custom, ok := descResp["CustomSecretConfig"].(map[string]any)
require.True(t, ok, "CustomSecretConfig missing from describe response: %v", descResp)
@@ -239,7 +240,7 @@ func TestDataSync_Smb_KerberosAndDNS(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn, ok := createResp["LocationArn"].(string)
require.True(t, ok)
@@ -247,7 +248,7 @@ func TestDataSync_Smb_KerberosAndDNS(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "KERBEROS", descResp["AuthenticationType"])
assert.Equal(t, "HOST/kerberosuser@MYDOMAIN.ORG", descResp["KerberosPrincipal"])
@@ -269,7 +270,7 @@ func TestDataSync_Smb_KerberosAndDNS(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
rec = doRequest(t, h, "DescribeLocationSmb", map[string]any{"LocationArn": locArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "HOST/other@MYDOMAIN.ORG", descResp["KerberosPrincipal"])
dns, ok = descResp["DnsIpAddresses"].([]any)
@@ -322,12 +323,12 @@ func TestDataSync_Smb_AuthenticationTypeEnum(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn := createResp["LocationArn"].(string)
rec = doRequest(t, h, "DescribeLocationSmb", map[string]any{"LocationArn": locArn})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, tt.expectEchoedAs, descResp["AuthenticationType"])
})
}
diff --git a/services/datasync/handler_locations_smb_test.go b/services/datasync/handler_locations_smb_test.go
index 2200920fa9..8cf9c62d82 100644
--- a/services/datasync/handler_locations_smb_test.go
+++ b/services/datasync/handler_locations_smb_test.go
@@ -1,12 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDataSync_Smb covers the SMB location lifecycle.
@@ -29,7 +31,7 @@ func TestDataSync_Smb(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn := createResp["LocationArn"].(string)
// Describe
@@ -37,7 +39,7 @@ func TestDataSync_Smb(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Contains(t, descResp["LocationUri"].(string), "smb://")
assert.Equal(t, "CORP", descResp["Domain"])
assert.Equal(t, "smbuser", descResp["User"])
@@ -62,7 +64,7 @@ func TestDataSync_Smb(t *testing.T) {
// Verify update
rec = doRequest(t, h, "DescribeLocationSmb", map[string]any{"LocationArn": locArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "NEWDOMAIN", descResp["Domain"])
mo := descResp["MountOptions"].(map[string]any)
assert.Equal(t, "AUTOMATIC", mo["Version"])
@@ -129,7 +131,7 @@ func TestDataSync_UpdateLocationSmb_ServerHostname(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn, ok := createResp["LocationArn"].(string)
require.True(t, ok)
@@ -141,7 +143,7 @@ func TestDataSync_UpdateLocationSmb_ServerHostname(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, tt.wantURI, descResp["LocationUri"])
if domain, isSet := tt.update["Domain"]; isSet {
diff --git a/services/datasync/handler_locations_test.go b/services/datasync/handler_locations_test.go
index c611b115e1..84011e8154 100644
--- a/services/datasync/handler_locations_test.go
+++ b/services/datasync/handler_locations_test.go
@@ -1,13 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/datasync"
)
@@ -36,7 +37,7 @@ func TestDataSync_LocationS3(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp["LocationArn"], "arn:aws:datasync:us-east-1:000000000000:location/")
},
},
@@ -76,7 +77,7 @@ func TestDataSync_LocationS3(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
locs, ok := resp["Locations"].([]any)
require.True(t, ok)
assert.Empty(t, locs)
@@ -114,14 +115,14 @@ func TestDataSync_LocationS3CRUD(t *testing.T) {
rec := doRequest(t, h, "DescribeLocationS3", map[string]any{"LocationArn": locationArn})
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Contains(t, descResp["LocationUri"].(string), "s3://my-test-bucket/")
// List
rec = doRequest(t, h, "ListLocations", map[string]any{})
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["Locations"], 1)
// Delete
@@ -195,14 +196,14 @@ func TestDataSync_LocationS3AgentArns(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
locArn := createResp["LocationArn"].(string)
rec = doRequest(t, h, "DescribeLocationS3", map[string]any{"LocationArn": locArn})
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
agentArns, ok := descResp["AgentArns"].([]any)
require.True(t, ok)
require.Len(t, agentArns, 1)
diff --git a/services/datasync/handler_tags_test.go b/services/datasync/handler_tags_test.go
index 5d074051a9..9b51cc36fe 100644
--- a/services/datasync/handler_tags_test.go
+++ b/services/datasync/handler_tags_test.go
@@ -1,12 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDataSync_Tags(t *testing.T) {
@@ -30,7 +32,7 @@ func TestDataSync_Tags(t *testing.T) {
rec = doRequest(t, h, "ListTagsForResource", map[string]any{"ResourceArn": agentArn})
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags, ok := listResp["Tags"].([]any)
require.True(t, ok)
assert.Len(t, tags, 2)
@@ -44,7 +46,7 @@ func TestDataSync_Tags(t *testing.T) {
// Verify untag
rec = doRequest(t, h, "ListTagsForResource", map[string]any{"ResourceArn": agentArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags = listResp["Tags"].([]any)
assert.Len(t, tags, 1)
@@ -79,7 +81,7 @@ func TestDataSync_TagsRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags, ok := listResp["Tags"].([]any)
require.True(t, ok)
@@ -102,7 +104,7 @@ func TestDataSync_TagsRoundTrip(t *testing.T) {
rec = doRequest(t, h, "ListTagsForResource", map[string]any{"ResourceArn": agentArn})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags, ok = listResp["Tags"].([]any)
require.True(t, ok)
diff --git a/services/datasync/handler_tasks_test.go b/services/datasync/handler_tasks_test.go
index d2f28e65e1..141727c413 100644
--- a/services/datasync/handler_tasks_test.go
+++ b/services/datasync/handler_tasks_test.go
@@ -1,13 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/datasync"
)
@@ -53,7 +54,7 @@ func TestDataSync_Task(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
tasks, ok := resp["Tasks"].([]any)
require.True(t, ok)
assert.Empty(t, tasks)
@@ -91,7 +92,7 @@ func TestDataSync_TaskCRUD(t *testing.T) {
rec := doRequest(t, h, "DescribeTask", map[string]any{"TaskArn": taskArn})
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "AVAILABLE", descResp["Status"])
assert.Equal(t, "test-task", descResp["Name"])
@@ -101,14 +102,14 @@ func TestDataSync_TaskCRUD(t *testing.T) {
// Verify update
rec = doRequest(t, h, "DescribeTask", map[string]any{"TaskArn": taskArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "renamed-task", descResp["Name"])
// List
rec = doRequest(t, h, "ListTasks", map[string]any{})
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["Tasks"], 1)
// Delete
@@ -131,7 +132,7 @@ func TestDataSync_TaskExecution(t *testing.T) {
rec := doRequest(t, h, "StartTaskExecution", map[string]any{"TaskArn": taskArn})
assert.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
execArn, ok := startResp["TaskExecutionArn"].(string)
require.True(t, ok)
assert.Contains(t, execArn, "/execution/")
@@ -144,7 +145,7 @@ func TestDataSync_TaskExecution(t *testing.T) {
rec = doRequest(t, h, "ListTaskExecutions", map[string]any{"TaskArn": taskArn})
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["TaskExecutions"], 1)
// CancelTaskExecution on an execution already settled into SUCCESS (by
@@ -155,7 +156,7 @@ func TestDataSync_TaskExecution(t *testing.T) {
// List after the rejected cancel - execution is unchanged, still SUCCESS.
rec = doRequest(t, h, "ListTaskExecutions", map[string]any{"TaskArn": taskArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
execs, ok := listResp["TaskExecutions"].([]any)
require.True(t, ok)
require.Len(t, execs, 1)
@@ -186,7 +187,7 @@ func TestDataSync_UpdateTaskExecution(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
execArn := startResp["TaskExecutionArn"].(string)
tests := []struct {
@@ -237,7 +238,7 @@ func TestDataSync_UpdateTaskExecution(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var startResp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &startResp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &startResp2, jsonx.V1()))
execArn2 := startResp2["TaskExecutionArn"].(string)
updRec := doRequest(t, h, "UpdateTaskExecution", map[string]any{
@@ -253,7 +254,7 @@ func TestDataSync_UpdateTaskExecution(t *testing.T) {
Options map[string]any `json:"Options"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.InDelta(t, float64(2097152), descResp.Options["BytesPerSecond"], 0)
for _, tc := range tests {
@@ -280,7 +281,7 @@ func TestDataSync_CancelTaskExecutionStatusChange(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
execArn := startResp["TaskExecutionArn"].(string)
// Cancel.
@@ -292,7 +293,7 @@ func TestDataSync_CancelTaskExecutionStatusChange(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
execs, ok := listResp["TaskExecutions"].([]any)
require.True(t, ok)
@@ -351,7 +352,7 @@ func TestDataSync_CancelTaskExecution_RejectsTerminal(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
execArn := startResp["TaskExecutionArn"].(string)
tt.settle(t, h, execArn)
@@ -365,7 +366,7 @@ func TestDataSync_CancelTaskExecution_RejectsTerminal(t *testing.T) {
rec = doRequest(t, h, "DescribeTaskExecution", map[string]any{"TaskExecutionArn": execArn})
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, tt.wantErr, descResp["Status"])
})
}
@@ -385,7 +386,7 @@ func TestDataSync_DescribeTaskExecutionLazyAdvance(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
execArn := startResp["TaskExecutionArn"].(string)
// Before describe: execution exists as LAUNCHING in list.
@@ -393,7 +394,7 @@ func TestDataSync_DescribeTaskExecutionLazyAdvance(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
execs := listResp["TaskExecutions"].([]any)
require.Len(t, execs, 1)
@@ -431,7 +432,7 @@ func TestDataSync_StartTaskExecutionRejectsConcurrent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
execArn := startResp["TaskExecutionArn"].(string)
// Second start while the first execution is still LAUNCHING must fail.
@@ -462,24 +463,24 @@ func TestDataSync_TaskStatusRunningWhileExecuting(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var taskResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &taskResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &taskResp, jsonx.V1()))
assert.Equal(t, "AVAILABLE", taskResp["Status"])
rec = doRequest(t, h, "StartTaskExecution", map[string]any{"TaskArn": taskArn})
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
execArn := startResp["TaskExecutionArn"].(string)
rec = doRequest(t, h, "DescribeTask", map[string]any{"TaskArn": taskArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &taskResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &taskResp, jsonx.V1()))
assert.Equal(t, "RUNNING", taskResp["Status"])
settleExecution(t, h, execArn)
rec = doRequest(t, h, "DescribeTask", map[string]any{"TaskArn": taskArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &taskResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &taskResp, jsonx.V1()))
assert.Equal(t, "AVAILABLE", taskResp["Status"])
}
@@ -522,14 +523,14 @@ func TestDataSync_TaskSettings(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
taskArn := createResp["TaskArn"].(string)
rec = doRequest(t, h, "DescribeTask", map[string]any{"TaskArn": taskArn})
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
opts, ok := descResp["Options"].(map[string]any)
require.True(t, ok)
@@ -571,7 +572,7 @@ func TestDataSync_TaskSettings(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
rec = doRequest(t, h, "DescribeTask", map[string]any{"TaskArn": taskArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "settings-task-renamed", descResp["Name"])
opts, ok = descResp["Options"].(map[string]any)
require.True(t, ok)
@@ -587,7 +588,7 @@ func TestDataSync_TaskSettings(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
rec = doRequest(t, h, "DescribeTask", map[string]any{"TaskArn": taskArn})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
excludes, ok = descResp["Excludes"].([]any)
require.True(t, ok)
require.Len(t, excludes, 1)
@@ -609,7 +610,7 @@ func TestDataSync_TaskModeDefaultsToBasic(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "BASIC", descResp["TaskMode"])
}
@@ -635,7 +636,7 @@ func TestDataSync_ListTaskExecutionsAllTasks(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
execs, ok := listResp["TaskExecutions"].([]any)
require.True(t, ok)
@@ -651,7 +652,7 @@ func settleExecution(t *testing.T, h *datasync.Handler, execArn string) map[stri
rec := doRequest(t, h, "DescribeTaskExecution", map[string]any{"TaskExecutionArn": execArn})
require.Equal(t, http.StatusOK, rec.Code)
out = map[string]any{}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if out["Status"] == "SUCCESS" {
break
@@ -670,7 +671,7 @@ func TestDataSync_TaskExecutionPhases(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var start map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &start))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &start, jsonx.V1()))
execArn := start["TaskExecutionArn"].(string)
got := make([]string, 0, 5)
@@ -678,7 +679,7 @@ func TestDataSync_TaskExecutionPhases(t *testing.T) {
for range 5 {
rec = doRequest(t, h, "DescribeTaskExecution", map[string]any{"TaskExecutionArn": execArn})
var d map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &d))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &d, jsonx.V1()))
got = append(got, d["Status"].(string))
}
diff --git a/services/datasync/handler_test.go b/services/datasync/handler_test.go
index 7a682d0371..79919b786a 100644
--- a/services/datasync/handler_test.go
+++ b/services/datasync/handler_test.go
@@ -2,7 +2,7 @@ package datasync_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/datasync"
)
@@ -30,7 +31,7 @@ func doRequest(t *testing.T, h *datasync.Handler, action string, body any) *http
if body != nil {
var marshalErr error
- bodyBytes, marshalErr = json.Marshal(body)
+ bodyBytes, marshalErr = json.Marshal(body, jsonx.V1())
require.NoError(t, marshalErr)
}
@@ -56,7 +57,7 @@ func createTestAgent(t *testing.T, h *datasync.Handler) string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, ok := resp["AgentArn"].(string)
require.True(t, ok)
@@ -77,7 +78,7 @@ func createTestLocationS3(t *testing.T, h *datasync.Handler) string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, ok := resp["LocationArn"].(string)
require.True(t, ok)
@@ -96,7 +97,7 @@ func createTestTask(t *testing.T, h *datasync.Handler, srcArn, dstArn string) st
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, ok := resp["TaskArn"].(string)
require.True(t, ok)
@@ -185,7 +186,7 @@ func TestDataSync_NotFoundReturnsInvalidRequest(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code, "expected 400 for %s with unknown ARN", tt.action)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", resp["__type"])
})
}
diff --git a/services/datasync/persistence.go b/services/datasync/persistence.go
index b946dab075..8ded002749 100644
--- a/services/datasync/persistence.go
+++ b/services/datasync/persistence.go
@@ -2,7 +2,7 @@ package datasync
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -31,7 +31,7 @@ const datasyncSnapshotVersion = 1
// incompatible (older or newer) build of this backend as though it were the
// current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
AccountID string `json:"accountID"`
Region string `json:"region"`
diff --git a/services/datasync/validation.go b/services/datasync/validation.go
index 6f3facf08e..d3d40a3fde 100644
--- a/services/datasync/validation.go
+++ b/services/datasync/validation.go
@@ -1,7 +1,7 @@
package datasync
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"slices"
@@ -9,6 +9,7 @@ import (
dstypes "github.com/aws/aws-sdk-go-v2/service/datasync/types"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -115,7 +116,7 @@ func describeNotFound(err error, body []byte) error {
}
var fields map[string]any
- if json.Unmarshal(body, &fields) != nil {
+ if json.Unmarshal(body, &fields, jsonx.V1()) != nil {
return err
}
diff --git a/services/datasync/validation_test.go b/services/datasync/validation_test.go
index 0783e48a3e..5278fda462 100644
--- a/services/datasync/validation_test.go
+++ b/services/datasync/validation_test.go
@@ -1,12 +1,14 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDataSync_RequestValidation(t *testing.T) {
@@ -86,7 +88,7 @@ func TestDataSync_RequestValidation(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", out["__type"])
assert.Contains(t, out["message"], tt.wantMsg)
assert.NotContains(t, out["message"], "InvalidRequestException:")
@@ -106,7 +108,7 @@ func TestDataSync_ResourceIDFormats(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var start map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &start))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &start, jsonx.V1()))
tests := []struct {
name string
diff --git a/services/datasync/wire_field_fixes_test.go b/services/datasync/wire_field_fixes_test.go
index 9ce0c91ea0..606391660f 100644
--- a/services/datasync/wire_field_fixes_test.go
+++ b/services/datasync/wire_field_fixes_test.go
@@ -1,7 +1,7 @@
package datasync_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/datasync"
)
@@ -169,7 +170,7 @@ func TestListLocations_NoFabricatedCreationTime(t *testing.T) {
var resp struct {
Locations []map[string]any `json:"Locations"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Locations, 1, "must exercise a non-empty collection")
_, hasCreationTime := resp.Locations[0]["CreationTime"]
diff --git a/services/dax/handler_clusters.go b/services/dax/handler_clusters.go
index 8829275065..2ef509884d 100644
--- a/services/dax/handler_clusters.go
+++ b/services/dax/handler_clusters.go
@@ -1,10 +1,11 @@
package dax
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createClusterRequest struct {
@@ -205,7 +206,7 @@ func toClusterResponse(c *Cluster) clusterResponse {
func (h *Handler) handleCreateCluster(body []byte) (any, error) {
var req createClusterRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -247,7 +248,7 @@ func (h *Handler) handleCreateCluster(body []byte) (any, error) {
func (h *Handler) handleDescribeClusters(body []byte) (any, error) {
var req describeClustersRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -279,7 +280,7 @@ func (h *Handler) handleDescribeClusters(body []byte) (any, error) {
func (h *Handler) handleUpdateCluster(body []byte) (any, error) {
var req updateClusterRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -295,7 +296,7 @@ func (h *Handler) handleUpdateCluster(body []byte) (any, error) {
func (h *Handler) handleDeleteCluster(body []byte) (any, error) {
var req deleteClusterRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -311,7 +312,7 @@ func (h *Handler) handleDeleteCluster(body []byte) (any, error) {
func (h *Handler) handleIncreaseReplicationFactor(body []byte) (any, error) {
var req increaseReplicationFactorRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -327,7 +328,7 @@ func (h *Handler) handleIncreaseReplicationFactor(body []byte) (any, error) {
func (h *Handler) handleDecreaseReplicationFactor(body []byte) (any, error) {
var req decreaseReplicationFactorRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -343,7 +344,7 @@ func (h *Handler) handleDecreaseReplicationFactor(body []byte) (any, error) {
func (h *Handler) handleRebootNode(body []byte) (any, error) {
var req rebootNodeRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/dax/handler_clusters_test.go b/services/dax/handler_clusters_test.go
index 3c42f2672d..aefd34ab8a 100644
--- a/services/dax/handler_clusters_test.go
+++ b/services/dax/handler_clusters_test.go
@@ -1,13 +1,14 @@
package dax_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dax"
)
@@ -81,7 +82,7 @@ func TestHandlerCreateCluster(t *testing.T) {
if tt.check != nil && rec.Code == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tt.check(t, resp)
}
})
@@ -104,7 +105,7 @@ func TestHandlerClusterResponseHasNoTagsField(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cluster := resp["Cluster"].(map[string]any)
_, hasTags := cluster["Tags"]
assert.False(t, hasTags, "Cluster response must not include a Tags field")
@@ -122,7 +123,7 @@ func TestHandlerCreateCluster_Duplicate(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec2.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ClusterAlreadyExistsFault", errResp["__type"])
}
@@ -164,7 +165,7 @@ func TestHandlerDescribeClusters(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusters := resp["Clusters"].([]any)
assert.Len(t, clusters, tt.wantCount)
})
@@ -237,7 +238,7 @@ func TestHandlerUpdateCluster(t *testing.T) {
if tt.check != nil && rec.Code == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tt.check(t, resp)
}
})
@@ -351,7 +352,7 @@ func TestHandlerReplicationFactor(t *testing.T) {
if tt.check != nil && rec.Code == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tt.check(t, resp)
}
})
@@ -407,7 +408,7 @@ func TestHandlerRebootNode(t *testing.T) {
if tt.check != nil && rec.Code == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tt.check(t, resp)
}
})
diff --git a/services/dax/handler_events.go b/services/dax/handler_events.go
index e4dc4b3067..9ee9600be7 100644
--- a/services/dax/handler_events.go
+++ b/services/dax/handler_events.go
@@ -1,21 +1,22 @@
package dax
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type describeEventsRequest struct {
- SourceName string `json:"SourceName"`
- SourceType string `json:"SourceType"`
- StartTime json.Number `json:"StartTime"`
- EndTime json.Number `json:"EndTime"`
- NextToken string `json:"NextToken"`
- MaxResults int `json:"MaxResults"`
- Duration int `json:"Duration"` // minutes to look back; applied when StartTime is absent
+ SourceName string `json:"SourceName"`
+ SourceType string `json:"SourceType"`
+ StartTime jsonx.Number `json:"StartTime"`
+ EndTime jsonx.Number `json:"EndTime"`
+ NextToken string `json:"NextToken"`
+ MaxResults int `json:"MaxResults"`
+ Duration int `json:"Duration"` // minutes to look back; applied when StartTime is absent
}
type eventResponse struct {
@@ -30,7 +31,7 @@ type eventResponse struct {
// awsjson1.1 Timestamp wire format DAX uses -- see smithytime.ParseEpochSeconds
// in the real SDK deserializer) into a time.Time, preserving sub-second
// precision carried in the fractional part.
-func parseEpochSeconds(n json.Number) (time.Time, error) {
+func parseEpochSeconds(n jsonx.Number) (time.Time, error) {
f, err := n.Float64()
if err != nil {
return time.Time{}, fmt.Errorf("%w: %w", errInvalidRequest, err)
@@ -45,7 +46,7 @@ func parseEpochSeconds(n json.Number) (time.Time, error) {
func (h *Handler) handleDescribeEvents(body []byte) (any, error) {
var req describeEventsRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
diff --git a/services/dax/handler_events_test.go b/services/dax/handler_events_test.go
index 3a5f996aa6..35e2395c2c 100644
--- a/services/dax/handler_events_test.go
+++ b/services/dax/handler_events_test.go
@@ -1,13 +1,14 @@
package dax_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dax"
)
@@ -61,7 +62,7 @@ func TestHandlerDescribeEvents(t *testing.T) {
if tt.check != nil {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tt.check(t, resp)
}
})
diff --git a/services/dax/handler_parameter_groups.go b/services/dax/handler_parameter_groups.go
index e71ffe49c4..5301481fe0 100644
--- a/services/dax/handler_parameter_groups.go
+++ b/services/dax/handler_parameter_groups.go
@@ -1,8 +1,10 @@
package dax
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createParameterGroupRequest struct {
@@ -81,7 +83,7 @@ func toParameterResponse(p *Parameter) parameterResponse {
func (h *Handler) handleCreateParameterGroup(body []byte) (any, error) {
var req createParameterGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -101,7 +103,7 @@ func (h *Handler) handleCreateParameterGroup(body []byte) (any, error) {
func (h *Handler) handleDescribeParameterGroups(body []byte) (any, error) {
var req describeParameterGroupsRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -136,7 +138,7 @@ func (h *Handler) handleDescribeParameterGroups(body []byte) (any, error) {
func (h *Handler) handleUpdateParameterGroup(body []byte) (any, error) {
var req updateParameterGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -169,7 +171,7 @@ func (h *Handler) handleUpdateParameterGroup(body []byte) (any, error) {
func (h *Handler) handleDeleteParameterGroup(body []byte) (any, error) {
var req deleteParameterGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -184,7 +186,7 @@ func (h *Handler) handleDeleteParameterGroup(body []byte) (any, error) {
func (h *Handler) handleDescribeParameters(body []byte) (any, error) {
var req describeParametersRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -217,7 +219,7 @@ func (h *Handler) handleDescribeParameters(body []byte) (any, error) {
func (h *Handler) handleDescribeDefaultParameters(body []byte) (any, error) {
var req describeDefaultParametersRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -245,7 +247,7 @@ func (h *Handler) handleDescribeDefaultParameters(body []byte) (any, error) {
func (h *Handler) handleResetParameterGroup(body []byte) (any, error) {
var req resetParameterGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/dax/handler_parameter_groups_test.go b/services/dax/handler_parameter_groups_test.go
index fb2cbd0ce4..ec2f21fc7e 100644
--- a/services/dax/handler_parameter_groups_test.go
+++ b/services/dax/handler_parameter_groups_test.go
@@ -1,13 +1,14 @@
package dax_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dax"
)
@@ -120,7 +121,7 @@ func TestHandlerParameterGroups(t *testing.T) {
if tt.check != nil && rec.Code == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tt.check(t, resp)
}
})
@@ -141,6 +142,6 @@ func TestHandlerUpdateParameterGroupRequiresParameterNameValues(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterValueException", errResp["__type"])
}
diff --git a/services/dax/handler_subnet_groups.go b/services/dax/handler_subnet_groups.go
index 329db1682e..6df9bf1fa3 100644
--- a/services/dax/handler_subnet_groups.go
+++ b/services/dax/handler_subnet_groups.go
@@ -1,8 +1,10 @@
package dax
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createSubnetGroupRequest struct {
@@ -66,7 +68,7 @@ func toSubnetGroupResponse(sg *SubnetGroup) subnetGroupResponse {
func (h *Handler) handleCreateSubnetGroup(body []byte) (any, error) {
var req createSubnetGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -83,7 +85,7 @@ func (h *Handler) handleCreateSubnetGroup(body []byte) (any, error) {
func (h *Handler) handleDescribeSubnetGroups(body []byte) (any, error) {
var req describeSubnetGroupsRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -115,7 +117,7 @@ func (h *Handler) handleDescribeSubnetGroups(body []byte) (any, error) {
func (h *Handler) handleUpdateSubnetGroup(body []byte) (any, error) {
var req updateSubnetGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -131,7 +133,7 @@ func (h *Handler) handleUpdateSubnetGroup(body []byte) (any, error) {
func (h *Handler) handleDeleteSubnetGroup(body []byte) (any, error) {
var req deleteSubnetGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/dax/handler_subnet_groups_test.go b/services/dax/handler_subnet_groups_test.go
index 741846be9e..7d0900e432 100644
--- a/services/dax/handler_subnet_groups_test.go
+++ b/services/dax/handler_subnet_groups_test.go
@@ -1,13 +1,14 @@
package dax_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dax"
)
@@ -104,7 +105,7 @@ func TestHandlerSubnetGroups(t *testing.T) {
if tt.check != nil && rec.Code == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tt.check(t, resp)
}
})
@@ -126,7 +127,7 @@ func TestHandlerSubnetGroupSubnetHasSupportedNetworkTypes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sg := resp["SubnetGroup"].(map[string]any)
subnets := sg["Subnets"].([]any)
require.Len(t, subnets, 1)
diff --git a/services/dax/handler_tags.go b/services/dax/handler_tags.go
index 6bb5336ea3..0e4f4cdb01 100644
--- a/services/dax/handler_tags.go
+++ b/services/dax/handler_tags.go
@@ -1,10 +1,12 @@
package dax
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
"unicode/utf8"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type tagResourceRequest struct {
@@ -24,7 +26,7 @@ type listTagsRequest struct {
func (h *Handler) handleTagResource(body []byte) (any, error) {
var req tagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -60,7 +62,7 @@ func (h *Handler) handleTagResource(body []byte) (any, error) {
func (h *Handler) handleUntagResource(body []byte) (any, error) {
var req untagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -87,7 +89,7 @@ func (h *Handler) handleUntagResource(body []byte) (any, error) {
func (h *Handler) handleListTags(body []byte) (any, error) {
var req listTagsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/dax/handler_tags_test.go b/services/dax/handler_tags_test.go
index 8ff61accca..f96af9c0bc 100644
--- a/services/dax/handler_tags_test.go
+++ b/services/dax/handler_tags_test.go
@@ -1,7 +1,7 @@
package dax_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dax"
)
@@ -29,7 +30,7 @@ func TestHandlerTagResource(t *testing.T) {
t.Helper()
rec := daxRequest(t, h, "CreateCluster", validClusterBody("tagged-cluster"))
var resp map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
return resp["Cluster"].(map[string]any)["ClusterArn"].(string)
},
@@ -72,14 +73,14 @@ func TestHandlerTagResourceRequiresTags(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
arn := createResp["Cluster"].(map[string]any)["ClusterArn"].(string)
rec := daxRequest(t, h, "TagResource", map[string]any{"ResourceName": arn})
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterValueException", errResp["__type"])
}
@@ -94,14 +95,14 @@ func TestHandlerUntagResourceRequiresTagKeys(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
arn := createResp["Cluster"].(map[string]any)["ClusterArn"].(string)
rec := daxRequest(t, h, "UntagResource", map[string]any{"ResourceName": arn})
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterValueException", errResp["__type"])
}
@@ -113,7 +114,7 @@ func TestHandlerListTags(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
clusterArn := createResp["Cluster"].(map[string]any)["ClusterArn"].(string)
tagRec := daxRequest(t, h, "TagResource", map[string]any{
@@ -126,7 +127,7 @@ func TestHandlerListTags(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags := listResp["Tags"].([]any)
assert.NotEmpty(t, tags)
}
@@ -204,7 +205,7 @@ func TestHandlerCreateClusterTagsAsArray(t *testing.T) {
if tt.wantStatus == http.StatusOK && tt.checkTag != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cluster := resp["Cluster"].(map[string]any)
// The Cluster response carries no Tags field (real AWS has none on
@@ -215,7 +216,7 @@ func TestHandlerCreateClusterTagsAsArray(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
rawTags := listResp["Tags"].([]any)
found := false
for _, rt := range rawTags {
@@ -287,7 +288,7 @@ func TestHandlerTagResourceValidation(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
arn := createResp["Cluster"].(map[string]any)["ClusterArn"].(string)
rec := daxRequest(t, h, "TagResource", map[string]any{
@@ -300,7 +301,7 @@ func TestHandlerTagResourceValidation(t *testing.T) {
} else {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantCode, errResp["__type"])
}
})
@@ -317,7 +318,7 @@ func TestHandlerTagResourceQuotaExceeded(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
arn := createResp["Cluster"].(map[string]any)["ClusterArn"].(string)
// Add 50 tags in two batches of 25.
@@ -345,7 +346,7 @@ func TestHandlerTagResourceQuotaExceeded(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "TagQuotaPerResourceExceeded", errResp["__type"])
}
@@ -359,7 +360,7 @@ func TestHandlerTagResourceReturnsAllTags(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
arn := createResp["Cluster"].(map[string]any)["ClusterArn"].(string)
// Add first tag.
@@ -377,7 +378,7 @@ func TestHandlerTagResourceReturnsAllTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
rawTags := resp["Tags"].([]any)
assert.Len(t, rawTags, 2, "TagResource must return all tags on the resource, not just added ones")
@@ -402,7 +403,7 @@ func TestHandlerUntagResourceReturnsRemainingTags(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
arn := createResp["Cluster"].(map[string]any)["ClusterArn"].(string)
tagRec := daxRequest(t, h, "TagResource", map[string]any{
@@ -418,7 +419,7 @@ func TestHandlerUntagResourceReturnsRemainingTags(t *testing.T) {
require.Equal(t, http.StatusOK, untagRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(untagRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(untagRec.Body.Bytes(), &resp, jsonx.V1()))
rawTags, ok := resp["Tags"].([]any)
require.True(t, ok, "UntagResource must return Tags in response")
diff --git a/services/dax/handler_test.go b/services/dax/handler_test.go
index 4fbde0373c..521e00da74 100644
--- a/services/dax/handler_test.go
+++ b/services/dax/handler_test.go
@@ -2,7 +2,8 @@ package dax_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dax"
)
@@ -26,7 +28,7 @@ func daxRequest(t *testing.T, h *dax.Handler, target string, body any) *httptest
var buf bytes.Buffer
if body != nil {
- require.NoError(t, json.NewEncoder(&buf).Encode(body))
+ require.NoError(t, json.MarshalEncode(jsontext.NewEncoder(&buf, jsonx.V1()), body))
}
req := httptest.NewRequest(http.MethodPost, "/", &buf)
@@ -163,7 +165,7 @@ func TestHandlerErrorMapping(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantCode, errResp["__type"])
})
}
@@ -183,7 +185,7 @@ func TestHandlerCreateSubnetGroupNameNotFormatValidated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sg := resp["SubnetGroup"].(map[string]any)
assert.Equal(t, "1sg--bad", sg["SubnetGroupName"])
}
@@ -201,7 +203,7 @@ func TestHandlerReset(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusters := resp["Clusters"].([]any)
assert.Empty(t, clusters)
}
diff --git a/services/dax/handler_wire_shape_test.go b/services/dax/handler_wire_shape_test.go
index c6014471a4..2a8b6583bd 100644
--- a/services/dax/handler_wire_shape_test.go
+++ b/services/dax/handler_wire_shape_test.go
@@ -1,7 +1,7 @@
package dax_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dax"
)
@@ -25,7 +26,7 @@ func TestHandlerClusterStatusWireKey(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cluster := resp["Cluster"].(map[string]any)
assert.Equal(t, "creating", cluster["Status"], "wire key must be Status")
@@ -48,7 +49,7 @@ func TestHandlerTimestampsAreEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
cluster := createResp["Cluster"].(map[string]any)
nodes := cluster["Nodes"].([]any)
@@ -63,7 +64,7 @@ func TestHandlerTimestampsAreEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, eventsRec.Code)
var eventsResp map[string]any
- require.NoError(t, json.Unmarshal(eventsRec.Body.Bytes(), &eventsResp))
+ require.NoError(t, json.Unmarshal(eventsRec.Body.Bytes(), &eventsResp, jsonx.V1()))
events := eventsResp["Events"].([]any)
require.NotEmpty(t, events)
@@ -93,7 +94,7 @@ func TestHandlerDescribeEventsAcceptsEpochSecondsRequest(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["Events"], "a StartTime in the future should exclude all existing events")
past := float64(time.Now().UTC().Add(-time.Hour).Unix())
@@ -104,7 +105,7 @@ func TestHandlerDescribeEventsAcceptsEpochSecondsRequest(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.NotEmpty(t, resp2["Events"], "a StartTime in the past should include existing events")
}
@@ -155,7 +156,7 @@ func TestHandlerParameterGroupNodeIdsToRebootWireKey(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusters := resp["Clusters"].([]any)
require.Len(t, clusters, 1)
@@ -185,7 +186,7 @@ func TestHandlerClusterNetworkTypeWireKey(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cluster := resp["Cluster"].(map[string]any)
assert.Equal(t, "ipv4", cluster["NetworkType"])
@@ -211,7 +212,7 @@ func TestHandlerDecreaseReplicationFactorNodeIdsToRemoveWireKey(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cluster := resp["Cluster"].(map[string]any)
nodeIDs, ok := cluster["NodeIdsToRemove"].([]any)
@@ -233,7 +234,7 @@ func TestHandlerSubnetGroupSupportedNetworkTypesWireKey(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sg := resp["SubnetGroup"].(map[string]any)
types, ok := sg["SupportedNetworkTypes"].([]any)
@@ -263,7 +264,7 @@ func TestHandlerDescribeParametersSourceFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
params := resp["Parameters"].([]any)
require.Len(t, params, 1)
diff --git a/services/dax/multi_region_test.go b/services/dax/multi_region_test.go
index 98cc37b4ca..1bf2150652 100644
--- a/services/dax/multi_region_test.go
+++ b/services/dax/multi_region_test.go
@@ -2,7 +2,7 @@ package dax_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dax"
)
@@ -41,7 +42,7 @@ func decode(t *testing.T, body string) map[string]any {
out := map[string]any{}
if body != "" {
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
}
return out
diff --git a/services/dax/persistence.go b/services/dax/persistence.go
index 4dfb239655..133a074a9e 100644
--- a/services/dax/persistence.go
+++ b/services/dax/persistence.go
@@ -2,11 +2,13 @@ package dax
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -37,7 +39,7 @@ const daxSnapshotVersion = 1
// (older or newer) build of this backend as though it were the current
// shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
AccountID string `json:"accountID"`
Region string `json:"region"`
@@ -124,7 +126,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
b.mu.RLock("Snapshot")
defer b.mu.RUnlock()
- var tables map[string]json.RawMessage
+ var tables map[string]jsontext.Value
tables, tblErr = b.registry.SnapshotAll()
@@ -144,7 +146,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
return nil
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).ErrorContext(ctx, "dax: failed to marshal snapshot", "error", err)
diff --git a/services/detective/handler_administrator.go b/services/detective/handler_administrator.go
index a06fa50f95..0b8faa40b8 100644
--- a/services/detective/handler_administrator.go
+++ b/services/detective/handler_administrator.go
@@ -1,12 +1,13 @@
package detective
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleStartMonitoringMember(c *echo.Context) error {
@@ -20,7 +21,7 @@ func (h *Handler) handleStartMonitoringMember(c *echo.Context) error {
AccountId string `json:"AccountId"` //nolint:revive,staticcheck // existing issue.
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -57,7 +58,7 @@ func (h *Handler) handleEnableOrganizationAdminAccount(c *echo.Context) error {
AccountId string `json:"AccountId"` //nolint:revive,staticcheck // existing issue.
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -84,7 +85,7 @@ func (h *Handler) handleListOrganizationAdminAccounts(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
}
diff --git a/services/detective/handler_administrator_test.go b/services/detective/handler_administrator_test.go
index 82d7560682..20b5725c1c 100644
--- a/services/detective/handler_administrator_test.go
+++ b/services/detective/handler_administrator_test.go
@@ -1,12 +1,14 @@
package detective_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDetective_StartMonitoringMember(t *testing.T) { //nolint:paralleltest // existing issue.
@@ -17,7 +19,7 @@ func TestDetective_StartMonitoringMember(t *testing.T) { //nolint:paralleltest /
require.Equal(t, http.StatusOK, rec.Code)
var graphResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &graphResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &graphResp, jsonx.V1()))
graphArn := graphResp["GraphArn"].(string)
// Add a member (status INVITED, not ACCEPTED_BUT_DISABLED).
@@ -98,7 +100,7 @@ func TestOrganizationAdminAccounts(t *testing.T) { //nolint:paralleltest // exis
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
admins, ok := resp["Administrators"].([]any)
require.True(t, ok)
assert.Empty(t, admins)
@@ -134,7 +136,7 @@ func TestOrganizationAdminAccounts(t *testing.T) { //nolint:paralleltest // exis
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
admins, ok := resp["Administrators"].([]any)
require.True(t, ok)
require.Len(t, admins, 1)
@@ -158,7 +160,7 @@ func TestOrganizationAdminAccounts(t *testing.T) { //nolint:paralleltest // exis
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
admins, ok := resp["Administrators"].([]any)
require.True(t, ok)
assert.Empty(t, admins)
diff --git a/services/detective/handler_datasource_packages.go b/services/detective/handler_datasource_packages.go
index 5ae981c4c4..89ec997576 100644
--- a/services/detective/handler_datasource_packages.go
+++ b/services/detective/handler_datasource_packages.go
@@ -1,13 +1,14 @@
package detective
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ingestHistoryToJSON encodes a MembershipDatasources.IngestHistory map to
@@ -45,7 +46,7 @@ func (h *Handler) handleBatchGetGraphMemberDatasources(c *echo.Context) error {
AccountIds []string `json:"AccountIds"` //nolint:revive // existing issue.
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -83,7 +84,7 @@ func (h *Handler) handleBatchGetMembershipDatasources(c *echo.Context) error {
GraphArns []string `json:"GraphArns"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -127,7 +128,7 @@ func (h *Handler) handleListDatasourcePackages(c *echo.Context) error {
MaxResults int32 `json:"MaxResults"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -169,7 +170,7 @@ func (h *Handler) handleUpdateDatasourcePackages(c *echo.Context) error {
DatasourcePackages []string `json:"DatasourcePackages"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
diff --git a/services/detective/handler_datasource_packages_test.go b/services/detective/handler_datasource_packages_test.go
index 25f581746b..7982869ca7 100644
--- a/services/detective/handler_datasource_packages_test.go
+++ b/services/detective/handler_datasource_packages_test.go
@@ -2,12 +2,14 @@ package detective_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDetective_Datasources(t *testing.T) { //nolint:paralleltest // existing issue.
@@ -18,7 +20,7 @@ func TestDetective_Datasources(t *testing.T) { //nolint:paralleltest // existing
require.Equal(t, http.StatusOK, rec.Code)
var graphResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &graphResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &graphResp, jsonx.V1()))
graphArn := graphResp["GraphArn"].(string)
// Add a member.
@@ -46,7 +48,7 @@ func TestDetective_Datasources(t *testing.T) { //nolint:paralleltest // existing
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
pkgs, ok := resp["DatasourcePackages"].(map[string]any)
require.True(t, ok)
assert.Contains(t, pkgs, "DETECTIVE_CORE")
@@ -69,7 +71,7 @@ func TestDetective_Datasources(t *testing.T) { //nolint:paralleltest // existing
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
pkgs, ok := resp["DatasourcePackages"].(map[string]any)
require.True(t, ok)
assert.Contains(t, pkgs, "DETECTIVE_CORE")
@@ -84,7 +86,7 @@ func TestDetective_Datasources(t *testing.T) { //nolint:paralleltest // existing
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
members, ok := resp["MemberDatasources"].([]any)
require.True(t, ok)
assert.Len(t, members, 1)
@@ -99,7 +101,7 @@ func TestDetective_Datasources(t *testing.T) { //nolint:paralleltest // existing
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
unprocessed, ok := resp["UnprocessedAccounts"].([]any)
require.True(t, ok)
assert.Len(t, unprocessed, 1)
@@ -114,7 +116,7 @@ func TestDetective_Datasources(t *testing.T) { //nolint:paralleltest // existing
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
members, ok := resp["MembershipDatasources"].([]any)
require.True(t, ok)
assert.Len(t, members, 1)
@@ -133,7 +135,7 @@ func TestDetective_Datasources(t *testing.T) { //nolint:paralleltest // existing
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
unprocessed, ok := resp["UnprocessedGraphs"].([]any)
require.True(t, ok)
assert.Len(t, unprocessed, 1)
@@ -195,7 +197,7 @@ func TestUpdateDatasourcePackages_InvalidPackageRejected(t *testing.T) {
rec := doRequest(t, h, http.MethodPost, "/graph", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
var gResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gResp, jsonx.V1()))
graphARN := gResp["GraphArn"].(string)
rec2 := doRequest(t, h, http.MethodPost, "/graph/datasources/update", map[string]any{
@@ -223,7 +225,7 @@ func TestBatchGetGraphMemberDatasources_WireShape(t *testing.T) {
rec := doRequest(t, h, http.MethodPost, "/graph", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
var gResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gResp, jsonx.V1()))
graphARN := gResp["GraphArn"].(string)
memberRec := doRequest(t, h, http.MethodPost, "/graph/members", map[string]any{
@@ -247,7 +249,7 @@ func TestBatchGetGraphMemberDatasources_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
members, ok := resp["MemberDatasources"].([]any)
require.True(t, ok)
require.Len(t, members, 1)
@@ -280,7 +282,7 @@ func TestListDatasourcePackagesOpaqueToken(t *testing.T) {
rec := doRequest(t, h, http.MethodPost, "/graph", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
var gResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gResp, jsonx.V1()))
graphARN := gResp["GraphArn"].(string)
updateRec := doRequest(t, h, http.MethodPost, "/graph/datasources/update", map[string]any{
@@ -295,7 +297,7 @@ func TestListDatasourcePackagesOpaqueToken(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
tok, hasTok := resp["NextToken"].(string)
require.True(t, hasTok, "NextToken must be present when more results exist")
@@ -310,7 +312,7 @@ func TestListDatasourcePackagesOpaqueToken(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec3.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp2, jsonx.V1()))
_, hasTok2 := resp2["NextToken"]
assert.False(t, hasTok2, "NextToken must be absent on the last page")
}
diff --git a/services/detective/handler_graphs.go b/services/detective/handler_graphs.go
index 3aed84dce6..6079dfdfe4 100644
--- a/services/detective/handler_graphs.go
+++ b/services/detective/handler_graphs.go
@@ -1,12 +1,13 @@
package detective
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateGraph(c *echo.Context) error {
@@ -20,7 +21,7 @@ func (h *Handler) handleCreateGraph(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
}
@@ -45,7 +46,7 @@ func (h *Handler) handleDeleteGraph(c *echo.Context) error {
GraphArn string `json:"GraphArn"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -72,7 +73,7 @@ func (h *Handler) handleListGraphs(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
}
diff --git a/services/detective/handler_graphs_test.go b/services/detective/handler_graphs_test.go
index 94749787c4..568002faee 100644
--- a/services/detective/handler_graphs_test.go
+++ b/services/detective/handler_graphs_test.go
@@ -2,7 +2,7 @@ package detective_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"regexp"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/detective"
)
@@ -32,7 +33,7 @@ func TestDetective_Graph(t *testing.T) { //nolint:paralleltest // existing issue
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
assert.Contains(t, resp["GraphArn"], "arn:aws:detective:")
},
},
@@ -48,7 +49,7 @@ func TestDetective_Graph(t *testing.T) { //nolint:paralleltest // existing issue
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
assert.Contains(t, resp["GraphArn"], "arn:aws:detective:")
},
},
@@ -86,7 +87,7 @@ func TestDetective_Graph(t *testing.T) { //nolint:paralleltest // existing issue
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
list, ok := resp["GraphList"].([]any)
require.True(t, ok)
assert.Empty(t, list)
@@ -104,7 +105,7 @@ func TestDetective_Graph(t *testing.T) { //nolint:paralleltest // existing issue
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
list, ok := resp["GraphList"].([]any)
require.True(t, ok)
assert.Len(t, list, 1)
@@ -125,7 +126,7 @@ func TestDetective_Graph(t *testing.T) { //nolint:paralleltest // existing issue
// get graphArn from listing
listRec := doRequest(t, h, http.MethodPost, "/graphs/list", map[string]any{})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
list := listResp["GraphList"].([]any)
if len(list) > 0 {
graph := list[0].(map[string]any)
@@ -158,7 +159,7 @@ func TestGraphARN_Shape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ := resp["GraphArn"].(string)
require.NotEmpty(t, arn, "CreateGraph must return a GraphArn")
@@ -187,7 +188,7 @@ func TestListGraphs_CreatedTime_Present(t *testing.T) {
} `json:"GraphList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.GraphList, 1)
assert.NotEmpty(t, resp.GraphList[0].CreatedTime, "ListGraphs must include CreatedTime")
assert.Contains(t, resp.GraphList[0].CreatedTime, "T",
@@ -209,7 +210,7 @@ func TestListGraphs_Pagination_NextToken_Absent_When_All_Fit(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasToken := resp["NextToken"]
assert.False(t, hasToken, "NextToken must be absent when all results fit on one page")
}
diff --git a/services/detective/handler_investigations.go b/services/detective/handler_investigations.go
index 0d6eea760f..3146a8654c 100644
--- a/services/detective/handler_investigations.go
+++ b/services/detective/handler_investigations.go
@@ -1,12 +1,13 @@
package detective
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleStartInvestigation(c *echo.Context) error {
@@ -22,7 +23,7 @@ func (h *Handler) handleStartInvestigation(c *echo.Context) error {
ScopeEndTime string `json:"ScopeEndTime"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -73,7 +74,7 @@ func (h *Handler) handleGetInvestigation(c *echo.Context) error {
InvestigationId string `json:"InvestigationId"` //nolint:revive,staticcheck // existing issue.
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -130,7 +131,7 @@ func (h *Handler) handleListInvestigations(c *echo.Context) error {
MaxResults int32 `json:"MaxResults"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -203,7 +204,7 @@ func (h *Handler) handleUpdateInvestigationState(c *echo.Context) error {
State string `json:"State"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -240,7 +241,7 @@ func (h *Handler) handleListIndicators(c *echo.Context) error {
MaxResults int32 `json:"MaxResults"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
diff --git a/services/detective/handler_investigations_test.go b/services/detective/handler_investigations_test.go
index 32ac23e60c..6721542c04 100644
--- a/services/detective/handler_investigations_test.go
+++ b/services/detective/handler_investigations_test.go
@@ -2,12 +2,14 @@ package detective_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDetective_Investigations(t *testing.T) { //nolint:paralleltest // existing issue.
@@ -18,7 +20,7 @@ func TestDetective_Investigations(t *testing.T) { //nolint:paralleltest // exist
require.Equal(t, http.StatusOK, rec.Code)
var graphResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &graphResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &graphResp, jsonx.V1()))
graphArn := graphResp["GraphArn"].(string)
unknownArn := missingGraphARN
@@ -79,7 +81,7 @@ func TestDetective_Investigations(t *testing.T) { //nolint:paralleltest // exist
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["InvestigationId"])
},
},
@@ -92,7 +94,7 @@ func TestDetective_Investigations(t *testing.T) { //nolint:paralleltest // exist
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
details, ok := resp["InvestigationDetails"].([]any)
require.True(t, ok)
assert.Len(t, details, 1)
@@ -183,7 +185,7 @@ func TestDetective_InvestigationGetAndUpdate(t *testing.T) { //nolint:parallelte
require.Equal(t, http.StatusOK, rec.Code)
var graphResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &graphResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &graphResp, jsonx.V1()))
graphArn := graphResp["GraphArn"].(string)
// Start investigation.
@@ -196,7 +198,7 @@ func TestDetective_InvestigationGetAndUpdate(t *testing.T) { //nolint:parallelte
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
invID := startResp["InvestigationId"].(string)
require.NotEmpty(t, invID)
@@ -217,7 +219,7 @@ func TestDetective_InvestigationGetAndUpdate(t *testing.T) { //nolint:parallelte
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, invID, resp["InvestigationId"])
assert.Equal(t, graphArn, resp["GraphArn"])
assert.Equal(t, "arn:aws:iam::000000000000:user/alice", resp["EntityArn"])
@@ -241,7 +243,7 @@ func TestDetective_InvestigationGetAndUpdate(t *testing.T) { //nolint:parallelte
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "ARCHIVED", resp["State"])
},
},
@@ -254,7 +256,7 @@ func TestDetective_InvestigationGetAndUpdate(t *testing.T) { //nolint:parallelte
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
indicators, ok := resp["Indicators"].([]any)
require.True(t, ok)
assert.NotEmpty(t, indicators, "ListIndicators should return indicators for an investigation")
diff --git a/services/detective/handler_members.go b/services/detective/handler_members.go
index 56dc17a52e..3d43acc8ec 100644
--- a/services/detective/handler_members.go
+++ b/services/detective/handler_members.go
@@ -1,12 +1,13 @@
package detective
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateMembers(c *echo.Context) error {
@@ -24,7 +25,7 @@ func (h *Handler) handleCreateMembers(c *echo.Context) error {
} `json:"Accounts"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -62,7 +63,7 @@ func (h *Handler) handleDeleteMembers(c *echo.Context) error {
AccountIDs []string `json:"AccountIds"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -92,7 +93,7 @@ func (h *Handler) handleGetMembers(c *echo.Context) error {
AccountIDs []string `json:"AccountIds"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -123,7 +124,7 @@ func (h *Handler) handleListMembers(c *echo.Context) error {
MaxResults int32 `json:"MaxResults"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -156,7 +157,7 @@ func (h *Handler) handleAcceptInvitation(c *echo.Context) error {
GraphArn string `json:"GraphArn"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -181,7 +182,7 @@ func (h *Handler) handleRejectInvitation(c *echo.Context) error {
GraphArn string `json:"GraphArn"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -208,7 +209,7 @@ func (h *Handler) handleListInvitations(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
}
@@ -238,7 +239,7 @@ func (h *Handler) handleDisassociateMembership(c *echo.Context) error {
GraphArn string `json:"GraphArn"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
diff --git a/services/detective/handler_members_test.go b/services/detective/handler_members_test.go
index c9cae3d32a..41085827d9 100644
--- a/services/detective/handler_members_test.go
+++ b/services/detective/handler_members_test.go
@@ -2,13 +2,15 @@ package detective_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDetective_Members(t *testing.T) { //nolint:paralleltest // existing issue.
@@ -96,7 +98,7 @@ func TestDetective_MembersLifecycle(t *testing.T) { //nolint:paralleltest // exi
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
graphArn := createResp["GraphArn"].(string)
require.NotEmpty(t, graphArn)
@@ -111,7 +113,7 @@ func TestDetective_MembersLifecycle(t *testing.T) { //nolint:paralleltest // exi
require.Equal(t, http.StatusOK, rec.Code)
var createMembersResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createMembersResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createMembersResp, jsonx.V1()))
members := createMembersResp["Members"].([]any)
assert.Len(t, members, 2)
@@ -122,7 +124,7 @@ func TestDetective_MembersLifecycle(t *testing.T) { //nolint:paralleltest // exi
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
memberDetails := listResp["MemberDetails"].([]any)
assert.Len(t, memberDetails, 2)
@@ -134,7 +136,7 @@ func TestDetective_MembersLifecycle(t *testing.T) { //nolint:paralleltest // exi
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
gotMembers := getResp["MemberDetails"].([]any)
assert.Len(t, gotMembers, 1)
unprocessed := getResp["UnprocessedAccounts"].([]any)
@@ -148,7 +150,7 @@ func TestDetective_MembersLifecycle(t *testing.T) { //nolint:paralleltest // exi
require.Equal(t, http.StatusOK, rec.Code)
var deleteResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &deleteResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &deleteResp, jsonx.V1()))
deleted := deleteResp["AccountIds"].([]any)
assert.Len(t, deleted, 1)
@@ -158,7 +160,7 @@ func TestDetective_MembersLifecycle(t *testing.T) { //nolint:paralleltest // exi
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
memberDetails = listResp["MemberDetails"].([]any)
assert.Len(t, memberDetails, 1)
}
@@ -284,7 +286,7 @@ func TestListMembers_Pagination_NextToken_Present(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
graphArn := createResp["GraphArn"].(string)
accounts := make([]any, 3)
@@ -308,7 +310,7 @@ func TestListMembers_Pagination_NextToken_Present(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page1, jsonx.V1()))
memberList1, _ := page1["MemberDetails"].([]any)
assert.Len(t, memberList1, 2, "MaxResults=2 must return exactly 2 members")
nextToken, hasToken := page1["NextToken"].(string)
@@ -321,7 +323,7 @@ func TestListMembers_Pagination_NextToken_Present(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var all map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &all))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &all, jsonx.V1()))
_, hasAllToken := all["NextToken"]
assert.False(t, hasAllToken, "NextToken must be absent when all members fit")
}
@@ -339,7 +341,7 @@ func TestMemberDetail_MasterId_Equals_AdministratorId(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
graphArn := createResp["GraphArn"].(string)
rec2 := doRequest(t, h, http.MethodPost, "/graph/members", map[string]any{
@@ -351,7 +353,7 @@ func TestMemberDetail_MasterId_Equals_AdministratorId(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var membersResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &membersResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &membersResp, jsonx.V1()))
members, _ := membersResp["Members"].([]any)
require.Len(t, members, 1)
@@ -376,7 +378,7 @@ func TestMemberDetail_AllRequiredFields_Present(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
graphArn := createResp["GraphArn"].(string)
rec2 := doRequest(t, h, http.MethodPost, "/graph/members", map[string]any{
@@ -388,7 +390,7 @@ func TestMemberDetail_AllRequiredFields_Present(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var membersResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &membersResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &membersResp, jsonx.V1()))
members, _ := membersResp["Members"].([]any)
require.Len(t, members, 1)
@@ -424,7 +426,7 @@ func TestMemberDetail_DatasourcePackageIngestStates_MirrorsGraph(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
graphArn := createResp["GraphArn"].(string)
rec2 := doRequest(t, h, http.MethodPost, "/graph/members", map[string]any{
@@ -448,7 +450,7 @@ func TestMemberDetail_DatasourcePackageIngestStates_MirrorsGraph(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
details, _ := getResp["MemberDetails"].([]any)
require.Len(t, details, 1)
@@ -471,7 +473,7 @@ func TestDeleteMembers_AllUnprocessed_AccountIds_Empty_Not_Null(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
graphArn := createResp["GraphArn"].(string)
rec2 := doRequest(t, h, http.MethodPost, "/graph/members/removal", map[string]any{
@@ -481,7 +483,7 @@ func TestDeleteMembers_AllUnprocessed_AccountIds_Empty_Not_Null(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
accountIDs, hasKey := resp["AccountIds"]
assert.True(t, hasKey, "DeleteMembers response must include AccountIds")
@@ -510,7 +512,7 @@ func TestCreateMembers_UnprocessedAccounts_Empty_Not_Null(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
graphArn := createResp["GraphArn"].(string)
rec2 := doRequest(t, h, http.MethodPost, "/graph/members", map[string]any{
@@ -522,7 +524,7 @@ func TestCreateMembers_UnprocessedAccounts_Empty_Not_Null(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
unprocessed, hasKey := resp["UnprocessedAccounts"]
assert.True(t, hasKey, "CreateMembers must always include UnprocessedAccounts")
@@ -591,7 +593,7 @@ func TestDetective_Invitations(t *testing.T) { //nolint:paralleltest // existing
require.Equal(t, http.StatusOK, rec.Code)
var graphResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &graphResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &graphResp, jsonx.V1()))
graphArn := graphResp["GraphArn"].(string)
// Invite account 000000000000 (the backend's own account) will fail; use another.
@@ -621,7 +623,7 @@ func TestDetective_Invitations(t *testing.T) { //nolint:paralleltest // existing
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
invitations, ok := resp["Invitations"].([]any)
require.True(t, ok)
assert.Empty(t, invitations)
@@ -700,7 +702,7 @@ func TestDetective_InvitationLifecycle(t *testing.T) { //nolint:paralleltest //
require.Equal(t, http.StatusOK, rec.Code)
var graphResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &graphResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &graphResp, jsonx.V1()))
graphArn := graphResp["GraphArn"].(string)
// Invite the backend's own account ID so it can accept/reject via the same backend.
@@ -724,7 +726,7 @@ func TestDetective_InvitationLifecycle(t *testing.T) { //nolint:paralleltest //
require.Equal(t, http.StatusOK, rec3.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &getResp, jsonx.V1()))
details := getResp["MemberDetails"].([]any)
require.Len(t, details, 1)
member := details[0].(map[string]any)
diff --git a/services/detective/handler_organization.go b/services/detective/handler_organization.go
index 34a8e9249c..f68afd99c8 100644
--- a/services/detective/handler_organization.go
+++ b/services/detective/handler_organization.go
@@ -1,12 +1,13 @@
package detective
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDescribeOrganizationConfiguration(c *echo.Context) error {
@@ -19,7 +20,7 @@ func (h *Handler) handleDescribeOrganizationConfiguration(c *echo.Context) error
GraphArn string `json:"GraphArn"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -48,7 +49,7 @@ func (h *Handler) handleUpdateOrganizationConfiguration(c *echo.Context) error {
AutoEnable bool `json:"AutoEnable"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
diff --git a/services/detective/handler_organization_test.go b/services/detective/handler_organization_test.go
index 73a2510b62..f8c1b138b4 100644
--- a/services/detective/handler_organization_test.go
+++ b/services/detective/handler_organization_test.go
@@ -1,12 +1,14 @@
package detective_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestOrganizationConfiguration covers the DescribeOrganizationConfiguration /
@@ -22,7 +24,7 @@ func TestOrganizationConfiguration(t *testing.T) { //nolint:paralleltest // exis
require.Equal(t, http.StatusOK, rec.Code)
var graphResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &graphResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &graphResp, jsonx.V1()))
graphArn := graphResp["GraphArn"].(string)
unknownArn := missingGraphARN
@@ -58,7 +60,7 @@ func TestOrganizationConfiguration(t *testing.T) { //nolint:paralleltest // exis
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, false, resp["AutoEnable"])
},
},
@@ -92,7 +94,7 @@ func TestOrganizationConfiguration(t *testing.T) { //nolint:paralleltest // exis
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, true, resp["AutoEnable"])
},
},
diff --git a/services/detective/handler_tags.go b/services/detective/handler_tags.go
index 519704798f..7eb5b4874f 100644
--- a/services/detective/handler_tags.go
+++ b/services/detective/handler_tags.go
@@ -1,13 +1,14 @@
package detective
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleTagResource(c *echo.Context) error {
@@ -25,7 +26,7 @@ func (h *Handler) handleTagResource(c *echo.Context) error {
Tags map[string]string `json:"Tags"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
diff --git a/services/detective/handler_tags_test.go b/services/detective/handler_tags_test.go
index 7703213b9e..977e37b52f 100644
--- a/services/detective/handler_tags_test.go
+++ b/services/detective/handler_tags_test.go
@@ -1,7 +1,7 @@
package detective_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/detective"
)
@@ -20,7 +21,7 @@ func TestDetective_Tags(t *testing.T) { //nolint:paralleltest // existing issue.
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
graphArn := createResp["GraphArn"].(string)
tests := []struct {
@@ -39,7 +40,7 @@ func TestDetective_Tags(t *testing.T) { //nolint:paralleltest // existing issue.
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
tags, ok := resp["Tags"].(map[string]any)
require.True(t, ok)
assert.Empty(t, tags)
@@ -67,7 +68,7 @@ func TestDetective_Tags(t *testing.T) { //nolint:paralleltest // existing issue.
tagRec := doRequest(t, h, http.MethodGet, "/tags/"+graphArn, nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(tagRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(tagRec.Body.Bytes(), &resp, jsonx.V1()))
tags := resp["Tags"].(map[string]any)
assert.Equal(t, "myval", tags["mykey"])
},
@@ -237,7 +238,7 @@ func TestTagResource_EmptyTags_Succeeds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
graphArn := createResp["GraphArn"].(string)
rec2 := doRequest(t, h, http.MethodPost, "/tags/"+graphArn, map[string]any{
@@ -259,7 +260,7 @@ func TestUntagResource_NonExistentKey_Succeeds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
graphArn := createResp["GraphArn"].(string)
req := doRequest(t, h, http.MethodDelete, "/tags/"+graphArn+"?tagKeys=does-not-exist", nil)
@@ -279,14 +280,14 @@ func TestListTagsForResource_NoTags_Returns_EmptyObject(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
graphArn := createResp["GraphArn"].(string)
rec2 := doRequest(t, h, http.MethodGet, "/tags/"+graphArn, nil)
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
tags, hasKey := resp["Tags"]
assert.True(t, hasKey, "ListTagsForResource must include Tags field")
@@ -309,14 +310,14 @@ func TestCreateGraph_Tags_VisibleInListTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
graphArn := createResp["GraphArn"].(string)
rec2 := doRequest(t, h, http.MethodGet, "/tags/"+graphArn, nil)
require.Equal(t, http.StatusOK, rec2.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagsResp, jsonx.V1()))
tags, _ := tagsResp["Tags"].(map[string]any)
assert.Equal(t, "prod", tags["env"], "tags set at graph creation must be visible via ListTagsForResource")
diff --git a/services/detective/handler_test.go b/services/detective/handler_test.go
index c6cab5fab4..9f7ca6a986 100644
--- a/services/detective/handler_test.go
+++ b/services/detective/handler_test.go
@@ -2,7 +2,7 @@ package detective_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/detective"
)
@@ -33,7 +34,7 @@ func doRequest(t *testing.T, h *detective.Handler, method, path string, body any
if body != nil {
var marshalErr error
- bodyBytes, marshalErr = json.Marshal(body)
+ bodyBytes, marshalErr = json.Marshal(body, jsonx.V1())
require.NoError(t, marshalErr)
}
@@ -53,7 +54,7 @@ func doRequest(t *testing.T, h *detective.Handler, method, path string, body any
func parseJSON(t *testing.T, data []byte, v any) {
t.Helper()
- require.NoError(t, json.Unmarshal(data, v))
+ require.NoError(t, json.Unmarshal(data, v, jsonx.V1()))
}
// TestDetectiveHandler_RouteMatcher verifies the RouteMatcher against every
@@ -254,7 +255,7 @@ func TestDetective_UnknownOperation_Returns400(t *testing.T) {
assert.Equal(t, tc.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidInputException", resp["__type"])
})
}
diff --git a/services/detective/persistence.go b/services/detective/persistence.go
index 8a95e88bc7..668d507b29 100644
--- a/services/detective/persistence.go
+++ b/services/detective/persistence.go
@@ -2,7 +2,7 @@ package detective
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -31,7 +31,7 @@ const detectiveSnapshotVersion = 1
// order-sensitive slice; all four are carried directly, matching the
// pre-Phase-3.3 snapshot shape for those fields.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
Datasources map[string]map[string]string `json:"datasources"`
// DatasourceChangedAt is additive (gopherstack-c902): graphARN -> package
diff --git a/services/detective/whitebox_test.go b/services/detective/whitebox_test.go
index e357f23e18..ac0641862d 100644
--- a/services/detective/whitebox_test.go
+++ b/services/detective/whitebox_test.go
@@ -3,7 +3,7 @@ package detective
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func doWhiteboxRequest(t *testing.T, h *Handler, method, path string, body any) *httptest.ResponseRecorder {
@@ -22,7 +24,7 @@ func doWhiteboxRequest(t *testing.T, h *Handler, method, path string, body any)
if body != nil {
var marshalErr error
- bodyBytes, marshalErr = json.Marshal(body)
+ bodyBytes, marshalErr = json.Marshal(body, jsonx.V1())
require.NoError(t, marshalErr)
}
@@ -87,7 +89,7 @@ func TestListInvitationsOpaqueToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tok, hasTok := resp["NextToken"].(string)
require.True(t, hasTok, "NextToken must be present when more results exist")
@@ -105,7 +107,7 @@ func TestListInvitationsOpaqueToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
invitations, ok := resp2["Invitations"].([]any)
require.True(t, ok)
assert.Len(t, invitations, 1, "second page must return the remaining invitation")
diff --git a/services/directconnect/cloudformation.go b/services/directconnect/cloudformation.go
index 92630c6d47..f0a3f4a10a 100644
--- a/services/directconnect/cloudformation.go
+++ b/services/directconnect/cloudformation.go
@@ -2,7 +2,8 @@ package directconnect
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -11,6 +12,8 @@ import (
"strconv"
"strings"
"unicode"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// CloudFormation resource types provisioned through CreateCFNResource.
@@ -94,15 +97,14 @@ func coerceNumbers(m map[string]any, keys ...string) {
}
func decodeStrict(m map[string]any, out any) error {
- raw, err := json.Marshal(lowerKeys(m))
+ raw, err := json.Marshal(lowerKeys(m), jsonx.V1())
if err != nil {
return clientError(err.Error())
}
- dec := json.NewDecoder(bytes.NewReader(raw))
- dec.DisallowUnknownFields()
+ dec := jsontext.NewDecoder(bytes.NewReader(raw))
- if err = dec.Decode(out); err != nil {
+ if err = json.UnmarshalDecode(dec, out, jsonx.V1(), json.RejectUnknownMembers(true)); err != nil {
return clientError("invalid properties: " + err.Error())
}
diff --git a/services/directconnect/connections.go b/services/directconnect/connections.go
index c98a389d53..b06177e054 100644
--- a/services/directconnect/connections.go
+++ b/services/directconnect/connections.go
@@ -1,10 +1,11 @@
package directconnect
import (
- "encoding/json"
+ "encoding/json/v2"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -511,7 +512,7 @@ func (b *InMemoryBackend) macSecSecretARNLocked(req *associateMacSecKeyRequest)
return b.synthesizeMacSecSecretARN(id), nil
}
- payload, err := json.Marshal(map[string]string{"ckn": req.Ckn, "cak": req.Cak})
+ payload, err := json.Marshal(map[string]string{"ckn": req.Ckn, "cak": req.Cak}, jsonx.V1())
if err != nil {
return "", serverError(err.Error())
}
diff --git a/services/directconnect/handler.go b/services/directconnect/handler.go
index 266f6fdcf6..a8aaec871f 100644
--- a/services/directconnect/handler.go
+++ b/services/directconnect/handler.go
@@ -2,7 +2,8 @@ package directconnect
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -13,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
@@ -186,8 +188,8 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
return ""
}
- var m map[string]json.RawMessage
- if unmarshalErr := json.Unmarshal(body, &m); unmarshalErr != nil {
+ var m map[string]jsontext.Value
+ if unmarshalErr := json.Unmarshal(body, &m, jsonx.V1()); unmarshalErr != nil {
return ""
}
@@ -199,11 +201,11 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
// extractFirstResourceByKeys returns the first string resource found for the provided JSON field names.
-func extractFirstResourceByKeys(body map[string]json.RawMessage, keys ...string) string {
+func extractFirstResourceByKeys(body map[string]jsontext.Value, keys ...string) string {
for _, key := range keys {
if raw, ok := body[key]; ok {
var resourceID string
- if err := json.Unmarshal(raw, &resourceID); err == nil && resourceID != "" {
+ if err := json.Unmarshal(raw, &resourceID, jsonx.V1()); err == nil && resourceID != "" {
return resourceID
}
}
@@ -281,7 +283,7 @@ func (h *Handler) handleError(ctx context.Context, c *echo.Context, action strin
}
body := map[string]string{"__type": errType, "message": err.Error()}
- payload, _ := json.Marshal(body)
+ payload, _ := json.Marshal(body, jsonx.V1())
c.Response().Header().Set("Content-Type", "application/x-amz-json-1.1")
@@ -316,7 +318,7 @@ func decodeBody[T any](body []byte) (*T, error) {
var v T
if len(body) > 0 {
- if err := json.Unmarshal(body, &v); err != nil {
+ if err := json.Unmarshal(body, &v, jsonx.V1()); err != nil {
return nil, clientError("malformed request body: " + err.Error())
}
}
@@ -328,7 +330,7 @@ func decodeBody[T any](body []byte) (*T, error) {
// exception (marshaling our own well-typed wire structs should never
// fail).
func marshalResponse(v any) ([]byte, error) {
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return nil, serverError("directconnect: marshal response: " + err.Error())
}
diff --git a/services/directconnect/persistence.go b/services/directconnect/persistence.go
index 36e672ce18..64811d2667 100644
--- a/services/directconnect/persistence.go
+++ b/services/directconnect/persistence.go
@@ -2,7 +2,7 @@ package directconnect
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -20,11 +20,11 @@ const directConnectSnapshotVersion = 1
// backendSnapshot is the top-level on-disk shape for the Direct Connect
// backend.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountId"`
- Region string `json:"region"`
- Version int `json:"version"`
- NextASN int64 `json:"nextAmazonSideAsn"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountId"`
+ Region string `json:"region"`
+ Version int `json:"version"`
+ NextASN int64 `json:"nextAmazonSideAsn"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/directoryservice/handler.go b/services/directoryservice/handler.go
index 8bd4698f88..b527cf1866 100644
--- a/services/directoryservice/handler.go
+++ b/services/directoryservice/handler.go
@@ -2,7 +2,8 @@ package directoryservice
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"slices"
@@ -13,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -367,7 +369,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var data map[string]any
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
@@ -424,8 +426,8 @@ func (h *Handler) handleTwoFieldOp(c *echo.Context, op twoFieldOp) error {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid body"))
}
- var raw map[string]json.RawMessage
- if jsonErr := json.Unmarshal(body, &raw); jsonErr != nil {
+ var raw map[string]jsontext.Value
+ if jsonErr := json.Unmarshal(body, &raw, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -447,14 +449,14 @@ func (h *Handler) handleTwoFieldOp(c *echo.Context, op twoFieldOp) error {
// jsonString returns the string value stored under key in raw, or "" if absent
// or not a JSON string.
-func jsonString(raw map[string]json.RawMessage, key string) string {
+func jsonString(raw map[string]jsontext.Value, key string) string {
v, ok := raw[key]
if !ok {
return ""
}
var s string
- if err := json.Unmarshal(v, &s); err != nil {
+ if err := json.Unmarshal(v, &s, jsonx.V1()); err != nil {
return ""
}
diff --git a/services/directoryservice/handler_ad_assessments.go b/services/directoryservice/handler_ad_assessments.go
index 8aa176f156..bb85d0941f 100644
--- a/services/directoryservice/handler_ad_assessments.go
+++ b/services/directoryservice/handler_ad_assessments.go
@@ -1,13 +1,14 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// startADAssessmentRequest mirrors the real StartADAssessmentInput, including
@@ -43,7 +44,7 @@ func (h *Handler) handleStartADAssessment(c *echo.Context) error {
var req startADAssessmentRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -117,7 +118,7 @@ func (h *Handler) handleDeleteADAssessment(c *echo.Context) error {
AssessmentID string `json:"AssessmentId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -148,7 +149,7 @@ func (h *Handler) handleDescribeADAssessment(c *echo.Context) error {
AssessmentID string `json:"AssessmentId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -246,7 +247,7 @@ func (h *Handler) handleListADAssessments(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
diff --git a/services/directoryservice/handler_ad_assessments_test.go b/services/directoryservice/handler_ad_assessments_test.go
index 214e66cca0..1cc19c8a71 100644
--- a/services/directoryservice/handler_ad_assessments_test.go
+++ b/services/directoryservice/handler_ad_assessments_test.go
@@ -1,12 +1,14 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestADAssessments(t *testing.T) {
@@ -28,7 +30,7 @@ func TestADAssessments(t *testing.T) {
rec1 := doRequest(t, h, "StartADAssessment", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec1.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1, jsonx.V1()))
assessID, _ := r1["AssessmentId"].(string)
assert.NotEmpty(t, assessID)
@@ -36,7 +38,7 @@ func TestADAssessments(t *testing.T) {
rec2 := doRequest(t, h, "ListADAssessments", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
assessments, _ := r2["Assessments"].([]any)
assert.Len(t, assessments, 1)
@@ -47,7 +49,7 @@ func TestADAssessments(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec3.Code)
var r3 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &r3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &r3, jsonx.V1()))
assessment, _ := r3["Assessment"].(map[string]any)
assert.Equal(t, assessID, assessment["AssessmentId"])
@@ -69,7 +71,7 @@ func TestADAssessments(t *testing.T) {
rec5 := doRequest(t, h, "ListADAssessments", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec5.Code)
var r5 map[string]any
- require.NoError(t, json.Unmarshal(rec5.Body.Bytes(), &r5))
+ require.NoError(t, json.Unmarshal(rec5.Body.Bytes(), &r5, jsonx.V1()))
assessments2, _ := r5["Assessments"].([]any)
assert.Empty(t, assessments2)
@@ -138,7 +140,7 @@ func TestStartADAssessment_ConfigurationValidation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errBody map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errBody["__type"])
})
}
@@ -179,7 +181,7 @@ func TestStartADAssessment_ConfigurationRoundTrip(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
assessID, _ := startResp["AssessmentId"].(string)
require.NotEmpty(t, assessID)
@@ -188,7 +190,7 @@ func TestStartADAssessment_ConfigurationRoundTrip(t *testing.T) {
descRec := doRequest(t, h, "DescribeADAssessment", map[string]any{"AssessmentId": assessID})
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assessment, _ := descResp["Assessment"].(map[string]any)
assert.NotContains(t, assessment, "DirectoryId")
@@ -214,7 +216,7 @@ func TestStartADAssessment_ConfigurationRoundTrip(t *testing.T) {
listRec := doRequest(t, h, "ListADAssessments", map[string]any{})
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
summaries, _ := listResp["Assessments"].([]any)
require.Len(t, summaries, 1)
summary, _ := summaries[0].(map[string]any)
diff --git a/services/directoryservice/handler_certificates.go b/services/directoryservice/handler_certificates.go
index 9ff8d7c193..3b0af7ebff 100644
--- a/services/directoryservice/handler_certificates.go
+++ b/services/directoryservice/handler_certificates.go
@@ -2,13 +2,14 @@ package directoryservice
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleRegisterCertificate(c *echo.Context) error {
@@ -26,7 +27,7 @@ func (h *Handler) handleRegisterCertificate(c *echo.Context) error {
Type string `json:"Type"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -86,7 +87,7 @@ func (h *Handler) handleListCertificates(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
@@ -135,7 +136,7 @@ func (h *Handler) handleDescribeCertificate(c *echo.Context) error {
CertificateID string `json:"CertificateId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -179,7 +180,7 @@ func (h *Handler) handleEnableCAEnrollmentPolicy(c *echo.Context) error {
PcaConnectorArn string `json:"PcaConnectorArn"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -210,7 +211,7 @@ func (h *Handler) handleDisableCAEnrollmentPolicy(c *echo.Context) error {
DirectoryID string `json:"DirectoryId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -235,7 +236,7 @@ func (h *Handler) handleDescribeCAEnrollmentPolicy(c *echo.Context) error {
DirectoryID string `json:"DirectoryId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
diff --git a/services/directoryservice/handler_certificates_test.go b/services/directoryservice/handler_certificates_test.go
index 61689d4c48..d1e5d15cdd 100644
--- a/services/directoryservice/handler_certificates_test.go
+++ b/services/directoryservice/handler_certificates_test.go
@@ -1,12 +1,14 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestListCertificates_Pagination(t *testing.T) {
@@ -73,7 +75,7 @@ func TestCertificates(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec1.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1, jsonx.V1()))
certID, _ := r1["CertificateId"].(string)
assert.NotEmpty(t, certID)
@@ -81,7 +83,7 @@ func TestCertificates(t *testing.T) {
rec2 := doRequest(t, h, "ListCertificates", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
certs, _ := r2["CertificatesInfo"].([]any)
assert.Len(t, certs, 1)
@@ -92,7 +94,7 @@ func TestCertificates(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec3.Code)
var r3 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &r3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &r3, jsonx.V1()))
cert, _ := r3["Certificate"].(map[string]any)
assert.Equal(t, certID, cert["CertificateId"])
assert.Equal(t, "test", cert["CommonName"], "CommonName must be parsed from the X.509 subject")
@@ -108,7 +110,7 @@ func TestCertificates(t *testing.T) {
rec5 := doRequest(t, h, "ListCertificates", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec5.Code)
var r5 map[string]any
- require.NoError(t, json.Unmarshal(rec5.Body.Bytes(), &r5))
+ require.NoError(t, json.Unmarshal(rec5.Body.Bytes(), &r5, jsonx.V1()))
certs2, _ := r5["CertificatesInfo"].([]any)
assert.Empty(t, certs2)
@@ -145,7 +147,7 @@ func TestCAEnrollmentPolicy(t *testing.T) {
rec2 := doRequest(t, h, "DescribeCAEnrollmentPolicy", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
assert.Equal(t, "Success", r2["CaEnrollmentPolicyStatus"])
assert.Equal(t, connectorArn, r2["PcaConnectorArn"])
assert.Equal(t, dirID, r2["DirectoryId"])
@@ -158,7 +160,7 @@ func TestCAEnrollmentPolicy(t *testing.T) {
rec4 := doRequest(t, h, "DescribeCAEnrollmentPolicy", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec4.Code)
var r4 map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &r4))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &r4, jsonx.V1()))
assert.Equal(t, "Disabled", r4["CaEnrollmentPolicyStatus"])
assert.Equal(t, connectorArn, r4["PcaConnectorArn"])
diff --git a/services/directoryservice/handler_client_auth.go b/services/directoryservice/handler_client_auth.go
index 84e82e6c20..1f6855cb08 100644
--- a/services/directoryservice/handler_client_auth.go
+++ b/services/directoryservice/handler_client_auth.go
@@ -1,13 +1,14 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleEnableClientAuthentication(c *echo.Context) error { //nolint:dupl // enable/disable pair.
@@ -21,7 +22,7 @@ func (h *Handler) handleEnableClientAuthentication(c *echo.Context) error { //no
Type string `json:"Type"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -52,7 +53,7 @@ func (h *Handler) handleDisableClientAuthentication(c *echo.Context) error { //n
Type string `json:"Type"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -86,7 +87,7 @@ func (h *Handler) handleDescribeClientAuthenticationSettings(c *echo.Context) er
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
diff --git a/services/directoryservice/handler_client_auth_test.go b/services/directoryservice/handler_client_auth_test.go
index a6fb302eb1..ef0c57e76e 100644
--- a/services/directoryservice/handler_client_auth_test.go
+++ b/services/directoryservice/handler_client_auth_test.go
@@ -1,13 +1,14 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/directoryservice"
)
@@ -37,7 +38,7 @@ func TestClientAuthentication(t *testing.T) {
rec2 := doRequest(t, h, "DescribeClientAuthenticationSettings", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
settings, _ := r2["ClientAuthenticationSettingsInfo"].([]any)
require.Len(t, settings, 1)
setting := settings[0].(map[string]any)
@@ -125,7 +126,7 @@ func TestEnableClientAuthentication_Validation(t *testing.T) {
})
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", body["__type"])
})
}
diff --git a/services/directoryservice/handler_conditional_forwarders.go b/services/directoryservice/handler_conditional_forwarders.go
index fcea9f04c6..dd397fe6b9 100644
--- a/services/directoryservice/handler_conditional_forwarders.go
+++ b/services/directoryservice/handler_conditional_forwarders.go
@@ -2,12 +2,13 @@ package directoryservice
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateConditionalForwarder(c *echo.Context) error { //nolint:dupl // existing issue.
@@ -23,7 +24,7 @@ func (h *Handler) handleCreateConditionalForwarder(c *echo.Context) error { //no
DNSIpv6Addrs []string `json:"DnsIpv6Addrs"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -60,7 +61,7 @@ func (h *Handler) handleUpdateConditionalForwarder(c *echo.Context) error { //no
DNSIpv6Addrs []string `json:"DnsIpv6Addrs"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -105,7 +106,7 @@ func (h *Handler) handleDescribeConditionalForwarders(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
diff --git a/services/directoryservice/handler_conditional_forwarders_test.go b/services/directoryservice/handler_conditional_forwarders_test.go
index 287ac36c60..9e0d29bbbb 100644
--- a/services/directoryservice/handler_conditional_forwarders_test.go
+++ b/services/directoryservice/handler_conditional_forwarders_test.go
@@ -1,12 +1,14 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestConditionalForwarder_Lifecycle(t *testing.T) {
@@ -110,7 +112,7 @@ func TestConditionalForwarders(t *testing.T) {
rec2 := doRequest(t, h, "DescribeConditionalForwarders", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
fwds, _ := r2["ConditionalForwarders"].([]any)
require.Len(t, fwds, 1)
fwd := fwds[0].(map[string]any)
@@ -128,7 +130,7 @@ func TestConditionalForwarders(t *testing.T) {
rec4 := doRequest(t, h, "DescribeConditionalForwarders", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec4.Code)
var r4 map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &r4))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &r4, jsonx.V1()))
fwds2, _ := r4["ConditionalForwarders"].([]any)
require.Len(t, fwds2, 1)
fwd2 := fwds2[0].(map[string]any)
@@ -146,7 +148,7 @@ func TestConditionalForwarders(t *testing.T) {
rec6 := doRequest(t, h, "DescribeConditionalForwarders", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec6.Code)
var r6 map[string]any
- require.NoError(t, json.Unmarshal(rec6.Body.Bytes(), &r6))
+ require.NoError(t, json.Unmarshal(rec6.Body.Bytes(), &r6, jsonx.V1()))
fwds3, _ := r6["ConditionalForwarders"].([]any)
assert.Empty(t, fwds3)
diff --git a/services/directoryservice/handler_directories.go b/services/directoryservice/handler_directories.go
index 9bbc9c6c65..0afadb6ba3 100644
--- a/services/directoryservice/handler_directories.go
+++ b/services/directoryservice/handler_directories.go
@@ -1,12 +1,13 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateDirectory(c *echo.Context) error {
@@ -29,7 +30,7 @@ func (h *Handler) handleCreateDirectory(c *echo.Context) error {
} `json:"Tags"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -97,7 +98,7 @@ func (h *Handler) handleCreateMicrosoftAD(c *echo.Context) error {
} `json:"Tags"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -153,6 +154,7 @@ func (h *Handler) handleCreateMicrosoftAD(c *echo.Context) error {
})
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) handleDeleteDirectory(c *echo.Context) error {
body, err := httputils.ReadBody(c.Request())
if err != nil {
@@ -163,7 +165,7 @@ func (h *Handler) handleDeleteDirectory(c *echo.Context) error {
DirectoryID string `json:"DirectoryId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -193,7 +195,7 @@ func (h *Handler) handleDescribeDirectories(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
@@ -238,7 +240,7 @@ func (h *Handler) handleCreateAlias(c *echo.Context) error {
Alias string `json:"Alias"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -292,7 +294,7 @@ func (h *Handler) handleCreateComputer(c *echo.Context) error {
} `json:"ComputerAttributes"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -353,7 +355,7 @@ func (h *Handler) handleResetUserPassword(c *echo.Context) error {
NewPassword string `json:"NewPassword"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -398,7 +400,7 @@ func (h *Handler) handleConnectDirectory(c *echo.Context) error {
} `json:"Tags"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
diff --git a/services/directoryservice/handler_directories_extra_test.go b/services/directoryservice/handler_directories_extra_test.go
index faf6b379be..481fcec993 100644
--- a/services/directoryservice/handler_directories_extra_test.go
+++ b/services/directoryservice/handler_directories_extra_test.go
@@ -1,7 +1,7 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/directoryservice"
)
@@ -403,7 +404,7 @@ func TestCreateComputer(t *testing.T) {
assert.Equal(t, tc.wantCode, rec.Code)
if tc.check != nil {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tc.check(t, resp)
}
})
@@ -492,7 +493,7 @@ func TestConnectDirectory(t *testing.T) {
assert.Equal(t, tc.wantCode, rec.Code)
if tc.check != nil {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tc.check(t, resp)
}
})
diff --git a/services/directoryservice/handler_directories_test.go b/services/directoryservice/handler_directories_test.go
index f849321392..7dda57343e 100644
--- a/services/directoryservice/handler_directories_test.go
+++ b/services/directoryservice/handler_directories_test.go
@@ -1,7 +1,7 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/directoryservice"
)
@@ -33,7 +34,7 @@ func TestDirectoryService_CreateDirectory(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
id, ok := resp["DirectoryId"].(string)
require.True(t, ok)
assert.NotEmpty(t, id)
@@ -79,7 +80,7 @@ func TestDirectoryService_CreateMicrosoftAD(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
id, ok := resp["DirectoryId"].(string)
require.True(t, ok)
assert.NotEmpty(t, id)
@@ -115,7 +116,7 @@ func TestDirectoryService_DeleteDirectory(t *testing.T) {
"Name": "corp.example.com", "Password": "Admin1234!", "Size": "Small",
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
dirID := createResp["DirectoryId"].(string)
rec := doRequest(t, h, "DeleteDirectory", map[string]any{"DirectoryId": dirID})
@@ -155,7 +156,7 @@ func TestDirectoryService_DescribeDirectories(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dirs, ok := resp["DirectoryDescriptions"].([]any)
require.True(t, ok)
assert.Len(t, dirs, 2)
@@ -169,7 +170,7 @@ func TestDirectoryService_DescribeDirectories(t *testing.T) {
"Name": "corp.example.com", "Password": "Admin1234!", "Size": "Small",
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
dirID := createResp["DirectoryId"].(string)
rec := doRequest(t, h, "DescribeDirectories", map[string]any{
@@ -178,7 +179,7 @@ func TestDirectoryService_DescribeDirectories(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dirs, ok := resp["DirectoryDescriptions"].([]any)
require.True(t, ok)
require.Len(t, dirs, 1)
@@ -188,7 +189,7 @@ func TestDirectoryService_DescribeDirectories(t *testing.T) {
rec = doRequest(t, h, "DescribeDirectories", map[string]any{
"DirectoryIds": []string{dirID},
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dirs = resp["DirectoryDescriptions"].([]any)
dir := dirs[0].(map[string]any)
assert.Equal(t, dirID, dir["DirectoryId"])
@@ -214,7 +215,7 @@ func TestDirectoryService_DescribeDirectories(t *testing.T) {
"Name": "stage.example.com", "Password": "Admin1234!", "Size": "Small",
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
dirID := createResp["DirectoryId"].(string)
rec := doRequest(t, h, "DescribeDirectories", map[string]any{"DirectoryIds": []string{dirID}})
@@ -259,7 +260,7 @@ func TestDirectoryService_DescribeDirectories(t *testing.T) {
},
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
dirID := createResp["DirectoryId"].(string)
rec := doRequest(t, h, "DescribeDirectories", map[string]any{"DirectoryIds": []string{dirID}})
@@ -283,7 +284,7 @@ func TestDirectoryService_DescribeDirectories(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dirs, ok := resp["DirectoryDescriptions"].([]any)
require.True(t, ok)
assert.Empty(t, dirs)
@@ -426,7 +427,7 @@ func TestDirectoryService_CreateAlias(t *testing.T) {
"Name": "corp.example.com", "Password": "Admin1234!", "Size": "Small",
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
dirID := createResp["DirectoryId"].(string)
rec := doRequest(t, h, "CreateAlias", map[string]any{
@@ -436,7 +437,7 @@ func TestDirectoryService_CreateAlias(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, dirID, resp["DirectoryId"])
assert.Equal(t, "myalias", resp["Alias"])
})
@@ -462,7 +463,7 @@ func TestDirectoryService_GetDirectoryLimits(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
limits, ok := resp["DirectoryLimits"].(map[string]any)
require.True(t, ok)
assert.EqualValues(t, 0, limits["CloudOnlyDirectoriesCurrentCount"])
@@ -479,7 +480,7 @@ func TestDirectoryService_GetDirectoryLimits(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
limits := resp["DirectoryLimits"].(map[string]any)
assert.EqualValues(t, 1, limits["CloudOnlyDirectoriesCurrentCount"])
})
@@ -495,7 +496,7 @@ func TestDirectoryService_MicrosoftAD_DescribeReturnsType(t *testing.T) {
"Edition": "Enterprise",
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
dirID := createResp["DirectoryId"].(string)
rec := doRequest(t, h, "DescribeDirectories", map[string]any{
@@ -504,7 +505,7 @@ func TestDirectoryService_MicrosoftAD_DescribeReturnsType(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dirs := resp["DirectoryDescriptions"].([]any)
require.Len(t, dirs, 1)
dir := dirs[0].(map[string]any)
@@ -520,7 +521,7 @@ func TestDirectoryService_DeleteDirectoryRemovesSnapshots(t *testing.T) {
"Name": "corp.example.com", "Password": "Admin1234!", "Size": "Small",
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
dirID := createResp["DirectoryId"].(string)
doRequest(t, h, "CreateSnapshot", map[string]any{"DirectoryId": dirID})
@@ -529,7 +530,7 @@ func TestDirectoryService_DeleteDirectoryRemovesSnapshots(t *testing.T) {
descRec := doRequest(t, h, "DescribeSnapshots", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
snaps := descResp["Snapshots"].([]any)
assert.Empty(t, snaps)
}
diff --git a/services/directoryservice/handler_domain_controllers.go b/services/directoryservice/handler_domain_controllers.go
index 2be4fcd8e0..229d133ffb 100644
--- a/services/directoryservice/handler_domain_controllers.go
+++ b/services/directoryservice/handler_domain_controllers.go
@@ -1,13 +1,14 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDescribeDomainControllers(c *echo.Context) error {
@@ -24,7 +25,7 @@ func (h *Handler) handleDescribeDomainControllers(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
@@ -76,7 +77,7 @@ func (h *Handler) handleUpdateNumberOfDomainControllers(c *echo.Context) error {
DesiredNumber int32 `json:"DesiredNumber"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
diff --git a/services/directoryservice/handler_domain_controllers_test.go b/services/directoryservice/handler_domain_controllers_test.go
index e17de7f22a..15615ceb48 100644
--- a/services/directoryservice/handler_domain_controllers_test.go
+++ b/services/directoryservice/handler_domain_controllers_test.go
@@ -1,12 +1,14 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDomainControllers_Lifecycle(t *testing.T) {
@@ -128,7 +130,7 @@ func TestDomainControllers(t *testing.T) {
rec1 := doRequest(t, h, "DescribeDomainControllers", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec1.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1, jsonx.V1()))
dcs, _ := r1["DomainControllers"].([]any)
assert.Empty(t, dcs)
@@ -143,7 +145,7 @@ func TestDomainControllers(t *testing.T) {
rec3 := doRequest(t, h, "DescribeDomainControllers", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec3.Code)
var r3 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &r3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &r3, jsonx.V1()))
dcs2, _ := r3["DomainControllers"].([]any)
assert.Len(t, dcs2, 2)
diff --git a/services/directoryservice/handler_event_topics.go b/services/directoryservice/handler_event_topics.go
index c5fcfea734..309efbae89 100644
--- a/services/directoryservice/handler_event_topics.go
+++ b/services/directoryservice/handler_event_topics.go
@@ -2,13 +2,14 @@ package directoryservice
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleRegisterEventTopic(c *echo.Context) error {
@@ -41,7 +42,7 @@ func (h *Handler) handleDescribeEventTopics(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
diff --git a/services/directoryservice/handler_event_topics_test.go b/services/directoryservice/handler_event_topics_test.go
index 343c314b2e..4c588b6c76 100644
--- a/services/directoryservice/handler_event_topics_test.go
+++ b/services/directoryservice/handler_event_topics_test.go
@@ -1,12 +1,14 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDescribeEventTopics_Filtering(t *testing.T) {
@@ -95,7 +97,7 @@ func TestEventTopics(t *testing.T) {
rec2 := doRequest(t, h, "DescribeEventTopics", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
topics, _ := r2["EventTopics"].([]any)
require.Len(t, topics, 1)
topic := topics[0].(map[string]any)
@@ -112,7 +114,7 @@ func TestEventTopics(t *testing.T) {
rec4 := doRequest(t, h, "DescribeEventTopics", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec4.Code)
var r4 map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &r4))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &r4, jsonx.V1()))
topics2, _ := r4["EventTopics"].([]any)
assert.Empty(t, topics2)
diff --git a/services/directoryservice/handler_hybrid_ad.go b/services/directoryservice/handler_hybrid_ad.go
index b914b15405..bba1be3782 100644
--- a/services/directoryservice/handler_hybrid_ad.go
+++ b/services/directoryservice/handler_hybrid_ad.go
@@ -1,13 +1,14 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// hybridUpdateInfoEntryJSON builds the real types.HybridUpdateInfoEntry wire
@@ -58,7 +59,7 @@ func (h *Handler) handleCreateHybridAD(c *echo.Context) error {
} `json:"Tags"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -96,7 +97,7 @@ func (h *Handler) handleUpdateHybridAD(c *echo.Context) error {
DirectoryID string `json:"DirectoryId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -157,7 +158,7 @@ func (h *Handler) handleDescribeHybridADUpdate(c *echo.Context) error {
UpdateType string `json:"UpdateType"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
diff --git a/services/directoryservice/handler_hybrid_ad_test.go b/services/directoryservice/handler_hybrid_ad_test.go
index d89dd98c24..4063ab50ca 100644
--- a/services/directoryservice/handler_hybrid_ad_test.go
+++ b/services/directoryservice/handler_hybrid_ad_test.go
@@ -1,13 +1,14 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/directoryservice"
)
@@ -20,7 +21,7 @@ func mustStartAssessment(t *testing.T, h *directoryservice.Handler, dirID string
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assessmentID, _ := out["AssessmentId"].(string)
require.NotEmpty(t, assessmentID)
@@ -44,7 +45,7 @@ func TestHybridAD_CreateUpdateDescribeCycle(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
hybridDirID, _ := createOut["DirectoryId"].(string)
require.NotEmpty(t, hybridDirID)
assert.NotContains(t, createOut, "RequestId", "CreateHybridADOutput has no RequestId member")
@@ -60,7 +61,7 @@ func TestHybridAD_CreateUpdateDescribeCycle(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateOut map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut, jsonx.V1()))
updateAssessmentID, _ := updateOut["AssessmentId"].(string)
assert.NotEmpty(t, updateAssessmentID)
assert.Equal(t, hybridDirID, updateOut["DirectoryId"])
@@ -73,7 +74,7 @@ func TestHybridAD_CreateUpdateDescribeCycle(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
activities, ok := descOut["UpdateActivities"].(map[string]any)
require.True(t, ok, "UpdateActivities must be an object")
@@ -102,7 +103,7 @@ func TestHybridAD_CreateUpdateDescribeCycle(t *testing.T) {
require.Equal(t, http.StatusOK, describeDirRec.Code)
var dirOut map[string]any
- require.NoError(t, json.Unmarshal(describeDirRec.Body.Bytes(), &dirOut))
+ require.NoError(t, json.Unmarshal(describeDirRec.Body.Bytes(), &dirOut, jsonx.V1()))
dirs, ok := dirOut["DirectoryDescriptions"].([]any)
require.True(t, ok)
require.Len(t, dirs, 1)
@@ -218,7 +219,7 @@ func TestHybridAD_UpdateHybridAD_Errors(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
hybridDirID, _ := createOut["DirectoryId"].(string)
return map[string]any{"DirectoryId": hybridDirID}
diff --git a/services/directoryservice/handler_ip_routes.go b/services/directoryservice/handler_ip_routes.go
index f71c124de3..2899984731 100644
--- a/services/directoryservice/handler_ip_routes.go
+++ b/services/directoryservice/handler_ip_routes.go
@@ -1,13 +1,14 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleAddIpRoutes(c *echo.Context) error { //nolint:revive,staticcheck // existing issue.
@@ -25,7 +26,7 @@ func (h *Handler) handleAddIpRoutes(c *echo.Context) error { //nolint:revive,sta
} `json:"IpRoutes"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -57,7 +58,7 @@ func (h *Handler) handleRemoveIpRoutes(c *echo.Context) error { //nolint:revive,
CidrIPv6s []string `json:"CidrIpv6s"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -86,7 +87,7 @@ func (h *Handler) handleListIpRoutes(c *echo.Context) error { //nolint:revive,st
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
diff --git a/services/directoryservice/handler_ip_routes_test.go b/services/directoryservice/handler_ip_routes_test.go
index 4f3be468b8..12e19b6e30 100644
--- a/services/directoryservice/handler_ip_routes_test.go
+++ b/services/directoryservice/handler_ip_routes_test.go
@@ -1,13 +1,15 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestListIpRoutes_Pagination(t *testing.T) {
@@ -70,7 +72,7 @@ func TestIpRoutes(t *testing.T) {
rec2 := doRequest(t, h, "ListIpRoutes", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
routes, _ := r2["IpRoutesInfo"].([]any)
assert.Len(t, routes, 1)
@@ -85,7 +87,7 @@ func TestIpRoutes(t *testing.T) {
rec4 := doRequest(t, h, "ListIpRoutes", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec4.Code)
var r4 map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &r4))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &r4, jsonx.V1()))
routes2, _ := r4["IpRoutesInfo"].([]any)
assert.Empty(t, routes2)
diff --git a/services/directoryservice/handler_ldaps.go b/services/directoryservice/handler_ldaps.go
index 810b552a92..e9d25e20e3 100644
--- a/services/directoryservice/handler_ldaps.go
+++ b/services/directoryservice/handler_ldaps.go
@@ -1,13 +1,14 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleEnableLDAPS(c *echo.Context) error { //nolint:dupl // existing issue.
@@ -21,7 +22,7 @@ func (h *Handler) handleEnableLDAPS(c *echo.Context) error { //nolint:dupl // ex
Type string `json:"Type"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -55,7 +56,7 @@ func (h *Handler) handleDisableLDAPS(c *echo.Context) error { //nolint:dupl // e
Type string `json:"Type"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -92,7 +93,7 @@ func (h *Handler) handleDescribeLDAPSSettings(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
diff --git a/services/directoryservice/handler_ldaps_test.go b/services/directoryservice/handler_ldaps_test.go
index 58ed6915fc..a5bfc9d021 100644
--- a/services/directoryservice/handler_ldaps_test.go
+++ b/services/directoryservice/handler_ldaps_test.go
@@ -1,13 +1,14 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/directoryservice"
)
@@ -37,7 +38,7 @@ func TestLDAPS(t *testing.T) {
rec2 := doRequest(t, h, "DescribeLDAPSSettings", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
settings, _ := r2["LDAPSSettingsInfo"].([]any)
require.Len(t, settings, 1)
setting := settings[0].(map[string]any)
@@ -126,6 +127,6 @@ func TestEnableLDAPS_InvalidType(t *testing.T) {
})
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", body["__type"])
}
diff --git a/services/directoryservice/handler_log_subscriptions.go b/services/directoryservice/handler_log_subscriptions.go
index fc868058fe..2eee4d9f44 100644
--- a/services/directoryservice/handler_log_subscriptions.go
+++ b/services/directoryservice/handler_log_subscriptions.go
@@ -2,13 +2,14 @@ package directoryservice
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateLogSubscription(c *echo.Context) error {
@@ -30,7 +31,7 @@ func (h *Handler) handleDeleteLogSubscription(c *echo.Context) error {
DirectoryID string `json:"DirectoryId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -58,7 +59,7 @@ func (h *Handler) handleListLogSubscriptions(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
diff --git a/services/directoryservice/handler_log_subscriptions_test.go b/services/directoryservice/handler_log_subscriptions_test.go
index d1d786001e..28a7c91c2b 100644
--- a/services/directoryservice/handler_log_subscriptions_test.go
+++ b/services/directoryservice/handler_log_subscriptions_test.go
@@ -1,12 +1,14 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestListLogSubscriptions_Pagination(t *testing.T) {
@@ -58,7 +60,7 @@ func TestLogSubscriptions(t *testing.T) {
rec2 := doRequest(t, h, "ListLogSubscriptions", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
subs, _ := r2["LogSubscriptions"].([]any)
assert.Len(t, subs, 1)
@@ -70,7 +72,7 @@ func TestLogSubscriptions(t *testing.T) {
rec4 := doRequest(t, h, "ListLogSubscriptions", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec4.Code)
var r4 map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &r4))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &r4, jsonx.V1()))
subs2, _ := r4["LogSubscriptions"].([]any)
assert.Empty(t, subs2)
diff --git a/services/directoryservice/handler_radius.go b/services/directoryservice/handler_radius.go
index d4118e5f12..890627d7b9 100644
--- a/services/directoryservice/handler_radius.go
+++ b/services/directoryservice/handler_radius.go
@@ -1,12 +1,13 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type radiusRequest struct {
@@ -32,7 +33,7 @@ func (h *Handler) handleEnableRadius(c *echo.Context) error { //nolint:dupl // e
var req radiusRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -69,7 +70,7 @@ func (h *Handler) handleDisableRadius(c *echo.Context) error {
DirectoryID string `json:"DirectoryId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -92,7 +93,7 @@ func (h *Handler) handleUpdateRadius(c *echo.Context) error { //nolint:dupl // e
var req radiusRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
diff --git a/services/directoryservice/handler_regions.go b/services/directoryservice/handler_regions.go
index 894bca4dfe..110ef3e152 100644
--- a/services/directoryservice/handler_regions.go
+++ b/services/directoryservice/handler_regions.go
@@ -1,13 +1,14 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleAddRegion(c *echo.Context) error {
@@ -25,7 +26,7 @@ func (h *Handler) handleAddRegion(c *echo.Context) error {
RegionName string `json:"RegionName"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -59,7 +60,7 @@ func (h *Handler) handleRemoveRegion(c *echo.Context) error {
DirectoryID string `json:"DirectoryId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -87,7 +88,7 @@ func (h *Handler) handleDescribeRegions(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
diff --git a/services/directoryservice/handler_regions_test.go b/services/directoryservice/handler_regions_test.go
index 02cef1a5da..f322f8da91 100644
--- a/services/directoryservice/handler_regions_test.go
+++ b/services/directoryservice/handler_regions_test.go
@@ -1,12 +1,14 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestRegions(t *testing.T) {
@@ -39,7 +41,7 @@ func TestRegions(t *testing.T) {
rec2 := doRequest(t, h, "DescribeRegions", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
regions, _ := r2["RegionsDescription"].([]any)
require.Len(t, regions, 1)
region := regions[0].(map[string]any)
@@ -57,7 +59,7 @@ func TestRegions(t *testing.T) {
rec4 := doRequest(t, h, "DescribeRegions", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec4.Code)
var r4 map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &r4))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &r4, jsonx.V1()))
regions2, _ := r4["RegionsDescription"].([]any)
assert.Empty(t, regions2)
@@ -78,6 +80,6 @@ func TestAddRegion_Validation(t *testing.T) {
})
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", body["__type"])
}
diff --git a/services/directoryservice/handler_schema_extensions.go b/services/directoryservice/handler_schema_extensions.go
index 956cd803bf..bfc6d514e5 100644
--- a/services/directoryservice/handler_schema_extensions.go
+++ b/services/directoryservice/handler_schema_extensions.go
@@ -1,13 +1,14 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleStartSchemaExtension(c *echo.Context) error {
@@ -23,7 +24,7 @@ func (h *Handler) handleStartSchemaExtension(c *echo.Context) error {
CreateSnapshotBeforeSchemaExtension bool `json:"CreateSnapshotBeforeSchemaExtension"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -58,7 +59,7 @@ func (h *Handler) handleCancelSchemaExtension(c *echo.Context) error {
SchemaExtensionID string `json:"SchemaExtensionId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -90,7 +91,7 @@ func (h *Handler) handleListSchemaExtensions(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
diff --git a/services/directoryservice/handler_schema_extensions_test.go b/services/directoryservice/handler_schema_extensions_test.go
index 1d6cb8a978..34bf0f0794 100644
--- a/services/directoryservice/handler_schema_extensions_test.go
+++ b/services/directoryservice/handler_schema_extensions_test.go
@@ -1,12 +1,14 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestSchemaExtensions_StateLifecycle(t *testing.T) {
@@ -155,7 +157,7 @@ func TestSchemaExtensions(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec1.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1, jsonx.V1()))
extID, _ := r1["SchemaExtensionId"].(string)
assert.NotEmpty(t, extID)
@@ -163,7 +165,7 @@ func TestSchemaExtensions(t *testing.T) {
rec2 := doRequest(t, h, "ListSchemaExtensions", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
exts, _ := r2["SchemaExtensionsInfo"].([]any)
assert.Len(t, exts, 1)
diff --git a/services/directoryservice/handler_settings.go b/services/directoryservice/handler_settings.go
index c5a8462b51..5efa56a59e 100644
--- a/services/directoryservice/handler_settings.go
+++ b/services/directoryservice/handler_settings.go
@@ -1,13 +1,14 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- Directory Data Access ---
@@ -22,7 +23,7 @@ func (h *Handler) handleEnableDirectoryDataAccess(c *echo.Context) error {
DirectoryID string `json:"DirectoryId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -47,7 +48,7 @@ func (h *Handler) handleDisableDirectoryDataAccess(c *echo.Context) error {
DirectoryID string `json:"DirectoryId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -72,7 +73,7 @@ func (h *Handler) handleDescribeDirectoryDataAccess(c *echo.Context) error {
DirectoryID string `json:"DirectoryId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -115,7 +116,7 @@ func (h *Handler) handleUpdateSettings(c *echo.Context) error {
} `json:"Settings"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -149,7 +150,7 @@ func (h *Handler) handleDescribeSettings(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
@@ -226,7 +227,7 @@ func (h *Handler) handleUpdateDirectorySetup(c *echo.Context) error {
CreateSnapshotBeforeUpdate bool `json:"CreateSnapshotBeforeUpdate"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -285,7 +286,7 @@ func (h *Handler) handleDescribeUpdateDirectory(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
diff --git a/services/directoryservice/handler_settings_test.go b/services/directoryservice/handler_settings_test.go
index ca6827e12e..2c56a6ce24 100644
--- a/services/directoryservice/handler_settings_test.go
+++ b/services/directoryservice/handler_settings_test.go
@@ -1,12 +1,14 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestSettings(t *testing.T) {
@@ -31,14 +33,14 @@ func TestSettings(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec1.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1, jsonx.V1()))
assert.Equal(t, dirID, r1["DirectoryId"])
// DescribeSettings
rec2 := doRequest(t, h, "DescribeSettings", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
settings, _ := r2["SettingEntries"].([]any)
require.Len(t, settings, 1)
setting := settings[0].(map[string]any)
@@ -79,7 +81,7 @@ func TestUpdateDirectorySetup(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
entries, _ := r2["UpdateActivities"].([]any)
assert.Len(t, entries, 1)
@@ -107,7 +109,7 @@ func TestDirectoryDataAccess(t *testing.T) {
rec1 := doRequest(t, h, "DescribeDirectoryDataAccess", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec1.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1, jsonx.V1()))
assert.Equal(t, "Disabled", r1["DataAccessStatus"])
// Enable
@@ -118,7 +120,7 @@ func TestDirectoryDataAccess(t *testing.T) {
rec3 := doRequest(t, h, "DescribeDirectoryDataAccess", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec3.Code)
var r3 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &r3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &r3, jsonx.V1()))
assert.Equal(t, "Enabled", r3["DataAccessStatus"])
// Disable
@@ -153,7 +155,7 @@ func TestUpdateDirectorySetup_Validation(t *testing.T) {
})
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", body["__type"])
})
}
diff --git a/services/directoryservice/handler_shared_directories.go b/services/directoryservice/handler_shared_directories.go
index d32be60215..44247d338e 100644
--- a/services/directoryservice/handler_shared_directories.go
+++ b/services/directoryservice/handler_shared_directories.go
@@ -1,13 +1,14 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleShareDirectory(c *echo.Context) error {
@@ -26,7 +27,7 @@ func (h *Handler) handleShareDirectory(c *echo.Context) error {
} `json:"ShareTarget"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -71,7 +72,7 @@ func (h *Handler) handleUnshareDirectory(c *echo.Context) error {
} `json:"UnshareTarget"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -101,7 +102,7 @@ func (h *Handler) handleAcceptSharedDirectory(c *echo.Context) error {
SharedDirectoryID string `json:"SharedDirectoryId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -144,7 +145,7 @@ func (h *Handler) handleRejectSharedDirectory(c *echo.Context) error {
SharedDirectoryID string `json:"SharedDirectoryId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -174,7 +175,7 @@ func (h *Handler) handleDescribeSharedDirectories(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
diff --git a/services/directoryservice/handler_shared_directories_test.go b/services/directoryservice/handler_shared_directories_test.go
index bb860cba8e..cbdf119ab8 100644
--- a/services/directoryservice/handler_shared_directories_test.go
+++ b/services/directoryservice/handler_shared_directories_test.go
@@ -1,13 +1,15 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestSharedDirectories(t *testing.T) {
@@ -34,7 +36,7 @@ func TestSharedDirectories(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec1.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1, jsonx.V1()))
sharedDirID, _ := r1["SharedDirectoryId"].(string)
assert.NotEmpty(t, sharedDirID)
@@ -45,7 +47,7 @@ func TestSharedDirectories(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
sd, _ := r2["SharedDirectory"].(map[string]any)
require.NotNil(t, sd, "AcceptSharedDirectoryOutput.SharedDirectory must be present")
assert.Equal(t, sharedDirID, sd["SharedDirectoryId"])
@@ -63,7 +65,7 @@ func TestSharedDirectories(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec3.Code)
var r3 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &r3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &r3, jsonx.V1()))
dirs, _ := r3["SharedDirectories"].([]any)
assert.Len(t, dirs, 1)
@@ -80,7 +82,7 @@ func TestSharedDirectories(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
assert.Equal(t, sharedDirID, r2["SharedDirectoryId"])
}
})
@@ -151,7 +153,7 @@ func TestShareDirectory_StatusLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, shareRec.Code)
var shareResp map[string]any
- require.NoError(t, json.Unmarshal(shareRec.Body.Bytes(), &shareResp))
+ require.NoError(t, json.Unmarshal(shareRec.Body.Bytes(), &shareResp, jsonx.V1()))
sharedID, _ := shareResp["SharedDirectoryId"].(string)
require.NotEmpty(t, sharedID)
diff --git a/services/directoryservice/handler_snapshots.go b/services/directoryservice/handler_snapshots.go
index ec54062a96..bd52126636 100644
--- a/services/directoryservice/handler_snapshots.go
+++ b/services/directoryservice/handler_snapshots.go
@@ -1,12 +1,13 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateSnapshot(c *echo.Context) error {
@@ -20,7 +21,7 @@ func (h *Handler) handleCreateSnapshot(c *echo.Context) error {
Name string `json:"Name"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -38,6 +39,7 @@ func (h *Handler) handleCreateSnapshot(c *echo.Context) error {
})
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) handleDeleteSnapshot(c *echo.Context) error {
body, err := httputils.ReadBody(c.Request())
if err != nil {
@@ -48,7 +50,7 @@ func (h *Handler) handleDeleteSnapshot(c *echo.Context) error {
SnapshotID string `json:"SnapshotId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -79,7 +81,7 @@ func (h *Handler) handleDescribeSnapshots(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
@@ -120,7 +122,7 @@ func (h *Handler) handleGetSnapshotLimits(c *echo.Context) error {
DirectoryID string `json:"DirectoryId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -152,7 +154,7 @@ func (h *Handler) handleRestoreFromSnapshot(c *echo.Context) error {
SnapshotID string `json:"SnapshotId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
diff --git a/services/directoryservice/handler_snapshots_test.go b/services/directoryservice/handler_snapshots_test.go
index cf531f530e..1bb905fa7b 100644
--- a/services/directoryservice/handler_snapshots_test.go
+++ b/services/directoryservice/handler_snapshots_test.go
@@ -1,7 +1,7 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/directoryservice"
)
@@ -21,7 +22,7 @@ func TestDirectoryService_Snapshots(t *testing.T) {
"Name": "corp.example.com", "Password": "Admin1234!", "Size": "Small",
})
var resp map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
return resp["DirectoryId"].(string)
}
@@ -37,7 +38,7 @@ func TestDirectoryService_Snapshots(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
snapID, ok := resp["SnapshotId"].(string)
require.True(t, ok)
assert.NotEmpty(t, snapID)
@@ -56,7 +57,7 @@ func TestDirectoryService_Snapshots(t *testing.T) {
dirID := createDir(h)
snapRec := doRequest(t, h, "CreateSnapshot", map[string]any{"DirectoryId": dirID})
var snapResp map[string]any
- require.NoError(t, json.Unmarshal(snapRec.Body.Bytes(), &snapResp))
+ require.NoError(t, json.Unmarshal(snapRec.Body.Bytes(), &snapResp, jsonx.V1()))
snapID := snapResp["SnapshotId"].(string)
rec := doRequest(t, h, "DeleteSnapshot", map[string]any{"SnapshotId": snapID})
@@ -66,7 +67,7 @@ func TestDirectoryService_Snapshots(t *testing.T) {
descRec := doRequest(t, h, "DescribeSnapshots", map[string]any{"SnapshotIds": []string{snapID}})
assert.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
snaps := descResp["Snapshots"].([]any)
assert.Empty(t, snaps)
})
@@ -87,7 +88,7 @@ func TestDirectoryService_Snapshots(t *testing.T) {
"Name": "other.example.com", "Password": "Admin1234!", "Size": "Small",
})
var resp map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
return resp["DirectoryId"].(string)
}()
@@ -99,7 +100,7 @@ func TestDirectoryService_Snapshots(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
snaps := resp["Snapshots"].([]any)
assert.Len(t, snaps, 1)
})
@@ -114,7 +115,7 @@ func TestDirectoryService_Snapshots(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
limits := resp["SnapshotLimits"].(map[string]any)
assert.EqualValues(t, 1, limits["ManualSnapshotsCurrentCount"])
})
@@ -125,7 +126,7 @@ func TestDirectoryService_Snapshots(t *testing.T) {
dirID := createDir(h)
snapRec := doRequest(t, h, "CreateSnapshot", map[string]any{"DirectoryId": dirID})
var snapResp map[string]any
- require.NoError(t, json.Unmarshal(snapRec.Body.Bytes(), &snapResp))
+ require.NoError(t, json.Unmarshal(snapRec.Body.Bytes(), &snapResp, jsonx.V1()))
snapID := snapResp["SnapshotId"].(string)
rec := doRequest(t, h, "RestoreFromSnapshot", map[string]any{"SnapshotId": snapID})
@@ -155,11 +156,11 @@ func TestRestoreFromSnapshot_TableDriven(t *testing.T) {
"Name": "corp.example.com", "Password": "Admin1234!", "Size": "Small",
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
dirID := createResp["DirectoryId"].(string)
snapRec := doRequest(t, h, "CreateSnapshot", map[string]any{"DirectoryId": dirID})
var snapResp map[string]any
- require.NoError(t, json.Unmarshal(snapRec.Body.Bytes(), &snapResp))
+ require.NoError(t, json.Unmarshal(snapRec.Body.Bytes(), &snapResp, jsonx.V1()))
return snapResp["SnapshotId"].(string)
},
@@ -179,11 +180,11 @@ func TestRestoreFromSnapshot_TableDriven(t *testing.T) {
"Name": "corp.example.com", "Password": "Admin1234!", "Size": "Small",
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
dirID := createResp["DirectoryId"].(string)
snapRec := doRequest(t, h, "CreateSnapshot", map[string]any{"DirectoryId": dirID})
var snapResp map[string]any
- require.NoError(t, json.Unmarshal(snapRec.Body.Bytes(), &snapResp))
+ require.NoError(t, json.Unmarshal(snapRec.Body.Bytes(), &snapResp, jsonx.V1()))
snapID := snapResp["SnapshotId"].(string)
doRequest(t, h, "DeleteDirectory", map[string]any{"DirectoryId": dirID})
diff --git a/services/directoryservice/handler_sso.go b/services/directoryservice/handler_sso.go
index 6d866e829c..2486e4ef0b 100644
--- a/services/directoryservice/handler_sso.go
+++ b/services/directoryservice/handler_sso.go
@@ -1,12 +1,13 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleEnableSso(c *echo.Context) error {
@@ -19,7 +20,7 @@ func (h *Handler) handleEnableSso(c *echo.Context) error {
DirectoryID string `json:"DirectoryId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -44,7 +45,7 @@ func (h *Handler) handleDisableSso(c *echo.Context) error {
DirectoryID string `json:"DirectoryId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
diff --git a/services/directoryservice/handler_sso_test.go b/services/directoryservice/handler_sso_test.go
index da13dcb679..d35f0d25c9 100644
--- a/services/directoryservice/handler_sso_test.go
+++ b/services/directoryservice/handler_sso_test.go
@@ -1,12 +1,13 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/directoryservice"
)
@@ -18,7 +19,7 @@ func TestDirectoryService_SSO(t *testing.T) {
"Name": "corp.example.com", "Password": "Admin1234!", "Size": "Small",
})
var resp map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
return resp["DirectoryId"].(string)
}
diff --git a/services/directoryservice/handler_tags.go b/services/directoryservice/handler_tags.go
index a156333d8e..13a4192efe 100644
--- a/services/directoryservice/handler_tags.go
+++ b/services/directoryservice/handler_tags.go
@@ -1,12 +1,13 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleAddTagsToResource(c *echo.Context) error {
@@ -23,7 +24,7 @@ func (h *Handler) handleAddTagsToResource(c *echo.Context) error {
} `json:"Tags"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -51,7 +52,7 @@ func (h *Handler) handleRemoveTagsFromResource(c *echo.Context) error {
TagKeys []string `json:"TagKeys"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -79,7 +80,7 @@ func (h *Handler) handleListTagsForResource(c *echo.Context) error {
Limit int32 `json:"Limit"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
diff --git a/services/directoryservice/handler_tags_test.go b/services/directoryservice/handler_tags_test.go
index 40fbef0f9e..ff8ba23d1a 100644
--- a/services/directoryservice/handler_tags_test.go
+++ b/services/directoryservice/handler_tags_test.go
@@ -1,7 +1,7 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/directoryservice"
)
@@ -20,7 +21,7 @@ func TestDirectoryService_Tags(t *testing.T) {
"Name": "corp.example.com", "Password": "Admin1234!", "Size": "Small",
})
var resp map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
return resp["DirectoryId"].(string)
}
@@ -40,7 +41,7 @@ func TestDirectoryService_Tags(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags, ok := listResp["Tags"].([]any)
require.True(t, ok)
require.Len(t, tags, 1)
@@ -70,7 +71,7 @@ func TestDirectoryService_Tags(t *testing.T) {
listRec := doRequest(t, h, "ListTagsForResource", map[string]any{"ResourceId": dirID})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags := listResp["Tags"].([]any)
assert.Len(t, tags, 1)
assert.Equal(t, "team", tags[0].(map[string]any)["Key"])
@@ -96,12 +97,12 @@ func TestDirectoryService_Tags(t *testing.T) {
"Tags": []map[string]any{{"Key": "created", "Value": "yes"}},
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
dirID := createResp["DirectoryId"].(string)
listRec := doRequest(t, h, "ListTagsForResource", map[string]any{"ResourceId": dirID})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags := listResp["Tags"].([]any)
assert.Len(t, tags, 1)
assert.Equal(t, "created", tags[0].(map[string]any)["Key"])
diff --git a/services/directoryservice/handler_test.go b/services/directoryservice/handler_test.go
index ca8950b562..35cff313ff 100644
--- a/services/directoryservice/handler_test.go
+++ b/services/directoryservice/handler_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/directoryservice"
)
@@ -62,7 +63,7 @@ func doRequest(t *testing.T, h *directoryservice.Handler, op string, body any) *
if body != nil {
var marshalErr error
- bodyBytes, marshalErr = json.Marshal(body)
+ bodyBytes, marshalErr = json.Marshal(body, jsonx.V1())
require.NoError(t, marshalErr)
}
@@ -89,7 +90,7 @@ func mustCreateSimpleAD(t *testing.T, h *directoryservice.Handler, name string)
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id, ok := resp["DirectoryId"].(string)
require.True(t, ok)
require.NotEmpty(t, id)
@@ -107,7 +108,7 @@ func mustCreateMicrosoftAD(t *testing.T, h *directoryservice.Handler, name strin
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id, ok := resp["DirectoryId"].(string)
require.True(t, ok)
require.NotEmpty(t, id)
@@ -119,7 +120,7 @@ func mustCreateMicrosoftAD(t *testing.T, h *directoryservice.Handler, name strin
func respBody(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/directoryservice/handler_trusts.go b/services/directoryservice/handler_trusts.go
index d60c0a5c5e..44c189d5dd 100644
--- a/services/directoryservice/handler_trusts.go
+++ b/services/directoryservice/handler_trusts.go
@@ -1,7 +1,7 @@
package directoryservice
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/google/uuid"
@@ -9,6 +9,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateTrust(c *echo.Context) error {
@@ -29,7 +30,7 @@ func (h *Handler) handleCreateTrust(c *echo.Context) error {
ConditionalForwarderIPv6Addrs []string `json:"ConditionalForwarderIpv6Addrs"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -88,7 +89,7 @@ func (h *Handler) handleDeleteTrust(c *echo.Context) error {
DeleteAssociatedConditionalForwarder bool `json:"DeleteAssociatedConditionalForwarder"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -120,7 +121,7 @@ func (h *Handler) handleDescribeTrusts(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
}
@@ -179,7 +180,7 @@ func (h *Handler) handleUpdateTrust(c *echo.Context) error {
SelectiveAuth string `json:"SelectiveAuth"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
@@ -211,7 +212,7 @@ func (h *Handler) handleVerifyTrust(c *echo.Context) error {
TrustID string `json:"TrustId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errResp("ClientException", "invalid JSON"))
}
diff --git a/services/directoryservice/handler_trusts_test.go b/services/directoryservice/handler_trusts_test.go
index 0f035f6946..073dfbea56 100644
--- a/services/directoryservice/handler_trusts_test.go
+++ b/services/directoryservice/handler_trusts_test.go
@@ -1,7 +1,7 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/directoryservice"
)
@@ -240,7 +241,7 @@ func TestTrusts(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec1.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1, jsonx.V1()))
trustID, _ := r1["TrustId"].(string)
assert.NotEmpty(t, trustID)
@@ -248,7 +249,7 @@ func TestTrusts(t *testing.T) {
rec2 := doRequest(t, h, "DescribeTrusts", map[string]any{"DirectoryId": dirID})
assert.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
trusts, _ := r2["Trusts"].([]any)
require.Len(t, trusts, 1)
trust := trusts[0].(map[string]any)
@@ -261,27 +262,27 @@ func TestTrusts(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec3.Code)
var r3 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &r3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &r3, jsonx.V1()))
assert.Equal(t, trustID, r3["TrustId"])
// Verify
rec4 := doRequest(t, h, "VerifyTrust", map[string]any{"TrustId": trustID})
assert.Equal(t, http.StatusOK, rec4.Code)
var r4 map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &r4))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &r4, jsonx.V1()))
assert.Equal(t, trustID, r4["TrustId"])
// Delete
rec5 := doRequest(t, h, "DeleteTrust", map[string]any{"TrustId": trustID})
assert.Equal(t, http.StatusOK, rec5.Code)
var r5 map[string]any
- require.NoError(t, json.Unmarshal(rec5.Body.Bytes(), &r5))
+ require.NoError(t, json.Unmarshal(rec5.Body.Bytes(), &r5, jsonx.V1()))
assert.Equal(t, trustID, r5["TrustId"])
require.Eventually(t, func() bool {
var r6 map[string]any
rec6 := doRequest(t, h, "DescribeTrusts", map[string]any{"DirectoryId": dirID})
- require.NoError(t, json.Unmarshal(rec6.Body.Bytes(), &r6))
+ require.NoError(t, json.Unmarshal(rec6.Body.Bytes(), &r6, jsonx.V1()))
trusts2, _ := r6["Trusts"].([]any)
return rec6.Code == http.StatusOK && len(trusts2) == 0
diff --git a/services/directoryservice/id_format_test.go b/services/directoryservice/id_format_test.go
index d7846cba1c..579db14509 100644
--- a/services/directoryservice/id_format_test.go
+++ b/services/directoryservice/id_format_test.go
@@ -1,12 +1,14 @@
package directoryservice_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"regexp"
"testing"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// realIDPattern matches terraform-provider-aws's client-side ValidateFunc for
@@ -35,7 +37,7 @@ func TestIDFormats_MatchRealAWSPattern(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var snapResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &snapResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &snapResp, jsonx.V1()))
snapID, _ := snapResp["SnapshotId"].(string)
require.Regexp(t, realIDPattern, snapID, "snapshot ID")
@@ -48,7 +50,7 @@ func TestIDFormats_MatchRealAWSPattern(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var trustResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &trustResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &trustResp, jsonx.V1()))
trustID, _ := trustResp["TrustId"].(string)
require.Regexp(t, realIDPattern, trustID, "trust ID")
}
diff --git a/services/directoryservice/persistence.go b/services/directoryservice/persistence.go
index 3de6400285..34398ce177 100644
--- a/services/directoryservice/persistence.go
+++ b/services/directoryservice/persistence.go
@@ -2,9 +2,11 @@ package directoryservice
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -52,7 +54,7 @@ func regionalDTOKeyFn[V any](d *regionalDTO[V]) string { return regionKey(d.Regi
// incompatible (older or newer) build of this backend as though it were the
// current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Aliases map[string]map[string]string `json:"aliases"`
IPRoutes map[string]map[string][]storedIpRoute `json:"ipRoutes"`
DirDataAccess map[string]map[string]bool `json:"dirDataAccess"`
@@ -257,7 +259,7 @@ func unwrapRegionalDTOs[V any](dtos *store.Table[regionalDTO[V]], setRegion func
// restoreResourceTables rebuilds every store.Table on b from snap's
// per-table JSON, factored out of Restore to keep Restore's own cognitive
// complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
dto := buildPersistenceDTOTables()
if err := dto.registry.RestoreAll(tables); err != nil {
@@ -361,7 +363,7 @@ func (b *InMemoryBackend) BackendSnapshot() []byte {
Region: b.region,
}
- data, _ := json.Marshal(snap)
+ data, _ := json.Marshal(snap, jsonx.V1())
return data
}
diff --git a/services/dlm/handler.go b/services/dlm/handler.go
index 5d73ed9e93..4f25006a4b 100644
--- a/services/dlm/handler.go
+++ b/services/dlm/handler.go
@@ -2,7 +2,7 @@ package dlm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -294,7 +295,7 @@ func (h *Handler) handleCreateLifecyclePolicy(c *echo.Context, body []byte) erro
CrossRegionCopyTargets []any `json:"CrossRegionCopyTargets"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errBody(errInvalidRequest, "invalid request body"))
}
@@ -427,7 +428,7 @@ func (h *Handler) handleUpdateLifecyclePolicy(c *echo.Context, policyID string,
CrossRegionCopyTargets []any `json:"CrossRegionCopyTargets"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errBody(errInvalidRequest, "invalid request body"))
}
@@ -464,7 +465,7 @@ func (h *Handler) handleTagResource(c *echo.Context, resourceARN string, body []
Tags map[string]string `json:"Tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errBody(errInvalidRequest, "invalid request body"))
}
diff --git a/services/dlm/handler_lifecycle_policies_test.go b/services/dlm/handler_lifecycle_policies_test.go
index 93b56557de..b41a6e640f 100644
--- a/services/dlm/handler_lifecycle_policies_test.go
+++ b/services/dlm/handler_lifecycle_policies_test.go
@@ -2,7 +2,7 @@ package dlm_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dlm"
)
@@ -31,7 +32,7 @@ func createPolicyWithDetails(t *testing.T, h *dlm.Handler, description string, d
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policyID, ok := resp["PolicyId"].(string)
require.True(t, ok, "PolicyId missing from response")
@@ -43,7 +44,7 @@ func getPoliciesResp(t *testing.T, rec *httptest.ResponseRecorder) []map[string]
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
raw, ok := resp["Policies"].([]any)
require.True(t, ok)
@@ -241,7 +242,7 @@ func TestHandler_GetLifecyclePolicies_DeterministicOrder(t *testing.T) {
assert.JSONEq(t, firstRec.Body.String(), secondRec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(firstRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(firstRec.Body.Bytes(), &resp, jsonx.V1()))
policies, ok := resp["Policies"].([]any)
require.True(t, ok)
require.Len(t, policies, tc.count)
@@ -304,7 +305,7 @@ func TestHandler_UpdateLifecyclePolicy_PolicyDetails(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
policyID := createResp["PolicyId"].(string)
// Update with new PolicyDetails.
@@ -318,7 +319,7 @@ func TestHandler_UpdateLifecyclePolicy_PolicyDetails(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
policy := getResp["Policy"].(map[string]any)
details := policy["PolicyDetails"].(map[string]any)
assert.Equal(t, tc.wantType, details["PolicyType"])
@@ -374,7 +375,7 @@ func TestHandler_UpdateLifecyclePolicy_ExplicitEmptyVsOmitted(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
policyID := createResp["PolicyId"].(string)
patchRec := doRequest(t, h, http.MethodPatch, fmt.Sprintf("/policies/%s", policyID), tc.patchBody)
@@ -384,7 +385,7 @@ func TestHandler_UpdateLifecyclePolicy_ExplicitEmptyVsOmitted(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
policy := getResp["Policy"].(map[string]any)
assert.Equal(t, tc.wantDesc, policy["Description"])
assert.Equal(t, tc.wantRoleArn, policy["ExecutionRoleArn"])
@@ -483,14 +484,14 @@ func TestHandler_CreateLifecyclePolicy_DefaultPolicyFields(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
policyID := createResp["PolicyId"].(string)
getRec := doRequest(t, h, http.MethodGet, fmt.Sprintf("/policies/%s", policyID), nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
policy := getResp["Policy"].(map[string]any)
details := policy["PolicyDetails"].(map[string]any)
@@ -526,7 +527,7 @@ func TestHandler_UpdateLifecyclePolicy_DefaultPolicyFields(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
policyID := createResp["PolicyId"].(string)
patchRec := doRequest(t, h, http.MethodPatch, fmt.Sprintf("/policies/%s", policyID), map[string]any{
@@ -539,7 +540,7 @@ func TestHandler_UpdateLifecyclePolicy_DefaultPolicyFields(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
policy := getResp["Policy"].(map[string]any)
details := policy["PolicyDetails"].(map[string]any)
@@ -601,14 +602,14 @@ func TestHandler_GetLifecyclePolicy_DefaultPolicyEcho(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
policyID := createResp["PolicyId"].(string)
getRec := doRequest(t, h, http.MethodGet, fmt.Sprintf("/policies/%s", policyID), nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
policy := getResp["Policy"].(map[string]any)
assert.Equal(t, tc.want, policy["DefaultPolicy"])
@@ -682,7 +683,7 @@ func TestHandler_CreateLifecyclePolicy_LimitExceeded(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "LimitExceededException", out["__type"])
}
@@ -727,7 +728,7 @@ func TestHandler_CreateLifecyclePolicy_MissingRequiredFields(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", resp["__type"])
})
}
@@ -864,7 +865,7 @@ func TestHandler_GetLifecyclePolicies_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policies := resp["Policies"].([]any)
if tc.wantMaxLen > 0 || tc.wantMinLen > 0 {
diff --git a/services/dlm/handler_tags_test.go b/services/dlm/handler_tags_test.go
index 3cd238f9a1..edb34e9323 100644
--- a/services/dlm/handler_tags_test.go
+++ b/services/dlm/handler_tags_test.go
@@ -2,7 +2,7 @@ package dlm_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dlm"
)
@@ -46,7 +47,7 @@ func TestHandler_TagResource_InvalidBody(t *testing.T) {
// Get the ARN.
rec := doRequest(t, h, http.MethodGet, fmt.Sprintf("/policies/%s", policyID), nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp["Policy"].(map[string]any)["PolicyArn"].(string)
req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/tags/%s", arn), bytes.NewReader(tc.body))
@@ -89,7 +90,7 @@ func TestHandler_UntagResource_MultipleQueryKeys(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, fmt.Sprintf("/policies/%s", policyID), nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp["Policy"].(map[string]any)["PolicyArn"].(string)
// Tag first.
@@ -141,12 +142,12 @@ func TestHandler_TagResource_GovCloudPartitionMatcherRouted(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
policyARN := getResp["Policy"].(map[string]any)["PolicyArn"].(string)
require.Contains(t, policyARN, "arn:aws-us-gov:dlm:", "sanity: PolicyArn must carry the GovCloud partition")
tagPath := fmt.Sprintf("/tags/%s", policyARN)
- body, err := json.Marshal(map[string]any{"Tags": map[string]string{"env": "prod"}})
+ body, err := json.Marshal(map[string]any{"Tags": map[string]string{"env": "prod"}}, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, tagPath, bytes.NewReader(body))
@@ -164,7 +165,7 @@ func TestHandler_TagResource_GovCloudPartitionMatcherRouted(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags, ok := listResp["Tags"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "prod", tags["env"])
@@ -203,7 +204,7 @@ func TestHandler_UntagResource_URLEncodedTagKey(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, fmt.Sprintf("/policies/%s", policyID), nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp["Policy"].(map[string]any)["PolicyArn"].(string)
doRequest(t, h, http.MethodPost, fmt.Sprintf("/tags/%s", arn), map[string]any{
@@ -228,7 +229,7 @@ func TestHandler_UntagResource_URLEncodedTagKey(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags, ok := listResp["Tags"].(map[string]any)
require.True(t, ok)
assert.NotContains(t, tags, tc.tagKey, "percent-decoded tag key must be removed")
diff --git a/services/dlm/handler_test.go b/services/dlm/handler_test.go
index 460b11e078..2d539a1813 100644
--- a/services/dlm/handler_test.go
+++ b/services/dlm/handler_test.go
@@ -2,7 +2,7 @@ package dlm_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dlm"
)
@@ -30,7 +31,7 @@ func doRequest(t *testing.T, h *dlm.Handler, method, path string, body any) *htt
if body != nil {
var marshalErr error
- bodyBytes, marshalErr = json.Marshal(body)
+ bodyBytes, marshalErr = json.Marshal(body, jsonx.V1())
require.NoError(t, marshalErr)
}
@@ -57,7 +58,7 @@ func createPolicy(t *testing.T, h *dlm.Handler) string {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policyID, ok := resp["PolicyId"].(string)
require.True(t, ok, "PolicyId missing from response")
@@ -90,7 +91,7 @@ func TestDLM_LifecyclePolicy(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
assert.Contains(t, resp["PolicyId"], "policy-")
},
},
@@ -105,7 +106,7 @@ func TestDLM_LifecyclePolicy(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
policies, ok := resp["Policies"].([]any)
require.True(t, ok)
assert.Len(t, policies, 1)
@@ -124,7 +125,7 @@ func TestDLM_LifecyclePolicy(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
policy, ok := resp["Policy"].(map[string]any)
require.True(t, ok)
assert.Contains(t, policy["PolicyId"], "policy-")
@@ -220,7 +221,7 @@ func TestDLM_Tags(t *testing.T) {
policyID := createPolicy(t, h)
rec := doRequest(t, h, http.MethodGet, fmt.Sprintf("/policies/%s", policyID), nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policy := resp["Policy"].(map[string]any)
arn := policy["PolicyArn"].(string)
@@ -239,7 +240,7 @@ func TestDLM_Tags(t *testing.T) {
policyID := createPolicy(t, h)
rec := doRequest(t, h, http.MethodGet, fmt.Sprintf("/policies/%s", policyID), nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policy := resp["Policy"].(map[string]any)
arn := policy["PolicyArn"].(string)
doRequest(t, h, http.MethodPost, fmt.Sprintf("/tags/%s", arn), map[string]any{
@@ -255,7 +256,7 @@ func TestDLM_Tags(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
tags, ok := resp["Tags"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "prod", tags["env"])
@@ -268,7 +269,7 @@ func TestDLM_Tags(t *testing.T) {
policyID := createPolicy(t, h)
rec := doRequest(t, h, http.MethodGet, fmt.Sprintf("/policies/%s", policyID), nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policy := resp["Policy"].(map[string]any)
arn := policy["PolicyArn"].(string)
doRequest(t, h, http.MethodPost, fmt.Sprintf("/tags/%s", arn), map[string]any{
@@ -359,7 +360,7 @@ func TestHandler_Reset(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policies := resp["Policies"].([]any)
assert.Empty(t, policies)
})
@@ -585,7 +586,7 @@ func TestHandler_MapError_InternalError(t *testing.T) {
var bodyBytes []byte
if tc.method == http.MethodPost {
- bodyBytes, _ = json.Marshal(map[string]any{"Tags": map[string]string{"k": "v"}})
+ bodyBytes, _ = json.Marshal(map[string]any{"Tags": map[string]string{"k": "v"}}, jsonx.V1())
}
req := httptest.NewRequest(tc.method, tc.path, bytes.NewReader(bodyBytes))
@@ -599,7 +600,7 @@ func TestHandler_MapError_InternalError(t *testing.T) {
assert.Equal(t, tc.wantCode, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantType, resp["__type"])
})
}
diff --git a/services/dlm/persistence.go b/services/dlm/persistence.go
index 1bb2b1bd8e..da0faec0d2 100644
--- a/services/dlm/persistence.go
+++ b/services/dlm/persistence.go
@@ -2,7 +2,7 @@ package dlm
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"math"
"strconv"
@@ -33,9 +33,9 @@ const dlmSnapshotVersion = 1
// (older or newer) build of this backend as though it were the current
// shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Counter int `json:"counter"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Counter int `json:"counter"`
+ Version int `json:"version"`
}
// Snapshot serializes the backend state to JSON.
diff --git a/services/dlm/persistence_test.go b/services/dlm/persistence_test.go
index a158965a93..0e83b4d51e 100644
--- a/services/dlm/persistence_test.go
+++ b/services/dlm/persistence_test.go
@@ -2,12 +2,14 @@ package dlm_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dlm"
)
@@ -307,8 +309,8 @@ func TestPersistence_CurrentVersionRoundTripsThroughJSON(t *testing.T) {
snap := b.Snapshot(context.Background())
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &raw, jsonx.V1()))
_, hasVersion := raw["version"]
assert.True(t, hasVersion, "snapshot must carry a top-level version field")
@@ -319,8 +321,8 @@ func TestPersistence_CurrentVersionRoundTripsThroughJSON(t *testing.T) {
_, hasCounter := raw["counter"]
assert.True(t, hasCounter, "snapshot must carry a top-level counter field")
- var tables map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(raw["tables"], &tables))
+ var tables map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(raw["tables"], &tables, jsonx.V1()))
_, hasPoliciesTable := tables["policies"]
assert.True(
diff --git a/services/dms/data_providers.go b/services/dms/data_providers.go
index df816c9b90..e489098965 100644
--- a/services/dms/data_providers.go
+++ b/services/dms/data_providers.go
@@ -2,7 +2,8 @@ package dms
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"time"
@@ -10,6 +11,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -226,8 +228,9 @@ func mergeDataProviderSettings(current, incoming string, exact bool) string {
return incoming
}
- var cur, inc map[string]map[string]json.RawMessage
- if json.Unmarshal([]byte(current), &cur) != nil || json.Unmarshal([]byte(incoming), &inc) != nil {
+ var cur, inc map[string]map[string]jsontext.Value
+ if json.Unmarshal([]byte(current), &cur, jsonx.V1()) != nil ||
+ json.Unmarshal([]byte(incoming), &inc, jsonx.V1()) != nil {
return incoming
}
@@ -240,7 +243,7 @@ func mergeDataProviderSettings(current, incoming string, exact bool) string {
maps.Copy(curFields, fields)
}
- out, err := json.Marshal(cur)
+ out, err := json.Marshal(cur, jsonx.V1())
if err != nil {
return incoming
}
diff --git a/services/dms/endpoints.go b/services/dms/endpoints.go
index 77c4763f2a..e8d68715eb 100644
--- a/services/dms/endpoints.go
+++ b/services/dms/endpoints.go
@@ -2,7 +2,8 @@ package dms
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"time"
@@ -10,6 +11,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -419,14 +421,15 @@ func mergeEndpointSettings(current, incoming string, exact bool) string {
return incoming
}
- var cur, inc map[string]json.RawMessage
- if json.Unmarshal([]byte(current), &cur) != nil || json.Unmarshal([]byte(incoming), &inc) != nil {
+ var cur, inc map[string]jsontext.Value
+ if json.Unmarshal([]byte(current), &cur, jsonx.V1()) != nil ||
+ json.Unmarshal([]byte(incoming), &inc, jsonx.V1()) != nil {
return incoming
}
maps.Copy(cur, inc)
- out, err := json.Marshal(cur)
+ out, err := json.Marshal(cur, jsonx.V1())
if err != nil {
return incoming
}
diff --git a/services/dms/engine.go b/services/dms/engine.go
index 52b3305115..da381f970f 100644
--- a/services/dms/engine.go
+++ b/services/dms/engine.go
@@ -4,13 +4,14 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"sync"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/dmsengine"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -153,7 +154,7 @@ func endpointConfig(ep *Endpoint) dmsengine.EndpointConfig {
Port int `json:"Port"`
}
- if json.Unmarshal([]byte(raw), &s) == nil && s.ServerName != "" {
+ if json.Unmarshal([]byte(raw), &s, jsonx.V1()) == nil && s.ServerName != "" {
cfg.Server, cfg.Port, cfg.Database = s.ServerName, s.Port, s.DatabaseName
cfg.User, cfg.Password = s.Username, s.Password
diff --git a/services/dms/handler.go b/services/dms/handler.go
index 01e9a6fcbd..2cd5e1f11a 100644
--- a/services/dms/handler.go
+++ b/services/dms/handler.go
@@ -2,7 +2,7 @@ package dms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -14,6 +14,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
@@ -332,7 +333,7 @@ func extractField(c *echo.Context, keys ...string) string {
}
var raw map[string]string
- if unmarshalErr := json.Unmarshal(bodyBytes, &raw); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(bodyBytes, &raw, jsonx.V1()); unmarshalErr != nil {
return ""
}
@@ -380,7 +381,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
diff --git a/services/dms/handler_assessment_runs_test.go b/services/dms/handler_assessment_runs_test.go
index bc5d16b60e..50239ccc3e 100644
--- a/services/dms/handler_assessment_runs_test.go
+++ b/services/dms/handler_assessment_runs_test.go
@@ -1,7 +1,7 @@
package dms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dms"
)
@@ -150,7 +151,7 @@ func TestStartReplicationTaskAssessment(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundFault", body["__type"])
})
@@ -250,7 +251,7 @@ func TestStartReplicationTaskAssessment(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidResourceStateFault", body["__type"])
})
@@ -274,7 +275,7 @@ func TestStartReplicationTaskAssessment(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidResourceStateFault", body["__type"])
})
}
diff --git a/services/dms/handler_data_providers.go b/services/dms/handler_data_providers.go
index 2d82f1ef47..ae82969d8d 100644
--- a/services/dms/handler_data_providers.go
+++ b/services/dms/handler_data_providers.go
@@ -2,30 +2,32 @@ package dms
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"sort"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
type createDataProviderInput struct {
- DataProviderName *string `json:"DataProviderName"`
- Engine *string `json:"Engine"`
- Description *string `json:"Description"`
- Settings json.RawMessage `json:"Settings"`
- Virtual *bool `json:"Virtual"`
- Tags []tagEntry `json:"Tags"`
+ DataProviderName *string `json:"DataProviderName"`
+ Engine *string `json:"Engine"`
+ Description *string `json:"Description"`
+ Settings jsontext.Value `json:"Settings"`
+ Virtual *bool `json:"Virtual"`
+ Tags []tagEntry `json:"Tags"`
}
type dataProviderJSON struct {
- DataProviderName string `json:"DataProviderName"`
- DataProviderArn string `json:"DataProviderArn"`
- Engine string `json:"Engine"`
- Description string `json:"Description,omitempty"`
- Settings json.RawMessage `json:"Settings,omitempty"`
- Virtual bool `json:"Virtual,omitempty"`
+ DataProviderName string `json:"DataProviderName"`
+ DataProviderArn string `json:"DataProviderArn"`
+ Engine string `json:"Engine"`
+ Description string `json:"Description,omitempty"`
+ Settings jsontext.Value `json:"Settings,omitempty"`
+ Virtual bool `json:"Virtual,omitempty"`
}
type createDataProviderOutput struct {
@@ -80,13 +82,13 @@ func isDataProviderSettingsArm(arm string) bool {
// validateDataProviderSettings checks that a supplied Settings document is a
// union with exactly one known member and returns it as a string.
-func validateDataProviderSettings(raw json.RawMessage) (string, error) {
+func validateDataProviderSettings(raw jsontext.Value) (string, error) {
if len(raw) == 0 || string(raw) == "null" {
return "", nil
}
- var doc map[string]json.RawMessage
- if err := json.Unmarshal(raw, &doc); err != nil || len(doc) != 1 {
+ var doc map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &doc, jsonx.V1()); err != nil || len(doc) != 1 {
return "", fmt.Errorf("%w: Settings must be a union with exactly one member", ErrValidation)
}
@@ -108,7 +110,7 @@ func dpToJSON(dp *DataProvider) dataProviderJSON {
Virtual: dp.Virtual,
}
if dp.Settings != "" {
- out.Settings = json.RawMessage(dp.Settings)
+ out.Settings = jsontext.Value(dp.Settings)
}
return out
@@ -173,13 +175,13 @@ func (h *Handler) handleDescribeDataProviders(
}
type modifyDataProviderInput struct {
- DataProviderIdentifier *string `json:"DataProviderIdentifier"`
- DataProviderName *string `json:"DataProviderName"`
- Engine *string `json:"Engine"`
- Description *string `json:"Description"`
- Virtual *bool `json:"Virtual"`
- ExactSettings *bool `json:"ExactSettings"`
- Settings json.RawMessage `json:"Settings"`
+ DataProviderIdentifier *string `json:"DataProviderIdentifier"`
+ DataProviderName *string `json:"DataProviderName"`
+ Engine *string `json:"Engine"`
+ Description *string `json:"Description"`
+ Virtual *bool `json:"Virtual"`
+ ExactSettings *bool `json:"ExactSettings"`
+ Settings jsontext.Value `json:"Settings"`
}
type modifyDataProviderOutput struct {
diff --git a/services/dms/handler_data_providers_test.go b/services/dms/handler_data_providers_test.go
index fdbe2237cd..eb884557a6 100644
--- a/services/dms/handler_data_providers_test.go
+++ b/services/dms/handler_data_providers_test.go
@@ -1,7 +1,7 @@
package dms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dms"
)
@@ -125,7 +126,7 @@ func TestDeleteDataProvider_NotFound(t *testing.T) {
assert.Equal(t, http.StatusNotFound, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundFault", body["__type"])
}
diff --git a/services/dms/handler_endpoints.go b/services/dms/handler_endpoints.go
index 83db2ccf75..65db07f692 100644
--- a/services/dms/handler_endpoints.go
+++ b/services/dms/handler_endpoints.go
@@ -2,13 +2,15 @@ package dms
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"sort"
"sync"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -17,25 +19,25 @@ import (
// CreateEndpoint/ModifyEndpoint (api_op_CreateEndpoint.go, databasemigrationservice@v1.66.4).
// Blocks are stored as raw JSON and echoed back; no engine consumes them.
type engineSettingsFields struct {
- DmsTransferSettings json.RawMessage `json:"DmsTransferSettings,omitempty"`
- DocDBSettings json.RawMessage `json:"DocDbSettings,omitempty"`
- DynamoDBSettings json.RawMessage `json:"DynamoDbSettings,omitempty"`
- ElasticsearchSettings json.RawMessage `json:"ElasticsearchSettings,omitempty"`
- GcpMySQLSettings json.RawMessage `json:"GcpMySQLSettings,omitempty"`
- IBMDb2Settings json.RawMessage `json:"IBMDb2Settings,omitempty"`
- KafkaSettings json.RawMessage `json:"KafkaSettings,omitempty"`
- KinesisSettings json.RawMessage `json:"KinesisSettings,omitempty"`
- MicrosoftSQLServerSettings json.RawMessage `json:"MicrosoftSQLServerSettings,omitempty"`
- MongoDBSettings json.RawMessage `json:"MongoDbSettings,omitempty"`
- MySQLSettings json.RawMessage `json:"MySQLSettings,omitempty"`
- NeptuneSettings json.RawMessage `json:"NeptuneSettings,omitempty"`
- OracleSettings json.RawMessage `json:"OracleSettings,omitempty"`
- PostgreSQLSettings json.RawMessage `json:"PostgreSQLSettings,omitempty"`
- RedisSettings json.RawMessage `json:"RedisSettings,omitempty"`
- RedshiftSettings json.RawMessage `json:"RedshiftSettings,omitempty"`
- S3Settings json.RawMessage `json:"S3Settings,omitempty"`
- SybaseSettings json.RawMessage `json:"SybaseSettings,omitempty"`
- TimestreamSettings json.RawMessage `json:"TimestreamSettings,omitempty"`
+ DmsTransferSettings jsontext.Value `json:"DmsTransferSettings,omitempty"`
+ DocDBSettings jsontext.Value `json:"DocDbSettings,omitempty"`
+ DynamoDBSettings jsontext.Value `json:"DynamoDbSettings,omitempty"`
+ ElasticsearchSettings jsontext.Value `json:"ElasticsearchSettings,omitempty"`
+ GcpMySQLSettings jsontext.Value `json:"GcpMySQLSettings,omitempty"`
+ IBMDb2Settings jsontext.Value `json:"IBMDb2Settings,omitempty"`
+ KafkaSettings jsontext.Value `json:"KafkaSettings,omitempty"`
+ KinesisSettings jsontext.Value `json:"KinesisSettings,omitempty"`
+ MicrosoftSQLServerSettings jsontext.Value `json:"MicrosoftSQLServerSettings,omitempty"`
+ MongoDBSettings jsontext.Value `json:"MongoDbSettings,omitempty"`
+ MySQLSettings jsontext.Value `json:"MySQLSettings,omitempty"`
+ NeptuneSettings jsontext.Value `json:"NeptuneSettings,omitempty"`
+ OracleSettings jsontext.Value `json:"OracleSettings,omitempty"`
+ PostgreSQLSettings jsontext.Value `json:"PostgreSQLSettings,omitempty"`
+ RedisSettings jsontext.Value `json:"RedisSettings,omitempty"`
+ RedshiftSettings jsontext.Value `json:"RedshiftSettings,omitempty"`
+ S3Settings jsontext.Value `json:"S3Settings,omitempty"`
+ SybaseSettings jsontext.Value `json:"SybaseSettings,omitempty"`
+ TimestreamSettings jsontext.Value `json:"TimestreamSettings,omitempty"`
}
// endpointSecretKeys are the credential members inside the engine settings
@@ -46,16 +48,16 @@ var endpointSecretKeys = []string{ //nolint:gochecknoglobals // immutable lookup
// rawIsSet reports whether a json.RawMessage field was present in the request
// body with a non-null value.
-func rawIsSet(raw json.RawMessage) bool {
+func rawIsSet(raw jsontext.Value) bool {
return len(raw) > 0 && string(raw) != "null"
}
func (f *engineSettingsFields) entries() []struct {
- raw *json.RawMessage
+ raw *jsontext.Value
name string
} {
return []struct {
- raw *json.RawMessage
+ raw *jsontext.Value
name string
}{
{&f.DmsTransferSettings, "DmsTransferSettings"},
@@ -89,8 +91,8 @@ func (f *engineSettingsFields) otherSettings() (map[string]string, error) {
continue
}
- var obj map[string]json.RawMessage
- if err := json.Unmarshal(*e.raw, &obj); err != nil {
+ var obj map[string]jsontext.Value
+ if err := json.Unmarshal(*e.raw, &obj, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %s must be an object", ErrValidation, e.name)
}
@@ -113,19 +115,19 @@ func (f *engineSettingsFields) apply(stored map[string]string) {
}
}
-func redactEndpointSecrets(raw string) json.RawMessage {
- var obj map[string]json.RawMessage
- if json.Unmarshal([]byte(raw), &obj) != nil {
- return json.RawMessage(raw)
+func redactEndpointSecrets(raw string) jsontext.Value {
+ var obj map[string]jsontext.Value
+ if json.Unmarshal([]byte(raw), &obj, jsonx.V1()) != nil {
+ return jsontext.Value(raw)
}
for _, k := range endpointSecretKeys {
delete(obj, k)
}
- out, err := json.Marshal(obj)
+ out, err := json.Marshal(obj, jsonx.V1())
if err != nil {
- return json.RawMessage(raw)
+ return jsontext.Value(raw)
}
return out
@@ -376,7 +378,7 @@ func epToJSON(ep *Endpoint) endpointJSON {
}
if ep.S3Settings != "" {
- out.S3Settings = json.RawMessage(ep.S3Settings)
+ out.S3Settings = jsontext.Value(ep.S3Settings)
}
out.apply(ep.EngineSettings)
diff --git a/services/dms/handler_endpoints_test.go b/services/dms/handler_endpoints_test.go
index c62db0e2ff..ad4af382b5 100644
--- a/services/dms/handler_endpoints_test.go
+++ b/services/dms/handler_endpoints_test.go
@@ -1,7 +1,7 @@
package dms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dms"
)
@@ -378,7 +379,7 @@ func TestDeleteEndpoint_RejectsIfInUse(t *testing.T) {
require.Equal(t, http.StatusBadRequest, delRec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidResourceStateFault", body["__type"],
"deleting an endpoint used by a task must return InvalidResourceStateFault")
})
diff --git a/services/dms/handler_event_subscriptions_test.go b/services/dms/handler_event_subscriptions_test.go
index 0e53c48675..99d77b9a1a 100644
--- a/services/dms/handler_event_subscriptions_test.go
+++ b/services/dms/handler_event_subscriptions_test.go
@@ -1,13 +1,14 @@
package dms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dms"
)
@@ -65,7 +66,7 @@ func TestCreateEventSubscription_Duplicate(t *testing.T) {
require.Equal(t, http.StatusConflict, rec2.Code)
var errBody map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "ResourceAlreadyExistsFault", errBody["__type"])
}
diff --git a/services/dms/handler_replication_configs.go b/services/dms/handler_replication_configs.go
index 562a3424e7..1a5a83666e 100644
--- a/services/dms/handler_replication_configs.go
+++ b/services/dms/handler_replication_configs.go
@@ -2,12 +2,13 @@ package dms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -449,7 +450,7 @@ func validatePremigrationAssessmentSettings(raw string) error {
FailOnAssessmentFailure *bool `json:"FailOnAssessmentFailure"`
}
- if err := json.Unmarshal([]byte(raw), &settings); err != nil {
+ if err := json.Unmarshal([]byte(raw), &settings, jsonx.V1()); err != nil {
return fmt.Errorf("%w: PremigrationAssessmentSettings is not a valid settings object: %w", ErrValidation, err)
}
diff --git a/services/dms/handler_replication_tasks.go b/services/dms/handler_replication_tasks.go
index c752128118..feecd090c0 100644
--- a/services/dms/handler_replication_tasks.go
+++ b/services/dms/handler_replication_tasks.go
@@ -2,12 +2,13 @@ package dms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/dmsengine"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -402,7 +403,7 @@ func buildTableStatistics(tableMappings string) []tableStatisticJSON {
}
var doc tableMappingsDoc
- if err := json.Unmarshal([]byte(tableMappings), &doc); err != nil {
+ if err := json.Unmarshal([]byte(tableMappings), &doc, jsonx.V1()); err != nil {
return []tableStatisticJSON{}
}
diff --git a/services/dms/handler_replication_tasks_test.go b/services/dms/handler_replication_tasks_test.go
index 1ee271336a..311f4139df 100644
--- a/services/dms/handler_replication_tasks_test.go
+++ b/services/dms/handler_replication_tasks_test.go
@@ -1,7 +1,7 @@
package dms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dms"
)
@@ -191,7 +192,7 @@ func TestCreateReplicationTask_ARNValidation(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundFault", body["__type"],
"non-existent ARN in CreateReplicationTask must return ResourceNotFoundFault")
})
@@ -227,7 +228,7 @@ func TestCreateReplicationTask_InvalidMigrationType(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"],
"invalid MigrationType must return ValidationException")
})
@@ -306,7 +307,7 @@ func TestStopReplicationTask_NotRunning(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidResourceStateFault", body["__type"],
"stopping a non-running task must return InvalidResourceStateFault")
})
diff --git a/services/dms/handler_test.go b/services/dms/handler_test.go
index 3ae1e74a6a..5bd5257e63 100644
--- a/services/dms/handler_test.go
+++ b/services/dms/handler_test.go
@@ -2,7 +2,7 @@ package dms_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dms"
)
@@ -24,7 +25,7 @@ func newTestDMSHandler() *dms.Handler {
func doDMS(t *testing.T, h *dms.Handler, action string, body any) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -45,7 +46,7 @@ func parseJSON(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
@@ -266,7 +267,7 @@ func TestHandler_ExtractResource(t *testing.T) {
t.Parallel()
h := newTestDMSHandler()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -733,7 +734,7 @@ func TestValidationException_NotInvalidResourceStateFault(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
errType, ok := body["__type"].(string)
require.True(t, ok, "response must have __type field")
@@ -800,7 +801,7 @@ func TestInvalidResourceStateFault_ForStateErrors(t *testing.T) {
require.Equal(t, http.StatusBadRequest, startAgainRec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(startAgainRec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(startAgainRec.Body.Bytes(), &body, jsonx.V1()))
errType, ok := body["__type"].(string)
require.True(t, ok, "response must have __type field")
diff --git a/services/dms/persistence.go b/services/dms/persistence.go
index 25e0ac2f72..59fd28f000 100644
--- a/services/dms/persistence.go
+++ b/services/dms/persistence.go
@@ -2,9 +2,11 @@ package dms
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -43,7 +45,7 @@ const dmsSnapshotVersion = 1
// non-serializable live fields. ResourceTags is the side table that
// survives what Tables can't carry -- see reinitTagsLocked.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ResourceTags map[string]map[string]string `json:"resourceTags,omitempty"`
AccountID string `json:"accountID"`
Region string `json:"region"`
@@ -70,7 +72,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.region,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "dms: Snapshot marshal failure", "error", err)
diff --git a/services/dms/validation.go b/services/dms/validation.go
index 0c5f1b3834..622b8e61ac 100644
--- a/services/dms/validation.go
+++ b/services/dms/validation.go
@@ -1,11 +1,13 @@
package dms
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"regexp"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -45,7 +47,7 @@ func validateReplicationInstanceInput(identifier, class string, allocatedStorage
}
func validateTableMappings(mappings string) error {
- if mappings != "" && !json.Valid([]byte(mappings)) {
+ if mappings != "" && !jsontext.Value([]byte(mappings)).IsValid(jsonx.V1()) {
return fmt.Errorf("%w: TableMappings must be valid JSON", ErrValidation)
}
@@ -59,7 +61,7 @@ func (h *Handler) checkMarker(body []byte) error {
NextToken *string `json:"NextToken"`
}
- if json.Unmarshal(body, &in) != nil {
+ if json.Unmarshal(body, &in, jsonx.V1()) != nil {
return nil //nolint:nilerr // malformed bodies are rejected by the op decoder
}
diff --git a/services/docdb/master_secret.go b/services/docdb/master_secret.go
index 1ad5f23abe..f7ed329317 100644
--- a/services/docdb/master_secret.go
+++ b/services/docdb/master_secret.go
@@ -3,13 +3,14 @@ package docdb
import (
"crypto/rand"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/url"
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -40,7 +41,7 @@ func masterSecretBody(user string) (string, string, error) {
password := hex.EncodeToString(raw)
- body, err := json.Marshal(map[string]string{"username": user, "password": password})
+ body, err := json.Marshal(map[string]string{"username": user, "password": password}, jsonx.V1())
if err != nil {
return "", "", fmt.Errorf("encode master user secret: %w", err)
}
diff --git a/services/docdb/persistence.go b/services/docdb/persistence.go
index 8b372b82eb..27f03718ce 100644
--- a/services/docdb/persistence.go
+++ b/services/docdb/persistence.go
@@ -2,7 +2,7 @@ package docdb
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -52,7 +52,7 @@ func regionalDTOKeyFn[V any](d *regionalDTO[V]) string { return regionKey(d.Regi
// guards against decoding a snapshot from an incompatible (older or newer)
// build of this backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string][]Tag `json:"tags"`
EventsLog map[string][]Event `json:"eventsLog"`
PendingMaintenanceActions map[string]map[string]PendingMaintenanceAction `json:"pendingMaintenanceActions"`
@@ -252,7 +252,7 @@ func unwrapRegionalDTOs[V any](dtos *store.Table[regionalDTO[V]], setRegion func
// restoreResourceTables rebuilds every store.Table on b from snap's
// per-table JSON, factored out of Restore to keep Restore's own cognitive
// complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
dtoReg, clusterDTOs, instanceDTOs, subnetGroupDTOs, clusterParameterGroupDTOs,
clusterSnapshotDTOs, eventSubscriptionDTOs, snapshotAttributesDTOs,
globalClusterDTOs := buildPersistenceDTORegistry()
diff --git a/services/dsql/handler_clusters.go b/services/dsql/handler_clusters.go
index f5a1383f95..953b54e073 100644
--- a/services/dsql/handler_clusters.go
+++ b/services/dsql/handler_clusters.go
@@ -2,11 +2,13 @@ package dsql
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// dispatchClusterOps handles cluster CRUD and vpc-endpoint-service-name operations.
@@ -37,7 +39,7 @@ func (h *Handler) dispatchClusterOps(
func (h *Handler) handleCreateCluster(ctx context.Context, c *echo.Context, body []byte) error {
var req createClusterRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeInvalidBody(c)
}
}
@@ -98,7 +100,7 @@ func (h *Handler) handleListClusters(c *echo.Context) error {
func (h *Handler) handleUpdateCluster(c *echo.Context, identifier string, body []byte) error {
var req updateClusterRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeInvalidBody(c)
}
}
diff --git a/services/dsql/handler_policy.go b/services/dsql/handler_policy.go
index 1b12f264e9..3eb6f8d5da 100644
--- a/services/dsql/handler_policy.go
+++ b/services/dsql/handler_policy.go
@@ -1,10 +1,12 @@
package dsql
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// dispatchPolicyOps handles cluster resource-policy operations.
@@ -32,7 +34,7 @@ func (h *Handler) handleGetClusterPolicy(c *echo.Context, identifier string) err
func (h *Handler) handlePutClusterPolicy(c *echo.Context, identifier string, body []byte) error {
var req putClusterPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeInvalidBody(c)
}
diff --git a/services/dsql/handler_streams.go b/services/dsql/handler_streams.go
index 7622ba5a3f..0f21015838 100644
--- a/services/dsql/handler_streams.go
+++ b/services/dsql/handler_streams.go
@@ -1,11 +1,13 @@
package dsql
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// dispatchStreamOps handles cluster change-data-capture stream operations.
@@ -26,7 +28,7 @@ func (h *Handler) dispatchStreamOps(c *echo.Context, op, resource string, body [
func (h *Handler) handleCreateStream(c *echo.Context, clusterIdentifier string, body []byte) error {
var req createStreamRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeInvalidBody(c)
}
diff --git a/services/dsql/handler_tags.go b/services/dsql/handler_tags.go
index 16c71a726c..e34eec5a6f 100644
--- a/services/dsql/handler_tags.go
+++ b/services/dsql/handler_tags.go
@@ -1,10 +1,12 @@
package dsql
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// dispatchTagOps handles the generic ARN-scoped tagging trio.
@@ -23,7 +25,7 @@ func (h *Handler) dispatchTagOps(c *echo.Context, op, resource string, body []by
func (h *Handler) handleTagResource(c *echo.Context, resourceARN string, body []byte) error {
var req tagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeInvalidBody(c)
}
diff --git a/services/dsql/persistence.go b/services/dsql/persistence.go
index f5c26dac25..0ff65d6a43 100644
--- a/services/dsql/persistence.go
+++ b/services/dsql/persistence.go
@@ -2,7 +2,7 @@ package dsql
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
@@ -29,8 +29,8 @@ const dsqlSnapshotVersion = 1
// holds one JSON-encoded array per registered table name (clusters, streams
// -- see store_setup.go), produced by b.registry.SnapshotAll().
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serializes backend state to JSON.
diff --git a/services/dsql/validation.go b/services/dsql/validation.go
index 14f414ab05..a6f7dbacbd 100644
--- a/services/dsql/validation.go
+++ b/services/dsql/validation.go
@@ -1,7 +1,7 @@
package dsql
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -46,7 +47,7 @@ func (b *InMemoryBackend) dwell(def time.Duration) time.Duration {
func validatePolicyDocument(policy string) error {
var doc map[string]any
- if policy == "" || json.Unmarshal([]byte(policy), &doc) != nil || doc == nil {
+ if policy == "" || json.Unmarshal([]byte(policy), &doc, jsonx.V1()) != nil || doc == nil {
return fmt.Errorf("%w: policy must be a valid JSON policy document", ErrValidation)
}
diff --git a/services/dynamodb/backup_interface_test.go b/services/dynamodb/backup_interface_test.go
index e695473ac5..c1b3217230 100644
--- a/services/dynamodb/backup_interface_test.go
+++ b/services/dynamodb/backup_interface_test.go
@@ -2,7 +2,7 @@ package dynamodb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"log/slog"
"net/http"
@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
)
@@ -645,7 +646,7 @@ func TestHandler_TagOperations_Via_Handler(t *testing.T) {
doHandlerReq := func(target string, payload any) *httptest.ResponseRecorder {
t.Helper()
- body, marshalErr := json.Marshal(payload)
+ body, marshalErr := json.Marshal(payload, jsonx.V1())
require.NoError(t, marshalErr)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
req.Header.Set("X-Amz-Target", target)
@@ -689,7 +690,7 @@ func TestHandler_TableMutationOps_Via_Handler(t *testing.T) {
doHandlerReq := func(target string, payload any) *httptest.ResponseRecorder {
t.Helper()
- body, marshalErr := json.Marshal(payload)
+ body, marshalErr := json.Marshal(payload, jsonx.V1())
require.NoError(t, marshalErr)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
req.Header.Set("X-Amz-Target", target)
@@ -732,7 +733,7 @@ func TestHandler_UpdateTable_Via_Handler(t *testing.T) {
body, err := json.Marshal(map[string]any{
"TableName": "UpdateMe",
"BillingMode": "PAY_PER_REQUEST",
- })
+ }, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
@@ -783,7 +784,7 @@ func TestHandler_RestoreTableToPointInTime(t *testing.T) {
"PointInTimeRecoverySpecification": map[string]any{
"PointInTimeRecoveryEnabled": true,
},
- })
+ }, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
req.Header.Set("X-Amz-Target", "DynamoDB_20120810.UpdateContinuousBackups")
req.Header.Set("Content-Type", "application/x-amz-json-1.0")
@@ -813,7 +814,7 @@ func TestHandler_RestoreTableToPointInTime(t *testing.T) {
tt.setup(t, db)
}
- body, err := json.Marshal(tt.payload)
+ body, err := json.Marshal(tt.payload, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
@@ -892,7 +893,7 @@ func TestInMemoryDB_ListBackups_BackupTypeFilter(t *testing.T) {
body, marshalErr := json.Marshal(map[string]any{
"TableName": "T",
"BackupType": "USER",
- })
+ }, jsonx.V1())
require.NoError(t, marshalErr)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
@@ -908,7 +909,7 @@ func TestInMemoryDB_ListBackups_BackupTypeFilter(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["BackupSummaries"].([]any)
require.True(t, ok)
assert.Len(t, summaries, 2)
@@ -931,7 +932,7 @@ func TestInMemoryDB_PutResourcePolicy_EmptyPolicy(t *testing.T) {
body, marshalErr := json.Marshal(map[string]any{
"ResourceArn": tableARN,
"Policy": "",
- })
+ }, jsonx.V1())
require.NoError(t, marshalErr)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
@@ -1324,7 +1325,7 @@ func TestInMemoryDB_DescribeBackup_ViaHandler(t *testing.T) {
require.NoError(t, err)
backupArn := aws.ToString(out.BackupDetails.BackupArn)
- reqBody, _ := json.Marshal(map[string]string{"BackupArn": backupArn})
+ reqBody, _ := json.Marshal(map[string]string{"BackupArn": backupArn}, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody))
req.Header.Set("Content-Type", "application/x-amz-json-1.0")
@@ -1336,7 +1337,7 @@ func TestInMemoryDB_DescribeBackup_ViaHandler(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var respBody map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &respBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &respBody, jsonx.V1()))
assert.Contains(t, respBody, "BackupDescription")
}
diff --git a/services/dynamodb/backup_ops.go b/services/dynamodb/backup_ops.go
index a4978814af..6b80d17ca6 100644
--- a/services/dynamodb/backup_ops.go
+++ b/services/dynamodb/backup_ops.go
@@ -2,7 +2,7 @@ package dynamodb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"sort"
@@ -17,6 +17,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
)
@@ -37,7 +38,7 @@ func backupARN(region, accountID, tableName string, ts time.Time) string {
func (h *DynamoDBHandler) createBackup(ctx context.Context, body []byte) (any, error) {
var req models.CreateBackupInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -65,7 +66,7 @@ func (h *DynamoDBHandler) createBackup(ctx context.Context, body []byte) (any, e
func (h *DynamoDBHandler) describeBackup(ctx context.Context, body []byte) (any, error) {
var req models.DescribeBackupInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -89,7 +90,7 @@ func (h *DynamoDBHandler) describeBackup(ctx context.Context, body []byte) (any,
func (h *DynamoDBHandler) deleteBackup(ctx context.Context, body []byte) (any, error) {
var req models.DeleteBackupInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -107,7 +108,7 @@ func (h *DynamoDBHandler) deleteBackup(ctx context.Context, body []byte) (any, e
func (h *DynamoDBHandler) listBackups(ctx context.Context, body []byte) (any, error) {
var req models.ListBackupsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -420,7 +421,7 @@ func toSDKLSIOverride(lsis []models.LocalSecondaryIndex) []sdktypes.LocalSeconda
func (h *DynamoDBHandler) restoreTableFromBackup(ctx context.Context, body []byte) (any, error) {
var req models.RestoreTableFromBackupInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -500,7 +501,7 @@ func toSDKRestoreDateTime(secs *float64) *time.Time {
func (h *DynamoDBHandler) restoreTableToPointInTime(ctx context.Context, body []byte) (any, error) {
var req models.RestoreTableToPointInTimeInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/dynamodb/backup_replica_test.go b/services/dynamodb/backup_replica_test.go
index 92f6f2b025..62f88014d5 100644
--- a/services/dynamodb/backup_replica_test.go
+++ b/services/dynamodb/backup_replica_test.go
@@ -3,7 +3,7 @@ package dynamodb_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/aws/aws-sdk-go-v2/aws"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
@@ -29,7 +30,7 @@ func doBackupRequest(
) (int, map[string]any) {
t.Helper()
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(raw))
@@ -40,7 +41,7 @@ func doBackupRequest(
require.NoError(t, serveEchoHandler(h.Handler(), w, req))
var result map[string]any
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &result, jsonx.V1()))
return w.Code, result
}
diff --git a/services/dynamodb/bench_extract_resource_test.go b/services/dynamodb/bench_extract_resource_test.go
index 4a3a1b649e..882f7e5ceb 100644
--- a/services/dynamodb/bench_extract_resource_test.go
+++ b/services/dynamodb/bench_extract_resource_test.go
@@ -2,7 +2,7 @@ package dynamodb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
)
@@ -28,7 +29,7 @@ func BenchmarkExtractResource(b *testing.B) {
"TableName": "BenchTable",
"Item": item,
}
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
if err != nil {
b.Fatalf("marshal request body: %v", err)
}
diff --git a/services/dynamodb/bench_hot_ops_test.go b/services/dynamodb/bench_hot_ops_test.go
index 01f5ba388b..86bba4b1e1 100644
--- a/services/dynamodb/bench_hot_ops_test.go
+++ b/services/dynamodb/bench_hot_ops_test.go
@@ -2,13 +2,14 @@ package dynamodb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
"strconv"
"testing"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
)
@@ -26,7 +27,7 @@ func benchRawOp(b *testing.B, h *dynamodb.DynamoDBHandler, action string, body [
func benchMarshal(b *testing.B, v any) []byte {
b.Helper()
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
b.Fatal(err)
}
diff --git a/services/dynamodb/cbor_test.go b/services/dynamodb/cbor_test.go
index 403fcd2f20..bea346638b 100644
--- a/services/dynamodb/cbor_test.go
+++ b/services/dynamodb/cbor_test.go
@@ -3,7 +3,7 @@ package dynamodb_test
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
)
@@ -478,7 +479,7 @@ func TestDynamoDBCBOR_JSONAndCBORCoexist(t *testing.T) {
require.Equal(t, http.StatusOK, getRR2.Code)
var jsonResp map[string]any
- require.NoError(t, json.Unmarshal(getRR2.Body.Bytes(), &jsonResp))
+ require.NoError(t, json.Unmarshal(getRR2.Body.Bytes(), &jsonResp, jsonx.V1()))
itemJSON := jsonResp["Item"].(map[string]any)
valueJSON := itemJSON["value"].(map[string]any)
assert.Equal(t, "from-cbor", valueJSON["S"])
diff --git a/services/dynamodb/condition_check_return_test.go b/services/dynamodb/condition_check_return_test.go
index ef200f1f75..aff502b79e 100644
--- a/services/dynamodb/condition_check_return_test.go
+++ b/services/dynamodb/condition_check_return_test.go
@@ -2,7 +2,7 @@ package dynamodb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
)
@@ -34,7 +35,7 @@ func doDDBRequest(
var decoded map[string]any
if w.Body.Len() > 0 {
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &decoded))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &decoded, jsonx.V1()))
}
return resp.StatusCode, decoded
diff --git a/services/dynamodb/expr/evaluator.go b/services/dynamodb/expr/evaluator.go
index 9c899e263d..79ee2471c9 100644
--- a/services/dynamodb/expr/evaluator.go
+++ b/services/dynamodb/expr/evaluator.go
@@ -2,7 +2,7 @@ package expr
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"math"
@@ -11,6 +11,7 @@ import (
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/dynamonum"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Sentinel errors for evaluator.
@@ -480,9 +481,9 @@ func containsList(l, targetVal any) bool {
if !isList {
return false
}
- targetJSON, _ := json.Marshal(targetVal)
+ targetJSON, _ := json.Marshal(targetVal, jsonx.V1())
for _, item := range items {
- itemJSON, _ := json.Marshal(item)
+ itemJSON, _ := json.Marshal(item, jsonx.V1())
if string(itemJSON) == string(targetJSON) {
return true
}
@@ -657,7 +658,7 @@ func (e *Evaluator) toString(v any) string {
case nil:
return ""
default:
- b, _ := json.Marshal(s)
+ b, _ := json.Marshal(s, jsonx.V1())
return string(b)
}
diff --git a/services/dynamodb/global_tables_test.go b/services/dynamodb/global_tables_test.go
index 6a74ba3c6f..1bfd3919fb 100644
--- a/services/dynamodb/global_tables_test.go
+++ b/services/dynamodb/global_tables_test.go
@@ -1,7 +1,7 @@
package dynamodb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
)
@@ -149,7 +150,7 @@ func TestGlobalTables(t *testing.T) {
assert.Equal(t, tt.wantStatus, code)
if tt.wantBodyContains != "" {
- bodyBytes, _ := json.Marshal(resp)
+ bodyBytes, _ := json.Marshal(resp, jsonx.V1())
assert.Contains(t, string(bodyBytes), tt.wantBodyContains)
}
})
@@ -883,7 +884,7 @@ func TestUpdateGlobalTableSettings_NotFound_ViaHandler(t *testing.T) {
"GlobalTableName": "NoSuchGT",
})
require.Equal(t, http.StatusBadRequest, code)
- bodyBytes, _ := json.Marshal(resp)
+ bodyBytes, _ := json.Marshal(resp, jsonx.V1())
assert.Contains(t, string(bodyBytes), "GlobalTableNotFoundException")
}
diff --git a/services/dynamodb/golden_perf_test.go b/services/dynamodb/golden_perf_test.go
index 9fcccf924c..b3d29cf05c 100644
--- a/services/dynamodb/golden_perf_test.go
+++ b/services/dynamodb/golden_perf_test.go
@@ -11,7 +11,8 @@ package dynamodb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"os"
"path/filepath"
@@ -20,6 +21,7 @@ import (
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
)
@@ -32,9 +34,9 @@ func goldenPath(name string) string {
func checkGolden(t *testing.T, name string, got []byte) {
t.Helper()
- var buf bytes.Buffer
- require.NoError(t, json.Indent(&buf, got, "", " "))
- pretty := buf.Bytes()
+ indented := jsontext.Value(bytes.Clone(got))
+ require.NoError(t, indented.Indent(jsonx.V1(), jsontext.WithIndent(" ")))
+ pretty := []byte(indented)
path := goldenPath(name)
if os.Getenv("DYNAMODB_GOLDEN_UPDATE") != "" {
@@ -114,7 +116,7 @@ func TestGolden_QueryKeyConditionFilterProjection(t *testing.T) {
})
require.Equal(t, 200, code, "Query: %v", resp)
- raw, err := json.Marshal(resp)
+ raw, err := json.Marshal(resp, jsonx.V1())
require.NoError(t, err)
checkGolden(t, "golden_query.json", raw)
}
@@ -141,7 +143,7 @@ func TestGolden_ScanFilter(t *testing.T) {
})
require.Equal(t, 200, code, "Scan: %v", resp)
- raw, err := json.Marshal(resp)
+ raw, err := json.Marshal(resp, jsonx.V1())
require.NoError(t, err)
checkGolden(t, "golden_scan.json", raw)
}
@@ -184,7 +186,7 @@ func TestGolden_TransactWriteItems(t *testing.T) {
})
require.Equal(t, 200, code, "TransactWriteItems: %v", resp)
- raw, err := json.Marshal(resp)
+ raw, err := json.Marshal(resp, jsonx.V1())
require.NoError(t, err)
checkGolden(t, "golden_transact.json", raw)
}
diff --git a/services/dynamodb/handler.go b/services/dynamodb/handler.go
index e4e250cea3..1ac9fd642d 100644
--- a/services/dynamodb/handler.go
+++ b/services/dynamodb/handler.go
@@ -2,7 +2,7 @@ package dynamodb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"hash/crc32"
@@ -19,6 +19,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/chaos"
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
@@ -458,7 +459,7 @@ func (h *DynamoDBHandler) ExtractResource(c *echo.Context) string {
TableName string `json:"TableName"`
BackupArn string `json:"BackupArn"`
}
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
@@ -614,14 +615,14 @@ func handleOpErr[WireIn any, SDKIn any, SDKOut any, WireOut any](
var input WireIn
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, err
}
}
debugEnabled := log.Enabled(ctx, slog.LevelDebug)
if debugEnabled {
- inputJSON, _ := json.Marshal(input)
+ inputJSON, _ := json.Marshal(input, jsonx.V1())
log.DebugContext(ctx, "handler input", "action", action, "input", string(inputJSON))
}
@@ -637,7 +638,7 @@ func handleOpErr[WireIn any, SDKIn any, SDKOut any, WireOut any](
wireOutput := fromSDK(sdkOutput)
if debugEnabled {
- outputJSON, _ := json.Marshal(wireOutput)
+ outputJSON, _ := json.Marshal(wireOutput, jsonx.V1())
log.DebugContext(ctx, "handler output", "action", action, "output", string(outputJSON))
}
@@ -657,14 +658,14 @@ func handleOp[WireIn any, SDKIn any, SDKOut any, WireOut any](
var input WireIn
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, err
}
}
debugEnabled := log.Enabled(ctx, slog.LevelDebug)
if debugEnabled {
- inputJSON, _ := json.Marshal(input)
+ inputJSON, _ := json.Marshal(input, jsonx.V1())
log.DebugContext(ctx, "handler input", "action", action, "input", string(inputJSON))
}
@@ -677,7 +678,7 @@ func handleOp[WireIn any, SDKIn any, SDKOut any, WireOut any](
wireOutput := fromSDK(sdkOutput)
if debugEnabled {
- outputJSON, _ := json.Marshal(wireOutput)
+ outputJSON, _ := json.Marshal(wireOutput, jsonx.V1())
log.DebugContext(ctx, "handler output", "action", action, "output", string(outputJSON))
}
@@ -877,7 +878,7 @@ func validateTableNameFromBody(body []byte) error {
TableName string `json:"TableName"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if req.TableName == "" {
return nil // missing table name is handled downstream
}
@@ -894,7 +895,7 @@ func validateTableNameFromBody(body []byte) error {
// such response reached a client as smithy.GenericAPIError{Code:"UnknownError"}
// (gopherstack-wlo1).
func writeDynamoDBDispatchError(c *echo.Context, status int, errType, message string) error {
- body, err := json.Marshal(service.JSONErrorResponse{Type: errType, Message: message})
+ body, err := json.Marshal(service.JSONErrorResponse{Type: errType, Message: message}, jsonx.V1())
if err != nil {
return err
}
@@ -932,7 +933,7 @@ func (h *DynamoDBHandler) handleError(
c.Response().Header().Set("Content-Type", "application/x-amz-json-1.0")
- payload, _ := json.Marshal(awsErr)
+ payload, _ := json.Marshal(awsErr, jsonx.V1())
checksum := crc32.ChecksumIEEE(payload)
c.Response().Header().Set("X-Amz-Crc32", strconv.FormatUint(uint64(checksum), 10))
@@ -955,9 +956,7 @@ func (h *DynamoDBHandler) classifyError(reqErr error) (int, *Error) {
}
// Fallback
- var syntaxErr *json.SyntaxError
- var unmarshalTypeError *json.UnmarshalTypeError
- if errors.As(reqErr, &syntaxErr) || errors.As(reqErr, &unmarshalTypeError) {
+ if jsonx.IsSyntaxError(reqErr) || jsonx.IsTypeError(reqErr) {
return http.StatusBadRequest, NewValidationException(
"JSON Error: " + reqErr.Error(),
)
diff --git a/services/dynamodb/handler_autoscaling.go b/services/dynamodb/handler_autoscaling.go
index 1221bacc14..29517a3bf8 100644
--- a/services/dynamodb/handler_autoscaling.go
+++ b/services/dynamodb/handler_autoscaling.go
@@ -7,11 +7,12 @@ package dynamodb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
sdkDDB "github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
)
@@ -237,7 +238,7 @@ func (h *DynamoDBHandler) handleUpdateTableReplicaAutoScaling(
body []byte,
) (any, error) {
var req updateTableReplicaAutoScalingInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/dynamodb/handler_backups.go b/services/dynamodb/handler_backups.go
index ea7dc0d90b..56e2c66e22 100644
--- a/services/dynamodb/handler_backups.go
+++ b/services/dynamodb/handler_backups.go
@@ -9,13 +9,14 @@ package dynamodb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
sdkdynamodb "github.com/aws/aws-sdk-go-v2/service/dynamodb"
sdktypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
)
@@ -75,7 +76,7 @@ func continuousBackupsOutputFromSDK(
func (h *DynamoDBHandler) describeContinuousBackups(ctx context.Context, body []byte) (any, error) {
var req describeContinuousBackupsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -106,7 +107,7 @@ type updateContinuousBackupsInput struct {
func (h *DynamoDBHandler) updateContinuousBackups(ctx context.Context, body []byte) (any, error) {
var req updateContinuousBackupsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -219,7 +220,7 @@ func exportDescFieldsFromSDK(d *sdktypes.ExportDescription) exportDescriptionFie
func (h *DynamoDBHandler) exportTableToPointInTime(ctx context.Context, body []byte) (any, error) {
var req exportTableToPointInTimeInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -250,7 +251,7 @@ type describeExportInput struct {
func (h *DynamoDBHandler) describeExport(ctx context.Context, body []byte) (any, error) {
var req describeExportInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -280,7 +281,7 @@ type listExportsInput struct {
func (h *DynamoDBHandler) listExports(ctx context.Context, body []byte) (any, error) {
var req listExportsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -327,7 +328,7 @@ func (h *DynamoDBHandler) describeTableReplicaAutoScaling(
body []byte,
) (any, error) {
var req describeTableReplicaAutoScalingInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/dynamodb/handler_contributor_insights.go b/services/dynamodb/handler_contributor_insights.go
index c0fe17207c..981a7ecaf8 100644
--- a/services/dynamodb/handler_contributor_insights.go
+++ b/services/dynamodb/handler_contributor_insights.go
@@ -7,11 +7,12 @@ package dynamodb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
sdkDDB "github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
)
@@ -34,7 +35,7 @@ func (h *DynamoDBHandler) handleDescribeContributorInsights(
body []byte,
) (any, error) {
var req describeContributorInsightsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -91,7 +92,7 @@ func (h *DynamoDBHandler) handleListContributorInsights(
body []byte,
) (any, error) {
var req listContributorInsightsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -145,7 +146,7 @@ func (h *DynamoDBHandler) handleUpdateContributorInsights(
body []byte,
) (any, error) {
var req updateContributorInsightsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/dynamodb/handler_debug_test.go b/services/dynamodb/handler_debug_test.go
index e866f44ba1..7355abb92c 100644
--- a/services/dynamodb/handler_debug_test.go
+++ b/services/dynamodb/handler_debug_test.go
@@ -2,7 +2,7 @@ package dynamodb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
@@ -98,7 +99,7 @@ func TestDebugLogging(t *testing.T) {
echoHandler := h.Handler()
if tt.setupTarget != "" {
- setupBody, err := json.Marshal(tt.setupInput)
+ setupBody, err := json.Marshal(tt.setupInput, jsonx.V1())
require.NoError(t, err)
setupReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(setupBody))
@@ -115,7 +116,7 @@ func TestDebugLogging(t *testing.T) {
logBuffer.Reset()
}
- body, err := json.Marshal(tt.input)
+ body, err := json.Marshal(tt.input, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
diff --git a/services/dynamodb/handler_execute_transaction.go b/services/dynamodb/handler_execute_transaction.go
index 59aec334d4..1a5036c98c 100644
--- a/services/dynamodb/handler_execute_transaction.go
+++ b/services/dynamodb/handler_execute_transaction.go
@@ -7,12 +7,13 @@ package dynamodb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
sdkDDB "github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
)
@@ -38,7 +39,7 @@ type executeTransactionOutput struct {
func (h *DynamoDBHandler) handleExecuteTransaction(ctx context.Context, body []byte) (any, error) {
var req executeTransactionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/dynamodb/handler_global_tables.go b/services/dynamodb/handler_global_tables.go
index de7336ecb0..bdfb7c4966 100644
--- a/services/dynamodb/handler_global_tables.go
+++ b/services/dynamodb/handler_global_tables.go
@@ -7,11 +7,12 @@ package dynamodb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
sdkDDB "github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
)
@@ -91,7 +92,7 @@ type listGlobalTablesOutput struct {
func (h *DynamoDBHandler) handleCreateGlobalTable(ctx context.Context, body []byte) (any, error) {
var req createGlobalTableInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -118,7 +119,7 @@ func (h *DynamoDBHandler) handleCreateGlobalTable(ctx context.Context, body []by
func (h *DynamoDBHandler) handleDescribeGlobalTable(ctx context.Context, body []byte) (any, error) {
var req describeGlobalTableInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -139,7 +140,7 @@ func (h *DynamoDBHandler) handleDescribeGlobalTableSettings(
body []byte,
) (any, error) {
var req describeGlobalTableSettingsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -164,7 +165,7 @@ func (h *DynamoDBHandler) handleDescribeGlobalTableSettings(
func (h *DynamoDBHandler) handleListGlobalTables(ctx context.Context, body []byte) (any, error) {
var req listGlobalTablesInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -213,7 +214,7 @@ func (h *DynamoDBHandler) handleListGlobalTables(ctx context.Context, body []byt
func (h *DynamoDBHandler) handleUpdateGlobalTable(ctx context.Context, body []byte) (any, error) {
var req updateGlobalTableInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -419,7 +420,7 @@ func (h *DynamoDBHandler) handleUpdateGlobalTableSettings(
body []byte,
) (any, error) {
var req updateGlobalTableSettingsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/dynamodb/handler_import.go b/services/dynamodb/handler_import.go
index 9963ea32ef..21bc1b39ba 100644
--- a/services/dynamodb/handler_import.go
+++ b/services/dynamodb/handler_import.go
@@ -7,12 +7,13 @@ package dynamodb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"github.com/aws/aws-sdk-go-v2/aws"
sdkDDB "github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
)
@@ -146,7 +147,7 @@ type describeImportOutput struct {
func (h *DynamoDBHandler) handleDescribeImport(ctx context.Context, body []byte) (any, error) {
var req describeImportInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -179,7 +180,7 @@ type importTableOutput struct {
func (h *DynamoDBHandler) handleImportTable(ctx context.Context, body []byte) (any, error) {
var req importTableInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -243,7 +244,7 @@ type listImportsInput struct {
func (h *DynamoDBHandler) handleListImports(ctx context.Context, body []byte) (any, error) {
var req listImportsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/dynamodb/handler_kinesis_streaming.go b/services/dynamodb/handler_kinesis_streaming.go
index 49b48067fb..1be7060ab2 100644
--- a/services/dynamodb/handler_kinesis_streaming.go
+++ b/services/dynamodb/handler_kinesis_streaming.go
@@ -7,11 +7,12 @@ package dynamodb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
sdkDDB "github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
)
@@ -64,7 +65,7 @@ func (h *DynamoDBHandler) handleDescribeKinesisStreamingDestination(
body []byte,
) (any, error) {
var req describeKinesisInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -96,7 +97,7 @@ func (h *DynamoDBHandler) handleDisableKinesisStreamingDestination(
body []byte,
) (any, error) {
var req disableKinesisInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -154,7 +155,7 @@ func (h *DynamoDBHandler) handleEnableKinesisStreamingDestination(
body []byte,
) (any, error) {
var req enableKinesisInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -229,7 +230,7 @@ func (h *DynamoDBHandler) handleUpdateKinesisStreamingDestination(
body []byte,
) (any, error) {
var req updateKinesisStreamingDestinationInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/dynamodb/handler_resource_policy.go b/services/dynamodb/handler_resource_policy.go
index 3196e23876..1c01cb4b63 100644
--- a/services/dynamodb/handler_resource_policy.go
+++ b/services/dynamodb/handler_resource_policy.go
@@ -7,10 +7,12 @@ package dynamodb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"github.com/aws/aws-sdk-go-v2/aws"
sdkDDB "github.com/aws/aws-sdk-go-v2/service/dynamodb"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// resourcePolicyInput is the wire format shared by Get and PutResourcePolicy.
@@ -45,7 +47,7 @@ func (h *DynamoDBHandler) handleDeleteResourcePolicy(
body []byte,
) (any, error) {
var req deleteResourcePolicyInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -67,7 +69,7 @@ func (h *DynamoDBHandler) handleDeleteResourcePolicy(
func (h *DynamoDBHandler) handleGetResourcePolicy(ctx context.Context, body []byte) (any, error) {
var req resourcePolicyInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -89,7 +91,7 @@ func (h *DynamoDBHandler) handleGetResourcePolicy(ctx context.Context, body []by
func (h *DynamoDBHandler) handlePutResourcePolicy(ctx context.Context, body []byte) (any, error) {
var req resourcePolicyInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/dynamodb/handler_streams_test.go b/services/dynamodb/handler_streams_test.go
index 9970130ec7..06ec18ab14 100644
--- a/services/dynamodb/handler_streams_test.go
+++ b/services/dynamodb/handler_streams_test.go
@@ -2,7 +2,7 @@ package dynamodb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
)
@@ -264,7 +265,7 @@ func TestHandler_ExportAndDescribeExport(t *testing.T) {
ExportArn string `json:"ExportArn"`
} `json:"ExportDescription"`
}
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &exp))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &exp, jsonx.V1()))
require.NotEmpty(t, exp.ExportDescription.ExportArn)
// DescribeExport on the returned ARN succeeds.
@@ -286,7 +287,7 @@ func TestHandler_DescribeTable_ReturnsStreamFields(t *testing.T) {
handler, streamARN := newStreamEnabledHandler(t)
- reqBody, err := json.Marshal(models.DescribeTableInput{TableName: "StreamHandlerTable"})
+ reqBody, err := json.Marshal(models.DescribeTableInput{TableName: "StreamHandlerTable"}, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody))
@@ -299,7 +300,7 @@ func TestHandler_DescribeTable_ReturnsStreamFields(t *testing.T) {
assert.Equal(t, http.StatusOK, w.Code)
var out models.DescribeTableOutput
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(
t,
diff --git a/services/dynamodb/handler_test.go b/services/dynamodb/handler_test.go
index 250801fae4..e294e3976f 100644
--- a/services/dynamodb/handler_test.go
+++ b/services/dynamodb/handler_test.go
@@ -2,7 +2,7 @@ package dynamodb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"hash/crc32"
"log/slog"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
@@ -386,7 +387,7 @@ func TestHandler_CRC32Header(t *testing.T) {
func mustMarshal(t *testing.T, v any) string {
t.Helper()
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
return string(data)
@@ -423,7 +424,7 @@ func TestHandler_TransactOps_AllActions(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- bodyBytes, _ := json.Marshal(tt.body)
+ bodyBytes, _ := json.Marshal(tt.body, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
req.Header.Set("X-Amz-Target", "DynamoDB_20120810."+tt.action)
w := httptest.NewRecorder()
diff --git a/services/dynamodb/import_export_s3.go b/services/dynamodb/import_export_s3.go
index 29da95e40b..6ec7cfd837 100644
--- a/services/dynamodb/import_export_s3.go
+++ b/services/dynamodb/import_export_s3.go
@@ -6,7 +6,8 @@ import (
"compress/gzip"
"context"
"encoding/csv"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -22,6 +23,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
)
@@ -342,7 +344,7 @@ func parseDynamoDBJSONLines(data []byte) ([]map[string]any, error) {
var rec struct {
Item map[string]any `json:"Item"`
}
- if err := json.Unmarshal(line, &rec); err != nil {
+ if err := json.Unmarshal(line, &rec, jsonx.V1()); err != nil {
return nil, fmt.Errorf("parse DynamoDB JSON line: %w", err)
}
@@ -422,11 +424,11 @@ func (db *InMemoryDB) exportTableToS3(
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
- enc := json.NewEncoder(gz)
+ enc := jsontext.NewEncoder(gz, jsonx.V1())
var count int64
for i := range items {
- if err := enc.Encode(struct {
+ if err := json.MarshalEncode(enc, struct {
Item map[string]any `json:"Item"`
}{Item: items[i]}); err != nil {
_ = gz.Close()
@@ -454,7 +456,7 @@ func (db *InMemoryDB) exportTableToS3(
"tableArn": tableARN,
"itemCount": count,
"dataFileS3Key": dataKey,
- })
+ }, jsonx.V1())
if _, err := s3.PutObject(ctx, &s3sdk.PutObjectInput{
Bucket: &bucket,
Key: &manifestKey,
diff --git a/services/dynamodb/import_export_s3_test.go b/services/dynamodb/import_export_s3_test.go
index f58f10f9e6..354b07e1c2 100644
--- a/services/dynamodb/import_export_s3_test.go
+++ b/services/dynamodb/import_export_s3_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"compress/gzip"
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"sort"
@@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
)
@@ -408,7 +409,7 @@ func TestDescribeImport(t *testing.T) {
assert.Equal(t, tt.wantStatus, code)
if tt.wantBodyContains != "" {
- bodyBytes, _ := json.Marshal(resp)
+ bodyBytes, _ := json.Marshal(resp, jsonx.V1())
assert.Contains(t, string(bodyBytes), tt.wantBodyContains)
}
})
diff --git a/services/dynamodb/item_collection_metrics_test.go b/services/dynamodb/item_collection_metrics_test.go
index e20699911e..f3eb2dca36 100644
--- a/services/dynamodb/item_collection_metrics_test.go
+++ b/services/dynamodb/item_collection_metrics_test.go
@@ -2,7 +2,7 @@ package dynamodb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
)
@@ -67,7 +68,7 @@ func TestQuery_ConsistentRead_GSI_Rejected(t *testing.T) {
},
},
"BillingMode": "PAY_PER_REQUEST",
- })
+ }, jsonx.V1())
require.NoError(t, err)
putItemBody, err := json.Marshal(map[string]any{
@@ -76,7 +77,7 @@ func TestQuery_ConsistentRead_GSI_Rejected(t *testing.T) {
"pk": map[string]any{"S": "user1"},
"email": map[string]any{"S": "test@example.com"},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
tests := []struct {
@@ -144,7 +145,7 @@ func TestQuery_ConsistentRead_GSI_Rejected(t *testing.T) {
require.Equal(t, http.StatusOK, w.Code, "PutItem failed: %s", w.Body.String())
// Execute query.
- queryRaw, marshalErr := json.Marshal(tc.query)
+ queryRaw, marshalErr := json.Marshal(tc.query, jsonx.V1())
require.NoError(t, marshalErr)
w = makeHandlerJSONRequest(t, h, "DynamoDB_20120810.Query", queryRaw)
@@ -159,7 +160,7 @@ func TestQuery_ConsistentRead_GSI_Rejected(t *testing.T) {
// marshalJSONBody marshals v to JSON bytes for use with makeHandlerJSONRequest.
func marshalJSONBody(t *testing.T, v any) []byte {
t.Helper()
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
return b
@@ -223,7 +224,7 @@ func TestPutItem_ItemCollectionMetrics_PKOnly(t *testing.T) {
SizeEstimateRangeGB []float64 `json:"SizeEstimateRangeGB"`
} `json:"ItemCollectionMetrics"`
}
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.ItemCollectionMetrics)
// ItemCollectionKey must contain only the partition key, not sk or non-key attrs.
@@ -309,7 +310,7 @@ func TestDeleteUpdate_ItemCollectionMetrics(t *testing.T) {
require.Equal(t, http.StatusOK, w.Code)
var upd icmResult
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &upd))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &upd, jsonx.V1()))
require.NotNil(t, upd.ItemCollectionMetrics)
assert.Contains(t, upd.ItemCollectionMetrics.ItemCollectionKey, "pk")
assert.NotContains(t, upd.ItemCollectionMetrics.ItemCollectionKey, "sk")
@@ -325,7 +326,7 @@ func TestDeleteUpdate_ItemCollectionMetrics(t *testing.T) {
require.Equal(t, http.StatusOK, w.Code)
var del icmResult
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &del))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &del, jsonx.V1()))
require.NotNil(t, del.ItemCollectionMetrics)
assert.Contains(t, del.ItemCollectionMetrics.ItemCollectionKey, "pk")
assert.NotContains(t, del.ItemCollectionMetrics.ItemCollectionKey, "sk")
@@ -374,7 +375,7 @@ func TestBatchWriteItem_ItemCollectionMetrics(t *testing.T) {
require.Equal(t, http.StatusOK, w.Code)
var withMetrics batchWriteICMResult
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &withMetrics))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &withMetrics, jsonx.V1()))
require.Contains(t, withMetrics.ItemCollectionMetrics, "bw-icm-tbl")
metrics := withMetrics.ItemCollectionMetrics["bw-icm-tbl"]
require.Len(t, metrics, 2, "one metric per PutRequest")
@@ -430,7 +431,7 @@ func TestTransactWriteItems_ItemCollectionMetrics(t *testing.T) {
require.Equal(t, http.StatusOK, w.Code)
var withMetrics batchWriteICMResult
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &withMetrics))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &withMetrics, jsonx.V1()))
require.Contains(t, withMetrics.ItemCollectionMetrics, "tw-icm-tbl")
metrics := withMetrics.ItemCollectionMetrics["tw-icm-tbl"]
require.Len(t, metrics, 2, "one metric per Put transact item")
diff --git a/services/dynamodb/json_check_test.go b/services/dynamodb/json_check_test.go
index 3530cac46e..85f9a17266 100644
--- a/services/dynamodb/json_check_test.go
+++ b/services/dynamodb/json_check_test.go
@@ -1,9 +1,10 @@
package dynamodb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -49,7 +50,7 @@ func TestSDKJSONMarshaling(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- b, err := json.Marshal(tt.input)
+ b, err := json.Marshal(tt.input, jsonx.V1())
require.NoError(t, err)
require.NotEmpty(t, b)
t.Logf("%s JSON: %s", tt.name, string(b))
diff --git a/services/dynamodb/kinesis_streaming_test.go b/services/dynamodb/kinesis_streaming_test.go
index 73c9a27fa2..dddf67c95c 100644
--- a/services/dynamodb/kinesis_streaming_test.go
+++ b/services/dynamodb/kinesis_streaming_test.go
@@ -1,7 +1,7 @@
package dynamodb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
)
@@ -153,7 +154,7 @@ func TestKinesisDestinations(t *testing.T) {
assert.Equal(t, tt.wantStatus, code)
if tt.wantBodyContains != "" {
- bodyBytes, _ := json.Marshal(resp)
+ bodyBytes, _ := json.Marshal(resp, jsonx.V1())
assert.Contains(t, string(bodyBytes), tt.wantBodyContains)
}
})
@@ -262,7 +263,7 @@ func TestEnableKinesisStreamingDestination(t *testing.T) {
assert.Equal(t, tt.wantStatus, code)
if tt.wantBodyContains != "" {
- bodyBytes, _ := json.Marshal(resp)
+ bodyBytes, _ := json.Marshal(resp, jsonx.V1())
assert.Contains(t, string(bodyBytes), tt.wantBodyContains)
}
})
@@ -492,7 +493,7 @@ func TestUpdateKinesisDestination_NotFound(t *testing.T) {
"StreamArn": "arn:aws:kinesis:us-east-1:123:stream/absent",
})
require.Equal(t, http.StatusBadRequest, code)
- bodyBytes, _ := json.Marshal(resp)
+ bodyBytes, _ := json.Marshal(resp, jsonx.V1())
assert.Contains(t, string(bodyBytes), "ResourceNotFoundException")
}
diff --git a/services/dynamodb/partiql.go b/services/dynamodb/partiql.go
index 14461e8cc7..7aa3483949 100644
--- a/services/dynamodb/partiql.go
+++ b/services/dynamodb/partiql.go
@@ -3,7 +3,7 @@ package dynamodb
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"log/slog"
@@ -16,6 +16,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
)
@@ -304,7 +305,7 @@ func (r *partiQLRunner) executeStatement(
// handleExecuteStatement routes to specific DML/DQL handlers based on the statement type.
func (h *DynamoDBHandler) handleExecuteStatement(ctx context.Context, body []byte) (any, error) {
var req executeStatementRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -336,7 +337,7 @@ func (h *DynamoDBHandler) handleBatchExecuteStatement(
body []byte,
) (any, error) {
var req batchExecuteStatementRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -882,7 +883,7 @@ func encodePartiQLNextToken(lastKey map[string]types.AttributeValue) string {
wire := models.FromSDKItem(lastKey)
- b, err := json.Marshal(wire)
+ b, err := json.Marshal(wire, jsonx.V1())
if err != nil {
return ""
}
@@ -903,7 +904,7 @@ func decodePartiQLNextToken(token string) map[string]types.AttributeValue {
}
var wire map[string]any
- if unmarshalErr := json.Unmarshal(b, &wire); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(b, &wire, jsonx.V1()); unmarshalErr != nil {
return nil
}
diff --git a/services/dynamodb/partiql_test.go b/services/dynamodb/partiql_test.go
index 7776ce05ab..9addcc9c45 100644
--- a/services/dynamodb/partiql_test.go
+++ b/services/dynamodb/partiql_test.go
@@ -3,7 +3,7 @@ package dynamodb_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"log/slog"
"net/http"
@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
)
@@ -92,7 +93,7 @@ func partiqlItemsFrom(t *testing.T, rec *httptest.ResponseRecorder) []any {
require.Equal(t, http.StatusOK, rec.Code, "response body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["Items"].([]any)
require.True(t, ok, "Items field missing or wrong type")
@@ -335,7 +336,7 @@ func TestPartiQL_Insert(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
item, ok := getResp["Item"].(map[string]any)
require.True(t, ok, "item not found for pk=%q", tc.lookupPK)
assert.Equal(t, tc.wantVal, item[tc.wantAttr], "attribute %q mismatch", tc.wantAttr)
@@ -415,7 +416,7 @@ func TestPartiQL_Update(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
item := getResp["Item"].(map[string]any)
assert.Equal(t, tc.wantVal, item[tc.wantAttr])
})
@@ -490,7 +491,7 @@ func TestPartiQL_Delete(t *testing.T) {
getRec := doRequest(t, handler, "DynamoDB_20120810.GetItem", getBody)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Nil(t, getResp["Item"], "deleted item should not be present")
// Surviving item must still be there.
@@ -501,7 +502,7 @@ func TestPartiQL_Delete(t *testing.T) {
getRec2 := doRequest(t, handler, "DynamoDB_20120810.GetItem", getBody2)
var getResp2 map[string]any
- require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getResp2))
+ require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getResp2, jsonx.V1()))
assert.NotNil(t, getResp2["Item"], "surviving item should still be present")
})
}
@@ -582,7 +583,7 @@ func TestPartiQL_CompositeKey_CaseInsensitiveAND(t *testing.T) {
getRec := doRequest(t, handler, "DynamoDB_20120810.GetItem", getBody)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
if tc.wantGone {
assert.Nil(t, getResp["Item"])
@@ -670,7 +671,7 @@ func TestPartiQL_Batch(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
responses, ok := resp["Responses"].([]any)
require.True(t, ok)
require.Len(t, responses, tc.wantLen)
@@ -1071,7 +1072,7 @@ func TestBatchExecuteStatement_ParameterConversionFailure_ErrorCode(t *testing.T
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
responses, ok := resp["Responses"].([]any)
require.True(t, ok)
require.Len(t, responses, 1)
diff --git a/services/dynamodb/perf_sweep_bench_test.go b/services/dynamodb/perf_sweep_bench_test.go
index cf3580f611..9019cc0516 100644
--- a/services/dynamodb/perf_sweep_bench_test.go
+++ b/services/dynamodb/perf_sweep_bench_test.go
@@ -6,7 +6,7 @@ package dynamodb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
)
@@ -22,7 +23,7 @@ import (
func invokeOpB(tb testing.TB, handler *dynamodb.DynamoDBHandler, action string, body any) (int, map[string]any) {
tb.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(tb, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
@@ -32,7 +33,7 @@ func invokeOpB(tb testing.TB, handler *dynamodb.DynamoDBHandler, action string,
var resp map[string]any
if w.Body.Len() > 0 {
- require.NoError(tb, json.Unmarshal(w.Body.Bytes(), &resp))
+ require.NoError(tb, json.Unmarshal(w.Body.Bytes(), &resp, jsonx.V1()))
}
return w.Code, resp
diff --git a/services/dynamodb/persistence.go b/services/dynamodb/persistence.go
index 6d7f7f1bee..aa0416bba0 100644
--- a/services/dynamodb/persistence.go
+++ b/services/dynamodb/persistence.go
@@ -2,10 +2,12 @@ package dynamodb
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"log/slog"
"strconv"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
@@ -35,7 +37,7 @@ type dbSnapshot struct {
type dbSnapshotWire struct {
DefaultRegion string `json:"defaultRegion"`
AccountID string `json:"accountID"`
- Tables json.RawMessage `json:"tables"`
+ Tables jsontext.Value `json:"tables"`
Backups []*Backup `json:"backups,omitempty"`
GlobalTables []*StoredGlobalTable `json:"globalTables,omitempty"`
Version int `json:"version"`
@@ -67,7 +69,7 @@ func (db *InMemoryDB) Snapshot(ctx context.Context) []byte {
AccountID: db.accountID,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx,
"DynamoDB: failed to serialise snapshot; state will not be persisted",
@@ -82,16 +84,16 @@ func (db *InMemoryDB) Snapshot(ctx context.Context) []byte {
// marshalTablesRLocked marshals each table under its own table.mu.RLock, then reassembles the JSON array.
// Reassembling per-table bytes, rather than marshaling the slice directly, keeps the wire format byte-identical.
-func marshalTablesRLocked(tables []*Table) (json.RawMessage, error) {
+func marshalTablesRLocked(tables []*Table) (jsontext.Value, error) {
if len(tables) == 0 {
- return json.RawMessage("null"), nil
+ return jsontext.Value("null"), nil
}
- parts := make([]json.RawMessage, len(tables))
+ parts := make([]jsontext.Value, len(tables))
for i, t := range tables {
t.mu.RLock("Snapshot")
- data, err := json.Marshal(t)
+ data, err := json.Marshal(t, jsonx.V1())
t.mu.RUnlock()
if err != nil {
@@ -101,7 +103,7 @@ func marshalTablesRLocked(tables []*Table) (json.RawMessage, error) {
parts[i] = data
}
- return json.Marshal(parts)
+ return json.Marshal(parts, jsonx.V1())
}
// Restore loads backend state from a JSON snapshot.
diff --git a/services/dynamodb/realism_test.go b/services/dynamodb/realism_test.go
index 4393067ce6..96e85df1e9 100644
--- a/services/dynamodb/realism_test.go
+++ b/services/dynamodb/realism_test.go
@@ -2,7 +2,7 @@ package dynamodb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
)
@@ -57,7 +58,7 @@ func TestHandler_Realism(t *testing.T) {
Items []map[string]any `json:"Items"`
ScannedCount int `json:"ScannedCount"`
}
- err := json.Unmarshal(w.Body.Bytes(), &output)
+ err := json.Unmarshal(w.Body.Bytes(), &output, jsonx.V1())
require.NoError(t, err)
assert.Less(t, len(output.Items), 20, "Scan should have truncated results")
@@ -117,7 +118,7 @@ func TestHandler_Realism(t *testing.T) {
LastEvaluatedKey map[string]any `json:"LastEvaluatedKey"`
Items []map[string]any `json:"Items"`
}
- err := json.Unmarshal(w.Body.Bytes(), &output)
+ err := json.Unmarshal(w.Body.Bytes(), &output, jsonx.V1())
require.NoError(t, err)
assert.Less(t, len(output.Items), 20, "Query should have truncated results")
@@ -170,7 +171,7 @@ func TestHandler_Realism(t *testing.T) {
},
},
}
- body, _ := json.Marshal(batchInput)
+ body, _ := json.Marshal(batchInput, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
req.Header.Set("X-Amz-Target", "DynamoDB_20120810.BatchGetItem")
w := httptest.NewRecorder()
@@ -185,7 +186,7 @@ func TestHandler_Realism(t *testing.T) {
Responses map[string][]map[string]any `json:"Responses"`
UnprocessedKeys map[string]any `json:"UnprocessedKeys"`
}
- err := json.Unmarshal(w.Body.Bytes(), &output)
+ err := json.Unmarshal(w.Body.Bytes(), &output, jsonx.V1())
require.NoError(t, err)
totalReturned := len(output.Responses[tableName])
diff --git a/services/dynamodb/resource_policy_test.go b/services/dynamodb/resource_policy_test.go
index b6edf0914d..413c65588c 100644
--- a/services/dynamodb/resource_policy_test.go
+++ b/services/dynamodb/resource_policy_test.go
@@ -1,7 +1,7 @@
package dynamodb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
)
@@ -77,7 +78,7 @@ func TestResourcePolicy(t *testing.T) {
handler := dynamodb.NewHandler(backend)
code, resp := invokeOp(t, handler, "GetResourcePolicy", map[string]any{"ResourceArn": ""})
assert.Equal(t, http.StatusBadRequest, code)
- bodyBytes, _ := json.Marshal(resp)
+ bodyBytes, _ := json.Marshal(resp, jsonx.V1())
assert.Contains(t, string(bodyBytes), "ValidationException")
})
@@ -100,7 +101,7 @@ func TestResourcePolicy(t *testing.T) {
map[string]any{"ResourceArn": tableARN},
)
assert.Equal(t, http.StatusOK, code2)
- bodyBytes, _ := json.Marshal(resp2)
+ bodyBytes, _ := json.Marshal(resp2, jsonx.V1())
assert.Contains(t, string(bodyBytes), "2012-10-17")
})
@@ -116,7 +117,7 @@ func TestResourcePolicy(t *testing.T) {
map[string]any{"ResourceArn": tableARN},
)
assert.Equal(t, http.StatusBadRequest, code)
- bodyBytes, _ := json.Marshal(resp)
+ bodyBytes, _ := json.Marshal(resp, jsonx.V1())
assert.Contains(t, string(bodyBytes), "ValidationException")
})
@@ -145,7 +146,7 @@ func TestResourcePolicy(t *testing.T) {
map[string]any{"ResourceArn": ""},
)
assert.Equal(t, http.StatusBadRequest, code)
- bodyBytes, _ := json.Marshal(resp)
+ bodyBytes, _ := json.Marshal(resp, jsonx.V1())
assert.Contains(t, string(bodyBytes), "ValidationException")
})
}
diff --git a/services/dynamodb/search_vectors_test.go b/services/dynamodb/search_vectors_test.go
index 708ceb3bf3..1ca8e7e6e6 100644
--- a/services/dynamodb/search_vectors_test.go
+++ b/services/dynamodb/search_vectors_test.go
@@ -2,7 +2,7 @@ package dynamodb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
)
@@ -63,7 +64,7 @@ func TestSearchVectors(t *testing.T) {
require.Equal(t, http.StatusBadRequest, w.Code)
var errBody map[string]string
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody, jsonx.V1()))
assert.Contains(t, errBody["__type"], "ResourceNotFoundException")
assert.Contains(t, errBody["message"], "table not found")
})
@@ -82,7 +83,7 @@ func TestSearchVectors(t *testing.T) {
require.Equal(t, http.StatusBadRequest, w.Code)
var errBody map[string]string
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody, jsonx.V1()))
assert.Contains(t, errBody["__type"], "ResourceNotFoundException")
assert.Contains(t, errBody["message"], "Index: vec-index not found")
})
diff --git a/services/dynamodb/test_utils_test.go b/services/dynamodb/test_utils_test.go
index 762f85ea1e..54f53bc8d0 100644
--- a/services/dynamodb/test_utils_test.go
+++ b/services/dynamodb/test_utils_test.go
@@ -3,7 +3,7 @@ package dynamodb_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
sdkddb "github.com/aws/aws-sdk-go-v2/service/dynamodb"
sdktypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
"github.com/blackbirdworks/gopherstack/services/dynamodb"
@@ -23,7 +24,7 @@ import (
func mustUnmarshal[T any](t *testing.T, jsonStr string) T {
t.Helper()
var val T
- err := json.Unmarshal([]byte(jsonStr), &val)
+ err := json.Unmarshal([]byte(jsonStr), &val, jsonx.V1())
require.NoError(t, err)
return val
@@ -190,7 +191,7 @@ func invokeOp(
) (int, map[string]any) {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
@@ -201,7 +202,7 @@ func invokeOp(
var resp map[string]any
if w.Body.Len() > 0 {
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp, jsonx.V1()))
}
return w.Code, resp
diff --git a/services/dynamodb/transact_ops.go b/services/dynamodb/transact_ops.go
index cc2b4512e6..cfcfed186a 100644
--- a/services/dynamodb/transact_ops.go
+++ b/services/dynamodb/transact_ops.go
@@ -4,12 +4,13 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"sort"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/collections"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
@@ -210,7 +211,7 @@ func commitTransactTokenLocked(db *InMemoryDB, token, hash string) {
// preserves TransactItems' slice order and each Go struct's fixed field
// order, so the same request always hashes the same way.
func hashTransactWriteItems(items []types.TransactWriteItem) string {
- b, err := json.Marshal(items)
+ b, err := json.Marshal(items, jsonx.V1())
if err != nil {
return ""
}
diff --git a/services/dynamodb/transact_validation.go b/services/dynamodb/transact_validation.go
index 5a5c39ac1c..56732069f7 100644
--- a/services/dynamodb/transact_validation.go
+++ b/services/dynamodb/transact_validation.go
@@ -5,12 +5,13 @@
package dynamodb
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
)
@@ -118,7 +119,7 @@ func checkTransactWriteItemSizeAndDupe(
// validation error), so it's not surfaced — the item is simply not
// tracked in seen and duplicate detection silently skips it, matching
// the pre-refactor loop's "continue on marshal error" behavior.
- keyBytes, marshalErr := json.Marshal(wireKey)
+ keyBytes, marshalErr := json.Marshal(wireKey, jsonx.V1())
if marshalErr == nil {
twk := transactWriteKey{TableName: tableName, KeyJSON: string(keyBytes)}
if seen[twk] {
diff --git a/services/dynamodb/wire_codec_internal_test.go b/services/dynamodb/wire_codec_internal_test.go
index 8c406032b1..e9b211e97f 100644
--- a/services/dynamodb/wire_codec_internal_test.go
+++ b/services/dynamodb/wire_codec_internal_test.go
@@ -2,7 +2,8 @@ package dynamodb
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"math/rand/v2"
"strconv"
"strings"
@@ -14,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
)
@@ -117,7 +119,7 @@ func TestAppendJSONString_MatchesEncodingJSON(t *testing.T) {
}
for _, s := range corpus {
- want, err := json.Marshal(s)
+ want, err := json.Marshal(s, jsonx.V1())
require.NoError(t, err)
assert.Equal(t, string(want), string(appendJSONString(nil, s)), "input %q", s)
}
@@ -243,7 +245,7 @@ func TestEncoders_MatchLegacyMarshal(t *testing.T) {
items[i] = randItem(rng, 0)
}
- want, err := json.Marshal(tt.legacy(it, items))
+ want, err := json.Marshal(tt.legacy(it, items), jsonx.V1())
require.NoError(t, err)
got, release, err := marshalResponse(tt.fast(it, items))
@@ -294,7 +296,7 @@ func TestEncoders_EmptyAndNilOutputs(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- want, err := json.Marshal(tt.legacy())
+ want, err := json.Marshal(tt.legacy(), jsonx.V1())
require.NoError(t, err)
got, release, err := marshalResponse(tt.fast())
@@ -307,7 +309,7 @@ func TestEncoders_EmptyAndNilOutputs(t *testing.T) {
func legacyDecode[In, SDK any](body string, toSDK func(*In) (*SDK, error)) (*SDK, error) {
var in In
- if err := json.Unmarshal([]byte(body), &in); err != nil {
+ if err := json.Unmarshal([]byte(body), &in, jsonx.V1()); err != nil {
return nil, err
}
@@ -499,10 +501,15 @@ func TestDecoders_RandomItemsMatchLegacy(t *testing.T) {
item := randItem(rng, 0)
wireItem := models.FromSDKItem(item)
- raw, err := json.Marshal(map[string]any{"TableName": "t", "Item": wireItem})
+ raw, err := json.Marshal(map[string]any{"TableName": "t", "Item": wireItem}, jsonx.V1())
require.NoError(t, err)
- indented, err := json.MarshalIndent(map[string]any{"TableName": "t", "Item": wireItem}, "", " ")
+ indented, err := json.Marshal(
+ map[string]any{"TableName": "t", "Item": wireItem},
+ jsonx.V1(),
+ jsontext.Multiline(true),
+ jsontext.WithIndent(" "),
+ )
require.NoError(t, err)
for _, body := range [][]byte{raw, indented, pythonEscape(raw)} {
@@ -562,7 +569,7 @@ func TestTopLevelStrings_MatchesStructDecode(t *testing.T) {
BackupArn string `json:"BackupArn"`
}
- legacyErr := json.Unmarshal([]byte(body), &data)
+ legacyErr := json.Unmarshal([]byte(body), &data, jsonx.V1())
table, backup, ok := topLevelStrings([]byte(body))
if !ok {
@@ -580,7 +587,7 @@ func legacyWireBytes(t *testing.T, item map[string]any) []byte {
t.Helper()
sdk, _ := models.ToSDKItem(item)
- out, err := json.Marshal(models.FromSDKItem(sdk))
+ out, err := json.Marshal(models.FromSDKItem(sdk), jsonx.V1())
require.NoError(t, err)
return out
@@ -590,11 +597,11 @@ func TestAppendWireItem_MatchesLegacyPipeline(t *testing.T) {
t.Parallel()
jsonRoundTrip := func(item map[string]any) map[string]any {
- raw, err := json.Marshal(item)
+ raw, err := json.Marshal(item, jsonx.V1())
require.NoError(t, err)
var out map[string]any
- require.NoError(t, json.Unmarshal(raw, &out))
+ require.NoError(t, json.Unmarshal(raw, &out, jsonx.V1()))
return out
}
diff --git a/services/dynamodb/wire_encode.go b/services/dynamodb/wire_encode.go
index 886cdf1cda..1fd2c53802 100644
--- a/services/dynamodb/wire_encode.go
+++ b/services/dynamodb/wire_encode.go
@@ -2,7 +2,7 @@ package dynamodb
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"slices"
"strconv"
"sync"
@@ -11,6 +11,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/dynamodb/models"
)
@@ -63,7 +64,7 @@ func marshalResponse(v any) ([]byte, func(), error) {
return e.bytes(), e.release, nil
}
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
return b, noopRelease, err
}
@@ -322,7 +323,7 @@ func appendSDKItems(dst []byte, items []map[string]types.AttributeValue) []byte
// appendMarshaled splices json.Marshal(v) for rarely-populated sub-documents.
func appendMarshaled(dst []byte, v any) []byte {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return append(dst, "null"...)
}
diff --git a/services/dynamodbstreams/handler.go b/services/dynamodbstreams/handler.go
index 55546fec68..c6284293af 100644
--- a/services/dynamodbstreams/handler.go
+++ b/services/dynamodbstreams/handler.go
@@ -2,7 +2,7 @@ package dynamodbstreams
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"hash/crc32"
@@ -15,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
ddbbackend "github.com/blackbirdworks/gopherstack/services/dynamodb"
@@ -99,7 +100,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var data map[string]any
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
@@ -154,7 +155,7 @@ func (h *Handler) Handler() echo.HandlerFunc {
return h.handleError(ctx, c, operation, reqErr)
}
- payload, err := json.Marshal(response)
+ payload, err := json.Marshal(response, jsonx.V1())
if err != nil {
log.ErrorContext(ctx, "failed to marshal JSON response", "error", err)
@@ -203,7 +204,7 @@ func dispatchGetShardIterator(
) (any, error) {
var input dynamodbstreams.GetShardIteratorInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errSerialization, err)
}
}
@@ -230,7 +231,7 @@ func dispatchListStreams(
) (any, error) {
var input dynamodbstreams.ListStreamsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errSerialization, err)
}
}
@@ -262,7 +263,7 @@ func dispatchGetRecords(
) (any, error) {
var input dynamodbstreams.GetRecordsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errSerialization, err)
}
}
@@ -286,7 +287,7 @@ func writeDynamoDBStreamsDispatchError(c *echo.Context, message string) error {
body, _ := json.Marshal(map[string]string{
keyErrType: "com.amazonaws.dynamodbstreams.v20120810#InternalServerError",
keyMessage: message,
- })
+ }, jsonx.V1())
c.Response().Header().Set("Content-Type", "application/x-amz-json-1.0")
return c.JSONBlob(http.StatusInternalServerError, body)
@@ -339,7 +340,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, operation stri
body, _ := json.Marshal(map[string]string{
keyErrType: "com.amazonaws.dynamodbstreams.v20120810#" + operation + "Exception",
keyMessage: msg,
- })
+ }, jsonx.V1())
c.Response().Header().Set("Content-Type", "application/x-amz-json-1.0")
return c.JSONBlob(http.StatusBadRequest, body)
@@ -352,7 +353,7 @@ func dispatchDescribeStream(
) (any, error) {
var input dynamodbstreams.DescribeStreamInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errSerialization, err)
}
}
@@ -370,7 +371,7 @@ func dispatchDescribeStream(
}
func writeJSONError(c *echo.Context, status int, errType, message string) error {
- body, _ := json.Marshal(map[string]string{keyErrType: errType, keyMessage: message})
+ body, _ := json.Marshal(map[string]string{keyErrType: errType, keyMessage: message}, jsonx.V1())
c.Response().Header().Set("Content-Type", "application/x-amz-json-1.0")
return c.JSONBlob(status, body)
diff --git a/services/dynamodbstreams/handler_errors_test.go b/services/dynamodbstreams/handler_errors_test.go
index b8c9416e91..52a433b81e 100644
--- a/services/dynamodbstreams/handler_errors_test.go
+++ b/services/dynamodbstreams/handler_errors_test.go
@@ -1,13 +1,14 @@
package dynamodbstreams_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ddbbackend "github.com/blackbirdworks/gopherstack/services/dynamodb"
"github.com/blackbirdworks/gopherstack/services/dynamodbstreams"
)
@@ -49,7 +50,7 @@ func TestHandler_ErrorPropagation_ResourceNotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, w.Code)
var errBody map[string]string
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody, jsonx.V1()))
// Must use a structured error type (not just the operation name).
assert.Contains(t, errBody["__type"], "ResourceNotFoundException",
@@ -95,7 +96,7 @@ func TestHandler_ErrorPropagation_ValidationException(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, w.Code)
var errBody map[string]string
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody, jsonx.V1()))
assert.Contains(t, errBody["__type"], tt.wantErrType,
"error __type must contain %s", tt.wantErrType)
})
@@ -140,7 +141,7 @@ func TestHandler_ErrorNamespace_DynamoDBStreams(t *testing.T) {
assert.Equal(t, 400, resp.Code)
var errBody map[string]string
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &errBody, jsonx.V1()))
errType := errBody["__type"]
assert.Contains(t, errType, tt.wantErr)
diff --git a/services/dynamodbstreams/handler_lifecycle_test.go b/services/dynamodbstreams/handler_lifecycle_test.go
index 338dc26093..79cde0583c 100644
--- a/services/dynamodbstreams/handler_lifecycle_test.go
+++ b/services/dynamodbstreams/handler_lifecycle_test.go
@@ -1,7 +1,7 @@
package dynamodbstreams_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ddbbackend "github.com/blackbirdworks/gopherstack/services/dynamodb"
"github.com/blackbirdworks/gopherstack/services/dynamodbstreams"
)
@@ -77,7 +78,7 @@ func TestHandler_FullLifecycle_StreamsFlow(t *testing.T) {
fmt.Sprintf(`{"TableName":"%s"}`, tableName))
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
streams, ok := listOut["Streams"].([]any)
require.True(t, ok)
require.Len(t, streams, 1)
@@ -87,7 +88,7 @@ func TestHandler_FullLifecycle_StreamsFlow(t *testing.T) {
fmt.Sprintf(`{"StreamArn":"%s"}`, table.StreamARN))
require.Equal(t, http.StatusOK, descResp.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut, jsonx.V1()))
streamDesc := descOut["StreamDescription"].(map[string]any)
shards := streamDesc["Shards"].([]any)
require.NotEmpty(t, shards)
@@ -99,7 +100,7 @@ func TestHandler_FullLifecycle_StreamsFlow(t *testing.T) {
table.StreamARN, shardID))
require.Equal(t, http.StatusOK, iterResp.Code)
var iterOut map[string]any
- require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut))
+ require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut, jsonx.V1()))
shardIter, ok := iterOut["ShardIterator"].(string)
require.True(t, ok)
@@ -108,7 +109,7 @@ func TestHandler_FullLifecycle_StreamsFlow(t *testing.T) {
fmt.Sprintf(`{"ShardIterator":"%s"}`, shardIter))
require.Equal(t, http.StatusOK, recResp.Code)
var recOut map[string]any
- require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &recOut))
+ require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &recOut, jsonx.V1()))
records, ok := recOut["Records"].([]any)
require.True(t, ok)
require.Len(t, records, 3, "must return all 3 stream events")
@@ -124,7 +125,7 @@ func TestHandler_FullLifecycle_StreamsFlow(t *testing.T) {
fmt.Sprintf(`{"ShardIterator":"%s"}`, nextIter))
require.Equal(t, http.StatusOK, emptyResp.Code)
var emptyOut map[string]any
- require.NoError(t, json.Unmarshal(emptyResp.Body.Bytes(), &emptyOut))
+ require.NoError(t, json.Unmarshal(emptyResp.Body.Bytes(), &emptyOut, jsonx.V1()))
emptyRecords, ok := emptyOut["Records"].([]any)
if ok {
assert.Empty(t, emptyRecords, "NextShardIterator must return no new records")
diff --git a/services/dynamodbstreams/handler_records_test.go b/services/dynamodbstreams/handler_records_test.go
index 1885edf9bb..e28b7f66a8 100644
--- a/services/dynamodbstreams/handler_records_test.go
+++ b/services/dynamodbstreams/handler_records_test.go
@@ -1,7 +1,7 @@
package dynamodbstreams_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ddbbackend "github.com/blackbirdworks/gopherstack/services/dynamodb"
"github.com/blackbirdworks/gopherstack/services/dynamodbstreams"
)
@@ -30,7 +31,7 @@ func TestHandler_ErrorPropagation_InvalidShardIterator(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, w.Code)
var errBody map[string]string
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody, jsonx.V1()))
assert.NotEmpty(t, errBody["__type"], "error response must include __type")
}
@@ -49,7 +50,7 @@ func TestHandler_WireFormat_ApproximateCreationDateTimeIsFloat(t *testing.T) {
require.Equal(t, http.StatusOK, iterResp.Code)
var iterOut map[string]any
- require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut))
+ require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut, jsonx.V1()))
shardIter, ok := iterOut["ShardIterator"].(string)
require.True(t, ok)
@@ -61,7 +62,7 @@ func TestHandler_WireFormat_ApproximateCreationDateTimeIsFloat(t *testing.T) {
// Parse raw JSON to verify ApproximateCreationDateTime is a number (float64),
// not a string. The DynamoDB Streams JSON 1.0 protocol encodes it as epoch seconds.
var rawOut map[string]any
- require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &rawOut))
+ require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &rawOut, jsonx.V1()))
records, ok := rawOut["Records"].([]any)
require.True(t, ok)
@@ -139,7 +140,7 @@ func TestHandler_WireFormat_AttributeValueEncoding(t *testing.T) {
require.Equal(t, http.StatusOK, iterResp.Code)
var iterOut map[string]any
- require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut))
+ require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut, jsonx.V1()))
shardIter, ok := iterOut["ShardIterator"].(string)
require.True(t, ok)
@@ -148,7 +149,7 @@ func TestHandler_WireFormat_AttributeValueEncoding(t *testing.T) {
require.Equal(t, http.StatusOK, recResp.Code)
var rawOut map[string]any
- require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &rawOut))
+ require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &rawOut, jsonx.V1()))
records := rawOut["Records"].([]any)
require.Len(t, records, 1)
@@ -177,7 +178,7 @@ func TestHandler_OpaqueIterator_TokenNotDecodable(t *testing.T) {
require.Equal(t, http.StatusOK, w.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &out, jsonx.V1()))
token, ok := out["ShardIterator"].(string)
require.True(t, ok)
@@ -231,7 +232,7 @@ func TestHandler_GetShardIterator_UnknownShardId(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, w.Code)
var errBody map[string]string
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody, jsonx.V1()))
assert.Contains(t, errBody["__type"], "ResourceNotFoundException")
}
@@ -259,7 +260,7 @@ func TestHandler_GetRecords_EventFields(t *testing.T) {
iterResp := doRequest(t, handler, "GetShardIterator", iterBody)
require.Equal(t, http.StatusOK, iterResp.Code)
var iterOut map[string]any
- require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut))
+ require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut, jsonx.V1()))
shardIter := iterOut["ShardIterator"].(string)
recResp := doRequest(t, handler, "GetRecords",
@@ -267,7 +268,7 @@ func TestHandler_GetRecords_EventFields(t *testing.T) {
require.Equal(t, http.StatusOK, recResp.Code)
var recOut map[string]any
- require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &recOut))
+ require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &recOut, jsonx.V1()))
records := recOut["Records"].([]any)
require.NotEmpty(t, records)
@@ -339,13 +340,13 @@ func TestHandler_GetShardIterator_AtAfterSequenceNumber_ViaHTTP(t *testing.T) {
trimIterResp := doRequest(t, handler, "GetShardIterator", trimIterBody)
require.Equal(t, http.StatusOK, trimIterResp.Code)
var trimIterOut map[string]any
- require.NoError(t, json.Unmarshal(trimIterResp.Body.Bytes(), &trimIterOut))
+ require.NoError(t, json.Unmarshal(trimIterResp.Body.Bytes(), &trimIterOut, jsonx.V1()))
trimIter := trimIterOut["ShardIterator"].(string)
allRecResp := doRequest(t, handler, "GetRecords", fmt.Sprintf(`{"ShardIterator":"%s"}`, trimIter))
require.Equal(t, http.StatusOK, allRecResp.Code)
var allRecOut map[string]any
- require.NoError(t, json.Unmarshal(allRecResp.Body.Bytes(), &allRecOut))
+ require.NoError(t, json.Unmarshal(allRecResp.Body.Bytes(), &allRecOut, jsonx.V1()))
allRecords := allRecOut["Records"].([]any)
require.Len(t, allRecords, 3)
midSeq := allRecords[1].(map[string]any)["dynamodb"].(map[string]any)["SequenceNumber"].(string)
@@ -379,14 +380,14 @@ func TestHandler_GetShardIterator_AtAfterSequenceNumber_ViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, iterResp.Code)
var iterOut map[string]any
- require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut))
+ require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut, jsonx.V1()))
shardIter := iterOut["ShardIterator"].(string)
recResp := doRequest(t, handler, "GetRecords", fmt.Sprintf(`{"ShardIterator":"%s"}`, shardIter))
require.Equal(t, http.StatusOK, recResp.Code)
var recOut map[string]any
- require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &recOut))
+ require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &recOut, jsonx.V1()))
records, _ := recOut["Records"].([]any)
assert.Len(t, records, tt.wantCount)
})
@@ -426,7 +427,7 @@ func TestHandler_GetShardIterator_AtAfterSequenceNumber_MissingSequenceNumber(t
assert.Equal(t, http.StatusBadRequest, w.Code)
var errBody map[string]string
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody, jsonx.V1()))
assert.Contains(t, errBody["__type"], "ValidationException")
assert.Contains(t, errBody["__type"], "dynamodbstreams")
})
@@ -490,7 +491,7 @@ func TestHandler_ErrorPropagation_TrimmedDataAccess(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, w.Code)
var errBody map[string]string
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &errBody, jsonx.V1()))
assert.Contains(t, errBody["__type"], "TrimmedDataAccessException")
assert.Contains(t, errBody["__type"], "dynamodbstreams",
"error __type must use the dynamodbstreams namespace")
@@ -606,13 +607,13 @@ func TestHandler_GetRecords_StreamViewType_RecordShapes(t *testing.T) {
iterResp := doRequest(t, handler, "GetShardIterator", iterBody)
require.Equal(t, http.StatusOK, iterResp.Code)
var iterOut map[string]any
- require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut))
+ require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut, jsonx.V1()))
shardIter := iterOut["ShardIterator"].(string)
recResp := doRequest(t, handler, "GetRecords", fmt.Sprintf(`{"ShardIterator":"%s"}`, shardIter))
require.Equal(t, http.StatusOK, recResp.Code)
var recOut map[string]any
- require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &recOut))
+ require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &recOut, jsonx.V1()))
records := recOut["Records"].([]any)
require.Len(t, records, 2, "must have exactly INSERT + MODIFY records")
@@ -693,14 +694,14 @@ func TestHandler_GetRecords_Limit(t *testing.T) {
iterResp := doRequest(t, handler, "GetShardIterator", iterBody)
require.Equal(t, http.StatusOK, iterResp.Code)
var iterOut map[string]any
- require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut))
+ require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut, jsonx.V1()))
shardIter := iterOut["ShardIterator"].(string)
// First page: Limit=2.
recResp := doRequest(t, handler, "GetRecords", fmt.Sprintf(`{"ShardIterator":"%s","Limit":2}`, shardIter))
require.Equal(t, http.StatusOK, recResp.Code)
var recOut map[string]any
- require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &recOut))
+ require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &recOut, jsonx.V1()))
records := recOut["Records"].([]any)
require.Len(t, records, 2, "Limit must cap the number of records returned")
@@ -711,7 +712,7 @@ func TestHandler_GetRecords_Limit(t *testing.T) {
recResp2 := doRequest(t, handler, "GetRecords", fmt.Sprintf(`{"ShardIterator":"%s"}`, nextIter))
require.Equal(t, http.StatusOK, recResp2.Code)
var recOut2 map[string]any
- require.NoError(t, json.Unmarshal(recResp2.Body.Bytes(), &recOut2))
+ require.NoError(t, json.Unmarshal(recResp2.Body.Bytes(), &recOut2, jsonx.V1()))
records2 := recOut2["Records"].([]any)
assert.Len(t, records2, 3, "remaining 3 records must be returned on the next page")
}
@@ -792,7 +793,7 @@ func TestHandler_GetShardIterator_AllIteratorTypes_ViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, iterResp.Code)
var iterOut map[string]any
- require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut))
+ require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut, jsonx.V1()))
shardIter := iterOut["ShardIterator"].(string)
recResp := doRequest(t, handler, "GetRecords",
@@ -800,7 +801,7 @@ func TestHandler_GetShardIterator_AllIteratorTypes_ViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, recResp.Code)
var recOut map[string]any
- require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &recOut))
+ require.NoError(t, json.Unmarshal(recResp.Body.Bytes(), &recOut, jsonx.V1()))
records, _ := recOut["Records"].([]any)
assert.Len(t, records, tt.wantCount)
})
diff --git a/services/dynamodbstreams/handler_streams_test.go b/services/dynamodbstreams/handler_streams_test.go
index c39602d073..8875d52a2b 100644
--- a/services/dynamodbstreams/handler_streams_test.go
+++ b/services/dynamodbstreams/handler_streams_test.go
@@ -1,7 +1,7 @@
package dynamodbstreams_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ddbbackend "github.com/blackbirdworks/gopherstack/services/dynamodb"
"github.com/blackbirdworks/gopherstack/services/dynamodbstreams"
)
@@ -92,7 +93,7 @@ func TestHandler_DescribeStream_ShardGenealogy(t *testing.T) {
require.Equal(t, 200, descResp.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut, jsonx.V1()))
streamDesc := descOut["StreamDescription"].(map[string]any)
shards := streamDesc["Shards"].([]any)
@@ -160,7 +161,7 @@ func TestHandler_DescribeStream_Pagination_ExclusiveStartShardId(t *testing.T) {
fmt.Sprintf(`{"StreamArn":"%s","Limit":1}`, table.StreamARN))
require.Equal(t, 200, page1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(page1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(page1.Body.Bytes(), &out1, jsonx.V1()))
desc1 := out1["StreamDescription"].(map[string]any)
shards1 := desc1["Shards"].([]any)
require.Len(t, shards1, 1, "Limit=1 must cap the shard list to 1")
@@ -174,7 +175,7 @@ func TestHandler_DescribeStream_Pagination_ExclusiveStartShardId(t *testing.T) {
fmt.Sprintf(`{"StreamArn":"%s","ExclusiveStartShardId":"%s"}`, table.StreamARN, lastShardID))
require.Equal(t, 200, page2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(page2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(page2.Body.Bytes(), &out2, jsonx.V1()))
desc2 := out2["StreamDescription"].(map[string]any)
shards2 := desc2["Shards"].([]any)
require.NotEmpty(t, shards2, "second page must return the remaining shards")
@@ -217,7 +218,7 @@ func TestHandler_ListStreams_PaginationViaHTTP(t *testing.T) {
w1 := doRequest(t, handler, "ListStreams", `{"Limit":1}`)
require.Equal(t, 200, w1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(w1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(w1.Body.Bytes(), &out1, jsonx.V1()))
streams1 := out1["Streams"].([]any)
require.Len(t, streams1, 1)
lastArn1, ok := out1["LastEvaluatedStreamArn"].(string)
@@ -228,7 +229,7 @@ func TestHandler_ListStreams_PaginationViaHTTP(t *testing.T) {
fmt.Sprintf(`{"Limit":1,"ExclusiveStartStreamArn":"%s"}`, lastArn1))
require.Equal(t, 200, w2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(w2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(w2.Body.Bytes(), &out2, jsonx.V1()))
streams2 := out2["Streams"].([]any)
require.Len(t, streams2, 1)
@@ -270,7 +271,7 @@ func TestHandler_DescribeStream_StreamLabel(t *testing.T) {
require.Equal(t, 200, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
desc, ok := out["StreamDescription"].(map[string]any)
require.True(t, ok)
@@ -301,7 +302,7 @@ func TestHandler_DescribeStream_CreationRequestDateTime(t *testing.T) {
require.Equal(t, 200, resp.Code)
var rawOut map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &rawOut))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &rawOut, jsonx.V1()))
desc, ok := rawOut["StreamDescription"].(map[string]any)
require.True(t, ok)
@@ -326,7 +327,7 @@ func TestHandler_ListStreams_StreamLabel(t *testing.T) {
require.Equal(t, 200, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
streams, ok := out["Streams"].([]any)
require.True(t, ok)
@@ -402,7 +403,7 @@ func TestHandler_DescribeStream_StreamARNConsistency(t *testing.T) {
require.Equal(t, 200, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
desc, ok := out["StreamDescription"].(map[string]any)
require.True(t, ok)
diff --git a/services/dynamodbstreams/handler_test.go b/services/dynamodbstreams/handler_test.go
index 54e21a7318..df56fa9cc4 100644
--- a/services/dynamodbstreams/handler_test.go
+++ b/services/dynamodbstreams/handler_test.go
@@ -2,7 +2,7 @@ package dynamodbstreams_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ddbbackend "github.com/blackbirdworks/gopherstack/services/dynamodb"
"github.com/blackbirdworks/gopherstack/services/dynamodbstreams"
)
@@ -260,7 +261,7 @@ func TestHandler_GetRecords(t *testing.T) {
require.Equal(t, http.StatusOK, iterResp.Code)
var iterOut map[string]any
- require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut))
+ require.NoError(t, json.Unmarshal(iterResp.Body.Bytes(), &iterOut, jsonx.V1()))
shardIterRaw, ok := iterOut["ShardIterator"]
require.True(t, ok, "ShardIterator key expected in response")
diff --git a/services/dynamodbstreams/handler_wire_test.go b/services/dynamodbstreams/handler_wire_test.go
index 769c5b9e53..9ab510ca51 100644
--- a/services/dynamodbstreams/handler_wire_test.go
+++ b/services/dynamodbstreams/handler_wire_test.go
@@ -1,7 +1,7 @@
package dynamodbstreams_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ddbbackend "github.com/blackbirdworks/gopherstack/services/dynamodb"
"github.com/blackbirdworks/gopherstack/services/dynamodbstreams"
@@ -127,7 +128,7 @@ func TestHandler_WireFormat_NoResultMetadataLeak(t *testing.T) {
} `json:"Shards"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(descW.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(descW.Body.Bytes(), &desc, jsonx.V1()))
require.NotEmpty(t, desc.StreamDescription.Shards)
body := `{"StreamArn":"` + streamARN + `","ShardId":"` +
diff --git a/services/dynamodbstreams/isolation_test.go b/services/dynamodbstreams/isolation_test.go
index 3165fc35f0..01a03171e3 100644
--- a/services/dynamodbstreams/isolation_test.go
+++ b/services/dynamodbstreams/isolation_test.go
@@ -2,7 +2,7 @@ package dynamodbstreams_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ddbbackend "github.com/blackbirdworks/gopherstack/services/dynamodb"
"github.com/blackbirdworks/gopherstack/services/dynamodbstreams"
)
@@ -101,7 +102,7 @@ func TestDynamoDBStreamsRegionIsolation(t *testing.T) {
StreamArn string `json:"StreamArn"`
} `json:"Streams"`
}
- require.NoError(t, json.Unmarshal(eastResp.Body.Bytes(), &eastOut))
+ require.NoError(t, json.Unmarshal(eastResp.Body.Bytes(), &eastOut, jsonx.V1()))
require.Len(t, eastOut.Streams, 1)
assert.Contains(t, eastOut.Streams[0].StreamArn, "us-east-1")
@@ -114,7 +115,7 @@ func TestDynamoDBStreamsRegionIsolation(t *testing.T) {
StreamArn string `json:"StreamArn"`
} `json:"Streams"`
}
- require.NoError(t, json.Unmarshal(westResp.Body.Bytes(), &westOut))
+ require.NoError(t, json.Unmarshal(westResp.Body.Bytes(), &westOut, jsonx.V1()))
require.Len(t, westOut.Streams, 1)
assert.Contains(t, westOut.Streams[0].StreamArn, "us-west-2")
@@ -158,7 +159,7 @@ func TestDynamoDBStreamsDefaultRegionFallback(t *testing.T) {
StreamArn string `json:"StreamArn"`
} `json:"Streams"`
}
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Streams, 1)
assert.Contains(t, out.Streams[0].StreamArn, "eu-central-1")
}
diff --git a/services/dynamodbstreams/request_errors_test.go b/services/dynamodbstreams/request_errors_test.go
index 62d763f652..1f81750052 100644
--- a/services/dynamodbstreams/request_errors_test.go
+++ b/services/dynamodbstreams/request_errors_test.go
@@ -1,13 +1,14 @@
package dynamodbstreams_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ddbbackend "github.com/blackbirdworks/gopherstack/services/dynamodb"
"github.com/blackbirdworks/gopherstack/services/dynamodbstreams"
)
@@ -60,7 +61,7 @@ func TestHandler_RequestErrorShapes(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, w.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(w.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, tt.wantType, body["__type"])
assert.NotEmpty(t, body["message"])
diff --git a/services/ec2/cleanup_test.go b/services/ec2/cleanup_test.go
index 22d43bf30c..2749e02970 100644
--- a/services/ec2/cleanup_test.go
+++ b/services/ec2/cleanup_test.go
@@ -1,7 +1,8 @@
package ec2_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"slices"
"testing"
"time"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ec2"
)
@@ -1208,8 +1210,8 @@ func TestSnapshot_NoRedundantInstanceIMDSOptions(t *testing.T) {
data := b.Snapshot(t.Context())
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(data, &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(data, &raw, jsonx.V1()))
_, ok := raw["instanceIMDSOptions"]
assert.False(t, ok, "instanceIMDSOptions must not be persisted; it is a dead shadow of Instance fields")
diff --git a/services/ec2/client_vpn_auth_policy.go b/services/ec2/client_vpn_auth_policy.go
index 65e6a9c45f..9ab34853ee 100644
--- a/services/ec2/client_vpn_auth_policy.go
+++ b/services/ec2/client_vpn_auth_policy.go
@@ -1,8 +1,10 @@
package ec2
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -49,7 +51,7 @@ func (b *InMemoryBackend) ModifyClientVpnEndpointAuthorizationPolicy(
if policyDocument != nil {
var doc map[string]any
- if err := json.Unmarshal([]byte(*policyDocument), &doc); err != nil || doc == nil {
+ if err := json.Unmarshal([]byte(*policyDocument), &doc, jsonx.V1()); err != nil || doc == nil {
return false, fmt.Errorf("%w: PolicyDocument must be a JSON object", ErrInvalidParameter)
}
}
diff --git a/services/ec2/docker_ports_internal_test.go b/services/ec2/docker_ports_internal_test.go
index 77626e61f0..cece872646 100644
--- a/services/ec2/docker_ports_internal_test.go
+++ b/services/ec2/docker_ports_internal_test.go
@@ -1,7 +1,7 @@
package ec2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,8 @@ import (
mobynetwork "github.com/moby/moby/api/types/network"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestWorldOpenPorts(t *testing.T) {
@@ -255,7 +257,7 @@ func TestServePublishedPorts(t *testing.T) {
}
var body publishedPortsResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, tt.instance, body.InstanceID)
require.Len(t, body.Ports, tt.wantPorts)
assert.Equal(t, 8080, body.Ports[0].ContainerPort)
diff --git a/services/ec2/images_public_catalog_test.go b/services/ec2/images_public_catalog_test.go
index ec191ce956..db122f7d4b 100644
--- a/services/ec2/images_public_catalog_test.go
+++ b/services/ec2/images_public_catalog_test.go
@@ -1,7 +1,7 @@
package ec2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"os"
"strings"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ec2"
)
@@ -28,7 +29,7 @@ func TestSSMPublicParameterAMIsResolve(t *testing.T) {
} `json:"parameters"`
}
- require.NoError(t, json.Unmarshal(raw, &catalog))
+ require.NoError(t, json.Unmarshal(raw, &catalog, jsonx.V1()))
b := ec2.NewInMemoryBackend("000000000000", "us-east-1")
client := newTestEC2Client(t, ec2.NewHandler(b))
diff --git a/services/ec2/imds_test.go b/services/ec2/imds_test.go
index 56e755837c..c930de96a4 100644
--- a/services/ec2/imds_test.go
+++ b/services/ec2/imds_test.go
@@ -3,7 +3,7 @@ package ec2_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/http/httptest"
@@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ec2"
)
@@ -381,7 +382,7 @@ func TestIMDSIdentityDocument(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var doc map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &doc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &doc, jsonx.V1()))
assert.Equal(t, f.instanceID, doc["instanceId"])
assert.Equal(t, "us-east-1", doc["region"])
assert.Equal(t, "000000000000", doc["accountId"])
@@ -403,7 +404,7 @@ func TestIMDSIAMCredentials(t *testing.T) {
require.Equal(t, http.StatusOK, info.Code)
var infoDoc map[string]string
- require.NoError(t, json.Unmarshal(info.Body.Bytes(), &infoDoc))
+ require.NoError(t, json.Unmarshal(info.Body.Bytes(), &infoDoc, jsonx.V1()))
assert.Equal(t, "Success", infoDoc["Code"])
assert.Equal(t, imdsProfile, infoDoc["InstanceProfileArn"])
assert.Equal(t, "AIPAEXAMPLE", infoDoc["InstanceProfileId"])
@@ -415,7 +416,7 @@ func TestIMDSIAMCredentials(t *testing.T) {
require.Equal(t, http.StatusOK, cred.Code)
var credDoc map[string]string
- require.NoError(t, json.Unmarshal(cred.Body.Bytes(), &credDoc))
+ require.NoError(t, json.Unmarshal(cred.Body.Bytes(), &credDoc, jsonx.V1()))
assert.Equal(t, "Success", credDoc["Code"])
assert.Equal(t, "AWS-HMAC", credDoc["Type"])
assert.Equal(t, "ASIA1", credDoc["AccessKeyId"])
diff --git a/services/ec2/imds_tree.go b/services/ec2/imds_tree.go
index 2e76d5aca1..857391dc43 100644
--- a/services/ec2/imds_tree.go
+++ b/services/ec2/imds_tree.go
@@ -4,11 +4,14 @@ import (
"context"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"slices"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -295,7 +298,7 @@ func (t *imdsTree) addIdentityDocument(v *imdsView) {
}
t.leaves["dynamic/instance-identity/document"] = func() (string, error) {
- out, err := json.MarshalIndent(doc, "", imdsDocumentIndex)
+ out, err := json.Marshal(doc, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(imdsDocumentIndex))
return string(out), err
}
@@ -353,9 +356,9 @@ func (h *Handler) addIAMLeaves(t *imdsTree, v *imdsView) {
updated := v.Instance.LaunchTime.UTC().Format(imdsTimeLayout)
t.leaves["meta-data/iam/info"] = func() (string, error) {
- out, mErr := json.MarshalIndent(imdsInfoDocument{
+ out, mErr := json.Marshal(imdsInfoDocument{
Code: imdsCodeSuccess, LastUpdated: updated, InstanceProfileArn: prof.ARN, InstanceProfileID: prof.ID,
- }, "", imdsDocumentIndex)
+ }, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(imdsDocumentIndex))
return string(out), mErr
}
@@ -366,11 +369,11 @@ func (h *Handler) addIAMLeaves(t *imdsTree, v *imdsView) {
return "", cErr
}
- out, mErr := json.MarshalIndent(imdsCredentialDocument{
+ out, mErr := json.Marshal(imdsCredentialDocument{
Code: imdsCodeSuccess, LastUpdated: updated, Type: imdsCredType,
AccessKeyID: creds.AccessKeyID, SecretAccessKey: creds.SecretAccessKey, Token: creds.SessionToken,
Expiration: creds.Expires.UTC().Format(imdsTimeLayout),
- }, "", imdsDocumentIndex)
+ }, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(imdsDocumentIndex))
return string(out), mErr
}
diff --git a/services/ec2/ipam_routing_policy.go b/services/ec2/ipam_routing_policy.go
index e6f2226666..a2ab92ffdd 100644
--- a/services/ec2/ipam_routing_policy.go
+++ b/services/ec2/ipam_routing_policy.go
@@ -1,12 +1,13 @@
package ec2
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- IPAM Routing Policy Registrations ----
@@ -40,7 +41,7 @@ func (b *InMemoryBackend) newRoutingPolicyDeltaLocked(
assocID string,
doc routingPolicyDeltaDoc,
) *IpamRoutingPolicyRegistrationDelta {
- body, _ := json.Marshal(doc)
+ body, _ := json.Marshal(doc, jsonx.V1())
delta := &IpamRoutingPolicyRegistrationDelta{
DeltaID: awsid.New("ipam-rpr-delta-"),
@@ -294,7 +295,7 @@ func (b *InMemoryBackend) BatchModifyIpamRoutingPolicyRegistrations(
}
var entries []routingPolicyBatchEntry
- if err := json.Unmarshal([]byte(deltaJSON), &entries); err != nil {
+ if err := json.Unmarshal([]byte(deltaJSON), &entries, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: DeltaJson is not a valid batch document: %w", ErrInvalidParameter, err)
}
diff --git a/services/ec2/mac_hosts.go b/services/ec2/mac_hosts.go
index 2873ad48d6..32aa91d175 100644
--- a/services/ec2/mac_hosts.go
+++ b/services/ec2/mac_hosts.go
@@ -1,12 +1,14 @@
package ec2
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"sort"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// mac_hosts.go: a Mac Dedicated Host has no separate creation API -- it's a
@@ -167,7 +169,7 @@ func validateMacCredentials(raw string) error {
}
var creds macCredentials
- if err := json.Unmarshal([]byte(raw), &creds); err != nil {
+ if err := json.Unmarshal([]byte(raw), &creds, jsonx.V1()); err != nil {
return fmt.Errorf("%w: MacCredentials must be a JSON object: %w", ErrInvalidParameter, err)
}
diff --git a/services/ec2/persistence.go b/services/ec2/persistence.go
index e8ecdf3b3b..b8ea0ba82c 100644
--- a/services/ec2/persistence.go
+++ b/services/ec2/persistence.go
@@ -2,10 +2,12 @@ package ec2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -23,7 +25,7 @@ const ec2SnapshotVersion = 2
type snapTGWRTProp = TransitGatewayRouteTablePropagation
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
SnapshotAttributes map[string]map[string]string `json:"snapshotAttributes"`
ImageDeprecated map[string]string `json:"imageDeprecated"`
Tags map[string]map[string]string `json:"tags,omitempty"`
@@ -167,7 +169,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
ManagedResourceVisibility: b.managedResourceDefaultVisibility,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "ec2: Snapshot marshal failure", "error", err)
diff --git a/services/ec2/usage_metrics.go b/services/ec2/usage_metrics.go
index 42405efaae..cea32f8efd 100644
--- a/services/ec2/usage_metrics.go
+++ b/services/ec2/usage_metrics.go
@@ -2,7 +2,8 @@ package ec2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strings"
@@ -10,6 +11,7 @@ import (
mobyclient "github.com/moby/moby/client"
"github.com/blackbirdworks/gopherstack/pkgs/cwmetric"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -120,7 +122,7 @@ func (d *DockerCompute) Usage(ctx context.Context, providerID string) (InstanceU
defer func() { _ = res.Body.Close() }()
var stats mobycontainer.StatsResponse
- if decodeErr := json.NewDecoder(res.Body).Decode(&stats); decodeErr != nil {
+ if decodeErr := json.UnmarshalDecode(jsontext.NewDecoder(res.Body), &stats, jsonx.V1()); decodeErr != nil {
return InstanceUsage{}, fmt.Errorf("decode container stats: %w", decodeErr)
}
diff --git a/services/ecr/auth_token_test.go b/services/ecr/auth_token_test.go
index a47ec0c9f0..c69e8a913b 100644
--- a/services/ecr/auth_token_test.go
+++ b/services/ecr/auth_token_test.go
@@ -6,7 +6,7 @@ package ecr_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecr"
)
@@ -261,7 +262,7 @@ func TestECR_GetAuthorizationToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
authData, ok := resp["authorizationData"].([]any)
require.True(t, ok)
diff --git a/services/ecr/handler.go b/services/ecr/handler.go
index 0cad5d68e5..93ac699393 100644
--- a/services/ecr/handler.go
+++ b/services/ecr/handler.go
@@ -2,7 +2,7 @@ package ecr
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -16,6 +16,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -345,7 +346,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
RepositoryNames []string `json:"repositoryNames"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if req.RepositoryName != "" {
return req.RepositoryName
@@ -543,7 +544,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// ecrErr builds the error body for an ECR error response.
@@ -611,14 +612,11 @@ func (h *Handler) classifyError(err error) (int, string) {
}
}
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, errUnknownAction):
return http.StatusBadRequest, "UnknownOperationException"
case errors.Is(err, ErrInvalidRepositoryName), errors.Is(err, errInvalidRequest),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
return http.StatusBadRequest, "InvalidParameterException"
default:
return http.StatusInternalServerError, "ServerException"
diff --git a/services/ecr/handler_images.go b/services/ecr/handler_images.go
index e2889d9f3b..c6f80a09af 100644
--- a/services/ecr/handler_images.go
+++ b/services/ecr/handler_images.go
@@ -5,11 +5,13 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// batchDeleteImageInput is the request body for BatchDeleteImage.
@@ -195,7 +197,7 @@ type manifestArtifactFields struct {
func parseManifestArtifactFields(manifest string) (string, string) {
var parsed manifestArtifactFields
- if err := json.Unmarshal([]byte(manifest), &parsed); err != nil {
+ if err := json.Unmarshal([]byte(manifest), &parsed, jsonx.V1()); err != nil {
return "", ""
}
@@ -212,7 +214,7 @@ func parseManifestAnnotations(manifest string) map[string]string {
Annotations map[string]string `json:"annotations"`
}
- if err := json.Unmarshal([]byte(manifest), &parsed); err != nil || len(parsed.Annotations) == 0 {
+ if err := json.Unmarshal([]byte(manifest), &parsed, jsonx.V1()); err != nil || len(parsed.Annotations) == 0 {
return nil
}
diff --git a/services/ecr/handler_test.go b/services/ecr/handler_test.go
index dcf8399394..28ec8a6f8f 100644
--- a/services/ecr/handler_test.go
+++ b/services/ecr/handler_test.go
@@ -12,7 +12,7 @@ package ecr_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -28,6 +28,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/ecr"
)
@@ -82,7 +83,7 @@ func doECRRequest(
) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -124,7 +125,7 @@ func newHandlerWithBackend() (*ecr.Handler, *ecr.InMemoryBackend) {
func doAccuracy(t *testing.T, h *ecr.Handler, action string, body any) *httptest.ResponseRecorder {
t.Helper()
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -141,7 +142,7 @@ func parseAccuracy(t *testing.T, rec *httptest.ResponseRecorder) map[string]any
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
@@ -469,7 +470,7 @@ func TestECR_LazyEndpointInit(t *testing.T) {
h := ecr.NewHandler(backend, nil)
// First request — Host header provides the server address.
- bodyBytes, err := json.Marshal(map[string]any{"repositoryName": "lazy-repo"})
+ bodyBytes, err := json.Marshal(map[string]any{"repositoryName": "lazy-repo"}, jsonx.V1())
require.NoError(t, err)
e := echo.New()
diff --git a/services/ecr/interfaces_test.go b/services/ecr/interfaces_test.go
index 77285d2699..85a072a985 100644
--- a/services/ecr/interfaces_test.go
+++ b/services/ecr/interfaces_test.go
@@ -7,7 +7,7 @@ package ecr_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecr"
)
@@ -412,7 +413,7 @@ func TestECR_Handler_AcceptsBackendInterface(t *testing.T) {
h := ecr.NewHandler(newStubBackend(), nil)
- bodyBytes, err := json.Marshal(map[string]any{"repositoryName": "my-repo"})
+ bodyBytes, err := json.Marshal(map[string]any{"repositoryName": "my-repo"}, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -424,7 +425,7 @@ func TestECR_Handler_AcceptsBackendInterface(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repo, ok := resp["repository"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "my-repo", repo["repositoryName"])
@@ -455,7 +456,7 @@ func TestECR_GetAuthorizationToken_ProxyEndpointFromBackend(t *testing.T) {
h := ecr.NewHandler(newStubBackend(), nil)
- bodyBytes, err := json.Marshal(map[string]any{})
+ bodyBytes, err := json.Marshal(map[string]any{}, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -467,7 +468,7 @@ func TestECR_GetAuthorizationToken_ProxyEndpointFromBackend(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
authData := resp["authorizationData"].([]any)
entry := authData[0].(map[string]any)
diff --git a/services/ecr/lifecycle.go b/services/ecr/lifecycle.go
index d02411d0c9..9c0505fb77 100644
--- a/services/ecr/lifecycle.go
+++ b/services/ecr/lifecycle.go
@@ -2,10 +2,12 @@ package ecr
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sort"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const weeksPerDays = 7
@@ -72,7 +74,7 @@ func evaluateLifecyclePolicy(
}
var doc lifecyclePolicyDoc
- if err := json.Unmarshal([]byte(policyText), &doc); err != nil {
+ if err := json.Unmarshal([]byte(policyText), &doc, jsonx.V1()); err != nil {
return nil
}
diff --git a/services/ecr/lifecycle_policy_test.go b/services/ecr/lifecycle_policy_test.go
index db9a39efa6..2f2e81badb 100644
--- a/services/ecr/lifecycle_policy_test.go
+++ b/services/ecr/lifecycle_policy_test.go
@@ -6,13 +6,14 @@ package ecr_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecr"
)
@@ -174,7 +175,7 @@ func TestECR_DeleteLifecyclePolicy(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.repositoryName, out["repositoryName"])
}
})
diff --git a/services/ecr/manifest_meta.go b/services/ecr/manifest_meta.go
index c986c754d3..6385a0c65d 100644
--- a/services/ecr/manifest_meta.go
+++ b/services/ecr/manifest_meta.go
@@ -1,9 +1,12 @@
package ecr
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"regexp"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -33,14 +36,14 @@ type manifestMeta struct {
Layers []struct {
Size int64 `json:"size"`
} `json:"layers"`
- Manifests []json.RawMessage `json:"manifests"`
+ Manifests []jsontext.Value `json:"manifests"`
}
// manifestMediaTypeAndSize reports the manifest's media type (declared, else
// inferred from its shape) and its compressed size: config plus layer sizes.
func manifestMediaTypeAndSize(manifest string) (string, int64) {
var m manifestMeta
- if err := json.Unmarshal([]byte(manifest), &m); err != nil {
+ if err := json.Unmarshal([]byte(manifest), &m, jsonx.V1()); err != nil {
return "", 0
}
diff --git a/services/ecr/multi_region_test.go b/services/ecr/multi_region_test.go
index 7e427cb3b1..5e502562ba 100644
--- a/services/ecr/multi_region_test.go
+++ b/services/ecr/multi_region_test.go
@@ -1,7 +1,7 @@
package ecr_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecr"
)
@@ -39,7 +40,7 @@ func regionCall(t *testing.T, h *ecr.Handler, region, op, body string) map[strin
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
out := map[string]any{}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/ecr/persistence.go b/services/ecr/persistence.go
index 6f3e364a1a..e36470e2b9 100644
--- a/services/ecr/persistence.go
+++ b/services/ecr/persistence.go
@@ -2,7 +2,7 @@ package ecr
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -52,7 +52,7 @@ const ecrSnapshotVersion = 2
// lifecycleLastEvaluated were never persisted before this conversion and
// still aren't.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
RepoTags map[string]map[string]string `json:"repoTags,omitempty"`
UploadedLayers map[string]map[string]int64 `json:"uploadedLayers,omitempty"`
RegistryScanningConfig *RegistryScanningSettings `json:"registryScanningConfig,omitempty"`
diff --git a/services/ecr/realism_validation_test.go b/services/ecr/realism_validation_test.go
index f68b202195..4c0b7e0c4e 100644
--- a/services/ecr/realism_validation_test.go
+++ b/services/ecr/realism_validation_test.go
@@ -1,7 +1,7 @@
package ecr_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -87,7 +89,7 @@ func TestECRWire_InputValidation(t *testing.T) {
}
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out["__type"])
assert.False(t, strings.HasPrefix(out["message"], tt.wantType), out["message"])
})
diff --git a/services/ecr/repositories_test.go b/services/ecr/repositories_test.go
index e1e716ba53..40e2adce69 100644
--- a/services/ecr/repositories_test.go
+++ b/services/ecr/repositories_test.go
@@ -8,13 +8,14 @@ package ecr_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecr"
)
@@ -53,7 +54,7 @@ func TestECR_CreateRepository(t *testing.T) {
if tt.wantARN || tt.wantURI {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repo, ok := resp["repository"].(map[string]any)
require.True(t, ok)
@@ -141,7 +142,7 @@ func TestECR_DescribeRepositories(t *testing.T) {
if tt.wantLen > 0 {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repos, ok := resp["repositories"].([]any)
require.True(t, ok)
@@ -200,7 +201,7 @@ func TestECR_DeleteRepository(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
repo, ok := resp["repository"].(map[string]any)
require.True(t, ok)
diff --git a/services/ecr/tags_test.go b/services/ecr/tags_test.go
index 97ff4939d3..ac9f33c1b8 100644
--- a/services/ecr/tags_test.go
+++ b/services/ecr/tags_test.go
@@ -6,13 +6,14 @@ package ecr_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecr"
)
@@ -26,7 +27,7 @@ func TestECR_ListTagsForResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
repo := createResp["repository"].(map[string]any)
arn := repo["repositoryArn"].(string)
@@ -35,7 +36,7 @@ func TestECR_ListTagsForResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tags, ok := out["tags"]
require.True(t, ok, "response should contain 'tags' field")
assert.IsType(t, []any{}, tags, "tags should be an array")
@@ -253,7 +254,7 @@ func TestListTagsForResource_HandlerSortsKeys(t *testing.T) {
Value string `json:"Value"`
} `json:"tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
gotKeys := make([]string, 0, len(resp.Tags))
for _, tag := range resp.Tags {
@@ -299,7 +300,7 @@ func TestCreateRepository_TagsRoundTrip(t *testing.T) {
RepositoryARN string `json:"repositoryArn"`
} `json:"repository"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
arn := createResp.Repository.RepositoryARN
listRec := doAccuracy(t, h, "ListTagsForResource", map[string]any{
@@ -310,7 +311,7 @@ func TestCreateRepository_TagsRoundTrip(t *testing.T) {
var listResp struct {
Tags []any `json:"tags"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp.Tags, tt.wantLen)
})
}
diff --git a/services/ecr/validation.go b/services/ecr/validation.go
index 5ab3c4c29d..443c356b18 100644
--- a/services/ecr/validation.go
+++ b/services/ecr/validation.go
@@ -1,10 +1,12 @@
package ecr
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"slices"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -33,7 +35,7 @@ func validateRepositoryName(name string) error {
func validateJSONObject(field, text string) error {
var doc map[string]any
- if err := json.Unmarshal([]byte(text), &doc); err != nil || doc == nil {
+ if err := json.Unmarshal([]byte(text), &doc, jsonx.V1()); err != nil || doc == nil {
return invalidParam("Invalid parameter at '%s' failed to satisfy constraint: 'Invalid JSON syntax'", field)
}
@@ -50,7 +52,7 @@ func validateRepositoryPolicyText(text string) error {
func validateLifecyclePolicyText(text string) error {
var doc lifecyclePolicyDoc
- if err := json.Unmarshal([]byte(text), &doc); err != nil {
+ if err := json.Unmarshal([]byte(text), &doc, jsonx.V1()); err != nil {
return invalidParam("Lifecycle policy validation failure: Invalid JSON")
}
diff --git a/services/ecrpublic/digest.go b/services/ecrpublic/digest.go
index e2df656a20..5c337db6b9 100644
--- a/services/ecrpublic/digest.go
+++ b/services/ecrpublic/digest.go
@@ -3,7 +3,9 @@ package ecrpublic
import (
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func sha256Digest(data []byte) string {
@@ -52,7 +54,7 @@ type manifestDoc struct {
func parseManifest(manifest string) (manifestDoc, bool) {
var m manifestDoc
- return m, json.Unmarshal([]byte(manifest), &m) == nil
+ return m, json.Unmarshal([]byte(manifest), &m, jsonx.V1()) == nil
}
// referencedDigests returns every layer/config digest an image manifest
diff --git a/services/ecrpublic/handler.go b/services/ecrpublic/handler.go
index 92a97f83c7..3d4ac88af6 100644
--- a/services/ecrpublic/handler.go
+++ b/services/ecrpublic/handler.go
@@ -2,7 +2,7 @@ package ecrpublic
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -95,7 +96,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
RepositoryName string `json:"repositoryName"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return req.RepositoryName
}
@@ -149,7 +150,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
@@ -234,14 +235,10 @@ func classifyError(err error) (int, string) {
}
}
- var syntaxErr *json.SyntaxError
-
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, errUnknownAction):
return http.StatusBadRequest, "UnknownOperationException"
- case errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ case jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
return http.StatusBadRequest, "InvalidParameterException"
case errors.Is(err, awserr.ErrNotFound):
return http.StatusBadRequest, "RepositoryNotFoundException"
diff --git a/services/ecrpublic/images_and_layers_test.go b/services/ecrpublic/images_and_layers_test.go
index 1d26dd0d67..36c590e24a 100644
--- a/services/ecrpublic/images_and_layers_test.go
+++ b/services/ecrpublic/images_and_layers_test.go
@@ -4,7 +4,7 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -13,6 +13,8 @@ import (
"github.com/aws/smithy-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func layerDigest(data []byte) string {
@@ -78,7 +80,7 @@ func buildManifest(configDigest string, layerDigests ...string) string {
m.Layers = append(m.Layers, layer{Digest: d})
}
- b, _ := json.Marshal(m)
+ b, _ := json.Marshal(m, jsonx.V1())
return string(b)
}
diff --git a/services/ecrpublic/manifest_index_test.go b/services/ecrpublic/manifest_index_test.go
index f2838b621c..324c649e66 100644
--- a/services/ecrpublic/manifest_index_test.go
+++ b/services/ecrpublic/manifest_index_test.go
@@ -1,7 +1,7 @@
package ecrpublic_test
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/aws/smithy-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func buildIndex(t *testing.T, childDigests ...string) string {
@@ -28,7 +30,7 @@ func buildIndex(t *testing.T, childDigests ...string) string {
doc.Manifests = append(doc.Manifests, child{Digest: d})
}
- b, err := json.Marshal(doc)
+ b, err := json.Marshal(doc, jsonx.V1())
require.NoError(t, err)
return string(b)
diff --git a/services/ecrpublic/persistence.go b/services/ecrpublic/persistence.go
index 2d5b142862..022f3e2b1a 100644
--- a/services/ecrpublic/persistence.go
+++ b/services/ecrpublic/persistence.go
@@ -2,7 +2,7 @@ package ecrpublic
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
"maps"
@@ -34,7 +34,7 @@ const ecrPublicSnapshotVersion = 1
// LayerUploads (in-flight sessions) is deliberately NOT persisted, matching
// AWS: an in-progress upload does not survive a restart.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
TagIndex map[string]map[string]tagBinding `json:"tagIndex,omitempty"`
UploadedLayers map[string]map[string]int64 `json:"uploadedLayers,omitempty"`
RegistryCatalogData RegistryCatalogData `json:"registryCatalogData"`
diff --git a/services/ecrpublic/policy.go b/services/ecrpublic/policy.go
index 5eecd7f604..420db903df 100644
--- a/services/ecrpublic/policy.go
+++ b/services/ecrpublic/policy.go
@@ -1,9 +1,11 @@
package ecrpublic
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// GetRepositoryPolicy returns the repository's resource policy text.
@@ -29,7 +31,8 @@ func (b *InMemoryBackend) GetRepositoryPolicy(registryID, name string) (string,
// SetRepositoryPolicy sets or replaces the repository's resource policy text.
func (b *InMemoryBackend) SetRepositoryPolicy(registryID, name, policyText string) (string, error) {
- if !json.Valid([]byte(policyText)) || !strings.HasPrefix(strings.TrimSpace(policyText), "{") {
+ if !jsontext.Value([]byte(policyText)).IsValid(jsonx.V1()) ||
+ !strings.HasPrefix(strings.TrimSpace(policyText), "{") {
return "", fmt.Errorf(
"%w: Invalid parameter at 'PolicyText' failed to satisfy constraint: 'Invalid repository policy provided'",
ErrInvalidParameter)
diff --git a/services/ecs/capacity_provider_strategy_validation_test.go b/services/ecs/capacity_provider_strategy_validation_test.go
index 355f21b0db..3f19606e49 100644
--- a/services/ecs/capacity_provider_strategy_validation_test.go
+++ b/services/ecs/capacity_provider_strategy_validation_test.go
@@ -1,12 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestCapacityProviderStrategy_RejectsUnknownProvider proves that every
@@ -169,7 +171,7 @@ func TestCapacityProviderStrategy_AcceptsCreatedProvider(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
cluster := out["cluster"].(map[string]any)
strategy := cluster["defaultCapacityProviderStrategy"].([]any)
require.Len(t, strategy, 1)
@@ -202,7 +204,7 @@ func TestRunTask_CapacityProviderStrategy_SetsCapacityProviderName(t *testing.T)
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
tasks := out["tasks"].([]any)
require.Len(t, tasks, 1)
task := tasks[0].(map[string]any)
diff --git a/services/ecs/docker_inputs.go b/services/ecs/docker_inputs.go
index de18c2b688..3178305555 100644
--- a/services/ecs/docker_inputs.go
+++ b/services/ecs/docker_inputs.go
@@ -2,11 +2,12 @@ package ecs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/registryauth"
)
@@ -85,7 +86,7 @@ func (b *InMemoryBackend) resolveRegistryAuth(ctx context.Context, cd ContainerD
Password string `json:"password"`
}
- if json.Unmarshal([]byte(raw), &creds) != nil || creds.Username == "" || creds.Password == "" {
+ if json.Unmarshal([]byte(raw), &creds, jsonx.V1()) != nil || creds.Username == "" || creds.Password == "" {
return "", secretLaunchError(sourceASM, param, errRegistryCredentials)
}
diff --git a/services/ecs/docker_inputs_internal_test.go b/services/ecs/docker_inputs_internal_test.go
index 21d2726c35..daf68e93f0 100644
--- a/services/ecs/docker_inputs_internal_test.go
+++ b/services/ecs/docker_inputs_internal_test.go
@@ -3,11 +3,13 @@ package ecs
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type fakeEnvStore struct {
@@ -122,7 +124,7 @@ func TestRepositoryCredentials(t *testing.T) {
var ac map[string]string
- require.NoError(t, json.Unmarshal(raw, &ac))
+ require.NoError(t, json.Unmarshal(raw, &ac, jsonx.V1()))
assert.Equal(t, tt.wantUser, ac["username"])
assert.Equal(t, "hunter2", ac["password"])
assert.Equal(t, "registry.example.com", ac["serveraddress"])
diff --git a/services/ecs/docker_linklocal_live_internal_test.go b/services/ecs/docker_linklocal_live_internal_test.go
index 5c36c5694c..cb2618da67 100644
--- a/services/ecs/docker_linklocal_live_internal_test.go
+++ b/services/ecs/docker_linklocal_live_internal_test.go
@@ -2,7 +2,8 @@ package ecs
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net"
"net/http"
@@ -21,6 +22,7 @@ import (
dockernetwork "github.com/blackbirdworks/gopherstack/internal/dockercompat/api/types/network"
"github.com/blackbirdworks/gopherstack/internal/dockercompat/client"
"github.com/blackbirdworks/gopherstack/internal/linklocal"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -85,7 +87,10 @@ func TestDockerRunnerLiveRelativeCredentialsURI(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("/_gopherstack/ecs/credentials/"+taskID, func(w http.ResponseWriter, _ *http.Request) {
- _ = json.NewEncoder(w).Encode(map[string]string{"AccessKeyId": "ASIALIVE", "Token": "tok"})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]string{"AccessKeyId": "ASIALIVE", "Token": "tok"},
+ )
})
srv := &http.Server{Handler: mux, ReadHeaderTimeout: time.Second}
diff --git a/services/ecs/docker_secrets.go b/services/ecs/docker_secrets.go
index 0adaa9bf32..2ba02d237b 100644
--- a/services/ecs/docker_secrets.go
+++ b/services/ecs/docker_secrets.go
@@ -2,10 +2,13 @@ package ecs
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -169,8 +172,8 @@ func resolveParameter(ctx context.Context, resolver SecretsResolver, ref secretR
}
func jsonSecretField(secret, key string) (string, error) {
- var fields map[string]json.RawMessage
- if err := json.Unmarshal([]byte(secret), &fields); err != nil {
+ var fields map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(secret), &fields, jsonx.V1()); err != nil {
return "", fmt.Errorf("secret value is not a JSON object: %w", err)
}
@@ -180,7 +183,7 @@ func jsonSecretField(secret, key string) (string, error) {
}
var s string
- if json.Unmarshal(raw, &s) == nil {
+ if json.Unmarshal(raw, &s, jsonx.V1()) == nil {
return s, nil
}
diff --git a/services/ecs/docker_usage.go b/services/ecs/docker_usage.go
index 05b4a3dc18..18ede3b67b 100644
--- a/services/ecs/docker_usage.go
+++ b/services/ecs/docker_usage.go
@@ -2,7 +2,11 @@ package ecs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "encoding/json/jsontext"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const cpuUnitsPerVCPU = 1024.0
@@ -66,7 +70,7 @@ func (r *realDockerRunner) TaskUsage(ctx context.Context, taskArn string) (float
var sample dockerStatsSample
- decodeErr := json.NewDecoder(body).Decode(&sample)
+ decodeErr := json.UnmarshalDecode(jsontext.NewDecoder(body), &sample, jsonx.V1())
_ = body.Close()
if decodeErr != nil {
@@ -83,7 +87,7 @@ func (r *realDockerRunner) TaskUsage(ctx context.Context, taskArn string) (float
}
// ContainerStatsJSON returns one raw Docker stats sample for containerID.
-func (r *realDockerRunner) ContainerStatsJSON(ctx context.Context, containerID string) (json.RawMessage, error) {
+func (r *realDockerRunner) ContainerStatsJSON(ctx context.Context, containerID string) (jsontext.Value, error) {
body, err := r.cli.ContainerStats(ctx, containerID)
if err != nil {
return nil, err
@@ -91,8 +95,8 @@ func (r *realDockerRunner) ContainerStatsJSON(ctx context.Context, containerID s
defer body.Close()
- var raw json.RawMessage
- if decErr := json.NewDecoder(body).Decode(&raw); decErr != nil {
+ var raw jsontext.Value
+ if decErr := json.UnmarshalDecode(jsontext.NewDecoder(body), &raw, jsonx.V1()); decErr != nil {
return nil, decErr
}
diff --git a/services/ecs/events.go b/services/ecs/events.go
index fa8fb9cc12..3350db2612 100644
--- a/services/ecs/events.go
+++ b/services/ecs/events.go
@@ -2,10 +2,11 @@ package ecs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -47,7 +48,7 @@ func (b *InMemoryBackend) emitLocked(detailType string, resources []string, deta
return
}
- body, err := json.Marshal(detail)
+ body, err := json.Marshal(detail, jsonx.V1())
if err != nil {
return
}
diff --git a/services/ecs/events_internal_test.go b/services/ecs/events_internal_test.go
index d6a29817ca..1f63c20fa4 100644
--- a/services/ecs/events_internal_test.go
+++ b/services/ecs/events_internal_test.go
@@ -2,7 +2,7 @@ package ecs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sync"
"testing"
"time"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type publishedEvent struct {
@@ -30,7 +31,7 @@ func (p *recordingPublisher) PublishEvent(
ctx context.Context, source, detailType string, resources []string, detail string,
) {
var d map[string]any
- if err := json.Unmarshal([]byte(detail), &d); err != nil {
+ if err := json.Unmarshal([]byte(detail), &d, jsonx.V1()); err != nil {
panic(err)
}
diff --git a/services/ecs/handler.go b/services/ecs/handler.go
index b5392ba8e1..c50b27c852 100644
--- a/services/ecs/handler.go
+++ b/services/ecs/handler.go
@@ -2,7 +2,7 @@ package ecs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
@@ -194,7 +195,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
TaskSet string `json:"taskSet"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
switch {
case req.ClusterName != "":
@@ -355,7 +356,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// Reset clears the backend state.
@@ -368,9 +369,6 @@ func (h *Handler) Reset() {
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, awserr.ErrNotFound):
code := errorCode(err)
@@ -395,8 +393,8 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
},
)
case errors.Is(err, awserr.ErrInvalidParameter),
- errors.As(err, &syntaxErr),
- errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err),
+ jsonx.IsTypeError(err):
code := errorCode(err)
return c.JSON(
diff --git a/services/ecs/handler_account_settings_test.go b/services/ecs/handler_account_settings_test.go
index f76f2d5120..7e44d82754 100644
--- a/services/ecs/handler_account_settings_test.go
+++ b/services/ecs/handler_account_settings_test.go
@@ -1,13 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -40,7 +41,7 @@ func TestAccountSettings_AllValidNames(t *testing.T) {
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
settings := listOut["settings"].([]any)
assert.GreaterOrEqual(t, len(settings), len(validNames))
}
@@ -71,7 +72,7 @@ func TestAccountSettings_PutDefault_vs_Put(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
settings := listOut["settings"].([]any)
assert.GreaterOrEqual(t, len(settings), 1)
}
@@ -99,7 +100,7 @@ func TestAccountSettings_Delete(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
settings := listOut["settings"].([]any)
assert.Empty(t, settings)
}
@@ -123,7 +124,7 @@ func TestAccountSettings_OverwriteExisting(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
settings := listOut["settings"].([]any)
require.NotEmpty(t, settings)
// Last write wins
@@ -150,7 +151,7 @@ func TestAccountSettings_TaskLongArnFormat(t *testing.T) {
require.Equal(t, http.StatusOK, putResp.Code)
var putOut map[string]any
- require.NoError(t, json.Unmarshal(putResp.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(putResp.Body.Bytes(), &putOut, jsonx.V1()))
setting := putOut["setting"].(map[string]any)
assert.Equal(t, "taskLongArnFormat", setting["name"])
assert.Equal(t, "enabled", setting["value"])
@@ -168,7 +169,7 @@ func TestAccountSettings_ContainerInstanceLongArnFormat(t *testing.T) {
require.Equal(t, http.StatusOK, putResp.Code)
var putOut map[string]any
- require.NoError(t, json.Unmarshal(putResp.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(putResp.Body.Bytes(), &putOut, jsonx.V1()))
setting := putOut["setting"].(map[string]any)
assert.Equal(t, "containerInstanceLongArnFormat", setting["name"])
assert.Equal(t, "enabled", setting["value"])
@@ -192,7 +193,7 @@ func TestAccountSettings_FargateSettings(t *testing.T) {
require.Equal(t, http.StatusOK, putResp.Code, "PutAccountSetting %s", name)
var putOut map[string]any
- require.NoError(t, json.Unmarshal(putResp.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(putResp.Body.Bytes(), &putOut, jsonx.V1()))
setting := putOut["setting"].(map[string]any)
assert.Equal(t, name, setting["name"])
}
@@ -215,7 +216,7 @@ func TestAccountSettings_ServiceLongArnFormat(t *testing.T) {
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
settings := listOut["settings"].([]any)
require.NotEmpty(t, settings)
assert.Equal(t, "enabled", settings[0].(map[string]any)["value"])
@@ -273,7 +274,7 @@ func TestECS_DeleteAccountSetting(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
setting, ok := resp["setting"].(map[string]any)
require.True(t, ok)
@@ -293,7 +294,7 @@ func TestECS_ListAccountSettings(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
settings, ok := resp["settings"].([]any)
require.True(t, ok)
@@ -321,7 +322,7 @@ func TestECS_ListAccountSettings(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
settings, ok := resp["settings"].([]any)
require.True(t, ok)
@@ -376,7 +377,7 @@ func TestECS_PutAccountSetting(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
setting, ok := resp["setting"].(map[string]any)
require.True(t, ok)
@@ -423,7 +424,7 @@ func TestECS_PutAccountSettingDefault(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
setting, ok := resp["setting"].(map[string]any)
require.True(t, ok)
diff --git a/services/ecs/handler_agent_ops_test.go b/services/ecs/handler_agent_ops_test.go
index 99064f0c5b..be2fc4b57a 100644
--- a/services/ecs/handler_agent_ops_test.go
+++ b/services/ecs/handler_agent_ops_test.go
@@ -1,13 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -34,7 +35,7 @@ func TestECS_DescribeServiceRevisions(t *testing.T) {
require.Equal(t, http.StatusOK, svcRec.Code)
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svc := svcResp["service"].(map[string]any)
deployments := svc["deployments"].([]any)
@@ -93,7 +94,7 @@ func TestECS_DescribeServiceRevisions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
revs, _ := resp["serviceRevisions"].([]any)
failures, _ := resp["failures"].([]any)
@@ -158,7 +159,7 @@ func TestECS_DescribeServiceRevisions_TracksUpdate(t *testing.T) {
})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
revs, _ := resp["serviceRevisions"].([]any)
assert.Len(t, revs, 2, "both the original and rotated revision remain describable")
}
@@ -177,7 +178,7 @@ func TestECS_DiscoverPollEndpoint(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["endpoint"], testRegion)
assert.Contains(t, resp["serviceConnectEndpoint"], testRegion)
@@ -202,7 +203,7 @@ func TestECS_SubmitTaskStateChange(t *testing.T) {
require.Equal(t, http.StatusOK, runRec.Code)
var runResp map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runResp))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runResp, jsonx.V1()))
tasks := runResp["tasks"].([]any)
require.Len(t, tasks, 1)
task := tasks[0].(map[string]any)
@@ -220,7 +221,7 @@ func TestECS_SubmitTaskStateChange(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ACK", resp["acknowledgment"])
descRec := doECSRequest(t, h, "DescribeTasks", map[string]any{
@@ -230,7 +231,7 @@ func TestECS_SubmitTaskStateChange(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
descTasks := descResp["tasks"].([]any)
require.Len(t, descTasks, 1)
@@ -259,7 +260,7 @@ func TestECS_SubmitTaskStateChange_UnknownTaskIsNoop(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ACK", resp["acknowledgment"])
}
@@ -278,7 +279,7 @@ func TestECS_SubmitContainerStateChange(t *testing.T) {
})
var runResp map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runResp))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runResp, jsonx.V1()))
taskArn := runResp["tasks"].([]any)[0].(map[string]any)["taskArn"].(string)
rec := doECSRequest(t, h, "SubmitContainerStateChange", map[string]any{
@@ -299,7 +300,7 @@ func TestECS_SubmitContainerStateChange(t *testing.T) {
})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
updated := descResp["tasks"].([]any)[0].(map[string]any)
container := updated["containers"].([]any)[0].(map[string]any)
@@ -326,7 +327,7 @@ func TestECS_SubmitAttachmentStateChanges(t *testing.T) {
})
var runResp map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runResp))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runResp, jsonx.V1()))
task := runResp["tasks"].([]any)[0].(map[string]any)
taskArn := task["taskArn"].(string)
attachments, _ := task["attachments"].([]any)
@@ -347,7 +348,7 @@ func TestECS_SubmitAttachmentStateChanges(t *testing.T) {
})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
updated := descResp["tasks"].([]any)[0].(map[string]any)
updatedAttachments := updated["attachments"].([]any)
require.NotEmpty(t, updatedAttachments)
@@ -392,7 +393,7 @@ func TestHandler_DiscoverPollEndpoint(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantEndpointPfx, out["endpoint"])
if tt.wantTelemetry {
assert.NotEmpty(t, out["telemetryEndpoint"])
@@ -477,7 +478,7 @@ func TestHandler_SubmitTaskStateChange(t *testing.T) {
require.Equal(t, http.StatusOK, runRec.Code)
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut, jsonx.V1()))
tasks := runOut["tasks"].([]any)
require.NotEmpty(t, tasks)
taskArn = tasks[0].(map[string]any)["taskArn"].(string)
@@ -488,7 +489,7 @@ func TestHandler_SubmitTaskStateChange(t *testing.T) {
if tt.wantACK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "ACK", out["acknowledgment"])
}
})
@@ -574,7 +575,7 @@ func TestHandler_SubmitContainerStateChange(t *testing.T) {
})
require.Equal(t, http.StatusOK, runRec.Code)
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut, jsonx.V1()))
taskArn = runOut["tasks"].([]any)[0].(map[string]any)["taskArn"].(string)
}
@@ -588,7 +589,7 @@ func TestHandler_SubmitContainerStateChange(t *testing.T) {
if tt.wantACK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "ACK", out["acknowledgment"])
}
})
@@ -664,7 +665,7 @@ func TestHandler_SubmitAttachmentStateChanges(t *testing.T) {
if tt.wantACK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "ACK", out["acknowledgment"])
}
})
@@ -695,7 +696,7 @@ func TestHandler_DescribeServiceRevisions(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
return svc["serviceArn"].(string)
@@ -749,7 +750,7 @@ func TestHandler_DescribeServiceRevisions(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
revs, _ := out["serviceRevisions"].([]any)
assert.Len(t, revs, tt.wantRevCount)
@@ -798,7 +799,7 @@ func TestHandler_DescribeServiceRevisions_CreateAndUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
revs := out2["serviceRevisions"].([]any)
require.Len(t, revs, 1, "expected one revision after CreateService")
rev := revs[0].(map[string]any)
@@ -828,7 +829,7 @@ func TestHandler_DescribeServiceRevisions_CreateAndUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var out3 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &out3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &out3, jsonx.V1()))
revs3 := out3["serviceRevisions"].([]any)
assert.Len(t, revs3, 1, "expected one revision after UpdateService")
}
diff --git a/services/ecs/handler_attributes_test.go b/services/ecs/handler_attributes_test.go
index a54f867e9d..34e47b1c60 100644
--- a/services/ecs/handler_attributes_test.go
+++ b/services/ecs/handler_attributes_test.go
@@ -1,7 +1,7 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -72,7 +73,7 @@ func TestAttributes_PutListDelete_Roundtrip(t *testing.T) {
})
require.Equal(t, http.StatusOK, ciResp.Code)
var ciOut map[string]any
- require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut))
+ require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut, jsonx.V1()))
ciArn := ciOut["containerInstance"].(map[string]any)["containerInstanceArn"].(string)
// Put attributes
@@ -102,7 +103,7 @@ func TestAttributes_PutListDelete_Roundtrip(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
attrs := listOut["attributes"].([]any)
assert.Len(t, attrs, 2)
@@ -126,7 +127,7 @@ func TestAttributes_PutListDelete_Roundtrip(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp2.Code)
var listOut2 map[string]any
- require.NoError(t, json.Unmarshal(listResp2.Body.Bytes(), &listOut2))
+ require.NoError(t, json.Unmarshal(listResp2.Body.Bytes(), &listOut2, jsonx.V1()))
attrs2 := listOut2["attributes"].([]any)
assert.Len(t, attrs2, 1)
assert.Equal(t, "com.example.zone", attrs2[0].(map[string]any)["name"])
@@ -144,7 +145,7 @@ func TestAttributes_FilterByName(t *testing.T) {
})
require.Equal(t, http.StatusOK, ciResp.Code)
var ciOut map[string]any
- require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut))
+ require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut, jsonx.V1()))
ciArn := ciOut["containerInstance"].(map[string]any)["containerInstanceArn"].(string)
doECSRequest(t, h, "PutAttributes", map[string]any{
@@ -173,7 +174,7 @@ func TestAttributes_FilterByName(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
attrs := listOut["attributes"].([]any)
assert.Len(t, attrs, 1)
assert.Equal(t, "ecs.gpu", attrs[0].(map[string]any)["name"])
@@ -192,7 +193,7 @@ func TestListAttributes_TargetID_Filter(t *testing.T) {
})
require.Equal(t, http.StatusOK, ci1Resp.Code)
var ci1Out map[string]any
- require.NoError(t, json.Unmarshal(ci1Resp.Body.Bytes(), &ci1Out))
+ require.NoError(t, json.Unmarshal(ci1Resp.Body.Bytes(), &ci1Out, jsonx.V1()))
ci1Arn := ci1Out["containerInstance"].(map[string]any)["containerInstanceArn"].(string)
ci2Resp := doECSRequest(t, h, "RegisterContainerInstance", map[string]any{
@@ -201,7 +202,7 @@ func TestListAttributes_TargetID_Filter(t *testing.T) {
})
require.Equal(t, http.StatusOK, ci2Resp.Code)
var ci2Out map[string]any
- require.NoError(t, json.Unmarshal(ci2Resp.Body.Bytes(), &ci2Out))
+ require.NoError(t, json.Unmarshal(ci2Resp.Body.Bytes(), &ci2Out, jsonx.V1()))
ci2Arn := ci2Out["containerInstance"].(map[string]any)["containerInstanceArn"].(string)
doECSRequest(t, h, "PutAttributes", map[string]any{
@@ -231,7 +232,7 @@ func TestListAttributes_TargetID_Filter(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
attrs := listOut["attributes"].([]any)
require.Len(t, attrs, 1)
assert.Equal(t, "us-east-1a", attrs[0].(map[string]any)["value"])
@@ -262,7 +263,7 @@ func TestListAttributes_TargetID_Filter_NotFound(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
attrs := listOut["attributes"].([]any)
assert.Empty(t, attrs)
}
@@ -304,7 +305,7 @@ func TestListAttributes_TargetID_NoFilter_ReturnsAll(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
attrs := listOut["attributes"].([]any)
assert.Len(t, attrs, 3)
}
@@ -372,7 +373,7 @@ func TestECS_DeleteAttributes(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
attrs, ok := resp["attributes"].([]any)
require.True(t, ok)
@@ -435,7 +436,7 @@ func TestECS_PutAndListAttributes(t *testing.T) {
}
var putResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
attrs, ok := putResp["attributes"].([]any)
require.True(t, ok)
@@ -446,7 +447,7 @@ func TestECS_PutAndListAttributes(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
listedAttrs, ok := listResp["attributes"].([]any)
require.True(t, ok)
diff --git a/services/ecs/handler_capacity_providers_test.go b/services/ecs/handler_capacity_providers_test.go
index 3603289464..3466750940 100644
--- a/services/ecs/handler_capacity_providers_test.go
+++ b/services/ecs/handler_capacity_providers_test.go
@@ -1,13 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -34,7 +35,7 @@ func TestCapacityProvider_ASGBacked_Roundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
cp := out["capacityProvider"].(map[string]any)
require.Equal(t, "asg-cp", cp["name"])
@@ -69,7 +70,7 @@ func TestCapacityProvider_ASGBacked_NoManagedScaling(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
cp := out["capacityProvider"].(map[string]any)
asg := cp["autoScalingGroupProvider"].(map[string]any)
assert.Nil(t, asg["managedScaling"])
@@ -93,7 +94,7 @@ func TestCapacityProvider_FARGATE_Types(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
providers := out["capacityProviders"].([]any)
require.Len(t, providers, 2)
@@ -141,7 +142,7 @@ func TestCapacityProvider_Update_ManagedScaling(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
cp := out["capacityProvider"].(map[string]any)
asg := cp["autoScalingGroupProvider"].(map[string]any)
ms := asg["managedScaling"].(map[string]any)
@@ -160,7 +161,7 @@ func TestCapacityProvider_DeleteByARN(t *testing.T) {
})
require.Equal(t, http.StatusOK, createResp.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut, jsonx.V1()))
cpArn := createOut["capacityProvider"].(map[string]any)["capacityProviderArn"].(string)
require.NotEmpty(t, cpArn)
@@ -192,7 +193,7 @@ func TestCapacityProvider_FARGATE_BuiltIn_DescribeWithoutCreate(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
providers := out["capacityProviders"].([]any)
require.Len(t, providers, 1)
cp := providers[0].(map[string]any)
@@ -211,7 +212,7 @@ func TestCapacityProvider_FARGATE_SPOT_BuiltIn(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
providers := out["capacityProviders"].([]any)
require.Len(t, providers, 2)
@@ -242,7 +243,7 @@ func TestCapacityProvider_FARGATE_BuiltIn_WithCreated(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
providers := out["capacityProviders"].([]any)
require.Len(t, providers, 2)
@@ -267,7 +268,7 @@ func TestCapacityProvider_Unknown_ReturnsFailure(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out["capacityProviders"])
failures := out["failures"].([]any)
@@ -322,7 +323,7 @@ func TestECS_CreateCapacityProvider(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cp, ok := resp["capacityProvider"].(map[string]any)
require.True(t, ok)
@@ -395,7 +396,7 @@ func TestECS_DeleteCapacityProvider(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cp, ok := resp["capacityProvider"].(map[string]any)
require.True(t, ok)
@@ -514,7 +515,7 @@ func TestECS_DescribeCapacityProviders(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
providers, ok := resp["capacityProviders"].([]any)
require.True(t, ok)
@@ -540,7 +541,7 @@ func TestECS_DescribeCapacityProviders_SortedByName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
providers, ok := resp["capacityProviders"].([]any)
require.True(t, ok)
@@ -559,7 +560,7 @@ func TestECS_DescribeCapacityProviders_ByARN(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
arn := createResp["capacityProvider"].(map[string]any)["capacityProviderArn"].(string)
@@ -572,7 +573,7 @@ func TestECS_DescribeCapacityProviders_ByARN(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
providers, ok := resp["capacityProviders"].([]any)
require.True(t, ok)
@@ -713,7 +714,7 @@ func TestDescribeCapacityProviders_Sorted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cps, ok := resp["capacityProviders"].([]any)
require.True(t, ok)
@@ -750,7 +751,7 @@ func TestDescribeCapacityProviders_TagsRequireInclude(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cp := resp["capacityProviders"].([]any)[0].(map[string]any)
assert.Nil(t, cp["tags"])
})
@@ -765,7 +766,7 @@ func TestDescribeCapacityProviders_TagsRequireInclude(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cp := resp["capacityProviders"].([]any)[0].(map[string]any)
tags := cp["tags"].([]any)
require.Len(t, tags, 1)
@@ -797,7 +798,7 @@ func TestDescribeCapacityProviders_ClusterFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cps := resp["capacityProviders"].([]any)
require.Len(t, cps, 2)
@@ -825,7 +826,7 @@ func TestDescribeCapacityProviders_ClusterFilter_UnknownCluster(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cps := resp["capacityProviders"].([]any)
assert.Empty(t, cps)
}
diff --git a/services/ecs/handler_cluster_tags_test.go b/services/ecs/handler_cluster_tags_test.go
index 5198069136..b058592a4c 100644
--- a/services/ecs/handler_cluster_tags_test.go
+++ b/services/ecs/handler_cluster_tags_test.go
@@ -1,12 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestECS_CreateCluster_TagsAndCapacityProviders proves that CreateCluster
@@ -49,7 +51,7 @@ func TestECS_CreateCluster_TagsAndCapacityProviders(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
cluster := createResp["cluster"].(map[string]any)
@@ -77,7 +79,7 @@ func TestECS_CreateCluster_TagsAndCapacityProviders(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var describeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &describeResp, jsonx.V1()))
described := describeResp["clusters"].([]any)[0].(map[string]any)
assert.Nil(t, described["tags"], "tags must be omitted when include=TAGS is not requested")
@@ -90,7 +92,7 @@ func TestECS_CreateCluster_TagsAndCapacityProviders(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var describeTaggedResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &describeTaggedResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &describeTaggedResp, jsonx.V1()))
describedTagged := describeTaggedResp["clusters"].([]any)[0].(map[string]any)
taggedTags, ok := describedTagged["tags"].([]any)
diff --git a/services/ecs/handler_clusters_test.go b/services/ecs/handler_clusters_test.go
index 1eede85592..c47979e86e 100644
--- a/services/ecs/handler_clusters_test.go
+++ b/services/ecs/handler_clusters_test.go
@@ -1,13 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -44,7 +45,7 @@ func TestECS_CreateCluster(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cluster, ok := resp["cluster"].(map[string]any)
require.True(t, ok)
@@ -133,7 +134,7 @@ func TestECS_DescribeClusters(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tt.wantCount > 0 {
clusters, ok := resp["clusters"].([]any)
@@ -402,7 +403,7 @@ func TestCluster_PutCapacityProviders_WithStrategy(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
cluster := out["cluster"].(map[string]any)
providers := cluster["capacityProviders"].([]any)
assert.Len(t, providers, 2)
@@ -427,7 +428,7 @@ func TestClusterSetting_ContainerInsights_Enable(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
cluster := out["cluster"].(map[string]any)
settings := cluster["settings"].([]any)
require.Len(t, settings, 1)
@@ -456,7 +457,7 @@ func TestClusterSetting_ContainerInsights_Disable(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
cluster := out["cluster"].(map[string]any)
settings := cluster["settings"].([]any)
assert.Equal(t, "disabled", settings[0].(map[string]any)["value"])
@@ -476,7 +477,7 @@ func TestCreateCluster_WithSettings(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
cluster := out["cluster"].(map[string]any)
rawSettings := cluster["settings"]
require.NotNil(t, rawSettings, "settings should be present in response")
@@ -508,7 +509,7 @@ func TestUpdateCluster_DoesNotAcceptCapacityProviders(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
cluster := out["cluster"].(map[string]any)
providers, ok := cluster["capacityProviders"].([]any)
require.True(t, ok)
@@ -533,7 +534,7 @@ func TestDescribeClusters_MultipleClusters(t *testing.T) {
})
require.Equal(t, http.StatusOK, descResp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &out, jsonx.V1()))
clusters := out["clusters"].([]any)
assert.Len(t, clusters, 3)
}
@@ -554,7 +555,7 @@ func TestListClusters_Pagination(t *testing.T) {
})
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
arns := out["clusterArns"].([]any)
assert.Len(t, arns, 3)
assert.NotEmpty(t, out["nextToken"])
@@ -654,7 +655,7 @@ func TestECS_PutClusterCapacityProviders(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cluster, ok := resp["cluster"].(map[string]any)
require.True(t, ok)
@@ -707,7 +708,7 @@ func TestECS_UpdateClusterSettings(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
c, ok := resp["cluster"].(map[string]any)
require.True(t, ok)
@@ -729,7 +730,7 @@ func TestDescribeClusters_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusters, _ := resp["clusters"].([]any)
assert.Empty(t, clusters)
@@ -754,7 +755,7 @@ func TestDescribeClusters_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusters, _ := resp["clusters"].([]any)
assert.Len(t, clusters, 1)
@@ -776,7 +777,7 @@ func TestDescribeClusters_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusters, _ := resp["clusters"].([]any)
assert.Empty(t, clusters)
@@ -796,7 +797,7 @@ func TestDescribeClusters_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusters, _ := resp["clusters"].([]any)
require.Len(t, clusters, 1)
@@ -821,7 +822,7 @@ func TestCluster_EmptyArrays_NotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cluster, ok := resp["cluster"].(map[string]any)
require.True(t, ok, "cluster key must be present")
@@ -846,7 +847,7 @@ func TestCluster_EmptyArrays_NotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cluster := resp["cluster"].(map[string]any)
@@ -872,7 +873,7 @@ func TestCluster_EmptyArrays_NotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusters, _ := resp["clusters"].([]any)
require.Len(t, clusters, 1)
@@ -935,7 +936,7 @@ func TestECS_UpdateCluster(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cluster, ok := resp["cluster"].(map[string]any)
require.True(t, ok)
@@ -989,7 +990,7 @@ func TestBackend_EnrichCluster_CachedCounters(t *testing.T) {
require.Equal(t, http.StatusOK, runRec.Code)
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut, jsonx.V1()))
for _, task := range runOut["tasks"].([]any) {
taskArns = append(taskArns, task.(map[string]any)["taskArn"].(string))
}
@@ -1009,7 +1010,7 @@ func TestBackend_EnrichCluster_CachedCounters(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
clusters := descOut["clusters"].([]any)
require.Len(t, clusters, 1)
c := clusters[0].(map[string]any)
@@ -1035,7 +1036,7 @@ func TestBackend_EnrichCluster_RunAndStopUpdatesCounters(t *testing.T) {
)
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
c := out["clusters"].([]any)[0].(map[string]any)
return c["runningTasksCount"].(float64), c["pendingTasksCount"].(float64)
@@ -1054,7 +1055,7 @@ func TestBackend_EnrichCluster_RunAndStopUpdatesCounters(t *testing.T) {
require.Equal(t, http.StatusOK, runRec.Code)
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut, jsonx.V1()))
tasks := runOut["tasks"].([]any)
require.Len(t, tasks, 2)
diff --git a/services/ecs/handler_container_instances.go b/services/ecs/handler_container_instances.go
index d3ae7babe0..efa1bc1ac4 100644
--- a/services/ecs/handler_container_instances.go
+++ b/services/ecs/handler_container_instances.go
@@ -2,9 +2,10 @@ package ecs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -54,7 +55,7 @@ func ec2InstanceIDFromIdentityDocument(doc string) string {
InstanceID string `json:"instanceId"`
}
- if err := json.Unmarshal([]byte(doc), &parsed); err != nil {
+ if err := json.Unmarshal([]byte(doc), &parsed, jsonx.V1()); err != nil {
return ""
}
diff --git a/services/ecs/handler_container_instances_test.go b/services/ecs/handler_container_instances_test.go
index 4715230853..ce1d34aa4e 100644
--- a/services/ecs/handler_container_instances_test.go
+++ b/services/ecs/handler_container_instances_test.go
@@ -1,13 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -23,7 +24,7 @@ func TestContainerInstance_DRAINING_State(t *testing.T) {
})
require.Equal(t, http.StatusOK, ciResp.Code)
var ciOut map[string]any
- require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut))
+ require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut, jsonx.V1()))
ciArn := ciOut["containerInstance"].(map[string]any)["containerInstanceArn"].(string)
assert.Equal(t, "ACTIVE", ciOut["containerInstance"].(map[string]any)["status"])
@@ -36,7 +37,7 @@ func TestContainerInstance_DRAINING_State(t *testing.T) {
require.Equal(t, http.StatusOK, drainResp.Code)
var drainOut map[string]any
- require.NoError(t, json.Unmarshal(drainResp.Body.Bytes(), &drainOut))
+ require.NoError(t, json.Unmarshal(drainResp.Body.Bytes(), &drainOut, jsonx.V1()))
instances := drainOut["containerInstances"].([]any)
require.Len(t, instances, 1)
assert.Equal(t, "DRAINING", instances[0].(map[string]any)["status"])
@@ -48,7 +49,7 @@ func TestContainerInstance_DRAINING_State(t *testing.T) {
})
require.Equal(t, http.StatusOK, descResp.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut, jsonx.V1()))
described := descOut["containerInstances"].([]any)
require.Len(t, described, 1)
assert.Equal(t, "DRAINING", described[0].(map[string]any)["status"])
@@ -65,7 +66,7 @@ func TestContainerInstance_DRAINING_to_ACTIVE(t *testing.T) {
"instanceIdentityDocument": fakeInstanceIdentityDocument("i-react-5678"),
})
var ciOut map[string]any
- require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut))
+ require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut, jsonx.V1()))
ciArn := ciOut["containerInstance"].(map[string]any)["containerInstanceArn"].(string)
// Drain then re-activate
@@ -82,7 +83,7 @@ func TestContainerInstance_DRAINING_to_ACTIVE(t *testing.T) {
require.Equal(t, http.StatusOK, activeResp.Code)
var activeOut map[string]any
- require.NoError(t, json.Unmarshal(activeResp.Body.Bytes(), &activeOut))
+ require.NoError(t, json.Unmarshal(activeResp.Body.Bytes(), &activeOut, jsonx.V1()))
instances := activeOut["containerInstances"].([]any)
assert.Equal(t, "ACTIVE", instances[0].(map[string]any)["status"])
}
@@ -99,7 +100,7 @@ func TestContainerInstance_UpdateAgent(t *testing.T) {
})
require.Equal(t, http.StatusOK, ciResp.Code)
var ciOut map[string]any
- require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut))
+ require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut, jsonx.V1()))
ciArn := ciOut["containerInstance"].(map[string]any)["containerInstanceArn"].(string)
resp := doECSRequest(t, h, "UpdateContainerAgent", map[string]any{
@@ -109,7 +110,7 @@ func TestContainerInstance_UpdateAgent(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
ci := out["containerInstance"].(map[string]any)
assert.NotEmpty(t, ci["containerInstanceArn"])
}
@@ -129,7 +130,7 @@ func TestContainerInstance_Deregister_Force(t *testing.T) {
"instanceIdentityDocument": fakeInstanceIdentityDocument("i-force-1234"),
})
var ciOut map[string]any
- require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut))
+ require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut, jsonx.V1()))
ciArn := ciOut["containerInstance"].(map[string]any)["containerInstanceArn"].(string)
// Start a task on this container instance
@@ -152,7 +153,7 @@ func TestContainerInstance_Deregister_Force(t *testing.T) {
"cluster": "deregforce-cluster",
})
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
arns := listOut["containerInstanceArns"].([]any)
assert.Empty(t, arns)
}
@@ -219,7 +220,7 @@ func TestECS_RegisterContainerInstance(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ci := resp["containerInstance"].(map[string]any)
assert.Equal(t, tt.wantID, ci["ec2InstanceId"])
@@ -302,7 +303,7 @@ func TestECS_ListContainerInstances(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arns := resp["containerInstanceArns"].([]any)
assert.Len(t, arns, tt.wantLen)
@@ -348,7 +349,7 @@ func TestECS_DescribeContainerInstances(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var regResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp, jsonx.V1()))
ciArn := regResp["containerInstance"].(map[string]any)["containerInstanceArn"].(string)
@@ -364,7 +365,7 @@ func TestECS_DescribeContainerInstances(t *testing.T) {
require.Equal(t, tt.wantCode, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
cis := resp["containerInstances"].([]any)
assert.Len(t, cis, tt.wantLen)
@@ -391,7 +392,7 @@ func TestECS_DescribeContainerInstances_NotFound(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
instances, _ := resp["containerInstances"].([]any)
assert.Empty(t, instances)
@@ -428,7 +429,7 @@ func TestECS_DeregisterContainerInstance(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var regResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp, jsonx.V1()))
ciArn := regResp["containerInstance"].(map[string]any)["containerInstanceArn"].(string)
@@ -442,7 +443,7 @@ func TestECS_DeregisterContainerInstance(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
ci := resp["containerInstance"].(map[string]any)
assert.Equal(t, "INACTIVE", ci["status"])
@@ -545,7 +546,7 @@ func TestECS_UpdateContainerInstancesState(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var regResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp, jsonx.V1()))
ciArn := regResp["containerInstance"].(map[string]any)["containerInstanceArn"].(string)
@@ -559,7 +560,7 @@ func TestECS_UpdateContainerInstancesState(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
cis := resp["containerInstances"].([]any)
require.Len(t, cis, 1)
@@ -585,7 +586,7 @@ func TestECS_UpdateContainerInstancesState_NotFound(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["containerInstances"])
@@ -633,7 +634,7 @@ func TestECS_UpdateContainerAgent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var regResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp, jsonx.V1()))
ci := regResp["containerInstance"].(map[string]any)
containerInstanceArn = ci["containerInstanceArn"].(string)
} else {
@@ -651,7 +652,7 @@ func TestECS_UpdateContainerAgent(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ci, ok := resp["containerInstance"].(map[string]any)
require.True(t, ok)
@@ -678,7 +679,7 @@ func TestDescribeContainerInstances_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
instances, _ := resp["containerInstances"].([]any)
assert.Empty(t, instances)
@@ -702,7 +703,7 @@ func TestDescribeContainerInstances_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, regResp.Code)
var regOut map[string]any
- require.NoError(t, json.Unmarshal(regResp.Body.Bytes(), ®Out))
+ require.NoError(t, json.Unmarshal(regResp.Body.Bytes(), ®Out, jsonx.V1()))
ciArn := regOut["containerInstance"].(map[string]any)["containerInstanceArn"].(string)
rec := doECSRequest(t, h, "DescribeContainerInstances", map[string]any{
@@ -715,7 +716,7 @@ func TestDescribeContainerInstances_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
instances, _ := resp["containerInstances"].([]any)
assert.Len(t, instances, 1)
@@ -750,7 +751,7 @@ func TestDescribeContainerInstances_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
instances, _ := resp["containerInstances"].([]any)
assert.Len(t, instances, 2)
@@ -778,7 +779,7 @@ func TestDescribeContainerInstances_TagsRequireInclude(t *testing.T) {
require.Equal(t, http.StatusOK, ciResp.Code)
var ciOut map[string]any
- require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut))
+ require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut, jsonx.V1()))
ciArn := ciOut["containerInstance"].(map[string]any)["containerInstanceArn"].(string)
tagResp := doECSRequest(t, h, "TagResource", map[string]any{
@@ -797,7 +798,7 @@ func TestDescribeContainerInstances_TagsRequireInclude(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ci := resp["containerInstances"].([]any)[0].(map[string]any)
assert.Nil(t, ci["tags"])
})
@@ -813,7 +814,7 @@ func TestDescribeContainerInstances_TagsRequireInclude(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ci := resp["containerInstances"].([]any)[0].(map[string]any)
tags := ci["tags"].([]any)
require.Len(t, tags, 1)
diff --git a/services/ecs/handler_daemon_test.go b/services/ecs/handler_daemon_test.go
index bba2c08db8..7e023e3aee 100644
--- a/services/ecs/handler_daemon_test.go
+++ b/services/ecs/handler_daemon_test.go
@@ -1,7 +1,7 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -29,7 +30,7 @@ func registerDaemonTaskDef(t *testing.T, h *ecs.Handler, family string) string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ := resp["daemonTaskDefinitionArn"].(string)
require.NotEmpty(t, arn)
@@ -45,7 +46,7 @@ func createCapacityProviderForDaemon(t *testing.T, h *ecs.Handler, name string)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cp, ok := resp["capacityProvider"].(map[string]any)
require.True(t, ok)
@@ -103,7 +104,7 @@ func TestECS_RegisterDaemonTaskDefinition(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ := resp["daemonTaskDefinitionArn"].(string)
assert.Contains(t, arn, "daemon-task-definition/agent-family:1")
@@ -155,7 +156,7 @@ func TestECS_DescribeDaemonTaskDefinition(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
td, ok := resp["daemonTaskDefinition"].(map[string]any)
require.True(t, ok)
@@ -176,7 +177,7 @@ func TestECS_DeleteDaemonTaskDefinition(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, arn, resp["daemonTaskDefinitionArn"])
// Deleted revisions are excluded from the default ACTIVE-only describe lookup by family.
@@ -188,7 +189,7 @@ func TestECS_DeleteDaemonTaskDefinition(t *testing.T) {
rec = doECSRequest(t, h, "DescribeDaemonTaskDefinition", map[string]any{"daemonTaskDefinition": arn})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
td, ok := resp["daemonTaskDefinition"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "DELETED", td["status"])
@@ -208,7 +209,7 @@ func TestECS_ListDaemonTaskDefinitions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tds, ok := resp["daemonTaskDefinitions"].([]any)
require.True(t, ok)
@@ -216,7 +217,7 @@ func TestECS_ListDaemonTaskDefinitions(t *testing.T) {
rec = doECSRequest(t, h, "ListDaemonTaskDefinitions", map[string]any{"familyPrefix": "list-family-a"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tds, ok = resp["daemonTaskDefinitions"].([]any)
require.True(t, ok)
assert.Len(t, tds, 1)
@@ -372,7 +373,7 @@ func TestECS_CreateDaemon(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ACTIVE", resp["status"])
assert.NotEmpty(t, resp["daemonArn"])
assert.NotEmpty(t, resp["deploymentArn"])
@@ -415,7 +416,7 @@ func TestECS_DescribeDaemon(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
daemonArn, _ := created["daemonArn"].(string)
require.NotEmpty(t, daemonArn)
@@ -423,7 +424,7 @@ func TestECS_DescribeDaemon(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
d, ok := resp["daemon"].(map[string]any)
require.True(t, ok)
@@ -616,7 +617,7 @@ func TestECS_UpdateDaemon(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
daemonArn, _ := created["daemonArn"].(string)
firstDeploymentArn, _ := created["deploymentArn"].(string)
@@ -628,7 +629,7 @@ func TestECS_UpdateDaemon(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ACTIVE", resp["status"])
assert.NotEqual(t, firstDeploymentArn, resp["deploymentArn"])
@@ -655,14 +656,14 @@ func TestECS_DeleteDaemon(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
daemonArn, _ := created["daemonArn"].(string)
rec = doECSRequest(t, h, "DeleteDaemon", map[string]any{"daemonArn": daemonArn})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "DELETE_IN_PROGRESS", resp["status"])
// Deleted daemons are gone from state; a subsequent Describe 404s.
@@ -693,7 +694,7 @@ func TestECS_ListDaemons(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
daemons, ok := resp["daemonSummariesList"].([]any)
require.True(t, ok)
@@ -701,7 +702,7 @@ func TestECS_ListDaemons(t *testing.T) {
rec = doECSRequest(t, h, "ListDaemons", map[string]any{"capacityProviderArns": []string{"arn:none"}})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
daemons, ok = resp["daemonSummariesList"].([]any)
require.True(t, ok)
assert.Empty(t, daemons)
@@ -730,14 +731,14 @@ func TestECS_ListDaemons_OmittedClusterScopesToDefault(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
daemons, ok := resp["daemonSummariesList"].([]any)
require.True(t, ok)
assert.Empty(t, daemons, "omitted clusterArn must scope to default cluster, not every cluster")
rec = doECSRequest(t, h, "ListDaemons", map[string]any{"clusterArn": "other-cluster"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
daemons, ok = resp["daemonSummariesList"].([]any)
require.True(t, ok)
assert.Len(t, daemons, 1)
@@ -760,7 +761,7 @@ func TestECS_DescribeDaemonDeployments(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
deploymentArn, _ := created["deploymentArn"].(string)
require.NotEmpty(t, deploymentArn)
@@ -770,7 +771,7 @@ func TestECS_DescribeDaemonDeployments(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
deps, ok := resp["daemonDeployments"].([]any)
require.True(t, ok)
@@ -804,14 +805,14 @@ func TestECS_ListDaemonDeployments(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
daemonArn, _ := created["daemonArn"].(string)
rec = doECSRequest(t, h, "ListDaemonDeployments", map[string]any{"daemonArn": daemonArn})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
deps, ok := resp["daemonDeployments"].([]any)
require.True(t, ok)
@@ -833,14 +834,14 @@ func TestECS_DescribeDaemonRevisions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
daemonArn, _ := created["daemonArn"].(string)
rec = doECSRequest(t, h, "DescribeDaemon", map[string]any{"daemonArn": daemonArn})
require.Equal(t, http.StatusOK, rec.Code)
var described map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &described))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &described, jsonx.V1()))
d, ok := described["daemon"].(map[string]any)
require.True(t, ok)
revs, ok := d["currentRevisions"].([]any)
@@ -857,7 +858,7 @@ func TestECS_DescribeDaemonRevisions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
revisions, ok := resp["daemonRevisions"].([]any)
require.True(t, ok)
diff --git a/services/ecs/handler_describe_taskdef_tags_test.go b/services/ecs/handler_describe_taskdef_tags_test.go
index 8b8c4b1cf0..f0f551d3f4 100644
--- a/services/ecs/handler_describe_taskdef_tags_test.go
+++ b/services/ecs/handler_describe_taskdef_tags_test.go
@@ -1,13 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -72,7 +73,7 @@ func TestECS_DescribeTaskDefinitionInclude(t *testing.T) {
Tags []ecs.Tag `json:"tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
gotKeys := make([]string, 0, len(resp.Tags))
for _, tag := range resp.Tags {
diff --git a/services/ecs/handler_express_gateway_test.go b/services/ecs/handler_express_gateway_test.go
index 264e8c2da8..b2c5fcf147 100644
--- a/services/ecs/handler_express_gateway_test.go
+++ b/services/ecs/handler_express_gateway_test.go
@@ -1,7 +1,7 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -74,7 +75,7 @@ func TestECS_CreateExpressGatewayService(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
svc, ok := resp["service"].(map[string]any)
require.True(t, ok)
@@ -127,7 +128,7 @@ func TestECS_DeleteExpressGatewayService(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var r map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r, jsonx.V1()))
return r["service"].(map[string]any)["serviceArn"].(string)
},
@@ -170,7 +171,7 @@ func TestECS_DeleteExpressGatewayService(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
svc, ok := resp["service"].(map[string]any)
require.True(t, ok)
@@ -198,7 +199,7 @@ func TestECS_DescribeExpressGatewayService(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var r map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r, jsonx.V1()))
return r["service"].(map[string]any)["serviceArn"].(string)
},
@@ -234,7 +235,7 @@ func TestECS_DescribeExpressGatewayService(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
svc, ok := resp["service"].(map[string]any)
require.True(t, ok)
@@ -258,7 +259,7 @@ func TestECS_ExpressGatewayService_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
arn := createResp["service"].(map[string]any)["serviceArn"].(string)
@@ -309,7 +310,7 @@ func TestECS_UpdateExpressGatewayService(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
svc := createResp["service"].(map[string]any)
serviceArn = svc["serviceArn"].(string)
} else {
@@ -328,7 +329,7 @@ func TestECS_UpdateExpressGatewayService(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// UpdateExpressGatewayServiceOutput.Service is the narrower
// UpdatedExpressGatewayService shape (types.UpdatedExpressGatewayService):
@@ -349,7 +350,7 @@ func TestECS_UpdateExpressGatewayService(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
descSvc := descResp["service"].(map[string]any)
assert.Equal(t, "arn:aws:iam::000000000000:role/new-infra", descSvc["infrastructureRoleArn"])
})
@@ -419,7 +420,7 @@ func TestExpressGatewayService_TagResource_VisibleOnDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut, jsonx.V1()))
serviceArn := createOut["service"].(map[string]any)["serviceArn"].(string)
tagResp := doECSRequest(t, h, "TagResource", map[string]any{
@@ -435,7 +436,7 @@ func TestExpressGatewayService_TagResource_VisibleOnDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
listTags := listOut["tags"].([]any)
require.Len(t, listTags, 1)
assert.Equal(t, "team", listTags[0].(map[string]any)["key"])
@@ -448,7 +449,7 @@ func TestExpressGatewayService_TagResource_VisibleOnDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, descResp.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut, jsonx.V1()))
descTags := descOut["service"].(map[string]any)["tags"].([]any)
require.Len(t, descTags, 1)
assert.Equal(t, "team", descTags[0].(map[string]any)["key"])
@@ -460,7 +461,7 @@ func TestExpressGatewayService_TagResource_VisibleOnDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, noTagsResp.Code)
var noTagsOut map[string]any
- require.NoError(t, json.Unmarshal(noTagsResp.Body.Bytes(), &noTagsOut))
+ require.NoError(t, json.Unmarshal(noTagsResp.Body.Bytes(), &noTagsOut, jsonx.V1()))
assert.Nil(t, noTagsOut["service"].(map[string]any)["tags"])
}
diff --git a/services/ecs/handler_service_config_test.go b/services/ecs/handler_service_config_test.go
index 10f9b74814..d09dff96b3 100644
--- a/services/ecs/handler_service_config_test.go
+++ b/services/ecs/handler_service_config_test.go
@@ -1,12 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestService_CapacityProviderStrategy_Roundtrip(t *testing.T) {
@@ -34,7 +36,7 @@ func TestService_CapacityProviderStrategy_Roundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
strategy := svc["capacityProviderStrategy"].([]any)
require.Len(t, strategy, 2)
@@ -85,7 +87,7 @@ func TestService_CapacityProviderStrategy_UpdateService(t *testing.T) {
require.Equal(t, http.StatusOK, updateResp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(updateResp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(updateResp.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
strategy := svc["capacityProviderStrategy"].([]any)
assert.Len(t, strategy, 2)
@@ -119,7 +121,7 @@ func TestService_CircuitBreaker_Roundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
dc := svc["deploymentConfiguration"].(map[string]any)
@@ -156,7 +158,7 @@ func TestService_CircuitBreaker_EnabledNoRollback(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
dc := svc["deploymentConfiguration"].(map[string]any)
cb := dc["deploymentCircuitBreaker"].(map[string]any)
@@ -196,7 +198,7 @@ func TestService_CircuitBreaker_UpdatePreserved(t *testing.T) {
require.Equal(t, http.StatusOK, updateResp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(updateResp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(updateResp.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
dc := svc["deploymentConfiguration"].(map[string]any)
cb := dc["deploymentCircuitBreaker"].(map[string]any)
@@ -224,7 +226,7 @@ func TestService_DeploymentConfig_Defaults(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
dc := svc["deploymentConfiguration"].(map[string]any)
// AWS defaults: minimumHealthyPercent=100, maximumPercent=200
@@ -265,7 +267,7 @@ func TestServiceConnect_Roundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
sc := primaryServiceConnect(t, svc)
assert.Equal(t, true, sc["enabled"])
@@ -307,7 +309,7 @@ func TestServiceConnect_Disabled(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
sc := primaryServiceConnect(t, svc)
assert.Equal(t, false, sc["enabled"])
@@ -341,7 +343,7 @@ func TestServiceConnect_UpdateService(t *testing.T) {
require.Equal(t, http.StatusOK, updateResp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(updateResp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(updateResp.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
sc := primaryServiceConnect(t, svc)
assert.Equal(t, true, sc["enabled"])
@@ -375,7 +377,7 @@ func TestServiceDiscovery_CloudMap_Roundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
registries := svc["serviceRegistries"].([]any)
require.Len(t, registries, 1)
@@ -420,7 +422,7 @@ func TestServiceDiscovery_MultipleRegistries(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
registries := svc["serviceRegistries"].([]any)
assert.Len(t, registries, 2)
@@ -454,7 +456,7 @@ func TestServiceDiscovery_DescribeServices_Preserved(t *testing.T) {
})
require.Equal(t, http.StatusOK, descResp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &out, jsonx.V1()))
services := out["services"].([]any)
require.Len(t, services, 1)
svc := services[0].(map[string]any)
diff --git a/services/ecs/handler_service_deployments_test.go b/services/ecs/handler_service_deployments_test.go
index 46a2e96464..af9b866a8d 100644
--- a/services/ecs/handler_service_deployments_test.go
+++ b/services/ecs/handler_service_deployments_test.go
@@ -1,7 +1,7 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -32,7 +33,7 @@ func TestServiceDeployment_DescribeList_Roundtrip(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
briefs := listOut["serviceDeployments"].([]any)
require.NotEmpty(t, briefs)
firstArn := briefs[0].(map[string]any)["serviceDeploymentArn"].(string)
@@ -42,7 +43,7 @@ func TestServiceDeployment_DescribeList_Roundtrip(t *testing.T) {
})
require.Equal(t, http.StatusOK, descResp.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut, jsonx.V1()))
deps := descOut["serviceDeployments"].([]any)
assert.Len(t, deps, 1)
dep := deps[0].(map[string]any)
@@ -85,7 +86,7 @@ func TestECS_ListServiceDeployments_StableOrder(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
briefs := resp["serviceDeployments"].([]any)
require.Len(t, briefs, 3)
@@ -118,7 +119,7 @@ func TestStopServiceDeployment(t *testing.T) {
})
require.Equal(t, http.StatusOK, stopResp.Code)
var stopOut map[string]any
- require.NoError(t, json.Unmarshal(stopResp.Body.Bytes(), &stopOut))
+ require.NoError(t, json.Unmarshal(stopResp.Body.Bytes(), &stopOut, jsonx.V1()))
assert.Equal(t, depArn, stopOut["serviceDeploymentArn"])
assert.NotContains(t, stopOut, "serviceDeployment")
@@ -204,7 +205,7 @@ func TestECS_DescribeServiceDeployments(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
deployments := resp["serviceDeployments"].([]any)
failures := resp["failures"].([]any)
@@ -240,7 +241,7 @@ func TestECS_ListServiceDeployments(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
briefs, ok := resp["serviceDeployments"].([]any)
require.True(t, ok)
@@ -276,7 +277,7 @@ func TestECS_ListServiceDeployments_Shape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasOldShape := resp["serviceDeploymentArns"]
assert.False(t, hasOldShape, "response must not carry the bare serviceDeploymentArns shape")
@@ -355,7 +356,7 @@ func TestECS_StopServiceDeployment(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["serviceDeploymentArn"])
assert.NotContains(t, resp, "serviceDeployment")
@@ -427,7 +428,7 @@ func TestECS_ContinueServiceDeployment(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, tt.wantTypeIsIn, resp["__type"])
})
}
diff --git a/services/ecs/handler_service_deployments_wiring_test.go b/services/ecs/handler_service_deployments_wiring_test.go
index a3ce2fa160..dbc8ebff5c 100644
--- a/services/ecs/handler_service_deployments_wiring_test.go
+++ b/services/ecs/handler_service_deployments_wiring_test.go
@@ -1,12 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestECS_ServiceDeployments_RealDeploymentsAreVisible proves
@@ -77,7 +79,7 @@ func TestECS_ServiceDeployments_RealDeploymentsAreVisible(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
briefs, ok := listResp["serviceDeployments"].([]any)
require.True(t, ok)
@@ -93,7 +95,7 @@ func TestECS_ServiceDeployments_RealDeploymentsAreVisible(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var describeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &describeResp, jsonx.V1()))
deployments, ok := describeResp["serviceDeployments"].([]any)
require.True(t, ok)
@@ -145,7 +147,7 @@ func TestECS_ServiceDeployments_DeletedOnServiceDelete(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
briefs, ok := listResp["serviceDeployments"].([]any)
require.True(t, ok)
diff --git a/services/ecs/handler_service_fields_test.go b/services/ecs/handler_service_fields_test.go
index ec1c1a9a61..8b5b995bba 100644
--- a/services/ecs/handler_service_fields_test.go
+++ b/services/ecs/handler_service_fields_test.go
@@ -1,12 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestService_EnableExecuteCommand_Create(t *testing.T) {
@@ -30,7 +32,7 @@ func TestService_EnableExecuteCommand_Create(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
assert.Equal(t, true, svc["enableExecuteCommand"])
}
@@ -55,7 +57,7 @@ func TestService_EnableExecuteCommand_Default_False(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
eec := svc["enableExecuteCommand"]
assert.NotEqual(t, true, eec)
@@ -87,7 +89,7 @@ func TestService_EnableExecuteCommand_UpdateService(t *testing.T) {
require.Equal(t, http.StatusOK, updResp.Code)
var updOut map[string]any
- require.NoError(t, json.Unmarshal(updResp.Body.Bytes(), &updOut))
+ require.NoError(t, json.Unmarshal(updResp.Body.Bytes(), &updOut, jsonx.V1()))
svc := updOut["service"].(map[string]any)
assert.Equal(t, true, svc["enableExecuteCommand"])
@@ -100,7 +102,7 @@ func TestService_EnableExecuteCommand_UpdateService(t *testing.T) {
require.Equal(t, http.StatusOK, disResp.Code)
var disOut map[string]any
- require.NoError(t, json.Unmarshal(disResp.Body.Bytes(), &disOut))
+ require.NoError(t, json.Unmarshal(disResp.Body.Bytes(), &disOut, jsonx.V1()))
disSvc := disOut["service"].(map[string]any)
eec := disSvc["enableExecuteCommand"]
assert.NotEqual(t, true, eec)
@@ -131,7 +133,7 @@ func TestService_EnableExecuteCommand_DescribeServices(t *testing.T) {
require.Equal(t, http.StatusOK, descResp.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut, jsonx.V1()))
svcs := descOut["services"].([]any)
require.Len(t, svcs, 1)
assert.Equal(t, true, svcs[0].(map[string]any)["enableExecuteCommand"])
@@ -159,7 +161,7 @@ func TestService_EmptyArrays_NotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
svc := resp["service"].(map[string]any)
@@ -192,7 +194,7 @@ func TestService_EmptyArrays_NotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
svc := resp["service"].(map[string]any)
@@ -229,7 +231,7 @@ func TestService_EmptyArrays_NotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
services, _ := resp["services"].([]any)
require.Len(t, services, 1)
@@ -268,7 +270,7 @@ func TestHandler_CreateService_DeploymentConfigDefaults(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
dc := svc["deploymentConfiguration"].(map[string]any)
assert.InDelta(t, float64(100), dc["minimumHealthyPercent"], 0.001)
@@ -297,7 +299,7 @@ func TestHandler_CreateService_DeploymentConfigCustom(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
dc := svc["deploymentConfiguration"].(map[string]any)
assert.InDelta(t, float64(50), dc["minimumHealthyPercent"], 0.001)
@@ -343,7 +345,7 @@ func TestHandler_CreateService_DeploymentController_Rolling_Accepted(t *testing.
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
dc := svc["deploymentController"].(map[string]any)
assert.Equal(t, "ROLLING", dc["type"])
@@ -374,7 +376,7 @@ func TestHandler_CreateService_LoadBalancers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
lbs := svc["loadBalancers"].([]any)
require.Len(t, lbs, 1)
@@ -408,7 +410,7 @@ func TestHandler_CreateService_ServiceRegistries(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
regs := svc["serviceRegistries"].([]any)
require.Len(t, regs, 1)
@@ -441,7 +443,7 @@ func TestHandler_CreateService_NetworkConfiguration(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
nc := svc["networkConfiguration"].(map[string]any)
avpc := nc["awsvpcConfiguration"].(map[string]any)
@@ -476,7 +478,7 @@ func TestHandler_UpdateService_NetworkConfiguration(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
nc := svc["networkConfiguration"].(map[string]any)
avpc := nc["awsvpcConfiguration"].(map[string]any)
@@ -512,7 +514,7 @@ func TestHandler_UpdateService_LoadBalancers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
lbs := svc["loadBalancers"].([]any)
require.Len(t, lbs, 1)
@@ -545,7 +547,7 @@ func TestHandler_UpdateService_DeploymentConfig_MinMaxPercent(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
dc := svc["deploymentConfiguration"].(map[string]any)
assert.InDelta(t, float64(25), dc["minimumHealthyPercent"], 0.001)
diff --git a/services/ecs/handler_services_deployments_test.go b/services/ecs/handler_services_deployments_test.go
index 80cde2865e..a3fde5f6b3 100644
--- a/services/ecs/handler_services_deployments_test.go
+++ b/services/ecs/handler_services_deployments_test.go
@@ -1,7 +1,7 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -104,7 +105,7 @@ func TestECS_ListServices(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arns := resp["serviceArns"].([]any)
assert.Len(t, arns, tt.wantLen)
@@ -195,7 +196,7 @@ func TestDescribeServices_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
services, _ := resp["services"].([]any)
assert.Empty(t, services)
@@ -225,7 +226,7 @@ func TestDescribeServices_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
services, _ := resp["services"].([]any)
assert.Len(t, services, 1)
@@ -273,7 +274,7 @@ func TestDescribeServices_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
services, _ := resp["services"].([]any)
assert.Len(t, services, 2)
@@ -302,7 +303,7 @@ func TestHandler_CreateService_PropagateTags(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
assert.Equal(t, "SERVICE", svc["propagateTags"])
}
@@ -325,7 +326,7 @@ func TestHandler_CreateService_PropagateTags_DefaultNone(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
svc := out["service"].(map[string]any)
assert.Equal(t, "NONE", svc["propagateTags"])
}
@@ -342,7 +343,7 @@ func TestCreateService_DeploymentsField_Populated(t *testing.T) {
})
require.Equal(t, http.StatusOK, tdRec.Code)
var tdOut map[string]any
- require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut))
+ require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut, jsonx.V1()))
tdArn := tdOut["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
// Create cluster + service.
@@ -357,7 +358,7 @@ func TestCreateService_DeploymentsField_Populated(t *testing.T) {
require.Equal(t, http.StatusOK, svcRec.Code, svcRec.Body.String())
var svcOut map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcOut))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcOut, jsonx.V1()))
svc := svcOut["service"].(map[string]any)
require.Contains(t, svc, "deployments", "service response must include deployments")
@@ -383,7 +384,7 @@ func TestUpdateService_DeploymentRotation_OnTaskDefChange(t *testing.T) {
})
require.Equal(t, http.StatusOK, tdRec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(tdRec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(tdRec1.Body.Bytes(), &out1, jsonx.V1()))
tdArn1 := out1["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
tdRec2 := doECSRequest(t, h, "RegisterTaskDefinition", map[string]any{
@@ -392,7 +393,7 @@ func TestUpdateService_DeploymentRotation_OnTaskDefChange(t *testing.T) {
})
require.Equal(t, http.StatusOK, tdRec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(tdRec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(tdRec2.Body.Bytes(), &out2, jsonx.V1()))
tdArn2 := out2["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
doECSRequest(t, h, "CreateCluster", map[string]any{"clusterName": "staging"})
@@ -414,7 +415,7 @@ func TestUpdateService_DeploymentRotation_OnTaskDefChange(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code, updRec.Body.String())
var updOut map[string]any
- require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updOut))
+ require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updOut, jsonx.V1()))
svc := updOut["service"].(map[string]any)
deployments := svc["deployments"].([]any)
@@ -442,7 +443,7 @@ func TestDescribeServices_DeploymentsField(t *testing.T) {
})
require.Equal(t, http.StatusOK, tdRec.Code)
var tdOut map[string]any
- require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut))
+ require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut, jsonx.V1()))
tdArn := tdOut["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
doECSRequest(t, h, "CreateCluster", map[string]any{"clusterName": "ds-cluster"})
@@ -461,7 +462,7 @@ func TestDescribeServices_DeploymentsField(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code, descRec.Body.String())
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
services := descOut["services"].([]any)
require.Len(t, services, 1)
@@ -528,7 +529,7 @@ func TestECS_CreateService_Tags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
svc, ok := resp["service"].(map[string]any)
require.True(t, ok)
@@ -623,7 +624,7 @@ func TestDeploymentRolloutState_CompletedWhenDesiredMet(t *testing.T) {
require.Equal(t, http.StatusOK, descResp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &out, jsonx.V1()))
svcs, ok := out["services"].([]any)
require.True(t, ok)
diff --git a/services/ecs/handler_services_test.go b/services/ecs/handler_services_test.go
index d8407f1ece..dbde9abc6c 100644
--- a/services/ecs/handler_services_test.go
+++ b/services/ecs/handler_services_test.go
@@ -1,7 +1,7 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -78,7 +79,7 @@ func TestECS_CreateService(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
svc := resp["service"].(map[string]any)
assert.NotEmpty(t, svc["serviceArn"])
@@ -131,7 +132,7 @@ func TestECS_DescribeServices(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
svcs := resp["services"].([]any)
assert.GreaterOrEqual(t, len(svcs), 1)
@@ -141,7 +142,7 @@ func TestECS_DescribeServices(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var resp3 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp3, jsonx.V1()))
svcs3 := resp3["services"].([]any)
require.Len(t, svcs3, 1)
@@ -198,7 +199,7 @@ func TestECS_UpdateService(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
svc := resp["service"].(map[string]any)
assert.Equal(t, tt.wantDC, int(svc["desiredCount"].(float64)))
@@ -224,7 +225,7 @@ func TestECS_DeleteService(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
svc := resp["service"].(map[string]any)
assert.Equal(t, "del-svc", svc["serviceName"])
@@ -237,7 +238,7 @@ func TestECS_DeleteService(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var resp3 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp3, jsonx.V1()))
svcs3, _ := resp3["services"].([]any)
require.Len(t, svcs3, 1)
@@ -251,7 +252,7 @@ func TestECS_DeleteService(t *testing.T) {
require.Equal(t, http.StatusOK, rec4.Code)
var resp4 map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &resp4))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &resp4, jsonx.V1()))
assert.Empty(t, resp4["serviceArns"])
}
@@ -579,7 +580,7 @@ func TestCreateService_Tags_PropagateToResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, svcResp.Code)
var svcOut map[string]any
- require.NoError(t, json.Unmarshal(svcResp.Body.Bytes(), &svcOut))
+ require.NoError(t, json.Unmarshal(svcResp.Body.Bytes(), &svcOut, jsonx.V1()))
tags := svcOut["service"].(map[string]any)["tags"].([]any)
assert.Len(t, tags, 2)
}
@@ -611,7 +612,7 @@ func TestListServices_LaunchTypeFilter(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
arns := listOut["serviceArns"].([]any)
assert.Len(t, arns, 3)
}
@@ -642,7 +643,7 @@ func TestDescribeServices_MultipleServices(t *testing.T) {
})
require.Equal(t, http.StatusOK, descResp.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut, jsonx.V1()))
services := descOut["services"].([]any)
assert.Len(t, services, 4)
}
@@ -736,7 +737,7 @@ func TestListServicesByNamespace_Filter(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &out, jsonx.V1()))
arns := out["serviceArns"].([]any)
assert.Len(t, arns, 2, "only the two services actually attached to the payments namespace")
}
@@ -768,7 +769,7 @@ func TestService_Tags_ResourceTagSync(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
serviceArn := createOut["service"].(map[string]any)["serviceArn"].(string)
listRec := doECSRequest(
@@ -777,7 +778,7 @@ func TestService_Tags_ResourceTagSync(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
tags := listOut["tags"].([]any)
require.Len(t, tags, 1)
assert.Equal(t, "team", tags[0].(map[string]any)["key"])
@@ -905,7 +906,7 @@ func TestService_Tags_ResourceTagSync(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
serviceArn := createOut["service"].(map[string]any)["serviceArn"].(string)
deleteRec := doECSRequest(t, h, "DeleteService", map[string]any{
@@ -915,7 +916,7 @@ func TestService_Tags_ResourceTagSync(t *testing.T) {
require.Equal(t, http.StatusOK, deleteRec.Code)
var deleteOut map[string]any
- require.NoError(t, json.Unmarshal(deleteRec.Body.Bytes(), &deleteOut))
+ require.NoError(t, json.Unmarshal(deleteRec.Body.Bytes(), &deleteOut, jsonx.V1()))
deletedTags := deleteOut["service"].(map[string]any)["tags"].([]any)
require.Len(t, deletedTags, 1)
assert.Equal(t, "team", deletedTags[0].(map[string]any)["key"])
@@ -926,7 +927,7 @@ func TestService_Tags_ResourceTagSync(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Empty(t, listOut["tags"])
},
},
diff --git a/services/ecs/handler_tags_test.go b/services/ecs/handler_tags_test.go
index df4ba118d5..2b898e3aba 100644
--- a/services/ecs/handler_tags_test.go
+++ b/services/ecs/handler_tags_test.go
@@ -1,13 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -92,7 +93,7 @@ func TestTagResource_Service(t *testing.T) {
"desiredCount": 1,
})
var svcOut map[string]any
- require.NoError(t, json.Unmarshal(svcResp.Body.Bytes(), &svcOut))
+ require.NoError(t, json.Unmarshal(svcResp.Body.Bytes(), &svcOut, jsonx.V1()))
svcArn := svcOut["service"].(map[string]any)["serviceArn"].(string)
// Tag
@@ -111,7 +112,7 @@ func TestTagResource_Service(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
tags := listOut["tags"].([]any)
assert.Len(t, tags, 2)
}
@@ -136,7 +137,7 @@ func TestTagResource_Overwrite(t *testing.T) {
"resourceArn": fakeArn,
})
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
tags := listOut["tags"].([]any)
assert.Len(t, tags, 1)
assert.Equal(t, "prod", tags[0].(map[string]any)["value"])
@@ -167,7 +168,7 @@ func TestUntagResource(t *testing.T) {
"resourceArn": fakeArn,
})
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
tags := listOut["tags"].([]any)
assert.Len(t, tags, 1)
assert.Equal(t, "team", tags[0].(map[string]any)["key"])
@@ -191,7 +192,7 @@ func TestCreateCapacityProvider_TagsVisibleViaListTagsForResource(t *testing.T)
require.Equal(t, http.StatusOK, createResp.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut, jsonx.V1()))
cpArn := createOut["capacityProvider"].(map[string]any)["capacityProviderArn"].(string)
listResp := doECSRequest(t, h, "ListTagsForResource", map[string]any{
@@ -200,7 +201,7 @@ func TestCreateCapacityProvider_TagsVisibleViaListTagsForResource(t *testing.T)
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
tags := listOut["tags"].([]any)
require.Len(t, tags, 1)
assert.Equal(t, "env", tags[0].(map[string]any)["key"])
@@ -222,7 +223,7 @@ func TestDescribeCapacityProviders_TagResource_ReflectsInDescribe(t *testing.T)
require.Equal(t, http.StatusOK, createResp.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut, jsonx.V1()))
cpArn := createOut["capacityProvider"].(map[string]any)["capacityProviderArn"].(string)
tagResp := doECSRequest(t, h, "TagResource", map[string]any{
@@ -238,7 +239,7 @@ func TestDescribeCapacityProviders_TagResource_ReflectsInDescribe(t *testing.T)
require.Equal(t, http.StatusOK, describeResp.Code)
var describeOut map[string]any
- require.NoError(t, json.Unmarshal(describeResp.Body.Bytes(), &describeOut))
+ require.NoError(t, json.Unmarshal(describeResp.Body.Bytes(), &describeOut, jsonx.V1()))
providers := describeOut["capacityProviders"].([]any)
require.Len(t, providers, 1)
tags := providers[0].(map[string]any)["tags"].([]any)
@@ -264,7 +265,7 @@ func TestTagResource_Cluster(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &out, jsonx.V1()))
tags := out["tags"].([]any)
assert.Len(t, tags, 1)
assert.Equal(t, "owner", tags[0].(map[string]any)["key"])
@@ -334,7 +335,7 @@ func TestECS_Tagging(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp, jsonx.V1()))
tagList, ok := resp["tags"].([]any)
require.True(t, ok)
diff --git a/services/ecs/handler_task_definition_fields_test.go b/services/ecs/handler_task_definition_fields_test.go
index 6009c299bf..0bdeec7c3c 100644
--- a/services/ecs/handler_task_definition_fields_test.go
+++ b/services/ecs/handler_task_definition_fields_test.go
@@ -1,12 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestTaskDefinition_RuntimePlatform_Roundtrip(t *testing.T) {
@@ -27,7 +29,7 @@ func TestTaskDefinition_RuntimePlatform_Roundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
rp := td["runtimePlatform"].(map[string]any)
assert.Equal(t, "ARM64", rp["cpuArchitecture"])
@@ -41,7 +43,7 @@ func TestTaskDefinition_RuntimePlatform_Roundtrip(t *testing.T) {
)
require.Equal(t, http.StatusOK, descResp.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut, jsonx.V1()))
descTD := descOut["taskDefinition"].(map[string]any)
assert.Equal(t, "ARM64", descTD["runtimePlatform"].(map[string]any)["cpuArchitecture"])
}
@@ -64,7 +66,7 @@ func TestTaskDefinition_RuntimePlatform_X86(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
rp := td["runtimePlatform"].(map[string]any)
assert.Equal(t, "X86_64", rp["cpuArchitecture"])
@@ -89,7 +91,7 @@ func TestTaskDefinition_RuntimePlatform_Windows(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
rp := td["runtimePlatform"].(map[string]any)
assert.Equal(t, "WINDOWS_SERVER_2022_FULL", rp["operatingSystemFamily"])
@@ -116,7 +118,7 @@ func TestTaskDefinition_EphemeralStorage_Roundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
es := td["ephemeralStorage"].(map[string]any)
assert.InDelta(t, float64(50), es["sizeInGiB"], 0.001)
@@ -129,7 +131,7 @@ func TestTaskDefinition_EphemeralStorage_Roundtrip(t *testing.T) {
)
require.Equal(t, http.StatusOK, descResp.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut, jsonx.V1()))
descTD := descOut["taskDefinition"].(map[string]any)
assert.InDelta(t, float64(50), descTD["ephemeralStorage"].(map[string]any)["sizeInGiB"], 0.001)
}
@@ -146,7 +148,7 @@ func TestTaskDefinition_EphemeralStorage_Default(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
_, hasEph := td["ephemeralStorage"]
assert.False(t, hasEph, "ephemeralStorage should be absent when not set")
@@ -176,7 +178,7 @@ func TestTaskDefinition_InferenceAccelerators_Roundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
accs := td["inferenceAccelerators"].([]any)
@@ -212,7 +214,7 @@ func TestTaskDefinition_InferenceAccelerators_Multiple(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
accs := td["inferenceAccelerators"].([]any)
assert.Len(t, accs, 2)
@@ -239,7 +241,7 @@ func TestContainerDef_GPU_ResourceRequirement(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
cds := td["containerDefinitions"].([]any)
require.Len(t, cds, 1)
@@ -275,7 +277,7 @@ func TestContainerDef_GPU_MultipleContainers(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
cds := td["containerDefinitions"].([]any)
require.Len(t, cds, 2)
@@ -312,7 +314,7 @@ func TestContainerDef_EnvironmentFiles_Roundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
cds := td["containerDefinitions"].([]any)
cd := cds[0].(map[string]any)
@@ -359,7 +361,7 @@ func TestContainerDef_EnvironmentFiles_WithEnv(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
cd := td["containerDefinitions"].([]any)[0].(map[string]any)
@@ -411,7 +413,7 @@ func TestTaskDefinition_AllAdvancedFields_Combined(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
rp := td["runtimePlatform"].(map[string]any)
@@ -461,7 +463,7 @@ func TestTaskDefinition_BackwardCompat_NoNewFields(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
// New fields should be absent
@@ -495,7 +497,7 @@ func TestTaskDefinition_Volumes_EmptyNotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
td := resp["taskDefinition"].(map[string]any)
@@ -524,7 +526,7 @@ func TestTaskDefinition_Volumes_EmptyNotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
td := resp["taskDefinition"].(map[string]any)
@@ -560,7 +562,7 @@ func TestTaskDefinition_Volumes_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
td := resp["taskDefinition"].(map[string]any)
vols, _ := td["volumes"].([]any)
@@ -588,7 +590,7 @@ func TestHandler_RegisterTaskDefinition_RequiresCompatibilities(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
rcs := td["requiresCompatibilities"].([]any)
require.Len(t, rcs, 1)
@@ -658,7 +660,7 @@ func TestHandler_RegisterTaskDefinition_TaskRoleArn(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
assert.Equal(t, "arn:aws:iam::000000000000:role/task-role", td["taskRoleArn"])
assert.Equal(t, "arn:aws:iam::000000000000:role/exec-role", td["executionRoleArn"])
@@ -698,7 +700,7 @@ func TestHandler_RegisterTaskDefinition_Volumes(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
vols := td["volumes"].([]any)
require.Len(t, vols, 2)
@@ -734,7 +736,7 @@ func TestHandler_RegisterTaskDefinition_LogConfiguration(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
cd := td["containerDefinitions"].([]any)[0].(map[string]any)
lc := cd["logConfiguration"].(map[string]any)
@@ -764,7 +766,7 @@ func TestHandler_RegisterTaskDefinition_Secrets(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
cd := td["containerDefinitions"].([]any)[0].(map[string]any)
secrets := cd["secrets"].([]any)
@@ -798,7 +800,7 @@ func TestHandler_RegisterTaskDefinition_PortMapping_Extended(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
cd := td["containerDefinitions"].([]any)[0].(map[string]any)
pm := cd["portMappings"].([]any)[0].(map[string]any)
@@ -831,7 +833,7 @@ func TestHandler_RegisterTaskDefinition_HealthCheck(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
cd := td["containerDefinitions"].([]any)[0].(map[string]any)
hc := cd["healthCheck"].(map[string]any)
@@ -858,7 +860,7 @@ func TestHandler_DescribeTaskDefinition_Volumes_RoundTrip(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
vols := td["volumes"].([]any)
require.Len(t, vols, 1)
@@ -886,7 +888,7 @@ func TestHandler_DescribeTaskDefinition_RequiresCompatibilities_RoundTrip(t *tes
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
td := out["taskDefinition"].(map[string]any)
rcs := td["requiresCompatibilities"].([]any)
assert.Len(t, rcs, 2)
@@ -937,7 +939,7 @@ func TestECS_RegisterTaskDefinition_CPU_Memory(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
td, ok := resp["taskDefinition"].(map[string]any)
require.True(t, ok)
diff --git a/services/ecs/handler_task_definitions_test.go b/services/ecs/handler_task_definitions_test.go
index b6cb457a8f..ac5bca6cb9 100644
--- a/services/ecs/handler_task_definitions_test.go
+++ b/services/ecs/handler_task_definitions_test.go
@@ -1,7 +1,7 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -92,7 +93,7 @@ func TestECS_RegisterTaskDefinition(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
td, ok := resp["taskDefinition"].(map[string]any)
require.True(t, ok)
@@ -119,7 +120,7 @@ func TestECS_RegisterTaskDefinition_MultipleRevisions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
td := resp["taskDefinition"].(map[string]any)
assert.Equal(t, i, int(td["revision"].(float64)))
@@ -145,7 +146,7 @@ func TestECS_DescribeTaskDefinition(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
td := resp["taskDefinition"].(map[string]any)
assert.Equal(t, "web", td["family"])
@@ -174,14 +175,14 @@ func TestECS_DeregisterTaskDefinition(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
tdArn := createResp["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
rec2 := doECSRequest(t, h, "DeregisterTaskDefinition", map[string]any{"taskDefinition": tdArn})
require.Equal(t, http.StatusOK, rec2.Code)
var deregResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &deregResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &deregResp, jsonx.V1()))
td := deregResp["taskDefinition"].(map[string]any)
assert.Equal(t, "INACTIVE", td["status"])
@@ -206,7 +207,7 @@ func TestECS_ListTaskDefinitions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arns := resp["taskDefinitionArns"].([]any)
assert.GreaterOrEqual(t, len(arns), 3)
@@ -216,7 +217,7 @@ func TestECS_ListTaskDefinitions(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
arns2 := resp2["taskDefinitionArns"].([]any)
assert.Len(t, arns2, 2)
@@ -317,7 +318,7 @@ func TestECS_Handler_ListTaskDefinitions_Empty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arns, ok := resp["taskDefinitionArns"].([]any)
require.True(t, ok)
@@ -391,7 +392,7 @@ func TestECS_ListTaskDefinitions_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arns := resp["taskDefinitionArns"].([]any)
assert.GreaterOrEqual(t, len(arns), tt.wantMinCount)
@@ -421,7 +422,7 @@ func TestECS_ListTaskDefinitions_TokenChaining(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
arns1 := page1["taskDefinitionArns"].([]any)
assert.Len(t, arns1, 2)
@@ -440,7 +441,7 @@ func TestECS_ListTaskDefinitions_TokenChaining(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var page2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
arns2 := page2["taskDefinitionArns"].([]any)
assert.GreaterOrEqual(t, len(arns2), 1)
@@ -482,7 +483,7 @@ func TestECS_TaskDefinitionRevisionCap(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
td := resp["taskDefinition"].(map[string]any)
latestRev := int(td["revision"].(float64))
@@ -498,7 +499,7 @@ func TestECS_TaskDefinitionRevisionCap(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
arns := listResp["taskDefinitionArns"].([]any)
assert.LessOrEqual(t, len(arns), 100, "stored revisions should not exceed the cap")
@@ -524,7 +525,7 @@ func TestTaskDefinition_MultipleRevisions_SameFamily(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
arns := listOut["taskDefinitionArns"].([]any)
assert.Len(t, arns, 3)
@@ -534,7 +535,7 @@ func TestTaskDefinition_MultipleRevisions_SameFamily(t *testing.T) {
})
require.Equal(t, http.StatusOK, famResp.Code)
var famOut map[string]any
- require.NoError(t, json.Unmarshal(famResp.Body.Bytes(), &famOut))
+ require.NoError(t, json.Unmarshal(famResp.Body.Bytes(), &famOut, jsonx.V1()))
families := famOut["families"].([]any)
assert.Len(t, families, 1)
assert.Equal(t, "rev-family", families[0])
@@ -558,7 +559,7 @@ func TestTaskDefinition_Deregister_Specific_Revision(t *testing.T) {
})
require.Equal(t, http.StatusOK, descResp.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut, jsonx.V1()))
td := descOut["taskDefinition"].(map[string]any)
rev1Arn := td["taskDefinitionArn"].(string)
require.NotEmpty(t, rev1Arn)
@@ -569,7 +570,7 @@ func TestTaskDefinition_Deregister_Specific_Revision(t *testing.T) {
})
require.Equal(t, http.StatusOK, deregResp.Code)
var deregOut map[string]any
- require.NoError(t, json.Unmarshal(deregResp.Body.Bytes(), &deregOut))
+ require.NoError(t, json.Unmarshal(deregResp.Body.Bytes(), &deregOut, jsonx.V1()))
deregTd := deregOut["taskDefinition"].(map[string]any)
assert.Equal(t, "INACTIVE", deregTd["status"])
@@ -580,7 +581,7 @@ func TestTaskDefinition_Deregister_Specific_Revision(t *testing.T) {
})
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
arns := listOut["taskDefinitionArns"].([]any)
assert.Len(t, arns, 2)
}
@@ -597,7 +598,7 @@ func TestTaskDefinition_DeleteDefinitions_Batch(t *testing.T) {
"containerDefinitions": []any{map[string]any{"name": "app", "image": "nginx"}},
})
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
// Deregister first (required before delete)
td := out["taskDefinition"].(map[string]any)
arn := td["taskDefinitionArn"].(string)
@@ -611,7 +612,7 @@ func TestTaskDefinition_DeleteDefinitions_Batch(t *testing.T) {
require.Equal(t, http.StatusOK, deleteResp.Code)
var deleteOut map[string]any
- require.NoError(t, json.Unmarshal(deleteResp.Body.Bytes(), &deleteOut))
+ require.NoError(t, json.Unmarshal(deleteResp.Body.Bytes(), &deleteOut, jsonx.V1()))
deleted := deleteOut["taskDefinitions"].([]any)
assert.Len(t, deleted, 3)
}
@@ -640,7 +641,7 @@ func TestTaskDefinition_ListByStatus_Active_Inactive(t *testing.T) {
"status": "ACTIVE",
})
var activeOut map[string]any
- require.NoError(t, json.Unmarshal(activeResp.Body.Bytes(), &activeOut))
+ require.NoError(t, json.Unmarshal(activeResp.Body.Bytes(), &activeOut, jsonx.V1()))
assert.Len(t, activeOut["taskDefinitionArns"].([]any), 1)
// List INACTIVE
@@ -649,7 +650,7 @@ func TestTaskDefinition_ListByStatus_Active_Inactive(t *testing.T) {
"status": "INACTIVE",
})
var inactiveOut map[string]any
- require.NoError(t, json.Unmarshal(inactiveResp.Body.Bytes(), &inactiveOut))
+ require.NoError(t, json.Unmarshal(inactiveResp.Body.Bytes(), &inactiveOut, jsonx.V1()))
assert.Len(t, inactiveOut["taskDefinitionArns"].([]any), 1)
}
@@ -676,7 +677,7 @@ func TestTaskDefinitionFamilies_MultipleStatus(t *testing.T) {
})
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
families := out["families"].([]any)
famNames := make([]string, 0, len(families))
for _, f := range families {
@@ -707,7 +708,7 @@ func TestECS_DeleteTaskDefinitions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var r map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r, jsonx.V1()))
arn := r["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
doECSRequest(
@@ -733,7 +734,7 @@ func TestECS_DeleteTaskDefinitions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var r map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r, jsonx.V1()))
return r["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
},
@@ -781,7 +782,7 @@ func TestECS_DeleteTaskDefinitions(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
deleted := resp["taskDefinitions"].([]any)
failures := resp["failures"].([]any)
@@ -828,7 +829,7 @@ func TestHandler_ListTaskDefinitions_StatusFilter(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
arn1 := out1["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
rec2 := doECSRequest(t, h, "RegisterTaskDefinition", map[string]any{
@@ -852,7 +853,7 @@ func TestHandler_ListTaskDefinitions_StatusFilter(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
arns := listOut["taskDefinitionArns"].([]any)
require.Len(t, arns, 1)
assert.Contains(t, arns[0].(string), tt.wantContains)
@@ -906,7 +907,7 @@ func TestECS_ListTaskDefinitionFamilies(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
families, ok := resp["families"].([]any)
require.True(t, ok)
diff --git a/services/ecs/handler_task_exec_test.go b/services/ecs/handler_task_exec_test.go
index 24ab8b04ec..a3980eda04 100644
--- a/services/ecs/handler_task_exec_test.go
+++ b/services/ecs/handler_task_exec_test.go
@@ -1,7 +1,7 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -25,7 +26,7 @@ func TestECS_StopTask(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var runResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &runResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &runResp, jsonx.V1()))
taskArn := runResp["tasks"].([]any)[0].(map[string]any)["taskArn"].(string)
@@ -36,7 +37,7 @@ func TestECS_StopTask(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
task := resp2["task"].(map[string]any)
assert.Equal(t, "STOPPED", task["lastStatus"])
@@ -59,7 +60,7 @@ func TestECS_ListTasks(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
arns := resp["taskArns"].([]any)
assert.Len(t, arns, 3)
@@ -82,7 +83,7 @@ func TestECS_ListTasks_DesiredStatusDefaultsToRunning(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var runResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &runResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &runResp, jsonx.V1()))
tasks := runResp["tasks"].([]any)
require.Len(t, tasks, 2)
stoppedArn := tasks[0].(map[string]any)["taskArn"].(string)
@@ -94,14 +95,14 @@ func TestECS_ListTasks_DesiredStatusDefaultsToRunning(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arns := resp["taskArns"].([]any)
assert.Len(t, arns, 1, "omitted desiredStatus must default to RUNNING only")
assert.NotContains(t, arns, stoppedArn)
rec = doECSRequest(t, h, "ListTasks", map[string]any{"desiredStatus": "STOPPED"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arns = resp["taskArns"].([]any)
assert.Equal(t, []any{stoppedArn}, arns)
}
@@ -208,7 +209,7 @@ func TestECS_ListTasks_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
arns := resp["taskArns"].([]any)
assert.GreaterOrEqual(t, len(arns), tt.wantMinCount)
@@ -238,7 +239,7 @@ func TestECS_ListTasks_TokenChaining(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1, jsonx.V1()))
arns1 := page1["taskArns"].([]any)
assert.Len(t, arns1, 2)
@@ -252,7 +253,7 @@ func TestECS_ListTasks_TokenChaining(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var page2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
arns2 := page2["taskArns"].([]any)
assert.GreaterOrEqual(t, len(arns2), 1)
@@ -287,7 +288,7 @@ func TestECSExec_Basic(t *testing.T) {
})
require.Equal(t, http.StatusOK, runResp.Code)
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runResp.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runResp.Body.Bytes(), &runOut, jsonx.V1()))
tasks := runOut["tasks"].([]any)
require.Len(t, tasks, 1)
taskArn := tasks[0].(map[string]any)["taskArn"].(string)
@@ -320,7 +321,7 @@ func TestECSExec_NonInteractive(t *testing.T) {
"enableExecuteCommand": true,
})
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runResp.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runResp.Body.Bytes(), &runOut, jsonx.V1()))
taskArn := runOut["tasks"].([]any)[0].(map[string]any)["taskArn"].(string)
execResp := doECSRequest(t, h, "ExecuteCommand", map[string]any{
@@ -351,7 +352,7 @@ func TestTaskProtection_Enable_Disable(t *testing.T) {
"count": 1,
})
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runResp.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runResp.Body.Bytes(), &runOut, jsonx.V1()))
taskArn := runOut["tasks"].([]any)[0].(map[string]any)["taskArn"].(string)
// Enable protection
@@ -365,7 +366,7 @@ func TestTaskProtection_Enable_Disable(t *testing.T) {
require.Equal(t, http.StatusOK, protResp.Code)
var protOut map[string]any
- require.NoError(t, json.Unmarshal(protResp.Body.Bytes(), &protOut))
+ require.NoError(t, json.Unmarshal(protResp.Body.Bytes(), &protOut, jsonx.V1()))
prot := protOut["protectedTasks"].([]any)[0].(map[string]any)
assert.Equal(t, true, prot["protectionEnabled"])
@@ -376,7 +377,7 @@ func TestTaskProtection_Enable_Disable(t *testing.T) {
})
require.Equal(t, http.StatusOK, getResp.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getResp.Body.Bytes(), &getOut, jsonx.V1()))
protTasks := getOut["protectedTasks"].([]any)
assert.Len(t, protTasks, 1)
assert.Equal(t, true, protTasks[0].(map[string]any)["protectionEnabled"])
@@ -411,7 +412,7 @@ func TestStartTask_MultipleInstances(t *testing.T) {
),
})
var ciOut map[string]any
- require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut))
+ require.NoError(t, json.Unmarshal(ciResp.Body.Bytes(), &ciOut, jsonx.V1()))
ciArns[i] = ciOut["containerInstance"].(map[string]any)["containerInstanceArn"].(string)
}
@@ -423,7 +424,7 @@ func TestStartTask_MultipleInstances(t *testing.T) {
require.Equal(t, http.StatusOK, startResp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(startResp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(startResp.Body.Bytes(), &out, jsonx.V1()))
tasks := out["tasks"].([]any)
assert.Len(t, tasks, 3)
}
@@ -453,7 +454,7 @@ func TestECS_ExecuteCommand(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
taskArn := resp["tasks"].([]any)[0].(map[string]any)["taskArn"].(string)
@@ -486,7 +487,7 @@ func TestECS_ExecuteCommand(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
taskArn := resp["tasks"].([]any)[0].(map[string]any)["taskArn"].(string)
@@ -518,7 +519,7 @@ func TestECS_ExecuteCommand(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["clusterArn"])
assert.NotEmpty(t, resp["taskArn"])
@@ -552,7 +553,7 @@ func TestTask_EnableExecuteCommand_RunTask(t *testing.T) {
require.Equal(t, http.StatusOK, runResp.Code)
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runResp.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runResp.Body.Bytes(), &runOut, jsonx.V1()))
tasks := runOut["tasks"].([]any)
require.Len(t, tasks, 1)
assert.Equal(t, true, tasks[0].(map[string]any)["enableExecuteCommand"])
@@ -586,7 +587,7 @@ func TestTask_EnableExecuteCommand_ServicePropagation(t *testing.T) {
listResp := doECSRequest(t, h2, "ListTasks", map[string]any{"cluster": "svc-exec-cluster"})
require.Equal(t, http.StatusOK, listResp.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
arns := listOut["taskArns"].([]any)
require.NotEmpty(t, arns)
taskArn := arns[0].(string)
@@ -597,7 +598,7 @@ func TestTask_EnableExecuteCommand_ServicePropagation(t *testing.T) {
})
require.Equal(t, http.StatusOK, descResp.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut, jsonx.V1()))
tasks := descOut["tasks"].([]any)
require.Len(t, tasks, 1)
assert.Equal(t, true, tasks[0].(map[string]any)["enableExecuteCommand"])
@@ -643,7 +644,7 @@ func TestECS_TaskProtection(t *testing.T) {
require.Equal(t, http.StatusOK, runRec.Code)
var runResp map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runResp))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runResp, jsonx.V1()))
tasks := runResp["tasks"].([]any)
require.Len(t, tasks, 1)
taskArn := tasks[0].(map[string]any)["taskArn"].(string)
@@ -657,7 +658,7 @@ func TestECS_TaskProtection(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
protectedTasks, ok := resp["protectedTasks"].([]any)
require.True(t, ok)
@@ -674,7 +675,7 @@ func TestECS_TaskProtection(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
protTasks, ok := getResp["protectedTasks"].([]any)
require.True(t, ok)
@@ -749,7 +750,7 @@ func TestECS_StartTask_WithSetup(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
taskList, ok := resp["tasks"].([]any)
require.True(t, ok)
@@ -782,7 +783,7 @@ func TestECS_ListTasksFiltered(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arns, ok := resp["taskArns"].([]any)
require.True(t, ok)
@@ -796,7 +797,7 @@ func TestECS_ListTasksFiltered(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
arns2, ok := resp2["taskArns"].([]any)
require.True(t, ok)
@@ -827,7 +828,7 @@ func TestECS_StartTask_StartedBy(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tasks, ok := resp["tasks"].([]any)
require.True(t, ok)
@@ -876,7 +877,7 @@ func TestECS_ListTasksFiltered_ContainerInstance(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arns, ok := resp["taskArns"].([]any)
require.True(t, ok)
@@ -924,7 +925,7 @@ func TestExecuteCommand_RequiresFlag(t *testing.T) {
require.Equal(t, http.StatusOK, runResp.Code)
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runResp.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runResp.Body.Bytes(), &runOut, jsonx.V1()))
taskArn := runOut["tasks"].([]any)[0].(map[string]any)["taskArn"].(string)
execResp := doECSRequest(t, h, "ExecuteCommand", map[string]any{
diff --git a/services/ecs/handler_task_sets_test.go b/services/ecs/handler_task_sets_test.go
index 0223d97e3c..ae2b953562 100644
--- a/services/ecs/handler_task_sets_test.go
+++ b/services/ecs/handler_task_sets_test.go
@@ -1,13 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -70,7 +71,7 @@ func TestTaskSet_ExternalController_CreateDescribeDelete(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut, jsonx.V1()))
taskSet := createOut["taskSet"].(map[string]any)
taskSetArn := taskSet["taskSetArn"].(string)
require.NotEmpty(t, taskSetArn)
@@ -85,7 +86,7 @@ func TestTaskSet_ExternalController_CreateDescribeDelete(t *testing.T) {
})
require.Equal(t, http.StatusOK, describeResp.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(describeResp.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(describeResp.Body.Bytes(), &descOut, jsonx.V1()))
taskSets := descOut["taskSets"].([]any)
require.Len(t, taskSets, 1)
@@ -131,7 +132,7 @@ func TestTaskSet_DescribeUpdateDeleteByShortID(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut, jsonx.V1()))
taskSet := createOut["taskSet"].(map[string]any)
shortID := taskSet["id"].(string)
require.NotEmpty(t, shortID)
@@ -145,7 +146,7 @@ func TestTaskSet_DescribeUpdateDeleteByShortID(t *testing.T) {
require.Equal(t, http.StatusOK, describeResp.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(describeResp.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(describeResp.Body.Bytes(), &descOut, jsonx.V1()))
require.Len(t, descOut["taskSets"].([]any), 1)
require.Empty(t, descOut["failures"])
@@ -191,7 +192,7 @@ func TestTaskSet_UpdateScale(t *testing.T) {
})
require.Equal(t, http.StatusOK, createResp.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut, jsonx.V1()))
taskSetArn := createOut["taskSet"].(map[string]any)["taskSetArn"].(string)
// Update scale to 100%
@@ -203,7 +204,7 @@ func TestTaskSet_UpdateScale(t *testing.T) {
})
require.Equal(t, http.StatusOK, updateResp.Code)
var updateOut map[string]any
- require.NoError(t, json.Unmarshal(updateResp.Body.Bytes(), &updateOut))
+ require.NoError(t, json.Unmarshal(updateResp.Body.Bytes(), &updateOut, jsonx.V1()))
updatedScale := updateOut["taskSet"].(map[string]any)["scale"].(map[string]any)
assert.InDelta(t, float64(100), updatedScale["value"], 0.001)
}
@@ -235,7 +236,7 @@ func TestTaskSet_UpdateServicePrimaryTaskSet_Switch(t *testing.T) {
})
require.Equal(t, http.StatusOK, resp1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(resp1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(resp1.Body.Bytes(), &out1, jsonx.V1()))
taskSet1Arn := out1["taskSet"].(map[string]any)["taskSetArn"].(string)
resp2 := doECSRequest(t, h, "CreateTaskSet", map[string]any{
@@ -246,7 +247,7 @@ func TestTaskSet_UpdateServicePrimaryTaskSet_Switch(t *testing.T) {
})
require.Equal(t, http.StatusOK, resp2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(resp2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(resp2.Body.Bytes(), &out2, jsonx.V1()))
taskSet2Arn := out2["taskSet"].(map[string]any)["taskSetArn"].(string)
// Update primary task set to ts1
@@ -265,7 +266,7 @@ func TestTaskSet_UpdateServicePrimaryTaskSet_Switch(t *testing.T) {
})
require.Equal(t, http.StatusOK, descResp.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut, jsonx.V1()))
ts := descOut["taskSets"].([]any)[0].(map[string]any)
assert.Equal(t, "PRIMARY", ts["status"])
@@ -285,7 +286,7 @@ func TestTaskSet_UpdateServicePrimaryTaskSet_Switch(t *testing.T) {
})
require.Equal(t, http.StatusOK, descResp2.Code)
var descOut2 map[string]any
- require.NoError(t, json.Unmarshal(descResp2.Body.Bytes(), &descOut2))
+ require.NoError(t, json.Unmarshal(descResp2.Body.Bytes(), &descOut2, jsonx.V1()))
ts1After := descOut2["taskSets"].([]any)[0].(map[string]any)
assert.NotEqual(t, "PRIMARY", ts1After["status"])
}
@@ -325,7 +326,7 @@ func TestTaskSet_DescribeAll_NoFilter(t *testing.T) {
})
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out["taskSets"].([]any), 3)
}
@@ -398,7 +399,7 @@ func TestECS_CreateTaskSet(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ts := resp["taskSet"].(map[string]any)
assert.NotEmpty(t, ts["taskSetArn"])
@@ -447,7 +448,7 @@ func TestECS_DescribeTaskSets(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
tsArn := createResp["taskSet"].(map[string]any)["taskSetArn"].(string)
@@ -486,7 +487,7 @@ func TestECS_DescribeTaskSets(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sets := resp["taskSets"].([]any)
assert.Len(t, sets, tt.wantLen)
@@ -517,7 +518,7 @@ func TestECS_UpdateTaskSet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
tsArn := createResp["taskSet"].(map[string]any)["taskSetArn"].(string)
@@ -558,7 +559,7 @@ func TestECS_UpdateTaskSet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
tsArn := createResp["taskSet"].(map[string]any)["taskSetArn"].(string)
@@ -585,7 +586,7 @@ func TestECS_UpdateTaskSet(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ts := resp["taskSet"].(map[string]any)
scale := ts["scale"].(map[string]any)
@@ -616,7 +617,7 @@ func TestECS_DeleteTaskSet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
tsArn := createResp["taskSet"].(map[string]any)["taskSetArn"].(string)
@@ -670,7 +671,7 @@ func TestECS_DeleteTaskSet(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
sets, _ := resp["taskSets"].([]any)
assert.Empty(t, sets)
@@ -704,7 +705,7 @@ func TestECS_UpdateServicePrimaryTaskSet(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var ts1Resp map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &ts1Resp))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &ts1Resp, jsonx.V1()))
ts1Arn := ts1Resp["taskSet"].(map[string]any)["taskSetArn"].(string)
@@ -763,7 +764,7 @@ func TestECS_UpdateServicePrimaryTaskSet(t *testing.T) {
if tt.wantPrimary {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ts := resp["taskSet"].(map[string]any)
assert.Equal(t, input["_ts1Arn"], ts["taskSetArn"])
@@ -816,7 +817,7 @@ func TestTaskSet_LoadBalancers_Roundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
ts := out["taskSet"].(map[string]any)
lbs := ts["loadBalancers"].([]any)
@@ -867,7 +868,7 @@ func TestTaskSet_ServiceRegistries_Roundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
ts := out["taskSet"].(map[string]any)
srs := ts["serviceRegistries"].([]any)
require.Len(t, srs, 1)
@@ -907,7 +908,7 @@ func TestTaskSet_DescribePreservesLBAndNC(t *testing.T) {
})
require.Equal(t, http.StatusOK, createResp.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut, jsonx.V1()))
tsArn := createOut["taskSet"].(map[string]any)["taskSetArn"].(string)
descResp := doECSRequest(t, h, "DescribeTaskSets", map[string]any{
@@ -917,7 +918,7 @@ func TestTaskSet_DescribePreservesLBAndNC(t *testing.T) {
})
require.Equal(t, http.StatusOK, descResp.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &descOut, jsonx.V1()))
sets := descOut["taskSets"].([]any)
require.Len(t, sets, 1)
ts := sets[0].(map[string]any)
@@ -945,7 +946,7 @@ func TestDescribeTaskSets_TagsRequireInclude(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createOut, jsonx.V1()))
taskSetArn := createOut["taskSet"].(map[string]any)["taskSetArn"].(string)
// CreateTaskSet echoes tags unconditionally (no Include gating on Create).
@@ -963,7 +964,7 @@ func TestDescribeTaskSets_TagsRequireInclude(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ts := resp["taskSets"].([]any)[0].(map[string]any)
assert.Nil(t, ts["tags"])
})
@@ -980,7 +981,7 @@ func TestDescribeTaskSets_TagsRequireInclude(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ts := resp["taskSets"].([]any)[0].(map[string]any)
tags := ts["tags"].([]any)
require.Len(t, tags, 1)
@@ -1010,7 +1011,7 @@ func TestCreateTaskSet_CapacityProviderStrategy_Roundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
strategy := out["taskSet"].(map[string]any)["capacityProviderStrategy"].([]any)
require.Len(t, strategy, 2)
}
@@ -1039,7 +1040,7 @@ func TestDescribeTaskSets_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sets, _ := resp["taskSets"].([]any)
assert.Empty(t, sets)
@@ -1066,7 +1067,7 @@ func TestDescribeTaskSets_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &created, jsonx.V1()))
realArn := created["taskSet"].(map[string]any)["taskSetArn"].(string)
rec := doECSRequest(t, h, "DescribeTaskSets", map[string]any{
@@ -1077,7 +1078,7 @@ func TestDescribeTaskSets_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sets, _ := resp["taskSets"].([]any)
require.Len(t, sets, 1)
diff --git a/services/ecs/handler_tasks_fields_test.go b/services/ecs/handler_tasks_fields_test.go
index a03fb90b93..243eebefe4 100644
--- a/services/ecs/handler_tasks_fields_test.go
+++ b/services/ecs/handler_tasks_fields_test.go
@@ -1,13 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -32,7 +33,7 @@ func TestRunTask_ContainersField_Populated(t *testing.T) {
require.Equal(t, http.StatusOK, tdRec.Code)
var tdOut map[string]any
- require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut))
+ require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut, jsonx.V1()))
tdArn := tdOut["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
// Run a task.
@@ -43,7 +44,7 @@ func TestRunTask_ContainersField_Populated(t *testing.T) {
require.Equal(t, http.StatusOK, runRec.Code, runRec.Body.String())
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut, jsonx.V1()))
tasks := runOut["tasks"].([]any)
require.Len(t, tasks, 1)
@@ -88,14 +89,14 @@ func TestRunTask_ContainersStatus_Synced_To_Running(t *testing.T) {
require.Equal(t, http.StatusOK, tdRec.Code)
var tdOut map[string]any
- require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut))
+ require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut, jsonx.V1()))
tdArn := tdOut["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
runRec := doECSRequest(t, h, "RunTask", map[string]any{"taskDefinition": tdArn})
require.Equal(t, http.StatusOK, runRec.Code)
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut, jsonx.V1()))
task := runOut["tasks"].([]any)[0].(map[string]any)
containers := task["containers"].([]any)
@@ -129,14 +130,14 @@ func TestRunTask_ContainersHealthStatus_WithHealthCheck(t *testing.T) {
require.Equal(t, http.StatusOK, tdRec.Code)
var tdOut map[string]any
- require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut))
+ require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut, jsonx.V1()))
tdArn := tdOut["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
runRec := doECSRequest(t, h, "RunTask", map[string]any{"taskDefinition": tdArn})
require.Equal(t, http.StatusOK, runRec.Code)
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut, jsonx.V1()))
task := runOut["tasks"].([]any)[0].(map[string]any)
containers := task["containers"].([]any)
@@ -163,7 +164,7 @@ func TestRunTask_PropagateTagsTaskDefinition(t *testing.T) {
})
require.Equal(t, http.StatusOK, tdRec.Code)
var tdOut map[string]any
- require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut))
+ require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut, jsonx.V1()))
tdArn := tdOut["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
// Run a task with propagateTags=TASK_DEFINITION.
@@ -174,7 +175,7 @@ func TestRunTask_PropagateTagsTaskDefinition(t *testing.T) {
require.Equal(t, http.StatusOK, runRec.Code, runRec.Body.String())
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut, jsonx.V1()))
task := runOut["tasks"].([]any)[0].(map[string]any)
tags, ok := task["tags"].([]any)
@@ -205,7 +206,7 @@ func TestRunTask_PropagateTagsTaskDefinition_ExplicitTagWins(t *testing.T) {
})
require.Equal(t, http.StatusOK, tdRec.Code)
var tdOut map[string]any
- require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut))
+ require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut, jsonx.V1()))
tdArn := tdOut["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
// Explicit tag overrides the propagated one.
@@ -217,7 +218,7 @@ func TestRunTask_PropagateTagsTaskDefinition_ExplicitTagWins(t *testing.T) {
require.Equal(t, http.StatusOK, runRec.Code)
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut, jsonx.V1()))
task := runOut["tasks"].([]any)[0].(map[string]any)
tags := task["tags"].([]any)
@@ -243,7 +244,7 @@ func TestRunTask_ECSManagedTags_Injected(t *testing.T) {
})
require.Equal(t, http.StatusOK, tdRec.Code)
var tdOut map[string]any
- require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut))
+ require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut, jsonx.V1()))
tdArn := tdOut["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
doECSRequest(t, h, "CreateCluster", map[string]any{"clusterName": "mycluster"})
@@ -256,7 +257,7 @@ func TestRunTask_ECSManagedTags_Injected(t *testing.T) {
require.Equal(t, http.StatusOK, runRec.Code, runRec.Body.String())
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut, jsonx.V1()))
task := runOut["tasks"].([]any)[0].(map[string]any)
tags, ok := task["tags"].([]any)
@@ -285,7 +286,7 @@ func TestRunTask_Tags_InResponse(t *testing.T) {
})
require.Equal(t, http.StatusOK, tdRec.Code)
var tdOut map[string]any
- require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut))
+ require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut, jsonx.V1()))
tdArn := tdOut["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
runRec := doECSRequest(t, h, "RunTask", map[string]any{
@@ -295,7 +296,7 @@ func TestRunTask_Tags_InResponse(t *testing.T) {
require.Equal(t, http.StatusOK, runRec.Code)
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut, jsonx.V1()))
task := runOut["tasks"].([]any)[0].(map[string]any)
tags, ok := task["tags"].([]any)
@@ -318,14 +319,14 @@ func TestDescribeTasks_ContainersField_Present(t *testing.T) {
})
require.Equal(t, http.StatusOK, tdRec.Code)
var tdOut map[string]any
- require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut))
+ require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut, jsonx.V1()))
tdArn := tdOut["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
runRec := doECSRequest(t, h, "RunTask", map[string]any{"taskDefinition": tdArn})
require.Equal(t, http.StatusOK, runRec.Code)
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut, jsonx.V1()))
taskArn := runOut["tasks"].([]any)[0].(map[string]any)["taskArn"].(string)
// DescribeTasks must also return containers.
@@ -335,7 +336,7 @@ func TestDescribeTasks_ContainersField_Present(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
tasks := descOut["tasks"].([]any)
require.Len(t, tasks, 1)
@@ -368,7 +369,7 @@ func TestECS_RunTask_StartedBy(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tasks, ok := resp["tasks"].([]any)
require.True(t, ok)
diff --git a/services/ecs/handler_tasks_test.go b/services/ecs/handler_tasks_test.go
index 190ee4b97d..a4801c700e 100644
--- a/services/ecs/handler_tasks_test.go
+++ b/services/ecs/handler_tasks_test.go
@@ -1,7 +1,7 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -78,7 +79,7 @@ func TestECS_RunTask(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tasks := resp["tasks"].([]any)
assert.Len(t, tasks, tt.wantLen)
@@ -105,7 +106,7 @@ func TestECS_DescribeTasks(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var runResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &runResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &runResp, jsonx.V1()))
taskArns := make([]string, 0, 2)
for _, t := range runResp["tasks"].([]any) {
@@ -117,7 +118,7 @@ func TestECS_DescribeTasks(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Len(t, resp2["tasks"].([]any), 2)
// Describe specific task.
@@ -125,7 +126,7 @@ func TestECS_DescribeTasks(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var resp3 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp3, jsonx.V1()))
assert.Len(t, resp3["tasks"].([]any), 1)
}
@@ -161,7 +162,7 @@ func TestECS_Handler_RunTask_WithGroup(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tasks := resp["tasks"].([]any)
task := tasks[0].(map[string]any)
@@ -202,7 +203,7 @@ func TestECS_Handler_DescribeTasks_NotFound(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tasks, _ := resp["tasks"].([]any)
assert.Empty(t, tasks)
@@ -312,7 +313,7 @@ func TestRunTask_WithOverrides(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
tasks := out["tasks"].([]any)
require.Len(t, tasks, 1)
}
@@ -336,7 +337,7 @@ func TestRunTask_Count_Multiple(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
tasks := out["tasks"].([]any)
assert.Len(t, tasks, 5)
@@ -365,7 +366,7 @@ func TestDescribeTasks_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tasks, _ := resp["tasks"].([]any)
assert.Empty(t, tasks)
@@ -390,7 +391,7 @@ func TestDescribeTasks_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, runResp.Code)
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runResp.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runResp.Body.Bytes(), &runOut, jsonx.V1()))
tasksRaw, _ := runOut["tasks"].([]any)
require.Len(t, tasksRaw, 1)
taskArn := tasksRaw[0].(map[string]any)["taskArn"].(string)
@@ -401,7 +402,7 @@ func TestDescribeTasks_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tasks, _ := resp["tasks"].([]any)
assert.Len(t, tasks, 1)
@@ -438,7 +439,7 @@ func TestDescribeTasks_FailureSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tasks, _ := resp["tasks"].([]any)
assert.Empty(t, tasks)
@@ -471,7 +472,7 @@ func TestHandler_RunTask_NetworkConfiguration(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tasks := out["tasks"].([]any)
require.Len(t, tasks, 1)
task := tasks[0].(map[string]any)
@@ -558,7 +559,7 @@ func TestHandler_RunTask_PropagateTags(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tasks := out["tasks"].([]any)
require.Len(t, tasks, 1)
task := tasks[0].(map[string]any)
diff --git a/services/ecs/handler_test.go b/services/ecs/handler_test.go
index dbcf888dda..f0fd4f007a 100644
--- a/services/ecs/handler_test.go
+++ b/services/ecs/handler_test.go
@@ -3,7 +3,7 @@ package ecs_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -46,7 +47,7 @@ func doECSRequest(
) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -74,7 +75,7 @@ func fakeInstanceIdentityDocument(instanceID string) string {
"instanceId": instanceID,
"region": testRegion,
"accountId": testAccountID,
- })
+ }, jsonx.V1())
if err != nil {
panic(err)
}
@@ -124,7 +125,7 @@ func registerTestTaskDef(t *testing.T, h *ecs.Handler, family string) string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
}
@@ -500,7 +501,7 @@ func TestConcurrent_CreateServices(t *testing.T) {
"serviceName": "concurrent-svc-" + string(rune('a'+idx)),
"taskDefinition": "concurrent-task",
"desiredCount": 1,
- })
+ }, jsonx.V1())
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/x-amz-json-1.1")
@@ -540,7 +541,7 @@ func TestConcurrent_RegisterContainerInstances(t *testing.T) {
"instanceIdentityDocument": fakeInstanceIdentityDocument(
"i-concurrent-" + string(rune('a'+idx)),
),
- })
+ }, jsonx.V1())
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/x-amz-json-1.1")
@@ -565,7 +566,7 @@ func TestConcurrent_RegisterContainerInstances(t *testing.T) {
"cluster": "concurrent-ci-cluster",
})
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listOut, jsonx.V1()))
arns := listOut["containerInstanceArns"].([]any)
assert.Len(t, arns, 20)
}
@@ -584,7 +585,7 @@ func TestHandler_Reset_ClearsAll(t *testing.T) {
listResp := doECSRequest(t, h, "ListClusters", map[string]any{})
require.Equal(t, http.StatusOK, listResp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &out, jsonx.V1()))
// No custom clusters after reset
arns := out["clusterArns"].([]any)
for _, arn := range arns {
@@ -691,7 +692,7 @@ func TestHandler_Reset_ClearsCapacityProviders(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cps, ok := resp["capacityProviders"].([]any)
require.True(t, ok)
@@ -870,7 +871,7 @@ func TestHandler_Snapshot_Restore(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cps, ok := resp["capacityProviders"].([]any)
require.True(t, ok)
diff --git a/services/ecs/lambda_hooks.go b/services/ecs/lambda_hooks.go
index 508fa2fc27..f191d5681f 100644
--- a/services/ecs/lambda_hooks.go
+++ b/services/ecs/lambda_hooks.go
@@ -2,10 +2,12 @@ package ecs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"maps"
"slices"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -43,7 +45,7 @@ func parseHookResponse(resp []byte, invokeErr error) (string, time.Duration, map
}
var r hookResponse
- if json.Unmarshal(resp, &r) != nil {
+ if json.Unmarshal(resp, &r, jsonx.V1()) != nil {
return hookStatusFailed, 0, nil
}
@@ -146,7 +148,7 @@ func (inv *hookInvocation) payload() []byte {
event["hookDetails"] = inv.hookDetails
}
- raw, _ := json.Marshal(event)
+ raw, _ := json.Marshal(event, jsonx.V1())
return raw
}
diff --git a/services/ecs/models.go b/services/ecs/models.go
index 0d0c16d00b..7e6be6f796 100644
--- a/services/ecs/models.go
+++ b/services/ecs/models.go
@@ -1,8 +1,10 @@
package ecs
import (
- "encoding/json"
+ "encoding/json/v2"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- Load balancer, logging, secrets, and volume models ----
@@ -913,7 +915,7 @@ func (c *ContainerDefinition) UnmarshalJSON(data []byte) error {
type plain ContainerDefinition
p := plain{Essential: true}
- if err := json.Unmarshal(data, &p); err != nil {
+ if err := json.Unmarshal(data, &p, jsonx.V1()); err != nil {
return err
}
diff --git a/services/ecs/multi_region_test.go b/services/ecs/multi_region_test.go
index 89a98ecbda..a16d66c70d 100644
--- a/services/ecs/multi_region_test.go
+++ b/services/ecs/multi_region_test.go
@@ -1,7 +1,7 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -39,7 +40,7 @@ func regionCall(t *testing.T, h *ecs.Handler, region, op, body string) map[strin
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
out := map[string]any{}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/ecs/pagination_test.go b/services/ecs/pagination_test.go
index 84fd1a6dc7..1030f7cf2e 100644
--- a/services/ecs/pagination_test.go
+++ b/services/ecs/pagination_test.go
@@ -1,7 +1,7 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -26,7 +27,7 @@ func TestPaginationCoverage(t *testing.T) {
rec := doECSRequest(t, h, "DescribeCapacityProviders", map[string]any{"maxResults": 2})
require.Equal(t, http.StatusOK, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
cps := body["capacityProviders"].([]any)
assert.Len(t, cps, 2)
assert.NotEmpty(t, body["nextToken"])
@@ -40,7 +41,7 @@ func TestPaginationCoverage(t *testing.T) {
}
first := doECSRequest(t, h, "DescribeCapacityProviders", map[string]any{"maxResults": 2})
var b1 map[string]any
- require.NoError(t, json.Unmarshal(first.Body.Bytes(), &b1))
+ require.NoError(t, json.Unmarshal(first.Body.Bytes(), &b1, jsonx.V1()))
next := b1["nextToken"].(string)
second := doECSRequest(
@@ -51,7 +52,7 @@ func TestPaginationCoverage(t *testing.T) {
)
require.Equal(t, http.StatusOK, second.Code)
var b2 map[string]any
- require.NoError(t, json.Unmarshal(second.Body.Bytes(), &b2))
+ require.NoError(t, json.Unmarshal(second.Body.Bytes(), &b2, jsonx.V1()))
cps := b2["capacityProviders"].([]any)
assert.Len(t, cps, 1)
assert.Empty(t, b2["nextToken"])
@@ -75,7 +76,7 @@ func TestPaginationCoverage(t *testing.T) {
rec := doECSRequest(t, h, "DescribeCapacityProviders", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body["capacityProviders"].([]any), 100)
assert.NotEmpty(t, body["nextToken"])
})
@@ -101,7 +102,7 @@ func TestPaginationCoverage_ListTaskDefFamilies(t *testing.T) {
rec := doECSRequest(t, h, "ListTaskDefinitionFamilies", map[string]any{"maxResults": 2})
require.Equal(t, http.StatusOK, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body["families"].([]any), 2)
assert.NotEmpty(t, body["nextToken"])
})
@@ -122,7 +123,7 @@ func TestPaginationCoverage_ListTaskDefFamilies(t *testing.T) {
}
first := doECSRequest(t, h, "ListTaskDefinitionFamilies", map[string]any{"maxResults": 2})
var b1 map[string]any
- require.NoError(t, json.Unmarshal(first.Body.Bytes(), &b1))
+ require.NoError(t, json.Unmarshal(first.Body.Bytes(), &b1, jsonx.V1()))
next := b1["nextToken"].(string)
second := doECSRequest(
t,
@@ -131,7 +132,7 @@ func TestPaginationCoverage_ListTaskDefFamilies(t *testing.T) {
map[string]any{"maxResults": 2, "nextToken": next},
)
var b2 map[string]any
- require.NoError(t, json.Unmarshal(second.Body.Bytes(), &b2))
+ require.NoError(t, json.Unmarshal(second.Body.Bytes(), &b2, jsonx.V1()))
assert.Len(t, b2["families"].([]any), 1)
assert.Empty(t, b2["nextToken"])
})
@@ -159,7 +160,7 @@ func TestPaginationCoverage_ListTaskDefFamilies(t *testing.T) {
map[string]any{"familyPrefix": "web", "maxResults": 1},
)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body["families"].([]any), 1)
assert.NotEmpty(t, body["nextToken"])
},
@@ -200,7 +201,7 @@ func TestPaginationCoverage_ListServices(t *testing.T) {
map[string]any{"namespace": "frontend", "maxResults": 1},
)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body["serviceArns"].([]any), 1)
assert.NotEmpty(t, body["nextToken"])
})
@@ -238,7 +239,7 @@ func TestPaginationCoverage_ListServices(t *testing.T) {
map[string]any{"namespace": "frontend", "maxResults": 2},
)
var b1 map[string]any
- require.NoError(t, json.Unmarshal(first.Body.Bytes(), &b1))
+ require.NoError(t, json.Unmarshal(first.Body.Bytes(), &b1, jsonx.V1()))
second := doECSRequest(
t,
h,
@@ -250,7 +251,7 @@ func TestPaginationCoverage_ListServices(t *testing.T) {
},
)
var b2 map[string]any
- require.NoError(t, json.Unmarshal(second.Body.Bytes(), &b2))
+ require.NoError(t, json.Unmarshal(second.Body.Bytes(), &b2, jsonx.V1()))
assert.Len(t, b2["serviceArns"].([]any), 1)
assert.Empty(t, b2["nextToken"])
})
@@ -290,7 +291,7 @@ func TestPaginationCoverage_ListServices(t *testing.T) {
map[string]any{"namespace": "frontend"},
)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body["serviceArns"].([]any), 100)
assert.NotEmpty(t, body["nextToken"])
})
@@ -326,7 +327,7 @@ func TestPaginationCoverage_ListServiceDeployments(t *testing.T) {
map[string]any{"cluster": "default", "service": "svc", "maxResults": 2},
)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body["serviceDeployments"].([]any), 2)
assert.NotEmpty(t, body["nextToken"])
})
@@ -358,7 +359,7 @@ func TestPaginationCoverage_ListServiceDeployments(t *testing.T) {
map[string]any{"cluster": "default", "service": "svc", "maxResults": 2},
)
var b1 map[string]any
- require.NoError(t, json.Unmarshal(first.Body.Bytes(), &b1))
+ require.NoError(t, json.Unmarshal(first.Body.Bytes(), &b1, jsonx.V1()))
second := doECSRequest(
t,
h,
@@ -371,7 +372,7 @@ func TestPaginationCoverage_ListServiceDeployments(t *testing.T) {
},
)
var b2 map[string]any
- require.NoError(t, json.Unmarshal(second.Body.Bytes(), &b2))
+ require.NoError(t, json.Unmarshal(second.Body.Bytes(), &b2, jsonx.V1()))
assert.Len(t, b2["serviceDeployments"].([]any), 1)
assert.Empty(t, b2["nextToken"])
})
@@ -403,7 +404,7 @@ func TestPaginationCoverage_ListServiceDeployments(t *testing.T) {
map[string]any{"cluster": "default", "service": "svc"},
)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body["serviceDeployments"].([]any), 100)
assert.NotEmpty(t, body["nextToken"])
})
@@ -425,7 +426,7 @@ func TestPaginationCoverage_ListAccountSettings(t *testing.T) {
}
rec := doECSRequest(t, h, "ListAccountSettings", map[string]any{"maxResults": 2})
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body["settings"].([]any), 2)
assert.NotEmpty(t, body["nextToken"])
})
@@ -443,7 +444,7 @@ func TestPaginationCoverage_ListAccountSettings(t *testing.T) {
}
first := doECSRequest(t, h, "ListAccountSettings", map[string]any{"maxResults": 2})
var b1 map[string]any
- require.NoError(t, json.Unmarshal(first.Body.Bytes(), &b1))
+ require.NoError(t, json.Unmarshal(first.Body.Bytes(), &b1, jsonx.V1()))
second := doECSRequest(
t,
h,
@@ -451,7 +452,7 @@ func TestPaginationCoverage_ListAccountSettings(t *testing.T) {
map[string]any{"maxResults": 2, "nextToken": b1["nextToken"].(string)},
)
var b2 map[string]any
- require.NoError(t, json.Unmarshal(second.Body.Bytes(), &b2))
+ require.NoError(t, json.Unmarshal(second.Body.Bytes(), &b2, jsonx.V1()))
assert.Len(t, b2["settings"].([]any), 1)
assert.Empty(t, b2["nextToken"])
})
@@ -478,7 +479,7 @@ func TestPaginationCoverage_ListAccountSettings(t *testing.T) {
map[string]any{"name": "containerInsights", "maxResults": 2},
)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body["settings"].([]any), 2)
assert.NotEmpty(t, body["nextToken"])
})
@@ -501,7 +502,7 @@ func TestPaginationCoverage_ListAccountSettings(t *testing.T) {
}
rec := doECSRequest(t, h, "ListAccountSettings", map[string]any{})
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body["settings"].([]any), 100)
assert.NotEmpty(t, body["nextToken"])
})
@@ -532,7 +533,7 @@ func TestPaginationCoverage_ListAttributes(t *testing.T) {
}
rec := doECSRequest(t, h, "ListAttributes", map[string]any{"maxResults": 2})
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body["attributes"].([]any), 2)
assert.NotEmpty(t, body["nextToken"])
})
@@ -559,7 +560,7 @@ func TestPaginationCoverage_ListAttributes(t *testing.T) {
}
first := doECSRequest(t, h, "ListAttributes", map[string]any{"maxResults": 2})
var b1 map[string]any
- require.NoError(t, json.Unmarshal(first.Body.Bytes(), &b1))
+ require.NoError(t, json.Unmarshal(first.Body.Bytes(), &b1, jsonx.V1()))
second := doECSRequest(
t,
h,
@@ -567,7 +568,7 @@ func TestPaginationCoverage_ListAttributes(t *testing.T) {
map[string]any{"maxResults": 2, "nextToken": b1["nextToken"].(string)},
)
var b2 map[string]any
- require.NoError(t, json.Unmarshal(second.Body.Bytes(), &b2))
+ require.NoError(t, json.Unmarshal(second.Body.Bytes(), &b2, jsonx.V1()))
assert.Len(t, b2["attributes"].([]any), 1)
assert.Empty(t, b2["nextToken"])
})
@@ -599,7 +600,7 @@ func TestPaginationCoverage_ListAttributes(t *testing.T) {
map[string]any{"targetType": "container-instance", "maxResults": 1},
)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body["attributes"].([]any), 1)
assert.NotEmpty(t, body["nextToken"])
})
@@ -628,7 +629,7 @@ func TestPaginationCoverage_ListAttributes(t *testing.T) {
}
rec := doECSRequest(t, h, "ListAttributes", map[string]any{})
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Len(t, body["attributes"].([]any), 100)
assert.NotEmpty(t, body["nextToken"])
})
diff --git a/services/ecs/persistence.go b/services/ecs/persistence.go
index b70e6306aa..324087e7a8 100644
--- a/services/ecs/persistence.go
+++ b/services/ecs/persistence.go
@@ -2,7 +2,7 @@ package ecs
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -34,7 +34,7 @@ const ecsSnapshotVersion = 1
// The remaining fields are resources deliberately left as plain maps by
// registerAllTables (see its exclusion list) and so are still serialised directly.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
TaskDefinitions map[string][]*TaskDefinition `json:"taskDefinitions"`
Attributes map[string]map[string]*Attribute `json:"attributes"`
DaemonTaskDefinitions map[string][]*DaemonTaskDefinition `json:"daemonTaskDefinitions"`
diff --git a/services/ecs/realism_validation_test.go b/services/ecs/realism_validation_test.go
index 50b0504802..783784032e 100644
--- a/services/ecs/realism_validation_test.go
+++ b/services/ecs/realism_validation_test.go
@@ -1,13 +1,15 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestECSWire_InputValidation(t *testing.T) {
@@ -91,7 +93,7 @@ func TestECSWire_InputValidation(t *testing.T) {
}
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out["__type"])
assert.False(t, strings.HasPrefix(out["message"], tt.wantType), out["message"])
})
diff --git a/services/ecs/reconciler_test.go b/services/ecs/reconciler_test.go
index fad1921da6..e318462307 100644
--- a/services/ecs/reconciler_test.go
+++ b/services/ecs/reconciler_test.go
@@ -1,11 +1,13 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestReconciler_MinimumHealthyPercent_CapsScaleDown(t *testing.T) {
@@ -19,7 +21,7 @@ func TestReconciler_MinimumHealthyPercent_CapsScaleDown(t *testing.T) {
})
require.Equal(t, http.StatusOK, tdRec.Code)
var tdOut map[string]any
- require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut))
+ require.NoError(t, json.Unmarshal(tdRec.Body.Bytes(), &tdOut, jsonx.V1()))
tdArn := tdOut["taskDefinition"].(map[string]any)["taskDefinitionArn"].(string)
doECSRequest(t, h, "CreateCluster", map[string]any{"clusterName": "test"})
diff --git a/services/ecs/run_task_count_test.go b/services/ecs/run_task_count_test.go
index 271447d7e3..b903517600 100644
--- a/services/ecs/run_task_count_test.go
+++ b/services/ecs/run_task_count_test.go
@@ -1,12 +1,14 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestRunTaskCount(t *testing.T) {
@@ -40,7 +42,7 @@ func TestRunTaskCount(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(resp.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out["tasks"], tt.want)
})
}
diff --git a/services/ecs/service_deployment_filters_test.go b/services/ecs/service_deployment_filters_test.go
index 2f4f2b90d1..829840beae 100644
--- a/services/ecs/service_deployment_filters_test.go
+++ b/services/ecs/service_deployment_filters_test.go
@@ -1,7 +1,7 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -128,7 +129,7 @@ func TestListDaemonDeployments_CreatedAtFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
daemonArn, _ := created["daemonArn"].(string)
require.NotEmpty(t, daemonArn)
diff --git a/services/ecs/task_metadata.go b/services/ecs/task_metadata.go
index 49cb6d019c..609ca87976 100644
--- a/services/ecs/task_metadata.go
+++ b/services/ecs/task_metadata.go
@@ -2,7 +2,7 @@ package ecs
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"net/http"
"strconv"
@@ -24,7 +24,7 @@ var ErrContainerStatsUnavailable = errors.New("container stats unavailable")
// containerStatsRunner is implemented by runners that can report a container's resource stats.
type containerStatsRunner interface {
- ContainerStatsJSON(ctx context.Context, containerID string) (json.RawMessage, error)
+ ContainerStatsJSON(ctx context.Context, containerID string) (jsontext.Value, error)
}
type metadataLimits struct {
@@ -298,7 +298,7 @@ func (b *InMemoryBackend) TaskMetadataFor(taskID string, withTags bool) (TaskMet
return doc, true
}
-func (b *InMemoryBackend) containerStats(ctx context.Context, containerID string) (json.RawMessage, error) {
+func (b *InMemoryBackend) containerStats(ctx context.Context, containerID string) (jsontext.Value, error) {
r, ok := b.runner.(containerStatsRunner)
if !ok {
return nil, ErrContainerStatsUnavailable
@@ -412,7 +412,7 @@ func (h *Handler) serveStats(c *echo.Context, bk *InMemoryBackend, containers []
ctx, cancel := context.WithTimeout(c.Request().Context(), statsTimeout)
defer cancel()
- all := make(map[string]json.RawMessage, len(containers))
+ all := make(map[string]jsontext.Value, len(containers))
for _, cm := range containers {
raw, err := bk.containerStats(ctx, cm.DockerID)
diff --git a/services/ecs/task_metadata_test.go b/services/ecs/task_metadata_test.go
index 26149530d9..dbea811d49 100644
--- a/services/ecs/task_metadata_test.go
+++ b/services/ecs/task_metadata_test.go
@@ -2,7 +2,8 @@ package ecs_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"net/http/httptest"
@@ -14,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/ecs"
)
@@ -45,8 +47,8 @@ func (m *metaIssuer) IssueTaskCredentials(roleArn, session string) (ecs.RoleCred
type statsRunner struct{ ecs.TaskRunner }
-func (statsRunner) ContainerStatsJSON(_ context.Context, id string) (json.RawMessage, error) {
- return json.RawMessage(`{"id":"` + id + `","cpu_stats":{"online_cpus":2},"memory_stats":{"usage":1024}}`), nil
+func (statsRunner) ContainerStatsJSON(_ context.Context, id string) (jsontext.Value, error) {
+ return jsontext.Value(`{"id":"` + id + `","cpu_stats":{"online_cpus":2},"memory_stats":{"usage":1024}}`), nil
}
type metaFixture struct {
@@ -111,7 +113,7 @@ func (f *metaFixture) get(t *testing.T, path string) (int, map[string]any) {
defer resp.Body.Close()
var out map[string]any
- _ = json.NewDecoder(resp.Body).Decode(&out)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1())
return resp.StatusCode, out
}
diff --git a/services/ecs/taskdef_validation.go b/services/ecs/taskdef_validation.go
index c4379721e5..8980697657 100644
--- a/services/ecs/taskdef_validation.go
+++ b/services/ecs/taskdef_validation.go
@@ -1,11 +1,12 @@
package ecs
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -206,7 +207,7 @@ func validateNextToken(body []byte) error {
NextToken string `json:"nextToken"`
}
- if json.Unmarshal(body, &in) == nil && in.NextToken != "" && page.ValidateToken(in.NextToken) != nil {
+ if json.Unmarshal(body, &in, jsonx.V1()) == nil && in.NextToken != "" && page.ValidateToken(in.NextToken) != nil {
return fmt.Errorf("%w: Invalid token", ErrInvalidParameter)
}
diff --git a/services/ecs/undeclared_members_test.go b/services/ecs/undeclared_members_test.go
index 059bde0c3b..773358428a 100644
--- a/services/ecs/undeclared_members_test.go
+++ b/services/ecs/undeclared_members_test.go
@@ -1,7 +1,7 @@
package ecs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/ecs/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_UndeclaredMembersAbsent(t *testing.T) {
@@ -63,7 +65,7 @@ func TestHandler_UndeclaredMembersAbsent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
obj := tc.path(out)
for _, a := range tc.absent {
diff --git a/services/efs/file_system_policy.go b/services/efs/file_system_policy.go
index cab1fb5a5f..e3e70d49ba 100644
--- a/services/efs/file_system_policy.go
+++ b/services/efs/file_system_policy.go
@@ -2,8 +2,10 @@ package efs
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// DescribeFileSystemPolicy returns the resource-based policy for a file system.
@@ -48,7 +50,7 @@ func (b *InMemoryBackend) DeleteFileSystemPolicy(ctx context.Context, fileSystem
// PutFileSystemPolicy sets the resource-based policy for a file system.
// The policy must be valid JSON and no larger than 20 KB.
func (b *InMemoryBackend) PutFileSystemPolicy(ctx context.Context, fileSystemID, policy string) error {
- if !json.Valid([]byte(policy)) {
+ if !jsontext.Value([]byte(policy)).IsValid(jsonx.V1()) {
return fmt.Errorf("%w: FileSystemPolicy is not valid JSON", ErrInvalidPolicy)
}
if len(policy) > maxFileSystemPolicyBytes {
diff --git a/services/efs/handler.go b/services/efs/handler.go
index ddca51aa5f..da0386fa32 100644
--- a/services/efs/handler.go
+++ b/services/efs/handler.go
@@ -2,7 +2,8 @@ package efs
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
@@ -13,6 +14,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -455,9 +457,9 @@ func (h *Handler) Handler() echo.HandlerFunc {
var body []byte
if c.Request().Body != nil {
- decoder := json.NewDecoder(c.Request().Body)
- var raw json.RawMessage
- if err := decoder.Decode(&raw); err == nil {
+ decoder := jsontext.NewDecoder(c.Request().Body)
+ var raw jsontext.Value
+ if err := json.UnmarshalDecode(decoder, &raw, jsonx.V1()); err == nil {
body = raw
}
}
diff --git a/services/efs/handler_access_points.go b/services/efs/handler_access_points.go
index a61d58c32b..cb80abcba2 100644
--- a/services/efs/handler_access_points.go
+++ b/services/efs/handler_access_points.go
@@ -1,10 +1,12 @@
package efs
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createAccessPointBody struct {
@@ -17,7 +19,7 @@ type createAccessPointBody struct {
func (h *Handler) handleCreateAccessPoint(c *echo.Context, body []byte) error {
var in createAccessPointBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequest", "invalid request body"))
}
diff --git a/services/efs/handler_access_points_test.go b/services/efs/handler_access_points_test.go
index a1523b82f4..8219268a22 100644
--- a/services/efs/handler_access_points_test.go
+++ b/services/efs/handler_access_points_test.go
@@ -1,7 +1,7 @@
package efs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/efs"
)
@@ -261,7 +262,7 @@ func TestAccessPoint_ClientTokenIdempotency_HTTP(t *testing.T) {
var fsOut struct {
FileSystemID string `json:"FileSystemId"`
}
- require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut))
+ require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut, jsonx.V1()))
firstRec := doREST(t, h, http.MethodPost, "/2015-02-01/access-points", map[string]any{
"FileSystemId": fsOut.FileSystemID,
@@ -272,7 +273,7 @@ func TestAccessPoint_ClientTokenIdempotency_HTTP(t *testing.T) {
var firstOut struct {
AccessPointID string `json:"AccessPointId"`
}
- require.NoError(t, json.Unmarshal(firstRec.Body.Bytes(), &firstOut))
+ require.NoError(t, json.Unmarshal(firstRec.Body.Bytes(), &firstOut, jsonx.V1()))
secondRec := doREST(t, h, http.MethodPost, "/2015-02-01/access-points", map[string]any{
"FileSystemId": fsOut.FileSystemID,
@@ -283,7 +284,7 @@ func TestAccessPoint_ClientTokenIdempotency_HTTP(t *testing.T) {
var secondOut struct {
AccessPointID string `json:"AccessPointId"`
}
- require.NoError(t, json.Unmarshal(secondRec.Body.Bytes(), &secondOut))
+ require.NoError(t, json.Unmarshal(secondRec.Body.Bytes(), &secondOut, jsonx.V1()))
assert.Equal(t, firstOut.AccessPointID, secondOut.AccessPointID,
"repeated CreateAccessPoint with same ClientToken must return the same access point")
@@ -304,7 +305,7 @@ func TestDeleteAccessPoint_CleansAPIndex(t *testing.T) {
require.Equal(t, http.StatusOK, apRec.Code)
var apOut map[string]any
- require.NoError(t, json.Unmarshal(apRec.Body.Bytes(), &apOut))
+ require.NoError(t, json.Unmarshal(apRec.Body.Bytes(), &apOut, jsonx.V1()))
apID := apOut["AccessPointId"].(string)
blockRec := doREST(t, h, http.MethodDelete, "/2015-02-01/file-systems/"+fsID, nil)
diff --git a/services/efs/handler_account_preferences.go b/services/efs/handler_account_preferences.go
index f2df42b005..ccd7136e1e 100644
--- a/services/efs/handler_account_preferences.go
+++ b/services/efs/handler_account_preferences.go
@@ -1,10 +1,12 @@
package efs
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDescribeAccountPreferences(c *echo.Context) error {
@@ -30,7 +32,7 @@ type putAccountPreferencesBody struct {
func (h *Handler) handlePutAccountPreferences(c *echo.Context, body []byte) error {
var in putAccountPreferencesBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequest", "invalid request body"))
}
diff --git a/services/efs/handler_backup_policy.go b/services/efs/handler_backup_policy.go
index 955a44b50f..71fc5ee336 100644
--- a/services/efs/handler_backup_policy.go
+++ b/services/efs/handler_backup_policy.go
@@ -1,10 +1,12 @@
package efs
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDescribeBackupPolicy(c *echo.Context, fileSystemID string) error {
@@ -28,7 +30,7 @@ type putBackupPolicyBody struct {
func (h *Handler) handlePutBackupPolicy(c *echo.Context, fileSystemID string, body []byte) error {
var in putBackupPolicyBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequest", "invalid request body"))
}
diff --git a/services/efs/handler_backup_policy_test.go b/services/efs/handler_backup_policy_test.go
index 6ba4997209..5a70c56594 100644
--- a/services/efs/handler_backup_policy_test.go
+++ b/services/efs/handler_backup_policy_test.go
@@ -1,13 +1,14 @@
package efs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/efs"
)
@@ -114,7 +115,7 @@ func TestBackupPolicy_EnabledRoundTrip(t *testing.T) {
var fsOut struct {
FileSystemID string `json:"FileSystemId"`
}
- require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut))
+ require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut, jsonx.V1()))
putRec := doREST(t, h, http.MethodPut,
"/2015-02-01/file-systems/"+fsOut.FileSystemID+"/backup-policy",
@@ -130,7 +131,7 @@ func TestBackupPolicy_EnabledRoundTrip(t *testing.T) {
Status string `json:"Status"`
} `json:"BackupPolicy"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, "ENABLED", descOut.BackupPolicy.Status)
}
@@ -150,7 +151,7 @@ func TestBackupPolicy_InvalidStatusRejected(t *testing.T) {
var fsOut struct {
FileSystemID string `json:"FileSystemId"`
}
- require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut))
+ require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut, jsonx.V1()))
rec := doREST(t, h, http.MethodPut,
"/2015-02-01/file-systems/"+fsOut.FileSystemID+"/backup-policy",
diff --git a/services/efs/handler_file_system_policy.go b/services/efs/handler_file_system_policy.go
index f5a8e1a814..4f147b6c4e 100644
--- a/services/efs/handler_file_system_policy.go
+++ b/services/efs/handler_file_system_policy.go
@@ -1,10 +1,12 @@
package efs
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDescribeFileSystemPolicy(c *echo.Context, fileSystemID string) error {
@@ -38,7 +40,7 @@ func (h *Handler) handlePutFileSystemPolicy(
body []byte,
) error {
var in putFileSystemPolicyBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequest", "invalid request body"))
}
diff --git a/services/efs/handler_file_system_policy_test.go b/services/efs/handler_file_system_policy_test.go
index e6c340d00e..3910cdd3ee 100644
--- a/services/efs/handler_file_system_policy_test.go
+++ b/services/efs/handler_file_system_policy_test.go
@@ -1,7 +1,7 @@
package efs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/efs"
)
@@ -262,7 +263,7 @@ func TestFileSystemPolicy_RoundTrip(t *testing.T) {
var fsOut struct {
FileSystemID string `json:"FileSystemId"`
}
- require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut))
+ require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut, jsonx.V1()))
policy := `{"Version":"2012-10-17","Statement":[` +
`{"Effect":"Allow","Principal":{"AWS":"*"},"Action":"elasticfilesystem:ClientMount"}]}`
@@ -279,7 +280,7 @@ func TestFileSystemPolicy_RoundTrip(t *testing.T) {
var descOut struct {
Policy string `json:"Policy"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.JSONEq(t, policy, descOut.Policy,
"DescribeFileSystemPolicy must return the stored policy verbatim")
diff --git a/services/efs/handler_file_systems.go b/services/efs/handler_file_systems.go
index de95e45729..bcfe658f75 100644
--- a/services/efs/handler_file_systems.go
+++ b/services/efs/handler_file_systems.go
@@ -1,12 +1,13 @@
package efs
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -24,7 +25,7 @@ type createFileSystemBody struct {
func (h *Handler) handleCreateFileSystem(c *echo.Context, body []byte) error {
var in createFileSystemBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequest", "invalid request body"))
}
@@ -154,7 +155,7 @@ type updateFileSystemBody struct {
func (h *Handler) handleUpdateFileSystem(c *echo.Context, fileSystemID string, body []byte) error {
var in updateFileSystemBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequest", "invalid request body"))
}
diff --git a/services/efs/handler_file_systems_test.go b/services/efs/handler_file_systems_test.go
index d5bb634666..3afba87518 100644
--- a/services/efs/handler_file_systems_test.go
+++ b/services/efs/handler_file_systems_test.go
@@ -1,7 +1,7 @@
package efs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/efs"
)
@@ -613,7 +614,7 @@ func TestSizeInBytes_IncludesStorageClassBreakdown(t *testing.T) {
var createOut struct {
FileSystemID string `json:"FileSystemId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
descRec := doREST(t, h, http.MethodGet,
"/2015-02-01/file-systems?FileSystemId="+createOut.FileSystemID, nil)
@@ -624,7 +625,7 @@ func TestSizeInBytes_IncludesStorageClassBreakdown(t *testing.T) {
SizeInBytes map[string]any `json:"SizeInBytes"`
} `json:"FileSystems"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
require.Len(t, descOut.FileSystems, 1)
sib := descOut.FileSystems[0].SizeInBytes
@@ -655,7 +656,7 @@ func TestDeleteFileSystem_RejectedWhenMountTargetsExist(t *testing.T) {
var fsOut struct {
FileSystemID string `json:"FileSystemId"`
}
- require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut))
+ require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut, jsonx.V1()))
mtRec := doREST(t, h, http.MethodPost, "/2015-02-01/mount-targets", map[string]any{
"FileSystemId": fsOut.FileSystemID,
@@ -685,7 +686,7 @@ func TestFileSystem_CreatingToAvailableLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "creating", out["LifeCycleState"],
"newly created FS should be in 'creating' state immediately after CreateFileSystem")
@@ -701,7 +702,7 @@ func TestFileSystem_CreatingToAvailableLifecycle(t *testing.T) {
LifeCycleState string `json:"LifeCycleState"`
} `json:"FileSystems"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
require.NotEmpty(t, descOut.FileSystems)
return descOut.FileSystems[0].LifeCycleState
@@ -850,7 +851,7 @@ func TestDescribeFileSystems_PaginationMarker(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page fsPage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page, jsonx.V1()))
require.NotEmpty(t, page.FileSystems, "page must not be empty while a marker is being followed")
pageSizes = append(pageSizes, len(page.FileSystems))
diff --git a/services/efs/handler_lifecycle_config.go b/services/efs/handler_lifecycle_config.go
index dd840e8b2b..f218bde313 100644
--- a/services/efs/handler_lifecycle_config.go
+++ b/services/efs/handler_lifecycle_config.go
@@ -1,10 +1,12 @@
package efs
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type putLifecycleConfigBody struct {
@@ -28,7 +30,7 @@ func (h *Handler) handlePutLifecycleConfiguration(
body []byte,
) error {
var in putLifecycleConfigBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequest", "invalid request body"))
}
diff --git a/services/efs/handler_lifecycle_config_test.go b/services/efs/handler_lifecycle_config_test.go
index 3e8162231c..50e7201aaa 100644
--- a/services/efs/handler_lifecycle_config_test.go
+++ b/services/efs/handler_lifecycle_config_test.go
@@ -1,12 +1,14 @@
package efs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestLifecycleConfiguration tests DescribeLifecycleConfiguration and PutLifecycleConfiguration.
@@ -142,7 +144,7 @@ func TestLifecyclePolicy_InvalidTransitionRejected(t *testing.T) {
var fsOut struct {
FileSystemID string `json:"FileSystemId"`
}
- require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut))
+ require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut, jsonx.V1()))
rec := doREST(t, h, http.MethodPut,
"/2015-02-01/file-systems/"+fsOut.FileSystemID+"/lifecycle-configuration",
diff --git a/services/efs/handler_mount_target_security_groups.go b/services/efs/handler_mount_target_security_groups.go
index 3f8ab3fdfd..fd04a6dcf4 100644
--- a/services/efs/handler_mount_target_security_groups.go
+++ b/services/efs/handler_mount_target_security_groups.go
@@ -1,10 +1,12 @@
package efs
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDescribeMountTargetSecurityGroups(
@@ -34,7 +36,7 @@ func (h *Handler) handleModifyMountTargetSecurityGroups(
body []byte,
) error {
var in modifyMountTargetSGBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequest", "invalid request body"))
}
diff --git a/services/efs/handler_mount_targets.go b/services/efs/handler_mount_targets.go
index 4208533d9c..b132daba17 100644
--- a/services/efs/handler_mount_targets.go
+++ b/services/efs/handler_mount_targets.go
@@ -1,11 +1,13 @@
package efs
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createMountTargetBody struct {
@@ -19,7 +21,7 @@ type createMountTargetBody struct {
func (h *Handler) handleCreateMountTarget(c *echo.Context, body []byte) error {
var in createMountTargetBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequest", "invalid request body"))
}
diff --git a/services/efs/handler_mount_targets_test.go b/services/efs/handler_mount_targets_test.go
index cf6ba7d98f..d0aa26b006 100644
--- a/services/efs/handler_mount_targets_test.go
+++ b/services/efs/handler_mount_targets_test.go
@@ -1,7 +1,7 @@
package efs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"net/http"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/efs"
)
@@ -559,7 +560,7 @@ func TestMountTarget_SecurityGroupLimitEnforced(t *testing.T) {
var fsOut struct {
FileSystemID string `json:"FileSystemId"`
}
- require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut))
+ require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut, jsonx.V1()))
rec := doREST(t, h, http.MethodPost, "/2015-02-01/mount-targets", map[string]any{
"FileSystemId": fsOut.FileSystemID,
@@ -589,7 +590,7 @@ func TestMountTarget_DuplicateSubnetRejected(t *testing.T) {
var fsOut struct {
FileSystemID string `json:"FileSystemId"`
}
- require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut))
+ require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut, jsonx.V1()))
firstRec := doREST(t, h, http.MethodPost, "/2015-02-01/mount-targets", map[string]any{
"FileSystemId": fsOut.FileSystemID,
@@ -633,7 +634,7 @@ func TestCreateMountTarget_PopulatesVpcAndAZ(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "CreateMountTarget: %s", rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
vpcID, _ := out["VpcId"].(string)
azName, _ := out["AvailabilityZoneName"].(string)
@@ -666,7 +667,7 @@ func TestCreateMountTarget_VpcIDStablePerSubnet(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var mt1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &mt1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &mt1, jsonx.V1()))
rec2 := doREST(t, h, http.MethodPost, "/2015-02-01/mount-targets", map[string]any{
"FileSystemId": fsID2,
@@ -675,7 +676,7 @@ func TestCreateMountTarget_VpcIDStablePerSubnet(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var mt2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &mt2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &mt2, jsonx.V1()))
assert.Equal(t, mt1["VpcId"], mt2["VpcId"],
"same subnet should yield same VpcId across different file systems")
@@ -697,7 +698,7 @@ func TestCreateMountTarget_OneZone_InheritsAZ(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var fsOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fsOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fsOut, jsonx.V1()))
fsID := fsOut["FileSystemId"].(string)
mtRec := doREST(t, h, http.MethodPost, "/2015-02-01/mount-targets", map[string]any{
@@ -707,7 +708,7 @@ func TestCreateMountTarget_OneZone_InheritsAZ(t *testing.T) {
require.Equal(t, http.StatusOK, mtRec.Code)
var mt map[string]any
- require.NoError(t, json.Unmarshal(mtRec.Body.Bytes(), &mt))
+ require.NoError(t, json.Unmarshal(mtRec.Body.Bytes(), &mt, jsonx.V1()))
assert.Equal(t, az, mt["AvailabilityZoneName"],
"mount target should inherit FS AvailabilityZoneName for One Zone storage class")
@@ -770,7 +771,7 @@ func TestDescribeMountTargets_Pagination_HTTP(t *testing.T) {
}
var pg1 mtPage
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &pg1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &pg1, jsonx.V1()))
require.Len(t, pg1.MountTargets, 3)
require.NotEmpty(t, pg1.NextMarker)
@@ -779,7 +780,7 @@ func TestDescribeMountTargets_Pagination_HTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var pg2 mtPage
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &pg2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &pg2, jsonx.V1()))
// 5 items, pageSize=3: page1=[0,1,2] marker=items[3], page2 resumes AT items[3] => [3,4].
assert.Len(t, pg2.MountTargets, 2)
assert.Empty(t, pg2.NextMarker)
@@ -815,7 +816,7 @@ func TestDeleteMountTarget_CleansSubnetIndex(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var mt1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &mt1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &mt1, jsonx.V1()))
mtID := mt1["MountTargetId"].(string)
delRec := doREST(t, h, http.MethodDelete, "/2015-02-01/mount-targets/"+mtID, nil)
diff --git a/services/efs/handler_replication.go b/services/efs/handler_replication.go
index f09b965a77..4cab8409f8 100644
--- a/services/efs/handler_replication.go
+++ b/services/efs/handler_replication.go
@@ -1,10 +1,12 @@
package efs
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createReplicationConfigBody struct {
@@ -17,7 +19,7 @@ func (h *Handler) handleCreateReplicationConfiguration(
body []byte,
) error {
var in createReplicationConfigBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequest", "invalid request body"))
}
@@ -132,7 +134,7 @@ func (h *Handler) handleUpdateFileSystemProtection(
body []byte,
) error {
var in updateFileSystemProtectionBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequest", "invalid request body"))
}
diff --git a/services/efs/handler_replication_test.go b/services/efs/handler_replication_test.go
index e981e135d7..0ee5845815 100644
--- a/services/efs/handler_replication_test.go
+++ b/services/efs/handler_replication_test.go
@@ -1,7 +1,7 @@
package efs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/efs"
)
@@ -351,7 +352,7 @@ func TestReplicationConfiguration_SourceOwnerInResponse(t *testing.T) {
var fsOut struct {
FileSystemID string `json:"FileSystemId"`
}
- require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut))
+ require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut, jsonx.V1()))
rcRec := doREST(t, h, http.MethodPost,
"/2015-02-01/file-systems/"+fsOut.FileSystemID+"/replication-configuration",
@@ -365,7 +366,7 @@ func TestReplicationConfiguration_SourceOwnerInResponse(t *testing.T) {
var rcOut struct {
SourceFileSystemOwnerID string `json:"SourceFileSystemOwnerId"`
}
- require.NoError(t, json.Unmarshal(rcRec.Body.Bytes(), &rcOut))
+ require.NoError(t, json.Unmarshal(rcRec.Body.Bytes(), &rcOut, jsonx.V1()))
assert.NotEmpty(t, rcOut.SourceFileSystemOwnerID,
"SourceFileSystemOwnerId must be present in CreateReplicationConfiguration response")
@@ -393,7 +394,7 @@ func TestReplicationConfiguration_DestinationHasOwnerNoArn(t *testing.T) {
var fsOut struct {
FileSystemID string `json:"FileSystemId"`
}
- require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut))
+ require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut, jsonx.V1()))
rcRec := doREST(t, h, http.MethodPost,
"/2015-02-01/file-systems/"+fsOut.FileSystemID+"/replication-configuration",
@@ -405,7 +406,7 @@ func TestReplicationConfiguration_DestinationHasOwnerNoArn(t *testing.T) {
require.Equal(t, http.StatusOK, rcRec.Code, "CreateReplicationConfiguration failed: %s", rcRec.Body.String())
var raw map[string]any
- require.NoError(t, json.Unmarshal(rcRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rcRec.Body.Bytes(), &raw, jsonx.V1()))
dests, ok := raw["Destinations"].([]any)
require.True(t, ok)
require.Len(t, dests, 1)
@@ -420,7 +421,7 @@ func TestReplicationConfiguration_DestinationHasOwnerNoArn(t *testing.T) {
Status string `json:"Status"`
} `json:"Destinations"`
}
- require.NoError(t, json.Unmarshal(rcRec.Body.Bytes(), &rcOut))
+ require.NoError(t, json.Unmarshal(rcRec.Body.Bytes(), &rcOut, jsonx.V1()))
require.Len(t, rcOut.Destinations, 1)
d := rcOut.Destinations[0]
@@ -448,7 +449,7 @@ func TestDescribeReplicationConfigurations_ByDestinationFileSystemID(t *testing.
var fsOut struct {
FileSystemID string `json:"FileSystemId"`
}
- require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut))
+ require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut, jsonx.V1()))
rcRec := doREST(t, h, http.MethodPost,
"/2015-02-01/file-systems/"+fsOut.FileSystemID+"/replication-configuration",
@@ -464,7 +465,7 @@ func TestDescribeReplicationConfigurations_ByDestinationFileSystemID(t *testing.
FileSystemID string `json:"FileSystemId"`
} `json:"Destinations"`
}
- require.NoError(t, json.Unmarshal(rcRec.Body.Bytes(), &rcOut))
+ require.NoError(t, json.Unmarshal(rcRec.Body.Bytes(), &rcOut, jsonx.V1()))
require.Len(t, rcOut.Destinations, 1)
destFSID := rcOut.Destinations[0].FileSystemID
@@ -483,7 +484,7 @@ func TestDescribeReplicationConfigurations_ByDestinationFileSystemID(t *testing.
SourceFileSystemID string `json:"SourceFileSystemId"`
} `json:"Replications"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut.Replications, 1,
"DescribeReplicationConfigurations must return result when filtering by destination FS ID")
@@ -602,7 +603,7 @@ func TestCreateReplicationConfiguration_AssignsDestinationFileSystemID(t *testin
var fsOut struct {
FileSystemID string `json:"FileSystemId"`
}
- require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut))
+ require.NoError(t, json.Unmarshal(fsRec.Body.Bytes(), &fsOut, jsonx.V1()))
rcRec := doREST(t, h, http.MethodPost,
"/2015-02-01/file-systems/"+fsOut.FileSystemID+"/replication-configuration",
@@ -618,7 +619,7 @@ func TestCreateReplicationConfiguration_AssignsDestinationFileSystemID(t *testin
FileSystemID string `json:"FileSystemId"`
} `json:"Destinations"`
}
- require.NoError(t, json.Unmarshal(rcRec.Body.Bytes(), &rcOut))
+ require.NoError(t, json.Unmarshal(rcRec.Body.Bytes(), &rcOut, jsonx.V1()))
require.Len(t, rcOut.Destinations, 1)
assert.NotEmpty(t, rcOut.Destinations[0].FileSystemID,
diff --git a/services/efs/handler_tags.go b/services/efs/handler_tags.go
index 5702059043..afdbd1c8b9 100644
--- a/services/efs/handler_tags.go
+++ b/services/efs/handler_tags.go
@@ -1,13 +1,14 @@
package efs
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -17,7 +18,7 @@ type tagResourceBody struct {
func (h *Handler) handleTagResource(c *echo.Context, resourceID string, body []byte) error {
var in tagResourceBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequest", "invalid request body"))
}
@@ -110,7 +111,7 @@ type createTagsBody struct {
func (h *Handler) handleCreateTags(c *echo.Context, fileSystemID string, body []byte) error {
var in createTagsBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequest", "invalid request body"))
}
@@ -128,7 +129,7 @@ type deleteTagsBody struct {
func (h *Handler) handleDeleteTags(c *echo.Context, fileSystemID string, body []byte) error {
var in deleteTagsBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequest", "invalid request body"))
}
diff --git a/services/efs/helpers_test.go b/services/efs/helpers_test.go
index 9d05f6d4f5..2228963364 100644
--- a/services/efs/helpers_test.go
+++ b/services/efs/helpers_test.go
@@ -2,7 +2,7 @@ package efs_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/efs"
)
@@ -37,7 +38,7 @@ func doREST(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -58,7 +59,7 @@ func parseResp(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
diff --git a/services/efs/persistence.go b/services/efs/persistence.go
index cbfc3f542d..8c58b6fce5 100644
--- a/services/efs/persistence.go
+++ b/services/efs/persistence.go
@@ -2,7 +2,7 @@ package efs
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -52,7 +52,7 @@ func regionalDTOKeyFn[V any](d *regionalDTO[V]) string { return regionKey(d.Regi
// from an incompatible (older or newer) build of this backend as though it
// were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
LifecyclePolicies map[string]map[string][]LifecyclePolicy `json:"lifecyclePolicies"`
BackupPolicies map[string]map[string]string `json:"backupPolicies"`
FileSystemPolicies map[string]map[string]string `json:"fileSystemPolicies"`
@@ -199,7 +199,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreResourceTables rebuilds every store.Table on b from snap's
// per-table JSON, factored out of Restore to keep Restore's own cognitive
// complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
dtos := buildPersistenceDTORegistry()
if err := dtos.registry.RestoreAll(tables); err != nil {
diff --git a/services/efs/wire_sdk_roundtrip_test.go b/services/efs/wire_sdk_roundtrip_test.go
index 7c0cd26100..a0d159ee19 100644
--- a/services/efs/wire_sdk_roundtrip_test.go
+++ b/services/efs/wire_sdk_roundtrip_test.go
@@ -1,7 +1,7 @@
package efs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/efs"
)
@@ -91,7 +92,7 @@ func TestReplicationDestination_NoFabricatedFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "CreateReplicationConfiguration failed: %s", rec.Body.String())
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
dests, ok := raw["Destinations"].([]any)
require.True(t, ok)
diff --git a/services/eks/addons.go b/services/eks/addons.go
index fde0c8cd77..88af4e5733 100644
--- a/services/eks/addons.go
+++ b/services/eks/addons.go
@@ -1,7 +1,7 @@
package eks
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -445,7 +446,7 @@ func (b *InMemoryBackend) DescribeAddonConfiguration(addonName, addonVersion str
schema, _ := json.Marshal(map[string]any{
keyType: "object",
"properties": map[string]any{},
- })
+ }, jsonx.V1())
return map[string]any{
keyAddonName: addonName,
diff --git a/services/eks/capability_configuration.go b/services/eks/capability_configuration.go
index 4678daca59..f013a834db 100644
--- a/services/eks/capability_configuration.go
+++ b/services/eks/capability_configuration.go
@@ -1,6 +1,12 @@
package eks
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "encoding/json/jsontext"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
// Wire-shape request/response bodies and validation/conversion for
// Capability.Configuration -- gopherstack-wf8f item 1. Every field/key here
@@ -424,12 +430,12 @@ func (c *Capability) UnmarshalJSON(data []byte) error {
aux := struct {
*capabilityAlias
- Configuration json.RawMessage `json:"configuration"`
+ Configuration jsontext.Value `json:"configuration"`
}{
capabilityAlias: (*capabilityAlias)(c),
}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -440,7 +446,7 @@ func (c *Capability) UnmarshalJSON(data []byte) error {
}
var cfg CapabilityConfiguration
- if err := json.Unmarshal(aux.Configuration, &cfg); err == nil {
+ if err := json.Unmarshal(aux.Configuration, &cfg, jsonx.V1()); err == nil {
c.Configuration = &cfg
}
diff --git a/services/eks/cluster_config_blocks.go b/services/eks/cluster_config_blocks.go
index 833d07808a..08260ad311 100644
--- a/services/eks/cluster_config_blocks.go
+++ b/services/eks/cluster_config_blocks.go
@@ -1,27 +1,30 @@
package eks
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// clusterConfigBlocksBody holds the cluster config members stored and echoed verbatim.
type clusterConfigBlocksBody struct {
- ControlPlaneScalingConfig json.RawMessage `json:"controlPlaneScalingConfig"`
- KubeAPIServerConfig json.RawMessage `json:"kubeApiServerConfig"`
- KubeControllerManagerConfig json.RawMessage `json:"kubeControllerManagerConfig"`
- KubeSchedulerConfig json.RawMessage `json:"kubeSchedulerConfig"`
- OutpostConfig json.RawMessage `json:"outpostConfig"`
- RemoteNetworkConfig json.RawMessage `json:"remoteNetworkConfig"`
- ZonalShiftConfig json.RawMessage `json:"zonalShiftConfig"`
+ ControlPlaneScalingConfig jsontext.Value `json:"controlPlaneScalingConfig"`
+ KubeAPIServerConfig jsontext.Value `json:"kubeApiServerConfig"`
+ KubeControllerManagerConfig jsontext.Value `json:"kubeControllerManagerConfig"`
+ KubeSchedulerConfig jsontext.Value `json:"kubeSchedulerConfig"`
+ OutpostConfig jsontext.Value `json:"outpostConfig"`
+ RemoteNetworkConfig jsontext.Value `json:"remoteNetworkConfig"`
+ ZonalShiftConfig jsontext.Value `json:"zonalShiftConfig"`
}
// blocks returns the supplied members keyed by wire name; outpostConfig is create-only.
-func (b clusterConfigBlocksBody) blocks(forCreate bool) map[string]json.RawMessage {
- all := map[string]json.RawMessage{
+func (b clusterConfigBlocksBody) blocks(forCreate bool) map[string]jsontext.Value {
+ all := map[string]jsontext.Value{
"controlPlaneScalingConfig": b.ControlPlaneScalingConfig,
"kubeApiServerConfig": b.KubeAPIServerConfig,
"kubeControllerManagerConfig": b.KubeControllerManagerConfig,
@@ -51,10 +54,10 @@ func validTiers() []string {
return []string{"standard", "tier-xl", "tier-2xl", "tier-4xl", "tier-8xl"}
}
-func validateConfigBlocks(blocks map[string]json.RawMessage) error {
+func validateConfigBlocks(blocks map[string]jsontext.Value) error {
for key, raw := range blocks {
- var obj map[string]json.RawMessage
- if err := json.Unmarshal(raw, &obj); err != nil {
+ var obj map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &obj, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %s must be an object", ErrValidation, key)
}
}
@@ -63,7 +66,7 @@ func validateConfigBlocks(blocks map[string]json.RawMessage) error {
var cfg struct {
Tier string `json:"tier"`
}
- _ = json.Unmarshal(raw, &cfg)
+ _ = json.Unmarshal(raw, &cfg, jsonx.V1())
if cfg.Tier != "" && !slices.Contains(validTiers(), cfg.Tier) {
return fmt.Errorf("%w: controlPlaneScalingConfig.tier must be one of %s",
@@ -89,7 +92,7 @@ func configBlockParamType(key string) string {
return strings.ToUpper(key[:1]) + key[1:]
}
-func cloneConfigBlocks(src map[string]json.RawMessage) map[string]json.RawMessage {
+func cloneConfigBlocks(src map[string]jsontext.Value) map[string]jsontext.Value {
if src == nil {
return nil
}
@@ -98,14 +101,14 @@ func cloneConfigBlocks(src map[string]json.RawMessage) map[string]json.RawMessag
}
// applyConfigBlocksLocked replaces the stored blocks and returns the update params. Caller holds b.mu.
-func applyConfigBlocksLocked(c *Cluster, blocks map[string]json.RawMessage) []UpdateParam {
+func applyConfigBlocksLocked(c *Cluster, blocks map[string]jsontext.Value) []UpdateParam {
if len(blocks) == 0 {
return nil
}
merged := cloneConfigBlocks(c.ConfigBlocks)
if merged == nil {
- merged = make(map[string]json.RawMessage, len(blocks))
+ merged = make(map[string]jsontext.Value, len(blocks))
}
var params []UpdateParam
@@ -128,11 +131,11 @@ func applyConfigBlocksLocked(c *Cluster, blocks map[string]json.RawMessage) []Up
return params
}
-func blockTier(raw json.RawMessage) string {
+func blockTier(raw jsontext.Value) string {
var cfg struct {
Tier string `json:"tier"`
}
- _ = json.Unmarshal(raw, &cfg)
+ _ = json.Unmarshal(raw, &cfg, jsonx.V1())
return cfg.Tier
}
diff --git a/services/eks/cluster_options_test.go b/services/eks/cluster_options_test.go
index fa8e520a93..cae5d09560 100644
--- a/services/eks/cluster_options_test.go
+++ b/services/eks/cluster_options_test.go
@@ -4,7 +4,7 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"io"
"net/http"
@@ -24,6 +24,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc"
"github.com/blackbirdworks/gopherstack/services/eks"
)
@@ -405,7 +406,7 @@ func TestServePublishedPorts(t *testing.T) {
HostPort int `json:"hostPort"`
} `json:"ports"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "c1", body.ClusterName)
require.Len(t, body.Ports, tt.wantPorts)
assert.Equal(t, 8080, body.Ports[0].ContainerPort)
diff --git a/services/eks/clusters.go b/services/eks/clusters.go
index 9dbb787b08..4f0b334d49 100644
--- a/services/eks/clusters.go
+++ b/services/eks/clusters.go
@@ -2,7 +2,7 @@ package eks
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"slices"
"strings"
@@ -21,7 +21,7 @@ type ClusterOptionalConfig struct {
AccessConfig *AccessConfig
ComputeConfig *ComputeConfig
StorageConfig *StorageConfig
- ConfigBlocks map[string]json.RawMessage
+ ConfigBlocks map[string]jsontext.Value
UpgradePolicySupportType string
CreatorARN string
LogEntries []ClusterLogEntry
diff --git a/services/eks/clusters_test.go b/services/eks/clusters_test.go
index 7d5addd1d3..8a7d959f39 100644
--- a/services/eks/clusters_test.go
+++ b/services/eks/clusters_test.go
@@ -1,7 +1,7 @@
package eks_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eks"
)
@@ -231,7 +232,7 @@ func TestVpcConfig_HandlerJSON_Fields(t *testing.T) {
rec := doREST(t, h, http.MethodGet, "/clusters/c1", nil)
body := rec.Body.Bytes()
var raw map[string]any
- require.NoError(t, json.Unmarshal(body, &raw))
+ require.NoError(t, json.Unmarshal(body, &raw, jsonx.V1()))
cluster := raw["cluster"].(map[string]any)
vpc := cluster["resourcesVpcConfig"].(map[string]any)
@@ -748,11 +749,11 @@ func TestSortedListClusters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
resp := parseResp(t, rec)
- raw, err := json.Marshal(resp["clusters"])
+ raw, err := json.Marshal(resp["clusters"], jsonx.V1())
require.NoError(t, err)
var names []string
- require.NoError(t, json.Unmarshal(raw, &names))
+ require.NoError(t, json.Unmarshal(raw, &names, jsonx.V1()))
require.Equal(t, []string{"aaa", "bbb", "mmm", "zzz"}, names)
}
diff --git a/services/eks/handler.go b/services/eks/handler.go
index ab446700ec..29d52702a1 100644
--- a/services/eks/handler.go
+++ b/services/eks/handler.go
@@ -1,7 +1,8 @@
package eks
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
@@ -10,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -539,9 +541,9 @@ func (h *Handler) Handler() echo.HandlerFunc {
var body []byte
if c.Request().Body != nil {
- decoder := json.NewDecoder(c.Request().Body)
- var raw json.RawMessage
- if err := decoder.Decode(&raw); err == nil {
+ decoder := jsontext.NewDecoder(c.Request().Body)
+ var raw jsontext.Value
+ if err := json.UnmarshalDecode(decoder, &raw, jsonx.V1()); err == nil {
body = raw
}
}
diff --git a/services/eks/handler_access_entries.go b/services/eks/handler_access_entries.go
index 23fc3bb7bc..f4abf78bac 100644
--- a/services/eks/handler_access_entries.go
+++ b/services/eks/handler_access_entries.go
@@ -1,13 +1,14 @@
package eks
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
"strings"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -146,7 +147,7 @@ type createAccessEntryBody struct {
func (h *Handler) handleCreateAccessEntry(c *echo.Context, clusterName string, body []byte) error {
var in createAccessEntryBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", "invalid request body"))
}
@@ -224,7 +225,7 @@ type updateAccessEntryBody struct {
func (h *Handler) handleUpdateAccessEntry(c *echo.Context, clusterName, principalARN string, body []byte) error {
var in updateAccessEntryBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", err.Error()))
}
}
@@ -248,7 +249,7 @@ type associateAccessPolicyBody struct {
func (h *Handler) handleAssociateAccessPolicy(c *echo.Context, clusterName, principalARN string, body []byte) error {
var in associateAccessPolicyBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", "invalid request body"))
}
diff --git a/services/eks/handler_addons.go b/services/eks/handler_addons.go
index f815cb8cf0..bbc628d2a6 100644
--- a/services/eks/handler_addons.go
+++ b/services/eks/handler_addons.go
@@ -1,7 +1,7 @@
package eks
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"slices"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -147,7 +148,7 @@ type createAddonBody struct {
func (h *Handler) handleCreateAddon(c *echo.Context, clusterName string, body []byte) error {
var in createAddonBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", "invalid request body"))
}
@@ -234,7 +235,7 @@ type updateAddonBody struct {
func (h *Handler) handleUpdateAddon(c *echo.Context, clusterName, addonName string, body []byte) error {
var in updateAddonBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", err.Error()))
}
}
diff --git a/services/eks/handler_capabilities.go b/services/eks/handler_capabilities.go
index 23507d9aeb..ccf684ff88 100644
--- a/services/eks/handler_capabilities.go
+++ b/services/eks/handler_capabilities.go
@@ -1,11 +1,12 @@
package eks
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -147,7 +148,7 @@ type createCapabilityBody struct {
func (h *Handler) handleCreateCapability(c *echo.Context, clusterName string, body []byte) error {
var in createCapabilityBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", "invalid request body"))
}
@@ -233,7 +234,7 @@ type updateCapabilityBody struct {
func (h *Handler) handleUpdateCapability(c *echo.Context, clusterName, capabilityName string, body []byte) error {
var in updateCapabilityBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", err.Error()))
}
}
diff --git a/services/eks/handler_certificate_authorities.go b/services/eks/handler_certificate_authorities.go
index 66e6d3c04e..44081b6c4f 100644
--- a/services/eks/handler_certificate_authorities.go
+++ b/services/eks/handler_certificate_authorities.go
@@ -1,12 +1,13 @@
package eks
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -134,7 +135,7 @@ func certificateAuthorityIdempotencyFingerprintBody(clusterName, id string) []by
payload["certificateAuthorityId"] = id
}
- raw, err := json.Marshal(payload)
+ raw, err := json.Marshal(payload, jsonx.V1())
if err != nil {
return nil
}
@@ -149,7 +150,7 @@ type createCertificateAuthorityBody struct {
func (h *Handler) handleCreateCertificateAuthority(c *echo.Context, clusterName string, body []byte) error {
var in createCertificateAuthorityBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", "invalid request body"))
}
}
@@ -176,7 +177,7 @@ type activateCertificateAuthorityBody struct {
func (h *Handler) handleActivateCertificateAuthority(c *echo.Context, clusterName, id string, body []byte) error {
var in activateCertificateAuthorityBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", "invalid request body"))
}
}
diff --git a/services/eks/handler_clusters.go b/services/eks/handler_clusters.go
index a884273d74..c4cebac5ba 100644
--- a/services/eks/handler_clusters.go
+++ b/services/eks/handler_clusters.go
@@ -1,7 +1,7 @@
package eks
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"slices"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -302,7 +303,7 @@ type createClusterBody struct {
func (h *Handler) handleCreateCluster(c *echo.Context, body []byte) error {
var in createClusterBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", "invalid request body"))
}
@@ -349,7 +350,7 @@ func (h *Handler) handleCreateCluster(c *echo.Context, body []byte) error {
netCfg = withNetworkDefaults(netCfg)
var blocksIn clusterConfigBlocksBody
- _ = json.Unmarshal(body, &blocksIn)
+ _ = json.Unmarshal(body, &blocksIn, jsonx.V1())
opt := buildClusterOptConfig(in)
opt.CreatorARN = awsmeta.CallerArn(c.Request().Context())
@@ -476,7 +477,7 @@ type registerClusterBody struct {
func (h *Handler) handleRegisterCluster(c *echo.Context, body []byte) error {
var in registerClusterBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", "invalid request body"))
}
diff --git a/services/eks/handler_fargate_profiles.go b/services/eks/handler_fargate_profiles.go
index 0b9c3d4708..da2ad862ad 100644
--- a/services/eks/handler_fargate_profiles.go
+++ b/services/eks/handler_fargate_profiles.go
@@ -1,11 +1,12 @@
package eks
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -102,7 +103,7 @@ type createFargateProfileBody struct {
func (h *Handler) handleCreateFargateProfile(c *echo.Context, clusterName string, body []byte) error {
var in createFargateProfileBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", "invalid request body"))
}
diff --git a/services/eks/handler_identity_providers.go b/services/eks/handler_identity_providers.go
index 71ee2752c3..377dc17877 100644
--- a/services/eks/handler_identity_providers.go
+++ b/services/eks/handler_identity_providers.go
@@ -1,12 +1,13 @@
package eks
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -84,7 +85,7 @@ type associateIdentityProviderConfigBody struct {
func (h *Handler) handleAssociateIdentityProviderConfig(c *echo.Context, clusterName string, body []byte) error {
var in associateIdentityProviderConfigBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", "invalid request body"))
}
@@ -163,7 +164,7 @@ type describeIDPRequestBody struct {
func (h *Handler) handleDescribeIdentityProviderConfig(c *echo.Context, clusterName string, body []byte) error {
var in describeIDPRequestBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", err.Error()))
}
}
@@ -233,7 +234,7 @@ type disassociateIDPBody struct {
func (h *Handler) handleDisassociateIdentityProviderConfig(c *echo.Context, clusterName string, body []byte) error {
var in disassociateIDPBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", err.Error()))
}
}
diff --git a/services/eks/handler_insights.go b/services/eks/handler_insights.go
index c1dfe454d2..470bfa5b9b 100644
--- a/services/eks/handler_insights.go
+++ b/services/eks/handler_insights.go
@@ -1,12 +1,13 @@
package eks
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -102,7 +103,7 @@ func (h *Handler) handleListInsights(c *echo.Context, clusterName string, body [
// A malformed body is tolerated (ListInsights' filter/pagination
// fields are all optional) rather than rejected, matching the
// permissive parsing used by every other optional-body op here.
- _ = json.Unmarshal(body, &in)
+ _ = json.Unmarshal(body, &in, jsonx.V1())
}
if in.Filter != nil {
diff --git a/services/eks/handler_irsa.go b/services/eks/handler_irsa.go
index af9ddb616a..7674d1accd 100644
--- a/services/eks/handler_irsa.go
+++ b/services/eks/handler_irsa.go
@@ -1,7 +1,8 @@
package eks
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"time"
@@ -9,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -111,7 +113,7 @@ func (h *Handler) serveMintToken(c *echo.Context, cluster string) error {
var in serviceAccountTokenBody
if c.Request().Body != nil {
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return h.handleError(c, errMsgf(ErrValidation, "invalid request body"))
}
}
diff --git a/services/eks/handler_node_groups.go b/services/eks/handler_node_groups.go
index 70ce993eab..fe936ddef5 100644
--- a/services/eks/handler_node_groups.go
+++ b/services/eks/handler_node_groups.go
@@ -1,7 +1,7 @@
package eks
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -294,7 +295,7 @@ type createNodegroupBody struct {
func (h *Handler) handleCreateNodegroup(c *echo.Context, clusterName string, body []byte) error {
var in createNodegroupBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", "invalid request body"))
}
@@ -448,7 +449,7 @@ func (h *Handler) handleUpdateNodegroupConfig(
) error {
var in updateNodegroupConfigInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", err.Error()))
}
}
@@ -524,7 +525,7 @@ type updateNodegroupVersionBody struct {
func (h *Handler) handleUpdateNodegroupVersion(c *echo.Context, clusterName, nodegroupName string, body []byte) error {
var in updateNodegroupVersionBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", err.Error()))
}
}
diff --git a/services/eks/handler_pod_identity.go b/services/eks/handler_pod_identity.go
index 604ea36023..6b4e800545 100644
--- a/services/eks/handler_pod_identity.go
+++ b/services/eks/handler_pod_identity.go
@@ -1,12 +1,13 @@
package eks
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -138,7 +139,7 @@ type createPodIdentityAssociationBody struct {
func (h *Handler) handleCreatePodIdentityAssociation(c *echo.Context, clusterName string, body []byte) error {
var in createPodIdentityAssociationBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", "invalid request body"))
}
@@ -232,7 +233,7 @@ type updatePodIdentityBody struct {
func (h *Handler) handleUpdatePodIdentityAssociation(c *echo.Context, clusterName, assocID string, body []byte) error {
var in updatePodIdentityBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", err.Error()))
}
}
diff --git a/services/eks/handler_subscriptions.go b/services/eks/handler_subscriptions.go
index b3144c682b..b8b787eb32 100644
--- a/services/eks/handler_subscriptions.go
+++ b/services/eks/handler_subscriptions.go
@@ -1,7 +1,7 @@
package eks
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"slices"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -144,7 +145,7 @@ func validateSubscriptionTerm(t *subscriptionTermJSON) error {
func (h *Handler) handleCreateEksAnywhereSubscription(c *echo.Context, body []byte) error {
var in createEksAnywhereSubscriptionBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", "invalid request body"))
}
@@ -227,7 +228,7 @@ type updateSubscriptionBody struct {
func (h *Handler) handleUpdateEksAnywhereSubscription(c *echo.Context, id string, body []byte) error {
var in updateSubscriptionBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", err.Error()))
}
}
diff --git a/services/eks/handler_tags.go b/services/eks/handler_tags.go
index f86e83b335..99e01cdb49 100644
--- a/services/eks/handler_tags.go
+++ b/services/eks/handler_tags.go
@@ -1,11 +1,13 @@
package eks
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// dispatchTagOps handles the generic ARN-based tag operations.
@@ -62,7 +64,7 @@ type tagResourceBody struct {
func (h *Handler) handleTagResource(c *echo.Context, resourceARN string, body []byte) error {
var in tagResourceBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("BadRequestException", "invalid request body"))
}
diff --git a/services/eks/handler_test.go b/services/eks/handler_test.go
index ac3549e183..f8ef960e42 100644
--- a/services/eks/handler_test.go
+++ b/services/eks/handler_test.go
@@ -2,7 +2,7 @@ package eks_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eks"
)
@@ -50,7 +51,7 @@ func doREST(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -70,7 +71,7 @@ func parseResp(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
diff --git a/services/eks/handler_updates.go b/services/eks/handler_updates.go
index f3d3e2cb13..54042d33be 100644
--- a/services/eks/handler_updates.go
+++ b/services/eks/handler_updates.go
@@ -1,13 +1,14 @@
package eks
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
"strings"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -84,7 +85,7 @@ type associateEncryptionConfigBody struct {
func (h *Handler) handleAssociateEncryptionConfig(c *echo.Context, clusterName string, body []byte) error {
var in associateEncryptionConfigBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", "invalid request body"))
}
@@ -149,7 +150,7 @@ func (h *Handler) handleUpdateClusterConfig(c *echo.Context, clusterName string,
var in updateClusterConfigBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", err.Error()))
}
}
@@ -157,7 +158,7 @@ func (h *Handler) handleUpdateClusterConfig(c *echo.Context, clusterName string,
cfgUpd := buildClusterConfigUpdate(in)
var blocksIn clusterConfigBlocksBody
- _ = json.Unmarshal(body, &blocksIn)
+ _ = json.Unmarshal(body, &blocksIn, jsonx.V1())
cfgUpd.ConfigBlocks = blocksIn.blocks(false)
return h.withIdempotency(c, opUpdateClusterConfig, in.ClientRequestToken, body, func() (int, any, error) {
@@ -270,7 +271,7 @@ type updateClusterVersionBody struct {
func (h *Handler) handleUpdateClusterVersion(c *echo.Context, clusterName string, body []byte) error {
var in updateClusterVersionBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", err.Error()))
}
}
@@ -369,7 +370,7 @@ type cancelUpdateBody struct {
func (h *Handler) handleCancelUpdate(c *echo.Context, clusterName, updateID string, body []byte) error {
var in cancelUpdateBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("InvalidParameterException", err.Error()))
}
}
diff --git a/services/eks/idempotency.go b/services/eks/idempotency.go
index e59e5f7569..50645f93a5 100644
--- a/services/eks/idempotency.go
+++ b/services/eks/idempotency.go
@@ -3,12 +3,14 @@ package eks
import (
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"time"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/store"
)
@@ -39,12 +41,12 @@ const idempotencyTokenTTL = 24 * time.Hour
const maxIdempotencyRecords = 1024
type idempotencyRecord struct {
- CreatedAt time.Time `json:"createdAt"`
- Op string `json:"op"`
- Token string `json:"token"`
- Fingerprint string `json:"fingerprint"`
- Body json.RawMessage `json:"body"`
- StatusCode int `json:"statusCode"`
+ CreatedAt time.Time `json:"createdAt"`
+ Op string `json:"op"`
+ Token string `json:"token"`
+ Fingerprint string `json:"fingerprint"`
+ Body jsontext.Value `json:"body"`
+ StatusCode int `json:"statusCode"`
}
func idempotencyKey(op, token string) string { return op + "\x00" + token }
@@ -59,7 +61,7 @@ func idempotencyKeyFn(v *idempotencyRecord) string { return idempotencyKey(v.Op,
// byte-identically.
func idempotencyFingerprint(reqBody []byte) string {
var v map[string]any
- if err := json.Unmarshal(reqBody, &v); err != nil {
+ if err := json.Unmarshal(reqBody, &v, jsonx.V1()); err != nil {
sum := sha256.Sum256(reqBody)
return hex.EncodeToString(sum[:])
@@ -67,7 +69,7 @@ func idempotencyFingerprint(reqBody []byte) string {
delete(v, "clientRequestToken")
- canon, err := json.Marshal(v)
+ canon, err := json.Marshal(v, jsonx.V1())
if err != nil {
canon = reqBody
}
@@ -100,7 +102,7 @@ func idempotencyExpired(rec *idempotencyRecord, now time.Time) bool {
}
// storeIdempotency records a successful (2xx) response for later replay.
-func (b *InMemoryBackend) storeIdempotency(op, token, fingerprint string, statusCode int, body json.RawMessage) {
+func (b *InMemoryBackend) storeIdempotency(op, token, fingerprint string, statusCode int, body jsontext.Value) {
b.mu.Lock("storeIdempotency")
defer b.mu.Unlock()
@@ -158,7 +160,7 @@ func (h *Handler) withIdempotency(
}
var body any
- if err := json.Unmarshal(rec.Body, &body); err != nil {
+ if err := json.Unmarshal(rec.Body, &body, jsonx.V1()); err != nil {
return c.JSON(http.StatusInternalServerError, errResp("InternalFailure", err.Error()))
}
@@ -171,7 +173,7 @@ func (h *Handler) withIdempotency(
}
if status >= http.StatusOK && status < http.StatusMultipleChoices {
- if raw, mErr := json.Marshal(body); mErr == nil {
+ if raw, mErr := json.Marshal(body, jsonx.V1()); mErr == nil {
h.Backend.storeIdempotency(op, token, fp, status, raw)
}
}
diff --git a/services/eks/irsa.go b/services/eks/irsa.go
index fada781d57..dd74360fac 100644
--- a/services/eks/irsa.go
+++ b/services/eks/irsa.go
@@ -5,7 +5,7 @@ import (
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"math/big"
"slices"
@@ -13,6 +13,8 @@ import (
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -284,12 +286,12 @@ func serviceAccountClaims(issuer string, req ServiceAccountTokenRequest, now, ex
}
func signServiceAccountJWT(key *rsa.PrivateKey, claims map[string]any) (string, error) {
- hdr, err := json.Marshal(map[string]string{"alg": jwtAlgRS256, "kid": irsaKeyID(&key.PublicKey)})
+ hdr, err := json.Marshal(map[string]string{"alg": jwtAlgRS256, "kid": irsaKeyID(&key.PublicKey)}, jsonx.V1())
if err != nil {
return "", err
}
- body, err := json.Marshal(claims)
+ body, err := json.Marshal(claims, jsonx.V1())
if err != nil {
return "", err
}
diff --git a/services/eks/irsa_test.go b/services/eks/irsa_test.go
index ea7040d5c6..84889d4592 100644
--- a/services/eks/irsa_test.go
+++ b/services/eks/irsa_test.go
@@ -5,7 +5,7 @@ import (
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"math/big"
"net/http"
"net/http/httptest"
@@ -18,6 +18,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eks"
)
@@ -34,7 +35,7 @@ func jwtParts(t *testing.T, token string) (map[string]any, map[string]any, []byt
for i, dst := range []*map[string]any{&hdr, &claims} {
raw, err := base64.RawURLEncoding.DecodeString(parts[i])
require.NoError(t, err)
- require.NoError(t, json.Unmarshal(raw, dst))
+ require.NoError(t, json.Unmarshal(raw, dst, jsonx.V1()))
}
sig, err := base64.RawURLEncoding.DecodeString(parts[2])
@@ -301,7 +302,7 @@ func TestOIDCEndpoints(t *testing.T) {
if tt.wantKey != "" {
var doc map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &doc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &doc, jsonx.V1()))
assert.Contains(t, doc, tt.wantKey)
}
})
diff --git a/services/eks/k8s_api.go b/services/eks/k8s_api.go
index df399162f7..e4d6310468 100644
--- a/services/eks/k8s_api.go
+++ b/services/eks/k8s_api.go
@@ -5,13 +5,15 @@ import (
"context"
"crypto/tls"
"crypto/x509"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -111,7 +113,7 @@ func (a *k8sAPI) getJSON(ctx context.Context, path string, out any) error {
return err
}
- if err = json.Unmarshal(raw, out); err != nil {
+ if err = json.Unmarshal(raw, out, jsonx.V1()); err != nil {
return fmt.Errorf("decode %s: %w", path, err)
}
@@ -120,7 +122,7 @@ func (a *k8sAPI) getJSON(ctx context.Context, path string, out any) error {
// apply server-side-applies obj (JSON is valid YAML) at path, forcing field ownership.
func (a *k8sAPI) apply(ctx context.Context, path string, obj any) error {
- raw, err := json.Marshal(obj)
+ raw, err := json.Marshal(obj, jsonx.V1())
if err != nil {
return fmt.Errorf("encode %s: %w", path, err)
}
@@ -132,7 +134,7 @@ func (a *k8sAPI) apply(ctx context.Context, path string, obj any) error {
}
func (a *k8sAPI) mergePatch(ctx context.Context, path string, patch any) error {
- raw, err := json.Marshal(patch)
+ raw, err := json.Marshal(patch, jsonx.V1())
if err != nil {
return fmt.Errorf("encode %s: %w", path, err)
}
diff --git a/services/eks/k8s_proxy.go b/services/eks/k8s_proxy.go
index b767fb5b34..2329ee1de9 100644
--- a/services/eks/k8s_proxy.go
+++ b/services/eks/k8s_proxy.go
@@ -9,7 +9,8 @@ import (
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"encoding/pem"
"errors"
"fmt"
@@ -24,6 +25,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -188,7 +190,7 @@ func (p *k8sProxy) close() { _ = p.srv.Close() }
func writeK8sStatus(w http.ResponseWriter, code int, reason string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
- _ = json.NewEncoder(w).Encode(map[string]any{
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), map[string]any{
keyKind: "Status", keyAPIVersion: "v1", "status": "Failure",
keyMessage: reason, "reason": reason, keyCode: code,
})
diff --git a/services/eks/k8s_reconcile_internal_test.go b/services/eks/k8s_reconcile_internal_test.go
index 965146dc18..58766a901e 100644
--- a/services/eks/k8s_reconcile_internal_test.go
+++ b/services/eks/k8s_reconcile_internal_test.go
@@ -2,7 +2,8 @@ package eks
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const rbacPrefix = "/apis/rbac.authorization.k8s.io/v1/"
@@ -125,7 +127,7 @@ func (f *fakeK8s) listBindings(set map[string]bool, nsMode string) map[string]an
func writeJSON(w http.ResponseWriter, v any) {
w.Header().Set("Content-Type", "application/json")
- _ = json.NewEncoder(w).Encode(v)
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), v)
}
// engineRuntime records containers and registers a node in the fake API for every agent started.
diff --git a/services/eks/models.go b/services/eks/models.go
index 2981183033..50dc3bd263 100644
--- a/services/eks/models.go
+++ b/services/eks/models.go
@@ -1,10 +1,12 @@
package eks
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -148,7 +150,7 @@ func (a activationExpiry) Time() time.Time { return time.Time(a) }
func (a activationExpiry) IsZero() bool { return time.Time(a).IsZero() }
func (a activationExpiry) MarshalJSON() ([]byte, error) {
- return json.Marshal(time.Time(a))
+ return json.Marshal(time.Time(a), jsonx.V1())
}
func (a *activationExpiry) UnmarshalJSON(data []byte) error {
@@ -157,14 +159,14 @@ func (a *activationExpiry) UnmarshalJSON(data []byte) error {
}
var t time.Time
- if err := json.Unmarshal(data, &t); err == nil {
+ if err := json.Unmarshal(data, &t, jsonx.V1()); err == nil {
*a = activationExpiry(t)
return nil
}
var epochSeconds float64
- if err := json.Unmarshal(data, &epochSeconds); err != nil {
+ if err := json.Unmarshal(data, &epochSeconds, jsonx.V1()); err != nil {
return fmt.Errorf("activationExpiry: unsupported JSON value %s: %w", data, err)
}
@@ -201,23 +203,23 @@ type Cluster struct {
StorageConfig *StorageConfig `json:"storageConfig,omitempty"`
ConnectorConfig *ConnectorConfig `json:"connectorConfig,omitempty"`
// ConfigBlocks holds the opaque create/update config members keyed by wire name.
- ConfigBlocks map[string]json.RawMessage `json:"configBlocks,omitempty"`
- ARN string `json:"arn"`
- Name string `json:"name"`
- Endpoint string `json:"endpoint,omitempty"`
- OIDCIssuer string `json:"oidcIssuer,omitempty"`
- Version string `json:"version"`
- Status string `json:"status"`
- RoleARN string `json:"roleArn,omitempty"`
- AccountID string `json:"accountId"`
- Region string `json:"region"`
- PlatformVersion string `json:"platformVersion,omitempty"`
- CreatorARN string `json:"creatorArn,omitempty"`
- CertificateAuthority string `json:"certificateAuthority,omitempty"`
- UpgradePolicySupportType string `json:"upgradePolicySupportType,omitempty"`
- ClusterLogging []ClusterLogEntry `json:"clusterLogging,omitempty"`
- EncryptionConfig []EncryptionConfig `json:"encryptionConfig,omitempty"`
- DeletionProtection bool `json:"deletionProtection,omitempty"`
+ ConfigBlocks map[string]jsontext.Value `json:"configBlocks,omitempty"`
+ ARN string `json:"arn"`
+ Name string `json:"name"`
+ Endpoint string `json:"endpoint,omitempty"`
+ OIDCIssuer string `json:"oidcIssuer,omitempty"`
+ Version string `json:"version"`
+ Status string `json:"status"`
+ RoleARN string `json:"roleArn,omitempty"`
+ AccountID string `json:"accountId"`
+ Region string `json:"region"`
+ PlatformVersion string `json:"platformVersion,omitempty"`
+ CreatorARN string `json:"creatorArn,omitempty"`
+ CertificateAuthority string `json:"certificateAuthority,omitempty"`
+ UpgradePolicySupportType string `json:"upgradePolicySupportType,omitempty"`
+ ClusterLogging []ClusterLogEntry `json:"clusterLogging,omitempty"`
+ EncryptionConfig []EncryptionConfig `json:"encryptionConfig,omitempty"`
+ DeletionProtection bool `json:"deletionProtection,omitempty"`
}
// NodegroupTaint represents a Kubernetes taint applied to managed nodes.
diff --git a/services/eks/multi_region_test.go b/services/eks/multi_region_test.go
index b8aabb515d..a532b948c6 100644
--- a/services/eks/multi_region_test.go
+++ b/services/eks/multi_region_test.go
@@ -1,7 +1,8 @@
package eks_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eks"
)
@@ -41,7 +43,7 @@ func regionCall(t *testing.T, h *eks.Handler, region, method, path, body string)
out := map[string]any{}
if rec.Body.Len() > 0 {
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
}
return out
@@ -130,9 +132,9 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(t.Context())
- var doc map[string]json.RawMessage
+ var doc map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(snap, &doc))
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
_, hasRegions := doc["regions"]
assert.Equal(t, tc.wantRegions, hasRegions)
diff --git a/services/eks/node_groups_test.go b/services/eks/node_groups_test.go
index 20e9fa9097..70889e0772 100644
--- a/services/eks/node_groups_test.go
+++ b/services/eks/node_groups_test.go
@@ -1,7 +1,7 @@
package eks_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eks"
)
@@ -975,11 +976,11 @@ func TestSortedListNodegroups(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
resp := parseResp(t, rec)
- raw, err := json.Marshal(resp["nodegroups"])
+ raw, err := json.Marshal(resp["nodegroups"], jsonx.V1())
require.NoError(t, err)
var names []string
- require.NoError(t, json.Unmarshal(raw, &names))
+ require.NoError(t, json.Unmarshal(raw, &names, jsonx.V1()))
require.Equal(t, []string{"aaa", "mmm", "zzz"}, names)
}
diff --git a/services/eks/persistence.go b/services/eks/persistence.go
index 8a87bdb81d..1ca5a7c70a 100644
--- a/services/eks/persistence.go
+++ b/services/eks/persistence.go
@@ -2,9 +2,11 @@ package eks
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
@@ -50,7 +52,7 @@ import (
const eksSnapshotVersion = 2
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
AccessPolicies map[string]map[string][]*AccessPolicyAssociation `json:"accessPolicies,omitempty"`
EncryptionConfigs map[string][]EncryptionConfig `json:"encryptionConfigs,omitempty"`
AccountID string `json:"accountId"`
@@ -79,7 +81,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.region,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "eks: Snapshot marshal failed", "error", err)
diff --git a/services/eks/pod_identity_auth.go b/services/eks/pod_identity_auth.go
index 1941656df9..62c83aed9b 100644
--- a/services/eks/pod_identity_auth.go
+++ b/services/eks/pod_identity_auth.go
@@ -5,7 +5,7 @@ import (
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"regexp"
"slices"
@@ -16,6 +16,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -124,7 +125,7 @@ type audience []string
func (a *audience) UnmarshalJSON(raw []byte) error {
var one string
- if json.Unmarshal(raw, &one) == nil {
+ if json.Unmarshal(raw, &one, jsonx.V1()) == nil {
*a = audience{one}
return nil
@@ -132,7 +133,7 @@ func (a *audience) UnmarshalJSON(raw []byte) error {
var many []string
- if err := json.Unmarshal(raw, &many); err != nil {
+ if err := json.Unmarshal(raw, &many, jsonx.V1()); err != nil {
return err
}
@@ -163,7 +164,7 @@ func verifyPodIdentityToken(token, issuer string, pub *rsa.PublicKey, now time.T
}
var c podIdentityClaims
- if json.Unmarshal(raw, &c) != nil {
+ if json.Unmarshal(raw, &c, jsonx.V1()) != nil {
return nil, errMsgf(ErrInvalidToken, "The token payload is malformed.")
}
@@ -309,7 +310,7 @@ func (h *Handler) handleAssumeRoleForPodIdentity(c *echo.Context, clusterName st
Token string `json:"token"`
}
- if err := json.Unmarshal(body, &in); err != nil || in.Token == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Token == "" {
return h.handleError(c, errMsgf(ErrValidation, "token is required."))
}
diff --git a/services/eks/updates.go b/services/eks/updates.go
index cf447837bf..45cee74ef9 100644
--- a/services/eks/updates.go
+++ b/services/eks/updates.go
@@ -1,7 +1,8 @@
package eks
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"slices"
"strconv"
@@ -10,6 +11,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/collections"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// updateTransitionDelay is the async delay before an InProgress Update reaches
@@ -81,7 +83,7 @@ func (b *InMemoryBackend) AssociateEncryptionConfig(
result := make([]EncryptionConfig, len(stored))
copy(result, stored)
- encryptionConfigJSON, err := json.Marshal(result)
+ encryptionConfigJSON, err := json.Marshal(result, jsonx.V1())
if err != nil {
return nil, nil, fmt.Errorf("marshal encryption config: %w", err)
}
@@ -111,7 +113,7 @@ type ClusterConfigUpdate struct {
ComputeConfig *ComputeConfig
StorageConfig *StorageConfig
// ConfigBlocks are the opaque config members, keyed by wire name.
- ConfigBlocks map[string]json.RawMessage
+ ConfigBlocks map[string]jsontext.Value
DeletionProtection *bool
ElasticLoadBalancing *bool
UpgradeSupportType string
diff --git a/services/eks/updates_test.go b/services/eks/updates_test.go
index e8719f8c76..e482ca5e55 100644
--- a/services/eks/updates_test.go
+++ b/services/eks/updates_test.go
@@ -1,7 +1,7 @@
package eks_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eks"
)
@@ -624,7 +625,7 @@ func TestLogging_HTTP_AllTypes_In_Response(t *testing.T) {
rec2 := doREST(t, h, http.MethodGet, "/clusters/logging-http-all", nil)
cluster := parseClusterResp(t, rec2)
- raw, _ := json.Marshal(cluster)
+ raw, _ := json.Marshal(cluster, jsonx.V1())
body := string(raw)
for _, lt := range []string{"api", "audit", "authenticator", "controllerManager", "scheduler"} {
diff --git a/services/elasticache/persistence.go b/services/elasticache/persistence.go
index 4da5251368..14ff8c6d85 100644
--- a/services/elasticache/persistence.go
+++ b/services/elasticache/persistence.go
@@ -2,7 +2,7 @@ package elasticache
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -66,7 +66,7 @@ type clusterSnapshot struct {
// directly below as region -> that table's own deterministic
// [store.Table.Snapshot] slice.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Clusters map[string][]*clusterSnapshot `json:"clusters"`
ReplicationGroups map[string][]*ReplicationGroup `json:"replicationGroups"`
ParameterGroups map[string][]*CacheParameterGroup `json:"parameterGroups"`
diff --git a/services/elasticache/persistence_test.go b/services/elasticache/persistence_test.go
index 9547503578..5b3ba830b9 100644
--- a/services/elasticache/persistence_test.go
+++ b/services/elasticache/persistence_test.go
@@ -2,12 +2,13 @@ package elasticache_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/elasticache"
)
@@ -205,10 +206,10 @@ func TestInMemoryBackend_Restore_IncompatibleVersion(t *testing.T) {
// current elasticacheSnapshotVersion constant.
var raw map[string]any
- require.NoError(t, json.Unmarshal(original.Snapshot(ctx), &raw))
+ require.NoError(t, json.Unmarshal(original.Snapshot(ctx), &raw, jsonx.V1()))
raw["version"] = -1
- badSnap, err := json.Marshal(raw)
+ badSnap, err := json.Marshal(raw, jsonx.V1())
require.NoError(t, err)
fresh := elasticache.NewInMemoryBackend("redis", "000000000000", "us-east-1", nil)
diff --git a/services/elasticbeanstalk/persistence.go b/services/elasticbeanstalk/persistence.go
index 1cf141771a..bcd449b678 100644
--- a/services/elasticbeanstalk/persistence.go
+++ b/services/elasticbeanstalk/persistence.go
@@ -2,7 +2,7 @@ package elasticbeanstalk
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -53,7 +53,7 @@ func regionalDTOKeyFn[V any](d *regionalDTO[V]) string { return regionKey(d.Regi
// snapshot from an incompatible (older or newer) build of this backend as
// though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ManagedActionHistory map[string]map[string][]*ManagedActionHistory `json:"managedActionHistory,omitempty"`
Events map[string][]*EventRecord `json:"events,omitempty"`
DeletedEnvironments map[string][]*Environment `json:"deletedEnvironments,omitempty"`
@@ -243,7 +243,7 @@ func unwrapRegionalDTOs[V any](dtos *store.Table[regionalDTO[V]], setRegion func
// restoreResourceTables rebuilds every store.Table on b from snap's
// per-table JSON, factored out of Restore to keep Restore's own cognitive
// complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
dtos := buildPersistenceDTORegistry()
if err := dtos.registry.RestoreAll(tables); err != nil {
diff --git a/services/elasticbeanstalk/store_test.go b/services/elasticbeanstalk/store_test.go
index 1ac070a850..308a14265a 100644
--- a/services/elasticbeanstalk/store_test.go
+++ b/services/elasticbeanstalk/store_test.go
@@ -2,12 +2,13 @@ package elasticbeanstalk_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/elasticbeanstalk"
)
@@ -65,7 +66,7 @@ func TestInMemoryBackend_Snapshot_NotNilOnFreshBackend(t *testing.T) {
// Must be valid JSON.
var m map[string]any
- require.NoError(t, json.Unmarshal(snap, &m))
+ require.NoError(t, json.Unmarshal(snap, &m, jsonx.V1()))
}
// TestInMemoryBackend_SeedHelpers_PopulateAllCollections verifies that seed
diff --git a/services/elasticsearch/handler.go b/services/elasticsearch/handler.go
index 6ec0d16def..d630ab1d5f 100644
--- a/services/elasticsearch/handler.go
+++ b/services/elasticsearch/handler.go
@@ -2,7 +2,7 @@ package elasticsearch
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -537,7 +538,7 @@ func (h *Handler) decodeRequest(w http.ResponseWriter, r *http.Request, out any)
return true
}
- if err = json.Unmarshal(body, out); err != nil {
+ if err = json.Unmarshal(body, out, jsonx.V1()); err != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return false
diff --git a/services/elasticsearch/handler_domain_advanced_options_test.go b/services/elasticsearch/handler_domain_advanced_options_test.go
index d98ff37c6a..0e8ec6b040 100644
--- a/services/elasticsearch/handler_domain_advanced_options_test.go
+++ b/services/elasticsearch/handler_domain_advanced_options_test.go
@@ -1,12 +1,14 @@
package elasticsearch_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestElasticsearchHandler_CreateDomain_VPCOptions verifies VPCOptions
@@ -401,7 +403,7 @@ func TestElasticsearchBackend_CreateDomain_InvalidAutoTuneDesiredState(t *testin
body := []byte(`{"DomainName":"raw-autotune","AutoTuneOptions":{"DesiredState":"BOGUS"}}`)
var decoded map[string]any
- require.NoError(t, json.Unmarshal(body, &decoded))
+ require.NoError(t, json.Unmarshal(body, &decoded, jsonx.V1()))
resp := doRequest(t, h, http.MethodPost, "/2015-01-01/es/domain", decoded)
defer resp.Body.Close()
diff --git a/services/elasticsearch/handler_domain_config.go b/services/elasticsearch/handler_domain_config.go
index 4e2859f34d..bf0b83b4e1 100644
--- a/services/elasticsearch/handler_domain_config.go
+++ b/services/elasticsearch/handler_domain_config.go
@@ -1,7 +1,7 @@
package elasticsearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -9,6 +9,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// updateDomainConfigRequest is the request body for UpdateElasticsearchDomainConfig.
@@ -38,7 +39,7 @@ func (h *Handler) handleUpdateDomainConfig(w http.ResponseWriter, r *http.Reques
}
var req updateDomainConfigRequest
- if err = json.Unmarshal(body, &req); err != nil {
+ if err = json.Unmarshal(body, &req, jsonx.V1()); err != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/elasticsearch/handler_domain_fields_test.go b/services/elasticsearch/handler_domain_fields_test.go
index 98d1b233e7..d6240ed841 100644
--- a/services/elasticsearch/handler_domain_fields_test.go
+++ b/services/elasticsearch/handler_domain_fields_test.go
@@ -1,12 +1,15 @@
package elasticsearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestElasticsearchHandler_DomainDedicatedMaster verifies dedicated master
@@ -57,7 +60,7 @@ func TestElasticsearchHandler_DomainDedicatedMaster(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status := out["DomainStatus"].(map[string]any)
cfg := status["ElasticsearchClusterConfig"].(map[string]any)
@@ -116,7 +119,7 @@ func TestElasticsearchHandler_DomainZoneAwareness(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status := out["DomainStatus"].(map[string]any)
cfg := status["ElasticsearchClusterConfig"].(map[string]any)
@@ -178,7 +181,7 @@ func TestElasticsearchHandler_DomainWarm(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status := out["DomainStatus"].(map[string]any)
cfg := status["ElasticsearchClusterConfig"].(map[string]any)
@@ -243,7 +246,7 @@ func TestElasticsearchHandler_DomainEBSIopsAndThroughput(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status := out["DomainStatus"].(map[string]any)
ebs := status["EBSOptions"].(map[string]any)
@@ -291,7 +294,7 @@ func TestElasticsearchHandler_DomainEncryptionAtRest(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status := out["DomainStatus"].(map[string]any)
enc := status["EncryptionAtRestOptions"].(map[string]any)
@@ -338,7 +341,7 @@ func TestElasticsearchHandler_DomainNodeToNodeEncryption(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status := out["DomainStatus"].(map[string]any)
n2n := status["NodeToNodeEncryptionOptions"].(map[string]any)
@@ -393,7 +396,7 @@ func TestElasticsearchHandler_DomainEndpointOptions(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status := out["DomainStatus"].(map[string]any)
endpoint := status["DomainEndpointOptions"].(map[string]any)
@@ -467,7 +470,7 @@ func TestElasticsearchHandler_DomainSnapshotOptions(t *testing.T) {
require.Equal(t, http.StatusOK, descResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(descResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(descResp.Body), &out, jsonx.V1()))
status := out["DomainStatus"].(map[string]any)
snap := status["SnapshotOptions"].(map[string]any)
@@ -536,7 +539,7 @@ func TestElasticsearchHandler_DomainAdvancedOptions(t *testing.T) {
require.Equal(t, http.StatusOK, descResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(descResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(descResp.Body), &out, jsonx.V1()))
status := out["DomainStatus"].(map[string]any)
advOpts := status["AdvancedOptions"].(map[string]any)
@@ -602,7 +605,7 @@ func TestElasticsearchHandler_DomainAccessPolicies(t *testing.T) {
require.Equal(t, http.StatusOK, descResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(descResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(descResp.Body), &out, jsonx.V1()))
status := out["DomainStatus"].(map[string]any)
assert.Equal(t, tc.wantPolicy, status["AccessPolicies"])
@@ -683,7 +686,7 @@ func TestElasticsearchHandler_UpdateDomainNewFields(t *testing.T) {
require.Equal(t, http.StatusOK, descResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(descResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(descResp.Body), &out, jsonx.V1()))
status := out["DomainStatus"].(map[string]any)
tc.checkFn(t, status)
diff --git a/services/elasticsearch/handler_domain_lifecycle.go b/services/elasticsearch/handler_domain_lifecycle.go
index 17d8aeb5f6..736a1ac990 100644
--- a/services/elasticsearch/handler_domain_lifecycle.go
+++ b/services/elasticsearch/handler_domain_lifecycle.go
@@ -1,13 +1,14 @@
package elasticsearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strconv"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -46,7 +47,7 @@ func (h *Handler) handleCancelElasticsearchServiceSoftwareUpdate(w http.Response
}
var req cancelSoftwareUpdateRequest
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/elasticsearch/handler_domains.go b/services/elasticsearch/handler_domains.go
index b012dfc60e..714a96014a 100644
--- a/services/elasticsearch/handler_domains.go
+++ b/services/elasticsearch/handler_domains.go
@@ -1,13 +1,14 @@
package elasticsearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// domainZoneAwarenessConfig holds zone awareness sub-config.
@@ -333,7 +334,7 @@ func (h *Handler) handleCreateDomain(w http.ResponseWriter, r *http.Request) {
}
var req domainJSON
- if err = json.Unmarshal(body, &req); err != nil {
+ if err = json.Unmarshal(body, &req, jsonx.V1()); err != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
@@ -553,7 +554,7 @@ func (h *Handler) handleDescribeElasticsearchDomains(w http.ResponseWriter, r *h
}
var req describeDomainsRequest
- if err = json.Unmarshal(body, &req); err != nil {
+ if err = json.Unmarshal(body, &req, jsonx.V1()); err != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/elasticsearch/handler_domains_test.go b/services/elasticsearch/handler_domains_test.go
index 6db1b1bc79..e2315c946f 100644
--- a/services/elasticsearch/handler_domains_test.go
+++ b/services/elasticsearch/handler_domains_test.go
@@ -2,7 +2,8 @@ package elasticsearch_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/elasticsearch"
)
@@ -237,7 +239,7 @@ func TestElasticsearchHandler_ListDomainNames(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
names, ok := out["DomainNames"].([]any)
require.True(t, ok)
@@ -363,7 +365,7 @@ func TestElasticsearchHandler_DomainCollectionRouteMatrix(t *testing.T) {
var out map[string]any
if resp.Header.Get("Content-Type") != "" {
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
}
resp.Body.Close()
@@ -442,7 +444,7 @@ func TestElasticsearchHandler_DescribeElasticsearchDomains(t *testing.T) {
assert.Equal(t, tt.wantCode, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
list, ok := out["DomainStatusList"].([]any)
require.True(t, ok)
@@ -611,7 +613,7 @@ func TestElasticsearchHandler_NonNilEmptyDomainList(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
domainNames, ok := out["DomainNames"]
require.True(t, ok)
diff --git a/services/elasticsearch/handler_outbound_connections.go b/services/elasticsearch/handler_outbound_connections.go
index 8b9e6235d4..e24913f14b 100644
--- a/services/elasticsearch/handler_outbound_connections.go
+++ b/services/elasticsearch/handler_outbound_connections.go
@@ -1,11 +1,12 @@
package elasticsearch
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// outboundConnectionJSON is the JSON representation of an outbound cross-cluster connection.
@@ -37,7 +38,7 @@ func (h *Handler) handleCreateOutboundCrossClusterSearchConnection(w http.Respon
}
var req createOutboundConnectionRequest
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/elasticsearch/handler_packages.go b/services/elasticsearch/handler_packages.go
index eec513afcc..7ae9a6ba13 100644
--- a/services/elasticsearch/handler_packages.go
+++ b/services/elasticsearch/handler_packages.go
@@ -1,7 +1,7 @@
package elasticsearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"slices"
@@ -10,6 +10,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -68,7 +69,7 @@ func (h *Handler) handleCreatePackage(w http.ResponseWriter, r *http.Request) {
}
var req createPackageRequest
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/elasticsearch/handler_tags.go b/services/elasticsearch/handler_tags.go
index 37b723561e..c43a80f549 100644
--- a/services/elasticsearch/handler_tags.go
+++ b/services/elasticsearch/handler_tags.go
@@ -1,7 +1,7 @@
package elasticsearch
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"net/http"
@@ -9,6 +9,7 @@ import (
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -52,7 +53,7 @@ func (h *Handler) handleAddTags(w http.ResponseWriter, r *http.Request) {
}
var req addTagsInput
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
@@ -130,7 +131,7 @@ func (h *Handler) handleRemoveTags(w http.ResponseWriter, r *http.Request) {
}
var req removeTagsInput
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/elasticsearch/handler_tags_test.go b/services/elasticsearch/handler_tags_test.go
index 8e07d4b789..52f7492e5d 100644
--- a/services/elasticsearch/handler_tags_test.go
+++ b/services/elasticsearch/handler_tags_test.go
@@ -1,7 +1,8 @@
package elasticsearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -10,6 +11,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestElasticsearchHandler_Tags(t *testing.T) {
@@ -63,7 +66,7 @@ func TestElasticsearchHandler_Tags(t *testing.T) {
assert.Equal(t, http.StatusOK, listResp.StatusCode)
var listOut map[string]any
- require.NoError(t, json.NewDecoder(listResp.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listResp.Body), &listOut, jsonx.V1()))
tagList, ok := listOut["TagList"].([]any)
require.True(t, ok)
@@ -300,7 +303,7 @@ func TestElasticsearchHandler_SortedListTagsResponse(t *testing.T) {
Value string `json:"Value"`
} `json:"TagList"`
}
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
require.Len(t, out.TagList, 3)
assert.Equal(t, "aaa", out.TagList[0].Key)
assert.Equal(t, "mmm", out.TagList[1].Key)
@@ -365,7 +368,7 @@ func TestElasticsearchHandler_AddTags_DuplicateKeyRejected(t *testing.T) {
assert.Equal(t, tt.wantCode, resp.StatusCode)
if tt.wantCode == http.StatusBadRequest {
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
assert.Contains(t, out["message"], "Duplicate tag key")
}
})
@@ -421,7 +424,7 @@ func TestElasticsearchHandler_AddRemoveTags_UnknownARN(t *testing.T) {
require.Equal(t, http.StatusBadRequest, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
assert.Contains(t, out["message"], "domain not found")
})
}
diff --git a/services/elasticsearch/handler_test.go b/services/elasticsearch/handler_test.go
index b0d34f98b6..30f114b4dc 100644
--- a/services/elasticsearch/handler_test.go
+++ b/services/elasticsearch/handler_test.go
@@ -2,7 +2,8 @@ package elasticsearch_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/elasticsearch"
)
@@ -27,7 +29,7 @@ func doRequest(t *testing.T, h *elasticsearch.Handler, method, path string, body
var reqBody io.Reader
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
reqBody = bytes.NewReader(b)
@@ -53,7 +55,7 @@ func createDomainAndGetARN(t *testing.T, h *elasticsearch.Handler, domainName st
})
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createResp.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createResp.Body), &createOut, jsonx.V1()))
createResp.Body.Close()
status := createOut["DomainStatus"].(map[string]any)
@@ -78,7 +80,7 @@ func readJSONBody(t *testing.T, resp *http.Response) map[string]any {
require.NoError(t, err)
var m map[string]any
- require.NoError(t, json.Unmarshal(b, &m))
+ require.NoError(t, json.Unmarshal(b, &m, jsonx.V1()))
return m
}
diff --git a/services/elasticsearch/handler_versions_test.go b/services/elasticsearch/handler_versions_test.go
index f5f8099c4b..4789518b77 100644
--- a/services/elasticsearch/handler_versions_test.go
+++ b/services/elasticsearch/handler_versions_test.go
@@ -1,12 +1,15 @@
package elasticsearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestElasticsearchHandler_ListElasticsearchVersions verifies all 22 valid
@@ -33,7 +36,7 @@ func TestElasticsearchHandler_ListElasticsearchVersions(t *testing.T) {
var out struct {
ElasticsearchVersions []string `json:"ElasticsearchVersions"`
}
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
require.Len(t, out.ElasticsearchVersions, 22, "expected all 22 versions")
versionSet := make(map[string]bool)
@@ -100,7 +103,7 @@ func TestElasticsearchHandler_CompatibleVersionsDomain(t *testing.T) {
TargetVersions []string `json:"TargetVersions"`
} `json:"CompatibleElasticsearchVersions"`
}
- require.NoError(t, json.NewDecoder(compatResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(compatResp.Body), &out, jsonx.V1()))
require.Len(t, out.CompatibleElasticsearchVersions, 1)
assert.Equal(t, tc.wantSourceVersion, out.CompatibleElasticsearchVersions[0].SourceVersion)
})
diff --git a/services/elasticsearch/handler_vpc_endpoints.go b/services/elasticsearch/handler_vpc_endpoints.go
index a73f7289f8..f7cfcd6f16 100644
--- a/services/elasticsearch/handler_vpc_endpoints.go
+++ b/services/elasticsearch/handler_vpc_endpoints.go
@@ -1,12 +1,13 @@
package elasticsearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// keyNextToken is the required (always-empty, single-page emulator) pagination
@@ -50,7 +51,7 @@ func (h *Handler) handleCreateVpcEndpoint(w http.ResponseWriter, r *http.Request
}
var req createVpcEndpointRequest
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
@@ -144,7 +145,7 @@ func (h *Handler) handleAuthorizeVpcEndpointAccess(w http.ResponseWriter, r *htt
}
var req authorizeVpcEndpointAccessRequest
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/elasticsearch/persistence.go b/services/elasticsearch/persistence.go
index 0a21fa91b7..cb5ebf8b28 100644
--- a/services/elasticsearch/persistence.go
+++ b/services/elasticsearch/persistence.go
@@ -2,7 +2,7 @@ package elasticsearch
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -96,7 +96,7 @@ func buildPersistenceDTORegistry() persistenceDTOTables {
// snapshot from an incompatible (older or newer) build of this backend as
// though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
PackagesByName map[string]map[string]string `json:"packagesByName"`
PackageAssociations map[string]map[string][]string `json:"packageAssociations"`
PackageAssociationDetails packageAssociationDetails `json:"packageAssociationDetails,omitempty"`
@@ -245,7 +245,7 @@ func (b *InMemoryBackend) rebuildArnIndex() {
// restoreResourceTables rebuilds every store.Table on b from snap's
// per-table JSON, factored out of Restore to keep Restore's own cognitive
// complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
dtos := buildPersistenceDTORegistry()
if err := dtos.registry.RestoreAll(tables); err != nil {
diff --git a/services/elasticsearch/persistence_test.go b/services/elasticsearch/persistence_test.go
index b7d4076c31..a3a150ebff 100644
--- a/services/elasticsearch/persistence_test.go
+++ b/services/elasticsearch/persistence_test.go
@@ -2,12 +2,13 @@ package elasticsearch_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/elasticsearch"
)
@@ -519,7 +520,7 @@ func TestElasticsearch_SnapshotWarnOnEmptyState(t *testing.T) {
require.NotNil(t, snap)
var v map[string]any
- require.NoError(t, json.Unmarshal(snap, &v))
+ require.NoError(t, json.Unmarshal(snap, &v, jsonx.V1()))
}
// TestElasticsearch_PersistenceReservedAndVpcAccessCombined verifies that VPC
diff --git a/services/elb/persistence.go b/services/elb/persistence.go
index 54f08dfcb5..674c6b9053 100644
--- a/services/elb/persistence.go
+++ b/services/elb/persistence.go
@@ -2,9 +2,10 @@ package elb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
@@ -217,7 +218,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Version: snapshotVersion,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "elb: Snapshot marshal failure", "error", err)
diff --git a/services/elb/persistence_test.go b/services/elb/persistence_test.go
index d5c01cc2a2..a8845c1524 100644
--- a/services/elb/persistence_test.go
+++ b/services/elb/persistence_test.go
@@ -2,7 +2,7 @@ package elb_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"encoding/xml"
"net/http"
"net/url"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
"github.com/blackbirdworks/gopherstack/services/elb"
@@ -370,7 +371,7 @@ func TestPersistenceEmptySnapshot(t *testing.T) {
b := newBackend()
snap := b.Snapshot(t.Context())
require.NotNil(t, snap)
- require.True(t, json.Valid(snap))
+ require.True(t, jsontext.Value(snap).IsValid(jsonx.V1()))
}
// TestPersistenceRoundTripBackendServerDescriptions verifies that Snapshot/Restore preserves
diff --git a/services/elbv2/dataplane.go b/services/elbv2/dataplane.go
index b4a32fd47b..0af8c8af4e 100644
--- a/services/elbv2/dataplane.go
+++ b/services/elbv2/dataplane.go
@@ -2,7 +2,8 @@ package elbv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/netip"
@@ -15,6 +16,8 @@ import (
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -300,5 +303,5 @@ func (h *Handler) serveListenerEndpoints(w http.ResponseWriter) {
sort.Slice(out, func(i, j int) bool { return out[i].ListenerArn < out[j].ListenerArn })
w.Header().Set("Content-Type", "application/json")
- _ = json.NewEncoder(w).Encode(out)
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), out)
}
diff --git a/services/elbv2/dataplane_lambda.go b/services/elbv2/dataplane_lambda.go
index c3e6dda871..dc02331ebf 100644
--- a/services/elbv2/dataplane_lambda.go
+++ b/services/elbv2/dataplane_lambda.go
@@ -2,7 +2,7 @@ package elbv2
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"mime"
"net/http"
@@ -11,6 +11,7 @@ import (
"unicode/utf8"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// maxLambdaTargetBodyBytes is the 1 MB request body limit for ALB Lambda targets.
@@ -72,7 +73,7 @@ func (h *Handler) serveLambda(w http.ResponseWriter, req *dpRequest, tg *TargetG
setForwardHeaders(hdr, req)
hdr.Set("Host", req.host)
- payload, err := json.Marshal(buildALBEvent(req, tg, hdr, body))
+ payload, err := json.Marshal(buildALBEvent(req, tg, hdr, body), jsonx.V1())
if err != nil {
writeDPError(w, http.StatusBadGateway, msgBadGateway)
@@ -169,7 +170,7 @@ func isTextContent(ct string) bool {
func writeLambdaResponse(w http.ResponseWriter, payload []byte) {
var resp albResponse
- if err := json.Unmarshal(payload, &resp); err != nil || resp.StatusCode == nil {
+ if err := json.Unmarshal(payload, &resp, jsonx.V1()); err != nil || resp.StatusCode == nil {
writeDPError(w, http.StatusBadGateway, msgBadGateway)
return
diff --git a/services/elbv2/dataplane_lambda_test.go b/services/elbv2/dataplane_lambda_test.go
index 6968c88f16..7f332b5539 100644
--- a/services/elbv2/dataplane_lambda_test.go
+++ b/services/elbv2/dataplane_lambda_test.go
@@ -3,7 +3,7 @@ package elbv2_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"sync"
"testing"
@@ -13,6 +13,8 @@ import (
elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const testFnARN = "arn:aws:lambda:us-east-1:123456789012:function:web"
@@ -43,7 +45,7 @@ func (f *fakeInvoker) event(t *testing.T) map[string]any {
var ev map[string]any
- require.NoError(t, json.Unmarshal(f.payload, &ev))
+ require.NoError(t, json.Unmarshal(f.payload, &ev, jsonx.V1()))
return ev
}
diff --git a/services/elbv2/persistence.go b/services/elbv2/persistence.go
index 62217465f4..0355a96ce4 100644
--- a/services/elbv2/persistence.go
+++ b/services/elbv2/persistence.go
@@ -2,7 +2,7 @@ package elbv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
"time"
@@ -37,7 +37,7 @@ const elbv2SnapshotVersion = 2
// ResourcePolicies / TargetReadyAt / TargetDrainingUntil are persisted
// separately because they are not store.Table-shaped -- see store_setup.go.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ResourcePolicies map[string]string `json:"resourcePolicies"`
TargetReadyAt map[string]map[string]time.Time `json:"targetReadyAt"`
TargetDrainingUntil map[string]map[string]time.Time `json:"targetDrainingUntil"`
diff --git a/services/elbv2/target_groups_test.go b/services/elbv2/target_groups_test.go
index fcfe897c49..7135de0ccc 100644
--- a/services/elbv2/target_groups_test.go
+++ b/services/elbv2/target_groups_test.go
@@ -2,7 +2,7 @@ package elbv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"net/http"
"net/url"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/elbv2"
)
@@ -255,7 +256,7 @@ func TestDeleteTargetGroup_LifecycleMapsCleaned(t *testing.T) {
TargetReadyAt map[string]map[string]time.Time `json:"targetReadyAt"`
TargetDrainingUntil map[string]map[string]time.Time `json:"targetDrainingUntil"`
}
- require.NoError(t, json.Unmarshal(h.Snapshot(context.Background()), &snap))
+ require.NoError(t, json.Unmarshal(h.Snapshot(context.Background()), &snap, jsonx.V1()))
byField := map[string]map[string]map[string]time.Time{
"targetReadyAt": snap.TargetReadyAt,
diff --git a/services/emr/handler.go b/services/emr/handler.go
index f19fb36497..859c94bf65 100644
--- a/services/emr/handler.go
+++ b/services/emr/handler.go
@@ -2,7 +2,7 @@ package emr
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -193,7 +194,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
TargetResourceArn string `json:"TargetResourceArn"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
switch {
case req.ClusterID != "":
@@ -330,7 +331,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// handleError maps backend errors to EMR's wire error shape. EMR's modeled
diff --git a/services/emr/handler_cluster_settings_test.go b/services/emr/handler_cluster_settings_test.go
index fcfc28034c..2b20cf6c71 100644
--- a/services/emr/handler_cluster_settings_test.go
+++ b/services/emr/handler_cluster_settings_test.go
@@ -1,12 +1,14 @@
package emr_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestTerminationProtection(t *testing.T) {
@@ -19,7 +21,7 @@ func TestTerminationProtection(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
clusterID := create.JobFlowID
protectRec := doEMRRequest(t, h, "SetTerminationProtection", map[string]any{
@@ -63,7 +65,7 @@ func TestSetKeepJobFlowAliveWhenNoSteps_SyncsAutoTerminate(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
clusterID := create.JobFlowID
describe := func() bool {
@@ -75,7 +77,7 @@ func TestSetKeepJobFlowAliveWhenNoSteps_SyncsAutoTerminate(t *testing.T) {
AutoTerminate bool `json:"AutoTerminate"`
} `json:"Cluster"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.Cluster.AutoTerminate
}
@@ -107,7 +109,7 @@ func TestModifyCluster(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
modRec := doEMRRequest(t, h, "ModifyCluster", map[string]any{
"ClusterId": create.JobFlowID,
@@ -118,7 +120,7 @@ func TestModifyCluster(t *testing.T) {
var modOut struct {
StepConcurrencyLevel int `json:"StepConcurrencyLevel"`
}
- require.NoError(t, json.Unmarshal(modRec.Body.Bytes(), &modOut))
+ require.NoError(t, json.Unmarshal(modRec.Body.Bytes(), &modOut, jsonx.V1()))
assert.Equal(t, 5, modOut.StepConcurrencyLevel)
}
@@ -132,7 +134,7 @@ func TestModifyCluster_InvalidRange(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
modRec := doEMRRequest(t, h, "ModifyCluster", map[string]any{
"ClusterId": create.JobFlowID,
diff --git a/services/emr/handler_clusters_test.go b/services/emr/handler_clusters_test.go
index 96bdd04d2b..a1f8af0705 100644
--- a/services/emr/handler_clusters_test.go
+++ b/services/emr/handler_clusters_test.go
@@ -2,7 +2,7 @@ package emr_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emr"
)
@@ -54,7 +55,7 @@ func TestEMR_RunJobFlow(t *testing.T) {
ClusterArn string `json:"ClusterArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.JobFlowID)
assert.Contains(t, out.ClusterArn, "elasticmapreduce")
})
@@ -95,7 +96,7 @@ func TestEMR_DescribeCluster(t *testing.T) {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
clusterID := tt.clusterID
if clusterID == "" {
@@ -114,7 +115,7 @@ func TestEMR_DescribeCluster(t *testing.T) {
} `json:"Cluster"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, clusterID, out.Cluster.ID)
assert.Equal(t, "test-cluster", out.Cluster.Name)
}
@@ -160,7 +161,7 @@ func TestEMR_ListClusters(t *testing.T) {
Clusters []any `json:"Clusters"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.GreaterOrEqual(t, len(out.Clusters), tt.wantMinCount)
})
}
@@ -183,7 +184,7 @@ func TestEMR_TerminateJobFlows(t *testing.T) {
var out struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return []string{out.JobFlowID}
},
@@ -282,7 +283,7 @@ func TestRunJobFlow_SecurityConfigurationValidation(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errOut, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", errOut["__type"])
})
@@ -322,7 +323,7 @@ func TestRunJobFlow_DefaultReleaseLabel(t *testing.T) {
var out struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
descRec := doEMRRequest(t, h, "DescribeCluster", map[string]any{"ClusterId": out.JobFlowID})
require.Equal(t, http.StatusOK, descRec.Code)
@@ -332,7 +333,7 @@ func TestRunJobFlow_DefaultReleaseLabel(t *testing.T) {
ReleaseLabel string `json:"ReleaseLabel"`
} `json:"Cluster"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc, jsonx.V1()))
assert.Equal(t, emr.DefaultReleaseLabel, desc.Cluster.ReleaseLabel)
}
@@ -349,7 +350,7 @@ func TestRunJobFlow_ApplicationsDefaulted(t *testing.T) {
var out struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
descRec := doEMRRequest(t, h, "DescribeCluster", map[string]any{"ClusterId": out.JobFlowID})
require.Equal(t, http.StatusOK, descRec.Code)
@@ -361,7 +362,7 @@ func TestRunJobFlow_ApplicationsDefaulted(t *testing.T) {
} `json:"Applications"`
} `json:"Cluster"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc, jsonx.V1()))
assert.NotEmpty(t, desc.Cluster.Applications)
}
@@ -389,7 +390,7 @@ func TestListClusters_DateFilter(t *testing.T) {
ID string `json:"Id"`
} `json:"Clusters"`
}
- require.NoError(t, json.Unmarshal(listInRange.Body.Bytes(), &inRange))
+ require.NoError(t, json.Unmarshal(listInRange.Body.Bytes(), &inRange, jsonx.V1()))
assert.Len(t, inRange.Clusters, 1)
listOld := doEMRRequest(t, h, "ListClusters", map[string]any{
@@ -402,7 +403,7 @@ func TestListClusters_DateFilter(t *testing.T) {
ID string `json:"Id"`
} `json:"Clusters"`
}
- require.NoError(t, json.Unmarshal(listOld.Body.Bytes(), &old))
+ require.NoError(t, json.Unmarshal(listOld.Body.Bytes(), &old, jsonx.V1()))
assert.Empty(t, old.Clusters)
}
@@ -427,7 +428,7 @@ func TestConfigurations_RunJobFlow(t *testing.T) {
var out struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
descRec := doEMRRequest(t, h, "DescribeCluster", map[string]any{"ClusterId": out.JobFlowID})
require.Equal(t, http.StatusOK, descRec.Code)
@@ -443,7 +444,7 @@ func TestConfigurations_RunJobFlow(t *testing.T) {
} `json:"Configurations"`
} `json:"Cluster"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc, jsonx.V1()))
require.Len(t, desc.Cluster.Configurations, 1)
assert.Equal(t, "spark-defaults", desc.Cluster.Configurations[0].Classification)
assert.Equal(t, "4g", desc.Cluster.Configurations[0].Properties["spark.executor.memory"])
@@ -466,7 +467,7 @@ func TestClusterFields_EbsAndOS(t *testing.T) {
var out struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
descRec := doEMRRequest(t, h, "DescribeCluster", map[string]any{"ClusterId": out.JobFlowID})
require.Equal(t, http.StatusOK, descRec.Code)
@@ -478,7 +479,7 @@ func TestClusterFields_EbsAndOS(t *testing.T) {
EbsRootVolumeSize int `json:"EbsRootVolumeSize"`
} `json:"Cluster"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc, jsonx.V1()))
assert.Equal(t, 64, desc.Cluster.EbsRootVolumeSize)
assert.Equal(t, "2.0.20230718", desc.Cluster.OSReleaseLabel)
assert.Equal(t, "ami-0123456789abcdef0", desc.Cluster.CustomAmiID)
@@ -579,13 +580,13 @@ func TestDescribeCluster_TagsEmptyNotAbsent(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
rec := doEMRRequest(t, h, "DescribeCluster", map[string]any{"ClusterId": create.JobFlowID})
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
cluster, ok := raw["Cluster"].(map[string]any)
require.True(t, ok, "Cluster must be an object")
@@ -612,13 +613,13 @@ func TestDescribeCluster_TagsRoundTrip(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
rec := doEMRRequest(t, h, "DescribeCluster", map[string]any{"ClusterId": create.JobFlowID})
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
cluster := raw["Cluster"].(map[string]any)
tags := cluster["Tags"].([]any)
@@ -642,7 +643,7 @@ func TestDescribeCluster_TagsEmptyAfterRemove(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
rmRec := doEMRRequest(t, h, "RemoveTags", map[string]any{
"ResourceId": create.JobFlowID,
@@ -654,7 +655,7 @@ func TestDescribeCluster_TagsEmptyAfterRemove(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
cluster := raw["Cluster"].(map[string]any)
tags, hasKey := cluster["Tags"]
@@ -673,7 +674,7 @@ func TestErrorMapping_NotFound_ClusterNotFoundException(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errOut, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", errOut["__type"])
}
@@ -728,7 +729,7 @@ func newSessionForTest(t *testing.T, h *emr.Handler, clusterName string) (string
var cluster struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cluster))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cluster, jsonx.V1()))
startRec := doEMRRequest(t, h, "StartSession", map[string]any{"ClusterId": cluster.JobFlowID})
require.Equal(t, http.StatusOK, startRec.Code, startRec.Body.String())
@@ -736,7 +737,7 @@ func newSessionForTest(t *testing.T, h *emr.Handler, clusterName string) (string
var session struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &session))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &session, jsonx.V1()))
return cluster.JobFlowID, session.ID
}
@@ -765,7 +766,7 @@ func TestEMR_StartSession(t *testing.T) {
var out struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.JobFlowID
},
@@ -789,7 +790,7 @@ func TestEMR_StartSession(t *testing.T) {
var out struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
termRec := doEMRRequest(t, h, "TerminateJobFlows", map[string]any{
"JobFlowIds": []string{out.JobFlowID},
@@ -822,7 +823,7 @@ func TestEMR_StartSession(t *testing.T) {
ARN string `json:"Arn"`
State string `json:"State"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.ID)
assert.Equal(t, clusterID, out.ClusterID)
assert.Equal(t, emr.SessionStateSubmitted, out.State)
@@ -879,7 +880,7 @@ func TestEMR_GetSession(t *testing.T) {
State string `json:"State"`
} `json:"Session"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, sessionID, out.Session.ID)
assert.Equal(t, clusterID, out.Session.ClusterID)
assert.Equal(t, emr.SessionStateSubmitted, out.Session.State)
@@ -921,7 +922,7 @@ func TestEMR_ListSessions(t *testing.T) {
var cluster struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cluster))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cluster, jsonx.V1()))
for range tt.numSessions {
startRec := doEMRRequest(t, h, "StartSession", map[string]any{"ClusterId": cluster.JobFlowID})
@@ -941,7 +942,7 @@ func TestEMR_ListSessions(t *testing.T) {
ID string `json:"Id"`
} `json:"Sessions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Sessions, tt.wantCount)
})
}
@@ -996,7 +997,7 @@ func TestEMR_TerminateSession(t *testing.T) {
SessionID string `json:"SessionId"`
State string `json:"State"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, clusterID, out.ClusterID)
assert.Equal(t, sessionID, out.SessionID)
assert.Equal(t, emr.SessionStateTerminated, out.State)
@@ -1050,7 +1051,7 @@ func TestEMR_GetSessionEndpoint(t *testing.T) {
Endpoint string `json:"Endpoint"`
AuthToken string `json:"AuthToken"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.Endpoint)
assert.NotEmpty(t, out.AuthToken)
assert.Contains(t, out.Credentials, "UsernamePassword")
@@ -1104,7 +1105,7 @@ func TestEMR_TerminateJobFlows_CascadesToSessions(t *testing.T) {
State string `json:"State"`
} `json:"Session"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, emr.SessionStateTerminated, out.Session.State,
"session must be terminated when its cluster is terminated")
})
diff --git a/services/emr/handler_instance_fleets_test.go b/services/emr/handler_instance_fleets_test.go
index ff19295b17..7c29afb470 100644
--- a/services/emr/handler_instance_fleets_test.go
+++ b/services/emr/handler_instance_fleets_test.go
@@ -2,13 +2,14 @@ package emr_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emr"
)
@@ -23,7 +24,7 @@ func TestEMR_ListInstanceFleets(t *testing.T) {
var createOut struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
rec := doEMRRequest(t, h, "ListInstanceFleets", map[string]any{
"ClusterId": createOut.JobFlowID,
@@ -35,7 +36,7 @@ func TestEMR_ListInstanceFleets(t *testing.T) {
InstanceFleets []emr.InstanceFleet `json:"InstanceFleets"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.InstanceFleets)
}
@@ -71,7 +72,7 @@ func TestEMR_AddInstanceFleet(t *testing.T) {
var createOut struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
clusterID := tt.clusterID
if clusterID == "" {
@@ -94,7 +95,7 @@ func TestEMR_AddInstanceFleet(t *testing.T) {
ClusterID string `json:"ClusterId"`
InstanceFleetID string `json:"InstanceFleetId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.InstanceFleetID)
assert.Equal(t, clusterID, out.ClusterID)
assert.Contains(t, out.ClusterArn, "elasticmapreduce")
@@ -114,7 +115,7 @@ func TestEMR_AddInstanceFleet_ListInstanceFleets(t *testing.T) {
var createOut struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
// Add a fleet.
addRec := doEMRRequest(t, h, "AddInstanceFleet", map[string]any{
@@ -138,7 +139,7 @@ func TestEMR_AddInstanceFleet_ListInstanceFleets(t *testing.T) {
Name string `json:"Name"`
} `json:"InstanceFleets"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.InstanceFleets, 1)
assert.Equal(t, "task-fleet", listOut.InstanceFleets[0].Name)
assert.NotEmpty(t, listOut.InstanceFleets[0].ID)
@@ -165,7 +166,7 @@ func TestInstanceFleet_CapacityTargets(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
addRec := doEMRRequest(t, h, "AddInstanceFleet", map[string]any{
"ClusterId": create.JobFlowID,
@@ -193,7 +194,7 @@ func TestInstanceFleet_CapacityTargets(t *testing.T) {
ProvisionedSpotCapacity int `json:"ProvisionedSpotCapacity"`
} `json:"InstanceFleets"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.InstanceFleets, 1)
f := out.InstanceFleets[0]
assert.Equal(t, 5, f.TargetOnDemandCapacity)
@@ -234,7 +235,7 @@ func TestListInstanceFleetsTyped(t *testing.T) {
var createOut struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
addRec := doEMRRequest(t, h, "AddInstanceFleet", map[string]any{
"ClusterId": createOut.JobFlowID,
@@ -250,7 +251,7 @@ func TestListInstanceFleetsTyped(t *testing.T) {
var out struct {
InstanceFleets []emr.InstanceFleet `json:"InstanceFleets"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.InstanceFleets, 1)
assert.Equal(t, "typed-fleet", out.InstanceFleets[0].Name)
assert.NotEmpty(t, out.InstanceFleets[0].ID)
diff --git a/services/emr/handler_instance_groups_test.go b/services/emr/handler_instance_groups_test.go
index da4fe31db5..fe184951a6 100644
--- a/services/emr/handler_instance_groups_test.go
+++ b/services/emr/handler_instance_groups_test.go
@@ -2,13 +2,14 @@ package emr_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emr"
)
@@ -48,7 +49,7 @@ func TestEMR_ListInstanceGroups(t *testing.T) {
var createOut struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
rec := doEMRRequest(t, h, "ListInstanceGroups", map[string]any{
"ClusterId": createOut.JobFlowID,
@@ -63,7 +64,7 @@ func TestEMR_ListInstanceGroups(t *testing.T) {
} `json:"Status"`
} `json:"InstanceGroups"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.InstanceGroups, tt.wantCount)
for _, g := range out.InstanceGroups {
@@ -116,7 +117,7 @@ func TestEMR_AddInstanceGroups(t *testing.T) {
var createOut struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
jobFlowID := tt.jobFlowID
if jobFlowID == "" {
@@ -138,7 +139,7 @@ func TestEMR_AddInstanceGroups(t *testing.T) {
JobFlowID string `json:"JobFlowId"`
InstanceGroupIDs []string `json:"InstanceGroupIds"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.InstanceGroupIDs, 1)
assert.Equal(t, jobFlowID, out.JobFlowID)
assert.Contains(t, out.ClusterArn, "elasticmapreduce")
@@ -194,7 +195,7 @@ func TestModifyInstanceGroups(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
listRec := doEMRRequest(t, h, "ListInstanceGroups", map[string]any{"ClusterId": create.JobFlowID})
var listOut struct {
@@ -202,7 +203,7 @@ func TestModifyInstanceGroups(t *testing.T) {
ID string `json:"Id"`
} `json:"InstanceGroups"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.NotEmpty(t, listOut.InstanceGroups)
modRec := doEMRRequest(t, h, "ModifyInstanceGroups", map[string]any{
@@ -238,7 +239,7 @@ func TestInstanceGroup_BidPrice(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
listRec := doEMRRequest(t, h, "ListInstanceGroups", map[string]any{"ClusterId": create.JobFlowID})
require.Equal(t, http.StatusOK, listRec.Code)
@@ -249,7 +250,7 @@ func TestInstanceGroup_BidPrice(t *testing.T) {
BidPrice string `json:"BidPrice"`
} `json:"InstanceGroups"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.InstanceGroups, 1)
assert.Equal(t, "SPOT", out.InstanceGroups[0].Market)
assert.Equal(t, "0.05", out.InstanceGroups[0].BidPrice)
@@ -322,8 +323,8 @@ func TestBuildInstanceGroups_UniqueIDs(t *testing.T) {
var c1, c2 struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &c1))
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &c2))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &c1, jsonx.V1()))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &c2, jsonx.V1()))
// Gather instance group IDs via ListInstanceGroups for each cluster
allIDs := make(map[string]bool)
@@ -336,7 +337,7 @@ func TestBuildInstanceGroups_UniqueIDs(t *testing.T) {
ID string `json:"Id"`
} `json:"InstanceGroups"`
}
- require.NoError(t, json.Unmarshal(igRec.Body.Bytes(), &igOut))
+ require.NoError(t, json.Unmarshal(igRec.Body.Bytes(), &igOut, jsonx.V1()))
for _, ig := range igOut.InstanceGroups {
assert.False(t, allIDs[ig.ID], "duplicate instance group ID %s", ig.ID)
diff --git a/services/emr/handler_instances_test.go b/services/emr/handler_instances_test.go
index 50e0b0ff40..4d8b9e1034 100644
--- a/services/emr/handler_instances_test.go
+++ b/services/emr/handler_instances_test.go
@@ -1,12 +1,14 @@
package emr_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestListInstances(t *testing.T) {
@@ -27,7 +29,7 @@ func TestListInstances(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
listRec := doEMRRequest(t, h, "ListInstances", map[string]any{"ClusterId": create.JobFlowID})
require.Equal(t, http.StatusOK, listRec.Code)
@@ -38,7 +40,7 @@ func TestListInstances(t *testing.T) {
Ec2InstanceID string `json:"Ec2InstanceId"`
} `json:"Instances"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Instances, 3)
}
@@ -53,7 +55,7 @@ func TestListInstances_InstanceFleets(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
addFleet := func(fleetType string, onDemand, spot int) string {
rec := doEMRRequest(t, h, "AddInstanceFleet", map[string]any{
@@ -70,7 +72,7 @@ func TestListInstances_InstanceFleets(t *testing.T) {
var out struct {
InstanceFleetID string `json:"InstanceFleetId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.InstanceFleetID
}
@@ -116,7 +118,7 @@ func TestListInstances_InstanceFleets(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out instancesResp
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Instances, tt.wantLen)
for _, inst := range out.Instances {
diff --git a/services/emr/handler_job_flows_test.go b/services/emr/handler_job_flows_test.go
index efd29a67bf..0a8135d0a5 100644
--- a/services/emr/handler_job_flows_test.go
+++ b/services/emr/handler_job_flows_test.go
@@ -1,12 +1,14 @@
package emr_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDescribeJobFlows(t *testing.T) {
@@ -19,7 +21,7 @@ func TestDescribeJobFlows(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
rec := doEMRRequest(t, h, "DescribeJobFlows", map[string]any{
"JobFlowIds": []string{create.JobFlowID},
@@ -32,7 +34,7 @@ func TestDescribeJobFlows(t *testing.T) {
Name string `json:"Name"`
} `json:"JobFlows"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.JobFlows, 1)
assert.Equal(t, create.JobFlowID, out.JobFlows[0].JobFlowID)
assert.Equal(t, "jf-cluster", out.JobFlows[0].Name)
@@ -65,7 +67,7 @@ func TestDescribeJobFlows_EmptyNotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
flows, hasKey := raw["JobFlows"]
assert.True(t, hasKey, "DescribeJobFlows must include 'JobFlows' key")
diff --git a/services/emr/handler_notebook_executions_test.go b/services/emr/handler_notebook_executions_test.go
index e033d742db..fcd592d1b1 100644
--- a/services/emr/handler_notebook_executions_test.go
+++ b/services/emr/handler_notebook_executions_test.go
@@ -2,13 +2,14 @@ package emr_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emr"
)
@@ -31,7 +32,7 @@ func TestNotebookExecution(t *testing.T) {
var startOut struct {
NotebookExecutionID string `json:"NotebookExecutionId"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
assert.NotEmpty(t, startOut.NotebookExecutionID)
// Describe it.
@@ -47,7 +48,7 @@ func TestNotebookExecution(t *testing.T) {
NotebookExecutionName string `json:"NotebookExecutionName"`
} `json:"NotebookExecution"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, startOut.NotebookExecutionID, descOut.NotebookExecution.NotebookExecutionID)
assert.Equal(t, "RUNNING", descOut.NotebookExecution.Status)
assert.Equal(t, "test-run", descOut.NotebookExecution.NotebookExecutionName)
@@ -62,7 +63,7 @@ func TestNotebookExecution(t *testing.T) {
Status string `json:"Status"`
} `json:"NotebookExecutions"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.NotebookExecutions, 1)
assert.Equal(t, startOut.NotebookExecutionID, listOut.NotebookExecutions[0].NotebookExecutionID)
@@ -83,7 +84,7 @@ func TestNotebookExecution(t *testing.T) {
Status string `json:"Status"`
} `json:"NotebookExecution"`
}
- require.NoError(t, json.Unmarshal(descRec2.Body.Bytes(), &descOut2))
+ require.NoError(t, json.Unmarshal(descRec2.Body.Bytes(), &descOut2, jsonx.V1()))
assert.Equal(t, "STOPPED", descOut2.NotebookExecution.Status)
}
@@ -127,7 +128,7 @@ func TestNotebookExecution_ListFilter(t *testing.T) {
NotebookExecutionID string `json:"NotebookExecutionId"`
} `json:"NotebookExecutions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.NotebookExecutions, 3)
}
@@ -155,7 +156,7 @@ func TestNotebookExecution_ListNoParamsOrTagsLeak(t *testing.T) {
var raw struct {
NotebookExecutions []map[string]any `json:"NotebookExecutions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
require.Len(t, raw.NotebookExecutions, 1)
item := raw.NotebookExecutions[0]
@@ -232,7 +233,7 @@ func TestNotebookExecution_TagsEmptyNotAbsent(t *testing.T) {
var start struct {
NotebookExecutionID string `json:"NotebookExecutionId"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &start))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &start, jsonx.V1()))
rec := doEMRRequest(t, h, "DescribeNotebookExecution", map[string]any{
"NotebookExecutionId": start.NotebookExecutionID,
@@ -240,7 +241,7 @@ func TestNotebookExecution_TagsEmptyNotAbsent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
ne, ok := raw["NotebookExecution"].(map[string]any)
require.True(t, ok, "NotebookExecution must be an object")
diff --git a/services/emr/handler_persistent_app_ui_test.go b/services/emr/handler_persistent_app_ui_test.go
index 51ee63559b..a4b9093d30 100644
--- a/services/emr/handler_persistent_app_ui_test.go
+++ b/services/emr/handler_persistent_app_ui_test.go
@@ -2,13 +2,14 @@ package emr_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emr"
)
@@ -48,7 +49,7 @@ func TestEMR_CreatePersistentAppUI(t *testing.T) {
var out struct {
PersistentAppUIID string `json:"PersistentAppUIId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.PersistentAppUIID)
}
})
@@ -65,7 +66,7 @@ func TestGetClusterSessionCredentials(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
rec := doEMRRequest(t, h, "GetClusterSessionCredentials", map[string]any{
"ClusterId": create.JobFlowID,
@@ -77,7 +78,7 @@ func TestGetClusterSessionCredentials(t *testing.T) {
Credentials map[string]any `json:"Credentials"`
ExpiresAt float64 `json:"ExpiresAt"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.Credentials)
assert.NotZero(t, out.ExpiresAt)
}
@@ -92,7 +93,7 @@ func TestGetClusterSessionCredentials_MissingRole(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
rec := doEMRRequest(t, h, "GetClusterSessionCredentials", map[string]any{
"ClusterId": create.JobFlowID,
@@ -131,6 +132,6 @@ func TestGetOnClusterPresignedURL_NonExistentCluster(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errOut, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", errOut["__type"])
}
diff --git a/services/emr/handler_policies_test.go b/services/emr/handler_policies_test.go
index 20959928a0..9d8c49edb2 100644
--- a/services/emr/handler_policies_test.go
+++ b/services/emr/handler_policies_test.go
@@ -1,12 +1,14 @@
package emr_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestManagedScalingPolicy(t *testing.T) {
@@ -19,7 +21,7 @@ func TestManagedScalingPolicy(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
clusterID := create.JobFlowID
putRec := doEMRRequest(t, h, "PutManagedScalingPolicy", map[string]any{
@@ -45,7 +47,7 @@ func TestManagedScalingPolicy(t *testing.T) {
} `json:"ComputeLimits"`
} `json:"ManagedScalingPolicy"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "InstanceFleetUnits", getOut.ManagedScalingPolicy.ComputeLimits.UnitType)
assert.Equal(t, 10, getOut.ManagedScalingPolicy.ComputeLimits.MaximumCapacityUnits)
@@ -63,7 +65,7 @@ func TestAutoTerminationPolicy(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
clusterID := create.JobFlowID
putRec := doEMRRequest(t, h, "PutAutoTerminationPolicy", map[string]any{
@@ -80,7 +82,7 @@ func TestAutoTerminationPolicy(t *testing.T) {
IdleTimeout int64 `json:"IdleTimeout"`
} `json:"AutoTerminationPolicy"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, int64(3600), getOut.AutoTerminationPolicy.IdleTimeout)
}
@@ -94,7 +96,7 @@ func TestAutoTerminationPolicy_InvalidTimeout(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
putRec := doEMRRequest(t, h, "PutAutoTerminationPolicy", map[string]any{
"ClusterId": create.JobFlowID,
@@ -116,7 +118,7 @@ func TestBlockPublicAccessConfiguration(t *testing.T) {
BlockPublicSecurityGroupRules bool `json:"BlockPublicSecurityGroupRules"`
} `json:"BlockPublicAccessConfiguration"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.True(t, getOut.BlockPublicAccessConfiguration.BlockPublicSecurityGroupRules)
putRec := doEMRRequest(t, h, "PutBlockPublicAccessConfiguration", map[string]any{
@@ -128,7 +130,7 @@ func TestBlockPublicAccessConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
getRec2 := doEMRRequest(t, h, "GetBlockPublicAccessConfiguration", map[string]any{})
- require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getOut, jsonx.V1()))
assert.False(t, getOut.BlockPublicAccessConfiguration.BlockPublicSecurityGroupRules)
}
@@ -149,7 +151,7 @@ func TestAutoScalingPolicy(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
listRec := doEMRRequest(t, h, "ListInstanceGroups", map[string]any{"ClusterId": create.JobFlowID})
var listOut struct {
@@ -157,7 +159,7 @@ func TestAutoScalingPolicy(t *testing.T) {
ID string `json:"Id"`
} `json:"InstanceGroups"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.NotEmpty(t, listOut.InstanceGroups)
groupID := listOut.InstanceGroups[0].ID
@@ -177,7 +179,7 @@ func TestAutoScalingPolicy(t *testing.T) {
} `json:"Constraints"`
} `json:"AutoScalingPolicy"`
}
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut, jsonx.V1()))
require.NotNil(t, putOut.AutoScalingPolicy)
assert.Equal(t, 10, putOut.AutoScalingPolicy.Constraints.MaxCapacity)
diff --git a/services/emr/handler_release_labels_test.go b/services/emr/handler_release_labels_test.go
index 3bedf9f21a..d62f68087e 100644
--- a/services/emr/handler_release_labels_test.go
+++ b/services/emr/handler_release_labels_test.go
@@ -1,12 +1,14 @@
package emr_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestListReleaseLabels(t *testing.T) {
@@ -20,7 +22,7 @@ func TestListReleaseLabels(t *testing.T) {
var out struct {
ReleaseLabels []string `json:"ReleaseLabels"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.ReleaseLabels)
assert.Contains(t, out.ReleaseLabels, "emr-7.3.0")
}
@@ -39,7 +41,7 @@ func TestDescribeReleaseLabel(t *testing.T) {
Name string `json:"Name"`
} `json:"Applications"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "emr-7.3.0", out.ReleaseLabel)
assert.NotEmpty(t, out.Applications)
}
@@ -58,6 +60,6 @@ func TestListSupportedInstanceTypes(t *testing.T) {
VCPU int `json:"VCPU"`
} `json:"SupportedInstanceTypes"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.SupportedInstanceTypes)
}
diff --git a/services/emr/handler_security_configurations_test.go b/services/emr/handler_security_configurations_test.go
index 5dc8fb0dd3..1f2598ad4d 100644
--- a/services/emr/handler_security_configurations_test.go
+++ b/services/emr/handler_security_configurations_test.go
@@ -2,7 +2,7 @@ package emr_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emr"
)
@@ -103,7 +104,7 @@ func TestEMR_SecurityConfiguration(t *testing.T) {
Name string `json:"Name"`
CreationDateTime float64 `json:"CreationDateTime"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.scName, out.Name)
assert.NotZero(t, out.CreationDateTime)
}
@@ -154,7 +155,7 @@ func TestListSecurityConfigurations(t *testing.T) {
Name string `json:"Name"`
} `json:"SecurityConfigurations"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.SecurityConfigurations, 3)
assert.Equal(t, "alpha", out.SecurityConfigurations[0].Name)
}
@@ -223,7 +224,7 @@ func TestDescribeSecurityConfiguration(t *testing.T) {
SecurityConfiguration string `json:"SecurityConfiguration"`
CreationDateTime float64 `json:"CreationDateTime"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.scName, out.Name)
assert.NotZero(t, out.CreationDateTime)
assert.JSONEq(t, `{"EncryptionConfiguration":{}}`, out.SecurityConfiguration)
diff --git a/services/emr/handler_steps_test.go b/services/emr/handler_steps_test.go
index 723a035171..29a044f94f 100644
--- a/services/emr/handler_steps_test.go
+++ b/services/emr/handler_steps_test.go
@@ -2,13 +2,14 @@ package emr_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emr"
)
@@ -33,7 +34,7 @@ func TestEMR_AddJobFlowSteps(t *testing.T) {
var createOut struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
rec := doEMRRequest(t, h, "AddJobFlowSteps", map[string]any{
"JobFlowId": createOut.JobFlowID,
@@ -52,7 +53,7 @@ func TestEMR_AddJobFlowSteps(t *testing.T) {
StepIDs []string `json:"StepIds"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.StepIDs, 1)
assert.Contains(t, out.StepIDs[0], "s-")
}
@@ -68,7 +69,7 @@ func TestEMR_AddJobFlowSteps_RejectsTerminatedCluster(t *testing.T) {
var createOut struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
termRec := doEMRRequest(t, h, "TerminateJobFlows", map[string]any{
"JobFlowIds": []string{createOut.JobFlowID},
@@ -91,7 +92,7 @@ func TestEMR_AddJobFlowSteps_RejectsTerminatedCluster(t *testing.T) {
var errBody struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", errBody.Type)
listRec := doEMRRequest(t, h, "ListSteps", map[string]any{"ClusterId": createOut.JobFlowID})
@@ -100,7 +101,7 @@ func TestEMR_AddJobFlowSteps_RejectsTerminatedCluster(t *testing.T) {
var listOut struct {
Steps []any `json:"Steps"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Empty(t, listOut.Steps)
}
@@ -115,7 +116,7 @@ func TestEMR_ListBootstrapActions(t *testing.T) {
var createOut struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
rec := doEMRRequest(t, h, "ListBootstrapActions", map[string]any{
"ClusterId": createOut.JobFlowID,
@@ -127,7 +128,7 @@ func TestEMR_ListBootstrapActions(t *testing.T) {
BootstrapActions []any `json:"BootstrapActions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.BootstrapActions)
}
@@ -163,7 +164,7 @@ func TestEMR_CancelSteps(t *testing.T) {
var createOut struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
clusterID := tt.clusterID
if clusterID == "" {
@@ -181,7 +182,7 @@ func TestEMR_CancelSteps(t *testing.T) {
var out struct {
CancelStepsInfoList []any `json:"CancelStepsInfoList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.CancelStepsInfoList)
}
})
@@ -199,7 +200,7 @@ func TestSteps(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
clusterID := create.JobFlowID
addRec := doEMRRequest(t, h, "AddJobFlowSteps", map[string]any{
@@ -222,7 +223,7 @@ func TestSteps(t *testing.T) {
var addOut struct {
StepIDs []string `json:"StepIds"`
}
- require.NoError(t, json.Unmarshal(addRec.Body.Bytes(), &addOut))
+ require.NoError(t, json.Unmarshal(addRec.Body.Bytes(), &addOut, jsonx.V1()))
require.Len(t, addOut.StepIDs, 2)
assert.NotEmpty(t, addOut.StepIDs[0])
@@ -234,7 +235,7 @@ func TestSteps(t *testing.T) {
ID string `json:"Id"`
} `json:"Steps"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut.Steps, 2)
descRec := doEMRRequest(t, h, "DescribeStep", map[string]any{
@@ -332,7 +333,7 @@ func TestListBootstrapActions_RoundTrip(t *testing.T) {
var createOut struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
listRec := doEMRRequest(t, h, "ListBootstrapActions", map[string]any{
"ClusterId": createOut.JobFlowID,
@@ -346,7 +347,7 @@ func TestListBootstrapActions_RoundTrip(t *testing.T) {
Args []string `json:"Args"`
} `json:"BootstrapActions"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
if tt.wantActions == nil {
assert.Empty(t, listOut.BootstrapActions)
@@ -423,7 +424,7 @@ func TestCancelSteps_ReturnsPerStepInfo(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
// List steps to get IDs
listRec := doEMRRequest(t, h, "ListSteps", map[string]any{"ClusterId": create.JobFlowID})
@@ -434,7 +435,7 @@ func TestCancelSteps_ReturnsPerStepInfo(t *testing.T) {
ID string `json:"Id"`
} `json:"Steps"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.NotEmpty(t, listOut.Steps)
stepID := listOut.Steps[0].ID
@@ -450,7 +451,7 @@ func TestCancelSteps_ReturnsPerStepInfo(t *testing.T) {
Status string `json:"Status"`
} `json:"CancelStepsInfoList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.CancelStepsInfoList, 1)
assert.Equal(t, stepID, out.CancelStepsInfoList[0].StepID)
assert.NotEmpty(t, out.CancelStepsInfoList[0].Status)
diff --git a/services/emr/handler_studios_test.go b/services/emr/handler_studios_test.go
index 94c40ea677..c4e0983b05 100644
--- a/services/emr/handler_studios_test.go
+++ b/services/emr/handler_studios_test.go
@@ -2,7 +2,7 @@ package emr_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emr"
)
@@ -52,7 +53,7 @@ func TestEMR_Studio(t *testing.T) {
var out struct {
StudioID string `json:"StudioId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.StudioID
},
@@ -112,7 +113,7 @@ func TestEMR_Studio(t *testing.T) {
StudioID string `json:"StudioId"`
URL string `json:"Url"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.StudioID)
assert.NotEmpty(t, out.URL)
}
@@ -178,7 +179,7 @@ func TestEMR_StudioSessionMapping(t *testing.T) {
var studioOut struct {
StudioID string `json:"StudioId"`
}
- require.NoError(t, json.Unmarshal(studioRec.Body.Bytes(), &studioOut))
+ require.NoError(t, json.Unmarshal(studioRec.Body.Bytes(), &studioOut, jsonx.V1()))
studioID = studioOut.StudioID
var rec *httptest.ResponseRecorder
@@ -253,7 +254,7 @@ func TestEMR_DeleteStudio_CascadesSessionMappings(t *testing.T) {
var studioOut struct {
StudioID string `json:"StudioId"`
}
- require.NoError(t, json.Unmarshal(studioRec.Body.Bytes(), &studioOut))
+ require.NoError(t, json.Unmarshal(studioRec.Body.Bytes(), &studioOut, jsonx.V1()))
// Create a session mapping.
mappingRec := doEMRRequest(t, h, "CreateStudioSessionMapping", map[string]any{
@@ -296,7 +297,7 @@ func TestStudio_DescribeAndList(t *testing.T) {
var create struct {
StudioID string `json:"StudioId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
studioID := create.StudioID
descRec := doEMRRequest(t, h, "DescribeStudio", map[string]any{"StudioId": studioID})
@@ -307,7 +308,7 @@ func TestStudio_DescribeAndList(t *testing.T) {
Name string `json:"Name"`
} `json:"Studio"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc, jsonx.V1()))
assert.Equal(t, "my-studio", desc.Studio.Name)
listRec := doEMRRequest(t, h, "ListStudios", map[string]any{})
@@ -318,7 +319,7 @@ func TestStudio_DescribeAndList(t *testing.T) {
Name string `json:"Name"`
} `json:"Studios"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut.Studios, 1)
}
@@ -340,7 +341,7 @@ func TestStudioSessionMapping_GetUpdateList(t *testing.T) {
var create struct {
StudioID string `json:"StudioId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
studioID := create.StudioID
doEMRRequest(t, h, "CreateStudioSessionMapping", map[string]any{
@@ -358,7 +359,7 @@ func TestStudioSessionMapping_GetUpdateList(t *testing.T) {
SessionPolicyArn string `json:"SessionPolicyArn"`
} `json:"SessionMappings"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.SessionMappings, 1)
updateRec := doEMRRequest(t, h, "UpdateStudioSessionMapping", map[string]any{
@@ -381,7 +382,7 @@ func TestStudioSessionMapping_GetUpdateList(t *testing.T) {
SessionPolicyArn string `json:"SessionPolicyArn"`
} `json:"SessionMapping"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "arn:policy:new", getOut.SessionMapping.SessionPolicyArn)
}
@@ -502,13 +503,13 @@ func TestDescribeStudio_TagsEmptyNotAbsent(t *testing.T) {
var create struct {
StudioID string `json:"StudioId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
rec := doEMRRequest(t, h, "DescribeStudio", map[string]any{"StudioId": create.StudioID})
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
studio, ok := raw["Studio"].(map[string]any)
require.True(t, ok, "Studio must be an object")
@@ -538,13 +539,13 @@ func TestDescribeStudio_SubnetIdsEmptyNotAbsent(t *testing.T) {
var create struct {
StudioID string `json:"StudioId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
rec := doEMRRequest(t, h, "DescribeStudio", map[string]any{"StudioId": create.StudioID})
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
studio := raw["Studio"].(map[string]any)
@@ -573,7 +574,7 @@ func TestListStudioSessionMappings_EmptyNotNull(t *testing.T) {
var create struct {
StudioID string `json:"StudioId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
rec := doEMRRequest(t, h, "ListStudioSessionMappings", map[string]any{
"StudioId": create.StudioID,
@@ -581,7 +582,7 @@ func TestListStudioSessionMappings_EmptyNotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
mappings, hasKey := raw["SessionMappings"]
assert.True(t, hasKey, "ListStudioSessionMappings must include 'SessionMappings' key")
@@ -615,7 +616,7 @@ func TestWireShape_CreateStudio_Description(t *testing.T) {
var out struct {
StudioID string `json:"StudioId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
descRec := doEMRRequest(t, h, "DescribeStudio", map[string]any{"StudioId": out.StudioID})
require.Equal(t, http.StatusOK, descRec.Code)
@@ -625,7 +626,7 @@ func TestWireShape_CreateStudio_Description(t *testing.T) {
Description string `json:"Description"`
} `json:"Studio"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc, jsonx.V1()))
assert.Equal(t, "a test studio", desc.Studio.Description,
"CreateStudio must forward Description to the created Studio")
}
@@ -654,7 +655,7 @@ func TestWireShape_UpdateStudio_AppliesSubnetIds(t *testing.T) {
var created struct {
StudioID string `json:"StudioId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
updateRec := doEMRRequest(t, h, "UpdateStudio", map[string]any{
"StudioId": created.StudioID,
@@ -670,7 +671,7 @@ func TestWireShape_UpdateStudio_AppliesSubnetIds(t *testing.T) {
SubnetIDs []string `json:"SubnetIds"`
} `json:"Studio"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc, jsonx.V1()))
assert.ElementsMatch(t, []string{"subnet-1", "subnet-2"}, desc.Studio.SubnetIDs,
"UpdateStudio must apply SubnetIds from the request, not discard them")
}
diff --git a/services/emr/handler_tags_test.go b/services/emr/handler_tags_test.go
index 8129d90cec..da3a3376e9 100644
--- a/services/emr/handler_tags_test.go
+++ b/services/emr/handler_tags_test.go
@@ -2,13 +2,14 @@ package emr_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emr"
)
@@ -37,7 +38,7 @@ func TestEMR_AddAndRemoveTags(t *testing.T) {
var createOut struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
addRec := doEMRRequest(t, h, "AddTags", map[string]any{
"ResourceId": createOut.JobFlowID,
@@ -75,7 +76,7 @@ func TestEMR_ListTagsForResource(t *testing.T) {
var createOut struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
return createOut.JobFlowID
},
@@ -96,7 +97,7 @@ func TestEMR_ListTagsForResource(t *testing.T) {
var createOut struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
return createOut.JobFlowID
},
@@ -131,7 +132,7 @@ func TestEMR_ListTagsForResource(t *testing.T) {
var tagOut struct {
Tags []emr.Tag `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagOut, jsonx.V1()))
tt.checkTags(t, tagOut.Tags)
}
})
@@ -270,7 +271,7 @@ func TestListTagsForResource_ReturnsList(t *testing.T) {
var createOut struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
listRec := doEMRRequest(t, h, "ListTagsForResource", map[string]any{
"ResourceId": createOut.JobFlowID,
@@ -280,7 +281,7 @@ func TestListTagsForResource_ReturnsList(t *testing.T) {
var out struct {
Tags []emr.Tag `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Tags, 2)
// Verify sorted order.
assert.Equal(t, "a", out.Tags[0].Key)
diff --git a/services/emr/handler_test.go b/services/emr/handler_test.go
index b0db401029..a92ae1b82d 100644
--- a/services/emr/handler_test.go
+++ b/services/emr/handler_test.go
@@ -3,7 +3,7 @@ package emr_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/emr"
)
@@ -42,7 +43,7 @@ func newTestHandler(t *testing.T) *emr.Handler {
func doEMRRequest(t *testing.T, h *emr.Handler, action string, body any) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -270,7 +271,7 @@ func TestEMR_ExtractResource(t *testing.T) {
h := newTestHandler(t)
e := echo.New()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
@@ -469,7 +470,7 @@ func TestErrValidationMapping(t *testing.T) {
var errBody struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
// EMR's real error model has only InvalidRequestException (client fault)
// and InternalServerException (server fault) -- no ValidationException.
assert.Equal(t, "InvalidRequestException", errBody.Type)
diff --git a/services/emr/handler_wire_shape_test.go b/services/emr/handler_wire_shape_test.go
index bc82328d83..0448a2048e 100644
--- a/services/emr/handler_wire_shape_test.go
+++ b/services/emr/handler_wire_shape_test.go
@@ -1,7 +1,7 @@
package emr_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
emrtypes "github.com/aws/aws-sdk-go-v2/service/emr/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestWireShape_ListClusters_IncludesTimeline verifies ListClusters' per-cluster
@@ -39,7 +41,7 @@ func TestWireShape_ListClusters_IncludesTimeline(t *testing.T) {
} `json:"Status"`
} `json:"Clusters"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Clusters, 1)
assert.NotZero(t, out.Clusters[0].Status.Timeline.CreationDateTime)
assert.NotZero(t, out.Clusters[0].Status.Timeline.ReadyDateTime)
@@ -60,7 +62,7 @@ func TestWireShape_ModifyInstanceFleet_AppliesCapacities(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
addRec := doEMRRequest(t, h, "AddInstanceFleet", map[string]any{
"ClusterId": create.JobFlowID,
@@ -76,7 +78,7 @@ func TestWireShape_ModifyInstanceFleet_AppliesCapacities(t *testing.T) {
var added struct {
InstanceFleetID string `json:"InstanceFleetId"`
}
- require.NoError(t, json.Unmarshal(addRec.Body.Bytes(), &added))
+ require.NoError(t, json.Unmarshal(addRec.Body.Bytes(), &added, jsonx.V1()))
modRec := doEMRRequest(t, h, "ModifyInstanceFleet", map[string]any{
"ClusterId": create.JobFlowID,
@@ -100,7 +102,7 @@ func TestWireShape_ModifyInstanceFleet_AppliesCapacities(t *testing.T) {
ProvisionedSpotCapacity int `json:"ProvisionedSpotCapacity"`
} `json:"InstanceFleets"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.InstanceFleets, 1)
f := out.InstanceFleets[0]
assert.Equal(t, 8, f.TargetOnDemandCapacity)
@@ -134,7 +136,7 @@ func TestWireShape_CancelSteps_RealEnumValues(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
listRec := doEMRRequest(t, h, "ListSteps", map[string]any{"ClusterId": create.JobFlowID})
require.Equal(t, http.StatusOK, listRec.Code)
@@ -144,7 +146,7 @@ func TestWireShape_CancelSteps_RealEnumValues(t *testing.T) {
ID string `json:"Id"`
} `json:"Steps"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.NotEmpty(t, listOut.Steps)
stepID := listOut.Steps[0].ID
@@ -162,7 +164,7 @@ func TestWireShape_CancelSteps_RealEnumValues(t *testing.T) {
Status string `json:"Status"`
} `json:"CancelStepsInfoList"`
}
- require.NoError(t, json.Unmarshal(firstRec.Body.Bytes(), &firstOut))
+ require.NoError(t, json.Unmarshal(firstRec.Body.Bytes(), &firstOut, jsonx.V1()))
require.Len(t, firstOut.CancelStepsInfoList, 1)
assert.Equal(t, "SUBMITTED", firstOut.CancelStepsInfoList[0].Status)
@@ -180,7 +182,7 @@ func TestWireShape_CancelSteps_RealEnumValues(t *testing.T) {
Reason string `json:"Reason"`
} `json:"CancelStepsInfoList"`
}
- require.NoError(t, json.Unmarshal(secondRec.Body.Bytes(), &secondOut))
+ require.NoError(t, json.Unmarshal(secondRec.Body.Bytes(), &secondOut, jsonx.V1()))
require.Len(t, secondOut.CancelStepsInfoList, 1)
assert.Equal(t, "FAILED", secondOut.CancelStepsInfoList[0].Status)
assert.NotEmpty(t, secondOut.CancelStepsInfoList[0].Reason)
@@ -207,7 +209,7 @@ func TestWireShape_NotebookExecution_EpochTimestamps(t *testing.T) {
var started struct {
NotebookExecutionID string `json:"NotebookExecutionId"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &started))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &started, jsonx.V1()))
descRec := doEMRRequest(t, h, "DescribeNotebookExecution", map[string]any{
"NotebookExecutionId": started.NotebookExecutionID,
@@ -219,7 +221,7 @@ func TestWireShape_NotebookExecution_EpochTimestamps(t *testing.T) {
StartTime float64 `json:"StartTime"`
} `json:"NotebookExecution"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
assert.NotZero(t, out.NotebookExecution.StartTime)
}
@@ -239,7 +241,7 @@ func TestWireShape_GetOnClusterAppUIPresignedURL_FieldName(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
rec := doEMRRequest(t, h, "GetOnClusterAppUIPresignedURL", map[string]any{"ClusterId": create.JobFlowID})
require.Equal(t, http.StatusOK, rec.Code)
@@ -248,13 +250,13 @@ func TestWireShape_GetOnClusterAppUIPresignedURL_FieldName(t *testing.T) {
PresignedURL string `json:"PresignedURL"`
PresignedURLReady bool `json:"PresignedURLReady"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.PresignedURL)
assert.True(t, out.PresignedURLReady)
// The old, wrong field name must not be the only thing populated.
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
assert.NotContains(t, raw, "URL")
}
@@ -274,7 +276,7 @@ func TestWireShape_GetPersistentAppUIPresignedURL_ReadyFlag(t *testing.T) {
PresignedURL string `json:"PresignedURL"`
PresignedURLReady bool `json:"PresignedURLReady"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.PresignedURL)
assert.True(t, out.PresignedURLReady)
}
@@ -297,7 +299,7 @@ func TestWireShape_RunJobFlow_JobFlowRole(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &create, jsonx.V1()))
descRec := doEMRRequest(t, h, "DescribeCluster", map[string]any{"ClusterId": create.JobFlowID})
require.Equal(t, http.StatusOK, descRec.Code)
@@ -309,7 +311,7 @@ func TestWireShape_RunJobFlow_JobFlowRole(t *testing.T) {
} `json:"Ec2InstanceAttributes"`
} `json:"Cluster"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "EMR_EC2_DefaultRole", out.Cluster.Ec2InstanceAttributes.IamInstanceProfile)
}
@@ -338,7 +340,7 @@ func TestWireShape_RunJobFlow_InlinePolicies(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &create, jsonx.V1()))
msRec := doEMRRequest(t, h, "GetManagedScalingPolicy", map[string]any{"ClusterId": create.JobFlowID})
require.Equal(t, http.StatusOK, msRec.Code)
@@ -350,7 +352,7 @@ func TestWireShape_RunJobFlow_InlinePolicies(t *testing.T) {
} `json:"ComputeLimits"`
} `json:"ManagedScalingPolicy"`
}
- require.NoError(t, json.Unmarshal(msRec.Body.Bytes(), &msOut))
+ require.NoError(t, json.Unmarshal(msRec.Body.Bytes(), &msOut, jsonx.V1()))
assert.Equal(t, 10, msOut.ManagedScalingPolicy.ComputeLimits.MaximumCapacityUnits)
atRec := doEMRRequest(t, h, "GetAutoTerminationPolicy", map[string]any{"ClusterId": create.JobFlowID})
@@ -361,7 +363,7 @@ func TestWireShape_RunJobFlow_InlinePolicies(t *testing.T) {
IdleTimeout int64 `json:"IdleTimeout"`
} `json:"AutoTerminationPolicy"`
}
- require.NoError(t, json.Unmarshal(atRec.Body.Bytes(), &atOut))
+ require.NoError(t, json.Unmarshal(atRec.Body.Bytes(), &atOut, jsonx.V1()))
assert.Equal(t, int64(3600), atOut.AutoTerminationPolicy.IdleTimeout)
}
@@ -382,13 +384,13 @@ func TestWireShape_GetManagedScalingPolicy_OmitsFieldWhenUnset(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
rec := doEMRRequest(t, h, "GetManagedScalingPolicy", map[string]any{"ClusterId": create.JobFlowID})
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
assert.NotContains(t, raw, "ManagedScalingPolicy")
}
@@ -406,13 +408,13 @@ func TestWireShape_GetAutoTerminationPolicy_OmitsFieldWhenUnset(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &create, jsonx.V1()))
rec := doEMRRequest(t, h, "GetAutoTerminationPolicy", map[string]any{"ClusterId": create.JobFlowID})
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
assert.NotContains(t, raw, "AutoTerminationPolicy")
}
@@ -436,7 +438,7 @@ func TestWireShape_AddTags_StudioResource(t *testing.T) {
var created struct {
StudioID string `json:"StudioId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
addRec := doEMRRequest(t, h, "AddTags", map[string]any{
"ResourceId": created.StudioID,
@@ -453,7 +455,7 @@ func TestWireShape_AddTags_StudioResource(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.Tags, 1)
assert.Equal(t, "env", listOut.Tags[0].Key)
assert.Equal(t, "prod", listOut.Tags[0].Value)
@@ -470,7 +472,7 @@ func TestWireShape_AddTags_StudioResource(t *testing.T) {
var listOut2 struct {
Tags []struct{} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listOut2))
+ require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listOut2, jsonx.V1()))
assert.Empty(t, listOut2.Tags)
}
@@ -501,7 +503,7 @@ func TestWireShape_RunJobFlow_InstanceFleets(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &create, jsonx.V1()))
descRec := doEMRRequest(t, h, "DescribeCluster", map[string]any{"ClusterId": create.JobFlowID})
require.Equal(t, http.StatusOK, descRec.Code)
@@ -511,7 +513,7 @@ func TestWireShape_RunJobFlow_InstanceFleets(t *testing.T) {
InstanceCollectionType string `json:"InstanceCollectionType"`
} `json:"Cluster"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, "INSTANCE_FLEET", descOut.Cluster.InstanceCollectionType)
listRec := doEMRRequest(t, h, "ListInstanceFleets", map[string]any{"ClusterId": create.JobFlowID})
@@ -523,7 +525,7 @@ func TestWireShape_RunJobFlow_InstanceFleets(t *testing.T) {
TargetOnDemandCapacity int `json:"TargetOnDemandCapacity"`
} `json:"InstanceFleets"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.InstanceFleets, 1)
assert.Equal(t, "primary", listOut.InstanceFleets[0].Name)
assert.Equal(t, 1, listOut.InstanceFleets[0].TargetOnDemandCapacity)
@@ -553,7 +555,7 @@ func TestWireShape_RunJobFlow_KerberosAttributesAndPlacementGroups(t *testing.T)
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &create, jsonx.V1()))
descRec := doEMRRequest(t, h, "DescribeCluster", map[string]any{"ClusterId": create.JobFlowID})
require.Equal(t, http.StatusOK, descRec.Code)
@@ -569,7 +571,7 @@ func TestWireShape_RunJobFlow_KerberosAttributesAndPlacementGroups(t *testing.T)
} `json:"PlacementGroups"`
} `json:"Cluster"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "EC2.INTERNAL", out.Cluster.KerberosAttributes.Realm)
require.Len(t, out.Cluster.PlacementGroups, 1)
assert.Equal(t, "MASTER", out.Cluster.PlacementGroups[0].InstanceRole)
@@ -607,7 +609,7 @@ func TestWireShape_RunJobFlow_OptionalClusterFields(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &create, jsonx.V1()))
descRec := doEMRRequest(t, h, "DescribeCluster", map[string]any{"ClusterId": create.JobFlowID})
require.Equal(t, http.StatusOK, descRec.Code)
@@ -629,7 +631,7 @@ func TestWireShape_RunJobFlow_OptionalClusterFields(t *testing.T) {
} `json:"MonitoringConfiguration"`
} `json:"Cluster"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "arn:aws:kms:us-east-1:123456789012:key/abc", out.Cluster.LogEncryptionKmsKeyID)
assert.Equal(t, "NONE", out.Cluster.RepoUpgradeOnBoot)
@@ -664,7 +666,7 @@ func TestWireShape_RunJobFlow_AutoTerminate(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &create, jsonx.V1()))
descRec := doEMRRequest(t, h, "DescribeCluster", map[string]any{"ClusterId": create.JobFlowID})
require.Equal(t, http.StatusOK, descRec.Code)
@@ -675,7 +677,7 @@ func TestWireShape_RunJobFlow_AutoTerminate(t *testing.T) {
KeepJobFlowAliveWhenNoSteps bool `json:"KeepJobFlowAliveWhenNoSteps"`
} `json:"Cluster"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
assert.True(t, out.Cluster.KeepJobFlowAliveWhenNoSteps)
assert.False(t, out.Cluster.AutoTerminate)
}
@@ -702,7 +704,7 @@ func TestWireShape_ListClusters_NoFabricatedReleaseLabel(t *testing.T) {
var raw struct {
Clusters []map[string]any `json:"Clusters"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &raw, jsonx.V1()))
require.Len(t, raw.Clusters, 1)
_, hasReleaseLabel := raw.Clusters[0]["ReleaseLabel"]
diff --git a/services/emr/janitor_test.go b/services/emr/janitor_test.go
index d82b23e24b..4a9d0ed2ac 100644
--- a/services/emr/janitor_test.go
+++ b/services/emr/janitor_test.go
@@ -2,7 +2,7 @@ package emr_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"testing/synctest"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emr"
)
@@ -250,7 +251,7 @@ func TestEMR_Handler_Reset(t *testing.T) {
Clusters []any `json:"Clusters"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.Clusters)
}
diff --git a/services/emr/kerberos_secrets_test.go b/services/emr/kerberos_secrets_test.go
index a5fe937663..572839fd5b 100644
--- a/services/emr/kerberos_secrets_test.go
+++ b/services/emr/kerberos_secrets_test.go
@@ -1,12 +1,14 @@
package emr_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDescribeCluster_KerberosAttributesOmitPasswords(t *testing.T) {
@@ -41,7 +43,7 @@ func TestDescribeCluster_KerberosAttributesOmitPasswords(t *testing.T) {
var create struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &create))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &create, jsonx.V1()))
desc := doEMRRequest(t, h, "DescribeCluster", map[string]any{"ClusterId": create.JobFlowID})
require.Equal(t, http.StatusOK, desc.Code)
@@ -51,7 +53,7 @@ func TestDescribeCluster_KerberosAttributesOmitPasswords(t *testing.T) {
KerberosAttributes map[string]any `json:"KerberosAttributes"`
} `json:"Cluster"`
}
- require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &out, jsonx.V1()))
ka := out.Cluster.KerberosAttributes
assert.Equal(t, "EC2.INTERNAL", ka["Realm"])
diff --git a/services/emr/models.go b/services/emr/models.go
index 79d00e007c..06bff5463d 100644
--- a/services/emr/models.go
+++ b/services/emr/models.go
@@ -1,8 +1,11 @@
package emr
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// regionContextKey is the context key under which the per-request AWS region is stored.
@@ -585,13 +588,13 @@ type KerberosAttributes struct {
func (k KerberosAttributes) MarshalJSON() ([]byte, error) {
type plain KerberosAttributes
- raw, err := json.Marshal(plain(k))
+ raw, err := json.Marshal(plain(k), jsonx.V1())
if err != nil {
return nil, err
}
- var fields map[string]json.RawMessage
- if err = json.Unmarshal(raw, &fields); err != nil {
+ var fields map[string]jsontext.Value
+ if err = json.Unmarshal(raw, &fields, jsonx.V1()); err != nil {
return nil, err
}
@@ -599,7 +602,7 @@ func (k KerberosAttributes) MarshalJSON() ([]byte, error) {
delete(fields, secret)
}
- return json.Marshal(fields)
+ return json.Marshal(fields, jsonx.V1())
}
// PlacementGroupConfig is the placement group configuration for a single
diff --git a/services/emr/persistence.go b/services/emr/persistence.go
index 0773a53a33..a7abc31f6a 100644
--- a/services/emr/persistence.go
+++ b/services/emr/persistence.go
@@ -2,7 +2,7 @@ package emr
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"slices"
"time"
@@ -89,7 +89,7 @@ func regionOnlyDTOKeyFn[V any](d *regionOnlyDTO[V]) string { return d.Region }
// guards against decoding a snapshot from an incompatible (older or newer)
// build of this backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ArnIndex map[string]map[string]string `json:"arnIndex"`
AccountID string `json:"accountID"`
Region string `json:"region"`
@@ -321,7 +321,7 @@ func unwrapRegionOnlyDTOs[V any](dtos *store.Table[regionOnlyDTO[V]], setRegion
// restoreResourceTables rebuilds every store.Table on b from snap's
// per-table JSON, factored out of Restore to keep Restore's own cognitive
// complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
dtos := buildPersistenceDTORegistry()
if err := dtos.registry.RestoreAll(tables); err != nil {
diff --git a/services/emr/request_validation_test.go b/services/emr/request_validation_test.go
index 2364c2b1dc..fbaf8d943b 100644
--- a/services/emr/request_validation_test.go
+++ b/services/emr/request_validation_test.go
@@ -1,12 +1,14 @@
package emr_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestEMR_RequestValidation(t *testing.T) {
@@ -78,13 +80,13 @@ func TestEMR_RequestValidation(t *testing.T) {
var out struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(created.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(created.Body.Bytes(), &out, jsonx.V1()))
rec := doEMRRequest(t, h, tt.action, tt.body(out.JobFlowID))
require.Equal(t, http.StatusBadRequest, rec.Code)
var errOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errOut, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", errOut["__type"])
assert.Contains(t, errOut["message"], tt.wantMsg)
assert.NotContains(t, errOut["message"], "Exception:")
diff --git a/services/emr/security_configurations.go b/services/emr/security_configurations.go
index 929463d5e7..19d5e22f80 100644
--- a/services/emr/security_configurations.go
+++ b/services/emr/security_configurations.go
@@ -2,12 +2,13 @@ package emr
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"sort"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -63,7 +64,7 @@ func (b *InMemoryBackend) CreateSecurityConfiguration(
return nil, fmt.Errorf("%w: Name is required", ErrValidation)
}
- if !json.Valid([]byte(securityConfig)) {
+ if !jsontext.Value([]byte(securityConfig)).IsValid(jsonx.V1()) {
return nil, fmt.Errorf("%w: SecurityConfiguration must be valid JSON", ErrValidation)
}
diff --git a/services/emr/validation.go b/services/emr/validation.go
index ad620798de..e253b80477 100644
--- a/services/emr/validation.go
+++ b/services/emr/validation.go
@@ -1,11 +1,12 @@
package emr
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"slices"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -62,7 +63,7 @@ func checkMarker(body []byte) error {
NextToken string `json:"NextToken"`
}
- if json.Unmarshal(body, &in) == nil {
+ if json.Unmarshal(body, &in, jsonx.V1()) == nil {
for _, tok := range []string{in.Marker, in.NextToken} {
if page.ValidateToken(tok) != nil {
return fmt.Errorf("%w: Invalid marker", ErrValidation)
diff --git a/services/emr/wire_field_fixes_test.go b/services/emr/wire_field_fixes_test.go
index f8efa367bb..cdfdcb8b84 100644
--- a/services/emr/wire_field_fixes_test.go
+++ b/services/emr/wire_field_fixes_test.go
@@ -1,7 +1,7 @@
package emr_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http/httptest"
"testing"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/emr"
)
@@ -250,7 +251,7 @@ func TestWireShape_Cluster_TerminatedAt_NotOnWire(t *testing.T) {
var created struct {
JobFlowID string `json:"JobFlowId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
termRec := doEMRRequest(t, h, "TerminateJobFlows", map[string]any{"JobFlowIds": []string{created.JobFlowID}})
require.Equal(t, 200, termRec.Code)
@@ -261,7 +262,7 @@ func TestWireShape_Cluster_TerminatedAt_NotOnWire(t *testing.T) {
var raw struct {
Cluster map[string]any `json:"Cluster"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw, jsonx.V1()))
_, hasTerminatedAt := raw.Cluster["TerminatedAt"]
assert.False(t, hasTerminatedAt,
"Cluster must not carry a TerminatedAt field on the wire -- real types.Cluster has no such member")
@@ -331,7 +332,7 @@ func TestWireShape_StudioSummary_NoFabricatedFields(t *testing.T) {
var raw struct {
Studios []map[string]any `json:"Studios"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &raw, jsonx.V1()))
require.Len(t, raw.Studios, 1)
_, hasArn := raw.Studios[0]["StudioArn"]
@@ -431,7 +432,7 @@ func TestWireShape_DescribePersistentAppUI_RealShape(t *testing.T) {
JobFlowID string `json:"JobFlowId"`
ClusterArn string `json:"ClusterArn"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cluster))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cluster, jsonx.V1()))
createUIRec := doEMRRequest(t, h, "CreatePersistentAppUI", map[string]any{
"TargetResourceArn": cluster.ClusterArn,
@@ -441,7 +442,7 @@ func TestWireShape_DescribePersistentAppUI_RealShape(t *testing.T) {
var createdUI struct {
PersistentAppUIID string `json:"PersistentAppUIId"`
}
- require.NoError(t, json.Unmarshal(createUIRec.Body.Bytes(), &createdUI))
+ require.NoError(t, json.Unmarshal(createUIRec.Body.Bytes(), &createdUI, jsonx.V1()))
descRec := doEMRRequest(t, h, "DescribePersistentAppUI", map[string]any{
"PersistentAppUIId": createdUI.PersistentAppUIID,
@@ -451,7 +452,7 @@ func TestWireShape_DescribePersistentAppUI_RealShape(t *testing.T) {
var raw struct {
PersistentAppUI map[string]any `json:"PersistentAppUI"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw, jsonx.V1()))
_, hasArn := raw.PersistentAppUI["TargetResourceArn"]
_, hasRole := raw.PersistentAppUI["RuntimeRoleEnabledCluster"]
diff --git a/services/emrserverless/handler.go b/services/emrserverless/handler.go
index 5adc5a7192..9c9a691adc 100644
--- a/services/emrserverless/handler.go
+++ b/services/emrserverless/handler.go
@@ -1,7 +1,7 @@
package emrserverless
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -16,6 +16,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
@@ -771,7 +772,7 @@ type createApplicationResponse struct {
func (h *Handler) handleCreateApplication(c *echo.Context, body []byte) error {
var in createApplicationBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("ValidationException", "invalid request body"))
}
}
@@ -879,7 +880,7 @@ func applyAutoConfigDefaults(extra map[string]any, whenAbsent bool) {
func (h *Handler) handleUpdateApplication(c *echo.Context, applicationID string, body []byte) error {
var in updateApplicationBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("ValidationException", "invalid request body"))
}
}
@@ -974,7 +975,7 @@ type startJobRunResponse struct {
func (h *Handler) handleStartJobRun(c *echo.Context, applicationID string, body []byte) error {
var in startJobRunBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("ValidationException", "invalid request body"))
}
}
@@ -1189,7 +1190,7 @@ type tagResourceBody struct {
func (h *Handler) handleTagResource(c *echo.Context, resourceARN string, body []byte) error {
var in tagResourceBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("ValidationException", "invalid request body"))
}
}
diff --git a/services/emrserverless/handler_applications_test.go b/services/emrserverless/handler_applications_test.go
index 65d9043c01..fa63b2c466 100644
--- a/services/emrserverless/handler_applications_test.go
+++ b/services/emrserverless/handler_applications_test.go
@@ -1,7 +1,7 @@
package emrserverless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emrserverless"
)
@@ -34,7 +35,7 @@ func createAppWithArch(t *testing.T, h *emrserverless.Handler, name, releaseLabe
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
id := out["applicationId"]
require.NotEmpty(t, id)
@@ -146,7 +147,7 @@ func TestHandler_CreateApplication_Architecture(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
app, _ := out["application"].(map[string]any)
if tt.wantInOutput {
diff --git a/services/emrserverless/handler_job_runs_test.go b/services/emrserverless/handler_job_runs_test.go
index ac555a3f0c..f39789287c 100644
--- a/services/emrserverless/handler_job_runs_test.go
+++ b/services/emrserverless/handler_job_runs_test.go
@@ -1,7 +1,7 @@
package emrserverless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emrserverless"
)
@@ -31,7 +32,7 @@ func startJobRunWithMode(t *testing.T, h *emrserverless.Handler, appID, mode str
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
id := out["jobRunId"]
require.NotEmpty(t, id)
@@ -151,7 +152,7 @@ func TestHandler_StartJobRun_DefaultsModeToBATCH(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
jr, _ := out["jobRun"].(map[string]any)
assert.Equal(t, tt.expectedMode, jr["mode"])
})
@@ -423,7 +424,7 @@ func TestHandler_GetJobRun_HasAttemptAndReleaseLabel(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
jr, _ := out["jobRun"].(map[string]any)
attempt, hasAttempt := jr["attempt"]
diff --git a/services/emrserverless/handler_list_summary_test.go b/services/emrserverless/handler_list_summary_test.go
index 8f5e77ae1f..b9fcf48111 100644
--- a/services/emrserverless/handler_list_summary_test.go
+++ b/services/emrserverless/handler_list_summary_test.go
@@ -1,7 +1,7 @@
package emrserverless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emrserverless"
)
@@ -144,7 +145,7 @@ func listSingleEMR(
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp[listKey].([]any)
require.True(t, ok, "%s response missing %q list", path, listKey)
diff --git a/services/emrserverless/handler_sessions_test.go b/services/emrserverless/handler_sessions_test.go
index da14757795..28fcb47b54 100644
--- a/services/emrserverless/handler_sessions_test.go
+++ b/services/emrserverless/handler_sessions_test.go
@@ -1,7 +1,7 @@
package emrserverless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emrserverless"
)
@@ -280,7 +281,7 @@ func TestHandler_SessionFilteringAndPersistence(t *testing.T) {
var filtered struct {
Sessions []map[string]any `json:"sessions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &filtered))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &filtered, jsonx.V1()))
require.Len(t, filtered.Sessions, 1)
assert.Equal(t, emrserverless.SessionStateTerminated, filtered.Sessions[0]["state"])
diff --git a/services/emrserverless/handler_test.go b/services/emrserverless/handler_test.go
index 4b8a071ab9..0a1195cbbf 100644
--- a/services/emrserverless/handler_test.go
+++ b/services/emrserverless/handler_test.go
@@ -2,7 +2,7 @@ package emrserverless_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/emrserverless"
)
@@ -29,7 +30,7 @@ func doRequest(t *testing.T, h *emrserverless.Handler, method, path string, body
var reqBody *bytes.Reader
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
reqBody = bytes.NewReader(b)
} else {
@@ -50,7 +51,7 @@ func doRequest(t *testing.T, h *emrserverless.Handler, method, path string, body
func mustUnmarshal(t *testing.T, rec *httptest.ResponseRecorder, v any) {
t.Helper()
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), v))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), v, jsonx.V1()))
}
// createApp is a test helper that creates an application and returns its ID.
@@ -623,7 +624,7 @@ func TestHandlerReset(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
apps := out["applications"].([]any)
assert.Empty(t, apps)
}
diff --git a/services/emrserverless/multi_region_test.go b/services/emrserverless/multi_region_test.go
index 77b70dd9a8..e3e3ef2f2a 100644
--- a/services/emrserverless/multi_region_test.go
+++ b/services/emrserverless/multi_region_test.go
@@ -2,7 +2,7 @@ package emrserverless_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emrserverless"
)
@@ -41,7 +42,7 @@ func decode(t *testing.T, body string) map[string]any {
out := map[string]any{}
if body != "" {
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
}
return out
diff --git a/services/emrserverless/persistence.go b/services/emrserverless/persistence.go
index fd464127c0..32b048a6fc 100644
--- a/services/emrserverless/persistence.go
+++ b/services/emrserverless/persistence.go
@@ -2,10 +2,12 @@ package emrserverless
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -33,7 +35,7 @@ const emrserverlessSnapshotVersion = 1
// against decoding a snapshot from an incompatible (older or newer) build of
// this backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
SessionTokens map[string]map[string]string `json:"sessionTokens"`
ApplicationTokens map[string]string `json:"applicationTokens"`
JobRunTokens map[string]map[string]string `json:"jobRunTokens"`
@@ -70,7 +72,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.region,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "emrserverless: Snapshot marshal failure", "error", err)
diff --git a/services/emrserverless/persistence_test.go b/services/emrserverless/persistence_test.go
index 582f2d9acf..e32e66121d 100644
--- a/services/emrserverless/persistence_test.go
+++ b/services/emrserverless/persistence_test.go
@@ -1,7 +1,7 @@
package emrserverless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/emrserverless"
)
@@ -168,10 +169,10 @@ func Test_Persistence_VersionMismatch(t *testing.T) {
require.NotNil(t, snap)
var raw map[string]any
- require.NoError(t, json.Unmarshal(snap, &raw))
+ require.NoError(t, json.Unmarshal(snap, &raw, jsonx.V1()))
tt.mutateSnap(t, raw)
- mutated, err := json.Marshal(raw)
+ mutated, err := json.Marshal(raw, jsonx.V1())
require.NoError(t, err)
// Restore into a backend that already holds unrelated state, to
diff --git a/services/emrserverless/request_validation_test.go b/services/emrserverless/request_validation_test.go
index a77b459cd0..3eca0f0c42 100644
--- a/services/emrserverless/request_validation_test.go
+++ b/services/emrserverless/request_validation_test.go
@@ -1,12 +1,14 @@
package emrserverless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestRequestValidation(t *testing.T) {
@@ -47,7 +49,7 @@ func TestRequestValidation(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out["code"])
assert.Contains(t, out["message"], tt.wantMsg)
assert.NotContains(t, out["message"], "Exception:")
@@ -73,7 +75,7 @@ func TestJobRunLifecycle(t *testing.T) {
State string `json:"state"`
} `json:"jobRun"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
got = append(got, out.JobRun.State)
}
diff --git a/services/emrserverless/session_handler.go b/services/emrserverless/session_handler.go
index f81f3daa86..3e2f9a0be5 100644
--- a/services/emrserverless/session_handler.go
+++ b/services/emrserverless/session_handler.go
@@ -1,7 +1,7 @@
package emrserverless
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
@@ -9,6 +9,8 @@ import (
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type startSessionBody struct {
@@ -23,7 +25,7 @@ type startSessionBody struct {
func (h *Handler) handleStartSession(c *echo.Context, applicationID string, body []byte) error {
var in startSessionBody
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errResp("ValidationException", "invalid request body"))
}
}
diff --git a/services/eventbridge/delivery.go b/services/eventbridge/delivery.go
index de2e349e1b..e5fd72cb07 100644
--- a/services/eventbridge/delivery.go
+++ b/services/eventbridge/delivery.go
@@ -2,7 +2,7 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"strconv"
@@ -11,6 +11,7 @@ import (
"time"
arnpkg "github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/google/uuid"
@@ -675,7 +676,7 @@ func sendToDLQ(
return
}
- payload, _ := json.Marshal(envelope)
+ payload, _ := json.Marshal(envelope, jsonx.V1())
var err error
if withAttrs, ok := dt.SQS.(SQSAttributeSender); ok {
@@ -732,7 +733,7 @@ func indexedRulesForEvent(
// buildEventEnvelope creates a JSON string representing the normalized event for pattern matching.
func buildEventEnvelope(entry EventEntry) string {
- b, _ := json.Marshal(buildEventEnvelopeMap(entry))
+ b, _ := json.Marshal(buildEventEnvelopeMap(entry), jsonx.V1())
return string(b)
}
@@ -760,7 +761,7 @@ func buildEventEnvelopeMap(entry EventEntry) map[string]any {
if entry.Detail != "" {
var detail map[string]any
- switch err := json.Unmarshal([]byte(entry.Detail), &detail); {
+ switch err := json.Unmarshal([]byte(entry.Detail), &detail, jsonx.V1()); {
case err != nil:
envelope["detail"] = entry.Detail
case detail == nil:
@@ -969,7 +970,7 @@ func buildPayload(target *Target, envelope map[string]any) string {
return applyInputTransformer(target.InputTransformer, envelope)
}
- b, _ := json.Marshal(envelope)
+ b, _ := json.Marshal(envelope, jsonx.V1())
return string(b)
}
@@ -987,7 +988,7 @@ func parseDeliveryDetail(entry EventEntry) any {
}
var d any
- if err := json.Unmarshal([]byte(entry.Detail), &d); err != nil {
+ if err := json.Unmarshal([]byte(entry.Detail), &d, jsonx.V1()); err != nil {
return entry.Detail
}
@@ -1157,7 +1158,7 @@ func entryFromEnvelope(envelope map[string]any, busName string) EventEntry {
}
if detail, ok := envelope["detail"]; ok {
- if b, err := json.Marshal(detail); err == nil {
+ if b, err := json.Marshal(detail, jsonx.V1()); err == nil {
entry.Detail = string(b)
}
}
diff --git a/services/eventbridge/delivery_api_destination.go b/services/eventbridge/delivery_api_destination.go
index 944e359411..952dedfd52 100644
--- a/services/eventbridge/delivery_api_destination.go
+++ b/services/eventbridge/delivery_api_destination.go
@@ -2,7 +2,7 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
"github.com/blackbirdworks/gopherstack/pkgs/httptarget"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -125,7 +126,7 @@ func mergeBodyParameters(payload string, params []ConnectionBodyParameter) strin
}
var obj map[string]any
- if err := json.Unmarshal([]byte(payload), &obj); err != nil || obj == nil {
+ if err := json.Unmarshal([]byte(payload), &obj, jsonx.V1()); err != nil || obj == nil {
return payload
}
@@ -133,7 +134,7 @@ func mergeBodyParameters(payload string, params []ConnectionBodyParameter) strin
obj[p.Key] = p.Value
}
- merged, err := json.Marshal(obj)
+ merged, err := json.Marshal(obj, jsonx.V1())
if err != nil {
return payload
}
@@ -253,7 +254,7 @@ func fetchOAuthToken(ctx context.Context, oauth *ResolvedOAuth) (string, error)
var tokenResp struct {
AccessToken string `json:"access_token"`
}
- if err = json.Unmarshal(data, &tokenResp); err != nil {
+ if err = json.Unmarshal(data, &tokenResp, jsonx.V1()); err != nil {
return "", err
}
if tokenResp.AccessToken == "" {
diff --git a/services/eventbridge/delivery_api_destination_test.go b/services/eventbridge/delivery_api_destination_test.go
index a1502b59c7..f49253a917 100644
--- a/services/eventbridge/delivery_api_destination_test.go
+++ b/services/eventbridge/delivery_api_destination_test.go
@@ -2,7 +2,7 @@ package eventbridge_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -333,7 +334,7 @@ func TestAPIDestinationDelivery_BodyMerge(t *testing.T) {
require.Len(t, reqs, 1)
var got map[string]any
- require.NoError(t, json.Unmarshal([]byte(reqs[0].body), &got))
+ require.NoError(t, json.Unmarshal([]byte(reqs[0].body), &got, jsonx.V1()))
assert.Equal(t, "world", got["hello"])
assert.Equal(t, "yes", got["injected"])
}
diff --git a/services/eventbridge/delivery_ssm_appsync.go b/services/eventbridge/delivery_ssm_appsync.go
index 2cd4275c7f..b5ccf0c0f6 100644
--- a/services/eventbridge/delivery_ssm_appsync.go
+++ b/services/eventbridge/delivery_ssm_appsync.go
@@ -2,12 +2,13 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"strings"
arnpkg "github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// arnParts is the field count of arn:partition:service:region:account:resource.
@@ -111,7 +112,7 @@ func BuildSSMCommandRequest(target *Target, payload string) (SSMCommandRequest,
var in ssmInput
- _ = json.Unmarshal([]byte(payload), &in)
+ _ = json.Unmarshal([]byte(payload), &in, jsonx.V1())
name := in.DocumentName
if name == "" {
@@ -136,7 +137,7 @@ func BuildSSMAutomationRequest(target *Target, payload string) SSMAutomationRequ
var raw map[string]any
- _ = json.Unmarshal([]byte(payload), &raw)
+ _ = json.Unmarshal([]byte(payload), &raw, jsonx.V1())
params := raw
if p, ok := raw["Parameters"].(map[string]any); ok {
@@ -197,7 +198,7 @@ func deliverToAppSync(ctx context.Context, target *Target, dt DeliveryTargets, p
var vars map[string]any
- if err := json.Unmarshal([]byte(payload), &vars); err != nil {
+ if err := json.Unmarshal([]byte(payload), &vars, jsonx.V1()); err != nil {
return logTargetErr(ctx, "AppSync", fmt.Errorf("payload is not a JSON object: %w", err))
}
diff --git a/services/eventbridge/delivery_test.go b/services/eventbridge/delivery_test.go
index 88a12d4266..1047c315c3 100644
--- a/services/eventbridge/delivery_test.go
+++ b/services/eventbridge/delivery_test.go
@@ -2,7 +2,7 @@ package eventbridge_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sync"
"testing"
"testing/synctest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -442,8 +443,8 @@ func TestDelivery_SharedEventIDAcrossTargets(t *testing.T) {
msg1 := sqsMock1.MessagesFor("arn:aws:sqs:us-east-1:000000000000:queue-a")[0]
msg2 := sqsMock2.MessagesFor("arn:aws:sqs:us-east-1:000000000000:queue-b")[0]
- require.NoError(t, json.Unmarshal([]byte(msg1), &id1))
- require.NoError(t, json.Unmarshal([]byte(msg2), &id2))
+ require.NoError(t, json.Unmarshal([]byte(msg1), &id1, jsonx.V1()))
+ require.NoError(t, json.Unmarshal([]byte(msg2), &id2, jsonx.V1()))
assert.NotEmpty(t, id1.ID)
assert.Equal(t, id1.ID, id2.ID, "all targets for the same rule+event must share the same event id")
})
diff --git a/services/eventbridge/delivery_transform.go b/services/eventbridge/delivery_transform.go
index 16b80cf33d..f056983f63 100644
--- a/services/eventbridge/delivery_transform.go
+++ b/services/eventbridge/delivery_transform.go
@@ -2,10 +2,13 @@ package eventbridge
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"regexp"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// inputPathsMapKeyRe validates InputPathsMap variable names per AWS spec.
@@ -19,7 +22,7 @@ func applyInputPath(path string, envelope map[string]any) string {
return ""
}
- b, _ := json.Marshal(val)
+ b, _ := json.Marshal(val, jsonx.V1())
return string(b)
}
@@ -175,9 +178,8 @@ func jsonEscapeInner(s string) string {
// mirrors AWS, which does not HTML-escape input-transformer substitutions.
func marshalNoHTMLEscape(v any) []byte {
var buf bytes.Buffer
- enc := json.NewEncoder(&buf)
- enc.SetEscapeHTML(false)
- if err := enc.Encode(v); err != nil {
+ enc := jsontext.NewEncoder(&buf, jsonx.V1(), jsontext.EscapeForHTML(false))
+ if err := json.MarshalEncode(enc, v); err != nil {
return nil
}
diff --git a/services/eventbridge/envelope_map_test.go b/services/eventbridge/envelope_map_test.go
index 473c3d1495..bdad4c8c5f 100644
--- a/services/eventbridge/envelope_map_test.go
+++ b/services/eventbridge/envelope_map_test.go
@@ -1,11 +1,13 @@
package eventbridge //nolint:testpackage // needs buildEventEnvelopeMap.
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestBuildEventEnvelopeMap_MatchesJSONRoundTrip(t *testing.T) {
@@ -36,7 +38,7 @@ func TestBuildEventEnvelopeMap_MatchesJSONRoundTrip(t *testing.T) {
t.Parallel()
var viaJSON map[string]any
- require.NoError(t, json.Unmarshal([]byte(buildEventEnvelope(tt.entry)), &viaJSON))
+ require.NoError(t, json.Unmarshal([]byte(buildEventEnvelope(tt.entry)), &viaJSON, jsonx.V1()))
require.Equal(t, viaJSON, buildEventEnvelopeMap(tt.entry))
})
}
diff --git a/services/eventbridge/event_buses.go b/services/eventbridge/event_buses.go
index 74bfc84bdc..e406502a61 100644
--- a/services/eventbridge/event_buses.go
+++ b/services/eventbridge/event_buses.go
@@ -2,12 +2,14 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"sort"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// CreateEventBusParams holds the input for CreateEventBus.
@@ -250,7 +252,7 @@ func (b *InMemoryBackend) PutPermission(ctx context.Context, input PutPermission
// If a raw Policy JSON is provided it replaces the whole policy.
if input.Policy != "" {
var doc eventBusPolicyDocument
- if err := json.Unmarshal([]byte(input.Policy), &doc); err == nil {
+ if err := json.Unmarshal([]byte(input.Policy), &doc, jsonx.V1()); err == nil {
policy.Statements = make(map[string]*EventBusPolicyStatement, len(doc.Statement))
for _, s := range doc.Statement {
policy.Statements[s.Sid] = s
@@ -342,7 +344,7 @@ func (b *InMemoryBackend) GetEventBusPolicy(ctx context.Context, eventBusName st
sort.Slice(stmts, func(i, j int) bool { return stmts[i].Sid < stmts[j].Sid })
- data, err := json.Marshal(eventBusPolicyDocument{Version: policyDocumentVersion, Statement: stmts})
+ data, err := json.Marshal(eventBusPolicyDocument{Version: policyDocumentVersion, Statement: stmts}, jsonx.V1())
if err != nil {
return "", err
}
@@ -386,7 +388,7 @@ func (b *InMemoryBackend) PutEventBusPolicy(ctx context.Context, input PutEventB
}
var doc eventBusPolicyDocument
- if err := json.Unmarshal([]byte(input.Policy), &doc); err != nil {
+ if err := json.Unmarshal([]byte(input.Policy), &doc, jsonx.V1()); err != nil {
return fmt.Errorf("%w: policy must be valid JSON: %w", ErrInvalidParameter, err)
}
diff --git a/services/eventbridge/event_buses_test.go b/services/eventbridge/event_buses_test.go
index 387225135f..022361a923 100644
--- a/services/eventbridge/event_buses_test.go
+++ b/services/eventbridge/event_buses_test.go
@@ -3,7 +3,7 @@ package eventbridge_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -166,7 +167,7 @@ func TestGetEventBusPolicy_IsPolicyDocumentNotBareArray(t *testing.T) {
Sid string `json:"Sid"`
} `json:"Statement"`
}
- require.NoError(t, json.Unmarshal([]byte(policy), &doc),
+ require.NoError(t, json.Unmarshal([]byte(policy), &doc, jsonx.V1()),
"Policy must be an IAM-style document (an object with a Statement array), not a bare array")
assert.Equal(t, "2012-10-17", doc.Version)
require.Len(t, doc.Statement, 1)
diff --git a/services/eventbridge/export_test.go b/services/eventbridge/export_test.go
index 6ccc6ac186..533bb5e5ba 100644
--- a/services/eventbridge/export_test.go
+++ b/services/eventbridge/export_test.go
@@ -2,9 +2,11 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// RegionContextKeyForTest exports the regionContextKey type for external tests.
@@ -30,7 +32,7 @@ func CompilePatternErrorForTest(pattern string) error {
// despite carrying a shape validation would normally reject.
func MatchCompiledPatternBypassingValidationForTest(patternJSON, event string) bool {
var pattern map[string]any
- if err := json.Unmarshal([]byte(patternJSON), &pattern); err != nil {
+ if err := json.Unmarshal([]byte(patternJSON), &pattern, jsonx.V1()); err != nil {
panic(err)
}
diff --git a/services/eventbridge/handler_api_destinations.go b/services/eventbridge/handler_api_destinations.go
index 22036fd6a9..1211f56aad 100644
--- a/services/eventbridge/handler_api_destinations.go
+++ b/services/eventbridge/handler_api_destinations.go
@@ -2,7 +2,9 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createAPIDestinationOutput struct {
@@ -19,7 +21,7 @@ func (h *Handler) apiDestinationActions() map[string]actionFn {
return map[string]actionFn{
"CreateApiDestination": func(ctx context.Context, b []byte) (any, error) {
var input CreateAPIDestinationInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
dst, err := h.Backend.CreateAPIDestination(ctx, input)
@@ -38,7 +40,7 @@ func (h *Handler) apiDestinationActions() map[string]actionFn {
var input struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.DeleteAPIDestination(ctx, input.Name); err != nil {
@@ -120,7 +122,7 @@ func (h *Handler) extendedAPIDestinationActions() map[string]actionFn {
var input struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -138,7 +140,7 @@ func (h *Handler) extendedAPIDestinationActions() map[string]actionFn {
NextToken string `json:"NextToken"`
Limit int `json:"Limit"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
dsts, next, err := h.Backend.ListAPIDestinations(
@@ -160,7 +162,7 @@ func (h *Handler) extendedAPIDestinationActions() map[string]actionFn {
},
"UpdateApiDestination": func(ctx context.Context, b []byte) (any, error) {
var input UpdateAPIDestinationInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
dst, err := h.Backend.UpdateAPIDestination(ctx, input)
diff --git a/services/eventbridge/handler_archives.go b/services/eventbridge/handler_archives.go
index fbade3a164..09f4d1303c 100644
--- a/services/eventbridge/handler_archives.go
+++ b/services/eventbridge/handler_archives.go
@@ -2,7 +2,9 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createArchiveOutput struct {
@@ -86,7 +88,7 @@ func (h *Handler) archiveActions() map[string]actionFn {
return map[string]actionFn{
"CreateArchive": func(ctx context.Context, b []byte) (any, error) {
var input CreateArchiveInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
archive, err := h.Backend.CreateArchive(ctx, input)
@@ -111,7 +113,7 @@ func (h *Handler) extendedArchiveActions() map[string]actionFn {
var input struct {
ArchiveName string `json:"ArchiveName"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -121,7 +123,7 @@ func (h *Handler) extendedArchiveActions() map[string]actionFn {
var input struct {
ArchiveName string `json:"ArchiveName"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -140,7 +142,7 @@ func (h *Handler) extendedArchiveActions() map[string]actionFn {
NextToken string `json:"NextToken"`
Limit int32 `json:"Limit"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
archives, next, err := h.Backend.ListArchives(
@@ -162,7 +164,7 @@ func (h *Handler) extendedArchiveActions() map[string]actionFn {
},
"UpdateArchive": func(ctx context.Context, b []byte) (any, error) {
var input UpdateArchiveInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
archive, err := h.Backend.UpdateArchive(ctx, input)
diff --git a/services/eventbridge/handler_connections.go b/services/eventbridge/handler_connections.go
index caa063a089..98ba355153 100644
--- a/services/eventbridge/handler_connections.go
+++ b/services/eventbridge/handler_connections.go
@@ -2,7 +2,9 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createConnectionOutput struct {
@@ -104,7 +106,7 @@ func (h *Handler) connectionActions() map[string]actionFn {
return map[string]actionFn{
"CreateConnection": func(ctx context.Context, b []byte) (any, error) {
var input CreateConnectionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
conn, err := h.Backend.CreateConnection(ctx, input)
@@ -123,7 +125,7 @@ func (h *Handler) connectionActions() map[string]actionFn {
var input struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
conn, err := h.Backend.DeauthorizeConnection(ctx, input.Name)
@@ -153,7 +155,7 @@ func (h *Handler) extendedConnectionActions() map[string]actionFn {
var input struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -163,7 +165,7 @@ func (h *Handler) extendedConnectionActions() map[string]actionFn {
var input struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -181,7 +183,7 @@ func (h *Handler) extendedConnectionActions() map[string]actionFn {
NextToken string `json:"NextToken"`
Limit int `json:"Limit"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
conns, next, err := h.Backend.ListConnections(
@@ -203,7 +205,7 @@ func (h *Handler) extendedConnectionActions() map[string]actionFn {
},
"UpdateConnection": func(ctx context.Context, b []byte) (any, error) {
var input UpdateConnectionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
conn, err := h.Backend.UpdateConnection(ctx, input)
diff --git a/services/eventbridge/handler_dispatch.go b/services/eventbridge/handler_dispatch.go
index 0bca911b97..3893f7c6c4 100644
--- a/services/eventbridge/handler_dispatch.go
+++ b/services/eventbridge/handler_dispatch.go
@@ -2,7 +2,7 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -15,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -346,7 +347,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var data map[string]any
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
@@ -440,7 +441,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(response)
+ return json.Marshal(response, jsonx.V1())
}
// handleError writes a standardized JSON error response.
@@ -509,7 +510,7 @@ func (h *Handler) handleError(
Message: stripExceptionPrefix(reqErr.Error()),
}
- payload, _ := json.Marshal(errResp)
+ payload, _ := json.Marshal(errResp, jsonx.V1())
return c.JSONBlob(statusCode, payload)
}
diff --git a/services/eventbridge/handler_endpoints.go b/services/eventbridge/handler_endpoints.go
index 3c60fa51a6..b09676c70c 100644
--- a/services/eventbridge/handler_endpoints.go
+++ b/services/eventbridge/handler_endpoints.go
@@ -2,7 +2,9 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// createEndpointOutput matches real CreateEndpointOutput
@@ -76,7 +78,7 @@ func (h *Handler) endpointActions() map[string]actionFn {
return map[string]actionFn{
"CreateEndpoint": func(ctx context.Context, b []byte) (any, error) {
var input CreateEndpointInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
ep, err := h.Backend.CreateEndpoint(ctx, input)
@@ -104,7 +106,7 @@ func (h *Handler) extendedEndpointActions() map[string]actionFn {
var input struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -114,7 +116,7 @@ func (h *Handler) extendedEndpointActions() map[string]actionFn {
var input struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
ep, err := h.Backend.DescribeEndpoint(ctx, input.Name)
@@ -130,7 +132,7 @@ func (h *Handler) extendedEndpointActions() map[string]actionFn {
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
eps, next, err := h.Backend.ListEndpoints(ctx, input.NamePrefix, input.NextToken, input.MaxResults)
@@ -150,7 +152,7 @@ func (h *Handler) extendedEndpointActions() map[string]actionFn {
},
"UpdateEndpoint": func(ctx context.Context, b []byte) (any, error) {
var input UpdateEndpointInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
ep, err := h.Backend.UpdateEndpoint(ctx, input)
diff --git a/services/eventbridge/handler_endpoints_test.go b/services/eventbridge/handler_endpoints_test.go
index 7a98ccc224..13d9735a85 100644
--- a/services/eventbridge/handler_endpoints_test.go
+++ b/services/eventbridge/handler_endpoints_test.go
@@ -1,7 +1,8 @@
package eventbridge_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -65,14 +67,14 @@ func TestHandler_Endpoint_TimestampsAreEpochSeconds(t *testing.T) {
rec = auditMakeRequest(t, h, e, "DescribeEndpoint", map[string]any{"Name": "epoch-endpoint"})
assert.Equal(t, http.StatusOK, rec.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
creationRaw, ok := raw["CreationTime"]
require.True(t, ok, "CreationTime must be present")
var f float64
- require.NoError(t, json.Unmarshal(creationRaw, &f),
+ require.NoError(t, json.Unmarshal(creationRaw, &f, jsonx.V1()),
"CreationTime must be a JSON number (epoch seconds), got: %s", string(creationRaw))
assert.Greater(t, f, float64(0))
}
diff --git a/services/eventbridge/handler_event_buses.go b/services/eventbridge/handler_event_buses.go
index b99b7274ed..97c52d4d1b 100644
--- a/services/eventbridge/handler_event_buses.go
+++ b/services/eventbridge/handler_event_buses.go
@@ -2,8 +2,9 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -116,7 +117,7 @@ func (h *Handler) eventBusActions() map[string]actionFn {
func (h *Handler) handleCreateEventBus(ctx context.Context, b []byte) (any, error) {
var input createEventBusInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
bus, err := h.Backend.CreateEventBus(ctx, CreateEventBusParams{
@@ -149,7 +150,7 @@ func (h *Handler) handleCreateEventBus(ctx context.Context, b []byte) (any, erro
func (h *Handler) handleDeleteEventBus(ctx context.Context, b []byte) (any, error) {
var input deleteEventBusInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
// Capture ARN before deletion so we can clean up tags.
@@ -166,7 +167,7 @@ func (h *Handler) handleDeleteEventBus(ctx context.Context, b []byte) (any, erro
func (h *Handler) handleListEventBuses(ctx context.Context, b []byte) (any, error) {
var input listEventBusesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
buses, next, err := h.Backend.ListEventBuses(ctx, input.NamePrefix, input.NextToken, input.Limit)
@@ -184,7 +185,7 @@ func (h *Handler) handleListEventBuses(ctx context.Context, b []byte) (any, erro
func (h *Handler) handleDescribeEventBus(ctx context.Context, b []byte) (any, error) {
var input describeEventBusInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
bus, err := h.Backend.DescribeEventBus(ctx, input.Name)
@@ -202,7 +203,7 @@ func (h *Handler) eventBusManagementActions() map[string]actionFn {
return map[string]actionFn{
"UpdateEventBus": func(ctx context.Context, b []byte) (any, error) {
var input UpdateEventBusInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
bus, err := h.Backend.UpdateEventBus(ctx, input)
@@ -228,7 +229,7 @@ func (h *Handler) eventBusManagementActions() map[string]actionFn {
},
"PutPermission": func(ctx context.Context, b []byte) (any, error) {
var input PutPermissionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -236,7 +237,7 @@ func (h *Handler) eventBusManagementActions() map[string]actionFn {
},
"RemovePermission": func(ctx context.Context, b []byte) (any, error) {
var input RemovePermissionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -249,7 +250,7 @@ func (h *Handler) policyActions() map[string]actionFn {
return map[string]actionFn{
"GetEventBusPolicy": func(ctx context.Context, b []byte) (any, error) {
var input GetEventBusPolicyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
policy, err := h.Backend.GetEventBusPolicy(ctx, input.EventBusName)
@@ -263,7 +264,7 @@ func (h *Handler) policyActions() map[string]actionFn {
},
"PutEventBusPolicy": func(ctx context.Context, b []byte) (any, error) {
var input PutEventBusPolicyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/eventbridge/handler_event_buses_test.go b/services/eventbridge/handler_event_buses_test.go
index 1c1de7b328..026915c7fc 100644
--- a/services/eventbridge/handler_event_buses_test.go
+++ b/services/eventbridge/handler_event_buses_test.go
@@ -2,7 +2,8 @@ package eventbridge_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -59,7 +61,7 @@ func TestHandler_DescribeEventBus_EchoesPolicy(t *testing.T) {
var describeResp struct {
Policy string `json:"Policy"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &describeResp, jsonx.V1()))
assert.Contains(t, describeResp.Policy, "allow-policy-echo")
rec = auditMakeRequest(t, h, e, "ListEventBuses", map[string]any{})
@@ -71,7 +73,7 @@ func TestHandler_DescribeEventBus_EchoesPolicy(t *testing.T) {
Policy string `json:"Policy"`
} `json:"EventBuses"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
found := false
for _, bus := range listResp.EventBuses {
if bus.Name == "default" {
@@ -97,14 +99,14 @@ func TestHandler_EventBus_TimestampsAreEpochSeconds(t *testing.T) {
rec := auditMakeRequest(t, h, e, "DescribeEventBus", map[string]any{})
assert.Equal(t, http.StatusOK, rec.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
creationRaw, ok := raw["CreationTime"]
require.True(t, ok, "CreationTime must be present")
var f float64
- require.NoError(t, json.Unmarshal(creationRaw, &f),
+ require.NoError(t, json.Unmarshal(creationRaw, &f, jsonx.V1()),
"CreationTime must be a JSON number (epoch seconds), got: %s", string(creationRaw))
assert.Greater(t, f, float64(0))
}
@@ -170,7 +172,7 @@ func TestCreateEventBus_TagsPersisted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
busArn := createResp["EventBusArn"]
require.NotEmpty(t, busArn)
@@ -183,7 +185,7 @@ func TestCreateEventBus_TagsPersisted(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1()))
got := make(map[string]string, len(tagsResp.Tags))
for _, kv := range tagsResp.Tags {
@@ -363,7 +365,7 @@ func TestHandler_CreateEventBus(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["EventBusArn"], tt.wantArnPart)
})
}
@@ -411,7 +413,7 @@ func TestHandler_CreateAndListEventBuses(t *testing.T) {
CreationTime float64 `json:"CreationTime"`
} `json:"EventBuses"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.GreaterOrEqual(t, len(resp.EventBuses), tt.wantMinCount)
})
}
diff --git a/services/eventbridge/handler_event_sources.go b/services/eventbridge/handler_event_sources.go
index 548a2f3546..16a6a9e789 100644
--- a/services/eventbridge/handler_event_sources.go
+++ b/services/eventbridge/handler_event_sources.go
@@ -2,7 +2,9 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type (
@@ -16,7 +18,7 @@ func (h *Handler) eventSourceActions() map[string]actionFn {
var input struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.ActivateEventSource(ctx, input.Name); err != nil {
@@ -29,7 +31,7 @@ func (h *Handler) eventSourceActions() map[string]actionFn {
var input struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.DeactivateEventSource(ctx, input.Name); err != nil {
@@ -84,7 +86,7 @@ func (h *Handler) extendedEventSourceActions() map[string]actionFn {
var input struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -101,7 +103,7 @@ func (h *Handler) extendedEventSourceActions() map[string]actionFn {
NextToken string `json:"NextToken"`
Limit int32 `json:"Limit"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
srcs, next, err := h.Backend.ListEventSources(ctx, input.NamePrefix, input.NextToken, int(input.Limit))
diff --git a/services/eventbridge/handler_event_sources_test.go b/services/eventbridge/handler_event_sources_test.go
index 1677ed7ab3..1f2298843a 100644
--- a/services/eventbridge/handler_event_sources_test.go
+++ b/services/eventbridge/handler_event_sources_test.go
@@ -1,7 +1,8 @@
package eventbridge_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -55,16 +57,16 @@ func TestDescribeEventSourceAndListEventSources_TimestampsAreEpochFloat(t *testi
rec := auditMakeRequest(t, h, e, tt.action, input)
require.Equal(t, http.StatusOK, rec.Code)
- var creationRaw json.RawMessage
+ var creationRaw jsontext.Value
if tt.action == "DescribeEventSource" {
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
creationRaw = raw["CreationTime"]
} else {
var raw struct {
- EventSources []map[string]json.RawMessage `json:"EventSources"`
+ EventSources []map[string]jsontext.Value `json:"EventSources"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
require.Len(t, raw.EventSources, 1)
creationRaw = raw.EventSources[0]["CreationTime"]
}
@@ -72,7 +74,7 @@ func TestDescribeEventSourceAndListEventSources_TimestampsAreEpochFloat(t *testi
require.NotNil(t, creationRaw, "CreationTime must be present")
var f float64
- err := json.Unmarshal(creationRaw, &f)
+ err := json.Unmarshal(creationRaw, &f, jsonx.V1())
require.NoError(t, err, "CreationTime must be a JSON number (epoch seconds), got: %s", string(creationRaw))
assert.Greater(t, f, float64(0))
})
diff --git a/services/eventbridge/handler_events.go b/services/eventbridge/handler_events.go
index c65385f31f..c65b5a08f4 100644
--- a/services/eventbridge/handler_events.go
+++ b/services/eventbridge/handler_events.go
@@ -2,7 +2,9 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type putEventsInput struct {
@@ -33,7 +35,7 @@ func (h *Handler) eventsActions() map[string]actionFn {
return map[string]actionFn{
"PutEvents": func(ctx context.Context, b []byte) (any, error) {
var input putEventsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
entries, err := h.Backend.PutEvents(ctx, input.Entries)
diff --git a/services/eventbridge/handler_events_test.go b/services/eventbridge/handler_events_test.go
index e1b4eee9ed..cb06d99614 100644
--- a/services/eventbridge/handler_events_test.go
+++ b/services/eventbridge/handler_events_test.go
@@ -1,7 +1,7 @@
package eventbridge_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -77,7 +78,7 @@ func TestHandler_PutEvents(t *testing.T) {
Entries []eventbridge.EventResultEntry `json:"Entries"`
FailedEntryCount int `json:"FailedEntryCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantFailedCount, resp.FailedEntryCount)
assert.Len(t, resp.Entries, tt.wantEntryCount)
for _, entry := range resp.Entries {
diff --git a/services/eventbridge/handler_partner_sources.go b/services/eventbridge/handler_partner_sources.go
index 011e423e3c..3000bcb2b3 100644
--- a/services/eventbridge/handler_partner_sources.go
+++ b/services/eventbridge/handler_partner_sources.go
@@ -2,8 +2,10 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createPartnerEventSourceOutput struct {
@@ -28,7 +30,7 @@ func (h *Handler) partnerSourceActions() map[string]actionFn {
Name string `json:"Name"`
Account string `json:"Account"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
src, err := h.Backend.CreatePartnerEventSource(ctx, input.Name, input.Account)
@@ -48,7 +50,7 @@ func (h *Handler) extendedPartnerSourceActions() map[string]actionFn {
var input struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -58,7 +60,7 @@ func (h *Handler) extendedPartnerSourceActions() map[string]actionFn {
var input struct {
Name string `json:"Name"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -70,7 +72,7 @@ func (h *Handler) extendedPartnerSourceActions() map[string]actionFn {
NextToken string `json:"NextToken"`
Limit int32 `json:"Limit"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
srcs, next, err := h.Backend.ListPartnerEventSources(
@@ -93,7 +95,7 @@ func (h *Handler) extendedPartnerSourceActions() map[string]actionFn {
EventSourceName string `json:"EventSourceName"`
NextToken string `json:"NextToken"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if input.EventSourceName == "" {
@@ -121,7 +123,7 @@ func (h *Handler) extendedPartnerSourceActions() map[string]actionFn {
},
"PutPartnerEvents": func(ctx context.Context, b []byte) (any, error) {
var input putEventsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
entries, err := h.Backend.PutPartnerEvents(ctx, input.Entries)
diff --git a/services/eventbridge/handler_registries.go b/services/eventbridge/handler_registries.go
index 34afc0567c..b2310f9d63 100644
--- a/services/eventbridge/handler_registries.go
+++ b/services/eventbridge/handler_registries.go
@@ -2,14 +2,16 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) registryActions() map[string]actionFn {
return map[string]actionFn{
opCreateRegistry: func(ctx context.Context, b []byte) (any, error) {
var input CreateRegistryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -19,7 +21,7 @@ func (h *Handler) registryActions() map[string]actionFn {
var input struct {
RegistryName string `json:"RegistryName"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -29,7 +31,7 @@ func (h *Handler) registryActions() map[string]actionFn {
var input struct {
RegistryName string `json:"RegistryName"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -41,7 +43,7 @@ func (h *Handler) registryActions() map[string]actionFn {
NextToken string `json:"NextToken"`
Limit int32 `json:"Limit"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
regs, next, err := h.Backend.ListRegistries(ctx, input.NamePrefix, input.NextToken, int(input.Limit))
@@ -56,7 +58,7 @@ func (h *Handler) registryActions() map[string]actionFn {
},
opUpdateRegistry: func(ctx context.Context, b []byte) (any, error) {
var input UpdateRegistryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/eventbridge/handler_replays.go b/services/eventbridge/handler_replays.go
index daf9164522..f73a113d89 100644
--- a/services/eventbridge/handler_replays.go
+++ b/services/eventbridge/handler_replays.go
@@ -2,7 +2,9 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type cancelReplayOutput struct {
@@ -85,7 +87,7 @@ func (h *Handler) replayActions() map[string]actionFn {
var input struct {
ReplayName string `json:"ReplayName"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
replay, err := h.Backend.CancelReplay(ctx, input.ReplayName)
@@ -109,7 +111,7 @@ func (h *Handler) extendedReplayActions() map[string]actionFn {
var input struct {
ReplayName string `json:"ReplayName"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
replay, err := h.Backend.DescribeReplay(ctx, input.ReplayName)
@@ -127,7 +129,7 @@ func (h *Handler) extendedReplayActions() map[string]actionFn {
NextToken string `json:"NextToken"`
Limit int32 `json:"Limit"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
replays, next, err := h.Backend.ListReplays(
@@ -149,7 +151,7 @@ func (h *Handler) extendedReplayActions() map[string]actionFn {
},
"StartReplay": func(ctx context.Context, b []byte) (any, error) {
var input StartReplayInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
replay, err := h.Backend.StartReplay(ctx, input)
diff --git a/services/eventbridge/handler_replays_test.go b/services/eventbridge/handler_replays_test.go
index 066f135ab5..0529a522a6 100644
--- a/services/eventbridge/handler_replays_test.go
+++ b/services/eventbridge/handler_replays_test.go
@@ -1,7 +1,8 @@
package eventbridge_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -82,14 +84,14 @@ func TestStartReplay_ReplayStartTimeIsEpochFloat(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
replayStartRaw, ok := raw["ReplayStartTime"]
require.True(t, ok, "ReplayStartTime must be present in StartReplay response")
var f float64
- err := json.Unmarshal(replayStartRaw, &f)
+ err := json.Unmarshal(replayStartRaw, &f, jsonx.V1())
require.NoError(
t,
err,
@@ -131,14 +133,14 @@ func TestHandler_DescribeReplay_TimestampsAreEpochSeconds(t *testing.T) {
rec = auditMakeRequest(t, h, e, "DescribeReplay", map[string]any{"ReplayName": "epoch-replay"})
require.Equal(t, http.StatusOK, rec.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
startRaw, ok := raw["EventStartTime"]
require.True(t, ok, "EventStartTime must be present")
var f float64
- require.NoError(t, json.Unmarshal(startRaw, &f),
+ require.NoError(t, json.Unmarshal(startRaw, &f, jsonx.V1()),
"EventStartTime must be a JSON number (epoch seconds), got: %s", string(startRaw))
assert.Greater(t, f, float64(0))
}
diff --git a/services/eventbridge/handler_rules.go b/services/eventbridge/handler_rules.go
index 9ac09598e0..f5322d17ae 100644
--- a/services/eventbridge/handler_rules.go
+++ b/services/eventbridge/handler_rules.go
@@ -2,8 +2,9 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -85,7 +86,7 @@ func (h *Handler) ruleActions() map[string]actionFn {
return map[string]actionFn{
"PutRule": func(ctx context.Context, b []byte) (any, error) {
var input PutRuleInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
rule, err := h.Backend.PutRule(ctx, input)
@@ -100,7 +101,7 @@ func (h *Handler) ruleActions() map[string]actionFn {
},
"DeleteRule": func(ctx context.Context, b []byte) (any, error) {
var input deleteRuleInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
// Capture ARN before deletion so we can clean up tags.
@@ -116,7 +117,7 @@ func (h *Handler) ruleActions() map[string]actionFn {
},
"ListRules": func(ctx context.Context, b []byte) (any, error) {
var input listRulesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
rules, next, err := h.Backend.ListRules(
@@ -139,7 +140,7 @@ func (h *Handler) ruleActions() map[string]actionFn {
},
"DescribeRule": func(ctx context.Context, b []byte) (any, error) {
var input describeRuleInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -158,7 +159,7 @@ func (h *Handler) ruleQueryActions() map[string]actionFn {
TargetArn string `json:"TargetArn"`
Limit int32 `json:"Limit"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
names, next, err := h.Backend.ListRuleNamesByTarget(
@@ -182,7 +183,7 @@ func (h *Handler) ruleQueryActions() map[string]actionFn {
Event string `json:"Event"`
EventPattern string `json:"EventPattern"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
result, err := h.Backend.TestEventPattern(ctx, input.EventPattern, input.Event)
@@ -201,7 +202,7 @@ func (h *Handler) ruleStateActions() map[string]actionFn {
return map[string]actionFn{
"EnableRule": func(ctx context.Context, b []byte) (any, error) {
var input enableRuleInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.EnableRule(ctx, input.Name, input.EventBusName); err != nil {
@@ -212,7 +213,7 @@ func (h *Handler) ruleStateActions() map[string]actionFn {
},
"DisableRule": func(ctx context.Context, b []byte) (any, error) {
var input disableRuleInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.DisableRule(ctx, input.Name, input.EventBusName); err != nil {
diff --git a/services/eventbridge/handler_rules_test.go b/services/eventbridge/handler_rules_test.go
index 515156884c..0b19c1e77f 100644
--- a/services/eventbridge/handler_rules_test.go
+++ b/services/eventbridge/handler_rules_test.go
@@ -2,7 +2,7 @@ package eventbridge_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -52,7 +53,7 @@ func TestHandler_TestEventPattern(t *testing.T) {
var resp struct {
Result bool `json:"Result"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.True(t, resp.Result)
}
@@ -124,7 +125,7 @@ func TestPutRule_TagsPersisted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var putResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
ruleArn := putResp["RuleArn"]
require.NotEmpty(t, ruleArn)
@@ -137,7 +138,7 @@ func TestPutRule_TagsPersisted(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1()))
got := make(map[string]string, len(tagsResp.Tags))
for _, kv := range tagsResp.Tags {
@@ -187,7 +188,7 @@ func TestDeleteRule_CleansUpTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var putOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putOut, jsonx.V1()))
ruleARN := putOut["RuleArn"]
require.NotEmpty(t, ruleARN)
@@ -248,7 +249,7 @@ func TestHandler_PutRuleAndListRules(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var putResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
assert.Contains(t, putResp["RuleArn"], tt.wantArnPart)
rec = makeRequestWithHandler(t, handler, e, "ListRules", `{}`)
@@ -257,7 +258,7 @@ func TestHandler_PutRuleAndListRules(t *testing.T) {
var listResp struct {
Rules []eventbridge.Rule `json:"Rules"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp.Rules, tt.wantRuleLen)
assert.Equal(t, tt.wantRuleName, listResp.Rules[0].Name)
})
@@ -300,7 +301,7 @@ func TestHandler_DescribeRule(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var rule eventbridge.Rule
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rule))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rule, jsonx.V1()))
assert.Equal(t, tt.wantRuleName, rule.Name)
assert.Equal(t, tt.wantDescription, rule.Description)
assert.Equal(t, tt.wantState, rule.State)
@@ -346,13 +347,13 @@ func TestHandler_EnableDisableRule(t *testing.T) {
rec = makeRequestWithHandler(t, handler, e, "DescribeRule", `{"Name":"`+tt.ruleName+`"}`)
var rule eventbridge.Rule
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rule))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rule, jsonx.V1()))
assert.Equal(t, tt.wantDisabledState, rule.State)
makeRequestWithHandler(t, handler, e, "EnableRule", `{"Name":"`+tt.ruleName+`"}`)
rec = makeRequestWithHandler(t, handler, e, "DescribeRule", `{"Name":"`+tt.ruleName+`"}`)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rule))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &rule, jsonx.V1()))
assert.Equal(t, tt.wantEnabledState, rule.State)
})
}
diff --git a/services/eventbridge/handler_schemas.go b/services/eventbridge/handler_schemas.go
index ede2802171..5de4d85f62 100644
--- a/services/eventbridge/handler_schemas.go
+++ b/services/eventbridge/handler_schemas.go
@@ -2,14 +2,16 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) schemaActions() map[string]actionFn {
return map[string]actionFn{
opCreateSchema: func(ctx context.Context, b []byte) (any, error) {
var input CreateSchemaInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -20,7 +22,7 @@ func (h *Handler) schemaActions() map[string]actionFn {
RegistryName string `json:"RegistryName"`
SchemaName string `json:"SchemaName"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -32,7 +34,7 @@ func (h *Handler) schemaActions() map[string]actionFn {
SchemaName string `json:"SchemaName"`
SchemaVersion string `json:"SchemaVersion"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -50,7 +52,7 @@ func (h *Handler) schemaActions() map[string]actionFn {
NextToken string `json:"NextToken"`
Limit int32 `json:"Limit"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
schemas, next, err := h.Backend.ListSchemas(
@@ -76,7 +78,7 @@ func (h *Handler) schemaActions() map[string]actionFn {
NextToken string `json:"NextToken"`
Limit int32 `json:"Limit"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
schemas, next, err := h.Backend.SearchSchemas(
@@ -97,7 +99,7 @@ func (h *Handler) schemaActions() map[string]actionFn {
},
opUpdateSchema: func(ctx context.Context, b []byte) (any, error) {
var input UpdateSchemaInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -115,7 +117,7 @@ func (h *Handler) schemaVersionActions() map[string]actionFn {
NextToken string `json:"NextToken"`
Limit int32 `json:"Limit"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
versions, next, err := h.Backend.ListSchemaVersions(
@@ -140,7 +142,7 @@ func (h *Handler) schemaVersionActions() map[string]actionFn {
SchemaName string `json:"SchemaName"`
SchemaVersion string `json:"SchemaVersion"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -157,7 +159,7 @@ func (h *Handler) schemaVersionActions() map[string]actionFn {
SchemaName string `json:"SchemaName"`
SchemaVersion string `json:"SchemaVersion"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -170,7 +172,7 @@ func (h *Handler) schemaVersionActions() map[string]actionFn {
},
opGetDiscoveredSchema: func(ctx context.Context, b []byte) (any, error) {
var input GetDiscoveredSchemaInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
content, err := h.Backend.GetDiscoveredSchema(ctx, input)
@@ -189,7 +191,7 @@ func (h *Handler) codeBindingActions() map[string]actionFn {
return map[string]actionFn{
opPutCodeBinding: func(ctx context.Context, b []byte) (any, error) {
var input PutCodeBindingInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -197,7 +199,7 @@ func (h *Handler) codeBindingActions() map[string]actionFn {
},
opDescribeCodeBinding: func(ctx context.Context, b []byte) (any, error) {
var input DescribeCodeBindingInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -205,7 +207,7 @@ func (h *Handler) codeBindingActions() map[string]actionFn {
},
"ListCodeBindings": func(ctx context.Context, b []byte) (any, error) {
var input ListCodeBindingsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
bindings, next, err := h.Backend.ListCodeBindings(ctx, input)
@@ -225,7 +227,7 @@ func (h *Handler) codeBindingActions() map[string]actionFn {
Language string `json:"Language"`
SchemaVersion string `json:"SchemaVersion"`
}
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
src, err := h.Backend.GetCodeBindingSource(
diff --git a/services/eventbridge/handler_schemas_rest.go b/services/eventbridge/handler_schemas_rest.go
index 88e722d682..1c630c26f5 100644
--- a/services/eventbridge/handler_schemas_rest.go
+++ b/services/eventbridge/handler_schemas_rest.go
@@ -2,7 +2,7 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// schemasRESTLimit parses the "limit" query param (schemas@v1.37.4
@@ -828,7 +829,7 @@ func (h *Handler) schemasRESTOps() map[string]schemasRESTOpFunc {
}
func (h *Handler) writeSchemasREST(c *echo.Context, body any) error {
- payload, err := json.Marshal(body)
+ payload, err := json.Marshal(body, jsonx.V1())
if err != nil {
return h.writeSchemasRESTError(c, err)
}
@@ -865,7 +866,7 @@ func (h *Handler) writeSchemasRESTError(c *echo.Context, err error) error {
c.Response().Header().Set("Content-Type", schemasRESTContentType)
c.Response().Header().Set("X-Amzn-Errortype", code)
- payload, marshalErr := json.Marshal(map[string]string{"message": err.Error()})
+ payload, marshalErr := json.Marshal(map[string]string{"message": err.Error()}, jsonx.V1())
if marshalErr != nil {
return c.String(http.StatusInternalServerError, "internal server error")
}
@@ -883,7 +884,7 @@ func schemasRESTDecodeBody(r *http.Request, out any) error {
return nil
}
- if uerr := json.Unmarshal(body, out); uerr != nil {
+ if uerr := json.Unmarshal(body, out, jsonx.V1()); uerr != nil {
return fmt.Errorf("%w: invalid JSON", ErrInvalidParameter)
}
diff --git a/services/eventbridge/handler_schemas_test.go b/services/eventbridge/handler_schemas_test.go
index ae12ef9e96..3c04bcc881 100644
--- a/services/eventbridge/handler_schemas_test.go
+++ b/services/eventbridge/handler_schemas_test.go
@@ -2,7 +2,7 @@ package eventbridge_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -233,7 +234,7 @@ func TestHandler_DeleteSchemaVersion_LastVersionReturns400(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
// Schema still exists and is intact.
diff --git a/services/eventbridge/handler_tags.go b/services/eventbridge/handler_tags.go
index 49086079d7..021d988159 100644
--- a/services/eventbridge/handler_tags.go
+++ b/services/eventbridge/handler_tags.go
@@ -2,9 +2,10 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -34,7 +35,7 @@ func (h *Handler) tagActions() map[string]actionFn {
return map[string]actionFn{
"ListTagsForResource": func(ctx context.Context, b []byte) (any, error) {
var input listTagsForResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.requireTaggable(ctx, input.ResourceARN); err != nil {
@@ -50,7 +51,7 @@ func (h *Handler) tagActions() map[string]actionFn {
},
"TagResource": func(ctx context.Context, b []byte) (any, error) {
var input tagResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.requireTaggable(ctx, input.ResourceARN); err != nil {
@@ -66,7 +67,7 @@ func (h *Handler) tagActions() map[string]actionFn {
},
"UntagResource": func(ctx context.Context, b []byte) (any, error) {
var input untagResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.requireTaggable(ctx, input.ResourceARN); err != nil {
diff --git a/services/eventbridge/handler_tags_test.go b/services/eventbridge/handler_tags_test.go
index a4b0f674ec..8f1a1d14bb 100644
--- a/services/eventbridge/handler_tags_test.go
+++ b/services/eventbridge/handler_tags_test.go
@@ -2,7 +2,7 @@ package eventbridge_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -107,7 +108,7 @@ func TestHandler_TagOperations(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp.Tags, tt.wantTagCount)
if len(tt.wantTags) > 0 {
diff --git a/services/eventbridge/handler_targets.go b/services/eventbridge/handler_targets.go
index c552bca5df..53c51b27eb 100644
--- a/services/eventbridge/handler_targets.go
+++ b/services/eventbridge/handler_targets.go
@@ -2,7 +2,9 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type putTargetsInput struct {
@@ -44,7 +46,7 @@ func (h *Handler) targetActions() map[string]actionFn {
return map[string]actionFn{
"PutTargets": func(ctx context.Context, b []byte) (any, error) {
var input putTargetsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
failed, err := h.Backend.PutTargets(ctx, input.Rule, input.EventBusName, input.Targets)
@@ -62,7 +64,7 @@ func (h *Handler) targetActions() map[string]actionFn {
},
"RemoveTargets": func(ctx context.Context, b []byte) (any, error) {
var input removeTargetsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
failed, err := h.Backend.RemoveTargetsForce(ctx, input.Rule, input.EventBusName, input.IDs, input.Force)
@@ -80,7 +82,7 @@ func (h *Handler) targetActions() map[string]actionFn {
},
"ListTargetsByRule": func(ctx context.Context, b []byte) (any, error) {
var input listTargetsByRuleInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
targets, next, err := h.Backend.ListTargetsByRule(
diff --git a/services/eventbridge/handler_targets_test.go b/services/eventbridge/handler_targets_test.go
index a3b746063a..350781e2e1 100644
--- a/services/eventbridge/handler_targets_test.go
+++ b/services/eventbridge/handler_targets_test.go
@@ -1,7 +1,7 @@
package eventbridge_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -87,7 +88,7 @@ func TestHandler_PutTargetsListAndRemove(t *testing.T) {
FailedEntries []eventbridge.FailedEntry `json:"FailedEntries"`
FailedEntryCount int `json:"FailedEntryCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
assert.Equal(t, 0, putResp.FailedEntryCount)
rec = makeRequestWithHandler(t, handler, e, "ListTargetsByRule", `{"Rule":"`+tt.ruleName+`"}`)
@@ -96,7 +97,7 @@ func TestHandler_PutTargetsListAndRemove(t *testing.T) {
var listResp struct {
Targets []eventbridge.Target `json:"Targets"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp.Targets, tt.wantInitialCount)
rec = makeRequestWithHandler(t, handler, e, "RemoveTargets",
@@ -104,7 +105,7 @@ func TestHandler_PutTargetsListAndRemove(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
rec = makeRequestWithHandler(t, handler, e, "ListTargetsByRule", `{"Rule":"`+tt.ruleName+`"}`)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp.Targets, tt.wantRemainingCount)
assert.Equal(t, tt.wantRemainingID, listResp.Targets[0].ID)
})
diff --git a/services/eventbridge/handler_test.go b/services/eventbridge/handler_test.go
index 7fbcbc31a2..c0d6b65a61 100644
--- a/services/eventbridge/handler_test.go
+++ b/services/eventbridge/handler_test.go
@@ -2,7 +2,7 @@ package eventbridge_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -104,7 +105,7 @@ func TestHandler_GetSupportedOperations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var ops []string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops, jsonx.V1()))
for _, op := range tt.wantOps {
assert.Contains(t, ops, op)
}
@@ -183,7 +184,7 @@ func TestHandler_DispatchErrors(t *testing.T) {
}
if tt.wantErrorType != "" {
var errResp service.JSONErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantErrorType, errResp.Type)
}
})
@@ -312,7 +313,7 @@ func auditMakeRequest(
body map[string]any,
) *httptest.ResponseRecorder {
t.Helper()
- b, _ := json.Marshal(body)
+ b, _ := json.Marshal(body, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(b)))
req.Header.Set("X-Amz-Target", "AmazonEventBridge."+action)
rec := httptest.NewRecorder()
@@ -759,6 +760,6 @@ func TestCancelReplay_TerminalStateMapping(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp service.JSONErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "IllegalStatusException", errResp.Type)
}
diff --git a/services/eventbridge/metrics.go b/services/eventbridge/metrics.go
index 96713e4fbb..bba9d09e7f 100644
--- a/services/eventbridge/metrics.go
+++ b/services/eventbridge/metrics.go
@@ -2,10 +2,11 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/cwmetric"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// AWS/Events rule metrics per docs.aws.amazon.com/eventbridge/latest/userguide/eb-monitoring.html.
@@ -48,7 +49,7 @@ func (b *InMemoryBackend) observePutEvents(
put("PutEventsEntriesCount", ebUnitCount, float64(len(entries)))
put("PutEventsLatency", ebUnitMillis, float64(elapsed.Milliseconds()))
- if body, mErr := json.Marshal(entries); mErr == nil {
+ if body, mErr := json.Marshal(entries, jsonx.V1()); mErr == nil {
put("PutEventsRequestSize", ebUnitBytes, float64(len(body)))
}
}
diff --git a/services/eventbridge/pattern.go b/services/eventbridge/pattern.go
index 79861834ff..338230779b 100644
--- a/services/eventbridge/pattern.go
+++ b/services/eventbridge/pattern.go
@@ -1,14 +1,14 @@
package eventbridge
import (
- "encoding/json"
- "errors"
+ "encoding/json/v2"
"fmt"
"reflect"
"slices"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/eventpattern"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -32,7 +32,7 @@ func compilePattern(patternJSON string) (*compiledPattern, error) {
}
var pattern map[string]any
- if err := json.Unmarshal([]byte(patternJSON), &pattern); err != nil {
+ if err := json.Unmarshal([]byte(patternJSON), &pattern, jsonx.V1()); err != nil {
return nil, err
}
@@ -268,7 +268,7 @@ func matchCompiledPattern(compiled *compiledPattern, event string) bool {
}
var eventData map[string]any
- if err := json.Unmarshal([]byte(event), &eventData); err != nil {
+ if err := json.Unmarshal([]byte(event), &eventData, jsonx.V1()); err != nil {
return false
}
@@ -682,12 +682,9 @@ func matchWildcard(pattern, s string) bool {
func invalidPatternErr(err error) error {
reason := "Invalid JSON"
- var syn *json.SyntaxError
- var typ *json.UnmarshalTypeError
-
switch {
- case errors.As(err, &syn):
- case errors.As(err, &typ):
+ case jsonx.IsSyntaxError(err):
+ case jsonx.IsTypeError(err):
reason = "Filter is not an object"
default:
reason = strings.TrimPrefix(err.Error(), ErrInvalidParameter.Error()+": ")
diff --git a/services/eventbridge/persistence.go b/services/eventbridge/persistence.go
index 5ae382cbee..3a0b97d122 100644
--- a/services/eventbridge/persistence.go
+++ b/services/eventbridge/persistence.go
@@ -2,11 +2,13 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strings"
"sync"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
@@ -59,7 +61,7 @@ const eventbridgeSnapshotVersion = 1
// no reason to leave bus policies unpersisted the way the genuinely
// unkeyable maps above are.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
BusPolicies map[string]map[string]*EventBusPolicy `json:"busPolicies,omitempty"`
AccountID string `json:"accountID"`
Region string `json:"region"`
@@ -93,7 +95,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.region,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).
WarnContext(ctx, "persistence: snapshot marshal failed", "service", "eventbridge", "error", err)
@@ -116,7 +118,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
// first "/" recovers the prefix, and tableAccessorsByPrefix's rules/targets
// entries split the remainder again themselves (region never contains "/",
// but bus/rule names may).
-func (b *InMemoryBackend) preRegisterSnapshotTables(tables map[string]json.RawMessage) {
+func (b *InMemoryBackend) preRegisterSnapshotTables(tables map[string]jsontext.Value) {
for key := range tables {
name, rest, found := strings.Cut(key, "/")
if !found {
@@ -168,7 +170,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
}
if snap.Tables == nil {
- snap.Tables = make(map[string]json.RawMessage)
+ snap.Tables = make(map[string]jsontext.Value)
}
b.preRegisterSnapshotTables(snap.Tables)
diff --git a/services/eventbridge/persistence_test.go b/services/eventbridge/persistence_test.go
index 43380bd71c..8bea5601fd 100644
--- a/services/eventbridge/persistence_test.go
+++ b/services/eventbridge/persistence_test.go
@@ -2,7 +2,7 @@ package eventbridge_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -309,7 +310,7 @@ func TestHandler_Snapshot_IncludesTags(t *testing.T) {
tagBody, err := json.Marshal(map[string]any{
"ResourceARN": tt.resourceARN,
"Tags": []map[string]string{{"Key": k, "Value": v}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := makeRequestWithHandler(t, h, e, "TagResource", string(tagBody))
require.Equal(t, http.StatusOK, rec.Code)
@@ -322,7 +323,7 @@ func TestHandler_Snapshot_IncludesTags(t *testing.T) {
fresh := eventbridge.NewHandler(eventbridge.NewInMemoryBackendWithConfig("123456789012", "us-east-1"))
require.NoError(t, fresh.Restore(t.Context(), snap))
- listBody, err := json.Marshal(map[string]string{"ResourceARN": tt.resourceARN})
+ listBody, err := json.Marshal(map[string]string{"ResourceARN": tt.resourceARN}, jsonx.V1())
require.NoError(t, err)
rec := makeRequestWithFreshHandler(t, fresh, "ListTagsForResource", string(listBody))
require.Equal(t, http.StatusOK, rec.Code)
@@ -333,7 +334,7 @@ func TestHandler_Snapshot_IncludesTags(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Tags, tt.wantTagCount)
if tt.wantTagCount > 0 {
@@ -387,7 +388,7 @@ func TestHandler_Reset_ClearsTags(t *testing.T) {
tagBody, err := json.Marshal(map[string]any{
"ResourceARN": tt.resourceARN,
"Tags": []map[string]string{{"Key": k, "Value": v}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := makeRequestWithHandler(t, h, e, "TagResource", string(tagBody))
require.Equal(t, http.StatusOK, rec.Code)
@@ -400,7 +401,7 @@ func TestHandler_Reset_ClearsTags(t *testing.T) {
`{"Name":"bleed-rule","EventPattern":"{\"source\":[\"test\"]}"}`)
require.Equal(t, http.StatusOK, put.Code)
- listBody, err := json.Marshal(map[string]string{"ResourceARN": tt.resourceARN})
+ listBody, err := json.Marshal(map[string]string{"ResourceARN": tt.resourceARN}, jsonx.V1())
require.NoError(t, err)
rec := makeRequestWithHandler(t, h, e, "ListTagsForResource", string(listBody))
require.Equal(t, http.StatusOK, rec.Code)
@@ -411,7 +412,7 @@ func TestHandler_Reset_ClearsTags(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp.Tags, "tags should be cleared after Reset()")
})
}
@@ -558,7 +559,7 @@ func TestHandlerReset(t *testing.T) {
var resp struct {
Tags []map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp.Tags)
}
diff --git a/services/eventbridge/put_events.go b/services/eventbridge/put_events.go
index 22bba2abe2..dc78b8173c 100644
--- a/services/eventbridge/put_events.go
+++ b/services/eventbridge/put_events.go
@@ -2,7 +2,7 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/apptrace"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// maxPutEventsEntries is AWS's per-request cap on PutEvents/PutPartnerEvents
@@ -298,7 +299,7 @@ func putEventsEntryBytes(e EventEntry) int {
// (MalformedDetail) or whose bus does not exist (ResourceNotFoundException).
func (b *InMemoryBackend) validatePutEventsTarget(region string, e EventEntry) (string, string, bool) {
var detail map[string]any
- if err := json.Unmarshal([]byte(e.Detail), &detail); err != nil {
+ if err := json.Unmarshal([]byte(e.Detail), &detail, jsonx.V1()); err != nil {
return "MalformedDetail", "Detail is malformed.", false
}
diff --git a/services/eventbridge/put_events_test.go b/services/eventbridge/put_events_test.go
index 5542c491e7..298cf66d82 100644
--- a/services/eventbridge/put_events_test.go
+++ b/services/eventbridge/put_events_test.go
@@ -2,7 +2,7 @@ package eventbridge_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -255,14 +256,14 @@ func TestPutEvents_FailedEntryCount(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- body, err := json.Marshal(map[string]any{"Entries": tt.entries})
+ body, err := json.Marshal(map[string]any{"Entries": tt.entries}, jsonx.V1())
require.NoError(t, err)
rec := makeRequest(t, tt.action, string(body))
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var got putEventsResult
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, tt.wantFailedCount, got.FailedEntryCount)
diff --git a/services/eventbridge/region_isolation_test.go b/services/eventbridge/region_isolation_test.go
index 90442cb985..522428e9c0 100644
--- a/services/eventbridge/region_isolation_test.go
+++ b/services/eventbridge/region_isolation_test.go
@@ -2,12 +2,14 @@ package eventbridge_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -230,17 +232,17 @@ func TestDefaultBus_RulesAndRestoreInSiblingRegion(t *testing.T) {
_, err = b.CreateEventBus(ctx, eventbridge.CreateEventBusParams{Name: "custom"})
require.NoError(t, err)
- var snap map[string]json.RawMessage
+ var snap map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(b.Snapshot(ctx), &snap))
+ require.NoError(t, json.Unmarshal(b.Snapshot(ctx), &snap, jsonx.V1()))
- var tables map[string]json.RawMessage
+ var tables map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(snap["tables"], &tables))
+ require.NoError(t, json.Unmarshal(snap["tables"], &tables, jsonx.V1()))
var buses []map[string]any
- require.NoError(t, json.Unmarshal(tables["buses/"+euRegion], &buses))
+ require.NoError(t, json.Unmarshal(tables["buses/"+euRegion], &buses, jsonx.V1()))
legacy := make([]map[string]any, 0, len(buses))
@@ -252,13 +254,13 @@ func TestDefaultBus_RulesAndRestoreInSiblingRegion(t *testing.T) {
require.Len(t, legacy, 1, "legacy snapshot keeps only the custom bus")
- tables["buses/"+euRegion], err = json.Marshal(legacy)
+ tables["buses/"+euRegion], err = json.Marshal(legacy, jsonx.V1())
require.NoError(t, err)
- snap["tables"], err = json.Marshal(tables)
+ snap["tables"], err = json.Marshal(tables, jsonx.V1())
require.NoError(t, err)
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
require.NoError(t, err)
restored := eventbridge.NewInMemoryBackend()
diff --git a/services/eventbridge/rule_arn_format_test.go b/services/eventbridge/rule_arn_format_test.go
index c183a9394f..70df82c680 100644
--- a/services/eventbridge/rule_arn_format_test.go
+++ b/services/eventbridge/rule_arn_format_test.go
@@ -1,7 +1,7 @@
package eventbridge_test
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -166,7 +167,7 @@ func legacySnapshot(t *testing.T, data []byte) []byte {
Tags map[string]map[string]string `json:"tags,omitempty"`
Backend []byte `json:"backend"`
}
- require.NoError(t, json.Unmarshal(data, &snap))
+ require.NoError(t, json.Unmarshal(data, &snap, jsonx.V1()))
modern := ruleARNPrefix + "r1"
legacy := ruleARNPrefix + "default/r1"
@@ -179,7 +180,7 @@ func legacySnapshot(t *testing.T, data []byte) []byte {
}
snap.Tags = tags
- out, err := json.Marshal(snap)
+ out, err := json.Marshal(snap, jsonx.V1())
require.NoError(t, err)
return out
diff --git a/services/eventbridge/rules.go b/services/eventbridge/rules.go
index fb40d36d9b..88cd327026 100644
--- a/services/eventbridge/rules.go
+++ b/services/eventbridge/rules.go
@@ -2,11 +2,13 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// checkManagedRule returns ManagedRuleException if rule is owned by an AWS
@@ -379,7 +381,7 @@ func (b *InMemoryBackend) TestEventPattern(
func isValidJSON(s string) bool {
var v any
- return json.Unmarshal([]byte(s), &v) == nil
+ return json.Unmarshal([]byte(s), &v, jsonx.V1()) == nil
}
var ruleNamePattern = regexp.MustCompile(`^[.\-_A-Za-z0-9]+$`)
diff --git a/services/eventbridge/ssm_integration.go b/services/eventbridge/ssm_integration.go
index 5619c70666..ed65a5017b 100644
--- a/services/eventbridge/ssm_integration.go
+++ b/services/eventbridge/ssm_integration.go
@@ -2,7 +2,9 @@ package eventbridge
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ssmParameterPolicyEventSource/DetailType are the real AWS wire values SSM
@@ -35,7 +37,7 @@ func (b *InMemoryBackend) NotifyParameterPolicyAction(
detail, err := json.Marshal(ssmParameterPolicyEventDetail{
ParameterName: parameterName,
PolicyType: policyType,
- })
+ }, jsonx.V1())
if err != nil {
return err
}
diff --git a/services/eventbridge/targets_test.go b/services/eventbridge/targets_test.go
index 550fa85f29..f67fe22b0e 100644
--- a/services/eventbridge/targets_test.go
+++ b/services/eventbridge/targets_test.go
@@ -2,7 +2,7 @@ package eventbridge_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"testing"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -698,7 +699,7 @@ func TestInputTransformer_ProducesValidJSON(t *testing.T) {
assert.JSONEq(t, tt.wantJSONEq, got)
}
if tt.wantValid {
- assert.True(t, json.Valid([]byte(got)), "expected valid JSON, got %q", got)
+ assert.True(t, jsontext.Value([]byte(got)).IsValid(jsonx.V1()), "expected valid JSON, got %q", got)
}
})
}
@@ -744,7 +745,7 @@ func TestInputTransformer_ValueContextEndToEnd(t *testing.T) {
}, 2*time.Second, 10*time.Millisecond)
msg := sqsMock.MessagesFor(queueARN)[0]
- assert.True(t, json.Valid([]byte(msg)), "delivered payload must be valid JSON: %q", msg)
+ assert.True(t, jsontext.Value([]byte(msg)).IsValid(jsonx.V1()), "delivered payload must be valid JSON: %q", msg)
assert.JSONEq(t, `{"wrapped":"it.svc"}`, msg)
}
diff --git a/services/eventbridge/wire_field_fixes_test.go b/services/eventbridge/wire_field_fixes_test.go
index e82774300c..d710a030bf 100644
--- a/services/eventbridge/wire_field_fixes_test.go
+++ b/services/eventbridge/wire_field_fixes_test.go
@@ -1,7 +1,7 @@
package eventbridge_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -387,7 +388,7 @@ func TestPutPermission_Condition_RoundTripsThroughPolicy(t *testing.T) {
Sid string `json:"Sid"`
} `json:"Statement"`
}
- require.NoError(t, json.Unmarshal([]byte(aws.ToString(described.Policy)), &doc))
+ require.NoError(t, json.Unmarshal([]byte(aws.ToString(described.Policy)), &doc, jsonx.V1()))
require.Len(t, doc.Statement, 1)
assert.Equal(t, "OrgGrant", doc.Statement[0].Sid)
require.NotEmpty(
diff --git a/services/eventbridge/wire_time.go b/services/eventbridge/wire_time.go
index c6f5bb243e..d92c19143d 100644
--- a/services/eventbridge/wire_time.go
+++ b/services/eventbridge/wire_time.go
@@ -1,8 +1,10 @@
package eventbridge
import (
- "encoding/json"
+ "encoding/json/v2"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// parseEpochSecondsPtr converts an AWS json-1.1 wire-format epoch-seconds
@@ -45,7 +47,7 @@ type wireEventEntry struct {
// quoted RFC3339 string.
func (e *EventEntry) UnmarshalJSON(data []byte) error {
var w wireEventEntry
- if err := json.Unmarshal(data, &w); err != nil {
+ if err := json.Unmarshal(data, &w, jsonx.V1()); err != nil {
return err
}
@@ -78,7 +80,7 @@ type wireStartReplayInput struct {
// struct-tag-driven json.Unmarshal.
func (in *StartReplayInput) UnmarshalJSON(data []byte) error {
var w wireStartReplayInput
- if err := json.Unmarshal(data, &w); err != nil {
+ if err := json.Unmarshal(data, &w, jsonx.V1()); err != nil {
return err
}
diff --git a/services/eventbridge/wire_time_test.go b/services/eventbridge/wire_time_test.go
index 905e95798f..09e2570d54 100644
--- a/services/eventbridge/wire_time_test.go
+++ b/services/eventbridge/wire_time_test.go
@@ -1,13 +1,14 @@
package eventbridge_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/eventbridge"
)
@@ -55,7 +56,7 @@ func TestEventEntry_UnmarshalJSON_TimeIsEpochSeconds(t *testing.T) {
t.Parallel()
var entry eventbridge.EventEntry
- err := json.Unmarshal([]byte(tt.body), &entry)
+ err := json.Unmarshal([]byte(tt.body), &entry, jsonx.V1())
if tt.wantErr {
require.Error(t, err)
@@ -97,7 +98,7 @@ func TestStartReplayInput_UnmarshalJSON_TimesAreEpochSeconds(t *testing.T) {
}`
var input eventbridge.StartReplayInput
- require.NoError(t, json.Unmarshal([]byte(body), &input))
+ require.NoError(t, json.Unmarshal([]byte(body), &input, jsonx.V1()))
assert.True(t, time.Unix(1_700_000_000, 0).UTC().Equal(input.EventStartTime))
assert.True(t, time.Unix(1_700_003_600, 0).UTC().Equal(input.EventEndTime))
diff --git a/services/firehose/delivery_http.go b/services/firehose/delivery_http.go
index 35e5b564f6..b8fb795af0 100644
--- a/services/firehose/delivery_http.go
+++ b/services/firehose/delivery_http.go
@@ -4,7 +4,8 @@ import (
"bytes"
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"mime"
@@ -17,6 +18,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -60,7 +62,10 @@ func buildHTTPEndpointBody(requestID string, records [][]byte) ([]byte, error) {
httpRecords = append(httpRecords, httpRecord{Data: base64.StdEncoding.EncodeToString(rec)})
}
- return json.Marshal(httpPayload{RequestID: requestID, Timestamp: time.Now().UnixMilli(), Records: httpRecords})
+ return json.Marshal(
+ httpPayload{RequestID: requestID, Timestamp: time.Now().UnixMilli(), Records: httpRecords},
+ jsonx.V1(),
+ )
}
// buildHTTPEndpointRequest constructs one POST with the documented Firehose request headers.
@@ -97,7 +102,7 @@ func buildHTTPEndpointRequest(
attrs[a.AttributeName] = a.AttributeValue
}
- if raw, mErr := json.Marshal(map[string]any{"commonAttributes": attrs}); mErr == nil {
+ if raw, mErr := json.Marshal(map[string]any{"commonAttributes": attrs}, jsonx.V1()); mErr == nil {
req.Header.Set("X-Amz-Firehose-Common-Attributes", string(raw))
}
}
@@ -219,10 +224,10 @@ func validateHTTPResponse(resp *http.Response, raw []byte, requestID string) err
}
var out struct {
- Timestamp *json.RawMessage `json:"timestamp"`
- RequestID string `json:"requestId"`
+ Timestamp *jsontext.Value `json:"timestamp"`
+ RequestID string `json:"requestId"`
}
- if err := json.Unmarshal(raw, &out); err != nil || out.RequestID != requestID || out.Timestamp == nil {
+ if err := json.Unmarshal(raw, &out, jsonx.V1()); err != nil || out.RequestID != requestID || out.Timestamp == nil {
return errHTTPResponse
}
@@ -233,7 +238,7 @@ func endpointErrorMessage(raw []byte, status int) string {
var out struct {
ErrorMessage string `json:"errorMessage"`
}
- if json.Unmarshal(raw, &out) == nil && out.ErrorMessage != "" {
+ if json.Unmarshal(raw, &out, jsonx.V1()) == nil && out.ErrorMessage != "" {
return out.ErrorMessage
}
diff --git a/services/firehose/delivery_opensearch.go b/services/firehose/delivery_opensearch.go
index 8d02b9fef4..1bb1bcd8ed 100644
--- a/services/firehose/delivery_opensearch.go
+++ b/services/firehose/delivery_opensearch.go
@@ -3,12 +3,13 @@ package firehose
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -151,7 +152,7 @@ func indexInProcess(idx OpenSearchIndexer, domainARN, index string, records [][]
for _, rec := range records {
var doc map[string]any
- if err := json.Unmarshal(rec, &doc); err != nil {
+ if err := json.Unmarshal(rec, &doc, jsonx.V1()); err != nil {
failed = append(
failed,
failureRecord(rec, "", "ES.JsonProcessingException", "record is not a JSON object", 1),
diff --git a/services/firehose/delivery_realism_helpers_test.go b/services/firehose/delivery_realism_helpers_test.go
index 17743a4df8..e89fd490aa 100644
--- a/services/firehose/delivery_realism_helpers_test.go
+++ b/services/firehose/delivery_realism_helpers_test.go
@@ -7,7 +7,7 @@ import (
"context"
"encoding/base64"
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"strings"
@@ -21,6 +21,7 @@ import (
"github.com/klauspost/compress/snappy"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/roleauth"
"github.com/blackbirdworks/gopherstack/services/firehose"
)
@@ -115,7 +116,7 @@ type transformEvent struct {
// metadata.partitionKeys{"tenant": }.
func contentTransform(payload []byte, _ int) ([]byte, error) {
var ev transformEvent
- if err := json.Unmarshal(payload, &ev); err != nil {
+ if err := json.Unmarshal(payload, &ev, jsonx.V1()); err != nil {
return nil, err
}
@@ -140,7 +141,7 @@ func contentTransform(payload []byte, _ int) ([]byte, error) {
}
}
- return json.Marshal(map[string]any{"records": recs})
+ return json.Marshal(map[string]any{"records": recs}, jsonx.V1())
}
// denyAuthorizer denies the listed actions; role "untrusted" cannot be assumed.
diff --git a/services/firehose/delivery_realism_test.go b/services/firehose/delivery_realism_test.go
index e1a22fc15c..cf555cf6f7 100644
--- a/services/firehose/delivery_realism_test.go
+++ b/services/firehose/delivery_realism_test.go
@@ -2,7 +2,8 @@ package firehose_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/firehose"
)
@@ -219,7 +221,7 @@ func TestS3Delivery_LambdaProcessorResults(t *testing.T) {
var ev transformEvent
require.Equal(t, 1, fn.callCount())
- require.NoError(t, json.Unmarshal(fn.payloads[0], &ev))
+ require.NoError(t, json.Unmarshal(fn.payloads[0], &ev, jsonx.V1()))
assert.NotEmpty(t, ev.InvocationID)
assert.Equal(t, rtTestRegion, ev.Region)
assert.Regexp(t, `^arn:aws:firehose:us-east-1:000000000000:deliverystream/proc$`, ev.DeliveryStreamARN)
@@ -234,7 +236,7 @@ func TestS3Delivery_LambdaProcessorResults(t *testing.T) {
require.Len(t, failed, 1)
var envlp map[string]string
- require.NoError(t, json.Unmarshal(failed[0].body, &envlp))
+ require.NoError(t, json.Unmarshal(failed[0].body, &envlp, jsonx.V1()))
assert.Equal(t, testLambdaARN, envlp["lambdaARN"])
assert.Equal(t, "ProcessingFailed", envlp["errorCode"])
assert.Equal(t, base64.StdEncoding.EncodeToString([]byte("fail-three")), envlp["rawData"])
@@ -422,7 +424,10 @@ func (e *endpoint) serve(t *testing.T) *httptest.Server {
func replyOK(w http.ResponseWriter, requestID string) {
w.Header().Set("Content-Type", "application/json")
- _ = json.NewEncoder(w).Encode(map[string]any{"requestId": requestID, "timestamp": time.Now().UnixMilli()})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]any{"requestId": requestID, "timestamp": time.Now().UnixMilli()},
+ )
}
func TestHTTPEndpoint_RequestContract(t *testing.T) {
@@ -468,7 +473,7 @@ func TestHTTPEndpoint_RequestContract(t *testing.T) {
} `json:"records"`
Timestamp int64 `json:"timestamp"`
}
- require.NoError(t, json.Unmarshal(call.body, &body))
+ require.NoError(t, json.Unmarshal(call.body, &body, jsonx.V1()))
assert.Equal(t, call.headers.Get("X-Amz-Firehose-Request-Id"), body.RequestID)
assert.Positive(t, body.Timestamp)
require.Len(t, body.Records, 2)
diff --git a/services/firehose/delivery_splunk.go b/services/firehose/delivery_splunk.go
index e3b0a906c3..40116d2f8e 100644
--- a/services/firehose/delivery_splunk.go
+++ b/services/firehose/delivery_splunk.go
@@ -3,11 +3,12 @@ package firehose
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -25,7 +26,7 @@ func buildSplunkBody(records [][]byte, hecType string) ([]byte, string) {
var buf bytes.Buffer
for _, rec := range records {
- line, marshalErr := json.Marshal(hecEvent{Event: string(rec)})
+ line, marshalErr := json.Marshal(hecEvent{Event: string(rec)}, jsonx.V1())
if marshalErr != nil {
continue
}
diff --git a/services/firehose/flush_test.go b/services/firehose/flush_test.go
index eeb823ce62..d3809037cf 100644
--- a/services/firehose/flush_test.go
+++ b/services/firehose/flush_test.go
@@ -3,7 +3,7 @@ package firehose_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/parquetio"
"github.com/blackbirdworks/gopherstack/services/firehose"
)
@@ -498,7 +499,7 @@ func TestHTTPEndpoint_DeliveryFormat_ViaHandler(t *testing.T) {
} `json:"records"`
Timestamp int64 `json:"timestamp"`
}
- require.NoError(t, json.Unmarshal(requests[0].body, &payload))
+ require.NoError(t, json.Unmarshal(requests[0].body, &payload, jsonx.V1()))
assert.NotEmpty(t, payload.RequestID)
assert.Positive(t, payload.Timestamp)
@@ -541,7 +542,7 @@ func TestOpenSearch_BulkNDJSON_ViaHandler(t *testing.T) {
require.GreaterOrEqual(t, len(lines), 2, "NDJSON must have at least action+document lines")
var action map[string]any
- require.NoError(t, json.Unmarshal([]byte(lines[0]), &action))
+ require.NoError(t, json.Unmarshal([]byte(lines[0]), &action, jsonx.V1()))
_, hasIndex := action["index"]
assert.True(t, hasIndex, "first NDJSON line must be an index action")
}
@@ -603,7 +604,7 @@ func TestSplunk_Modes_ViaHandler(t *testing.T) {
assert.Contains(t, string(requests[0].body), "splunk-raw-data")
} else {
var evtBody map[string]any
- require.NoError(t, json.Unmarshal(requests[0].body, &evtBody))
+ require.NoError(t, json.Unmarshal(requests[0].body, &evtBody, jsonx.V1()))
_, hasEvent := evtBody["event"]
assert.True(t, hasEvent, "Event mode must wrap data in {event:...}")
}
@@ -629,7 +630,7 @@ func okLambdaResponse(records ...struct{ id, data string }) []byte {
Data: base64.StdEncoding.EncodeToString([]byte(r.data)),
})
}
- b, _ := json.Marshal(out)
+ b, _ := json.Marshal(out, jsonx.V1())
return b
}
@@ -689,7 +690,7 @@ func TestLambdaTransform_AllDestinations(t *testing.T) {
Data string `json:"data"`
} `json:"records"`
}
- require.NoError(t, json.Unmarshal(srv.captured()[0].body, &payload))
+ require.NoError(t, json.Unmarshal(srv.captured()[0].body, &payload, jsonx.V1()))
require.Len(t, payload.Records, 1)
decoded, decErr := base64.StdEncoding.DecodeString(payload.Records[0].Data)
require.NoError(t, decErr)
diff --git a/services/firehose/formats.go b/services/firehose/formats.go
index e7fd0a2f54..4e8e0f3cdf 100644
--- a/services/firehose/formats.go
+++ b/services/firehose/formats.go
@@ -2,10 +2,12 @@ package firehose
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// DataFormatConversionConfig controls record-format conversion (JSON → Parquet/ORC).
@@ -168,7 +170,7 @@ func convertRecords(
// record is not a valid JSON object, which the caller treats as a conversion failure.
func deserializeRecord(deser *Deserializer, rec []byte) ([]byte, bool) {
var obj map[string]any
- if err := json.Unmarshal(bytes.TrimSpace(rec), &obj); err != nil {
+ if err := json.Unmarshal(bytes.TrimSpace(rec), &obj, jsonx.V1()); err != nil {
return nil, false
}
@@ -225,13 +227,13 @@ func marshalCanonical(obj map[string]any) ([]byte, error) {
if i > 0 {
buf.WriteByte(',')
}
- keyJSON, err := json.Marshal(k)
+ keyJSON, err := json.Marshal(k, jsonx.V1())
if err != nil {
return nil, err
}
buf.Write(keyJSON)
buf.WriteByte(':')
- valJSON, err := json.Marshal(obj[k])
+ valJSON, err := json.Marshal(obj[k], jsonx.V1())
if err != nil {
return nil, err
}
diff --git a/services/firehose/formats_infer_test.go b/services/firehose/formats_infer_test.go
index 6a411b08dc..06aa7cf168 100644
--- a/services/firehose/formats_infer_test.go
+++ b/services/firehose/formats_infer_test.go
@@ -1,11 +1,11 @@
package firehose_test
import (
- "encoding/json"
"testing"
"github.com/stretchr/testify/assert"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/firehose"
)
@@ -19,23 +19,23 @@ func TestInferColumnsWidens(t *testing.T) {
}{
{
name: "int then float", want: "double",
- objs: []map[string]any{{"c": json.Number("3")}, {"c": json.Number("3.5")}},
+ objs: []map[string]any{{"c": jsonx.Number("3")}, {"c": jsonx.Number("3.5")}},
},
{
name: "float then int", want: "double",
- objs: []map[string]any{{"c": json.Number("3.5")}, {"c": json.Number("3")}},
+ objs: []map[string]any{{"c": jsonx.Number("3.5")}, {"c": jsonx.Number("3")}},
},
{
name: "string then int", want: "string",
- objs: []map[string]any{{"c": "abc"}, {"c": json.Number("5")}},
+ objs: []map[string]any{{"c": "abc"}, {"c": jsonx.Number("5")}},
},
{
name: "int then string", want: "string",
- objs: []map[string]any{{"c": json.Number("5")}, {"c": "abc"}},
+ objs: []map[string]any{{"c": jsonx.Number("5")}, {"c": "abc"}},
},
{
name: "leading null", want: "bigint",
- objs: []map[string]any{{"c": nil}, {"c": json.Number("5")}},
+ objs: []map[string]any{{"c": nil}, {"c": jsonx.Number("5")}},
},
{
name: "only null", want: "string",
@@ -43,7 +43,7 @@ func TestInferColumnsWidens(t *testing.T) {
},
{
name: "bool and int", want: "string",
- objs: []map[string]any{{"c": true}, {"c": json.Number("1")}},
+ objs: []map[string]any{{"c": true}, {"c": jsonx.Number("1")}},
},
}
diff --git a/services/firehose/formats_parquet.go b/services/firehose/formats_parquet.go
index cd2929e0c3..3d030bdd7f 100644
--- a/services/firehose/formats_parquet.go
+++ b/services/firehose/formats_parquet.go
@@ -3,12 +3,14 @@ package firehose
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"sort"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/parquetio"
)
@@ -120,7 +122,7 @@ func inferType(v any) string {
switch t := v.(type) {
case bool:
return colBoolean
- case json.Number:
+ case jsonx.Number:
if _, err := t.Int64(); err == nil {
return colBigint
}
@@ -132,11 +134,10 @@ func inferType(v any) string {
}
func decodeObject(rec []byte) (map[string]any, bool) {
- dec := json.NewDecoder(bytes.NewReader(bytes.TrimSpace(rec)))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(bytes.TrimSpace(rec)))
var obj map[string]any
- if err := dec.Decode(&obj); err != nil || obj == nil {
+ if err := json.UnmarshalDecode(dec, &obj, jsonx.V1(), jsonx.UseNumber()); err != nil || obj == nil {
return nil, false
}
@@ -162,10 +163,10 @@ func cellFor(obj map[string]any, col string) any {
return t
case bool:
return t
- case json.Number:
+ case jsonx.Number:
return t.String()
default:
- b, err := json.Marshal(t)
+ b, err := json.Marshal(t, jsonx.V1())
if err != nil {
return nil
}
diff --git a/services/firehose/handler.go b/services/firehose/handler.go
index 448dfb73bc..0172096361 100644
--- a/services/firehose/handler.go
+++ b/services/firehose/handler.go
@@ -2,7 +2,7 @@ package firehose
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -145,7 +146,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var req deliveryStreamNameInput
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return req.DeliveryStreamName
}
@@ -195,13 +196,10 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
respond := func(status int, errType string) error {
msg := strings.TrimPrefix(err.Error(), errType+": ")
@@ -218,7 +216,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
case errors.Is(err, errUnknownAction):
return respond(http.StatusBadRequest, "UnknownOperationException")
case errors.Is(err, errInvalidRequest), errors.Is(err, awserr.ErrInvalidParameter),
- errors.Is(err, ErrValidation), errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ errors.Is(err, ErrValidation), jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
return respond(http.StatusBadRequest, "InvalidArgumentException")
default:
return c.JSON(http.StatusInternalServerError, map[string]string{errFieldMessage: err.Error()})
diff --git a/services/firehose/handler_delivery_streams.go b/services/firehose/handler_delivery_streams.go
index e646932a98..4e0ad367a7 100644
--- a/services/firehose/handler_delivery_streams.go
+++ b/services/firehose/handler_delivery_streams.go
@@ -2,7 +2,7 @@ package firehose
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"strings"
@@ -328,7 +328,7 @@ type createDeliveryStreamInput struct {
// not a typed struct) so handleCreateDeliveryStream can reject it explicitly instead
// of silently dropping it and creating a stream with zero destinations -- see
// validateSingleDestination.
- AmazonOpenSearchServerlessDestinationConfiguration json.RawMessage `json:"AmazonOpenSearchServerlessDestinationConfiguration,omitempty"` //nolint:lll // AWS field name
+ AmazonOpenSearchServerlessDestinationConfiguration jsontext.Value `json:"AmazonOpenSearchServerlessDestinationConfiguration,omitempty"` //nolint:lll // AWS field name
DatabaseSourceConfiguration *databaseSourceConfigurationInput `json:"DatabaseSourceConfiguration"` //nolint:lll // AWS field name
DirectPutSourceConfiguration *directPutSourceConfigurationInput `json:"DirectPutSourceConfiguration"` //nolint:lll // AWS field name
DeliveryStreamEncryptionConfigurationInput *EncryptionConfigInput `json:"DeliveryStreamEncryptionConfigurationInput"` //nolint:lll // AWS field name
diff --git a/services/firehose/handler_delivery_streams_test.go b/services/firehose/handler_delivery_streams_test.go
index cdd8203d49..db49e56ab5 100644
--- a/services/firehose/handler_delivery_streams_test.go
+++ b/services/firehose/handler_delivery_streams_test.go
@@ -1,7 +1,7 @@
package firehose_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/firehose"
)
@@ -530,7 +531,7 @@ func TestDeleteThenDescribeReturns404(t *testing.T) {
assert.Equal(t, http.StatusNotFound, descRec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", body["__type"])
}
@@ -681,7 +682,7 @@ func TestDescribeDeliveryStream_SourceField(t *testing.T) {
Source *firehose.SourceDescription `json:"Source"`
} `json:"DeliveryStreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.DeliveryStreamDescription.Source)
require.NotNil(t, out.DeliveryStreamDescription.Source.KinesisStreamSourceDescription)
assert.Equal(t, "arn:aws:kinesis:us-east-1:000000000000:stream/my-ks",
@@ -761,7 +762,7 @@ func TestDescribeDeliveryStream_Timestamps(t *testing.T) {
LastUpdateTimestamp *int64 `json:"LastUpdateTimestamp"`
} `json:"DeliveryStreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotNil(t, out.DeliveryStreamDescription.CreateTimestamp)
assert.NotNil(t, out.DeliveryStreamDescription.LastUpdateTimestamp)
assert.Positive(t, *out.DeliveryStreamDescription.CreateTimestamp)
@@ -1139,7 +1140,7 @@ func TestListDeliveryStreams_Empty(t *testing.T) {
DeliveryStreamNames []string `json:"DeliveryStreamNames"`
HasMoreDeliveryStreams bool `json:"HasMoreDeliveryStreams"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.DeliveryStreamNames)
assert.False(t, out.HasMoreDeliveryStreams)
}
@@ -1161,7 +1162,7 @@ func TestListDeliveryStreams_Limit(t *testing.T) {
DeliveryStreamNames []string `json:"DeliveryStreamNames"`
HasMoreDeliveryStreams bool `json:"HasMoreDeliveryStreams"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.DeliveryStreamNames, 2)
assert.True(t, out.HasMoreDeliveryStreams)
}
@@ -1183,7 +1184,7 @@ func TestListDeliveryStreams_ExclusiveStart(t *testing.T) {
var out struct {
DeliveryStreamNames []string `json:"DeliveryStreamNames"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, []string{"b-stream", "c-stream"}, out.DeliveryStreamNames)
}
@@ -1205,7 +1206,7 @@ func TestListDeliveryStreams_Pagination(t *testing.T) {
DeliveryStreamNames []string `json:"DeliveryStreamNames"`
HasMoreDeliveryStreams bool `json:"HasMoreDeliveryStreams"`
}
- require.NoError(t, json.Unmarshal(page1Rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(page1Rec.Body.Bytes(), &page1, jsonx.V1()))
assert.Len(t, page1.DeliveryStreamNames, 2)
assert.True(t, page1.HasMoreDeliveryStreams)
@@ -1219,7 +1220,7 @@ func TestListDeliveryStreams_Pagination(t *testing.T) {
DeliveryStreamNames []string `json:"DeliveryStreamNames"`
HasMoreDeliveryStreams bool `json:"HasMoreDeliveryStreams"`
}
- require.NoError(t, json.Unmarshal(page2Rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(page2Rec.Body.Bytes(), &page2, jsonx.V1()))
assert.Len(t, page2.DeliveryStreamNames, 2)
// No overlap between pages.
for _, n := range page2.DeliveryStreamNames {
@@ -1249,7 +1250,7 @@ func TestListDeliveryStreams_TypeFilter(t *testing.T) {
var out struct {
DeliveryStreamNames []string `json:"DeliveryStreamNames"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out.DeliveryStreamNames, "direct-put-stream")
assert.NotContains(t, out.DeliveryStreamNames, "kinesis-src-stream-lf")
@@ -1259,7 +1260,7 @@ func TestListDeliveryStreams_TypeFilter(t *testing.T) {
var out2 struct {
DeliveryStreamNames []string `json:"DeliveryStreamNames"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
assert.Contains(t, out2.DeliveryStreamNames, "kinesis-src-stream-lf")
assert.NotContains(t, out2.DeliveryStreamNames, "direct-put-stream")
@@ -1273,7 +1274,7 @@ func TestListDeliveryStreams_TypeFilter(t *testing.T) {
var outInvalid struct {
DeliveryStreamNames []string `json:"DeliveryStreamNames"`
}
- require.NoError(t, json.Unmarshal(recInvalid.Body.Bytes(), &outInvalid))
+ require.NoError(t, json.Unmarshal(recInvalid.Body.Bytes(), &outInvalid, jsonx.V1()))
assert.NotContains(t, outInvalid.DeliveryStreamNames, "direct-put-stream")
assert.NotContains(t, outInvalid.DeliveryStreamNames, "kinesis-src-stream-lf")
}
diff --git a/services/firehose/handler_destination_updates.go b/services/firehose/handler_destination_updates.go
index 9cc06868fa..d19c1d6326 100644
--- a/services/firehose/handler_destination_updates.go
+++ b/services/firehose/handler_destination_updates.go
@@ -2,7 +2,7 @@ package firehose
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
)
@@ -29,7 +29,7 @@ type updateDestinationInput struct {
// AmazonOpenSearchServerlessDestinationUpdate: see createDeliveryStreamInput's
// AmazonOpenSearchServerlessDestinationConfiguration doc comment -- same
// unimplemented-11th-destination-type reasoning, captured only to reject explicitly.
- AmazonOpenSearchServerlessDestinationUpdate json.RawMessage `json:"AmazonOpenSearchServerlessDestinationUpdate,omitempty"` //nolint:lll // AWS field name
+ AmazonOpenSearchServerlessDestinationUpdate jsontext.Value `json:"AmazonOpenSearchServerlessDestinationUpdate,omitempty"` //nolint:lll // AWS field name
}
type updateDestinationOutput struct{}
diff --git a/services/firehose/handler_destination_updates_test.go b/services/firehose/handler_destination_updates_test.go
index 888e8786e2..b127f5d165 100644
--- a/services/firehose/handler_destination_updates_test.go
+++ b/services/firehose/handler_destination_updates_test.go
@@ -1,13 +1,14 @@
package firehose_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/firehose"
)
@@ -85,7 +86,7 @@ func TestUpdateDestination_Success_VersionIncrements(t *testing.T) {
} `json:"Destinations"`
} `json:"DeliveryStreamDescription"`
}
- require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &descOut, jsonx.V1()))
versionBefore := descOut.DeliveryStreamDescription.VersionID
rec := doFirehoseRequest(t, h, "UpdateDestination", map[string]any{
@@ -112,7 +113,7 @@ func TestUpdateDestination_Success_VersionIncrements(t *testing.T) {
} `json:"Destinations"`
} `json:"DeliveryStreamDescription"`
}
- require.NoError(t, json.Unmarshal(desc2.Body.Bytes(), &descOut2))
+ require.NoError(t, json.Unmarshal(desc2.Body.Bytes(), &descOut2, jsonx.V1()))
assert.NotEqual(t, versionBefore, descOut2.DeliveryStreamDescription.VersionID)
require.Len(t, descOut2.DeliveryStreamDescription.Destinations, 1)
assert.Equal(t, "arn:aws:s3:::updated-bucket",
@@ -247,7 +248,7 @@ func TestUpdateDestination_VersionMismatchRejected(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ConcurrentModificationException", body["__type"])
}
diff --git a/services/firehose/handler_records_test.go b/services/firehose/handler_records_test.go
index bb16b06e4c..d518b27c15 100644
--- a/services/firehose/handler_records_test.go
+++ b/services/firehose/handler_records_test.go
@@ -2,13 +2,14 @@ package firehose_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/firehose"
)
@@ -82,7 +83,7 @@ func TestPutRecord_Success_ReturnsRecordId(t *testing.T) {
var out struct {
RecordID string `json:"RecordId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.RecordID)
}
@@ -170,7 +171,7 @@ func TestKinesisStreamAsSource_PutRecordRejected(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidArgumentException", body["__type"])
}
@@ -249,7 +250,7 @@ func TestPutRecordBatch_Success_ResponseShape(t *testing.T) {
RequestResponses []any `json:"RequestResponses"`
FailedPutCount int `json:"FailedPutCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, 0, out.FailedPutCount)
assert.NotNil(t, out.RequestResponses)
}
@@ -303,7 +304,7 @@ func TestPutRecordBatch_ResponseShape(t *testing.T) {
} `json:"RequestResponses"`
FailedPutCount int `json:"FailedPutCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, 0, out.FailedPutCount, "FailedPutCount must always be 0")
assert.Len(t, out.RequestResponses, len(tc.recordData), "one response per record")
@@ -355,7 +356,7 @@ func TestPutRecordBatch_PerRecordRecordId(t *testing.T) {
} `json:"RequestResponses"`
FailedPutCount int `json:"FailedPutCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, 0, out.FailedPutCount)
require.Len(t, out.RequestResponses, tt.recordCount,
@@ -457,7 +458,7 @@ func TestPutRecord_EncryptedField(t *testing.T) {
var putOut struct {
Encrypted bool `json:"Encrypted"`
}
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putOut, jsonx.V1()))
assert.Equal(t, tt.wantEncrypted, putOut.Encrypted, "PutRecord Encrypted mismatch")
batchRec := doFirehoseRequest(t, h, "PutRecordBatch", map[string]any{
@@ -469,7 +470,7 @@ func TestPutRecord_EncryptedField(t *testing.T) {
var batchOut struct {
Encrypted bool `json:"Encrypted"`
}
- require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batchOut))
+ require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batchOut, jsonx.V1()))
assert.Equal(t, tt.wantEncrypted, batchOut.Encrypted, "PutRecordBatch Encrypted mismatch")
})
}
diff --git a/services/firehose/handler_tags_test.go b/services/firehose/handler_tags_test.go
index c1ceaa052b..92cc0e625a 100644
--- a/services/firehose/handler_tags_test.go
+++ b/services/firehose/handler_tags_test.go
@@ -1,13 +1,14 @@
package firehose_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/firehose"
)
@@ -148,7 +149,7 @@ func TestUntagDeliveryStream_RemovesSpecificKeys(t *testing.T) {
var out struct {
Tags []map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(list.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(list.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Tags, 1)
assert.Equal(t, "team", out.Tags[0]["Key"])
}
@@ -280,7 +281,7 @@ func TestListTagsForDeliveryStream_Limit(t *testing.T) {
Tags []map[string]string `json:"Tags"`
HasMoreTags bool `json:"HasMoreTags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Tags, 3)
assert.True(t, out.HasMoreTags)
}
@@ -313,7 +314,7 @@ func TestListTagsForDeliveryStream_ExclusiveStart(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Tags, 2)
assert.Equal(t, "b-key", out.Tags[0].Key)
assert.Equal(t, "c-key", out.Tags[1].Key)
@@ -409,7 +410,7 @@ func TestTagsLifecycle(t *testing.T) {
Tags []map[string]string `json:"Tags"`
HasMoreTags bool `json:"HasMoreTags"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.Tags, 4)
assert.False(t, listOut.HasMoreTags)
// Keys must be sorted: env, region, team, version.
@@ -425,7 +426,7 @@ func TestTagsLifecycle(t *testing.T) {
Tags []map[string]string `json:"Tags"`
HasMoreTags bool `json:"HasMoreTags"`
}
- require.NoError(t, json.Unmarshal(page1Rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(page1Rec.Body.Bytes(), &page1, jsonx.V1()))
require.Len(t, page1.Tags, 2)
assert.True(t, page1.HasMoreTags)
assert.Equal(t, "env", page1.Tags[0]["Key"])
@@ -441,7 +442,7 @@ func TestTagsLifecycle(t *testing.T) {
Tags []map[string]string `json:"Tags"`
HasMoreTags bool `json:"HasMoreTags"`
}
- require.NoError(t, json.Unmarshal(page2Rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(page2Rec.Body.Bytes(), &page2, jsonx.V1()))
require.Len(t, page2.Tags, 2)
assert.False(t, page2.HasMoreTags)
assert.Equal(t, "team", page2.Tags[0]["Key"])
@@ -459,7 +460,7 @@ func TestTagsLifecycle(t *testing.T) {
var finalOut struct {
Tags []map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(finalRec.Body.Bytes(), &finalOut))
+ require.NoError(t, json.Unmarshal(finalRec.Body.Bytes(), &finalOut, jsonx.V1()))
require.Len(t, finalOut.Tags, 2)
keys := map[string]bool{}
diff --git a/services/firehose/handler_test.go b/services/firehose/handler_test.go
index 59faed73d5..97634dc783 100644
--- a/services/firehose/handler_test.go
+++ b/services/firehose/handler_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"compress/gzip"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"log/slog"
"maps"
@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/firehose"
)
@@ -33,7 +34,7 @@ func doFirehoseRequest(t *testing.T, h *firehose.Handler, action string, body an
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -90,7 +91,7 @@ func auditCreateStream(t *testing.T, h *firehose.Handler, name string, extra map
var out struct {
DeliveryStreamARN string `json:"DeliveryStreamARN"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.DeliveryStreamARN
}
@@ -104,7 +105,7 @@ func auditDescribe(t *testing.T, h *firehose.Handler, name string) map[string]an
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["DeliveryStreamDescription"].(map[string]any)
}
@@ -423,7 +424,7 @@ func TestErrorResponse_ResourceInUseException_HasType(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ResourceInUseException", body["__type"],
"duplicate-stream error must carry __type=ResourceInUseException")
}
@@ -475,7 +476,7 @@ func TestErrorResponse_InvalidArgumentException_HasType(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidArgumentException", body["__type"],
"validation error must carry __type=InvalidArgumentException; got: %v", body)
})
@@ -491,7 +492,7 @@ func TestErrorResponse_ResourceNotFoundException_HasType(t *testing.T) {
assert.Equal(t, http.StatusNotFound, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", body["__type"],
"not-found error must carry __type=ResourceNotFoundException")
}
@@ -504,7 +505,7 @@ func TestErrorResponse_UnknownOperation_HasType(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "UnknownOperationException", body["__type"],
"unknown operation must carry __type=UnknownOperationException")
}
diff --git a/services/firehose/http_endpoint_secret_test.go b/services/firehose/http_endpoint_secret_test.go
index 3441dd79d2..bb78cfc88d 100644
--- a/services/firehose/http_endpoint_secret_test.go
+++ b/services/firehose/http_endpoint_secret_test.go
@@ -1,12 +1,14 @@
package firehose_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDescribeDeliveryStream_HTTPEndpointOmitsAccessKey(t *testing.T) {
@@ -51,7 +53,7 @@ func TestDescribeDeliveryStream_HTTPEndpointOmitsAccessKey(t *testing.T) {
} `json:"Destinations"`
} `json:"DeliveryStreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.DeliveryStreamDescription.Destinations, 1)
ec := out.DeliveryStreamDescription.Destinations[0].HTTP.EndpointConfiguration
diff --git a/services/firehose/isolation_test.go b/services/firehose/isolation_test.go
index d7a55dbe3f..f904eff5f6 100644
--- a/services/firehose/isolation_test.go
+++ b/services/firehose/isolation_test.go
@@ -2,7 +2,7 @@ package firehose //nolint:testpackage // existing issue.
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestFirehoseRegionIsolation(t *testing.T) { //nolint:paralleltest // existing issue.
@@ -25,7 +26,7 @@ func TestFirehoseRegionIsolation(t *testing.T) { //nolint:paralleltest // existi
DeliveryStreamName: name,
DeliveryStreamType: "DirectPut",
}
- body, _ := json.Marshal(input)
+ body, _ := json.Marshal(input, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
req.Header.Set("X-Amz-Target", firehoseTargetPrefix+"CreateDeliveryStream")
req.Header.Set(
@@ -43,7 +44,7 @@ func TestFirehoseRegionIsolation(t *testing.T) { //nolint:paralleltest // existi
listStreams := func(region string) []string {
input := listDeliveryStreamsInput{}
- body, _ := json.Marshal(input)
+ body, _ := json.Marshal(input, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
req.Header.Set("X-Amz-Target", firehoseTargetPrefix+"ListDeliveryStreams")
req.Header.Set(
@@ -59,7 +60,7 @@ func TestFirehoseRegionIsolation(t *testing.T) { //nolint:paralleltest // existi
assert.Equal(t, http.StatusOK, rec.Code)
var output listDeliveryStreamsOutput
- err = json.Unmarshal(rec.Body.Bytes(), &output)
+ err = json.Unmarshal(rec.Body.Bytes(), &output, jsonx.V1())
require.NoError(t, err)
return output.DeliveryStreamNames
diff --git a/services/firehose/non_s3_delivery_test.go b/services/firehose/non_s3_delivery_test.go
index ba1c4e6461..9f08e931d9 100644
--- a/services/firehose/non_s3_delivery_test.go
+++ b/services/firehose/non_s3_delivery_test.go
@@ -3,7 +3,8 @@ package firehose_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -15,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/firehose"
)
@@ -50,7 +52,7 @@ func newCaptureServer(t *testing.T, statusCode int) *captureServer {
cs.mu.Unlock()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(statusCode)
- _ = json.NewEncoder(w).Encode(map[string]any{
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), map[string]any{
"requestId": r.Header.Get("X-Amz-Firehose-Request-Id"),
"timestamp": time.Now().UnixMilli(),
})
@@ -151,7 +153,7 @@ func TestFirehose_HTTPEndpoint_Delivery(t *testing.T) {
} `json:"records"`
Timestamp int64 `json:"timestamp"`
}
- require.NoError(t, json.Unmarshal(reqs[0].body, &payload))
+ require.NoError(t, json.Unmarshal(reqs[0].body, &payload, jsonx.V1()))
assert.NotEmpty(t, payload.RequestID)
assert.Positive(t, payload.Timestamp)
diff --git a/services/firehose/partitioning.go b/services/firehose/partitioning.go
index 1eaf96cb91..2eed5d7491 100644
--- a/services/firehose/partitioning.go
+++ b/services/firehose/partitioning.go
@@ -1,9 +1,11 @@
package firehose
import (
- "encoding/json"
+ "encoding/json/v2"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// partitionQueryNamespace is the Firehose namespace for jq-derived partition keys.
@@ -81,7 +83,7 @@ func partitionValue(
if !*parsed {
*parsed = true
- if err := json.Unmarshal(rec, obj); err != nil {
+ if err := json.Unmarshal(rec, obj, jsonx.V1()); err != nil {
return "", false
}
}
@@ -292,7 +294,7 @@ func scalarToString(v any) (string, bool) {
}
return "false", true
- case json.Number:
+ case jsonx.Number:
return val.String(), true
case float64:
// Render integers without a trailing ".0" to match typical partition values.
diff --git a/services/firehose/persistence.go b/services/firehose/persistence.go
index 321c066787..3269eba568 100644
--- a/services/firehose/persistence.go
+++ b/services/firehose/persistence.go
@@ -2,10 +2,12 @@ package firehose
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -39,10 +41,10 @@ const firehoseSnapshotVersion = 2
// key is derived from), so no DTO layer is needed the way services/ses or
// services/neptune required for region-nested types with a hidden field.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
@@ -66,7 +68,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.region,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "firehose: failed to marshal snapshot", "error", err)
diff --git a/services/firehose/persistence_test.go b/services/firehose/persistence_test.go
index ad82d67cea..c1a5bebe0a 100644
--- a/services/firehose/persistence_test.go
+++ b/services/firehose/persistence_test.go
@@ -2,12 +2,13 @@ package firehose_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/firehose"
)
@@ -252,9 +253,9 @@ func TestInMemoryBackend_RestoreVersionMismatch(t *testing.T) {
require.NotNil(t, raw)
var generic map[string]any
- require.NoError(t, json.Unmarshal(raw, &generic))
+ require.NoError(t, json.Unmarshal(raw, &generic, jsonx.V1()))
tt.mutate(t, generic)
- mutated, err := json.Marshal(generic)
+ mutated, err := json.Marshal(generic, jsonx.V1())
require.NoError(t, err)
// Target already holds unrelated state, which the version-mismatch path must
diff --git a/services/firehose/transform.go b/services/firehose/transform.go
index a0b8020b03..0bb730a404 100644
--- a/services/firehose/transform.go
+++ b/services/firehose/transform.go
@@ -3,7 +3,7 @@ package firehose
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"strconv"
@@ -12,6 +12,7 @@ import (
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/roleauth"
)
@@ -94,7 +95,7 @@ func failureRecord(raw []byte, lambdaARN, code, msg string, attempts int) []byte
AttemptEndingTimestamp: now,
RawData: base64.StdEncoding.EncodeToString(raw),
LambdaARN: lambdaARN,
- })
+ }, jsonx.V1())
if err != nil {
return raw
}
@@ -126,7 +127,7 @@ func buildLambdaTransformPayload(records [][]byte, streamARN, region string) ([]
}
}
- payload, err := json.Marshal(event)
+ payload, err := json.Marshal(event, jsonx.V1())
if err != nil {
return nil, nil
}
@@ -145,7 +146,7 @@ func parseLambdaTransformResponse(
ErrorMessage string `json:"errorMessage"`
Records []lambdaTransformResponseRecord `json:"records"`
}
- if err := json.Unmarshal(result, &resp); err != nil {
+ if err := json.Unmarshal(result, &resp, jsonx.V1()); err != nil {
return transformOutcome{}, &transformError{code: codeLambdaJSON, err: err}
}
diff --git a/services/fis/actions_test.go b/services/fis/actions_test.go
index 9c230c8c77..4a74c23e3c 100644
--- a/services/fis/actions_test.go
+++ b/services/fis/actions_test.go
@@ -1,7 +1,7 @@
package fis_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/fis"
)
@@ -612,7 +613,7 @@ func TestFISHandler_StopExperiment_AlreadyStopped(t *testing.T) {
} `json:"experiment"`
}
- if err := json.Unmarshal(rec3.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rec3.Body.Bytes(), &resp, jsonx.V1()); err != nil {
return false
}
@@ -710,7 +711,7 @@ func TestFISHandler_ExperimentFails_WhenActionProviderFails(t *testing.T) {
rec3 := doRequest(t, h, http.MethodGet, "/experiments/"+expID, nil)
require.Equal(t, http.StatusOK, rec3.Code)
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &finalResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &finalResp, jsonx.V1()))
require.Equal(t, "failed", finalResp.Experiment.Status.Status)
// Regression test: cleanupActions used to unconditionally overwrite
@@ -799,7 +800,7 @@ func TestFISHandler_ExperimentSucceeds_WithMockActionProvider(t *testing.T) {
} `json:"experiment"`
}
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp, jsonx.V1()))
require.Equal(t, "completed", resp.Experiment.Status.Status)
})
}
diff --git a/services/fis/experiment_actions_mode_test.go b/services/fis/experiment_actions_mode_test.go
index af73407e38..ebc5822b5d 100644
--- a/services/fis/experiment_actions_mode_test.go
+++ b/services/fis/experiment_actions_mode_test.go
@@ -1,7 +1,8 @@
package fis_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/fis"
)
@@ -39,10 +41,10 @@ func actionsModeTemplateBody() map[string]any {
// pollExperimentUntilTerminal polls GET /experiments/{id} until the experiment
// reaches a terminal status and returns the decoded response body.
-func pollExperimentUntilTerminal(t *testing.T, h *fis.Handler, expID string) map[string]json.RawMessage {
+func pollExperimentUntilTerminal(t *testing.T, h *fis.Handler, expID string) map[string]jsontext.Value {
t.Helper()
- var result map[string]json.RawMessage
+ var result map[string]jsontext.Value
require.Eventually(t, func() bool {
r := doRequest(t, h, http.MethodGet, "/experiments/"+expID, nil)
@@ -51,10 +53,10 @@ func pollExperimentUntilTerminal(t *testing.T, h *fis.Handler, expID string) map
}
var full struct {
- Experiment map[string]json.RawMessage `json:"experiment"`
+ Experiment map[string]jsontext.Value `json:"experiment"`
}
- if err := json.Unmarshal(r.Body.Bytes(), &full); err != nil {
+ if err := json.Unmarshal(r.Body.Bytes(), &full, jsonx.V1()); err != nil {
return false
}
@@ -62,7 +64,7 @@ func pollExperimentUntilTerminal(t *testing.T, h *fis.Handler, expID string) map
Status string `json:"status"`
}
- if err := json.Unmarshal(full.Experiment["status"], &status); err != nil {
+ if err := json.Unmarshal(full.Experiment["status"], &status, jsonx.V1()); err != nil {
return false
}
@@ -127,7 +129,7 @@ func TestStartExperiment_ActionsMode_SkipAll_SkipsActionsAndProvider(t *testing.
Status string `json:"status"`
}
- require.NoError(t, json.Unmarshal(exp["status"], &status))
+ require.NoError(t, json.Unmarshal(exp["status"], &status, jsonx.V1()))
assert.Equal(t, "completed", status.Status)
var actions map[string]struct {
@@ -136,7 +138,7 @@ func TestStartExperiment_ActionsMode_SkipAll_SkipsActionsAndProvider(t *testing.
} `json:"status"`
}
- require.NoError(t, json.Unmarshal(exp["actions"], &actions))
+ require.NoError(t, json.Unmarshal(exp["actions"], &actions, jsonx.V1()))
require.Contains(t, actions, "modeAction")
assert.Equal(t, "skipped", actions["modeAction"].Status.Status)
@@ -188,7 +190,7 @@ func TestStartExperiment_ActionsMode_RunAll_InvokesProvider(t *testing.T) {
Status string `json:"status"`
}
- require.NoError(t, json.Unmarshal(exp["status"], &status))
+ require.NoError(t, json.Unmarshal(exp["status"], &status, jsonx.V1()))
assert.Equal(t, "completed", status.Status)
assert.Equal(t, 1, mock.Calls, "run-all must invoke the external action provider exactly once")
})
diff --git a/services/fis/experiment_execution_test.go b/services/fis/experiment_execution_test.go
index f868fdaf2d..298545dc4e 100644
--- a/services/fis/experiment_execution_test.go
+++ b/services/fis/experiment_execution_test.go
@@ -1,7 +1,7 @@
package fis_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fis"
)
@@ -75,7 +76,7 @@ func TestFISHandler_ExperimentCompletesAfterDuration(t *testing.T) {
} `json:"experiment"`
}
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp, jsonx.V1()))
require.Equal(t, "completed", resp.Experiment.Status.Status)
})
}
@@ -156,7 +157,7 @@ func TestFISHandler_ExperimentCompletes_NoTimedActions(t *testing.T) {
} `json:"experiment"`
}
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp, jsonx.V1()))
require.Equal(t, "completed", resp.Experiment.Status.Status)
})
}
diff --git a/services/fis/experiment_reports_test.go b/services/fis/experiment_reports_test.go
index 666af94159..2b35b29de3 100644
--- a/services/fis/experiment_reports_test.go
+++ b/services/fis/experiment_reports_test.go
@@ -1,7 +1,8 @@
package fis_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fis"
)
@@ -228,7 +230,7 @@ func startExperimentAndPollTerminal(t *testing.T, h *fis.Handler, templateID str
Experiment experimentReportPollResult `json:"experiment"`
}
- if err := json.Unmarshal(r.Body.Bytes(), &full); err != nil {
+ if err := json.Unmarshal(r.Body.Bytes(), &full, jsonx.V1()); err != nil {
return false
}
@@ -321,13 +323,13 @@ func TestGetExperiment_NoReportConfig_OmitsReportFields(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rec.Code)
- var raw map[string]json.RawMessage
+ var raw map[string]jsontext.Value
mustJSON(t, rec, &raw)
- var exp map[string]json.RawMessage
+ var exp map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(raw["experiment"], &exp))
+ require.NoError(t, json.Unmarshal(raw["experiment"], &exp, jsonx.V1()))
_, hasReportConfig := exp["experimentReportConfiguration"]
_, hasReport := exp["experimentReport"]
diff --git a/services/fis/experiment_status_test.go b/services/fis/experiment_status_test.go
index b74c153c6c..0889d871fa 100644
--- a/services/fis/experiment_status_test.go
+++ b/services/fis/experiment_status_test.go
@@ -1,7 +1,8 @@
package fis_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fis"
)
@@ -51,13 +53,13 @@ func TestExperiment_EndTime_AbsentBeforeComplete(t *testing.T) {
require.Equal(t, http.StatusCreated, rec2.Code)
// Immediately after start, endTime should be absent (omitempty).
- var raw map[string]json.RawMessage
+ var raw map[string]jsontext.Value
mustJSON(t, rec2, &raw)
- var expRaw map[string]json.RawMessage
+ var expRaw map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(raw["experiment"], &expRaw))
+ require.NoError(t, json.Unmarshal(raw["experiment"], &expRaw, jsonx.V1()))
_, hasEndTime := expRaw["endTime"]
assert.False(t, hasEndTime, "endTime must be absent on a running experiment")
}
@@ -113,13 +115,13 @@ func TestExperiment_EndTime_PresentAfterComplete(t *testing.T) {
rec3 := doRequest(t, h, http.MethodGet, "/experiments/"+expID, nil)
require.Equal(t, http.StatusOK, rec3.Code)
- var raw map[string]json.RawMessage
+ var raw map[string]jsontext.Value
mustJSON(t, rec3, &raw)
- var expRaw map[string]json.RawMessage
+ var expRaw map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(raw["experiment"], &expRaw))
+ require.NoError(t, json.Unmarshal(raw["experiment"], &expRaw, jsonx.V1()))
statusRaw, hasStatus := expRaw["status"]
require.True(t, hasStatus)
@@ -128,7 +130,7 @@ func TestExperiment_EndTime_PresentAfterComplete(t *testing.T) {
Status string `json:"status"`
}
- require.NoError(t, json.Unmarshal(statusRaw, &status))
+ require.NoError(t, json.Unmarshal(statusRaw, &status, jsonx.V1()))
require.Equal(t, "completed", status.Status)
endTimeRaw, hasEndTime := expRaw["endTime"]
@@ -136,7 +138,7 @@ func TestExperiment_EndTime_PresentAfterComplete(t *testing.T) {
var endTime float64
- require.NoError(t, json.Unmarshal(endTimeRaw, &endTime))
+ require.NoError(t, json.Unmarshal(endTimeRaw, &endTime, jsonx.V1()))
assert.Greater(t, endTime, 0.0, "endTime must be a positive Unix timestamp")
})
}
@@ -359,13 +361,13 @@ func TestExperiment_StatusAndState_BothPresent(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rec.Code)
- var raw map[string]json.RawMessage
+ var raw map[string]jsontext.Value
mustJSON(t, rec, &raw)
- var expRaw map[string]json.RawMessage
+ var expRaw map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(raw["experiment"], &expRaw))
+ require.NoError(t, json.Unmarshal(raw["experiment"], &expRaw, jsonx.V1()))
_, hasStatus := expRaw["status"]
_, hasState := expRaw["state"]
assert.True(t, hasStatus, "experiment must have 'status' field")
@@ -522,22 +524,22 @@ func TestExperimentStatus_ErrorField_Present(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rec.Code)
- var resp map[string]json.RawMessage
+ var resp map[string]jsontext.Value
mustJSON(t, rec, &resp)
expRaw := resp["experiment"]
- var exp map[string]json.RawMessage
+ var exp map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(expRaw, &exp))
+ require.NoError(t, json.Unmarshal(expRaw, &exp, jsonx.V1()))
statusRaw, ok := exp["status"]
require.True(t, ok, "experiment should have status field")
- var status map[string]json.RawMessage
+ var status map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(statusRaw, &status))
+ require.NoError(t, json.Unmarshal(statusRaw, &status, jsonx.V1()))
// state.error should be absent for non-failed experiments (omitempty).
_, hasError := status["error"]
diff --git a/services/fis/experiments_test.go b/services/fis/experiments_test.go
index 4c46837045..a8ee5a72c5 100644
--- a/services/fis/experiments_test.go
+++ b/services/fis/experiments_test.go
@@ -1,7 +1,7 @@
package fis_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fis"
)
@@ -532,7 +533,7 @@ func TestListExperiments_FilterByStatus(t *testing.T) {
} `json:"experiments"`
}
- require.NoError(t, json.Unmarshal(r.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(r.Body.Bytes(), &gr, jsonx.V1()))
assert.NotEmpty(t, gr.Experiments)
})
}
diff --git a/services/fis/handler_experiment_templates.go b/services/fis/handler_experiment_templates.go
index e8adb7019d..4da3662003 100644
--- a/services/fis/handler_experiment_templates.go
+++ b/services/fis/handler_experiment_templates.go
@@ -2,10 +2,12 @@ package fis
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ----------------------------------------
@@ -18,7 +20,7 @@ func (h *Handler) handleCreateExperimentTemplate(
body []byte,
) error {
var input createExperimentTemplateRequest
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "invalid request body: "+err.Error(), "")
}
@@ -45,7 +47,7 @@ func (h *Handler) handleGetExperimentTemplate(c *echo.Context, id string) error
func (h *Handler) handleUpdateExperimentTemplate(c *echo.Context, id string, body []byte) error {
var input updateExperimentTemplateRequest
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "invalid request body: "+err.Error(), id)
}
diff --git a/services/fis/handler_experiments.go b/services/fis/handler_experiments.go
index ceed6b3a4c..11fb193a1d 100644
--- a/services/fis/handler_experiments.go
+++ b/services/fis/handler_experiments.go
@@ -2,11 +2,13 @@ package fis
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ----------------------------------------
@@ -15,7 +17,7 @@ import (
func (h *Handler) handleStartExperiment(ctx context.Context, c *echo.Context, body []byte) error {
var input startExperimentRequest
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "invalid request body: "+err.Error(), "")
}
diff --git a/services/fis/handler_safety_levers.go b/services/fis/handler_safety_levers.go
index bdc6a747a0..d79ada85a1 100644
--- a/services/fis/handler_safety_levers.go
+++ b/services/fis/handler_safety_levers.go
@@ -1,10 +1,12 @@
package fis
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ----------------------------------------
@@ -22,7 +24,7 @@ func (h *Handler) handleGetSafetyLever(c *echo.Context, id string) error {
func (h *Handler) handleUpdateSafetyLeverState(c *echo.Context, id string, body []byte) error {
var input updateSafetyLeverStateRequest
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "invalid request body: "+err.Error(), id)
}
diff --git a/services/fis/handler_tags.go b/services/fis/handler_tags.go
index ad4140cb96..26b54195a2 100644
--- a/services/fis/handler_tags.go
+++ b/services/fis/handler_tags.go
@@ -1,11 +1,13 @@
package fis
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ----------------------------------------
@@ -17,7 +19,7 @@ func (h *Handler) handleTagResource(c *echo.Context, arnStr string, body []byte)
Tags map[string]string `json:"tags"`
}
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "invalid request body: "+err.Error(), arnStr)
}
diff --git a/services/fis/handler_target_account_configurations.go b/services/fis/handler_target_account_configurations.go
index 9e27a7dfd4..4961009de4 100644
--- a/services/fis/handler_target_account_configurations.go
+++ b/services/fis/handler_target_account_configurations.go
@@ -1,7 +1,7 @@
package fis
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ----------------------------------------
@@ -31,7 +32,7 @@ func (h *Handler) handleCreateTargetAccountConfiguration(
templateID, accountID := splitCompositeID(compositeID)
var input createTargetAccountConfigurationRequest
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -101,7 +102,7 @@ func (h *Handler) handleUpdateTargetAccountConfiguration(
templateID, accountID := splitCompositeID(compositeID)
var input updateTargetAccountConfigurationRequest
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/fis/handler_test.go b/services/fis/handler_test.go
index a8250958f1..5b9280c50d 100644
--- a/services/fis/handler_test.go
+++ b/services/fis/handler_test.go
@@ -3,7 +3,7 @@ package fis_test
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/fis"
)
@@ -36,7 +37,7 @@ func doRequest(t *testing.T, h *fis.Handler, method, path string, body any) *htt
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -55,7 +56,7 @@ func doRequest(t *testing.T, h *fis.Handler, method, path string, body any) *htt
func mustJSON(t *testing.T, rec *httptest.ResponseRecorder, v any) {
t.Helper()
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), v))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), v, jsonx.V1()))
}
// ----------------------------------------
@@ -99,7 +100,7 @@ func seedTemplate(t *testing.T, h *fis.Handler) string {
// jsonUnmarshalFIS decodes JSON from a recorder into v.
func jsonUnmarshalFIS(t *testing.T, data []byte, v any) {
t.Helper()
- require.NoError(t, json.Unmarshal(data, v))
+ require.NoError(t, json.Unmarshal(data, v, jsonx.V1()))
}
func TestFISHandler_Name(t *testing.T) {
diff --git a/services/fis/persistence.go b/services/fis/persistence.go
index e1c9ebb6d9..18d79353b0 100644
--- a/services/fis/persistence.go
+++ b/services/fis/persistence.go
@@ -2,11 +2,13 @@ package fis
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -32,11 +34,11 @@ const fisSnapshotVersion = 1
// snapshot either, so idempotency tokens do not survive a snapshot/restore
// cycle -- this preserves that pre-existing behaviour unchanged.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- SafetyLever *SafetyLever `json:"safetyLever"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ SafetyLever *SafetyLever `json:"safetyLever"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
@@ -59,7 +61,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.region,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "fis: Snapshot marshal failure", "error", err)
diff --git a/services/fis/safety_levers_test.go b/services/fis/safety_levers_test.go
index 52827f70c3..ae2e274286 100644
--- a/services/fis/safety_levers_test.go
+++ b/services/fis/safety_levers_test.go
@@ -2,7 +2,8 @@ package fis_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fis"
)
@@ -74,13 +76,13 @@ func TestFISHandler_GetSafetyLever_NoTagsFieldOnWire(t *testing.T) {
rec2 := doRequest(t, h, http.MethodGet, "/safetyLevers/000000000000", nil)
require.Equal(t, http.StatusOK, rec2.Code)
- var raw map[string]json.RawMessage
+ var raw map[string]jsontext.Value
mustJSON(t, rec2, &raw)
- var lever map[string]json.RawMessage
+ var lever map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(raw["safetyLever"], &lever))
+ require.NoError(t, json.Unmarshal(raw["safetyLever"], &lever, jsonx.V1()))
_, hasTags := lever["tags"]
assert.False(t, hasTags, "GetSafetyLever response must not include a tags field")
diff --git a/services/fis/target_resolution_test.go b/services/fis/target_resolution_test.go
index 68bed50db0..4994964329 100644
--- a/services/fis/target_resolution_test.go
+++ b/services/fis/target_resolution_test.go
@@ -2,13 +2,14 @@ package fis_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/fis"
)
@@ -142,7 +143,7 @@ func TestStartExperiment_TargetResolution(t *testing.T) {
Status string `json:"status"`
Reason string `json:"reason"`
}
- require.NoError(t, json.Unmarshal(exp["status"], &status))
+ require.NoError(t, json.Unmarshal(exp["status"], &status, jsonx.V1()))
assert.Equal(t, tt.wantStatus, status.Status)
assert.Equal(t, tt.wantReason, status.Reason)
@@ -152,7 +153,7 @@ func TestStartExperiment_TargetResolution(t *testing.T) {
Status string `json:"status"`
} `json:"state"`
}
- require.NoError(t, json.Unmarshal(exp["actions"], &actions))
+ require.NoError(t, json.Unmarshal(exp["actions"], &actions, jsonx.V1()))
assert.Equal(t, tt.wantActions, actions["act"].State.Status)
}
diff --git a/services/fis/wire_field_test.go b/services/fis/wire_field_test.go
index 85315a0c61..864e3933b1 100644
--- a/services/fis/wire_field_test.go
+++ b/services/fis/wire_field_test.go
@@ -1,7 +1,8 @@
package fis_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http/httptest"
"testing"
@@ -14,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/fis"
@@ -229,11 +231,11 @@ func assertNoRawKey(t *testing.T, url, listKey, itemKey string) {
t.Cleanup(func() { _ = resp.Body.Close() })
- var body map[string]json.RawMessage
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&body))
+ var body map[string]jsontext.Value
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &body, jsonx.V1()))
- var items []map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(body[listKey], &items))
+ var items []map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(body[listKey], &items, jsonx.V1()))
require.NotEmpty(t, items)
for _, item := range items {
diff --git a/services/forecast/handler.go b/services/forecast/handler.go
index fdc672d6bc..9c6bf169fc 100644
--- a/services/forecast/handler.go
+++ b/services/forecast/handler.go
@@ -2,7 +2,7 @@ package forecast
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -126,7 +127,7 @@ func (h *Handler) Handler() echo.HandlerFunc {
func (h *Handler) dispatch(_ context.Context, action string, body []byte) ([]byte, error) {
var input map[string]any
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -165,7 +166,7 @@ func (h *Handler) dispatch(_ context.Context, action string, body []byte) ([]byt
return nil, err
}
- return json.Marshal(output)
+ return json.Marshal(output, jsonx.V1())
}
func (h *Handler) execute(action string, spec operationSpec, input map[string]any) (map[string]any, error) {
@@ -250,7 +251,7 @@ func (h *Handler) dispatchListMonitorEvaluations(input map[string]any) ([]byte,
out["NextToken"] = pg.Next
}
- return json.Marshal(out)
+ return json.Marshal(out, jsonx.V1())
}
// filterMonitorEvaluations applies ListMonitorEvaluations's Filters
@@ -321,7 +322,7 @@ func (h *Handler) dispatchDeleteResourceTree(input map[string]any) ([]byte, erro
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
func (h *Handler) dispatchResumeResource(input map[string]any) ([]byte, error) {
@@ -330,7 +331,7 @@ func (h *Handler) dispatchResumeResource(input map[string]any) ([]byte, error) {
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
func (h *Handler) dispatchStopResource(input map[string]any) ([]byte, error) {
@@ -339,7 +340,7 @@ func (h *Handler) dispatchStopResource(input map[string]any) ([]byte, error) {
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
func (h *Handler) dispatchGetAccuracyMetrics(input map[string]any) ([]byte, error) {
@@ -348,7 +349,7 @@ func (h *Handler) dispatchGetAccuracyMetrics(input map[string]any) ([]byte, erro
return nil, err
}
- return json.Marshal(metrics)
+ return json.Marshal(metrics, jsonx.V1())
}
func (h *Handler) dispatchListTagsForResource(input map[string]any) ([]byte, error) {
@@ -361,7 +362,7 @@ func (h *Handler) dispatchListTagsForResource(input map[string]any) ([]byte, err
tagList = append(tagList, map[string]string{"Key": k, "Value": v})
}
- return json.Marshal(map[string]any{"Tags": tagList})
+ return json.Marshal(map[string]any{"Tags": tagList}, jsonx.V1())
}
func (h *Handler) dispatchTagResource(input map[string]any) ([]byte, error) {
@@ -370,7 +371,7 @@ func (h *Handler) dispatchTagResource(input map[string]any) ([]byte, error) {
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
func (h *Handler) dispatchUntagResource(input map[string]any) ([]byte, error) {
@@ -385,7 +386,7 @@ func (h *Handler) dispatchUntagResource(input map[string]any) ([]byte, error) {
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
func resourceIdentifier(spec operationSpec, input map[string]any) string {
@@ -600,7 +601,10 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
code, errType = http.StatusInternalServerError, "InternalServerException"
}
- payload, marshalErr := json.Marshal(map[string]string{"__type": errType, "message": publicMessage(err, errType)})
+ payload, marshalErr := json.Marshal(
+ map[string]string{"__type": errType, "message": publicMessage(err, errType)},
+ jsonx.V1(),
+ )
if marshalErr != nil {
return c.String(http.StatusInternalServerError, "internal server error")
}
diff --git a/services/forecast/handler_sdk_route_table_test.go b/services/forecast/handler_sdk_route_table_test.go
index c91589b8da..94ec160973 100644
--- a/services/forecast/handler_sdk_route_table_test.go
+++ b/services/forecast/handler_sdk_route_table_test.go
@@ -1,7 +1,7 @@
package forecast_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/forecast"
)
@@ -167,7 +168,7 @@ func TestExtractOperation_SDKRouteTable(t *testing.T) {
var resp struct {
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEqual(t, "InvalidInputException: "+tc.op, resp.Message,
"target=%s op=%s: dispatched to the unmatched-route handler, body=%s",
tc.target, tc.op, rec.Body.String())
diff --git a/services/forecast/handler_test.go b/services/forecast/handler_test.go
index ecef421812..350a6cd5f7 100644
--- a/services/forecast/handler_test.go
+++ b/services/forecast/handler_test.go
@@ -2,7 +2,7 @@ package forecast_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/forecast"
)
@@ -40,7 +41,7 @@ func request(t *testing.T, h *forecast.Handler, operation string, body map[strin
func doRequest(t *testing.T, h *forecast.Handler, operation string, body map[string]any) *httptest.ResponseRecorder {
t.Helper()
- payload, err := json.Marshal(body)
+ payload, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -69,7 +70,7 @@ func unmarshalResponse(t *testing.T, rec *httptest.ResponseRecorder) map[string]
t.Helper()
var response map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &response))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &response, jsonx.V1()))
return response
}
diff --git a/services/forecast/persistence.go b/services/forecast/persistence.go
index ec45b7e8a9..7bac31da80 100644
--- a/services/forecast/persistence.go
+++ b/services/forecast/persistence.go
@@ -2,7 +2,7 @@ package forecast
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
@@ -41,7 +41,7 @@ const forecastSnapshotVersion = 1
// restored tables instead of trusting a persisted copy that could otherwise
// drift out of sync with the tables.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Evaluations map[string][]MonitorEvaluation `json:"evaluations"`
Tags map[string]map[string]string `json:"tags"`
AccountID string `json:"accountID"`
diff --git a/services/forecast/store.go b/services/forecast/store.go
index c59a6278c6..e59cea3732 100644
--- a/services/forecast/store.go
+++ b/services/forecast/store.go
@@ -1,7 +1,7 @@
package forecast
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"sort"
@@ -9,6 +9,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/store"
)
@@ -344,13 +345,13 @@ func cloneMap(data map[string]any) map[string]any {
return make(map[string]any)
}
- encoded, err := json.Marshal(data)
+ encoded, err := json.Marshal(data, jsonx.V1())
if err != nil {
return make(map[string]any)
}
var result map[string]any
- if unmarshalErr := json.Unmarshal(encoded, &result); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(encoded, &result, jsonx.V1()); unmarshalErr != nil {
return make(map[string]any)
}
diff --git a/services/fsx/client_request_token_test.go b/services/fsx/client_request_token_test.go
index 7f8d4aa5ac..3f742a41da 100644
--- a/services/fsx/client_request_token_test.go
+++ b/services/fsx/client_request_token_test.go
@@ -1,7 +1,7 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fsx"
)
@@ -17,7 +18,7 @@ func responseID(t *testing.T, body []byte, outKey, idKey string) string {
var out map[string]map[string]any
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
id, ok := out[outKey][idKey].(string)
require.True(t, ok, string(body))
diff --git a/services/fsx/data_repository_and_cache_config_test.go b/services/fsx/data_repository_and_cache_config_test.go
index dc097dfdc4..8cd831c7a9 100644
--- a/services/fsx/data_repository_and_cache_config_test.go
+++ b/services/fsx/data_repository_and_cache_config_test.go
@@ -1,13 +1,15 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDataRepositoryAssociation_S3Configuration(t *testing.T) {
@@ -90,7 +92,7 @@ func TestDataRepositoryAssociation_S3Configuration(t *testing.T) {
} `json:"Associations"`
}
- require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Associations, 1)
assert.Equal(t, tt.wantExport, out.Associations[0].S3.AutoExportPolicy.Events)
assert.Equal(t, tt.wantImport, out.Associations[0].S3.AutoImportPolicy.Events)
@@ -130,7 +132,7 @@ func TestDeleteDataRepositoryAssociation_DeleteDataInFileSystem(t *testing.T) {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "DELETING", out["Lifecycle"])
assert.Equal(t, tt.flag, out["DeleteDataInFileSystem"])
})
@@ -176,7 +178,7 @@ func TestCreateFileCache_AssociationsAndCopyTags(t *testing.T) {
} `json:"FileCache"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
require.Len(t, created.FileCache.DRAIDs, 1)
require.NotNil(t, created.FileCache.CopyTags)
assert.Equal(t, tt.copyTags, *created.FileCache.CopyTags)
@@ -200,7 +202,7 @@ func TestCreateFileCache_AssociationsAndCopyTags(t *testing.T) {
} `json:"Associations"`
}
- require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &assoc))
+ require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &assoc, jsonx.V1()))
require.Len(t, assoc.Associations, 1)
a := assoc.Associations[0]
diff --git a/services/fsx/file_cache_wire_test.go b/services/fsx/file_cache_wire_test.go
index 9d2c1220e9..26c731e1d0 100644
--- a/services/fsx/file_cache_wire_test.go
+++ b/services/fsx/file_cache_wire_test.go
@@ -1,7 +1,7 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/fsx/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// testFileCacheLustreConfig builds a minimal valid CreateFileCache
@@ -74,7 +76,7 @@ func TestFileCache_TagsWireShape(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
fcID := createOut["FileCache"].(map[string]any)["FileCacheId"].(string)
describeRec := doFSxRequest(t, h, "DescribeFileCaches", map[string]any{
@@ -83,7 +85,7 @@ func TestFileCache_TagsWireShape(t *testing.T) {
require.Equal(t, http.StatusOK, describeRec.Code)
var describeOut map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeOut))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeOut, jsonx.V1()))
caches, ok := describeOut["FileCaches"].([]any)
require.True(t, ok)
@@ -113,7 +115,7 @@ func TestFileCache_TagsWireShape(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
fcID := createOut["FileCache"].(map[string]any)["FileCacheId"].(string)
updateRec := doFSxRequest(t, h, "UpdateFileCache", map[string]any{
@@ -123,7 +125,7 @@ func TestFileCache_TagsWireShape(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateOut map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut, jsonx.V1()))
fc, ok := updateOut["FileCache"].(map[string]any)
require.True(t, ok)
diff --git a/services/fsx/file_system_type_configuration_test.go b/services/fsx/file_system_type_configuration_test.go
index 394ee02d02..3377fb111b 100644
--- a/services/fsx/file_system_type_configuration_test.go
+++ b/services/fsx/file_system_type_configuration_test.go
@@ -1,12 +1,14 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestFSx_FileSystem_WindowsConfiguration locks the wire shape of the
@@ -31,7 +33,7 @@ func TestFSx_FileSystem_WindowsConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
fs := out["FileSystem"].(map[string]any)
require.Contains(t, fs, "WindowsConfiguration")
@@ -62,7 +64,7 @@ func TestFSx_FileSystem_OntapConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
fs := out["FileSystem"].(map[string]any)
require.Contains(t, fs, "OntapConfiguration")
@@ -96,7 +98,7 @@ func TestFSx_FileSystem_OpenZFSConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
fs := out["FileSystem"].(map[string]any)
require.Contains(t, fs, "OpenZFSConfiguration")
@@ -114,7 +116,7 @@ func TestFSx_FileSystem_OpenZFSConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, describeRec.Code)
var describeOut map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeOut))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeOut, jsonx.V1()))
volumes := describeOut["Volumes"].([]any)
require.Len(t, volumes, 1)
assert.Equal(t, "fsx", volumes[0].(map[string]any)["Name"])
@@ -150,7 +152,7 @@ func TestFSx_CreateFileSystem_MissingConfiguration(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "MissingFileSystemConfiguration", out["__type"])
})
}
@@ -210,7 +212,7 @@ func TestFSx_CreateFileSystem_RequiredConfigMembers(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "BadRequest", out["__type"])
})
}
@@ -235,7 +237,7 @@ func TestFSx_UpdateFileSystem_TypeSpecificConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
fs := out["FileSystem"].(map[string]any)
winCfg := fs["WindowsConfiguration"].(map[string]any)
assert.InEpsilon(t, float64(32), winCfg["ThroughputCapacity"], 0)
@@ -257,7 +259,7 @@ func TestFSx_CreateFileSystemFromBackup_CarriesTypeConfig(t *testing.T) {
require.Equal(t, http.StatusOK, backupRec.Code)
var backupOut map[string]any
- require.NoError(t, json.Unmarshal(backupRec.Body.Bytes(), &backupOut))
+ require.NoError(t, json.Unmarshal(backupRec.Body.Bytes(), &backupOut, jsonx.V1()))
backupID := backupOut["Backup"].(map[string]any)["BackupId"].(string)
restoreRec := doFSxRequest(
@@ -269,7 +271,7 @@ func TestFSx_CreateFileSystemFromBackup_CarriesTypeConfig(t *testing.T) {
require.Equal(t, http.StatusOK, restoreRec.Code)
var restoreOut map[string]any
- require.NoError(t, json.Unmarshal(restoreRec.Body.Bytes(), &restoreOut))
+ require.NoError(t, json.Unmarshal(restoreRec.Body.Bytes(), &restoreOut, jsonx.V1()))
restoredFS := restoreOut["FileSystem"].(map[string]any)
require.Contains(t, restoredFS, "WindowsConfiguration")
diff --git a/services/fsx/file_systems.go b/services/fsx/file_systems.go
index b26db974dd..180699575a 100644
--- a/services/fsx/file_systems.go
+++ b/services/fsx/file_systems.go
@@ -1,7 +1,7 @@
package fsx
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"regexp"
@@ -12,6 +12,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// storedFileSystem is the persisted form of a FileSystem.
@@ -486,7 +487,7 @@ func fingerprintCreateFileSystemInput(input *createFileSystemInput) (string, err
cp := *input
cp.ClientRequestToken = ""
- b, err := json.Marshal(cp)
+ b, err := json.Marshal(cp, jsonx.V1())
if err != nil {
return "", fmt.Errorf("canonicalize CreateFileSystem request: %w", err)
}
diff --git a/services/fsx/handler.go b/services/fsx/handler.go
index ba6804a5ec..c1ef5bbf11 100644
--- a/services/fsx/handler.go
+++ b/services/fsx/handler.go
@@ -2,7 +2,7 @@ package fsx
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -198,7 +199,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
ResourceARN string `json:"ResourceARN"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
switch {
case req.ResourceARN != "":
@@ -326,7 +327,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- out, err := json.Marshal(result)
+ out, err := json.Marshal(result, jsonx.V1())
if err != nil {
return nil, err
}
diff --git a/services/fsx/handler_backups_test.go b/services/fsx/handler_backups_test.go
index 88fe101835..d32a710184 100644
--- a/services/fsx/handler_backups_test.go
+++ b/services/fsx/handler_backups_test.go
@@ -1,7 +1,7 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fsx"
)
@@ -22,7 +23,7 @@ func TestFSx_Backup(t *testing.T) {
rec := doFSxRequest(t, h, "CreateBackup", map[string]any{"FileSystemId": fsID})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
bk := resp["Backup"].(map[string]any)
assert.Contains(t, bk["BackupId"].(string), "backup-")
assert.Contains(t, bk["ResourceARN"].(string), "arn:aws:fsx:")
@@ -46,7 +47,7 @@ func TestFSx_Backup(t *testing.T) {
rec := doFSxRequest(t, h, "DescribeBackups", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
bks := resp["Backups"].([]any)
assert.Len(t, bks, 2)
})
@@ -58,7 +59,7 @@ func TestFSx_Backup(t *testing.T) {
rec := doFSxRequest(t, h, "DescribeBackups", map[string]any{"BackupIds": []string{bkID}})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
bks := resp["Backups"].([]any)
require.Len(t, bks, 1)
assert.Equal(t, bkID, bks[0].(map[string]any)["BackupId"])
@@ -92,7 +93,7 @@ func TestFSx_Backup(t *testing.T) {
)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fs := resp["FileSystem"].(map[string]any)
assert.Contains(t, fs["FileSystemId"].(string), "fs-")
assert.Equal(t, "AVAILABLE", fs["Lifecycle"])
@@ -141,7 +142,7 @@ func TestFSx_CreateFileSystemFromBackup_FileSystemTypeVersion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fs := resp["FileSystem"].(map[string]any)
got, _ := fs["FileSystemTypeVersion"].(string)
@@ -184,7 +185,7 @@ func TestFSx_CopyBackup(t *testing.T) {
if tc.wantCode == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
bk := out["Backup"].(map[string]any)
assert.Contains(t, bk["BackupId"].(string), "backup-")
newID := bk["BackupId"].(string)
@@ -219,7 +220,7 @@ func TestFSx_DeleteBackup_Response(t *testing.T) {
require.Equal(t, tc.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, bkID, resp["BackupId"])
assert.Equal(t, tc.wantLC, resp["Lifecycle"])
})
@@ -261,7 +262,7 @@ func TestFSx_BackupNotFound_ErrorCode(t *testing.T) {
require.Equal(t, tc.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantType, resp["__type"])
})
}
diff --git a/services/fsx/handler_data_repository_associations_test.go b/services/fsx/handler_data_repository_associations_test.go
index 4a5e343788..73cebdef89 100644
--- a/services/fsx/handler_data_repository_associations_test.go
+++ b/services/fsx/handler_data_repository_associations_test.go
@@ -1,13 +1,14 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fsx"
)
@@ -42,7 +43,7 @@ func TestFSx_DataRepositoryAssociation(t *testing.T) {
require.Equal(t, tc.wantCode, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
a := out["Association"].(map[string]any)
assert.Contains(t, a["AssociationId"].(string), "dra-")
assert.Equal(t, tc.fsPath, a["FileSystemPath"])
@@ -67,7 +68,7 @@ func TestFSx_DataRepositoryAssociationLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
id := cr["Association"].(map[string]any)["AssociationId"].(string)
assert.Equal(t, 1, fsx.DRACount(b))
@@ -79,7 +80,7 @@ func TestFSx_DataRepositoryAssociationLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec2.Code)
var ur map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &ur))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &ur, jsonx.V1()))
assert.Equal(t, "s3://bucket/new", ur["Association"].(map[string]any)["DataRepositoryPath"])
// describe
@@ -88,7 +89,7 @@ func TestFSx_DataRepositoryAssociationLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec3.Code)
var dr map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &dr))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &dr, jsonx.V1()))
assert.Len(t, dr["Associations"].([]any), 1)
// delete
diff --git a/services/fsx/handler_data_repository_tasks_test.go b/services/fsx/handler_data_repository_tasks_test.go
index af58826f38..65004c612c 100644
--- a/services/fsx/handler_data_repository_tasks_test.go
+++ b/services/fsx/handler_data_repository_tasks_test.go
@@ -1,13 +1,15 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestFSx_DataRepositoryTask(t *testing.T) {
@@ -45,7 +47,7 @@ func TestFSx_DataRepositoryTask(t *testing.T) {
require.Equal(t, tc.wantCode, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
task := out["DataRepositoryTask"].(map[string]any)
assert.Contains(t, task["TaskId"].(string), "task-")
assert.Equal(t, "EXECUTING", task["Lifecycle"])
@@ -105,7 +107,7 @@ func TestFSx_DataRepositoryTaskLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
taskID := cr["DataRepositoryTask"].(map[string]any)["TaskId"].(string)
// describe
@@ -114,14 +116,14 @@ func TestFSx_DataRepositoryTaskLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec2.Code)
var dr map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &dr))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &dr, jsonx.V1()))
assert.Len(t, dr["DataRepositoryTasks"].([]any), 1)
// cancel
rec3 := doFSxRequest(t, h, "CancelDataRepositoryTask", map[string]any{"TaskId": taskID})
require.Equal(t, http.StatusOK, rec3.Code)
var car map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &car))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &car, jsonx.V1()))
assert.Equal(t, "CANCELING", car["Lifecycle"])
})
}
@@ -158,7 +160,7 @@ func TestFSx_CreateDataRepositoryTask_RejectsConcurrentExecuting(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec2.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "DataRepositoryTaskExecuting", out["__type"])
})
@@ -183,7 +185,7 @@ func TestFSx_CreateDataRepositoryTask_RejectsConcurrentExecuting(t *testing.T) {
rec1 := doFSxRequest(t, h, "CreateDataRepositoryTask", newTaskBody(fsID))
require.Equal(t, http.StatusOK, rec1.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &cr, jsonx.V1()))
taskID := cr["DataRepositoryTask"].(map[string]any)["TaskId"].(string)
cancelRec := doFSxRequest(t, h, "CancelDataRepositoryTask", map[string]any{"TaskId": taskID})
@@ -242,7 +244,7 @@ func TestDataRepositoryTask_TagsStoredAtCreation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
task := out["DataRepositoryTask"].(map[string]any)
taskARN := task["ResourceARN"].(string)
@@ -253,7 +255,7 @@ func TestDataRepositoryTask_TagsStoredAtCreation(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code, "ListTagsForResource on DRT must succeed")
var tagOut map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagOut))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagOut, jsonx.V1()))
tags, ok := tagOut["Tags"].([]any)
require.True(t, ok, "Tags must be a JSON array")
@@ -279,7 +281,7 @@ func TestDataRepositoryTask_TagResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
taskARN := out["DataRepositoryTask"].(map[string]any)["ResourceARN"].(string)
// TagResource on a DRT ARN must succeed.
@@ -294,7 +296,7 @@ func TestDataRepositoryTask_TagResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var tagOut map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &tagOut))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &tagOut, jsonx.V1()))
tags := tagOut["Tags"].([]any)
require.Len(t, tags, 1)
assert.Equal(t, "added", tags[0].(map[string]any)["Key"])
diff --git a/services/fsx/handler_file_caches_test.go b/services/fsx/handler_file_caches_test.go
index 2bfb7686ba..efe22302fd 100644
--- a/services/fsx/handler_file_caches_test.go
+++ b/services/fsx/handler_file_caches_test.go
@@ -1,13 +1,14 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fsx"
)
@@ -83,7 +84,7 @@ func TestFSx_CreateFileCache_RequiresLustreConfiguration(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tc.wantType, out["__type"])
})
}
@@ -104,7 +105,7 @@ func TestFSx_CreateFileCache_RequiresLustreConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
fc := out["FileCache"].(map[string]any)
lc, ok := fc["LustreConfiguration"].(map[string]any)
require.True(t, ok, "response must have a LustreConfiguration block")
@@ -188,7 +189,7 @@ func TestFSx_FileCache(t *testing.T) {
if !tc.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
c := out["FileCache"].(map[string]any)
assert.Contains(t, c["FileCacheId"].(string), "fc-")
assert.Equal(t, "AVAILABLE", c["Lifecycle"])
@@ -218,7 +219,7 @@ func TestFSx_FileCacheLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var dr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dr, jsonx.V1()))
assert.Len(t, dr["FileCaches"].([]any), 1)
// update
@@ -234,7 +235,7 @@ func TestFSx_FileCacheLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec2.Code)
var ur map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &ur))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &ur, jsonx.V1()))
lc := ur["FileCache"].(map[string]any)["LustreConfiguration"].(map[string]any)
assert.Equal(t, "3:04:05", lc["WeeklyMaintenanceStartTime"])
@@ -242,7 +243,7 @@ func TestFSx_FileCacheLifecycle(t *testing.T) {
rec3 := doFSxRequest(t, h, "DeleteFileCache", map[string]any{"FileCacheId": fcID})
require.Equal(t, http.StatusOK, rec3.Code)
var del map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &del))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &del, jsonx.V1()))
assert.Equal(t, fcID, del["FileCacheId"])
assert.Equal(t, "DELETING", del["Lifecycle"])
assert.Equal(t, 0, fsx.FileCacheCount(b))
diff --git a/services/fsx/handler_file_systems_test.go b/services/fsx/handler_file_systems_test.go
index d2d3cbc2dd..2581fd80bb 100644
--- a/services/fsx/handler_file_systems_test.go
+++ b/services/fsx/handler_file_systems_test.go
@@ -1,13 +1,14 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fsx"
)
@@ -29,7 +30,7 @@ func TestFSx_FileSystem(t *testing.T) {
)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fs := resp["FileSystem"].(map[string]any)
assert.Contains(t, fs["FileSystemId"].(string), "fs-")
assert.Contains(t, fs["ResourceARN"].(string), "arn:aws:fsx:")
@@ -52,7 +53,7 @@ func TestFSx_FileSystem(t *testing.T) {
rec := doFSxRequest(t, h, "DescribeFileSystems", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fss := resp["FileSystems"].([]any)
assert.Len(t, fss, 2)
})
@@ -65,7 +66,7 @@ func TestFSx_FileSystem(t *testing.T) {
rec := doFSxRequest(t, h, "DescribeFileSystems", map[string]any{"FileSystemIds": []string{id}})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fss := resp["FileSystems"].([]any)
require.Len(t, fss, 1)
assert.Equal(t, id, fss[0].(map[string]any)["FileSystemId"])
@@ -101,7 +102,7 @@ func TestFSx_FileSystem(t *testing.T) {
rec := doFSxRequest(t, h, "UpdateFileSystem", map[string]any{"FileSystemId": id, "StorageCapacity": 64})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fs := resp["FileSystem"].(map[string]any)
assert.Equal(t, id, fs["FileSystemId"])
})
@@ -131,13 +132,13 @@ func TestFSx_FileSystem(t *testing.T) {
rec1 := doFSxRequest(t, h, "CreateFileSystem", body)
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
fs1 := resp1["FileSystem"].(map[string]any)
rec2 := doFSxRequest(t, h, "CreateFileSystem", body)
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
fs2 := resp2["FileSystem"].(map[string]any)
// Prove it's the SAME resource, not just a second success: identical
@@ -169,7 +170,7 @@ func TestFSx_FileSystem(t *testing.T) {
})
require.Equal(t, http.StatusBadRequest, rec2.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "IncompatibleParameterError", errResp["__type"])
assert.Equal(t, 1, fsx.FileSystemCount(fsx.GetBackend(h)), "mismatched retry must not create a resource")
})
@@ -189,8 +190,8 @@ func TestFSx_FileSystem(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp1, resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.NotEqual(
t,
resp1["FileSystem"].(map[string]any)["FileSystemId"],
@@ -213,7 +214,7 @@ func TestFSx_FileSystem(t *testing.T) {
})
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidNetworkSettings", errResp["__type"])
})
@@ -228,7 +229,7 @@ func TestFSx_FileSystem(t *testing.T) {
})
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidNetworkSettings", errResp["__type"])
})
@@ -404,7 +405,7 @@ func TestFSx_DeleteFileSystem_Response(t *testing.T) {
require.Equal(t, tc.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tc.wantID {
assert.Equal(t, id, resp["FileSystemId"])
@@ -458,7 +459,7 @@ func TestFSx_Aliases(t *testing.T) {
require.Equal(t, tc.wantCode, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
aliases := out["Aliases"].([]any)
assert.Len(t, aliases, tc.wantCount)
assert.Equal(t, tc.lifecycle, aliases[0].(map[string]any)["Lifecycle"])
@@ -482,7 +483,7 @@ func TestFSx_AliasesLifecycle(t *testing.T) {
rec := doFSxRequest(t, h, "DescribeFileSystemAliases", map[string]any{"FileSystemId": fsID})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out["Aliases"].([]any), 1)
})
@@ -502,14 +503,14 @@ func TestFSx_AliasesLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
aliases := out["Aliases"].([]any)
assert.Len(t, aliases, 1)
assert.Equal(t, "DELETING", aliases[0].(map[string]any)["Lifecycle"])
rec2 := doFSxRequest(t, h, "DescribeFileSystemAliases", map[string]any{"FileSystemId": fsID})
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
assert.Empty(t, out2["Aliases"].([]any))
})
}
@@ -558,7 +559,7 @@ func TestFSx_ReleaseFileSystemNfsV3Locks(t *testing.T) {
if !tc.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotNil(t, out["FileSystem"])
}
})
@@ -605,7 +606,7 @@ func TestFSx_StartMisconfiguredStateRecovery(t *testing.T) {
if !tc.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotNil(t, out["FileSystem"])
}
})
diff --git a/services/fsx/handler_s3_access_points_test.go b/services/fsx/handler_s3_access_points_test.go
index 201c703aa6..1f9f7f3965 100644
--- a/services/fsx/handler_s3_access_points_test.go
+++ b/services/fsx/handler_s3_access_points_test.go
@@ -1,12 +1,14 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestFSx_S3AccessPoint(t *testing.T) {
@@ -59,7 +61,7 @@ func TestFSx_S3AccessPoint(t *testing.T) {
if !tc.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
ap := out["S3AccessPointAttachment"].(map[string]any)
assert.Equal(t, tc.apName, ap["Name"])
assert.Equal(t, "AVAILABLE", ap["Lifecycle"])
@@ -93,7 +95,7 @@ func TestFSx_S3AccessPointLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var dr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dr, jsonx.V1()))
assert.Len(t, dr["S3AccessPointAttachments"].([]any), 1)
// detach: real DetachAndDeleteS3AccessPointInput has no FileSystemId member.
@@ -106,7 +108,7 @@ func TestFSx_S3AccessPointLifecycle(t *testing.T) {
rec3 := doFSxRequest(t, h, "DescribeS3AccessPointAttachments", map[string]any{})
require.Equal(t, http.StatusOK, rec3.Code)
var dr2 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &dr2))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &dr2, jsonx.V1()))
assert.Empty(t, dr2["S3AccessPointAttachments"].([]any))
})
}
diff --git a/services/fsx/handler_shared_vpc_configuration_test.go b/services/fsx/handler_shared_vpc_configuration_test.go
index eb98d13165..466437fae0 100644
--- a/services/fsx/handler_shared_vpc_configuration_test.go
+++ b/services/fsx/handler_shared_vpc_configuration_test.go
@@ -1,12 +1,14 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestFSx_SharedVpcConfiguration(t *testing.T) {
@@ -47,7 +49,7 @@ func TestFSx_SharedVpcConfiguration(t *testing.T) {
rec := doFSxRequest(t, h, "DescribeSharedVpcConfiguration", map[string]any{})
require.Equal(t, tc.wantCode, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tc.wantEnabled, out["EnableFsxRouteTableUpdatesFromParticipantAccounts"])
})
}
diff --git a/services/fsx/handler_snapshots_test.go b/services/fsx/handler_snapshots_test.go
index edabddc164..bad63fb805 100644
--- a/services/fsx/handler_snapshots_test.go
+++ b/services/fsx/handler_snapshots_test.go
@@ -1,13 +1,14 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fsx"
)
@@ -49,7 +50,7 @@ func TestFSx_Snapshot(t *testing.T) {
if !tc.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
s := out["Snapshot"].(map[string]any)
assert.Contains(t, s["SnapshotId"].(string), "fsvolsnap-")
assert.Equal(t, "AVAILABLE", s["Lifecycle"])
@@ -75,7 +76,7 @@ func TestFSx_SnapshotLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
snapID := cr["Snapshot"].(map[string]any)["SnapshotId"].(string)
assert.Equal(t, 1, fsx.SnapshotCount(b))
@@ -86,7 +87,7 @@ func TestFSx_SnapshotLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec2.Code)
var dr map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &dr))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &dr, jsonx.V1()))
assert.Len(t, dr["Snapshots"].([]any), 1)
// update
@@ -96,14 +97,14 @@ func TestFSx_SnapshotLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec3.Code)
var ur map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &ur))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &ur, jsonx.V1()))
assert.Equal(t, "snap-renamed", ur["Snapshot"].(map[string]any)["Name"])
// delete
rec4 := doFSxRequest(t, h, "DeleteSnapshot", map[string]any{"SnapshotId": snapID})
require.Equal(t, http.StatusOK, rec4.Code)
var del map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &del))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &del, jsonx.V1()))
assert.Equal(t, snapID, del["SnapshotId"])
assert.Equal(t, "DELETING", del["Lifecycle"])
assert.Equal(t, 0, fsx.SnapshotCount(b))
@@ -125,7 +126,7 @@ func TestFSx_CopySnapshotAndUpdateVolume(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
snapARN := cr["Snapshot"].(map[string]any)["ResourceARN"].(string)
rec2 := doFSxRequest(t, h, "CopySnapshotAndUpdateVolume", map[string]any{
@@ -134,7 +135,7 @@ func TestFSx_CopySnapshotAndUpdateVolume(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec2.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, volID, out["VolumeId"])
assert.NotEmpty(t, out["Lifecycle"])
actions := out["AdministrativeActions"].([]any)
diff --git a/services/fsx/handler_storage_virtual_machines_test.go b/services/fsx/handler_storage_virtual_machines_test.go
index 148b0cb082..9b451f122c 100644
--- a/services/fsx/handler_storage_virtual_machines_test.go
+++ b/services/fsx/handler_storage_virtual_machines_test.go
@@ -1,13 +1,14 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fsx"
)
@@ -50,7 +51,7 @@ func TestFSx_StorageVirtualMachine(t *testing.T) {
if !tc.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
svm := out["StorageVirtualMachine"].(map[string]any)
assert.Contains(t, svm["StorageVirtualMachineId"].(string), "svm-")
assert.Equal(t, "CREATED", svm["Lifecycle"])
@@ -78,7 +79,7 @@ func TestFSx_SVMLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var dr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dr, jsonx.V1()))
assert.Len(t, dr["StorageVirtualMachines"].([]any), 1)
// update: SvmAdminPassword is the real op's only scalar updatable
@@ -90,7 +91,7 @@ func TestFSx_SVMLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec2.Code)
var ur map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &ur))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &ur, jsonx.V1()))
assert.Equal(t, svmID, ur["StorageVirtualMachine"].(map[string]any)["StorageVirtualMachineId"])
// delete
@@ -99,7 +100,7 @@ func TestFSx_SVMLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec3.Code)
var del map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &del))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &del, jsonx.V1()))
assert.Equal(t, svmID, del["StorageVirtualMachineId"])
assert.Equal(t, "DELETING", del["Lifecycle"])
assert.Equal(t, 0, fsx.SVMCount(b))
diff --git a/services/fsx/handler_tags_test.go b/services/fsx/handler_tags_test.go
index 4ed4d665ec..bec07c5fb2 100644
--- a/services/fsx/handler_tags_test.go
+++ b/services/fsx/handler_tags_test.go
@@ -1,7 +1,8 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fsx"
)
@@ -25,7 +27,7 @@ func TestFSx_Tags(t *testing.T) {
)
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["FileSystem"].(map[string]any)["ResourceARN"].(string)
}
@@ -52,7 +54,7 @@ func TestFSx_Tags(t *testing.T) {
rec := doFSxRequest(t, h, "ListTagsForResource", map[string]any{"ResourceARN": arn})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags := resp["Tags"].([]any)
assert.Len(t, tags, 1)
tag := tags[0].(map[string]any)
@@ -72,7 +74,7 @@ func TestFSx_Tags(t *testing.T) {
rec := doFSxRequest(t, h, "ListTagsForResource", map[string]any{"ResourceARN": arn})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags := resp["Tags"].([]any)
assert.Len(t, tags, 1)
assert.Equal(t, "Name", tags[0].(map[string]any)["Key"])
@@ -148,7 +150,7 @@ func TestFSx_TagValidation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
arn := out["FileSystem"].(map[string]any)["ResourceARN"].(string)
@@ -160,7 +162,7 @@ func TestFSx_TagValidation(t *testing.T) {
if tc.wantType != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantType, resp["__type"])
}
})
@@ -204,7 +206,7 @@ func TestFSx_TagValidation_OnCreate(t *testing.T) {
if tc.wantType != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantType, resp["__type"])
}
})
@@ -227,7 +229,7 @@ func TestFSx_TagLimit(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
arn := out["FileSystem"].(map[string]any)["ResourceARN"].(string)
@@ -248,7 +250,7 @@ func TestFSx_TagLimit(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// TagResource's own switch (fsx@v1.68.4 deserializers.go
// deserializeOpErrorTagResource) does not declare
// ServiceLimitExceeded; BadRequest is the correct wire type
@@ -269,7 +271,7 @@ func TestFSx_TagLimit(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
arn := out["FileSystem"].(map[string]any)["ResourceARN"].(string)
@@ -312,7 +314,7 @@ func TestListTagsForResource_EmptyIsArray(t *testing.T) {
)
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["FileSystem"].(map[string]any)["ResourceARN"].(string)
},
@@ -324,7 +326,7 @@ func TestListTagsForResource_EmptyIsArray(t *testing.T) {
rec := doFSxRequest(t, h, "CreateBackup", map[string]any{"FileSystemId": fsID})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["Backup"].(map[string]any)["ResourceARN"].(string)
},
@@ -342,8 +344,8 @@ func TestListTagsForResource_EmptyIsArray(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
// Parse the raw JSON to verify "Tags" is an array, not null.
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
tagsRaw, ok := raw["Tags"]
require.True(t, ok, "Tags key must be present")
@@ -398,7 +400,7 @@ func Test_TagOps_UnknownARN_ReturnsResourceNotFound(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errBody map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "ResourceNotFound", errBody["__type"],
"%s on an unknown ARN must return the generic ResourceNotFound code", tt.op)
})
diff --git a/services/fsx/handler_test.go b/services/fsx/handler_test.go
index 1147cc1db1..4e83ca551a 100644
--- a/services/fsx/handler_test.go
+++ b/services/fsx/handler_test.go
@@ -2,7 +2,7 @@ package fsx_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fsx"
)
@@ -33,7 +34,7 @@ func doFSxRequest(t *testing.T, h *fsx.Handler, op string, body any) *httptest.R
if body != nil {
var marshalErr error
- bodyBytes, marshalErr = json.Marshal(body)
+ bodyBytes, marshalErr = json.Marshal(body, jsonx.V1())
require.NoError(t, marshalErr)
}
@@ -76,7 +77,7 @@ func createFS(t *testing.T, h *fsx.Handler, fsType string) string {
rec := doFSxRequest(t, h, "CreateFileSystem", fileSystemCreateBody(fsType))
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["FileSystem"].(map[string]any)["FileSystemId"].(string)
}
@@ -87,7 +88,7 @@ func createFSandBackup(t *testing.T, h *fsx.Handler, fsType string) string {
rec := doFSxRequest(t, h, "CreateBackup", map[string]any{"FileSystemId": fsID})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["Backup"].(map[string]any)["BackupId"].(string)
}
@@ -123,7 +124,7 @@ func createVolume(t *testing.T, h *fsx.Handler, fsID, volType, name string) stri
rec := doFSxRequest(t, h, "CreateVolume", body)
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["Volume"].(map[string]any)["VolumeId"].(string)
}
@@ -136,7 +137,7 @@ func openZFSRootVolumeID(t *testing.T, h *fsx.Handler, fsID string) string {
rec := doFSxRequest(t, h, "DescribeFileSystems", map[string]any{"FileSystemIds": []string{fsID}})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
fileSystems, ok := out["FileSystems"].([]any)
require.True(t, ok)
require.Len(t, fileSystems, 1)
@@ -156,7 +157,7 @@ func createSVM(t *testing.T, h *fsx.Handler, fsID, name string) string {
})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["StorageVirtualMachine"].(map[string]any)["StorageVirtualMachineId"].(string)
}
@@ -172,7 +173,7 @@ func createFileCache(t *testing.T, h *fsx.Handler, cacheType string) string { //
})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["FileCache"].(map[string]any)["FileCacheId"].(string)
}
@@ -196,7 +197,7 @@ func decodeField(t *testing.T, rec *httptest.ResponseRecorder, field string) map
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
obj, ok := out[field].(map[string]any)
require.True(t, ok, "response must contain object field %q, got %v", field, out)
diff --git a/services/fsx/handler_volumes_test.go b/services/fsx/handler_volumes_test.go
index 3d2cc3ff14..1fc4af2f55 100644
--- a/services/fsx/handler_volumes_test.go
+++ b/services/fsx/handler_volumes_test.go
@@ -1,7 +1,7 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fsx"
)
@@ -74,7 +75,7 @@ func TestFSx_Volume(t *testing.T) {
if !tc.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
v := out["Volume"].(map[string]any)
assert.Contains(t, v["VolumeId"].(string), "fsvol-")
assert.Equal(t, "AVAILABLE", v["Lifecycle"])
@@ -102,7 +103,7 @@ func TestFSx_VolumeLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var dr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dr, jsonx.V1()))
assert.Len(t, dr["Volumes"].([]any), 1)
// update
@@ -112,14 +113,14 @@ func TestFSx_VolumeLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec2.Code)
var ur map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &ur))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &ur, jsonx.V1()))
assert.Equal(t, "vol-renamed", ur["Volume"].(map[string]any)["Name"])
// delete
rec3 := doFSxRequest(t, h, "DeleteVolume", map[string]any{"VolumeId": volID})
require.Equal(t, http.StatusOK, rec3.Code)
var del map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &del))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &del, jsonx.V1()))
assert.Equal(t, volID, del["VolumeId"])
assert.Equal(t, "DELETING", del["Lifecycle"])
assert.Equal(t, 0, fsx.VolumeCount(b))
@@ -142,7 +143,7 @@ func TestFSx_CreateVolumeFromBackup(t *testing.T) {
})
require.Equal(t, http.StatusOK, svmRec.Code)
var svmOut map[string]any
- require.NoError(t, json.Unmarshal(svmRec.Body.Bytes(), &svmOut))
+ require.NoError(t, json.Unmarshal(svmRec.Body.Bytes(), &svmOut, jsonx.V1()))
svmID := svmOut["StorageVirtualMachine"].(map[string]any)["StorageVirtualMachineId"].(string)
rec := doFSxRequest(t, h, "CreateVolumeFromBackup", map[string]any{
@@ -155,7 +156,7 @@ func TestFSx_CreateVolumeFromBackup(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
v := out["Volume"].(map[string]any)
assert.Contains(t, v["VolumeId"].(string), "fsvol-")
assert.Equal(t, "restored-vol", v["Name"])
@@ -176,7 +177,7 @@ func TestFSx_CreateVolumeFromBackup(t *testing.T) {
})
require.Equal(t, http.StatusOK, svmRec.Code)
var svmOut map[string]any
- require.NoError(t, json.Unmarshal(svmRec.Body.Bytes(), &svmOut))
+ require.NoError(t, json.Unmarshal(svmRec.Body.Bytes(), &svmOut, jsonx.V1()))
svmID := svmOut["StorageVirtualMachine"].(map[string]any)["StorageVirtualMachineId"].(string)
rec := doFSxRequest(t, h, "CreateVolumeFromBackup", map[string]any{
@@ -219,7 +220,7 @@ func TestFSx_RestoreVolumeFromSnapshot(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
snapID := cr["Snapshot"].(map[string]any)["SnapshotId"].(string)
// restore
@@ -229,7 +230,7 @@ func TestFSx_RestoreVolumeFromSnapshot(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec2.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, volID, out["VolumeId"])
assert.NotEmpty(t, out["Lifecycle"])
actions := out["AdministrativeActions"].([]any)
diff --git a/services/fsx/idempotency.go b/services/fsx/idempotency.go
index 624a8e1e28..4a5374c793 100644
--- a/services/fsx/idempotency.go
+++ b/services/fsx/idempotency.go
@@ -1,9 +1,11 @@
package fsx
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const clientRequestTokenField = "ClientRequestToken"
@@ -11,19 +13,19 @@ const clientRequestTokenField = "ClientRequestToken"
// tokenFingerprint canonicalises input minus its ClientRequestToken so a retry
// with the same parameters hashes equal and a changed one does not.
func tokenFingerprint(input any) (string, error) {
- raw, err := json.Marshal(input)
+ raw, err := json.Marshal(input, jsonx.V1())
if err != nil {
return "", fmt.Errorf("canonicalize request: %w", err)
}
var fields map[string]any
- if err = json.Unmarshal(raw, &fields); err != nil {
+ if err = json.Unmarshal(raw, &fields, jsonx.V1()); err != nil {
return "", fmt.Errorf("canonicalize request: %w", err)
}
delete(fields, clientRequestTokenField)
- out, err := json.Marshal(fields)
+ out, err := json.Marshal(fields, jsonx.V1())
if err != nil {
return "", fmt.Errorf("canonicalize request: %w", err)
}
diff --git a/services/fsx/interfaces.go b/services/fsx/interfaces.go
index 9471a45ac5..b78437d688 100644
--- a/services/fsx/interfaces.go
+++ b/services/fsx/interfaces.go
@@ -2,7 +2,7 @@ package fsx
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"strconv"
"time"
)
@@ -230,10 +230,10 @@ type LustreConfiguration struct {
DriveCacheType string `json:"DriveCacheType,omitempty"`
MountName string `json:"MountName,omitempty"`
WeeklyMaintenanceStartTime string `json:"WeeklyMaintenanceStartTime,omitempty"`
- RootSquashConfiguration json.RawMessage `json:"RootSquashConfiguration,omitempty"`
- DataReadCacheConfiguration json.RawMessage `json:"DataReadCacheConfiguration,omitempty"`
- MetadataConfiguration json.RawMessage `json:"MetadataConfiguration,omitempty"`
- LogConfiguration json.RawMessage `json:"LogConfiguration,omitempty"`
+ RootSquashConfiguration jsontext.Value `json:"RootSquashConfiguration,omitempty"`
+ DataReadCacheConfiguration jsontext.Value `json:"DataReadCacheConfiguration,omitempty"`
+ MetadataConfiguration jsontext.Value `json:"MetadataConfiguration,omitempty"`
+ LogConfiguration jsontext.Value `json:"LogConfiguration,omitempty"`
AutomaticBackupRetentionDays int32 `json:"AutomaticBackupRetentionDays,omitempty"`
ThroughputCapacity int32 `json:"ThroughputCapacity,omitempty"`
PerUnitStorageThroughput int32 `json:"PerUnitStorageThroughput,omitempty"`
diff --git a/services/fsx/lustre_config.go b/services/fsx/lustre_config.go
index 12996df350..6b0380e39e 100644
--- a/services/fsx/lustre_config.go
+++ b/services/fsx/lustre_config.go
@@ -1,10 +1,13 @@
package fsx
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"regexp"
"slices"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -25,41 +28,41 @@ var weeklyMaintenancePattern = regexp.MustCompile(`^[1-7]:([01]\d|2[0-3]):?([0-5
// createLustreConfiguration mirrors CreateFileSystemLustreConfiguration.
type createLustreConfiguration struct {
- DriveCacheType string `json:"DriveCacheType,omitempty"`
- ImportPath string `json:"ImportPath,omitempty"`
- WeeklyMaintenanceStartTime string `json:"WeeklyMaintenanceStartTime,omitempty"`
- ExportPath string `json:"ExportPath,omitempty"`
- DeploymentType string `json:"DeploymentType,omitempty"`
- AutoImportPolicy string `json:"AutoImportPolicy,omitempty"`
- DailyAutomaticBackupStartTime string `json:"DailyAutomaticBackupStartTime,omitempty"`
- DataCompressionType string `json:"DataCompressionType,omitempty"`
- DataReadCacheConfiguration json.RawMessage `json:"DataReadCacheConfiguration,omitempty"`
- RootSquashConfiguration json.RawMessage `json:"RootSquashConfiguration,omitempty"`
- LogConfiguration json.RawMessage `json:"LogConfiguration,omitempty"`
- MetadataConfiguration json.RawMessage `json:"MetadataConfiguration,omitempty"`
- AutomaticBackupRetentionDays int32 `json:"AutomaticBackupRetentionDays,omitempty"`
- ImportedFileChunkSize int32 `json:"ImportedFileChunkSize,omitempty"`
- PerUnitStorageThroughput int32 `json:"PerUnitStorageThroughput,omitempty"`
- ThroughputCapacity int32 `json:"ThroughputCapacity,omitempty"`
- CopyTagsToBackups bool `json:"CopyTagsToBackups,omitempty"`
- EfaEnabled bool `json:"EfaEnabled,omitempty"`
+ DriveCacheType string `json:"DriveCacheType,omitempty"`
+ ImportPath string `json:"ImportPath,omitempty"`
+ WeeklyMaintenanceStartTime string `json:"WeeklyMaintenanceStartTime,omitempty"`
+ ExportPath string `json:"ExportPath,omitempty"`
+ DeploymentType string `json:"DeploymentType,omitempty"`
+ AutoImportPolicy string `json:"AutoImportPolicy,omitempty"`
+ DailyAutomaticBackupStartTime string `json:"DailyAutomaticBackupStartTime,omitempty"`
+ DataCompressionType string `json:"DataCompressionType,omitempty"`
+ DataReadCacheConfiguration jsontext.Value `json:"DataReadCacheConfiguration,omitempty"`
+ RootSquashConfiguration jsontext.Value `json:"RootSquashConfiguration,omitempty"`
+ LogConfiguration jsontext.Value `json:"LogConfiguration,omitempty"`
+ MetadataConfiguration jsontext.Value `json:"MetadataConfiguration,omitempty"`
+ AutomaticBackupRetentionDays int32 `json:"AutomaticBackupRetentionDays,omitempty"`
+ ImportedFileChunkSize int32 `json:"ImportedFileChunkSize,omitempty"`
+ PerUnitStorageThroughput int32 `json:"PerUnitStorageThroughput,omitempty"`
+ ThroughputCapacity int32 `json:"ThroughputCapacity,omitempty"`
+ CopyTagsToBackups bool `json:"CopyTagsToBackups,omitempty"`
+ EfaEnabled bool `json:"EfaEnabled,omitempty"`
}
// storedLustreExtras keeps the Lustre members that have no counterpart on the shared
// storedFileSystem fields.
type storedLustreExtras struct {
- AutoImportPolicy string `json:"autoImportPolicy,omitempty"`
- DataCompressionType string `json:"dataCompression,omitempty"`
- DriveCacheType string `json:"driveCache,omitempty"`
- ExportPath string `json:"exportPath,omitempty"`
- ImportPath string `json:"importPath,omitempty"`
- RootSquashConfiguration json.RawMessage `json:"rootSquash,omitempty"`
- DataReadCacheConfiguration json.RawMessage `json:"dataReadCache,omitempty"`
- MetadataConfiguration json.RawMessage `json:"metadata,omitempty"`
- LogConfiguration json.RawMessage `json:"log,omitempty"`
- ImportedFileChunkSize int32 `json:"importedChunk,omitempty"`
- PerUnitStorageThroughput int32 `json:"perUnitThroughput,omitempty"`
- EfaEnabled bool `json:"efa,omitempty"`
+ AutoImportPolicy string `json:"autoImportPolicy,omitempty"`
+ DataCompressionType string `json:"dataCompression,omitempty"`
+ DriveCacheType string `json:"driveCache,omitempty"`
+ ExportPath string `json:"exportPath,omitempty"`
+ ImportPath string `json:"importPath,omitempty"`
+ RootSquashConfiguration jsontext.Value `json:"rootSquash,omitempty"`
+ DataReadCacheConfiguration jsontext.Value `json:"dataReadCache,omitempty"`
+ MetadataConfiguration jsontext.Value `json:"metadata,omitempty"`
+ LogConfiguration jsontext.Value `json:"log,omitempty"`
+ ImportedFileChunkSize int32 `json:"importedChunk,omitempty"`
+ PerUnitStorageThroughput int32 `json:"perUnitThroughput,omitempty"`
+ EfaEnabled bool `json:"efa,omitempty"`
}
func validLustreThroughputs(deployment, storageType string) []int32 {
@@ -230,12 +233,12 @@ func applyLustreConfig(fs *storedFileSystem, cfg *createLustreConfiguration) err
}
// metadataMode extracts MetadataConfiguration.Mode for defaultLustreVersion.
-func metadataMode(raw json.RawMessage) string {
+func metadataMode(raw jsontext.Value) string {
var m struct {
Mode string `json:"Mode"`
}
- if len(raw) == 0 || json.Unmarshal(raw, &m) != nil {
+ if len(raw) == 0 || json.Unmarshal(raw, &m, jsonx.V1()) != nil {
return ""
}
@@ -244,17 +247,17 @@ func metadataMode(raw json.RawMessage) string {
// updateLustreConfiguration mirrors UpdateFileSystemLustreConfiguration.
type updateLustreConfiguration struct {
- AutoImportPolicy string `json:"AutoImportPolicy,omitempty"`
- DailyAutomaticBackupStartTime string `json:"DailyAutomaticBackupStartTime,omitempty"`
- DataCompressionType string `json:"DataCompressionType,omitempty"`
- WeeklyMaintenanceStartTime string `json:"WeeklyMaintenanceStartTime,omitempty"`
- RootSquashConfiguration json.RawMessage `json:"RootSquashConfiguration,omitempty"`
- DataReadCacheConfiguration json.RawMessage `json:"DataReadCacheConfiguration,omitempty"`
- MetadataConfiguration json.RawMessage `json:"MetadataConfiguration,omitempty"`
- LogConfiguration json.RawMessage `json:"LogConfiguration,omitempty"`
- AutomaticBackupRetentionDays int32 `json:"AutomaticBackupRetentionDays,omitempty"`
- PerUnitStorageThroughput int32 `json:"PerUnitStorageThroughput,omitempty"`
- ThroughputCapacity int32 `json:"ThroughputCapacity,omitempty"`
+ AutoImportPolicy string `json:"AutoImportPolicy,omitempty"`
+ DailyAutomaticBackupStartTime string `json:"DailyAutomaticBackupStartTime,omitempty"`
+ DataCompressionType string `json:"DataCompressionType,omitempty"`
+ WeeklyMaintenanceStartTime string `json:"WeeklyMaintenanceStartTime,omitempty"`
+ RootSquashConfiguration jsontext.Value `json:"RootSquashConfiguration,omitempty"`
+ DataReadCacheConfiguration jsontext.Value `json:"DataReadCacheConfiguration,omitempty"`
+ MetadataConfiguration jsontext.Value `json:"MetadataConfiguration,omitempty"`
+ LogConfiguration jsontext.Value `json:"LogConfiguration,omitempty"`
+ AutomaticBackupRetentionDays int32 `json:"AutomaticBackupRetentionDays,omitempty"`
+ PerUnitStorageThroughput int32 `json:"PerUnitStorageThroughput,omitempty"`
+ ThroughputCapacity int32 `json:"ThroughputCapacity,omitempty"`
}
func applyLustreUpdate(fs *storedFileSystem, cfg *updateLustreConfiguration) error {
@@ -321,7 +324,7 @@ func setIf(dst *string, v string) {
}
}
-func setRaw(dst *json.RawMessage, v json.RawMessage) {
+func setRaw(dst *jsontext.Value, v jsontext.Value) {
if len(v) > 0 {
*dst = v
}
diff --git a/services/fsx/multi_region_test.go b/services/fsx/multi_region_test.go
index 56fcdbcae1..14cd76ff19 100644
--- a/services/fsx/multi_region_test.go
+++ b/services/fsx/multi_region_test.go
@@ -2,7 +2,7 @@ package fsx_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fsx"
)
@@ -41,7 +42,7 @@ func decode(t *testing.T, body string) map[string]any {
out := map[string]any{}
if body != "" {
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
}
return out
diff --git a/services/fsx/openzfs_volume_config_test.go b/services/fsx/openzfs_volume_config_test.go
index a54ba9047b..cc246b9227 100644
--- a/services/fsx/openzfs_volume_config_test.go
+++ b/services/fsx/openzfs_volume_config_test.go
@@ -1,7 +1,7 @@
package fsx_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"net/http/httptest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fsx"
)
@@ -70,7 +71,7 @@ func (f zfsFixture) describe(t *testing.T, volID string) map[string]any {
} `json:"Volumes"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Volumes, 1)
return out.Volumes[0].OpenZFSConfiguration
@@ -200,7 +201,7 @@ func TestOpenZFSVolume_CopyTagsToSnapshots(t *testing.T) {
} `json:"Snapshot"`
}
- require.NoError(t, json.Unmarshal(snap.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(snap.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Snapshot.Tags, tt.want)
})
}
diff --git a/services/fsx/page_token.go b/services/fsx/page_token.go
index b611d04f14..f7466c22dc 100644
--- a/services/fsx/page_token.go
+++ b/services/fsx/page_token.go
@@ -3,8 +3,11 @@ package fsx
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -13,18 +16,18 @@ const (
)
// tokenFromBody returns the body's decoded fields and its non-empty NextToken, if any.
-func tokenFromBody(body []byte) (map[string]json.RawMessage, string, bool) {
+func tokenFromBody(body []byte) (map[string]jsontext.Value, string, bool) {
if !bytes.Contains(body, []byte(`"`+nextTokenField+`"`)) {
return nil, "", false
}
- var m map[string]json.RawMessage
- if err := json.Unmarshal(body, &m); err != nil {
+ var m map[string]jsontext.Value
+ if err := json.Unmarshal(body, &m, jsonx.V1()); err != nil {
return nil, "", false
}
var tok string
- if err := json.Unmarshal(m[nextTokenField], &tok); err != nil || tok == "" {
+ if err := json.Unmarshal(m[nextTokenField], &tok, jsonx.V1()); err != nil || tok == "" {
return nil, "", false
}
@@ -44,9 +47,9 @@ func decodeNextToken(body []byte) ([]byte, error) {
return nil, fmt.Errorf("%w: NextToken is not valid", ErrValidation)
}
- m[nextTokenField], _ = json.Marshal(string(key))
+ m[nextTokenField], _ = json.Marshal(string(key), jsonx.V1())
- out, err := json.Marshal(m)
+ out, err := json.Marshal(m, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("%w: %w", ErrValidation, err)
}
@@ -61,9 +64,9 @@ func encodeNextToken(body []byte) []byte {
return body
}
- m[nextTokenField], _ = json.Marshal(base64.RawURLEncoding.EncodeToString([]byte(key)))
+ m[nextTokenField], _ = json.Marshal(base64.RawURLEncoding.EncodeToString([]byte(key)), jsonx.V1())
- out, err := json.Marshal(m)
+ out, err := json.Marshal(m, jsonx.V1())
if err != nil {
return body
}
diff --git a/services/fsx/persistence.go b/services/fsx/persistence.go
index 7a860acf75..c3388c7700 100644
--- a/services/fsx/persistence.go
+++ b/services/fsx/persistence.go
@@ -2,7 +2,7 @@ package fsx
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -36,7 +36,7 @@ const fsxSnapshotVersion = 2
// store_setup.go's file doc comment for why they do not fit store.Table's
// keyed-by-identity-value shape.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
Aliases map[string][]string `json:"aliases"`
CreateFileSystemTokens map[string]fsCreateTokenEntry `json:"createFileSystemTokens"`
diff --git a/services/fsx/persistence_test.go b/services/fsx/persistence_test.go
index 6e2f86ae3a..6b3f85a752 100644
--- a/services/fsx/persistence_test.go
+++ b/services/fsx/persistence_test.go
@@ -8,7 +8,7 @@ package fsx_test
// sharedVpcEnabled scalar, and a snapshot-version-mismatch test.
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/fsx"
)
@@ -148,7 +149,7 @@ func TestInMemoryBackend_SnapshotRestore_FullState(t *testing.T) {
require.Equal(t, http.StatusOK, dedupRec.Code)
var dedupOut map[string]any
- require.NoError(t, json.Unmarshal(dedupRec.Body.Bytes(), &dedupOut))
+ require.NoError(t, json.Unmarshal(dedupRec.Body.Bytes(), &dedupOut, jsonx.V1()))
assert.Equal(t, fsID, dedupOut["FileSystem"].(map[string]any)["FileSystemId"])
assert.Equal(
t, fsx.FileSystemCount(b), fsx.FileSystemCount(fresh), "dedup replay must not create a new file system",
@@ -190,7 +191,7 @@ func assertFullStateRestored(t *testing.T, h *fsx.Handler, ids fullStateIDs) {
require.Equal(t, http.StatusOK, rec.Code)
var fsOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fsOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fsOut, jsonx.V1()))
fsList, ok := fsOut["FileSystems"].([]any)
require.True(t, ok)
require.Len(t, fsList, 1)
@@ -212,7 +213,7 @@ func assertFullStateRestored(t *testing.T, h *fsx.Handler, ids fullStateIDs) {
require.Equal(t, http.StatusOK, rec.Code)
var aliasOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &aliasOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &aliasOut, jsonx.V1()))
aliasList, ok := aliasOut["Aliases"].([]any)
require.True(t, ok)
require.Len(t, aliasList, 1)
@@ -260,7 +261,7 @@ func assertFullStateRestored(t *testing.T, h *fsx.Handler, ids fullStateIDs) {
require.Equal(t, http.StatusOK, rec.Code)
var vpcOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &vpcOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &vpcOut, jsonx.V1()))
assert.Equal(t, "true", vpcOut["EnableFsxRouteTableUpdatesFromParticipantAccounts"])
rec = doFSxRequest(t, h, "ListTagsForResource", map[string]any{"ResourceARN": fsRecord["ResourceARN"]})
@@ -275,7 +276,7 @@ func assertOutputLen(t *testing.T, rec *httptest.ResponseRecorder, field string)
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
list, ok := out[field].([]any)
require.True(t, ok, "field %q missing or not an array in response %s", field, rec.Body.String())
@@ -305,7 +306,7 @@ func createTaggedFS(t *testing.T, h *fsx.Handler) string {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
fs, ok := out["FileSystem"].(map[string]any)
require.True(t, ok)
@@ -327,7 +328,7 @@ func createBackupForFS(t *testing.T, h *fsx.Handler, fsID string) string {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
backup, ok := out["Backup"].(map[string]any)
require.True(t, ok)
@@ -349,7 +350,7 @@ func createDRA(t *testing.T, h *fsx.Handler, fsID string) string {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assoc, ok := out["Association"].(map[string]any)
require.True(t, ok)
@@ -377,7 +378,7 @@ func createDRT(t *testing.T, h *fsx.Handler, fsID string) string {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
task, ok := out["DataRepositoryTask"].(map[string]any)
require.True(t, ok)
@@ -398,7 +399,7 @@ func createVolSnapshot(t *testing.T, h *fsx.Handler, volumeID string) string {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
snap, ok := out["Snapshot"].(map[string]any)
require.True(t, ok)
@@ -423,7 +424,7 @@ func createS3AP(t *testing.T, h *fsx.Handler, volumeID string) string {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
ap, ok := out["S3AccessPointAttachment"].(map[string]any)
require.True(t, ok)
diff --git a/services/gcpapigateway/auth.go b/services/gcpapigateway/auth.go
index 87c6616941..cc898179a7 100644
--- a/services/gcpapigateway/auth.go
+++ b/services/gcpapigateway/auth.go
@@ -8,7 +8,7 @@ import (
"crypto/rsa"
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"errors"
"fmt"
@@ -21,6 +21,7 @@ import (
"github.com/golang-jwt/jwt/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
)
@@ -143,7 +144,7 @@ func parseKeySet(raw []byte) map[string]crypto.PublicKey {
Keys []jwk `json:"keys"`
}
- if json.Unmarshal(raw, &set) == nil && len(set.Keys) > 0 {
+ if json.Unmarshal(raw, &set, jsonx.V1()) == nil && len(set.Keys) > 0 {
for _, k := range set.Keys {
if pub, err := k.public(); err == nil {
keys[k.Kid] = pub
@@ -154,7 +155,7 @@ func parseKeySet(raw []byte) map[string]crypto.PublicKey {
}
var certs map[string]string
- if json.Unmarshal(raw, &certs) != nil {
+ if json.Unmarshal(raw, &certs, jsonx.V1()) != nil {
return keys
}
diff --git a/services/gcpapigateway/dataplane.go b/services/gcpapigateway/dataplane.go
index cf425f8731..b00fb6eb35 100644
--- a/services/gcpapigateway/dataplane.go
+++ b/services/gcpapigateway/dataplane.go
@@ -3,7 +3,8 @@ package gcpapigateway
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +17,8 @@ import (
"time"
"github.com/golang-jwt/jwt/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -156,7 +159,7 @@ func apiOfConfig(config string) string {
func writeErr(w http.ResponseWriter, status int, msg string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
- _ = json.NewEncoder(w).Encode(map[string]any{"code": status, "message": msg})
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), map[string]any{"code": status, "message": msg})
}
// DataPlane serves requests to gateways.
@@ -288,7 +291,7 @@ func (b *InMemoryBackend) forward(
}
if claims != nil {
- if raw, mErr := json.Marshal(claims); mErr == nil {
+ if raw, mErr := json.Marshal(claims, jsonx.V1()); mErr == nil {
out.Header.Set(userinfoHeader, base64.RawURLEncoding.EncodeToString(raw))
}
}
diff --git a/services/gcpapigateway/helpers_test.go b/services/gcpapigateway/helpers_test.go
index f582c07536..27ed8dbee5 100644
--- a/services/gcpapigateway/helpers_test.go
+++ b/services/gcpapigateway/helpers_test.go
@@ -2,7 +2,8 @@ package gcpapigateway_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/gcpapigateway"
)
@@ -169,7 +171,7 @@ func (e *env) do(t *testing.T, req *http.Request) reply {
require.NoError(t, err)
r := reply{status: resp.StatusCode, raw: string(raw)}
- _ = json.Unmarshal(raw, &r.body)
+ _ = json.Unmarshal(raw, &r.body, jsonx.V1())
return r
}
@@ -188,7 +190,7 @@ func echoBackend(t *testing.T) *httptest.Server {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("X-Backend", "echo")
- _ = json.NewEncoder(w).Encode(map[string]any{
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), map[string]any{
"method": r.Method, "path": r.URL.Path, "query": r.URL.RawQuery, "headers": hdr, "body": string(body),
})
}))
diff --git a/services/gcpapigateway/persistence.go b/services/gcpapigateway/persistence.go
index 10278b5993..f9b41fb9c4 100644
--- a/services/gcpapigateway/persistence.go
+++ b/services/gcpapigateway/persistence.go
@@ -2,7 +2,7 @@ package gcpapigateway
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"cloud.google.com/go/apigateway/apiv1/apigatewaypb"
@@ -18,9 +18,9 @@ import (
const snapshotVersion = 1
type recSnap struct {
- Resource json.RawMessage `json:"resource"`
- Policy json.RawMessage `json:"policy,omitempty"`
- Life life `json:"life"`
+ Resource jsontext.Value `json:"resource"`
+ Policy jsontext.Value `json:"policy,omitempty"`
+ Life life `json:"life"`
}
type backendSnapshot struct {
@@ -32,7 +32,7 @@ type backendSnapshot struct {
Seq uint64 `json:"seq"`
}
-func marshalPB(m proto.Message) json.RawMessage {
+func marshalPB(m proto.Message) jsontext.Value {
if m == nil || !m.ProtoReflect().IsValid() {
return nil
}
@@ -49,7 +49,7 @@ func unmarshalPB(raw []byte, m proto.Message) error {
return protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(raw, m)
}
-func restorePolicy(raw json.RawMessage) (*iampb.Policy, error) {
+func restorePolicy(raw jsontext.Value) (*iampb.Policy, error) {
if len(raw) == 0 {
return nil, nil //nolint:nilnil // no policy stored
}
diff --git a/services/gcpapigateway/security_test.go b/services/gcpapigateway/security_test.go
index f7949bc4e5..1d0ea11ee0 100644
--- a/services/gcpapigateway/security_test.go
+++ b/services/gcpapigateway/security_test.go
@@ -6,7 +6,8 @@ import (
"crypto/rand"
"crypto/rsa"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"math/big"
@@ -19,6 +20,8 @@ import (
"github.com/golang-jwt/jwt/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -110,7 +113,7 @@ func newSigner(t *testing.T) *signer {
s := &signer{rsa: sharedRSA(), ec: ec}
s.jwks = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
- _ = json.NewEncoder(w).Encode(map[string]any{"keys": []map[string]string{
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), map[string]any{"keys": []map[string]string{
{"kty": "RSA", "kid": "rsa-1", "n": b64u(s.rsa.N), "e": b64u(big.NewInt(int64(s.rsa.E)))},
{
"kty": "EC",
@@ -396,7 +399,7 @@ func TestJWTUserinfoForwarded(t *testing.T) {
require.NoError(t, err)
var claims map[string]any
- require.NoError(t, json.Unmarshal(raw, &claims))
+ require.NoError(t, json.Unmarshal(raw, &claims, jsonx.V1()))
assert.Equal(t, "user-1", claims["sub"])
assert.Equal(t, "u@example.com", claims["email"])
assert.Equal(t, "Bearer "+tok, hdr["X-Forwarded-Authorization"])
diff --git a/services/gcpartifactregistry/filehttp.go b/services/gcpartifactregistry/filehttp.go
index 1a9f3515a9..65307a2410 100644
--- a/services/gcpartifactregistry/filehttp.go
+++ b/services/gcpartifactregistry/filehttp.go
@@ -3,7 +3,8 @@ package gcpartifactregistry
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"mime"
@@ -18,6 +19,7 @@ import (
"google.golang.org/protobuf/encoding/protojson"
"github.com/blackbirdworks/gopherstack/pkgs/gcprest"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -49,7 +51,7 @@ func writeRESTError(w http.ResponseWriter, err error) {
body, _ := json.Marshal(map[string]any{"error": map[string]any{
"code": gcprest.HTTPStatus(st.Code()), "message": st.Message(), "status": codeName(st.Code()),
- }})
+ }}, jsonx.V1())
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(gcprest.HTTPStatus(st.Code()))
@@ -95,7 +97,7 @@ func readMultipart(r *http.Request) (uploadReq, []byte, error) {
}
var req uploadReq
- if err = json.NewDecoder(meta).Decode(&req); err != nil {
+ if err = json.UnmarshalDecode(jsontext.NewDecoder(meta), &req, jsonx.V1()); err != nil {
return uploadReq{}, nil, errInvalid("The upload metadata is not valid JSON: %v", err)
}
diff --git a/services/gcpartifactregistry/files.go b/services/gcpartifactregistry/files.go
index 4dac4c9b5f..d53a7d8b87 100644
--- a/services/gcpartifactregistry/files.go
+++ b/services/gcpartifactregistry/files.go
@@ -4,7 +4,7 @@ import (
"cmp"
"context"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"maps"
"regexp"
"slices"
@@ -15,6 +15,7 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -77,7 +78,7 @@ func (b *InMemoryBackend) dockerFiles(ctx context.Context, ref repoRef) []*artif
add(d, int64(len(m.Body)), src)
var doc manifestBlobs
- if json.Unmarshal(m.Body, &doc) != nil {
+ if json.Unmarshal(m.Body, &doc, jsonx.V1()) != nil {
continue
}
diff --git a/services/gcpartifactregistry/files_test.go b/services/gcpartifactregistry/files_test.go
index 3423f1fb37..740847e795 100644
--- a/services/gcpartifactregistry/files_test.go
+++ b/services/gcpartifactregistry/files_test.go
@@ -1,7 +1,7 @@
package gcpartifactregistry_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"cloud.google.com/go/artifactregistry/apiv1/artifactregistrypb"
@@ -12,6 +12,7 @@ import (
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/fieldmaskpb"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociregistry/ocitest"
)
@@ -54,7 +55,7 @@ func configDigest(t *testing.T, img ocitest.Image) string {
} `json:"config"`
}
- require.NoError(t, json.Unmarshal(img.Body, &doc))
+ require.NoError(t, json.Unmarshal(img.Body, &doc, jsonx.V1()))
return doc.Config.Digest
}
diff --git a/services/gcpartifactregistry/helpers_test.go b/services/gcpartifactregistry/helpers_test.go
index a1f4158d7f..e6ad4a7b77 100644
--- a/services/gcpartifactregistry/helpers_test.go
+++ b/services/gcpartifactregistry/helpers_test.go
@@ -4,7 +4,7 @@ import (
"context"
"encoding/base64"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"net"
"net/http"
"slices"
@@ -17,6 +17,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociregistry/ocitest"
"github.com/blackbirdworks/gopherstack/services/gcpartifactregistry"
)
@@ -108,7 +109,7 @@ func decodeJSON(t *testing.T, body []byte) map[string]any {
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
return out
}
diff --git a/services/gcpartifactregistry/npm.go b/services/gcpartifactregistry/npm.go
index 68ccc38146..472a7a190b 100644
--- a/services/gcpartifactregistry/npm.go
+++ b/services/gcpartifactregistry/npm.go
@@ -5,13 +5,16 @@ import (
"crypto/sha512"
"encoding/base64"
"encoding/hex"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"maps"
"net/http"
"regexp"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -28,10 +31,10 @@ type npmAttachment struct {
}
type npmPublishDoc struct {
- DistTags map[string]string `json:"dist-tags"`
- Versions map[string]json.RawMessage `json:"versions"`
- Attachments map[string]npmAttachment `json:"_attachments"`
- Name string `json:"name"`
+ DistTags map[string]string `json:"dist-tags"`
+ Versions map[string]jsontext.Value `json:"versions"`
+ Attachments map[string]npmAttachment `json:"_attachments"`
+ Name string `json:"name"`
}
func writeNPMError(w http.ResponseWriter, code int, msg string) {
@@ -123,7 +126,7 @@ func readNPMDoc(w http.ResponseWriter, r *http.Request) (npmPublishDoc, bool) {
var doc npmPublishDoc
body := http.MaxBytesReader(w, r.Body, maxNPMDocBytes)
- if err := json.NewDecoder(body).Decode(&doc); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(body), &doc, jsonx.V1()); err != nil {
writeNPMError(w, http.StatusBadRequest, "Invalid package document: "+err.Error())
return doc, false
@@ -201,7 +204,7 @@ func (b *InMemoryBackend) publishNPM(w http.ResponseWriter, r *http.Request, rc
}
func npmVersionFile(
- pkg, version string, manifest json.RawMessage, atts map[string]npmAttachment,
+ pkg, version string, manifest jsontext.Value, atts map[string]npmAttachment,
) (pkgFile, int, string) {
if !npmVersionRe.MatchString(version) {
return pkgFile{}, http.StatusBadRequest, "Invalid semver version " + version + "."
@@ -278,7 +281,7 @@ func (b *InMemoryBackend) serveNPMPackument(w http.ResponseWriter, r *http.Reque
}
base := baseURL(r, npmRoute, rc)
- docs := map[string]json.RawMessage{}
+ docs := map[string]jsontext.Value{}
times := map[string]string{}
rev := sha1.New() //nolint:gosec // opaque revision token
@@ -305,10 +308,10 @@ func (b *InMemoryBackend) serveNPMPackument(w http.ResponseWriter, r *http.Reque
}
// withDist adds the dist block npm clients install from to a stored version manifest.
-func withDist(f *genFile, manifest []byte, tarballURL string) json.RawMessage {
- var m map[string]json.RawMessage
- if json.Unmarshal(manifest, &m) != nil {
- m = map[string]json.RawMessage{}
+func withDist(f *genFile, manifest []byte, tarballURL string) jsontext.Value {
+ var m map[string]jsontext.Value
+ if json.Unmarshal(manifest, &m, jsonx.V1()) != nil {
+ m = map[string]jsontext.Value{}
}
s1 := sha1.Sum(f.Data) //nolint:gosec // npm dist.shasum is SHA-1 by protocol
@@ -316,10 +319,10 @@ func withDist(f *genFile, manifest []byte, tarballURL string) json.RawMessage {
dist, _ := json.Marshal(map[string]any{
"tarball": tarballURL, "shasum": hex.EncodeToString(s1[:]),
"integrity": "sha512-" + base64.StdEncoding.EncodeToString(s512[:]),
- })
+ }, jsonx.V1())
m["dist"] = dist
- out, _ := json.Marshal(m)
+ out, _ := json.Marshal(m, jsonx.V1())
return out
}
@@ -414,7 +417,11 @@ func (b *InMemoryBackend) putNPMTag(
w http.ResponseWriter, r *http.Request, rc repoCtx, pkg, tag string, existing map[string]bool,
) {
var version string
- if err := json.NewDecoder(io.LimitReader(r.Body, maxTagBody)).Decode(&version); err != nil {
+ if err := json.UnmarshalDecode(
+ jsontext.NewDecoder(io.LimitReader(r.Body, maxTagBody)),
+ &version,
+ jsonx.V1(),
+ ); err != nil {
writeNPMError(w, http.StatusBadRequest, "The body must be a JSON string holding a version.")
return
diff --git a/services/gcpartifactregistry/persistence.go b/services/gcpartifactregistry/persistence.go
index 0e7f7ff00d..e52d39a859 100644
--- a/services/gcpartifactregistry/persistence.go
+++ b/services/gcpartifactregistry/persistence.go
@@ -2,7 +2,7 @@ package gcpartifactregistry
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"cloud.google.com/go/artifactregistry/apiv1/artifactregistrypb"
@@ -19,8 +19,8 @@ import (
const snapshotVersion = 1
type repoSnap struct {
- Repository json.RawMessage `json:"repository"`
- IAM json.RawMessage `json:"iam,omitempty"`
+ Repository jsontext.Value `json:"repository"`
+ IAM jsontext.Value `json:"iam,omitempty"`
}
type backendSnapshot struct {
@@ -31,14 +31,14 @@ type backendSnapshot struct {
GenericVers map[string]*resMeta `json:"genericVersions,omitempty"`
GenericFiles map[string]*genFile `json:"genericFiles,omitempty"`
PackageTags map[string]map[string]string `json:"packageTags,omitempty"`
- Rules map[string]json.RawMessage `json:"rules,omitempty"`
- Settings map[string]json.RawMessage `json:"projectSettings,omitempty"`
- VPCSC map[string]json.RawMessage `json:"vpcscConfigs,omitempty"`
- Operations map[string]json.RawMessage `json:"operations"`
+ Rules map[string]jsontext.Value `json:"rules,omitempty"`
+ Settings map[string]jsontext.Value `json:"projectSettings,omitempty"`
+ VPCSC map[string]jsontext.Value `json:"vpcscConfigs,omitempty"`
+ Operations map[string]jsontext.Value `json:"operations"`
Version int `json:"version"`
}
-func marshalPB(m proto.Message) json.RawMessage {
+func marshalPB(m proto.Message) jsontext.Value {
out, err := protojson.Marshal(m)
if err != nil {
return nil
@@ -47,7 +47,7 @@ func marshalPB(m proto.Message) json.RawMessage {
return out
}
-func unmarshalPB(raw json.RawMessage, m proto.Message) error {
+func unmarshalPB(raw jsontext.Value, m proto.Message) error {
return protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(raw, m)
}
@@ -133,8 +133,8 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
return nil
}
-func marshalAll[T proto.Message](in map[string]T) map[string]json.RawMessage {
- out := make(map[string]json.RawMessage, len(in))
+func marshalAll[T proto.Message](in map[string]T) map[string]jsontext.Value {
+ out := make(map[string]jsontext.Value, len(in))
for k, v := range in {
out[k] = marshalPB(v)
}
@@ -142,7 +142,7 @@ func marshalAll[T proto.Message](in map[string]T) map[string]json.RawMessage {
return out
}
-func unmarshalAll[T proto.Message](in map[string]json.RawMessage, newMsg func() T) (map[string]T, error) {
+func unmarshalAll[T proto.Message](in map[string]jsontext.Value, newMsg func() T) (map[string]T, error) {
out := make(map[string]T, len(in))
for k, raw := range in {
diff --git a/services/gcpartifactregistry/pkgfmt.go b/services/gcpartifactregistry/pkgfmt.go
index 6f49fd5eb2..aa15364dd0 100644
--- a/services/gcpartifactregistry/pkgfmt.go
+++ b/services/gcpartifactregistry/pkgfmt.go
@@ -2,7 +2,7 @@ package gcpartifactregistry
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"regexp"
@@ -13,6 +13,7 @@ import (
"cloud.google.com/go/artifactregistry/apiv1/artifactregistrypb"
"github.com/blackbirdworks/gopherstack/pkgs/gcprest"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -160,7 +161,7 @@ func writePlain(w http.ResponseWriter, code int, msg string) {
}
func writeJSONBody(w http.ResponseWriter, code int, v any) {
- raw, err := json.Marshal(v)
+ raw, err := json.Marshal(v, jsonx.V1())
if err != nil {
writePlain(w, http.StatusInternalServerError, err.Error())
diff --git a/services/gcpartifactregistry/pkgfmt_test.go b/services/gcpartifactregistry/pkgfmt_test.go
index 2c6edfd9a6..d8b383d88e 100644
--- a/services/gcpartifactregistry/pkgfmt_test.go
+++ b/services/gcpartifactregistry/pkgfmt_test.go
@@ -7,7 +7,7 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"mime/multipart"
@@ -17,6 +17,7 @@ import (
"testing"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"cloud.google.com/go/artifactregistry/apiv1/artifactregistrypb"
"github.com/stretchr/testify/assert"
@@ -160,7 +161,7 @@ func npmPublishBody(t *testing.T, name, version, tag string, tarball []byte) []b
}},
}
- raw, err := json.Marshal(doc)
+ raw, err := json.Marshal(doc, jsonx.V1())
require.NoError(t, err)
return raw
@@ -392,7 +393,7 @@ func TestNPMProtocol(t *testing.T) {
"name": "left-pad", "dist-tags": map[string]string{"latest": "1.0.0"},
"versions": map[string]any{"1.0.0": map[string]any{}},
}
- raw, _ := json.Marshal(unpub)
+ raw, _ := json.Marshal(unpub, jsonx.V1())
require.Equal(
t,
diff --git a/services/gcpartifactregistry/python.go b/services/gcpartifactregistry/python.go
index b3bc5ab05e..7f4f78b99c 100644
--- a/services/gcpartifactregistry/python.go
+++ b/services/gcpartifactregistry/python.go
@@ -4,13 +4,15 @@ import (
"crypto/md5" //nolint:gosec // twine sends md5_digest by protocol
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"html"
"io"
"net/http"
"net/url"
"regexp"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -100,7 +102,12 @@ func (b *InMemoryBackend) uploadPython(w http.ResponseWriter, r *http.Request, r
norm := pyNormalize(name)
doc, _ := json.Marshal(
- pyDoc{Name: name, RequiresPython: r.FormValue("requires_python"), SHA256: hex.EncodeToString(sum[:])},
+ pyDoc{
+ Name: name,
+ RequiresPython: r.FormValue("requires_python"),
+ SHA256: hex.EncodeToString(sum[:]),
+ },
+ jsonx.V1(),
)
err = b.putPackageFile(rc.ref, pkgFile{
@@ -194,7 +201,7 @@ func (b *InMemoryBackend) pyProject(w http.ResponseWriter, r *http.Request, rc r
for _, v := range versions {
for _, nf := range v.files {
var d pyDoc
- _ = json.Unmarshal(nf.file.Doc, &d)
+ _ = json.Unmarshal(nf.file.Doc, &d, jsonx.V1())
filename := nf.id[strings.LastIndex(nf.id, ":")+1:]
href := "/python/" + rc.ref.project + "/" + rc.ref.id + "/packages/" + url.PathEscape(norm) + "/" +
diff --git a/services/gcpartifactregistry/util.go b/services/gcpartifactregistry/util.go
index 5e365bba1a..97383fc0ed 100644
--- a/services/gcpartifactregistry/util.go
+++ b/services/gcpartifactregistry/util.go
@@ -1,10 +1,12 @@
package gcpartifactregistry
import (
- "encoding/json"
+ "encoding/json/v2"
"os"
"sort"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func sortStrings(s []string) { sort.Strings(s) }
@@ -13,4 +15,4 @@ func removeAll(path string) { _ = os.RemoveAll(path) }
func timeFromNanos(n int64) time.Time { return time.Unix(0, n).UTC() }
-func marshalJSON(v any) ([]byte, error) { return json.Marshal(v) }
+func marshalJSON(v any) ([]byte, error) { return json.Marshal(v, jsonx.V1()) }
diff --git a/services/gcpbatch/persistence.go b/services/gcpbatch/persistence.go
index ca47826643..5a34accd44 100644
--- a/services/gcpbatch/persistence.go
+++ b/services/gcpbatch/persistence.go
@@ -2,7 +2,7 @@ package gcpbatch
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -17,8 +17,8 @@ import (
const snapshotVersion = 1
type jobSnap struct {
- Rec *jobRec `json:"rec"`
- Job json.RawMessage `json:"job"`
+ Rec *jobRec `json:"rec"`
+ Job jsontext.Value `json:"job"`
}
type backendSnapshot struct {
diff --git a/services/gcpbigquery/autodetect.go b/services/gcpbigquery/autodetect.go
index 3f72ac8a6f..f9d31b3f41 100644
--- a/services/gcpbigquery/autodetect.go
+++ b/services/gcpbigquery/autodetect.go
@@ -2,7 +2,8 @@ package gcpbigquery
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -10,6 +11,7 @@ import (
"strconv"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/parquetio"
"github.com/blackbirdworks/gopherstack/services/gcpbigquery/bqsql"
)
@@ -176,15 +178,14 @@ func dedupeNames(fs []bqsql.Field) []bqsql.Field {
}
func detectJSON(lc loadConfig, data []byte) ([]bqsql.Field, error) {
- dec := json.NewDecoder(bytes.NewReader(decodeText(data, lc.Encoding)))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(decodeText(data, lc.Encoding)))
var fields []bqsql.Field
for range detectSample {
var obj map[string]any
- err := dec.Decode(&obj)
+ err := json.UnmarshalDecode(dec, &obj, jsonx.V1(), jsonx.UseNumber())
if errors.Is(err, io.EOF) {
break
}
@@ -281,7 +282,7 @@ func inferJSONField(name string, v any) (bqsql.Field, bool) {
return f, len(f.Fields) > 0
case bool:
f.Type = litBoolean
- case json.Number:
+ case jsonx.Number:
f.Type = litInteger
if _, err := t.Int64(); err != nil {
f.Type = litFloat
diff --git a/services/gcpbigquery/bqsql/engine_test.go b/services/gcpbigquery/bqsql/engine_test.go
index a75559cf3a..121244e995 100644
--- a/services/gcpbigquery/bqsql/engine_test.go
+++ b/services/gcpbigquery/bqsql/engine_test.go
@@ -1,13 +1,15 @@
package bqsql_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/gcpbigquery/bqsql"
)
@@ -28,7 +30,7 @@ func fieldsJSON(t *testing.T, s string) []bqsql.Field {
var fs []bqsql.Field
- require.NoError(t, json.Unmarshal([]byte(s), &fs))
+ require.NoError(t, json.Unmarshal([]byte(s), &fs, jsonx.V1()))
require.NoError(t, bqsql.NormalizeSchema(fs))
return fs
@@ -37,12 +39,11 @@ func fieldsJSON(t *testing.T, s string) []bqsql.Field {
func rowsJSON(t *testing.T, fs []bqsql.Field, s string) [][]any {
t.Helper()
- dec := json.NewDecoder(strings.NewReader(s))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(strings.NewReader(s))
var raw []map[string]any
- require.NoError(t, dec.Decode(&raw))
+ require.NoError(t, json.UnmarshalDecode(dec, &raw, jsonx.V1(), jsonx.UseNumber()))
out := make([][]any, len(raw))
@@ -222,7 +223,7 @@ func TestSelectQueries(t *testing.T) {
return
}
- got, merr := json.Marshal(wireRows(res))
+ got, merr := json.Marshal(wireRows(res), jsonx.V1())
require.NoError(t, merr)
assert.JSONEq(t, tt.want, string(got))
})
diff --git a/services/gcpbigquery/bqsql/sqlvalues.go b/services/gcpbigquery/bqsql/sqlvalues.go
index 1e9c7ff135..5be3ab2179 100644
--- a/services/gcpbigquery/bqsql/sqlvalues.go
+++ b/services/gcpbigquery/bqsql/sqlvalues.go
@@ -4,10 +4,13 @@ import (
"bytes"
"database/sql/driver"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"math"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ToSQLite converts a stored value into the value bound into SQLite: scalars map to INTEGER, REAL or TEXT and
@@ -111,7 +114,7 @@ func writeJSONRecord(buf *bytes.Buffer, fields []Field, stored any) {
buf.WriteByte(',')
}
- name, _ := json.Marshal(fl.Name)
+ name, _ := json.Marshal(fl.Name, jsonx.V1())
buf.Write(name)
buf.WriteByte(':')
@@ -133,7 +136,7 @@ func writeJSONScalar(buf *bytes.Buffer, k Kind, s string) {
case KindFloat:
f, _ := parseFloatText(s)
if math.IsNaN(f) || math.IsInf(f, 0) {
- q, _ := json.Marshal(s)
+ q, _ := json.Marshal(s, jsonx.V1())
buf.Write(q)
return
@@ -142,7 +145,7 @@ func writeJSONScalar(buf *bytes.Buffer, k Kind, s string) {
buf.WriteString(strconv.FormatFloat(f, 'g', -1, bitSize))
case KindUnknown, KindString, KindBytes, KindTimestamp, KindDate, KindTime, KindDatetime, KindNumeric,
KindBigNumeric, KindJSON, KindGeography, KindStruct, KindArray:
- q, _ := json.Marshal(s)
+ q, _ := json.Marshal(s, jsonx.V1())
buf.Write(q)
}
}
@@ -177,11 +180,10 @@ func fromJSONText(f Field, v any) (any, error) {
return nil, valueErr("expected JSON text for %s, got %T", f.Name, v)
}
- dec := json.NewDecoder(strings.NewReader(text))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(strings.NewReader(text))
var node any
- if err := dec.Decode(&node); err != nil {
+ if err := json.UnmarshalDecode(dec, &node, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, valueErr("invalid JSON for %s: %v", f.Name, err)
}
diff --git a/services/gcpbigquery/bqsql/udf_array.go b/services/gcpbigquery/bqsql/udf_array.go
index e4e7f7db7d..418a096ffe 100644
--- a/services/gcpbigquery/bqsql/udf_array.go
+++ b/services/gcpbigquery/bqsql/udf_array.go
@@ -3,16 +3,18 @@ package bqsql
import (
"bytes"
"database/sql/driver"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func decodeJSONArray(v driver.Value) ([]any, error) {
- dec := json.NewDecoder(strings.NewReader(toStr(v)))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(strings.NewReader(toStr(v)))
var arr []any
- if err := dec.Decode(&arr); err != nil {
+ if err := json.UnmarshalDecode(dec, &arr, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, errBQ("expected an ARRAY value")
}
@@ -22,9 +24,8 @@ func decodeJSONArray(v driver.Value) ([]any, error) {
func encodeJSON(v any) string {
var buf bytes.Buffer
- enc := json.NewEncoder(&buf)
- enc.SetEscapeHTML(false)
- _ = enc.Encode(v)
+ enc := jsontext.NewEncoder(&buf, jsonx.V1(), jsontext.EscapeForHTML(false))
+ _ = json.MarshalEncode(enc, v)
return strings.TrimSpace(buf.String())
}
@@ -34,7 +35,7 @@ func jsonToSQL(v any) driver.Value {
switch t := v.(type) {
case nil:
return nil
- case json.Number:
+ case jsonx.Number:
if n, err := t.Int64(); err == nil {
return n
}
diff --git a/services/gcpbigquery/bqsql/udf_json.go b/services/gcpbigquery/bqsql/udf_json.go
index 7b95dd8105..5d52d43200 100644
--- a/services/gcpbigquery/bqsql/udf_json.go
+++ b/services/gcpbigquery/bqsql/udf_json.go
@@ -3,11 +3,14 @@ package bqsql
import (
"crypto/rand"
"database/sql/driver"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"math"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -56,7 +59,7 @@ func udfParseJSON(a []driver.Value) (driver.Value, error) {
}
s := toStr(a[0])
- if !json.Valid([]byte(s)) {
+ if !jsontext.Value([]byte(s)).IsValid(jsonx.V1()) {
return nil, errBQ("syntax error while parsing value in JSON")
}
@@ -75,7 +78,7 @@ func udfJSONValue(scalarOnly bool) udfFn {
}
var doc any
- if err := json.Unmarshal([]byte(toStr(a[0])), &doc); err != nil {
+ if err := json.Unmarshal([]byte(toStr(a[0])), &doc, jsonx.V1()); err != nil {
return nil, errBQ("Invalid JSON: %v", err)
}
@@ -103,7 +106,7 @@ func jsonNodeText(node any, scalarOnly bool) driver.Value {
}
}
- b, _ := json.Marshal(node)
+ b, _ := json.Marshal(node, jsonx.V1())
return string(b)
}
diff --git a/services/gcpbigquery/bqsql/udf_regex.go b/services/gcpbigquery/bqsql/udf_regex.go
index 043783687c..9d163c95be 100644
--- a/services/gcpbigquery/bqsql/udf_regex.go
+++ b/services/gcpbigquery/bqsql/udf_regex.go
@@ -2,9 +2,11 @@ package bqsql
import (
"database/sql/driver"
- "encoding/json"
+ "encoding/json/v2"
"regexp"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func compileRe(v driver.Value) (*regexp.Regexp, error) {
@@ -138,7 +140,7 @@ func udfRegexExtractAll(a []driver.Value) (driver.Value, error) {
out = append(out, firstGroup(m))
}
- b, _ := json.Marshal(out)
+ b, _ := json.Marshal(out, jsonx.V1())
return string(b), nil
}
diff --git a/services/gcpbigquery/bqsql/udf_str.go b/services/gcpbigquery/bqsql/udf_str.go
index e2b5d95efd..c53a33b5c0 100644
--- a/services/gcpbigquery/bqsql/udf_str.go
+++ b/services/gcpbigquery/bqsql/udf_str.go
@@ -8,10 +8,12 @@ import (
"database/sql/driver"
"encoding/base64"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"strings"
"unicode"
"unicode/utf8"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func strFn(n int32, f func(s string, a []driver.Value) (driver.Value, error)) udfDef {
@@ -379,7 +381,7 @@ func udfSplit(a []driver.Value) (driver.Value, error) {
parts = []string{""}
}
- b, _ := json.Marshal(parts)
+ b, _ := json.Marshal(parts, jsonx.V1())
return string(b), nil
}
diff --git a/services/gcpbigquery/bqsql/udf_time.go b/services/gcpbigquery/bqsql/udf_time.go
index 61a24c0acf..bbd2e08888 100644
--- a/services/gcpbigquery/bqsql/udf_time.go
+++ b/services/gcpbigquery/bqsql/udf_time.go
@@ -2,10 +2,12 @@ package bqsql
import (
"database/sql/driver"
- "encoding/json"
+ "encoding/json/v2"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// parseAny parses a stored temporal text value and reports which family it belongs to.
@@ -661,7 +663,7 @@ func udfGenerateDates(a []driver.Value) (driver.Value, error) {
t, _, _ = parseAny(next)
}
- b, _ := json.Marshal(out)
+ b, _ := json.Marshal(out, jsonx.V1())
return string(b), nil
}
diff --git a/services/gcpbigquery/bqsql/values.go b/services/gcpbigquery/bqsql/values.go
index 6213d6f74c..eb1b9f81ac 100644
--- a/services/gcpbigquery/bqsql/values.go
+++ b/services/gcpbigquery/bqsql/values.go
@@ -2,7 +2,8 @@ package bqsql
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"math"
@@ -10,6 +11,8 @@ import (
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Canonical stored encodings of temporal values.
@@ -168,7 +171,7 @@ func storedString(v any) (string, error) {
return t, nil
case []byte:
return string(t), nil
- case json.Number:
+ case jsonx.Number:
return t.String(), nil
case bool:
return strconv.FormatBool(t), nil
@@ -183,14 +186,14 @@ func storedString(v any) (string, error) {
func storedJSON(v any) (string, error) {
if s, ok := v.(string); ok {
- if !json.Valid([]byte(s)) {
+ if !jsontext.Value([]byte(s)).IsValid(jsonx.V1()) {
return "", valueErr("invalid JSON text")
}
return s, nil
}
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return "", valueErr("cannot encode JSON: %v", err)
}
@@ -204,7 +207,7 @@ func numberText(v any) (string, bool) {
return strings.TrimSpace(t), true
case []byte:
return strings.TrimSpace(string(t)), true
- case json.Number:
+ case jsonx.Number:
return t.String(), true
case int64:
return strconv.FormatInt(t, decimalBase), true
@@ -293,7 +296,7 @@ func storedBool(v any) (string, error) {
return strconv.FormatBool(t), nil
case int64:
return strconv.FormatBool(t != 0), nil
- case json.Number:
+ case jsonx.Number:
return strconv.FormatBool(t.String() != "0"), nil
case string:
switch strings.ToLower(strings.TrimSpace(t)) {
@@ -449,7 +452,7 @@ func epochTime(v any) (time.Time, bool) {
var f float64
switch t := v.(type) {
- case json.Number:
+ case jsonx.Number:
g, err := t.Float64()
if err != nil {
return time.Time{}, false
diff --git a/services/gcpbigquery/handler.go b/services/gcpbigquery/handler.go
index c880a3bed4..7abd7bb8cf 100644
--- a/services/gcpbigquery/handler.go
+++ b/services/gcpbigquery/handler.go
@@ -2,7 +2,8 @@ package gcpbigquery
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +17,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/gcpenforce"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -179,11 +181,10 @@ func decodeObject(data []byte) (map[string]any, error) {
return map[string]any{}, nil
}
- dec := json.NewDecoder(strings.NewReader(string(data)))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(strings.NewReader(string(data)))
var body map[string]any
- if err := dec.Decode(&body); err != nil {
+ if err := json.UnmarshalDecode(dec, &body, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, errInvalid("Invalid JSON payload received: %v", err)
}
@@ -366,10 +367,9 @@ func (h *Handler) insertAll(c *echo.Context, rt route) error {
return newErr(http.StatusRequestEntityTooLarge, "uploadTooLarge", "Request body is too large or unreadable.")
}
- dec := json.NewDecoder(strings.NewReader(string(data)))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(strings.NewReader(string(data)))
- if err := dec.Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(dec, &req, jsonx.V1(), jsonx.UseNumber()); err != nil {
return errInvalid("Invalid JSON payload received: %v", err)
}
diff --git a/services/gcpbigquery/jobs_extract.go b/services/gcpbigquery/jobs_extract.go
index 6bc1f41408..8429e0fd9f 100644
--- a/services/gcpbigquery/jobs_extract.go
+++ b/services/gcpbigquery/jobs_extract.go
@@ -3,13 +3,15 @@ package gcpbigquery
import (
"bytes"
"compress/gzip"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"math"
"strconv"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/parquetio"
"github.com/blackbirdworks/gopherstack/services/gcpbigquery/bqsql"
)
@@ -214,30 +216,30 @@ func encodeCSV(ec extractConfig, t *Table) ([]byte, error) {
return buf.Bytes(), nil
}
-func jsonCell(f bqsql.Field, v any) json.RawMessage {
+func jsonCell(f bqsql.Field, v any) jsontext.Value {
if v == nil && f.Mode != bqsql.ModeRepeated {
- return json.RawMessage("null")
+ return jsontext.Value("null")
}
k, _ := bqsql.KindOf(f.Type)
if k == bqsql.KindStruct || f.Mode == bqsql.ModeRepeated {
text, _ := bqsql.ToSQLite(f, v).(string)
- return json.RawMessage(text)
+ return jsontext.Value(text)
}
s, _ := v.(string)
switch k {
case bqsql.KindInt, bqsql.KindBool, bqsql.KindNumeric, bqsql.KindBigNumeric:
- return json.RawMessage(s)
+ return jsontext.Value(s)
case bqsql.KindFloat:
if f, err := strconv.ParseFloat(
s,
64,
); err == nil && !math.IsNaN(f) &&
!math.IsInf(f, 0) {
- return json.RawMessage(strconv.FormatFloat(f, 'g', -1, 64))
+ return jsontext.Value(strconv.FormatFloat(f, 'g', -1, 64))
}
case bqsql.KindTimestamp:
if t, err := time.Parse(bqsql.TimestampLayout, s); err == nil {
@@ -249,7 +251,7 @@ func jsonCell(f bqsql.Field, v any) json.RawMessage {
bqsql.KindStruct, bqsql.KindArray:
}
- q, _ := json.Marshal(s)
+ q, _ := json.Marshal(s, jsonx.V1())
return q
}
@@ -265,7 +267,7 @@ func encodeNDJSON(t *Table) []byte {
buf.WriteByte(',')
}
- name, _ := json.Marshal(f.Name)
+ name, _ := json.Marshal(f.Name, jsonx.V1())
buf.Write(name)
buf.WriteByte(':')
buf.Write(jsonCell(f, row[i]))
diff --git a/services/gcpbigquery/jobs_query.go b/services/gcpbigquery/jobs_query.go
index db8ea1c476..89cb8d3a03 100644
--- a/services/gcpbigquery/jobs_query.go
+++ b/services/gcpbigquery/jobs_query.go
@@ -2,13 +2,14 @@ package gcpbigquery
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"regexp"
"strconv"
"strings"
"sync"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/gcpbigquery/bqsql"
)
@@ -38,10 +39,10 @@ type datasetRef struct {
func intOf(v any) (int, bool) {
switch t := v.(type) {
- case json.Number:
- n, err := t.Int64()
+ case jsonx.Number:
+ n, err := t.Int()
- return int(n), err == nil
+ return n, err == nil
case float64:
return int(t), true
case string:
@@ -56,12 +57,12 @@ func intOf(v any) (int, bool) {
func decodeQueryConfig(raw any) (queryConfig, error) {
var qc queryConfig
- b, err := json.Marshal(raw)
+ b, err := json.Marshal(raw, jsonx.V1())
if err != nil {
return qc, errInvalid("Invalid query configuration")
}
- if err = json.Unmarshal(b, &qc); err != nil {
+ if err = json.Unmarshal(b, &qc, jsonx.V1()); err != nil {
return qc, errInvalid("Invalid query configuration: %v", err)
}
diff --git a/services/gcpbigquery/load_formats.go b/services/gcpbigquery/load_formats.go
index 37fe198667..f37c8221cc 100644
--- a/services/gcpbigquery/load_formats.go
+++ b/services/gcpbigquery/load_formats.go
@@ -3,12 +3,14 @@ package gcpbigquery
import (
"bytes"
"encoding/csv"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/parquetio"
"github.com/blackbirdworks/gopherstack/services/gcpbigquery/bqsql"
)
@@ -133,8 +135,7 @@ func csvRow(lc loadConfig, fields []bqsql.Field, rec []string) ([]any, error) {
}
func parseJSONInput(lc loadConfig, fields []bqsql.Field, data []byte) ([][]any, []error, error) {
- dec := json.NewDecoder(bytes.NewReader(decodeText(data, lc.Encoding)))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(decodeText(data, lc.Encoding)), jsonx.V1())
var (
rows [][]any
@@ -144,7 +145,7 @@ func parseJSONInput(lc loadConfig, fields []bqsql.Field, data []byte) ([][]any,
for n := 1; ; n++ {
var obj map[string]any
- err := dec.Decode(&obj)
+ err := json.UnmarshalDecode(dec, &obj, jsonx.V1(), jsonx.UseNumber())
if errors.Is(err, io.EOF) {
return rows, bad, nil
}
diff --git a/services/gcpbigquery/tables.go b/services/gcpbigquery/tables.go
index cbaba8067d..c784afe0f9 100644
--- a/services/gcpbigquery/tables.go
+++ b/services/gcpbigquery/tables.go
@@ -2,10 +2,11 @@ package gcpbigquery
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sort"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/services/gcpbigquery/bqsql"
)
@@ -24,7 +25,7 @@ func decodeSchema(raw any) ([]bqsql.Field, error) {
return nil, nil
}
- b, err := json.Marshal(raw)
+ b, err := json.Marshal(raw, jsonx.V1())
if err != nil {
return nil, errInvalid("Invalid schema")
}
@@ -33,7 +34,7 @@ func decodeSchema(raw any) ([]bqsql.Field, error) {
Fields []bqsql.Field `json:"fields"`
}
- if err = json.Unmarshal(b, &s); err != nil {
+ if err = json.Unmarshal(b, &s, jsonx.V1()); err != nil {
return nil, errInvalid("Invalid schema: %v", err)
}
diff --git a/services/gcpbigtable/persistence.go b/services/gcpbigtable/persistence.go
index bab8da7450..d62b30f428 100644
--- a/services/gcpbigtable/persistence.go
+++ b/services/gcpbigtable/persistence.go
@@ -2,7 +2,7 @@ package gcpbigtable
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"cloud.google.com/go/bigtable/admin/apiv2/adminpb"
@@ -34,35 +34,35 @@ type rowSnap struct {
}
type tableSnap struct {
- Table json.RawMessage `json:"table"`
- Policy json.RawMessage `json:"policy,omitempty"`
- Splits [][]byte `json:"splits,omitempty"`
- Rows []rowSnap `json:"rows,omitempty"`
+ Table jsontext.Value `json:"table"`
+ Policy jsontext.Value `json:"policy,omitempty"`
+ Splits [][]byte `json:"splits,omitempty"`
+ Rows []rowSnap `json:"rows,omitempty"`
}
type backupSnap struct {
- Backup json.RawMessage `json:"backup"`
- Policy json.RawMessage `json:"policy,omitempty"`
- Table tableSnap `json:"table"`
+ Backup jsontext.Value `json:"backup"`
+ Policy jsontext.Value `json:"policy,omitempty"`
+ Table tableSnap `json:"table"`
}
type instSnap struct {
- Clusters map[string]json.RawMessage `json:"clusters,omitempty"`
- Profiles map[string]json.RawMessage `json:"profiles,omitempty"`
- Tables map[string]tableSnap `json:"tables,omitempty"`
- Backups map[string]backupSnap `json:"backups,omitempty"`
- Instance json.RawMessage `json:"instance"`
- Policy json.RawMessage `json:"policy,omitempty"`
+ Clusters map[string]jsontext.Value `json:"clusters,omitempty"`
+ Profiles map[string]jsontext.Value `json:"profiles,omitempty"`
+ Tables map[string]tableSnap `json:"tables,omitempty"`
+ Backups map[string]backupSnap `json:"backups,omitempty"`
+ Instance jsontext.Value `json:"instance"`
+ Policy jsontext.Value `json:"policy,omitempty"`
}
type backendSnapshot struct {
- Instances map[string]instSnap `json:"instances"`
- Operations map[string]json.RawMessage `json:"operations"`
- Version int `json:"version"`
- Seq uint64 `json:"seq"`
+ Instances map[string]instSnap `json:"instances"`
+ Operations map[string]jsontext.Value `json:"operations"`
+ Version int `json:"version"`
+ Seq uint64 `json:"seq"`
}
-func marshalPB(m proto.Message) json.RawMessage {
+func marshalPB(m proto.Message) jsontext.Value {
if m == nil || !m.ProtoReflect().IsValid() {
return nil
}
@@ -124,7 +124,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
for name, r := range b.instances {
s := instSnap{
Instance: marshalPB(r.pb), Policy: marshalPB(r.policy),
- Clusters: map[string]json.RawMessage{}, Profiles: map[string]json.RawMessage{},
+ Clusters: map[string]jsontext.Value{}, Profiles: map[string]jsontext.Value{},
Tables: map[string]tableSnap{}, Backups: map[string]backupSnap{},
}
diff --git a/services/gcpcloudbuild/bindings.go b/services/gcpcloudbuild/bindings.go
index 7f89ff16b4..33c0905648 100644
--- a/services/gcpcloudbuild/bindings.go
+++ b/services/gcpcloudbuild/bindings.go
@@ -2,11 +2,14 @@ package gcpcloudbuild
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"regexp"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errBindingMissing = errors.New("not present in the payload")
@@ -32,10 +35,9 @@ func bindPayload(subs map[string]string, body []byte) (map[string]string, error)
}
if !parsed {
- dec := json.NewDecoder(bytes.NewReader(body))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(body))
- if err := dec.Decode(&doc); err != nil {
+ if err := json.UnmarshalDecode(dec, &doc, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, errInvalid(
"The webhook payload is not valid JSON, so $(body...) bindings cannot resolve: %v.", err)
}
@@ -79,13 +81,13 @@ func lookupBinding(doc any, path string) (string, error) {
return "", nil
case string:
return v, nil
- case json.Number:
+ case jsonx.Number:
return v.String(), nil
case bool:
return strconv.FormatBool(v), nil
}
- raw, err := json.Marshal(cur)
+ raw, err := json.Marshal(cur, jsonx.V1())
if err != nil {
return "", errBindingMissing
}
diff --git a/services/gcpcloudbuild/persistence.go b/services/gcpcloudbuild/persistence.go
index 9106fb5ead..f5a5c75439 100644
--- a/services/gcpcloudbuild/persistence.go
+++ b/services/gcpcloudbuild/persistence.go
@@ -2,7 +2,7 @@ package gcpcloudbuild
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"cloud.google.com/go/cloudbuild/apiv1/v2/cloudbuildpb"
@@ -19,20 +19,20 @@ import (
const snapshotVersion = 1
type buildSnap struct {
- OpName string `json:"opName"`
- Build json.RawMessage `json:"build"`
- Orig json.RawMessage `json:"orig"`
+ OpName string `json:"opName"`
+ Build jsontext.Value `json:"build"`
+ Orig jsontext.Value `json:"orig"`
}
type backendSnapshot struct {
- Builds map[string]buildSnap `json:"builds"`
- Triggers map[string]json.RawMessage `json:"triggers"`
- Pools map[string]json.RawMessage `json:"workerPools"`
- Operations map[string]json.RawMessage `json:"operations"`
- Version int `json:"version"`
+ Builds map[string]buildSnap `json:"builds"`
+ Triggers map[string]jsontext.Value `json:"triggers"`
+ Pools map[string]jsontext.Value `json:"workerPools"`
+ Operations map[string]jsontext.Value `json:"operations"`
+ Version int `json:"version"`
}
-func marshalPB(m proto.Message) json.RawMessage {
+func marshalPB(m proto.Message) jsontext.Value {
out, err := protojson.Marshal(m)
if err != nil {
return nil
@@ -41,7 +41,7 @@ func marshalPB(m proto.Message) json.RawMessage {
return out
}
-func unmarshalPB(raw json.RawMessage, m proto.Message) error {
+func unmarshalPB(raw jsontext.Value, m proto.Message) error {
return protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(raw, m)
}
@@ -54,8 +54,8 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
snap := backendSnapshot{
Version: snapshotVersion, Operations: b.ops.Snapshot(),
Builds: make(map[string]buildSnap, len(b.builds)),
- Triggers: make(map[string]json.RawMessage, len(b.triggers)),
- Pools: make(map[string]json.RawMessage, len(b.pools)),
+ Triggers: make(map[string]jsontext.Value, len(b.triggers)),
+ Pools: make(map[string]jsontext.Value, len(b.pools)),
}
for k, rec := range b.builds {
@@ -170,7 +170,7 @@ func restoreBuilds(in map[string]buildSnap) (map[string]*buildRec, error) {
return out, nil
}
-func restoreMap[T proto.Message](in map[string]json.RawMessage, mk func() T) (map[string]T, error) {
+func restoreMap[T proto.Message](in map[string]jsontext.Value, mk func() T) (map[string]T, error) {
out := make(map[string]T, len(in))
for k, raw := range in {
diff --git a/services/gcpcloudbuild/publish.go b/services/gcpcloudbuild/publish.go
index 1a64bad914..04762ccf67 100644
--- a/services/gcpcloudbuild/publish.go
+++ b/services/gcpcloudbuild/publish.go
@@ -4,7 +4,7 @@ import (
"context"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/url"
@@ -17,6 +17,7 @@ import (
"cloud.google.com/go/cloudbuild/apiv1/v2/cloudbuildpb"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -221,7 +222,7 @@ func (r *runner) writeManifest(bucket, prefix string, lines []manifestLine) (str
var sb strings.Builder
for _, l := range lines {
- raw, err := json.Marshal(l)
+ raw, err := json.Marshal(l, jsonx.V1())
if err != nil {
return "", fmt.Errorf("encode manifest: %w", err)
}
diff --git a/services/gcpcloudbuild/pubsubtrigger.go b/services/gcpcloudbuild/pubsubtrigger.go
index d7f49dc36a..d7959f72db 100644
--- a/services/gcpcloudbuild/pubsubtrigger.go
+++ b/services/gcpcloudbuild/pubsubtrigger.go
@@ -2,11 +2,12 @@ package gcpcloudbuild
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"time"
"cloud.google.com/go/cloudbuild/apiv1/v2/cloudbuildpb"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -65,5 +66,5 @@ func pubsubPayload(
"publishTime": published.UTC().Format(time.RFC3339Nano),
},
"subscription": subscription,
- })
+ }, jsonx.V1())
}
diff --git a/services/gcpcloudtasks/helpers_test.go b/services/gcpcloudtasks/helpers_test.go
index 0b69aabd85..47c1299021 100644
--- a/services/gcpcloudtasks/helpers_test.go
+++ b/services/gcpcloudtasks/helpers_test.go
@@ -3,7 +3,7 @@ package gcpcloudtasks_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -23,6 +23,7 @@ import (
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/timestamppb"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/gcpcloudtasks"
)
@@ -201,7 +202,7 @@ func decodeJWT(t *testing.T, token string) map[string]any {
require.NoError(t, err)
var claims map[string]any
- require.NoError(t, json.Unmarshal(raw, &claims))
+ require.NoError(t, json.Unmarshal(raw, &claims, jsonx.V1()))
return claims
}
diff --git a/services/gcpcloudtasks/persistence.go b/services/gcpcloudtasks/persistence.go
index 2d1123f88e..c171328803 100644
--- a/services/gcpcloudtasks/persistence.go
+++ b/services/gcpcloudtasks/persistence.go
@@ -2,7 +2,7 @@ package gcpcloudtasks
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"time"
@@ -20,22 +20,22 @@ import (
const snapshotVersion = 1
type queueSnap struct {
- Tasks map[string]json.RawMessage `json:"tasks,omitempty"`
- Queue json.RawMessage `json:"queue"`
+ Tasks map[string]jsontext.Value `json:"tasks,omitempty"`
+ Queue jsontext.Value `json:"queue"`
}
type backendSnapshot struct {
- Queues map[string]queueSnap `json:"queues"`
- Tombstones map[string]time.Time `json:"tombstones"`
- Policies map[string]json.RawMessage `json:"policies"`
- Cmek map[string]json.RawMessage `json:"cmek"`
- Ops map[string]json.RawMessage `json:"operations,omitempty"`
- EtagSeq uint64 `json:"etagSeq"`
- TaskSeq uint64 `json:"taskSeq"`
- Version int `json:"version"`
+ Queues map[string]queueSnap `json:"queues"`
+ Tombstones map[string]time.Time `json:"tombstones"`
+ Policies map[string]jsontext.Value `json:"policies"`
+ Cmek map[string]jsontext.Value `json:"cmek"`
+ Ops map[string]jsontext.Value `json:"operations,omitempty"`
+ EtagSeq uint64 `json:"etagSeq"`
+ TaskSeq uint64 `json:"taskSeq"`
+ Version int `json:"version"`
}
-func marshalPB(m proto.Message) json.RawMessage {
+func marshalPB(m proto.Message) jsontext.Value {
out, err := protojson.Marshal(m)
if err != nil {
return nil
@@ -44,7 +44,7 @@ func marshalPB(m proto.Message) json.RawMessage {
return out
}
-func unmarshalPB(raw json.RawMessage, m proto.Message) error {
+func unmarshalPB(raw jsontext.Value, m proto.Message) error {
return protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(raw, m)
}
@@ -59,12 +59,12 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
TaskSeq: b.taskSeq,
Queues: make(map[string]queueSnap, len(b.queues)),
Tombstones: make(map[string]time.Time, len(b.tombstones)),
- Policies: make(map[string]json.RawMessage, len(b.policies)),
- Cmek: make(map[string]json.RawMessage, len(b.cmek)),
+ Policies: make(map[string]jsontext.Value, len(b.policies)),
+ Cmek: make(map[string]jsontext.Value, len(b.cmek)),
}
for name, q := range b.queues {
- qs := queueSnap{Queue: marshalPB(q.pb), Tasks: make(map[string]json.RawMessage, len(q.tasks))}
+ qs := queueSnap{Queue: marshalPB(q.pb), Tasks: make(map[string]jsontext.Value, len(q.tasks))}
for tn, t := range q.tasks {
qs.Tasks[tn] = marshalPB(t.pb)
}
diff --git a/services/gcpcompute/filter.go b/services/gcpcompute/filter.go
index 79100c77d0..ecba84d2cd 100644
--- a/services/gcpcompute/filter.go
+++ b/services/gcpcompute/filter.go
@@ -1,11 +1,13 @@
package gcpcompute
import (
- "encoding/json"
+ "encoding/json/v2"
"regexp"
"slices"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const quotePair = 2
@@ -325,13 +327,13 @@ func parseFilter(expr string) (filterNode, *APIError) {
}
func toDoc(item any) map[string]any {
- data, err := json.Marshal(item)
+ data, err := json.Marshal(item, jsonx.V1())
if err != nil {
return nil
}
var m map[string]any
- _ = json.Unmarshal(data, &m)
+ _ = json.Unmarshal(data, &m, jsonx.V1())
return m
}
diff --git a/services/gcpcompute/handler.go b/services/gcpcompute/handler.go
index 3af0e8daa2..9b21fbfb22 100644
--- a/services/gcpcompute/handler.go
+++ b/services/gcpcompute/handler.go
@@ -2,7 +2,7 @@ package gcpcompute
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -15,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/gcpenforce"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc"
@@ -264,11 +265,11 @@ func decode[T any](rq *request) (*T, map[string]any, *APIError) {
return v, raw, nil
}
- if err := json.Unmarshal(rq.body, v); err != nil {
+ if err := json.Unmarshal(rq.body, v, jsonx.V1()); err != nil {
return nil, nil, errInvalid("Invalid JSON payload received. %v", err)
}
- if err := json.Unmarshal(rq.body, &raw); err != nil {
+ if err := json.Unmarshal(rq.body, &raw, jsonx.V1()); err != nil {
return nil, nil, errInvalid("Invalid JSON payload received. %v", err)
}
diff --git a/services/gcpcompute/labels.go b/services/gcpcompute/labels.go
index 1e4dab58ca..d8e7d5aa23 100644
--- a/services/gcpcompute/labels.go
+++ b/services/gcpcompute/labels.go
@@ -1,10 +1,12 @@
package gcpcompute
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"regexp"
"sort"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -74,7 +76,7 @@ func labelFP(labels map[string]string) string {
pairs[i] = [2]string{k, labels[k]}
}
- data, _ := json.Marshal(pairs)
+ data, _ := json.Marshal(pairs, jsonx.V1())
return fingerprint(string(data))
}
diff --git a/services/gcpcompute/lb_neg_test.go b/services/gcpcompute/lb_neg_test.go
index c9f5e27464..6267b871a5 100644
--- a/services/gcpcompute/lb_neg_test.go
+++ b/services/gcpcompute/lb_neg_test.go
@@ -1,7 +1,7 @@
package gcpcompute_test
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"testing"
@@ -12,6 +12,8 @@ import (
"github.com/stretchr/testify/require"
"google.golang.org/api/iterator"
"google.golang.org/protobuf/proto"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type globalAttach = computepb.GlobalNetworkEndpointGroupsAttachEndpointsRequest
@@ -416,7 +418,7 @@ func TestNEGAsBackend(t *testing.T) {
} `json:"items"`
}
- require.NoError(t, json.Unmarshal([]byte(body), &listed))
+ require.NoError(t, json.Unmarshal([]byte(body), &listed, jsonx.V1()))
require.Len(t, listed.Items, 1)
require.Len(t, listed.Items[0].Healths, 1)
assert.Equal(t, "HEALTHY", listed.Items[0].Healths[0].HealthState)
diff --git a/services/gcpcompute/meta.go b/services/gcpcompute/meta.go
index 229f954476..57029e4b95 100644
--- a/services/gcpcompute/meta.go
+++ b/services/gcpcompute/meta.go
@@ -5,8 +5,10 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -95,13 +97,13 @@ func clone[T any](v *T) *T {
return nil
}
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return v
}
out := new(T)
- if err = json.Unmarshal(data, out); err != nil {
+ if err = json.Unmarshal(data, out, jsonx.V1()); err != nil {
return v
}
@@ -127,13 +129,13 @@ func regionOfZone(zone string) string {
}
func fromDoc[T any](m map[string]any) (*T, error) {
- data, err := json.Marshal(m)
+ data, err := json.Marshal(m, jsonx.V1())
if err != nil {
return nil, err
}
out := new(T)
- if err = json.Unmarshal(data, out); err != nil {
+ if err = json.Unmarshal(data, out, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/gcpcompute/rest_test.go b/services/gcpcompute/rest_test.go
index 566dea5e28..8aa9b5749e 100644
--- a/services/gcpcompute/rest_test.go
+++ b/services/gcpcompute/rest_test.go
@@ -1,7 +1,7 @@
package gcpcompute_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/url"
@@ -12,6 +12,8 @@ import (
"github.com/stretchr/testify/require"
"google.golang.org/api/iterator"
"google.golang.org/protobuf/proto"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type listDoc struct {
@@ -29,7 +31,7 @@ func getJSON[T any](t *testing.T, e *env, path string, want int) T {
require.Equal(t, want, status, body)
var out T
- require.NoError(t, json.Unmarshal([]byte(body), &out), body)
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()), body)
return out
}
@@ -209,7 +211,7 @@ func TestListQueryErrors(t *testing.T) {
} `json:"error"`
}
- require.NoError(t, json.Unmarshal([]byte(body), &doc))
+ require.NoError(t, json.Unmarshal([]byte(body), &doc, jsonx.V1()))
assert.Equal(t, http.StatusBadRequest, doc.Error.Code)
require.NotEmpty(t, doc.Error.Errors)
assert.Equal(t, "invalid", doc.Error.Errors[0].Reason)
@@ -473,7 +475,7 @@ func TestErrorBodyShape(t *testing.T) {
} `json:"error"`
}
- require.NoError(t, json.Unmarshal([]byte(body), &doc))
+ require.NoError(t, json.Unmarshal([]byte(body), &doc, jsonx.V1()))
assert.Equal(t, 404, doc.Error.Code)
assert.Equal(t, "The resource 'projects/p/global/networks/ghost' was not found", doc.Error.Message)
require.Len(t, doc.Error.Errors, 1)
diff --git a/services/gcpcontainer/persistence.go b/services/gcpcontainer/persistence.go
index d65acfa1d8..2bc0a871d5 100644
--- a/services/gcpcontainer/persistence.go
+++ b/services/gcpcontainer/persistence.go
@@ -2,7 +2,7 @@ package gcpcontainer
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -18,32 +18,32 @@ import (
const snapshotVersion = 1
type poolSnap struct {
- PrevVersion string `json:"prevVersion"`
- DeleteOp string `json:"deleteOp"`
- Pool json.RawMessage `json:"pool"`
- Size int32 `json:"size"`
+ PrevVersion string `json:"prevVersion"`
+ DeleteOp string `json:"deleteOp"`
+ Pool jsontext.Value `json:"pool"`
+ Size int32 `json:"size"`
}
type clusterSnap struct {
- Rotation *rotationRec `json:"rotation,omitempty"`
- Cluster json.RawMessage `json:"cluster"`
- Project string `json:"project"`
- Failure string `json:"failure"`
- DeleteOp string `json:"deleteOp"`
- Pools []poolSnap `json:"pools"`
+ Rotation *rotationRec `json:"rotation,omitempty"`
+ Cluster jsontext.Value `json:"cluster"`
+ Project string `json:"project"`
+ Failure string `json:"failure"`
+ DeleteOp string `json:"deleteOp"`
+ Pools []poolSnap `json:"pools"`
}
type opSnap struct {
- EndAt time.Time `json:"endAt"`
- Project string `json:"project"`
- Location string `json:"location"`
- Cluster string `json:"cluster"`
- Pool string `json:"pool"`
- Failure string `json:"failure"`
- Op json.RawMessage `json:"op"`
- Kind opKind `json:"kind"`
- Manual bool `json:"manual"`
- Finished bool `json:"finished"`
+ EndAt time.Time `json:"endAt"`
+ Project string `json:"project"`
+ Location string `json:"location"`
+ Cluster string `json:"cluster"`
+ Pool string `json:"pool"`
+ Failure string `json:"failure"`
+ Op jsontext.Value `json:"op"`
+ Kind opKind `json:"kind"`
+ Manual bool `json:"manual"`
+ Finished bool `json:"finished"`
}
type backendSnapshot struct {
@@ -53,7 +53,7 @@ type backendSnapshot struct {
Seq uint64 `json:"seq"`
}
-func marshalPB(m proto.Message) json.RawMessage {
+func marshalPB(m proto.Message) jsontext.Value {
raw, err := protojson.Marshal(m)
if err != nil {
return nil
diff --git a/services/gcpcontainer/rest_test.go b/services/gcpcontainer/rest_test.go
index ca4c1aa1b4..c9c5fbb218 100644
--- a/services/gcpcontainer/rest_test.go
+++ b/services/gcpcontainer/rest_test.go
@@ -3,7 +3,7 @@ package gcpcontainer_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -115,7 +116,7 @@ func (e *env) raw(t *testing.T, method, path string, body any) (int, map[string]
var rdr io.Reader
if body != nil {
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
rdr = bytes.NewReader(raw)
@@ -136,7 +137,7 @@ func (e *env) raw(t *testing.T, method, path string, body any) (int, map[string]
require.NoError(t, err)
if len(raw) > 0 {
- require.NoError(t, json.Unmarshal(raw, &out), string(raw))
+ require.NoError(t, json.Unmarshal(raw, &out, jsonx.V1()), string(raw))
}
return resp.StatusCode, out
diff --git a/services/gcpdatastore/persistence.go b/services/gcpdatastore/persistence.go
index 093551b1af..2b6c07b43b 100644
--- a/services/gcpdatastore/persistence.go
+++ b/services/gcpdatastore/persistence.go
@@ -2,7 +2,7 @@ package gcpdatastore
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -18,20 +18,20 @@ import (
const snapshotVersion = 1
type entitySnap struct {
- Created time.Time `json:"created"`
- Updated time.Time `json:"updated"`
- Entity json.RawMessage `json:"entity"`
- Version int64 `json:"version"`
+ Created time.Time `json:"created"`
+ Updated time.Time `json:"updated"`
+ Entity jsontext.Value `json:"entity"`
+ Version int64 `json:"version"`
}
type backendSnapshot struct {
- LastCommit time.Time `json:"lastCommit"`
- Entities map[string]entitySnap `json:"entities"`
- UsedIDs map[string][]int64 `json:"usedIds"`
- Indexes map[string]json.RawMessage `json:"indexes"`
- Operations map[string]json.RawMessage `json:"operations"`
- NextIndex int64 `json:"nextIndex"`
- Version int `json:"version"`
+ LastCommit time.Time `json:"lastCommit"`
+ Entities map[string]entitySnap `json:"entities"`
+ UsedIDs map[string][]int64 `json:"usedIds"`
+ Indexes map[string]jsontext.Value `json:"indexes"`
+ Operations map[string]jsontext.Value `json:"operations"`
+ NextIndex int64 `json:"nextIndex"`
+ Version int `json:"version"`
}
// Snapshot serialises the current state of every entity, reserved ids, indexes and operations.
@@ -46,7 +46,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
NextIndex: b.nextIndex,
Entities: make(map[string]entitySnap, len(b.ents)),
UsedIDs: make(map[string][]int64, len(b.used)),
- Indexes: make(map[string]json.RawMessage, len(b.indexes)),
+ Indexes: make(map[string]jsontext.Value, len(b.indexes)),
Operations: b.ops.Snapshot(),
}
diff --git a/services/gcpdns/handler.go b/services/gcpdns/handler.go
index 885c5f6025..be0ef10350 100644
--- a/services/gcpdns/handler.go
+++ b/services/gcpdns/handler.go
@@ -2,7 +2,7 @@ package gcpdns
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +16,7 @@ import (
pkgdns "github.com/blackbirdworks/gopherstack/pkgs/dns"
"github.com/blackbirdworks/gopherstack/pkgs/gcpenforce"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc"
@@ -185,11 +186,11 @@ func decode[T any](c *echo.Context) (*T, map[string]any, error) {
return v, raw, nil
}
- if err = json.Unmarshal(data, v); err != nil {
+ if err = json.Unmarshal(data, v, jsonx.V1()); err != nil {
return nil, nil, errInvalid("Invalid JSON payload received. %v", err)
}
- if err = json.Unmarshal(data, &raw); err != nil {
+ if err = json.Unmarshal(data, &raw, jsonx.V1()); err != nil {
return nil, nil, errInvalid("Invalid JSON payload received. %v", err)
}
diff --git a/services/gcpdns/handler_test.go b/services/gcpdns/handler_test.go
index 37543094fe..c612f2887f 100644
--- a/services/gcpdns/handler_test.go
+++ b/services/gcpdns/handler_test.go
@@ -1,7 +1,8 @@
package gcpdns_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"strings"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/gcpdns"
)
@@ -65,7 +67,7 @@ func TestWireErrorsAndRouting(t *testing.T) {
} `json:"error"`
}
- require.NoError(t, json.Unmarshal(raw, &body), string(raw))
+ require.NoError(t, json.Unmarshal(raw, &body, jsonx.V1()), string(raw))
assert.Equal(t, tt.status, body.Error.Code)
assert.NotEmpty(t, body.Error.Message)
require.Len(t, body.Error.Errors, 1)
@@ -89,7 +91,7 @@ func TestWireShapes(t *testing.T) {
var m map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&m))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &m, jsonx.V1()))
return m
}
diff --git a/services/gcpdns/rrsets.go b/services/gcpdns/rrsets.go
index 2e2b5209ad..c32c30fb1b 100644
--- a/services/gcpdns/rrsets.go
+++ b/services/gcpdns/rrsets.go
@@ -1,7 +1,7 @@
package gcpdns
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strconv"
@@ -10,6 +10,7 @@ import (
"github.com/miekg/dns"
dnsapi "google.golang.org/api/dns/v1"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -52,8 +53,8 @@ func sameRRSet(a, b *dnsapi.ResourceRecordSet) bool {
return false
}
- ar, _ := json.Marshal(a.RoutingPolicy)
- br, _ := json.Marshal(b.RoutingPolicy)
+ ar, _ := json.Marshal(a.RoutingPolicy, jsonx.V1())
+ br, _ := json.Marshal(b.RoutingPolicy, jsonx.V1())
return string(ar) == string(br)
}
diff --git a/services/gcpdns/store.go b/services/gcpdns/store.go
index a42edd648f..9dbfce8f08 100644
--- a/services/gcpdns/store.go
+++ b/services/gcpdns/store.go
@@ -3,13 +3,14 @@ package gcpdns
import (
"crypto/rand"
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"strings"
"time"
"github.com/miekg/dns"
dnsapi "google.golang.org/api/dns/v1"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
)
@@ -109,13 +110,13 @@ func clone[T any](v *T) *T {
return nil
}
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return v
}
out := new(T)
- if err = json.Unmarshal(data, out); err != nil {
+ if err = json.Unmarshal(data, out, jsonx.V1()); err != nil {
return v
}
@@ -154,25 +155,25 @@ func mergePatch(dst, patch map[string]any) {
}
func patched[T any](cur *T, patch map[string]any) (*T, error) {
- data, err := json.Marshal(cur)
+ data, err := json.Marshal(cur, jsonx.V1())
if err != nil {
return nil, errInvalid("Invalid JSON payload received.")
}
var m map[string]any
- if err = json.Unmarshal(data, &m); err != nil {
+ if err = json.Unmarshal(data, &m, jsonx.V1()); err != nil {
return nil, errInvalid("Invalid JSON payload received.")
}
mergePatch(m, patch)
- data, err = json.Marshal(m)
+ data, err = json.Marshal(m, jsonx.V1())
if err != nil {
return nil, errInvalid("Invalid JSON payload received.")
}
out := new(T)
- if err = json.Unmarshal(data, out); err != nil {
+ if err = json.Unmarshal(data, out, jsonx.V1()); err != nil {
return nil, errInvalid("Invalid JSON payload received: %v", err)
}
diff --git a/services/gcpeventarc/delivery.go b/services/gcpeventarc/delivery.go
index 3e0c681290..9ebb6d54af 100644
--- a/services/gcpeventarc/delivery.go
+++ b/services/gcpeventarc/delivery.go
@@ -4,7 +4,8 @@ import (
"bytes"
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net"
"net/http"
@@ -18,6 +19,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/gcpdispatch"
"github.com/blackbirdworks/gopherstack/pkgs/gcpserve"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -96,7 +98,11 @@ func (b *InMemoryBackend) servePush(w http.ResponseWriter, r *http.Request) {
}
var env pushedEnvelope
- if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, maxPushBytes)).Decode(&env); err != nil {
+ if err := json.UnmarshalDecode(
+ jsontext.NewDecoder(http.MaxBytesReader(w, r.Body, maxPushBytes)),
+ &env,
+ jsonx.V1(),
+ ); err != nil {
http.Error(w, "invalid push envelope: "+err.Error(), http.StatusBadRequest)
return
@@ -162,20 +168,20 @@ func (b *InMemoryBackend) deliverToWorkflow(
}
type structuredEvent struct {
- SpecVersion string `json:"specversion"`
- ID string `json:"id"`
- Source string `json:"source"`
- Type string `json:"type"`
- DataContentType string `json:"datacontenttype"`
- Subject string `json:"subject,omitempty"`
- Time string `json:"time,omitempty"`
- Data json.RawMessage `json:"data"`
+ SpecVersion string `json:"specversion"`
+ ID string `json:"id"`
+ Source string `json:"source"`
+ Type string `json:"type"`
+ DataContentType string `json:"datacontenttype"`
+ Subject string `json:"subject,omitempty"`
+ Time string `json:"time,omitempty"`
+ Data jsontext.Value `json:"data"`
}
func workflowArgument(ev cloudEvent) ([]byte, error) {
- data := json.RawMessage(ev.body)
- if !json.Valid(data) {
- quoted, err := json.Marshal(string(ev.body))
+ data := jsontext.Value(ev.body)
+ if !data.IsValid(jsonx.V1()) {
+ quoted, err := json.Marshal(string(ev.body), jsonx.V1())
if err != nil {
return nil, err
}
@@ -186,12 +192,12 @@ func workflowArgument(ev cloudEvent) ([]byte, error) {
return json.Marshal(structuredEvent{
SpecVersion: "1.0", ID: ev.id, Source: ev.source, Type: ev.typ, Subject: ev.subject, Time: ev.time,
DataContentType: contentTypeJSON, Data: data,
- })
+ }, jsonx.V1())
}
func buildEvent(t *eventarcpb.Trigger, env pushedEnvelope) (cloudEvent, bool) {
if eventType(t.GetEventFilters()) == typePubsubPublished {
- body, err := json.Marshal(pushedEnvelope{Subscription: env.Subscription, Message: env.Message})
+ body, err := json.Marshal(pushedEnvelope{Subscription: env.Subscription, Message: env.Message}, jsonx.V1())
if err != nil {
return cloudEvent{}, false
}
diff --git a/services/gcpeventarc/delivery_test.go b/services/gcpeventarc/delivery_test.go
index 5e9ea1f2c4..ce9465c500 100644
--- a/services/gcpeventarc/delivery_test.go
+++ b/services/gcpeventarc/delivery_test.go
@@ -2,7 +2,7 @@ package gcpeventarc_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
@@ -14,6 +14,8 @@ import (
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/types/known/fieldmaskpb"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestPubsubDelivery(t *testing.T) {
@@ -89,7 +91,7 @@ func TestPubsubDelivery(t *testing.T) {
} `json:"message"`
Subscription string `json:"subscription"`
}
- require.NoError(t, json.Unmarshal(got.body, &body))
+ require.NoError(t, json.Unmarshal(got.body, &body, jsonx.V1()))
raw, err := base64.StdEncoding.DecodeString(body.Message.Data)
require.NoError(t, err)
@@ -231,7 +233,7 @@ func TestStorageDelivery(t *testing.T) {
assert.Equal(t, "application/json", got.header.Get("Content-Type"))
var data map[string]any
- require.NoError(t, json.Unmarshal(got.body, &data))
+ require.NoError(t, json.Unmarshal(got.body, &data, jsonx.V1()))
assert.Equal(t, "photos", data["bucket"])
assert.Equal(t, strings.TrimPrefix(tt.subject, "objects/"), data["name"])
})
diff --git a/services/gcpeventarc/persistence.go b/services/gcpeventarc/persistence.go
index 245db08d77..3d34b43ba1 100644
--- a/services/gcpeventarc/persistence.go
+++ b/services/gcpeventarc/persistence.go
@@ -2,7 +2,7 @@ package gcpeventarc
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"cloud.google.com/go/eventarc/apiv1/eventarcpb"
@@ -18,27 +18,27 @@ import (
const snapshotVersion = 1
type triggerSnap struct {
- Trigger json.RawMessage `json:"trigger"`
- Policy json.RawMessage `json:"policy,omitempty"`
- OwnedTopic bool `json:"ownedTopic,omitempty"`
+ Trigger jsontext.Value `json:"trigger"`
+ Policy jsontext.Value `json:"policy,omitempty"`
+ OwnedTopic bool `json:"ownedTopic,omitempty"`
}
type channelSnap struct {
- Channel json.RawMessage `json:"channel"`
- Policy json.RawMessage `json:"policy,omitempty"`
+ Channel jsontext.Value `json:"channel"`
+ Policy jsontext.Value `json:"policy,omitempty"`
}
type backendSnapshot struct {
- Triggers map[string]triggerSnap `json:"triggers"`
- Channels map[string]channelSnap `json:"channels"`
- Connections map[string]json.RawMessage `json:"connections"`
- Configs map[string]json.RawMessage `json:"configs"`
- Operations map[string]json.RawMessage `json:"operations"`
- Version int `json:"version"`
- Seq uint64 `json:"seq"`
+ Triggers map[string]triggerSnap `json:"triggers"`
+ Channels map[string]channelSnap `json:"channels"`
+ Connections map[string]jsontext.Value `json:"connections"`
+ Configs map[string]jsontext.Value `json:"configs"`
+ Operations map[string]jsontext.Value `json:"operations"`
+ Version int `json:"version"`
+ Seq uint64 `json:"seq"`
}
-func marshalPB(m proto.Message) json.RawMessage {
+func marshalPB(m proto.Message) jsontext.Value {
if m == nil || !m.ProtoReflect().IsValid() {
return nil
}
@@ -55,7 +55,7 @@ func unmarshalPB(raw []byte, m proto.Message) error {
return protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(raw, m)
}
-func restorePolicy(raw json.RawMessage) (*iampb.Policy, error) {
+func restorePolicy(raw jsontext.Value) (*iampb.Policy, error) {
if len(raw) == 0 {
return nil, nil //nolint:nilnil // no policy stored
}
@@ -77,8 +77,8 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Version: snapshotVersion, Seq: b.seq,
Triggers: make(map[string]triggerSnap, len(b.triggers)),
Channels: make(map[string]channelSnap, len(b.channels)),
- Connections: make(map[string]json.RawMessage, len(b.conns)),
- Configs: make(map[string]json.RawMessage, len(b.configs)),
+ Connections: make(map[string]jsontext.Value, len(b.conns)),
+ Configs: make(map[string]jsontext.Value, len(b.configs)),
Operations: b.ops.Snapshot(),
}
diff --git a/services/gcpeventarc/workflow_delivery_test.go b/services/gcpeventarc/workflow_delivery_test.go
index d32e207427..a194103177 100644
--- a/services/gcpeventarc/workflow_delivery_test.go
+++ b/services/gcpeventarc/workflow_delivery_test.go
@@ -2,7 +2,7 @@ package gcpeventarc_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"cloud.google.com/go/eventarc/apiv1/eventarcpb"
@@ -13,6 +13,8 @@ import (
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/types/known/fieldmaskpb"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const eventWorkflow = `
@@ -94,7 +96,7 @@ func TestPubsubEventStartsWorkflowExecution(t *testing.T) {
require.Equal(t, executionspb.Execution_SUCCEEDED, execs[0].GetState(), execs[0].GetError().GetPayload())
var arg map[string]any
- require.NoError(t, json.Unmarshal([]byte(execs[0].GetArgument()), &arg))
+ require.NoError(t, json.Unmarshal([]byte(execs[0].GetArgument()), &arg, jsonx.V1()))
assert.Equal(t, "1.0", arg["specversion"])
assert.Equal(t, "google.cloud.pubsub.topic.v1.messagePublished", arg["type"])
@@ -111,7 +113,7 @@ func TestPubsubEventStartsWorkflowExecution(t *testing.T) {
Subscription string `json:"subscription"`
} `json:"data"`
}
- require.NoError(t, json.Unmarshal([]byte(execs[0].GetResult()), &res))
+ require.NoError(t, json.Unmarshal([]byte(execs[0].GetResult()), &res, jsonx.V1()))
assert.Equal(t, "1.0", res.Specversion)
assert.Equal(t, "//pubsub.googleapis.com/"+topic, res.Source)
@@ -148,7 +150,7 @@ func TestStorageEventStartsWorkflowExecution(t *testing.T) {
Name string `json:"name"`
} `json:"data"`
}
- require.NoError(t, json.Unmarshal([]byte(execs[0].GetResult()), &res))
+ require.NoError(t, json.Unmarshal([]byte(execs[0].GetResult()), &res, jsonx.V1()))
assert.Equal(t, "google.cloud.storage.object.v1.finalized", res.Type)
assert.Equal(t, "events-bkt", res.Data.Bucket)
assert.Equal(t, "dir/a.txt", res.Data.Name)
diff --git a/services/gcpfirestore/persistence.go b/services/gcpfirestore/persistence.go
index 9604e8a4a9..1609f61c9f 100644
--- a/services/gcpfirestore/persistence.go
+++ b/services/gcpfirestore/persistence.go
@@ -2,7 +2,7 @@ package gcpfirestore
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -17,9 +17,9 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- LastCommit time.Time `json:"lastCommit"`
- Docs map[string]json.RawMessage `json:"docs"`
- Version int `json:"version"`
+ LastCommit time.Time `json:"lastCommit"`
+ Docs map[string]jsontext.Value `json:"docs"`
+ Version int `json:"version"`
}
// Snapshot serialises the current state of every document; version history and transactions are not persisted.
@@ -30,7 +30,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
snap := backendSnapshot{
Version: snapshotVersion,
LastCommit: b.lastCommit,
- Docs: make(map[string]json.RawMessage, len(b.docs)),
+ Docs: make(map[string]jsontext.Value, len(b.docs)),
}
for name, rec := range b.docs {
diff --git a/services/gcpfunctions/docker_test.go b/services/gcpfunctions/docker_test.go
index ec796726d6..a04e097978 100644
--- a/services/gcpfunctions/docker_test.go
+++ b/services/gcpfunctions/docker_test.go
@@ -4,7 +4,7 @@ import (
"context"
"crypto/rand"
"encoding/hex"
- "encoding/json"
+ "encoding/json/jsontext"
"log/slog"
"net/http"
"os"
@@ -109,7 +109,7 @@ func TestDocker_StorageTriggeredFunction(t *testing.T) {
}
gcs := gcpstorage.NewInMemoryBackend()
- _, err = gcs.CreateBucket("test-project", gcpstorage.Patch{"name": json.RawMessage(`"inbox"`)})
+ _, err = gcs.CreateBucket("test-project", gcpstorage.Patch{"name": jsontext.Value(`"inbox"`)})
require.NoError(t, err)
e := newEnv(t, false)
diff --git a/services/gcpfunctions/fake_runtime_test.go b/services/gcpfunctions/fake_runtime_test.go
index a103f6fc09..be4924b62b 100644
--- a/services/gcpfunctions/fake_runtime_test.go
+++ b/services/gcpfunctions/fake_runtime_test.go
@@ -2,7 +2,8 @@ package gcpfunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"net"
@@ -13,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/container"
"github.com/blackbirdworks/gopherstack/pkgs/gcpserve"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type received struct {
@@ -118,7 +120,7 @@ func (f *fakeRuntime) CreateAndStart(_ context.Context, spec container.Spec) (st
}
w.WriteHeader(int(f.reply.Load()))
- _ = json.NewEncoder(w).Encode(rec)
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), rec)
})}
go f.serve(c, l, addr)
diff --git a/services/gcpfunctions/helpers_test.go b/services/gcpfunctions/helpers_test.go
index d733c2e9e1..d3d2de1596 100644
--- a/services/gcpfunctions/helpers_test.go
+++ b/services/gcpfunctions/helpers_test.go
@@ -4,7 +4,7 @@ import (
"archive/zip"
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"strings"
@@ -21,6 +21,7 @@ import (
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/status"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/gcpfunctions"
)
@@ -222,7 +223,7 @@ func decode(t *testing.T, body string) received {
var r received
- require.NoError(t, json.Unmarshal([]byte(body), &r))
+ require.NoError(t, json.Unmarshal([]byte(body), &r, jsonx.V1()))
return r
}
diff --git a/services/gcpfunctions/ingress.go b/services/gcpfunctions/ingress.go
index 2aea6a1ad2..324e7681e2 100644
--- a/services/gcpfunctions/ingress.go
+++ b/services/gcpfunctions/ingress.go
@@ -2,7 +2,8 @@ package gcpfunctions
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net"
@@ -17,6 +18,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/gcpevents"
"github.com/blackbirdworks/gopherstack/pkgs/gcpscale"
"github.com/blackbirdworks/gopherstack/pkgs/gcpserve"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -303,7 +305,11 @@ func (b *InMemoryBackend) serveEvent(w http.ResponseWriter, r *http.Request) {
}
var env pushEnvelope
- if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, maxEventBytes)).Decode(&env); err != nil {
+ if err := json.UnmarshalDecode(
+ jsontext.NewDecoder(http.MaxBytesReader(w, r.Body, maxEventBytes)),
+ &env,
+ jsonx.V1(),
+ ); err != nil {
http.Error(w, "invalid push envelope: "+err.Error(), http.StatusBadRequest)
return
@@ -315,7 +321,7 @@ func (b *InMemoryBackend) serveEvent(w http.ResponseWriter, r *http.Request) {
return
}
- body, err := json.Marshal(env)
+ body, err := json.Marshal(env, jsonx.V1())
if err != nil {
http.Error(w, "encode event: "+err.Error(), http.StatusInternalServerError)
diff --git a/services/gcpfunctions/persistence.go b/services/gcpfunctions/persistence.go
index bdb3c5eff6..5241b6f699 100644
--- a/services/gcpfunctions/persistence.go
+++ b/services/gcpfunctions/persistence.go
@@ -2,7 +2,7 @@ package gcpfunctions
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"cloud.google.com/go/functions/apiv2/functionspb"
@@ -18,11 +18,11 @@ import (
const snapshotVersion = 1
type fnSnap struct {
- Function json.RawMessage `json:"function"`
- Failure string `json:"failure"`
- SubName string `json:"subName"`
- Zip []byte `json:"zip"`
- RevSeq int `json:"revSeq"`
+ Function jsontext.Value `json:"function"`
+ Failure string `json:"failure"`
+ SubName string `json:"subName"`
+ Zip []byte `json:"zip"`
+ RevSeq int `json:"revSeq"`
}
type uploadSnap struct {
@@ -31,10 +31,10 @@ type uploadSnap struct {
}
type backendSnapshot struct {
- Functions map[string]fnSnap `json:"functions"`
- Uploads map[string]uploadSnap `json:"uploads"`
- Operations map[string]json.RawMessage `json:"operations"`
- Version int `json:"version"`
+ Functions map[string]fnSnap `json:"functions"`
+ Uploads map[string]uploadSnap `json:"uploads"`
+ Operations map[string]jsontext.Value `json:"operations"`
+ Version int `json:"version"`
}
// Snapshot serialises all functions and sources; containers are not part of it.
diff --git a/services/gcpiam/keymat.go b/services/gcpiam/keymat.go
index 9f65dac486..155e3a52b2 100644
--- a/services/gcpiam/keymat.go
+++ b/services/gcpiam/keymat.go
@@ -6,7 +6,8 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"encoding/pem"
"math/big"
"net/url"
@@ -14,6 +15,8 @@ import (
"cloud.google.com/go/iam/admin/apiv1/adminpb"
pkcs12 "software.sslmate.com/src/go-pkcs12"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -118,7 +121,7 @@ func (m *material) credentials(acct *adminpb.ServiceAccount, keyID string) ([]by
return nil, errInternal(err)
}
- out, err := json.MarshalIndent(credentialsFile{
+ out, err := json.Marshal(credentialsFile{
Type: credentialsType,
ProjectID: acct.GetProjectId(),
PrivateKeyID: keyID,
@@ -130,7 +133,7 @@ func (m *material) credentials(acct *adminpb.ServiceAccount, keyID string) ([]by
AuthProviderX509CertURL: authCertsURL,
ClientX509CertURL: clientCertPrefix + url.PathEscape(acct.GetEmail()),
UniverseDomain: universeDomain,
- }, "", " ")
+ }, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
if err != nil {
return nil, errInternal(err)
}
diff --git a/services/gcpiam/keys_test.go b/services/gcpiam/keys_test.go
index d1577c53c1..992af2dfb7 100644
--- a/services/gcpiam/keys_test.go
+++ b/services/gcpiam/keys_test.go
@@ -6,7 +6,7 @@ import (
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"math/big"
"testing"
@@ -17,6 +17,8 @@ import (
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
pkcs12 "software.sslmate.com/src/go-pkcs12"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type keyFile struct {
@@ -39,7 +41,7 @@ func createKey(t *testing.T, e *env, sa *adminpb.ServiceAccount) (*adminpb.Servi
require.NoError(t, err)
var kf keyFile
- require.NoError(t, json.Unmarshal(key.GetPrivateKeyData(), &kf))
+ require.NoError(t, json.Unmarshal(key.GetPrivateKeyData(), &kf, jsonx.V1()))
return key, kf
}
@@ -123,7 +125,11 @@ func TestKeyAlgorithmsAndFormats(t *testing.T) {
assert.Equal(t, adminpb.ServiceAccountKeyAlgorithm_KEY_ALG_RSA_2048, small.GetKeyAlgorithm())
var kf keyFile
- require.NoError(t, json.Unmarshal(small.GetPrivateKeyData(), &kf), "default type is the credentials file")
+ require.NoError(
+ t,
+ json.Unmarshal(small.GetPrivateKeyData(), &kf, jsonx.V1()),
+ "default type is the credentials file",
+ )
assert.Equal(t, 2048, parsePrivate(t, kf.PrivateKey).N.BitLen())
p12, err := e.client.CreateServiceAccountKey(t.Context(), &adminpb.CreateServiceAccountKeyRequest{
diff --git a/services/gcpiam/persistence.go b/services/gcpiam/persistence.go
index ac740127b7..fd93423ac8 100644
--- a/services/gcpiam/persistence.go
+++ b/services/gcpiam/persistence.go
@@ -2,7 +2,7 @@ package gcpiam
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -19,23 +19,23 @@ import (
const snapshotVersion = 1
type keySnap struct {
- Key json.RawMessage `json:"key"`
- Cert []byte `json:"cert,omitempty"`
- Public []byte `json:"public,omitempty"`
- Private []byte `json:"private,omitempty"`
+ Key jsontext.Value `json:"key"`
+ Cert []byte `json:"cert,omitempty"`
+ Public []byte `json:"public,omitempty"`
+ Private []byte `json:"private,omitempty"`
}
type accountSnap struct {
Keys map[string]keySnap `json:"keys,omitempty"`
System *keySnap `json:"system,omitempty"`
DeletedAt time.Time `json:"deletedAt"`
- Account json.RawMessage `json:"account"`
- Policy json.RawMessage `json:"policy,omitempty"`
+ Account jsontext.Value `json:"account"`
+ Policy jsontext.Value `json:"policy,omitempty"`
}
type roleSnap struct {
- DeletedAt time.Time `json:"deletedAt"`
- Role json.RawMessage `json:"role"`
+ DeletedAt time.Time `json:"deletedAt"`
+ Role jsontext.Value `json:"role"`
}
type backendSnapshot struct {
@@ -45,7 +45,7 @@ type backendSnapshot struct {
Version int `json:"version"`
}
-func marshalPB(m proto.Message) json.RawMessage {
+func marshalPB(m proto.Message) jsontext.Value {
out, err := protojson.Marshal(m)
if err != nil {
return nil
@@ -54,7 +54,7 @@ func marshalPB(m proto.Message) json.RawMessage {
return out
}
-func unmarshalPB(raw json.RawMessage, m proto.Message) error {
+func unmarshalPB(raw jsontext.Value, m proto.Message) error {
return protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(raw, m)
}
diff --git a/services/gcpiam/sign.go b/services/gcpiam/sign.go
index 803846aaaa..a9c38d98c3 100644
--- a/services/gcpiam/sign.go
+++ b/services/gcpiam/sign.go
@@ -7,12 +7,15 @@ import (
"crypto/sha256"
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strings"
"time"
"cloud.google.com/go/iam/admin/apiv1/adminpb"
"google.golang.org/protobuf/types/known/timestamppb"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -160,7 +163,7 @@ func (b *InMemoryBackend) SignJwt(name, payload string) (string, string, error)
return "", "", err
}
- header, err := json.Marshal(map[string]string{"alg": "RS256", "typ": "JWT", "kid": keyID})
+ header, err := json.Marshal(map[string]string{"alg": "RS256", "typ": "JWT", "kid": keyID}, jsonx.V1())
if err != nil {
return "", "", errInternal(err)
}
@@ -179,17 +182,16 @@ func (b *InMemoryBackend) SignJwt(name, payload string) (string, string, error)
func (b *InMemoryBackend) jwtClaims(payload string) ([]byte, error) {
var claims map[string]any
- dec := json.NewDecoder(strings.NewReader(payload))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(strings.NewReader(payload))
- if err := dec.Decode(&claims); err != nil || claims == nil {
+ if err := json.UnmarshalDecode(dec, &claims, jsonx.V1(), jsonx.UseNumber()); err != nil || claims == nil {
return nil, errInvalid("The JWT payload must be a serialized JSON object.")
}
now := b.now()
if raw, ok := claims["exp"]; ok {
- num, isNum := raw.(json.Number)
+ num, isNum := raw.(jsonx.Number)
exp, err := num.Int64()
if !isNum || err != nil {
@@ -203,7 +205,7 @@ func (b *InMemoryBackend) jwtClaims(payload string) ([]byte, error) {
claims["exp"] = now.Add(jwtDefaultTTL).Unix()
}
- out, err := json.Marshal(claims)
+ out, err := json.Marshal(claims, jsonx.V1())
if err != nil {
return nil, errInternal(err)
}
diff --git a/services/gcpiam/sign_test.go b/services/gcpiam/sign_test.go
index fdcc93cdf7..a485f5b042 100644
--- a/services/gcpiam/sign_test.go
+++ b/services/gcpiam/sign_test.go
@@ -2,7 +2,7 @@ package gcpiam_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func systemPublicKey(t *testing.T, e *env, sa *adminpb.ServiceAccount, keyID string) []byte {
@@ -61,14 +63,14 @@ func TestSignJwt(t *testing.T) {
require.NoError(t, err)
var header map[string]string
- require.NoError(t, json.Unmarshal(hdr, &header))
+ require.NoError(t, json.Unmarshal(hdr, &header, jsonx.V1()))
assert.Equal(t, map[string]string{"alg": "RS256", "typ": "JWT", "kid": keyID}, header)
body, err := enc.DecodeString(parts[1])
require.NoError(t, err)
var claims map[string]any
- require.NoError(t, json.Unmarshal(body, &claims))
+ require.NoError(t, json.Unmarshal(body, &claims, jsonx.V1()))
assert.Equal(t, "someone", claims["sub"])
assert.InDelta(t, float64(now+3600), claims["exp"], 0, "exp defaults to one hour")
@@ -137,7 +139,7 @@ func (e *env) signJwt(t *testing.T, name, payload string) (string, string, error
}
func itoa(n int64) string {
- b, _ := json.Marshal(n)
+ b, _ := json.Marshal(n, jsonx.V1())
return string(b)
}
diff --git a/services/gcpidentitytoolkit/handler.go b/services/gcpidentitytoolkit/handler.go
index 5f46d821da..75ff9a0c93 100644
--- a/services/gcpidentitytoolkit/handler.go
+++ b/services/gcpidentitytoolkit/handler.go
@@ -4,7 +4,7 @@ import (
"context"
"crypto/rsa"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -21,6 +21,7 @@ import (
idtkv2 "google.golang.org/api/identitytoolkit/v2"
"github.com/blackbirdworks/gopherstack/pkgs/gcpenforce"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -159,11 +160,11 @@ func readJSON[T any](c *echo.Context) (*T, map[string]any, error) {
return v, raw, nil
}
- if err = json.Unmarshal(data, v); err != nil {
+ if err = json.Unmarshal(data, v, jsonx.V1()); err != nil {
return nil, nil, errInvalidArg("Invalid JSON payload received. %v", err)
}
- if err = json.Unmarshal(data, &raw); err != nil {
+ if err = json.Unmarshal(data, &raw, jsonx.V1()); err != nil {
return nil, nil, errInvalidArg("Invalid JSON payload received. %v", err)
}
diff --git a/services/gcpidentitytoolkit/helpers_test.go b/services/gcpidentitytoolkit/helpers_test.go
index a15f4771e1..66619c080e 100644
--- a/services/gcpidentitytoolkit/helpers_test.go
+++ b/services/gcpidentitytoolkit/helpers_test.go
@@ -3,7 +3,8 @@ package gcpidentitytoolkit_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -15,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/gcpidentitytoolkit"
)
@@ -106,7 +108,7 @@ func (e *env) do(t *testing.T, method, path, auth string, body any) result {
var rdr *bytes.Reader
if body != nil {
- data, err := json.Marshal(body)
+ data, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
rdr = bytes.NewReader(data)
@@ -129,7 +131,7 @@ func (e *env) do(t *testing.T, method, path, auth string, body any) result {
defer resp.Body.Close()
out := result{status: resp.StatusCode}
- _ = json.NewDecoder(resp.Body).Decode(&out.body)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out.body, jsonx.V1())
return out
}
diff --git a/services/gcpidentitytoolkit/store.go b/services/gcpidentitytoolkit/store.go
index 8a290cc0f5..b9899279d3 100644
--- a/services/gcpidentitytoolkit/store.go
+++ b/services/gcpidentitytoolkit/store.go
@@ -7,7 +7,7 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"math/big"
"sort"
@@ -16,6 +16,7 @@ import (
idtkv2 "google.golang.org/api/identitytoolkit/v2"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -365,13 +366,13 @@ func cloneJSON[T any](v *T) *T {
return nil
}
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return v
}
out := new(T)
- if json.Unmarshal(data, out) != nil {
+ if json.Unmarshal(data, out, jsonx.V1()) != nil {
return v
}
@@ -401,25 +402,25 @@ func mergePatch(dst, patch map[string]any) {
}
func patched[T any](cur *T, patch map[string]any) (*T, error) {
- data, err := json.Marshal(cur)
+ data, err := json.Marshal(cur, jsonx.V1())
if err != nil {
return nil, errInvalidArg("Invalid JSON payload received.")
}
var m map[string]any
- if err = json.Unmarshal(data, &m); err != nil {
+ if err = json.Unmarshal(data, &m, jsonx.V1()); err != nil {
return nil, errInvalidArg("Invalid JSON payload received.")
}
mergePatch(m, patch)
- data, err = json.Marshal(m)
+ data, err = json.Marshal(m, jsonx.V1())
if err != nil {
return nil, errInvalidArg("Invalid JSON payload received.")
}
out := new(T)
- if err = json.Unmarshal(data, out); err != nil {
+ if err = json.Unmarshal(data, out, jsonx.V1()); err != nil {
return nil, errInvalidArg("Invalid JSON payload received: %v", err)
}
diff --git a/services/gcpidentitytoolkit/tokens.go b/services/gcpidentitytoolkit/tokens.go
index ee7ff38336..da1efa44bf 100644
--- a/services/gcpidentitytoolkit/tokens.go
+++ b/services/gcpidentitytoolkit/tokens.go
@@ -5,13 +5,15 @@ import (
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"maps"
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -48,7 +50,7 @@ func (b *InMemoryBackend) signRefreshLocked(p refreshPayload) (string, error) {
return "", err
}
- body, err := json.Marshal(p)
+ body, err := json.Marshal(p, jsonx.V1())
if err != nil {
return "", err //nolint:wrapcheck // unreachable
}
@@ -84,7 +86,7 @@ func (b *InMemoryBackend) parseRefreshLocked(tok string) (*refreshPayload, bool)
}
var p refreshPayload
- if json.Unmarshal(body, &p) != nil {
+ if json.Unmarshal(body, &p, jsonx.V1()) != nil {
return nil, false
}
@@ -126,7 +128,7 @@ func (b *InMemoryBackend) tokenClaims(
if u.CustomAttrs != "" {
var custom map[string]any
- if json.Unmarshal([]byte(u.CustomAttrs), &custom) == nil {
+ if json.Unmarshal([]byte(u.CustomAttrs), &custom, jsonx.V1()) == nil {
maps.Copy(claims, custom)
}
}
diff --git a/services/gcpidentitytoolkit/users.go b/services/gcpidentitytoolkit/users.go
index f80ddb0c4b..8d330c2f02 100644
--- a/services/gcpidentitytoolkit/users.go
+++ b/services/gcpidentitytoolkit/users.go
@@ -2,7 +2,7 @@ package gcpidentitytoolkit
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/mail"
"regexp"
"strconv"
@@ -12,6 +12,7 @@ import (
idtkv2 "google.golang.org/api/identitytoolkit/v2"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -286,7 +287,7 @@ func parseClaims(raw string) (string, error) {
}
var m map[string]any
- if err := json.Unmarshal([]byte(raw), &m); err != nil {
+ if err := json.Unmarshal([]byte(raw), &m, jsonx.V1()); err != nil {
return "", idtk("INVALID_CLAIMS")
}
diff --git a/services/gcpidentitytoolkit/wire.go b/services/gcpidentitytoolkit/wire.go
index f7d20370bc..7c553ed636 100644
--- a/services/gcpidentitytoolkit/wire.go
+++ b/services/gcpidentitytoolkit/wire.go
@@ -2,8 +2,10 @@ package gcpidentitytoolkit
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"strconv"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// flexInt decodes a JSON number or a quoted number, the two forms Google clients send for int64 fields.
@@ -84,7 +86,7 @@ func (u userWire) MarshalJSON() ([]byte, error) {
aux.ValidSince = strconv.FormatInt(int64(u.ValidSince), 10)
}
- return json.Marshal(aux)
+ return json.Marshal(aux, jsonx.V1())
}
// authRequest is the union of the request fields the account operations read.
@@ -128,14 +130,14 @@ type flexStrings []string
func (f *flexStrings) UnmarshalJSON(data []byte) error {
var one string
- if json.Unmarshal(data, &one) == nil {
+ if json.Unmarshal(data, &one, jsonx.V1()) == nil {
*f = flexStrings{one}
return nil
}
var many []string
- if err := json.Unmarshal(data, &many); err != nil {
+ if err := json.Unmarshal(data, &many, jsonx.V1()); err != nil {
return err //nolint:wrapcheck // surfaced as a JSON decode error
}
diff --git a/services/gcpkms/persistence.go b/services/gcpkms/persistence.go
index dd23f7ede1..b756ce28c3 100644
--- a/services/gcpkms/persistence.go
+++ b/services/gcpkms/persistence.go
@@ -2,7 +2,7 @@ package gcpkms
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"cloud.google.com/go/iam/apiv1/iampb"
@@ -19,34 +19,34 @@ const snapshotVersion = 1
// Key material is stored unencrypted in the snapshot: this is a local emulator, not a secure key store.
type versionSnap struct {
- Version json.RawMessage `json:"version"`
- Material []byte `json:"material,omitempty"`
- Deleted bool `json:"deleted,omitempty"`
+ Version jsontext.Value `json:"version"`
+ Material []byte `json:"material,omitempty"`
+ Deleted bool `json:"deleted,omitempty"`
}
type keySnap struct {
- Key json.RawMessage `json:"key"`
- Versions []versionSnap `json:"versions,omitempty"`
- Primary int64 `json:"primary"`
+ Key jsontext.Value `json:"key"`
+ Versions []versionSnap `json:"versions,omitempty"`
+ Primary int64 `json:"primary"`
}
type jobSnap struct {
- Job json.RawMessage `json:"job"`
- Private []byte `json:"private,omitempty"`
+ Job jsontext.Value `json:"job"`
+ Private []byte `json:"private,omitempty"`
}
type backendSnapshot struct {
- Rings map[string]json.RawMessage `json:"rings"`
- Keys map[string]keySnap `json:"keys"`
- Jobs map[string]jobSnap `json:"jobs"`
- Retired map[string]json.RawMessage `json:"retired,omitempty"`
- Ops map[string]json.RawMessage `json:"operations,omitempty"`
- Policies map[string]json.RawMessage `json:"policies"`
- EtagSeq uint64 `json:"etagSeq"`
- Version int `json:"version"`
+ Rings map[string]jsontext.Value `json:"rings"`
+ Keys map[string]keySnap `json:"keys"`
+ Jobs map[string]jobSnap `json:"jobs"`
+ Retired map[string]jsontext.Value `json:"retired,omitempty"`
+ Ops map[string]jsontext.Value `json:"operations,omitempty"`
+ Policies map[string]jsontext.Value `json:"policies"`
+ EtagSeq uint64 `json:"etagSeq"`
+ Version int `json:"version"`
}
-func marshalPB(m proto.Message) json.RawMessage {
+func marshalPB(m proto.Message) jsontext.Value {
out, err := protojson.Marshal(m)
if err != nil {
return nil
@@ -55,7 +55,7 @@ func marshalPB(m proto.Message) json.RawMessage {
return out
}
-func unmarshalPB(raw json.RawMessage, m proto.Message) error {
+func unmarshalPB(raw jsontext.Value, m proto.Message) error {
return protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(raw, m)
}
@@ -67,11 +67,11 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
snap := backendSnapshot{
Version: snapshotVersion,
EtagSeq: b.etagSeq,
- Rings: make(map[string]json.RawMessage, len(b.rings)),
+ Rings: make(map[string]jsontext.Value, len(b.rings)),
Keys: make(map[string]keySnap, len(b.keys)),
Jobs: make(map[string]jobSnap, len(b.jobs)),
- Retired: make(map[string]json.RawMessage, len(b.retired)),
- Policies: make(map[string]json.RawMessage, len(b.policies)),
+ Retired: make(map[string]jsontext.Value, len(b.retired)),
+ Policies: make(map[string]jsontext.Value, len(b.policies)),
}
for name, r := range b.rings {
diff --git a/services/gcplogging/export_bq.go b/services/gcplogging/export_bq.go
index 0998eb4867..a417ec3a09 100644
--- a/services/gcplogging/export_bq.go
+++ b/services/gcplogging/export_bq.go
@@ -1,12 +1,14 @@
package gcplogging
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
"cloud.google.com/go/logging/apiv2/loggingpb"
"google.golang.org/protobuf/encoding/protojson"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func bqField(name, typ string) map[string]any {
@@ -31,7 +33,7 @@ func bqRow(e *loggingpb.LogEntry) (map[string]any, error) {
}
doc := map[string]any{}
- if err = json.Unmarshal(raw, &doc); err != nil {
+ if err = json.Unmarshal(raw, &doc, jsonx.V1()); err != nil {
return nil, fmt.Errorf("decode entry: %w", err)
}
@@ -46,7 +48,7 @@ func bqRow(e *loggingpb.LogEntry) (map[string]any, error) {
}
if f["type"] == "JSON" {
- enc, _ := json.Marshal(v)
+ enc, _ := json.Marshal(v, jsonx.V1())
v = string(enc)
}
diff --git a/services/gcplogging/filter_eval.go b/services/gcplogging/filter_eval.go
index 43aa799498..72bb834a8e 100644
--- a/services/gcplogging/filter_eval.go
+++ b/services/gcplogging/filter_eval.go
@@ -1,7 +1,7 @@
package gcplogging
import (
- "encoding/json"
+ "encoding/json/v2"
"hash/fnv"
"math"
"net/url"
@@ -13,6 +13,8 @@ import (
ltype "google.golang.org/genproto/googleapis/logging/type"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// entryDoc lazily renders a log entry as JSON-shaped data for filter evaluation.
@@ -42,7 +44,7 @@ func (d *entryDoc) doc() map[string]any {
raw, _ = protojson.Marshal(stripped)
}
- _ = json.Unmarshal(raw, &d.m)
+ _ = json.Unmarshal(raw, &d.m, jsonx.V1())
return d.m
}
@@ -199,7 +201,7 @@ func scalarString(v any) string {
return ""
}
- b, _ := json.Marshal(v)
+ b, _ := json.Marshal(v, jsonx.V1())
return string(b)
}
diff --git a/services/gcplogging/persistence.go b/services/gcplogging/persistence.go
index e6f4ca289e..ecadec7139 100644
--- a/services/gcplogging/persistence.go
+++ b/services/gcplogging/persistence.go
@@ -2,7 +2,7 @@ package gcplogging
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
"time"
@@ -21,9 +21,9 @@ var errBadSnapshot = errors.New("gcplogging: corrupt snapshot")
const snapshotVersion = 1
type bucketSnap struct {
- DeleteRequested time.Time `json:"deleteRequested"`
- Bucket json.RawMessage `json:"bucket"`
- Entries []int `json:"entries,omitempty"`
+ DeleteRequested time.Time `json:"deleteRequested"`
+ Bucket jsontext.Value `json:"bucket"`
+ Entries []int `json:"entries,omitempty"`
}
type seriesSnap struct {
@@ -36,26 +36,26 @@ type seriesSnap struct {
}
type metricSnap struct {
- Metric json.RawMessage `json:"metric"`
- Series []seriesSnap `json:"series,omitempty"`
+ Metric jsontext.Value `json:"metric"`
+ Series []seriesSnap `json:"series,omitempty"`
}
type backendSnapshot struct {
- Buckets map[string]bucketSnap `json:"buckets"`
- Views map[string]json.RawMessage `json:"views"`
- Sinks map[string]json.RawMessage `json:"sinks"`
- Exclusions map[string]json.RawMessage `json:"exclusions"`
- Metrics map[string]metricSnap `json:"metrics"`
- Settings map[string]json.RawMessage `json:"settings"`
- Cmek map[string]json.RawMessage `json:"cmek"`
- Ops map[string]json.RawMessage `json:"ops"`
- Entries []json.RawMessage `json:"entries"`
- Seeded []string `json:"seeded"`
- Seq uint64 `json:"seq"`
- Version int `json:"version"`
+ Buckets map[string]bucketSnap `json:"buckets"`
+ Views map[string]jsontext.Value `json:"views"`
+ Sinks map[string]jsontext.Value `json:"sinks"`
+ Exclusions map[string]jsontext.Value `json:"exclusions"`
+ Metrics map[string]metricSnap `json:"metrics"`
+ Settings map[string]jsontext.Value `json:"settings"`
+ Cmek map[string]jsontext.Value `json:"cmek"`
+ Ops map[string]jsontext.Value `json:"ops"`
+ Entries []jsontext.Value `json:"entries"`
+ Seeded []string `json:"seeded"`
+ Seq uint64 `json:"seq"`
+ Version int `json:"version"`
}
-func marshalPB(m proto.Message) json.RawMessage {
+func marshalPB(m proto.Message) jsontext.Value {
out, err := protojson.Marshal(m)
if err != nil {
return nil
@@ -64,12 +64,12 @@ func marshalPB(m proto.Message) json.RawMessage {
return out
}
-func unmarshalPB(raw json.RawMessage, m proto.Message) error {
+func unmarshalPB(raw jsontext.Value, m proto.Message) error {
return protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(raw, m)
}
-func snapMap[T proto.Message](in map[string]T) map[string]json.RawMessage {
- out := make(map[string]json.RawMessage, len(in))
+func snapMap[T proto.Message](in map[string]T) map[string]jsontext.Value {
+ out := make(map[string]jsontext.Value, len(in))
for k, v := range in {
out[k] = marshalPB(v)
}
@@ -136,7 +136,7 @@ func snapMetric(rec *metricRec) metricSnap {
return ms
}
-func restoreMap[T proto.Message](raw map[string]json.RawMessage, newT func() T) (map[string]T, error) {
+func restoreMap[T proto.Message](raw map[string]jsontext.Value, newT func() T) (map[string]T, error) {
out := make(map[string]T, len(raw))
for k, r := range raw {
@@ -235,7 +235,7 @@ func (b *InMemoryBackend) load(snap *backendSnapshot) error {
return b.loadMetrics(snap.Metrics)
}
-func decodeEntries(raw []json.RawMessage) ([]*loggingpb.LogEntry, error) {
+func decodeEntries(raw []jsontext.Value) ([]*loggingpb.LogEntry, error) {
out := make([]*loggingpb.LogEntry, len(raw))
for i, r := range raw {
diff --git a/services/gcpmonitoring/alerts_test.go b/services/gcpmonitoring/alerts_test.go
index cca9d3d8c2..b04e2746b3 100644
--- a/services/gcpmonitoring/alerts_test.go
+++ b/services/gcpmonitoring/alerts_test.go
@@ -1,7 +1,7 @@
package gcpmonitoring_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
@@ -11,6 +11,8 @@ import (
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/fieldmaskpb"
"google.golang.org/protobuf/types/known/wrapperspb"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const alertTopic = "projects/" + testProject + "/topics/alerts"
@@ -90,7 +92,7 @@ func decode(t *testing.T, msg published) payload {
t.Helper()
var p payload
- require.NoError(t, json.Unmarshal([]byte(msg.data), &p))
+ require.NoError(t, json.Unmarshal([]byte(msg.data), &p, jsonx.V1()))
return p
}
diff --git a/services/gcpmonitoring/helpers_test.go b/services/gcpmonitoring/helpers_test.go
index a901703408..3e1859f32c 100644
--- a/services/gcpmonitoring/helpers_test.go
+++ b/services/gcpmonitoring/helpers_test.go
@@ -2,7 +2,7 @@ package gcpmonitoring_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"sync"
"sync/atomic"
@@ -22,6 +22,7 @@ import (
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/gcpmonitoring"
)
@@ -265,7 +266,7 @@ func mustLabels(t *testing.T, msg published) map[string]string {
} `json:"metric"`
} `json:"incident"`
}
- require.NoError(t, json.Unmarshal([]byte(msg.data), &p))
+ require.NoError(t, json.Unmarshal([]byte(msg.data), &p, jsonx.V1()))
return p.Incident.Metric.Labels
}
diff --git a/services/gcpmonitoring/notify.go b/services/gcpmonitoring/notify.go
index 8d8f9d4294..d0e79d7eab 100644
--- a/services/gcpmonitoring/notify.go
+++ b/services/gcpmonitoring/notify.go
@@ -3,7 +3,7 @@ package gcpmonitoring
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -15,6 +15,7 @@ import (
"cloud.google.com/go/monitoring/apiv3/v2/monitoringpb"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -83,7 +84,7 @@ func (b *InMemoryBackend) record(d Delivery) {
// deliver sends the notification to every target; failures are logged and recorded, never fatal.
func (b *InMemoryBackend) deliver(ctx context.Context, n notification) {
- payload, err := json.Marshal(incidentPayload(n))
+ payload, err := json.Marshal(incidentPayload(n), jsonx.V1())
if err != nil {
return
}
diff --git a/services/gcpmonitoring/persistence.go b/services/gcpmonitoring/persistence.go
index 4fb4a96f72..f4617c9ab3 100644
--- a/services/gcpmonitoring/persistence.go
+++ b/services/gcpmonitoring/persistence.go
@@ -2,7 +2,7 @@ package gcpmonitoring
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -19,38 +19,38 @@ import (
const snapshotVersion = 1
type seriesSnap struct {
- Project string `json:"project"`
- Unit string `json:"unit,omitempty"`
- Series json.RawMessage `json:"series"`
+ Project string `json:"project"`
+ Unit string `json:"unit,omitempty"`
+ Series jsontext.Value `json:"series"`
}
type channelSnap struct {
CodeAt time.Time `json:"codeAt"`
Sensitive map[string]string `json:"sensitive,omitempty"`
Code string `json:"code,omitempty"`
- Channel json.RawMessage `json:"channel"`
+ Channel jsontext.Value `json:"channel"`
}
type uptimeSnap struct {
- LastRun time.Time `json:"lastRun"`
- Config json.RawMessage `json:"config"`
+ LastRun time.Time `json:"lastRun"`
+ Config jsontext.Value `json:"config"`
}
type backendSnapshot struct {
- Descriptors map[string]json.RawMessage `json:"descriptors"`
- Policies map[string]json.RawMessage `json:"policies"`
- Groups map[string]json.RawMessage `json:"groups"`
- Snoozes map[string]json.RawMessage `json:"snoozes,omitempty"`
- Services map[string]json.RawMessage `json:"services,omitempty"`
- SLOs map[string]json.RawMessage `json:"slos,omitempty"`
- Channels map[string]channelSnap `json:"channels"`
- Uptime map[string]uptimeSnap `json:"uptime"`
- Series []seriesSnap `json:"series"`
- Incidents []Incident `json:"incidents"`
- Version int `json:"version"`
+ Descriptors map[string]jsontext.Value `json:"descriptors"`
+ Policies map[string]jsontext.Value `json:"policies"`
+ Groups map[string]jsontext.Value `json:"groups"`
+ Snoozes map[string]jsontext.Value `json:"snoozes,omitempty"`
+ Services map[string]jsontext.Value `json:"services,omitempty"`
+ SLOs map[string]jsontext.Value `json:"slos,omitempty"`
+ Channels map[string]channelSnap `json:"channels"`
+ Uptime map[string]uptimeSnap `json:"uptime"`
+ Series []seriesSnap `json:"series"`
+ Incidents []Incident `json:"incidents"`
+ Version int `json:"version"`
}
-func marshalPB(m proto.Message) json.RawMessage {
+func marshalPB(m proto.Message) jsontext.Value {
out, err := protojson.Marshal(m)
if err != nil {
return nil
@@ -59,12 +59,12 @@ func marshalPB(m proto.Message) json.RawMessage {
return out
}
-func unmarshalPB(raw json.RawMessage, m proto.Message) error {
+func unmarshalPB(raw jsontext.Value, m proto.Message) error {
return protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(raw, m)
}
-func snapMap[T proto.Message](in map[string]T) map[string]json.RawMessage {
- out := make(map[string]json.RawMessage, len(in))
+func snapMap[T proto.Message](in map[string]T) map[string]jsontext.Value {
+ out := make(map[string]jsontext.Value, len(in))
for k, v := range in {
out[k] = marshalPB(v)
}
@@ -72,7 +72,7 @@ func snapMap[T proto.Message](in map[string]T) map[string]json.RawMessage {
return out
}
-func restoreMap[T proto.Message](raw map[string]json.RawMessage, newT func() T) (map[string]T, error) {
+func restoreMap[T proto.Message](raw map[string]jsontext.Value, newT func() T) (map[string]T, error) {
out := make(map[string]T, len(raw))
for k, r := range raw {
diff --git a/services/gcpmonitoring/promql_http.go b/services/gcpmonitoring/promql_http.go
index 4923a48982..dba68777cd 100644
--- a/services/gcpmonitoring/promql_http.go
+++ b/services/gcpmonitoring/promql_http.go
@@ -1,7 +1,8 @@
package gcpmonitoring
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -14,6 +15,7 @@ import (
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/amp/promql"
)
@@ -69,7 +71,7 @@ type promResponse struct {
func writePromJSON(w http.ResponseWriter, code int, body promResponse) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
- _ = json.NewEncoder(w).Encode(body)
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), body)
}
func promError(w http.ResponseWriter, code int, errType, msg string) {
diff --git a/services/gcpmonitoring/promql_test.go b/services/gcpmonitoring/promql_test.go
index 89b6e5c8dc..296f9dabdb 100644
--- a/services/gcpmonitoring/promql_test.go
+++ b/services/gcpmonitoring/promql_test.go
@@ -1,7 +1,7 @@
package gcpmonitoring_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/url"
@@ -18,6 +18,7 @@ import (
"google.golang.org/genproto/googleapis/api/monitoredres"
"google.golang.org/protobuf/types/known/timestamppb"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -67,7 +68,7 @@ func (e *env) promQuery(t *testing.T, q string) promReply {
require.Equal(t, http.StatusOK, code, string(body))
var r promReply
- require.NoError(t, json.Unmarshal(body, &r))
+ require.NoError(t, json.Unmarshal(body, &r, jsonx.V1()))
return r
}
@@ -165,7 +166,7 @@ func TestPromQLRangeQuery(t *testing.T) {
require.Equal(t, http.StatusOK, code, string(body))
var r promReply
- require.NoError(t, json.Unmarshal(body, &r))
+ require.NoError(t, json.Unmarshal(body, &r, jsonx.V1()))
require.Equal(t, "matrix", r.Data.ResultType)
require.Len(t, r.Data.Result, 1)
@@ -294,7 +295,7 @@ func TestPromQLProjectIsolation(t *testing.T) {
require.Equal(t, http.StatusOK, code)
var r promReply
- require.NoError(t, json.Unmarshal(body, &r))
+ require.NoError(t, json.Unmarshal(body, &r, jsonx.V1()))
assert.Empty(t, r.Data.Result)
}
@@ -363,7 +364,7 @@ func TestPromQLErrors(t *testing.T) {
assert.Equal(t, tt.status, code, string(body))
var r promReply
- require.NoError(t, json.Unmarshal(body, &r))
+ require.NoError(t, json.Unmarshal(body, &r, jsonx.V1()))
assert.Equal(t, "error", r.Status)
assert.Equal(t, tt.errType, r.ErrorType)
})
diff --git a/services/gcppubsub/avro.go b/services/gcppubsub/avro.go
index a45e90ab47..dfd8b71890 100644
--- a/services/gcppubsub/avro.go
+++ b/services/gcppubsub/avro.go
@@ -2,13 +2,15 @@ package gcppubsub
import (
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"math"
"slices"
"strings"
"unicode/utf8"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errAvro = errors.New("avro")
@@ -71,7 +73,7 @@ func primitiveKind(s string) (avroKind, bool) {
func parseAvroSchema(def string) (*avroType, error) {
var raw any
- if err := json.Unmarshal([]byte(def), &raw); err != nil {
+ if err := json.Unmarshal([]byte(def), &raw, jsonx.V1()); err != nil {
return nil, avroErr("schema is not valid JSON: %v", err)
}
diff --git a/services/gcppubsub/persistence.go b/services/gcppubsub/persistence.go
index 01bd7669ca..702eb7c9a3 100644
--- a/services/gcppubsub/persistence.go
+++ b/services/gcppubsub/persistence.go
@@ -2,7 +2,7 @@ package gcppubsub
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
"time"
@@ -22,34 +22,34 @@ var errNullMessage = errors.New("gcppubsub: snapshot has a null message")
const snapshotVersion = 1
type topicSnap struct {
- Topic json.RawMessage `json:"topic"`
- Retained []*message `json:"retained,omitempty"`
+ Topic jsontext.Value `json:"topic"`
+ Retained []*message `json:"retained,omitempty"`
}
type subSnap struct {
- LastActive time.Time `json:"lastActive"`
- Sub json.RawMessage `json:"sub"`
- Msgs []*subMsg `json:"msgs,omitempty"`
+ LastActive time.Time `json:"lastActive"`
+ Sub jsontext.Value `json:"sub"`
+ Msgs []*subMsg `json:"msgs,omitempty"`
}
type pbSnapSnap struct {
- Created time.Time `json:"created"`
- Snapshot json.RawMessage `json:"snapshot"`
- Unacked []string `json:"unacked,omitempty"`
+ Created time.Time `json:"created"`
+ Snapshot jsontext.Value `json:"snapshot"`
+ Unacked []string `json:"unacked,omitempty"`
}
type backendSnapshot struct {
- Topics map[string]topicSnap `json:"topics"`
- Subs map[string]subSnap `json:"subs"`
- Snapshots map[string]pbSnapSnap `json:"snapshots"`
- Schemas map[string]*schemaRec `json:"schemas"`
- Policies map[string]json.RawMessage `json:"policies,omitempty"`
- LastMsg int64 `json:"lastMsg"`
- EtagSeq uint64 `json:"etagSeq,omitempty"`
- Version int `json:"version"`
+ Topics map[string]topicSnap `json:"topics"`
+ Subs map[string]subSnap `json:"subs"`
+ Snapshots map[string]pbSnapSnap `json:"snapshots"`
+ Schemas map[string]*schemaRec `json:"schemas"`
+ Policies map[string]jsontext.Value `json:"policies,omitempty"`
+ LastMsg int64 `json:"lastMsg"`
+ EtagSeq uint64 `json:"etagSeq,omitempty"`
+ Version int `json:"version"`
}
-func marshalPB(m proto.Message) json.RawMessage {
+func marshalPB(m proto.Message) jsontext.Value {
out, err := protojson.Marshal(m)
if err != nil {
return nil
@@ -58,7 +58,7 @@ func marshalPB(m proto.Message) json.RawMessage {
return out
}
-func unmarshalPB(raw json.RawMessage, m proto.Message) error {
+func unmarshalPB(raw jsontext.Value, m proto.Message) error {
return protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(raw, m)
}
@@ -73,7 +73,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Version: snapshotVersion,
LastMsg: b.lastMsg,
EtagSeq: iamSeq,
- Policies: make(map[string]json.RawMessage, len(iamPolicies)),
+ Policies: make(map[string]jsontext.Value, len(iamPolicies)),
Schemas: b.schemas,
Topics: make(map[string]topicSnap, len(b.topics)),
Subs: make(map[string]subSnap, len(b.subs)),
@@ -153,7 +153,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
return nil
}
-func decodePolicies(raw map[string]json.RawMessage) (map[string]*iampb.Policy, error) {
+func decodePolicies(raw map[string]jsontext.Value) (map[string]*iampb.Policy, error) {
out := make(map[string]*iampb.Policy, len(raw))
for n, r := range raw {
diff --git a/services/gcppubsub/push.go b/services/gcppubsub/push.go
index e5fb82157e..2a9e6c23ee 100644
--- a/services/gcppubsub/push.go
+++ b/services/gcppubsub/push.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"sync"
"time"
@@ -12,6 +12,7 @@ import (
"cloud.google.com/go/pubsub/v2/apiv1/pubsubpb"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -196,7 +197,7 @@ func (b *InMemoryBackend) sendPush(
}
var err error
- if body, err = json.Marshal(env); err != nil {
+ if body, err = json.Marshal(env, jsonx.V1()); err != nil {
return false
}
diff --git a/services/gcppubsub/schema.go b/services/gcppubsub/schema.go
index b28c9953d8..a7d4dad0bc 100644
--- a/services/gcppubsub/schema.go
+++ b/services/gcppubsub/schema.go
@@ -2,7 +2,8 @@ package gcppubsub
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"slices"
"strings"
"time"
@@ -12,6 +13,7 @@ import (
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -110,9 +112,10 @@ func validateData(typ pubsubpb.Schema_Type, def string, enc pubsubpb.Encoding, d
}
var v any
- dec := json.NewDecoder(bytes.NewReader(data))
+ dec := jsontext.NewDecoder(bytes.NewReader(data), jsonx.V1())
- return dec.Decode(&v) == nil && !dec.More() && t.validJSON(v)
+ return json.UnmarshalDecode(dec, &v, jsonx.V1()) == nil && !moreJSON(data, dec.InputOffset()) &&
+ t.validJSON(v)
case pubsubpb.Schema_PROTOCOL_BUFFER:
if enc == pubsubpb.Encoding_BINARY {
return validWire(data)
@@ -120,7 +123,7 @@ func validateData(typ pubsubpb.Schema_Type, def string, enc pubsubpb.Encoding, d
var v map[string]any
- return json.Unmarshal(data, &v) == nil
+ return json.Unmarshal(data, &v, jsonx.V1()) == nil
case pubsubpb.Schema_TYPE_UNSPECIFIED:
return false
default:
@@ -480,3 +483,11 @@ func (b *InMemoryBackend) ValidateMessage(req *pubsubpb.ValidateMessageRequest)
return nil
}
+
+// moreJSON mirrors encoding/json Decoder.More: data remains after offset and
+// does not start with a closing bracket.
+func moreJSON(data []byte, offset int64) bool {
+ rest := bytes.TrimLeft(data[offset:], " \t\r\n")
+
+ return len(rest) > 0 && rest[0] != ']' && rest[0] != '}'
+}
diff --git a/services/gcppubsub/sdk_test.go b/services/gcppubsub/sdk_test.go
index 5e9090b772..fd44b1bde1 100644
--- a/services/gcppubsub/sdk_test.go
+++ b/services/gcppubsub/sdk_test.go
@@ -3,7 +3,8 @@ package gcppubsub_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"sync"
@@ -24,6 +25,7 @@ import (
"google.golang.org/protobuf/types/known/fieldmaskpb"
"google.golang.org/protobuf/types/known/timestamppb"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/gcppubsub"
)
@@ -840,7 +842,7 @@ func TestSDKPushSubscription(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var env envelope
- _ = json.NewDecoder(r.Body).Decode(&env)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(r.Body), &env, jsonx.V1())
mu.Lock()
fail := first
diff --git a/services/gcpredis/persistence.go b/services/gcpredis/persistence.go
index 1a4410e3ed..03cc7db5da 100644
--- a/services/gcpredis/persistence.go
+++ b/services/gcpredis/persistence.go
@@ -2,7 +2,7 @@ package gcpredis
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"cloud.google.com/go/redis/apiv1/redispb"
@@ -18,15 +18,15 @@ import (
const snapshotVersion = 1
type instSnap struct {
- Instance json.RawMessage `json:"instance"`
- AuthString string `json:"authString,omitempty"`
- Keyspace []byte `json:"keyspace,omitempty"`
+ Instance jsontext.Value `json:"instance"`
+ AuthString string `json:"authString,omitempty"`
+ Keyspace []byte `json:"keyspace,omitempty"`
}
type backendSnapshot struct {
- Instances map[string]instSnap `json:"instances"`
- Operations map[string]json.RawMessage `json:"operations"`
- Version int `json:"version"`
+ Instances map[string]instSnap `json:"instances"`
+ Operations map[string]jsontext.Value `json:"operations"`
+ Version int `json:"version"`
}
// Snapshot serialises every instance with its keyspace; Redis servers and TLS CAs are rebuilt on restore.
diff --git a/services/gcpredis/rest_test.go b/services/gcpredis/rest_test.go
index 96f602d482..cde3957b77 100644
--- a/services/gcpredis/rest_test.go
+++ b/services/gcpredis/rest_test.go
@@ -1,7 +1,7 @@
package gcpredis_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/gcprest/resttest"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -69,7 +70,7 @@ func TestRESTFlow(t *testing.T) {
Auth bool `json:"authEnabled"`
}
- return json.Unmarshal(body, &inst) == nil && inst.State == "READY" && inst.Host != "" &&
+ return json.Unmarshal(body, &inst, jsonx.V1()) == nil && inst.State == "READY" && inst.Host != "" &&
assert.True(t, inst.Auth)
}, 10*time.Second, 5*time.Millisecond)
}
diff --git a/services/gcpresourcemanager/helpers_test.go b/services/gcpresourcemanager/helpers_test.go
index 02982e6659..f9138a8792 100644
--- a/services/gcpresourcemanager/helpers_test.go
+++ b/services/gcpresourcemanager/helpers_test.go
@@ -2,7 +2,7 @@ package gcpresourcemanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"sync/atomic"
"testing"
@@ -19,6 +19,7 @@ import (
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/status"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/gcpresourcemanager"
)
@@ -200,7 +201,8 @@ func codeOf(err error) codes.Code {
var c codes.Code
- if json.Unmarshal([]byte(ge.Body), &body) == nil && c.UnmarshalJSON([]byte(`"`+body.Error.Status+`"`)) == nil {
+ if json.Unmarshal([]byte(ge.Body), &body, jsonx.V1()) == nil &&
+ c.UnmarshalJSON([]byte(`"`+body.Error.Status+`"`)) == nil {
return c
}
}
diff --git a/services/gcpresourcemanager/persistence.go b/services/gcpresourcemanager/persistence.go
index 5d4455e118..8d71f07a1d 100644
--- a/services/gcpresourcemanager/persistence.go
+++ b/services/gcpresourcemanager/persistence.go
@@ -2,7 +2,7 @@ package gcpresourcemanager
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -23,8 +23,8 @@ type backendSnapshot struct {
TagValues map[string]*tagValueRec `json:"tagValues"`
Bindings map[string]*bindingRec `json:"tagBindings"`
Enabled map[string]map[string]time.Time `json:"enabledServices"`
- Policies map[string]json.RawMessage `json:"policies"`
- Ops map[string]json.RawMessage `json:"operations"`
+ Policies map[string]jsontext.Value `json:"policies"`
+ Ops map[string]jsontext.Value `json:"operations"`
Seed string `json:"seed"`
Version int `json:"version"`
PolicySeq uint64 `json:"policySeq"`
@@ -39,7 +39,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
snap := backendSnapshot{
Version: snapshotVersion, Seed: b.seed, Projects: b.projects, Folders: b.folders, TagKeys: b.tagKeys,
TagValues: b.tagValues, Bindings: b.bindings, Enabled: b.enabled, PolicySeq: seq,
- Policies: make(map[string]json.RawMessage, len(policies)), Ops: b.lro.Snapshot(),
+ Policies: make(map[string]jsontext.Value, len(policies)), Ops: b.lro.Snapshot(),
}
for n, p := range policies {
diff --git a/services/gcpresourcemanager/v1.go b/services/gcpresourcemanager/v1.go
index b55c7012f6..05efa055c2 100644
--- a/services/gcpresourcemanager/v1.go
+++ b/services/gcpresourcemanager/v1.go
@@ -1,7 +1,8 @@
package gcpresourcemanager
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"regexp"
@@ -16,6 +17,7 @@ import (
"google.golang.org/protobuf/proto"
"github.com/blackbirdworks/gopherstack/pkgs/gcprest"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -102,7 +104,7 @@ func writeV1Error(w http.ResponseWriter, err error) {
st, _ := status.FromError(err)
httpCode := gcprest.HTTPStatus(st.Code())
- details := make([]json.RawMessage, 0, len(st.Proto().GetDetails()))
+ details := make([]jsontext.Value, 0, len(st.Proto().GetDetails()))
for _, d := range st.Proto().GetDetails() {
if raw, mErr := protojson.Marshal(d); mErr == nil {
@@ -140,7 +142,7 @@ func codeName(c codes.Code) string {
}
func writeV1JSON(w http.ResponseWriter, code int, v any) {
- raw, err := json.Marshal(v)
+ raw, err := json.Marshal(v, jsonx.V1())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -153,7 +155,7 @@ func writeV1JSON(w http.ResponseWriter, code int, v any) {
func writeV1Raw(w http.ResponseWriter, code int, raw []byte) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(code)
- _, _ = w.Write(raw)
+ _, _ = w.Write(raw) // #nosec G705
}
func writeV1Proto(w http.ResponseWriter, m proto.Message) {
@@ -177,7 +179,7 @@ func readBody(r *http.Request, into any) error {
return nil
}
- if err = json.Unmarshal(raw, into); err != nil {
+ if err = json.Unmarshal(raw, into, jsonx.V1()); err != nil {
return errInvalid("Invalid JSON payload: %v", err)
}
diff --git a/services/gcprun/persistence.go b/services/gcprun/persistence.go
index b1a1db2f8a..5f554717b2 100644
--- a/services/gcprun/persistence.go
+++ b/services/gcprun/persistence.go
@@ -2,7 +2,7 @@ package gcprun
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"cloud.google.com/go/iam/apiv1/iampb"
@@ -20,38 +20,38 @@ import (
const snapshotVersion = 1
type svcSnap struct {
- Failure string `json:"failure"`
- Service json.RawMessage `json:"service"`
- RevSeq int `json:"revSeq"`
+ Failure string `json:"failure"`
+ Service jsontext.Value `json:"service"`
+ RevSeq int `json:"revSeq"`
}
type revSnap struct {
- StartErr string `json:"startErr"`
- Revision json.RawMessage `json:"revision"`
- Seq int `json:"seq"`
+ StartErr string `json:"startErr"`
+ Revision jsontext.Value `json:"revision"`
+ Seq int `json:"seq"`
}
type taskSnap struct {
- State string `json:"state"`
- Task json.RawMessage `json:"task"`
+ State string `json:"state"`
+ Task jsontext.Value `json:"task"`
}
type backendSnapshot struct {
- Services map[string]svcSnap `json:"services"`
- Revisions map[string]revSnap `json:"revisions"`
- Jobs map[string]json.RawMessage `json:"jobs"`
- Executions map[string]json.RawMessage `json:"executions"`
- Tasks map[string]taskSnap `json:"tasks"`
- Operations map[string]json.RawMessage `json:"operations"`
- DeletedSvc map[string]json.RawMessage `json:"deletedServices"`
- DeletedJob map[string]json.RawMessage `json:"deletedJobs"`
- DeletedExe map[string]json.RawMessage `json:"deletedExecutions"`
- IAM map[string]json.RawMessage `json:"iamPolicies"`
- IAMSeq uint64 `json:"iamSeq"`
- Version int `json:"version"`
+ Services map[string]svcSnap `json:"services"`
+ Revisions map[string]revSnap `json:"revisions"`
+ Jobs map[string]jsontext.Value `json:"jobs"`
+ Executions map[string]jsontext.Value `json:"executions"`
+ Tasks map[string]taskSnap `json:"tasks"`
+ Operations map[string]jsontext.Value `json:"operations"`
+ DeletedSvc map[string]jsontext.Value `json:"deletedServices"`
+ DeletedJob map[string]jsontext.Value `json:"deletedJobs"`
+ DeletedExe map[string]jsontext.Value `json:"deletedExecutions"`
+ IAM map[string]jsontext.Value `json:"iamPolicies"`
+ IAMSeq uint64 `json:"iamSeq"`
+ Version int `json:"version"`
}
-func marshalPB(m proto.Message) json.RawMessage {
+func marshalPB(m proto.Message) jsontext.Value {
raw, err := protojson.Marshal(m)
if err != nil {
return nil
@@ -60,8 +60,8 @@ func marshalPB(m proto.Message) json.RawMessage {
return raw
}
-func marshalMap[T proto.Message](m map[string]T) map[string]json.RawMessage {
- out := make(map[string]json.RawMessage, len(m))
+func marshalMap[T proto.Message](m map[string]T) map[string]jsontext.Value {
+ out := make(map[string]jsontext.Value, len(m))
for k, v := range m {
out[k] = marshalPB(v)
}
@@ -69,7 +69,7 @@ func marshalMap[T proto.Message](m map[string]T) map[string]json.RawMessage {
return out
}
-func unmarshalMap[T proto.Message](raw map[string]json.RawMessage, mk func() T, what string) (map[string]T, error) {
+func unmarshalMap[T proto.Message](raw map[string]jsontext.Value, mk func() T, what string) (map[string]T, error) {
out := make(map[string]T, len(raw))
for k, r := range raw {
@@ -84,7 +84,7 @@ func unmarshalMap[T proto.Message](raw map[string]json.RawMessage, mk func() T,
return out, nil
}
-func unmarshalPB[T proto.Message](raw json.RawMessage, m T) (T, error) {
+func unmarshalPB[T proto.Message](raw jsontext.Value, m T) (T, error) {
err := (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal(raw, m)
return m, err
@@ -99,8 +99,8 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Version: snapshotVersion,
Services: make(map[string]svcSnap, len(b.services)),
Revisions: make(map[string]revSnap, len(b.revisions)),
- Jobs: make(map[string]json.RawMessage, len(b.jobs)),
- Executions: make(map[string]json.RawMessage, len(b.executions)),
+ Jobs: make(map[string]jsontext.Value, len(b.jobs)),
+ Executions: make(map[string]jsontext.Value, len(b.executions)),
Tasks: make(map[string]taskSnap, len(b.tasks)),
Operations: b.ops.Snapshot(),
}
diff --git a/services/gcpscheduler/helpers_test.go b/services/gcpscheduler/helpers_test.go
index 239ab77283..2c6e9a7c50 100644
--- a/services/gcpscheduler/helpers_test.go
+++ b/services/gcpscheduler/helpers_test.go
@@ -3,7 +3,7 @@ package gcpscheduler_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -21,6 +21,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/gcpscheduler"
)
@@ -165,7 +166,7 @@ func decodeJWT(t *testing.T, token string) map[string]any {
require.NoError(t, err)
var claims map[string]any
- require.NoError(t, json.Unmarshal(raw, &claims))
+ require.NoError(t, json.Unmarshal(raw, &claims, jsonx.V1()))
return claims
}
diff --git a/services/gcpscheduler/persistence.go b/services/gcpscheduler/persistence.go
index 46a777a29c..25fff0d21a 100644
--- a/services/gcpscheduler/persistence.go
+++ b/services/gcpscheduler/persistence.go
@@ -2,7 +2,7 @@ package gcpscheduler
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -17,10 +17,10 @@ import (
const snapshotVersion = 1
type jobSnap struct {
- RetryAt time.Time `json:"retryAt"`
- ExecStart time.Time `json:"execStart"`
- Job json.RawMessage `json:"job"`
- Failures int32 `json:"failures"`
+ RetryAt time.Time `json:"retryAt"`
+ ExecStart time.Time `json:"execStart"`
+ Job jsontext.Value `json:"job"`
+ Failures int32 `json:"failures"`
}
type backendSnapshot struct {
diff --git a/services/gcpsecretmanager/persistence.go b/services/gcpsecretmanager/persistence.go
index ad3b5fc1ff..8da62144cb 100644
--- a/services/gcpsecretmanager/persistence.go
+++ b/services/gcpsecretmanager/persistence.go
@@ -2,7 +2,7 @@ package gcpsecretmanager
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"cloud.google.com/go/iam/apiv1/iampb"
@@ -18,16 +18,16 @@ import (
const snapshotVersion = 1
type versionSnap struct {
- Version json.RawMessage `json:"version"`
- Data []byte `json:"data,omitempty"`
- Num int64 `json:"num"`
+ Version jsontext.Value `json:"version"`
+ Data []byte `json:"data,omitempty"`
+ Num int64 `json:"num"`
}
type secretSnap struct {
- Secret json.RawMessage `json:"secret"`
- Policy json.RawMessage `json:"policy,omitempty"`
- Versions []versionSnap `json:"versions,omitempty"`
- NextVer int64 `json:"nextVer"`
+ Secret jsontext.Value `json:"secret"`
+ Policy jsontext.Value `json:"policy,omitempty"`
+ Versions []versionSnap `json:"versions,omitempty"`
+ NextVer int64 `json:"nextVer"`
}
type backendSnapshot struct {
@@ -36,7 +36,7 @@ type backendSnapshot struct {
Version int `json:"version"`
}
-func marshalPB(m proto.Message) json.RawMessage {
+func marshalPB(m proto.Message) jsontext.Value {
out, err := protojson.Marshal(m)
if err != nil {
return nil
@@ -45,7 +45,7 @@ func marshalPB(m proto.Message) json.RawMessage {
return out
}
-func unmarshalPB(raw json.RawMessage, m proto.Message) error {
+func unmarshalPB(raw jsontext.Value, m proto.Message) error {
return protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(raw, m)
}
diff --git a/services/gcpspanner/persistence.go b/services/gcpspanner/persistence.go
index 1932704bba..54e7645c85 100644
--- a/services/gcpspanner/persistence.go
+++ b/services/gcpspanner/persistence.go
@@ -2,7 +2,7 @@ package gcpspanner
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"cloud.google.com/go/iam/apiv1/iampb"
@@ -20,21 +20,21 @@ import (
const snapshotVersion = 1
type dbSnap struct {
- Database json.RawMessage `json:"database"`
- DDL []string `json:"ddl,omitempty"`
+ Database jsontext.Value `json:"database"`
+ DDL []string `json:"ddl,omitempty"`
}
type instSnap struct {
Databases map[string]dbSnap `json:"databases,omitempty"`
- Instance json.RawMessage `json:"instance"`
- Policy json.RawMessage `json:"policy,omitempty"`
+ Instance jsontext.Value `json:"instance"`
+ Policy jsontext.Value `json:"policy,omitempty"`
}
type backendSnapshot struct {
- Instances map[string]instSnap `json:"instances"`
- Operations map[string]json.RawMessage `json:"operations"`
- Version int `json:"version"`
- EtagSeq uint64 `json:"etagSeq"`
+ Instances map[string]instSnap `json:"instances"`
+ Operations map[string]jsontext.Value `json:"operations"`
+ Version int `json:"version"`
+ EtagSeq uint64 `json:"etagSeq"`
}
// Snapshot serialises instances, policies, databases and their DDL. Table data lives in the emulator and is not
diff --git a/services/gcpspanner/rest_test.go b/services/gcpspanner/rest_test.go
index fac931f18d..882b835182 100644
--- a/services/gcpspanner/rest_test.go
+++ b/services/gcpspanner/rest_test.go
@@ -1,7 +1,7 @@
package gcpspanner_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -32,7 +33,7 @@ func restDo(t *testing.T, base, method, path, body string) (int, map[string]any)
out := map[string]any{}
if len(raw) > 0 {
- require.NoError(t, json.Unmarshal(raw, &out), string(raw))
+ require.NoError(t, json.Unmarshal(raw, &out, jsonx.V1()), string(raw))
}
return resp.StatusCode, out
diff --git a/services/gcpsql/backups.go b/services/gcpsql/backups.go
index 9f3a93e350..c41d603633 100644
--- a/services/gcpsql/backups.go
+++ b/services/gcpsql/backups.go
@@ -2,17 +2,18 @@ package gcpsql
import (
"context"
- "encoding/json"
"slices"
"sort"
"strconv"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// RestoreBackupInput is the restoreBackupContext of instances.restoreBackup.
type RestoreBackupInput struct {
- BackupRunID json.Number `json:"backupRunId"`
- InstanceID string `json:"instanceId"`
- Project string `json:"project"`
+ BackupRunID jsonx.Number `json:"backupRunId"`
+ InstanceID string `json:"instanceId"`
+ Project string `json:"project"`
}
func (b *InMemoryBackend) nextBackupID() string {
diff --git a/services/gcpsql/handler.go b/services/gcpsql/handler.go
index 2c2d6f5983..a794dc6986 100644
--- a/services/gcpsql/handler.go
+++ b/services/gcpsql/handler.go
@@ -2,7 +2,8 @@ package gcpsql
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -14,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/gcpenforce"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -160,7 +162,7 @@ func decode[T any](c *echo.Context) (T, error) {
return v, nil
}
- if err = json.Unmarshal(data, &v); err != nil {
+ if err = json.Unmarshal(data, &v, jsonx.V1()); err != nil {
return v, errInvalid("Invalid JSON payload received: %v", err)
}
@@ -171,7 +173,7 @@ func decode[T any](c *echo.Context) (T, error) {
func decodeContext[T any](c *echo.Context, key string) (T, map[string]any, error) {
var typed T
- wrapper, err := decode[map[string]json.RawMessage](c)
+ wrapper, err := decode[map[string]jsontext.Value](c)
if err != nil {
return typed, nil, err
}
@@ -181,13 +183,13 @@ func decodeContext[T any](c *echo.Context, key string) (T, map[string]any, error
return typed, nil, errInvalid("Missing parameter: %s.", key)
}
- if err = json.Unmarshal(raw, &typed); err != nil {
+ if err = json.Unmarshal(raw, &typed, jsonx.V1()); err != nil {
return typed, nil, errInvalid("Invalid %s: %v", key, err)
}
var echoed map[string]any
- _ = json.Unmarshal(raw, &echoed)
+ _ = json.Unmarshal(raw, &echoed, jsonx.V1())
return typed, echoed, nil
}
diff --git a/services/gcpsql/settingsdef.go b/services/gcpsql/settingsdef.go
index 4c41d0337e..f0f4047144 100644
--- a/services/gcpsql/settingsdef.go
+++ b/services/gcpsql/settingsdef.go
@@ -3,8 +3,10 @@ package gcpsql
import (
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const retainDays = 7
@@ -133,7 +135,7 @@ func flagsSig(f []Flag) string {
}
func etagOf(v any) string {
- data, _ := json.Marshal(v)
+ data, _ := json.Marshal(v, jsonx.V1())
sum := sha256.Sum256(data)
return base64.RawURLEncoding.EncodeToString(sum[:16])
diff --git a/services/gcpstorage/acl.go b/services/gcpstorage/acl.go
index 906c352014..80e3594ae1 100644
--- a/services/gcpstorage/acl.go
+++ b/services/gcpstorage/acl.go
@@ -1,11 +1,13 @@
package gcpstorage
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ACLEntry is one access-control entry; the derived fields (email, domain, projectTeam) are rendered from Entity.
@@ -239,7 +241,7 @@ func (b *Bucket) ubla() bool {
}
raw, ok := b.Config["iamConfiguration"]
- if !ok || json.Unmarshal(raw, &cfg) != nil {
+ if !ok || json.Unmarshal(raw, &cfg, jsonx.V1()) != nil {
return false
}
diff --git a/services/gcpstorage/events.go b/services/gcpstorage/events.go
index 5e4892699a..a51cf868a7 100644
--- a/services/gcpstorage/events.go
+++ b/services/gcpstorage/events.go
@@ -1,8 +1,11 @@
package gcpstorage
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ObjectEventType names a change to an object, matching the suffix of the google.cloud.storage.object.v1 event types.
@@ -27,8 +30,8 @@ type ObjectEvent struct {
}
// Data is the event payload: the object resource, as the Cloud Storage JSON API renders it.
-func (e ObjectEvent) Data() json.RawMessage {
- raw, err := json.Marshal(objectResource("", e.Object))
+func (e ObjectEvent) Data() jsontext.Value {
+ raw, err := json.Marshal(objectResource("", e.Object), jsonx.V1())
if err != nil {
return nil
}
diff --git a/services/gcpstorage/events_test.go b/services/gcpstorage/events_test.go
index 2bf146afdd..01b8456be6 100644
--- a/services/gcpstorage/events_test.go
+++ b/services/gcpstorage/events_test.go
@@ -1,7 +1,7 @@
package gcpstorage_test
import (
- "encoding/json"
+ "encoding/json/v2"
"sync"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/gcpstorage"
)
@@ -146,7 +147,7 @@ func TestObjectEventData(t *testing.T) {
require.Len(t, rec.got, 1)
var data map[string]any
- require.NoError(t, json.Unmarshal(rec.got[0].Data(), &data))
+ require.NoError(t, json.Unmarshal(rec.got[0].Data(), &data, jsonx.V1()))
assert.Equal(t, "ev-data", data["bucket"])
assert.Equal(t, "dir/a.txt", data["name"])
assert.Equal(t, "5", data["size"])
diff --git a/services/gcpstorage/handler_acl.go b/services/gcpstorage/handler_acl.go
index 480d59e4a4..fefeaa9016 100644
--- a/services/gcpstorage/handler_acl.go
+++ b/services/gcpstorage/handler_acl.go
@@ -1,10 +1,12 @@
package gcpstorage
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type aclBody struct {
@@ -96,7 +98,7 @@ func (h *Handler) aclWrite(c *echo.Context, rt route, t aclTarget, base string)
}
var in aclBody
- if jsonErr := json.Unmarshal(body, &in); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &in, jsonx.V1()); jsonErr != nil {
return errInvalid("Invalid JSON payload received.")
}
diff --git a/services/gcpstorage/handler_bucket.go b/services/gcpstorage/handler_bucket.go
index 6ebae2633f..1b2afd6421 100644
--- a/services/gcpstorage/handler_bucket.go
+++ b/services/gcpstorage/handler_bucket.go
@@ -1,13 +1,14 @@
package gcpstorage
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/gcpiampolicy"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func errRequired(param string) *APIError {
@@ -121,7 +122,7 @@ func (h *Handler) setIamPolicy(c *echo.Context, rt route) error {
}
var in IAMPolicy
- if jsonErr := json.Unmarshal(body, &in); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &in, jsonx.V1()); jsonErr != nil {
return errInvalid("Invalid JSON payload received.")
}
diff --git a/services/gcpstorage/handler_object.go b/services/gcpstorage/handler_object.go
index cf7d82a198..a0273ae1f6 100644
--- a/services/gcpstorage/handler_object.go
+++ b/services/gcpstorage/handler_object.go
@@ -1,12 +1,14 @@
package gcpstorage
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"strconv"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type objectsJSON struct {
@@ -229,7 +231,7 @@ func (h *Handler) composeObject(c *echo.Context, rt route) error {
}
var req composeRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return errInvalid("Invalid JSON payload received.")
}
diff --git a/services/gcpstorage/handler_test.go b/services/gcpstorage/handler_test.go
index 169d835165..64217a38e8 100644
--- a/services/gcpstorage/handler_test.go
+++ b/services/gcpstorage/handler_test.go
@@ -2,7 +2,7 @@ package gcpstorage_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/gcpstorage"
@@ -129,7 +130,7 @@ func TestErrorEnvelope(t *testing.T) {
} `json:"error"`
}
- require.NoError(t, json.Unmarshal([]byte(body), &env), body)
+ require.NoError(t, json.Unmarshal([]byte(body), &env, jsonx.V1()), body)
assert.Equal(t, tt.status, env.Error.Code)
assert.NotEmpty(t, env.Error.Message)
require.Len(t, env.Error.Errors, 1)
@@ -200,7 +201,7 @@ func TestUploadsAndDownloads(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var obj map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &obj))
+ require.NoError(t, json.Unmarshal([]byte(body), &obj, jsonx.V1()))
assert.Equal(t, "a/b.bin", obj["name"])
assert.Equal(t, "6", obj["size"])
assert.Equal(t, "application/x-bin", obj["contentType"])
@@ -297,7 +298,7 @@ func TestRewriteAndPatch(t *testing.T) {
Done bool `json:"done"`
}
- require.NoError(t, json.Unmarshal([]byte(body), &rw))
+ require.NoError(t, json.Unmarshal([]byte(body), &rw, jsonx.V1()))
assert.Equal(t, "storage#rewriteResponse", rw.Kind)
assert.True(t, rw.Done)
assert.Equal(t, "4", rw.Total)
@@ -318,7 +319,7 @@ func TestRewriteAndPatch(t *testing.T) {
Metageneration string `json:"metageneration"`
}
- require.NoError(t, json.Unmarshal([]byte(body), &obj))
+ require.NoError(t, json.Unmarshal([]byte(body), &obj, jsonx.V1()))
assert.Equal(t, map[string]string{"b": "2"}, obj.Metadata)
assert.Empty(t, obj.ContentLang)
assert.Equal(t, "3", obj.Metageneration)
diff --git a/services/gcpstorage/iam.go b/services/gcpstorage/iam.go
index 5257307624..07e18c0d14 100644
--- a/services/gcpstorage/iam.go
+++ b/services/gcpstorage/iam.go
@@ -3,12 +3,14 @@ package gcpstorage
import (
"encoding/base64"
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"cloud.google.com/go/iam/apiv1/iampb"
"google.golang.org/genproto/googleapis/type/expr"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type conditionJSON struct {
@@ -36,7 +38,7 @@ func policyToPB(p IAMPolicy) *iampb.Policy {
if len(bd.Condition) > 0 && string(bd.Condition) != "null" {
var c conditionJSON
- _ = json.Unmarshal(bd.Condition, &c)
+ _ = json.Unmarshal(bd.Condition, &c, jsonx.V1())
pb.Condition = &expr.Expr{Expression: c.Expression, Title: c.Title, Description: c.Description}
}
@@ -53,7 +55,10 @@ func policyFromPB(p *iampb.Policy) IAMPolicy {
ib := IAMBinding{Role: bd.GetRole(), Members: bd.GetMembers()}
if c := bd.GetCondition(); c != nil {
- ib.Condition, _ = json.Marshal(conditionJSON{c.GetExpression(), c.GetTitle(), c.GetDescription()})
+ ib.Condition, _ = json.Marshal(
+ conditionJSON{c.GetExpression(), c.GetTitle(), c.GetDescription()},
+ jsonx.V1(),
+ )
}
out.Bindings = append(out.Bindings, ib)
diff --git a/services/gcpstorage/iam_policy_test.go b/services/gcpstorage/iam_policy_test.go
index ea03211981..0724dcf0e7 100644
--- a/services/gcpstorage/iam_policy_test.go
+++ b/services/gcpstorage/iam_policy_test.go
@@ -1,12 +1,14 @@
package gcpstorage_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -72,7 +74,7 @@ func TestBucketIAMConditions(t *testing.T) {
Etag string `json:"etag"`
}
- require.NoError(t, json.Unmarshal([]byte(seeded), &cur))
+ require.NoError(t, json.Unmarshal([]byte(seeded), &cur, jsonx.V1()))
if tt.freshEtag {
body = `{"etag":"` + cur.Etag + `","version":3}`
@@ -87,7 +89,7 @@ func TestBucketIAMConditions(t *testing.T) {
Version int `json:"version"`
}
- require.NoError(t, json.Unmarshal([]byte(out), &got))
+ require.NoError(t, json.Unmarshal([]byte(out), &got, jsonx.V1()))
assert.Equal(t, tt.wantPolVer, got.Version)
}
})
diff --git a/services/gcpstorage/models.go b/services/gcpstorage/models.go
index af57c686f1..a609e891ed 100644
--- a/services/gcpstorage/models.go
+++ b/services/gcpstorage/models.go
@@ -1,7 +1,7 @@
package gcpstorage
import (
- "encoding/json"
+ "encoding/json/jsontext"
"maps"
"slices"
"time"
@@ -9,9 +9,9 @@ import (
// IAMBinding is one role binding of a bucket IAM policy.
type IAMBinding struct {
- Condition json.RawMessage `json:"condition,omitempty"`
- Role string `json:"role"`
- Members []string `json:"members"`
+ Condition jsontext.Value `json:"condition,omitempty"`
+ Role string `json:"role"`
+ Members []string `json:"members"`
}
// IAMPolicy is a bucket IAM policy.
@@ -70,21 +70,21 @@ func (o *Object) clone() *Object {
// Bucket is a bucket with all of its object versions.
type Bucket struct {
- Created time.Time `json:"created"`
- Updated time.Time `json:"updated"`
- Objects map[string][]*Object `json:"objects"`
- Labels map[string]string `json:"labels,omitempty"`
- Config map[string]json.RawMessage `json:"config,omitempty"`
- Project string `json:"project"`
- Name string `json:"name"`
- Location string `json:"location"`
- StorageClass string `json:"storageClass"`
- ACL []ACLEntry `json:"acl,omitempty"`
- DefaultObjectACL []ACLEntry `json:"defaultObjectAcl,omitempty"`
- IAM IAMPolicy `json:"iam"`
- Metageneration int64 `json:"metageneration"`
- Versioning bool `json:"versioning,omitempty"`
- DefaultEventBasedHold bool `json:"defaultEventBasedHold,omitempty"`
+ Created time.Time `json:"created"`
+ Updated time.Time `json:"updated"`
+ Objects map[string][]*Object `json:"objects"`
+ Labels map[string]string `json:"labels,omitempty"`
+ Config map[string]jsontext.Value `json:"config,omitempty"`
+ Project string `json:"project"`
+ Name string `json:"name"`
+ Location string `json:"location"`
+ StorageClass string `json:"storageClass"`
+ ACL []ACLEntry `json:"acl,omitempty"`
+ DefaultObjectACL []ACLEntry `json:"defaultObjectAcl,omitempty"`
+ IAM IAMPolicy `json:"iam"`
+ Metageneration int64 `json:"metageneration"`
+ Versioning bool `json:"versioning,omitempty"`
+ DefaultEventBasedHold bool `json:"defaultEventBasedHold,omitempty"`
}
// clone copies the bucket metadata without its objects.
diff --git a/services/gcpstorage/patch.go b/services/gcpstorage/patch.go
index 74fd400833..332733f9e8 100644
--- a/services/gcpstorage/patch.go
+++ b/services/gcpstorage/patch.go
@@ -2,13 +2,16 @@ package gcpstorage
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Patch is a decoded JSON request body, keyed by field name. A null value
// clears the field, as in a JSON merge patch.
-type Patch map[string]json.RawMessage
+type Patch map[string]jsontext.Value
const (
keyContentType = "contentType"
@@ -26,16 +29,16 @@ const (
defaultContentType = "application/octet-stream"
)
-func isNull(raw json.RawMessage) bool { return string(bytes.TrimSpace(raw)) == "null" }
+func isNull(raw jsontext.Value) bool { return string(bytes.TrimSpace(raw)) == "null" }
-func decodeField[T any](raw json.RawMessage, field string) (T, *APIError) {
+func decodeField[T any](raw jsontext.Value, field string) (T, *APIError) {
var v T
if isNull(raw) {
return v, nil
}
- if err := json.Unmarshal(raw, &v); err != nil {
+ if err := json.Unmarshal(raw, &v, jsonx.V1()); err != nil {
return v, errInvalid("Invalid value for field '" + field + "'.")
}
@@ -58,7 +61,7 @@ func validStorageClass(sc string) bool {
}
}
-func mergeStringMap(raw json.RawMessage, dst map[string]string, field string) (map[string]string, *APIError) {
+func mergeStringMap(raw jsontext.Value, dst map[string]string, field string) (map[string]string, *APIError) {
if isNull(raw) {
return nil, nil
}
@@ -233,7 +236,7 @@ func applyBucket(b *Bucket, p Patch, create bool) *APIError {
}
if b.Config == nil {
- b.Config = map[string]json.RawMessage{}
+ b.Config = map[string]jsontext.Value{}
}
b.Config[k] = raw
diff --git a/services/gcpstorage/util.go b/services/gcpstorage/util.go
index 9783374d69..4ea8004312 100644
--- a/services/gcpstorage/util.go
+++ b/services/gcpstorage/util.go
@@ -1,15 +1,18 @@
package gcpstorage
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strconv"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func itoa(n int) string { return strconv.Itoa(n) }
-func marshalRaw(v any) json.RawMessage {
- raw, _ := json.Marshal(v) //nolint:errchkjson // strings and string maps always marshal
+func marshalRaw(v any) jsontext.Value {
+ raw, _ := json.Marshal(v, jsonx.V1()) //nolint:errchkjson // strings and string maps always marshal
return raw
}
diff --git a/services/gcpstorage/wire.go b/services/gcpstorage/wire.go
index 7d4e38d4dd..4535cda5a6 100644
--- a/services/gcpstorage/wire.go
+++ b/services/gcpstorage/wire.go
@@ -2,10 +2,12 @@ package gcpstorage
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"strconv"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type objectJSON struct {
@@ -222,7 +224,7 @@ func decodePatch(body []byte) (Patch, *APIError) {
return p, nil
}
- if err := json.Unmarshal(body, &p); err != nil || p == nil {
+ if err := json.Unmarshal(body, &p, jsonx.V1()); err != nil || p == nil {
return nil, errInvalid("Invalid JSON payload received.")
}
diff --git a/services/gcpworkflows/callbacks.go b/services/gcpworkflows/callbacks.go
index 4cd02ace24..d1fedf2525 100644
--- a/services/gcpworkflows/callbacks.go
+++ b/services/gcpworkflows/callbacks.go
@@ -2,7 +2,8 @@ package gcpworkflows
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -10,6 +11,7 @@ import (
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/wfinterp"
)
@@ -74,8 +76,10 @@ func IsCallbackPath(path string) bool {
func writeJSONError(w http.ResponseWriter, code int, status, msg string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
- _ = json.NewEncoder(w).
- Encode(map[string]any{"error": map[string]any{"code": code, "message": msg, "status": status}})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]any{"error": map[string]any{"code": code, "message": msg, "status": status}},
+ )
}
func (b *InMemoryBackend) serveCallback(w http.ResponseWriter, r *http.Request) {
diff --git a/services/gcpworkflows/integration_test.go b/services/gcpworkflows/integration_test.go
index 1754c5440a..3f46d248ca 100644
--- a/services/gcpworkflows/integration_test.go
+++ b/services/gcpworkflows/integration_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/gcprest/resttest"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/gcpfirestore"
"github.com/blackbirdworks/gopherstack/services/gcppubsub"
@@ -468,4 +469,6 @@ func TestRESTFlow(t *testing.T) {
})
}
-func jsonUnmarshal(raw []byte, v any) error { return json.Unmarshal(bytes.TrimSpace(raw), v) }
+func jsonUnmarshal(raw []byte, v any) error {
+ return json.Unmarshal(bytes.TrimSpace(raw), v, jsonx.V1())
+}
diff --git a/services/gcpworkflows/persistence.go b/services/gcpworkflows/persistence.go
index 0a4bba082b..2cf0135f01 100644
--- a/services/gcpworkflows/persistence.go
+++ b/services/gcpworkflows/persistence.go
@@ -2,7 +2,7 @@ package gcpworkflows
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
@@ -23,17 +23,17 @@ var errNoRevisions = errors.New("no revisions")
const snapshotVersion = 1
type workflowSnap struct {
- Revisions []json.RawMessage `json:"revisions"`
+ Revisions []jsontext.Value `json:"revisions"`
}
type backendSnapshot struct {
- Workflows map[string]workflowSnap `json:"workflows"`
- Executions map[string]json.RawMessage `json:"executions"`
- Operations map[string]json.RawMessage `json:"operations"`
- Version int `json:"version"`
+ Workflows map[string]workflowSnap `json:"workflows"`
+ Executions map[string]jsontext.Value `json:"executions"`
+ Operations map[string]jsontext.Value `json:"operations"`
+ Version int `json:"version"`
}
-func marshalPB(m proto.Message) json.RawMessage {
+func marshalPB(m proto.Message) jsontext.Value {
raw, err := protojson.Marshal(m)
if err != nil {
return nil
@@ -54,12 +54,12 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
snap := backendSnapshot{
Version: snapshotVersion,
Workflows: make(map[string]workflowSnap, len(b.workflows)),
- Executions: make(map[string]json.RawMessage, len(b.execs)),
+ Executions: make(map[string]jsontext.Value, len(b.execs)),
Operations: b.ops.Snapshot(),
}
for n, rec := range b.workflows {
- ws := workflowSnap{Revisions: make([]json.RawMessage, len(rec.revs))}
+ ws := workflowSnap{Revisions: make([]jsontext.Value, len(rec.revs))}
for i, r := range rec.revs {
ws.Revisions[i] = marshalPB(r)
}
diff --git a/services/glacier/handler_archives_test.go b/services/glacier/handler_archives_test.go
index 1900a12e56..f46c33c4c0 100644
--- a/services/glacier/handler_archives_test.go
+++ b/services/glacier/handler_archives_test.go
@@ -3,7 +3,7 @@ package glacier_test
import (
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glacier"
)
@@ -57,7 +58,7 @@ func TestUploadDeleteArchive(t *testing.T) {
assert.Equal(t, http.StatusCreated, rec2.Code)
var uploadResp map[string]any
- err = json.Unmarshal(rec2.Body.Bytes(), &uploadResp)
+ err = json.Unmarshal(rec2.Body.Bytes(), &uploadResp, jsonx.V1())
require.NoError(t, err)
archiveID := uploadResp["archiveId"].(string)
@@ -190,7 +191,7 @@ func TestUploadArchive_TreeHashReturnedInResponse(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, expectedHash, resp["checksum"])
assert.Equal(t, expectedHash, rec.Header().Get("X-Amz-Sha256-Tree-Hash"))
}
diff --git a/services/glacier/handler_data_retrieval_policy.go b/services/glacier/handler_data_retrieval_policy.go
index 42d20a5879..f5054237ab 100644
--- a/services/glacier/handler_data_retrieval_policy.go
+++ b/services/glacier/handler_data_retrieval_policy.go
@@ -1,11 +1,13 @@
package glacier
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleDataRetrievalPolicy handles GetDataRetrievalPolicy and SetDataRetrievalPolicy.
@@ -30,7 +32,7 @@ func (h *Handler) handleGetDataRetrievalPolicy(c *echo.Context) error {
}
var parsed any
- if err := json.Unmarshal([]byte(policy), &parsed); err == nil {
+ if err := json.Unmarshal([]byte(policy), &parsed, jsonx.V1()); err == nil {
return c.JSON(http.StatusOK, parsed)
}
@@ -39,7 +41,7 @@ func (h *Handler) handleGetDataRetrievalPolicy(c *echo.Context) error {
func (h *Handler) handleSetDataRetrievalPolicy(c *echo.Context, body []byte) error {
var req dataRetrievalPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException",
"invalid data retrieval policy: "+err.Error())
}
diff --git a/services/glacier/handler_data_retrieval_policy_test.go b/services/glacier/handler_data_retrieval_policy_test.go
index 9811be5c4c..f6a85eeaba 100644
--- a/services/glacier/handler_data_retrieval_policy_test.go
+++ b/services/glacier/handler_data_retrieval_policy_test.go
@@ -1,13 +1,15 @@
package glacier_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDataRetrievalPolicy(t *testing.T) {
@@ -43,7 +45,7 @@ func TestDataRetrievalPolicy(t *testing.T) {
if tt.wantPolicy {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "Policy")
}
})
@@ -86,7 +88,7 @@ func TestDataRetrievalPolicy_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.NotEmpty(t, got)
})
}
@@ -112,7 +114,7 @@ func TestDataRetrievalPolicy_DefaultFreeTier(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
policy, ok := got["Policy"].(map[string]any)
require.True(t, ok)
@@ -198,7 +200,7 @@ func TestDataRetrievalPolicy_GetDefault(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policy := resp["Policy"].(map[string]any)
rules := policy["Rules"].([]any)
@@ -269,7 +271,7 @@ func TestDataRetrievalPolicy_SetAndGet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policy := resp["Policy"].(map[string]any)
rules := policy["Rules"].([]any)
require.NotEmpty(t, rules)
diff --git a/services/glacier/handler_job_output_test.go b/services/glacier/handler_job_output_test.go
index 6046bdecfe..55d51dde09 100644
--- a/services/glacier/handler_job_output_test.go
+++ b/services/glacier/handler_job_output_test.go
@@ -2,7 +2,7 @@ package glacier_test
import (
"encoding/csv"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glacier"
)
@@ -50,7 +51,7 @@ func TestGetJobOutput_ArchiveDescriptionHeader(t *testing.T) {
require.Equal(t, http.StatusAccepted, rec.Code)
var initResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
jobID := initResp["jobId"].(string)
rec = doRequest(t, h, http.MethodGet,
@@ -203,7 +204,7 @@ func TestInventoryCSV_RFC4180QuotedDescriptions(t *testing.T) {
require.Equal(t, http.StatusAccepted, jobRec.Code)
var jobResp map[string]string
- require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &jobResp))
+ require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &jobResp, jsonx.V1()))
jobID := jobResp["jobId"]
require.NotEmpty(t, jobID)
@@ -242,7 +243,7 @@ func TestInventoryCSV_DescriptionWithDoubleQuote(t *testing.T) {
require.Equal(t, http.StatusAccepted, jobRec.Code)
var jobResp map[string]string
- require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &jobResp))
+ require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &jobResp, jsonx.V1()))
jobID := jobResp["jobId"]
outRec := doRequest(t, h, http.MethodGet,
@@ -278,7 +279,7 @@ func TestGetJobOutput_CompletedFilter_Integration(t *testing.T) {
require.Equal(t, http.StatusAccepted, jobRec.Code)
var jr map[string]string
- require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &jr))
+ require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &jr, jsonx.V1()))
jobID := jr["jobId"]
require.NotEmpty(t, jobID)
@@ -340,7 +341,7 @@ func TestGetJobOutput_SHA256Header(t *testing.T) {
require.Equal(t, http.StatusAccepted, rec.Code)
var initResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
jobID := initResp["jobId"].(string)
rec = doRequest(t, h, http.MethodGet,
@@ -361,7 +362,7 @@ func uploadArchiveData(t *testing.T, h *glacier.Handler, vaultName string, data
require.NoError(t, h.Handler()(c))
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id := resp["archiveId"]
require.NotEmpty(t, id)
@@ -375,7 +376,7 @@ func initiateJobWithBody(t *testing.T, h *glacier.Handler, vaultName, body strin
rec := doRequestWithHeaders(t, h, http.MethodPost, "/"+testAccountID+"/vaults/"+vaultName+"/jobs", body, nil)
require.Equal(t, http.StatusAccepted, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id := resp["jobId"]
require.NotEmpty(t, id)
@@ -413,7 +414,7 @@ func TestArchiveRetrieval_FullLifecycle(t *testing.T) {
"/"+testAccountID+"/vaults/arch-lifecycle/jobs/"+jobID, "", nil)
require.Equal(t, http.StatusOK, rec.Code)
var desc map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc, jsonx.V1()))
assert.Equal(t, "Succeeded", desc["StatusCode"])
assert.Equal(t, true, desc["Completed"])
@@ -551,7 +552,7 @@ func TestInventoryRetrieval_JSONLifecycle(t *testing.T) {
assert.Equal(t, "application/json", rec.Header().Get("Content-Type"))
var inv map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &inv))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &inv, jsonx.V1()))
archList, ok := inv["ArchiveList"].([]any)
require.True(t, ok)
assert.Len(t, archList, tt.archiveCount)
@@ -634,7 +635,7 @@ func TestInventoryRetrieval_InventorySizeInBytes(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var desc map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &desc, jsonx.V1()))
raw, present := desc["InventorySizeInBytes"]
assert.True(t, present, "InventorySizeInBytes must be present after GetJobOutput")
got := int64(raw.(float64))
@@ -663,7 +664,7 @@ func TestInventoryRetrieval_LastInventoryDateUpdated(t *testing.T) {
rec := doRequestWithHeaders(t, h, http.MethodGet, "/"+testAccountID+"/vaults/lastinv-vault", "", nil)
require.Equal(t, http.StatusOK, rec.Code)
var vaultDesc map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &vaultDesc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &vaultDesc, jsonx.V1()))
assert.Empty(t, vaultDesc["LastInventoryDate"])
initiateJobWithBody(t, h, "lastinv-vault", `{"Type":"inventory-retrieval"}`)
@@ -671,7 +672,7 @@ func TestInventoryRetrieval_LastInventoryDateUpdated(t *testing.T) {
// After job: LastInventoryDate set.
rec = doRequestWithHeaders(t, h, http.MethodGet, "/"+testAccountID+"/vaults/lastinv-vault", "", nil)
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &vaultDesc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &vaultDesc, jsonx.V1()))
assert.NotEmpty(t, vaultDesc["LastInventoryDate"], tt.name)
})
}
diff --git a/services/glacier/handler_jobs.go b/services/glacier/handler_jobs.go
index cc0ec7f1a6..2e34357979 100644
--- a/services/glacier/handler_jobs.go
+++ b/services/glacier/handler_jobs.go
@@ -3,7 +3,7 @@ package glacier
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -11,11 +11,13 @@ import (
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleInitiateJob(c *echo.Context, vaultName string, body []byte) error {
var req initiateJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -265,7 +267,7 @@ func (h *Handler) writeInventoryJSON(c *echo.Context, j *Job, vaultName string,
"VaultARN": j.VaultARN,
"InventoryDate": j.CompletionDate,
"ArchiveList": items,
- })
+ }, jsonx.V1())
if err != nil {
return h.writeError(
c,
diff --git a/services/glacier/handler_jobs_test.go b/services/glacier/handler_jobs_test.go
index 4452779c4c..312e0c0cb5 100644
--- a/services/glacier/handler_jobs_test.go
+++ b/services/glacier/handler_jobs_test.go
@@ -1,7 +1,8 @@
package glacier_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -14,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glacier"
)
@@ -48,7 +50,7 @@ func TestInitiateListDescribeJob(t *testing.T) {
assert.Equal(t, http.StatusAccepted, rec.Code)
var jobResp map[string]any
- err := json.Unmarshal(rec.Body.Bytes(), &jobResp)
+ err := json.Unmarshal(rec.Body.Bytes(), &jobResp, jsonx.V1())
require.NoError(t, err)
jobID := jobResp["jobId"].(string)
@@ -63,7 +65,7 @@ func TestInitiateListDescribeJob(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- err = json.Unmarshal(rec.Body.Bytes(), &descResp)
+ err = json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1())
require.NoError(t, err)
assert.Equal(t, jobID, descResp["JobId"])
@@ -122,7 +124,7 @@ func TestJobTier_StoredInDescribeJob(t *testing.T) {
require.Equal(t, http.StatusAccepted, rec.Code)
var initResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
jobID := initResp["jobId"].(string)
rec = doRequest(t, h, http.MethodGet,
@@ -130,7 +132,7 @@ func TestJobTier_StoredInDescribeJob(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Bulk", descResp["Tier"])
}
@@ -150,7 +152,7 @@ func TestInventoryJob_UpdatesLastInventoryDate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var desc map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc, jsonx.V1()))
_, hasDate := desc["LastInventoryDate"]
assert.False(t, hasDate, "LastInventoryDate should not be set before first inventory")
@@ -165,7 +167,7 @@ func TestInventoryJob_UpdatesLastInventoryDate(t *testing.T) {
"/"+testAccountID+"/vaults/inv-date-vault", "")
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc, jsonx.V1()))
lastInventory, ok := desc["LastInventoryDate"]
assert.True(t, ok, "LastInventoryDate should be set after inventory job")
assert.NotEmpty(t, lastInventory)
@@ -184,7 +186,7 @@ func TestArchiveJob_DoesNotUpdateLastInventoryDate(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var archResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &archResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &archResp, jsonx.V1()))
archiveID := archResp["archiveId"].(string)
// Initiate archive-retrieval job.
@@ -244,7 +246,7 @@ func TestListJobs_LimitAndMarker(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
jobList := resp["JobList"]
if jobList == nil {
@@ -304,7 +306,7 @@ func TestArchiveRetrievalJob_StoresRetrievalByteRange(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var archResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &archResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &archResp, jsonx.V1()))
archiveID := archResp["archiveId"].(string)
// Initiate archive retrieval with RetrievalByteRange.
@@ -314,7 +316,7 @@ func TestArchiveRetrievalJob_StoresRetrievalByteRange(t *testing.T) {
require.Equal(t, http.StatusAccepted, rec.Code)
var initResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
jobID := initResp["jobId"].(string)
// DescribeJob should reflect the stored range.
@@ -323,7 +325,7 @@ func TestArchiveRetrievalJob_StoresRetrievalByteRange(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
// The RetrievalByteRange field is forwarded from the request.
_ = descResp
}
@@ -468,7 +470,7 @@ func TestDescribeJob_ArchiveSizeInBytes(t *testing.T) {
require.Equal(t, http.StatusAccepted, rec.Code)
var initResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
jobID := initResp["jobId"].(string)
rec = doRequest(
@@ -481,7 +483,7 @@ func TestDescribeJob_ArchiveSizeInBytes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var jobResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &jobResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &jobResp, jsonx.V1()))
if tt.wantSizePresent {
assert.NotNil(t, jobResp["ArchiveSizeInBytes"])
}
@@ -540,7 +542,7 @@ func TestDescribeJob_ArchiveSHA256TreeHash(t *testing.T) {
require.Equal(t, http.StatusAccepted, rec.Code)
var initResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
jobID := initResp["jobId"].(string)
rec = doRequest(t, h, http.MethodGet,
@@ -548,7 +550,7 @@ func TestDescribeJob_ArchiveSHA256TreeHash(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var jobResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &jobResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &jobResp, jsonx.V1()))
if tt.wantArchiveSHA {
assert.Equal(t, "cafebabe", jobResp["ArchiveSHA256TreeHash"],
@@ -594,7 +596,7 @@ func initiateJob(t *testing.T, h *glacier.Handler, vaultName, jobType string) st
require.Equal(t, http.StatusAccepted, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
jobID, ok := resp["jobId"].(string)
require.True(t, ok, "jobId missing from initiate response")
@@ -635,7 +637,7 @@ func TestInitiateJob_StartsInProgress(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var desc map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc, jsonx.V1()))
assert.Equal(t, "InProgress", desc["StatusCode"])
})
}
@@ -678,7 +680,7 @@ func TestInitiateJob_SucceedsAfterDelay(t *testing.T) {
)
require.Equal(t, http.StatusOK, recEarly.Code)
var earlyDesc map[string]any
- require.NoError(t, json.Unmarshal(recEarly.Body.Bytes(), &earlyDesc))
+ require.NoError(t, json.Unmarshal(recEarly.Body.Bytes(), &earlyDesc, jsonx.V1()))
assert.Equal(t, "InProgress", earlyDesc["StatusCode"])
// After the delay elapses the job should be Succeeded.
@@ -687,7 +689,7 @@ func TestInitiateJob_SucceedsAfterDelay(t *testing.T) {
recLate := doRequest(t, h, http.MethodGet, "/"+testAccountID+"/vaults/"+tt.vaultName+"/jobs/"+jobID, "")
require.Equal(t, http.StatusOK, recLate.Code)
var lateDesc map[string]any
- require.NoError(t, json.Unmarshal(recLate.Body.Bytes(), &lateDesc))
+ require.NoError(t, json.Unmarshal(recLate.Body.Bytes(), &lateDesc, jsonx.V1()))
assert.Equal(t, "Succeeded", lateDesc["StatusCode"])
})
})
@@ -721,7 +723,7 @@ func TestInitiateJob_ZeroDelay_ImmediateSucceeded(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var desc map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc, jsonx.V1()))
assert.Equal(t, "Succeeded", desc["StatusCode"])
})
}
@@ -762,8 +764,8 @@ func TestListJobs_JobListAlwaysPresent(t *testing.T) {
"/"+testAccountID+"/vaults/listjobs-audit-vault/jobs", "")
require.Equal(t, http.StatusOK, rec.Code)
- var resp map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ var resp map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, present := resp["JobList"]
assert.True(t, present, "JobList key must always be present in ListJobs response")
@@ -821,7 +823,7 @@ func TestDescribeJob_SNSTopicPresent(t *testing.T) {
require.Equal(t, http.StatusAccepted, initRec.Code)
var initResp map[string]string
- require.NoError(t, json.Unmarshal(initRec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(initRec.Body.Bytes(), &initResp, jsonx.V1()))
jobID := initResp["jobId"]
require.NotEmpty(t, jobID)
@@ -829,18 +831,18 @@ func TestDescribeJob_SNSTopicPresent(t *testing.T) {
"/"+testAccountID+"/vaults/snstopic-audit-vault/jobs/"+jobID, "")
require.Equal(t, http.StatusOK, descRec.Code)
- var resp map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ var resp map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
snsRaw, present := resp["SNSTopic"]
if tt.wantPresent {
assert.True(t, present, "SNSTopic must be present when job was initiated with SNSTopic")
var got string
- require.NoError(t, json.Unmarshal(snsRaw, &got))
+ require.NoError(t, json.Unmarshal(snsRaw, &got, jsonx.V1()))
assert.Equal(t, tt.wantSNSTopic, got)
} else if present {
var got string
- require.NoError(t, json.Unmarshal(snsRaw, &got))
+ require.NoError(t, json.Unmarshal(snsRaw, &got, jsonx.V1()))
assert.Empty(t, got, "SNSTopic should be empty or absent when not set")
}
})
@@ -889,7 +891,7 @@ func TestDescribeJob_RetrievalByteRange(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var archResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &archResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &archResp, jsonx.V1()))
archiveID := archResp["archiveId"]
require.NotEmpty(t, archiveID)
@@ -906,7 +908,7 @@ func TestDescribeJob_RetrievalByteRange(t *testing.T) {
require.Equal(t, http.StatusAccepted, initRec.Code)
var initResp map[string]string
- require.NoError(t, json.Unmarshal(initRec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(initRec.Body.Bytes(), &initResp, jsonx.V1()))
jobID := initResp["jobId"]
require.NotEmpty(t, jobID)
@@ -914,18 +916,18 @@ func TestDescribeJob_RetrievalByteRange(t *testing.T) {
"/"+testAccountID+"/vaults/byterange-audit-vault/jobs/"+jobID, "")
require.Equal(t, http.StatusOK, descRec.Code)
- var resp map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ var resp map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
rangeRaw, present := resp["RetrievalByteRange"]
if tt.wantInResp {
assert.True(t, present, "RetrievalByteRange must be present when set on the job")
var got string
- require.NoError(t, json.Unmarshal(rangeRaw, &got))
+ require.NoError(t, json.Unmarshal(rangeRaw, &got, jsonx.V1()))
assert.Equal(t, tt.byteRange, got)
} else if present {
var got string
- require.NoError(t, json.Unmarshal(rangeRaw, &got))
+ require.NoError(t, json.Unmarshal(rangeRaw, &got, jsonx.V1()))
assert.Empty(t, got, "RetrievalByteRange should be empty or absent when not set")
}
})
@@ -1020,7 +1022,7 @@ func TestListJobs_CompletedFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
jobs := resp["JobList"].([]any)
assert.Len(t, jobs, tt.wantCount)
})
@@ -1054,7 +1056,7 @@ func TestListJobs_StatusCodeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
jobs := resp["JobList"].([]any)
assert.Len(t, jobs, tt.wantCount)
})
@@ -1127,7 +1129,7 @@ func TestDescribeJob_Fidelity(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantAction, resp["Action"])
assert.Equal(t, tt.wantTier, resp["Tier"])
assert.NotEmpty(t, resp["JobId"])
@@ -1164,7 +1166,7 @@ func TestDescribeJob_ArchiveSizePopulated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
archiveSize, ok := resp["ArchiveSizeInBytes"].(float64)
assert.True(t, ok, "ArchiveSizeInBytes must be present for archive-retrieval job")
assert.InDelta(t, float64(len(tt.content)), archiveSize, 0)
diff --git a/services/glacier/handler_multipart_uploads_test.go b/services/glacier/handler_multipart_uploads_test.go
index 06b76914fa..12755b75b8 100644
--- a/services/glacier/handler_multipart_uploads_test.go
+++ b/services/glacier/handler_multipart_uploads_test.go
@@ -1,7 +1,8 @@
package glacier_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glacier"
)
@@ -83,7 +85,7 @@ func TestInitiateMultipartUpload(t *testing.T) {
if tt.wantUploadID {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["uploadId"])
uploadID := rec.Header().Get("X-Amz-Multipart-Upload-Id")
@@ -257,7 +259,7 @@ func TestCompleteMultipartUpload(t *testing.T) {
if tt.wantArchive {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["archiveId"])
assert.NotEmpty(t, rec.Header().Get("X-Amz-Archive-Id"))
}
@@ -377,7 +379,7 @@ func TestListMultipartUploads(t *testing.T) {
var resp struct {
UploadsList []any `json:"UploadsList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.UploadsList, tt.wantCount)
})
}
@@ -469,7 +471,7 @@ func TestListParts(t *testing.T) {
var resp struct {
Parts []any `json:"Parts"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Parts, tt.wantParts)
}
})
@@ -541,7 +543,7 @@ func TestMultipartUpload_FullRoundTrip(t *testing.T) {
var partsResp struct {
Parts []any `json:"Parts"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &partsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &partsResp, jsonx.V1()))
assert.Len(t, partsResp.Parts, 1)
// List uploads — should have 1
@@ -551,7 +553,7 @@ func TestMultipartUpload_FullRoundTrip(t *testing.T) {
var uploadsResp struct {
UploadsList []any `json:"UploadsList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &uploadsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &uploadsResp, jsonx.V1()))
assert.Len(t, uploadsResp.UploadsList, 1)
// Complete the upload
@@ -567,13 +569,13 @@ func TestMultipartUpload_FullRoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var completeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &completeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &completeResp, jsonx.V1()))
assert.NotEmpty(t, completeResp["archiveId"])
// After completion, list uploads should be empty
rec = doRequest(t, h, http.MethodGet, "/-/vaults/"+tt.vaultName+"/multipart-uploads", "")
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &uploadsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &uploadsResp, jsonx.V1()))
assert.Empty(t, uploadsResp.UploadsList)
})
}
@@ -818,7 +820,7 @@ func TestUploadMultipartPart_RequiresContentRange(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var initResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
uploadID := initResp["uploadId"]
require.NotEmpty(t, uploadID)
@@ -893,7 +895,7 @@ func TestCompleteMultipartUpload_RequiresHeaders(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var initResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
uploadID := initResp["uploadId"]
require.NotEmpty(t, uploadID)
@@ -953,7 +955,7 @@ func TestListMultipartUploads_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
uploads := resp["UploadsList"].([]any)
assert.Len(t, uploads, 2)
@@ -1007,7 +1009,7 @@ func TestListParts_Pagination(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var upResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &upResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &upResp, jsonx.V1()))
uploadID := upResp["uploadId"].(string)
// Upload 3 parts.
@@ -1029,7 +1031,7 @@ func TestListParts_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var partsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &partsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &partsResp, jsonx.V1()))
parts := partsResp["Parts"].([]any)
require.Len(t, parts, 3)
@@ -1039,7 +1041,7 @@ func TestListParts_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var pPage1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pPage1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pPage1, jsonx.V1()))
assert.Len(t, pPage1["Parts"].([]any), 1)
assert.NotEmpty(t, pPage1["Marker"])
}
@@ -1082,7 +1084,7 @@ func TestListParts_PartsAlwaysArray(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var initResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
uploadID := initResp["uploadId"]
require.NotEmpty(t, uploadID)
@@ -1101,8 +1103,8 @@ func TestListParts_PartsAlwaysArray(t *testing.T) {
"/"+testAccountID+"/vaults/listparts-audit-vault/multipart-uploads/"+uploadID, "")
require.Equal(t, http.StatusOK, listRec.Code)
- var resp map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ var resp map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
partsRaw, present := resp["Parts"]
assert.True(t, present, "Parts key must be present in ListParts response")
@@ -1150,7 +1152,7 @@ func TestMultipartUpload_FullLifecycle(t *testing.T) {
"", headers)
require.Equal(t, http.StatusCreated, rec.Code)
var initResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
uploadID := initResp["uploadId"]
require.NotEmpty(t, uploadID)
// Location header must be set.
@@ -1175,7 +1177,7 @@ func TestMultipartUpload_FullLifecycle(t *testing.T) {
"", nil)
require.Equal(t, http.StatusOK, rec2.Code)
var listParts map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listParts))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listParts, jsonx.V1()))
parts := listParts["Parts"].([]any)
assert.Len(t, parts, 1)
@@ -1189,7 +1191,7 @@ func TestMultipartUpload_FullLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rec3.Code)
var completeResp map[string]string
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &completeResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &completeResp, jsonx.V1()))
archiveID := completeResp["archiveId"]
assert.NotEmpty(t, archiveID)
assert.Equal(t, checksum, rec3.Header().Get("X-Amz-Sha256-Tree-Hash"))
@@ -1199,7 +1201,7 @@ func TestMultipartUpload_FullLifecycle(t *testing.T) {
"/"+testAccountID+"/vaults/mp-lifecycle-"+tt.name+"/multipart-uploads", "", nil)
require.Equal(t, http.StatusOK, rec4.Code)
var listUploads map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &listUploads))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &listUploads, jsonx.V1()))
uploads := listUploads["UploadsList"].([]any)
assert.Empty(t, uploads)
@@ -1215,7 +1217,7 @@ func TestMultipartUpload_FullLifecycle(t *testing.T) {
"/"+testAccountID+"/vaults/mp-lifecycle-"+tt.name, "", nil)
require.Equal(t, http.StatusOK, descVault.Code)
var vaultDesc map[string]any
- require.NoError(t, json.Unmarshal(descVault.Body.Bytes(), &vaultDesc))
+ require.NoError(t, json.Unmarshal(descVault.Body.Bytes(), &vaultDesc, jsonx.V1()))
assert.EqualValues(t, 1, vaultDesc["NumberOfArchives"])
})
}
@@ -1242,7 +1244,7 @@ func TestMultipartUpload_AbortLifecycle(t *testing.T) {
"", map[string]string{"X-Amz-Part-Size": "1048576"})
require.Equal(t, http.StatusCreated, rec.Code)
var initResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
uploadID := initResp["uploadId"]
// Abort.
@@ -1255,7 +1257,7 @@ func TestMultipartUpload_AbortLifecycle(t *testing.T) {
"/"+testAccountID+"/vaults/mp-abort-vault/multipart-uploads", "", nil)
require.Equal(t, http.StatusOK, rec3.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listResp, jsonx.V1()))
uploads := listResp["UploadsList"].([]any)
assert.Empty(t, uploads, tt.name)
@@ -1293,7 +1295,7 @@ func TestListParts_MarkerNotFound(t *testing.T) {
map[string]string{"X-Amz-Part-Size": "1048576"})
require.Equal(t, http.StatusCreated, rec.Code)
var initResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
uploadID := initResp["uploadId"]
require.NotEmpty(t, uploadID)
@@ -1303,8 +1305,8 @@ func TestListParts_MarkerNotFound(t *testing.T) {
"", nil)
require.Equal(t, http.StatusOK, rec2.Code)
- var resp map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ var resp map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
partsRaw := resp["Parts"]
assert.Equal(t, "[]", string(partsRaw), tt.name)
})
@@ -1338,7 +1340,7 @@ func TestListParts_MarkerPagination(t *testing.T) {
"", map[string]string{"X-Amz-Part-Size": "1048576"})
require.Equal(t, http.StatusCreated, rec.Code)
var initResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
uploadID := initResp["uploadId"]
// Upload 2 parts.
@@ -1363,7 +1365,7 @@ func TestListParts_MarkerPagination(t *testing.T) {
"", nil)
require.Equal(t, http.StatusOK, rec.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
parts1 := page1["Parts"].([]any)
require.Len(t, parts1, 1)
markerVal, hasMarker := page1["Marker"].(string)
@@ -1375,7 +1377,7 @@ func TestListParts_MarkerPagination(t *testing.T) {
rec = doRequestWithHeaders(t, h, http.MethodGet, secondPage, "", nil)
require.Equal(t, http.StatusOK, rec.Code)
var page2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
parts2 := page2["Parts"].([]any)
assert.Len(t, parts2, 1)
_, hasMarker2 := page2["Marker"]
diff --git a/services/glacier/handler_provisioned_capacity_test.go b/services/glacier/handler_provisioned_capacity_test.go
index d59ad91ac9..9ea26411e5 100644
--- a/services/glacier/handler_provisioned_capacity_test.go
+++ b/services/glacier/handler_provisioned_capacity_test.go
@@ -1,7 +1,7 @@
package glacier_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glacier"
)
@@ -51,7 +52,7 @@ func TestListProvisionedCapacity(t *testing.T) {
var resp struct {
ProvisionedCapacityList []any `json:"ProvisionedCapacityList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.ProvisionedCapacityList, tt.wantCount)
})
}
@@ -84,7 +85,7 @@ func TestPurchaseProvisionedCapacity(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["capacityId"])
assert.NotEmpty(t, rec.Header().Get("X-Amz-Capacity-Id"))
})
@@ -113,7 +114,7 @@ func TestProvisionedCapacity_MaxTwoPerAccount(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code, rec.Body.String())
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "LimitExceededException", errResp["code"])
}
@@ -139,7 +140,7 @@ func TestProvisionedCapacity_ExpiresAfterOneMonth(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
capList := resp["ProvisionedCapacityList"].([]any)
require.Len(t, capList, 1)
@@ -222,7 +223,7 @@ func TestProvisionedCapacity_PurchaseAndList(t *testing.T) {
"/"+testAccountID+"/provisioned-capacity", "", nil)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list := resp["ProvisionedCapacityList"].([]any)
assert.Len(t, list, tt.wantCount)
})
@@ -274,7 +275,7 @@ func TestProvisionedCapacity_DatesPresent(t *testing.T) {
"/"+testAccountID+"/provisioned-capacity", "", nil)
require.Equal(t, http.StatusCreated, rec.Code)
var purchaseResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &purchaseResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &purchaseResp, jsonx.V1()))
capID := purchaseResp["capacityId"]
require.NotEmpty(t, capID)
@@ -282,7 +283,7 @@ func TestProvisionedCapacity_DatesPresent(t *testing.T) {
"/"+testAccountID+"/provisioned-capacity", "", nil)
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
list := listResp["ProvisionedCapacityList"].([]any)
require.Len(t, list, 1)
capItem := list[0].(map[string]any)
diff --git a/services/glacier/handler_tags.go b/services/glacier/handler_tags.go
index 84acc34460..902f142035 100644
--- a/services/glacier/handler_tags.go
+++ b/services/glacier/handler_tags.go
@@ -1,15 +1,17 @@
package glacier
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleAddTagsToVault(c *echo.Context, vaultName string, body []byte) error {
var req addTagsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -36,7 +38,7 @@ func (h *Handler) handleListTagsForVault(c *echo.Context, vaultName string) erro
func (h *Handler) handleRemoveTagsFromVault(c *echo.Context, vaultName string, body []byte) error {
var req removeTagsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/glacier/handler_tags_test.go b/services/glacier/handler_tags_test.go
index af667371bd..0e88b8b20a 100644
--- a/services/glacier/handler_tags_test.go
+++ b/services/glacier/handler_tags_test.go
@@ -1,7 +1,7 @@
package glacier_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -11,6 +11,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestTags(t *testing.T) {
@@ -56,7 +58,7 @@ func TestTags(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var tagsResp map[string]any
- err = json.Unmarshal(rec.Body.Bytes(), &tagsResp)
+ err = json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1())
require.NoError(t, err)
tags := tagsResp["Tags"].(map[string]any)
@@ -118,7 +120,7 @@ func TestTags_KeyValidation(t *testing.T) {
h := newTestHandler()
createVault(t, h, "tag-val-vault")
- tagsJSON, _ := json.Marshal(tt.tags)
+ tagsJSON, _ := json.Marshal(tt.tags, jsonx.V1())
body := fmt.Sprintf(`{"Tags":%s}`, tagsJSON)
rec := doRequest(t, h, http.MethodPost,
"/"+testAccountID+"/vaults/tag-val-vault/tags?operation=add", body)
@@ -155,7 +157,7 @@ func TestTags_ExactLimitAccepted(t *testing.T) {
tags[fmt.Sprintf("key-%02d", i)] = fmt.Sprintf("val-%02d", i)
}
- body, err := json.Marshal(map[string]any{"Tags": tags})
+ body, err := json.Marshal(map[string]any{"Tags": tags}, jsonx.V1())
require.NoError(t, err)
rec := doRequestWithHeaders(t, h, http.MethodPost,
@@ -248,7 +250,7 @@ func TestTags_ListTagsEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags, ok := resp["Tags"].(map[string]any)
assert.True(t, ok, tt.name)
assert.Empty(t, tags)
diff --git a/services/glacier/handler_test.go b/services/glacier/handler_test.go
index 6124101b8f..7a7653724d 100644
--- a/services/glacier/handler_test.go
+++ b/services/glacier/handler_test.go
@@ -1,7 +1,8 @@
package glacier_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strconv"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glacier"
)
@@ -230,7 +232,7 @@ func TestHandlerReset(t *testing.T) {
var out struct {
VaultList []any `json:"VaultList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.VaultList, tt.wantAfter)
})
}
@@ -582,7 +584,7 @@ func TestErrorResponse_IncludesTypeField(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var errBody map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.NotEmpty(t, errBody["code"], "error body should have code field")
assert.NotEmpty(t, errBody["__type"], "error body should have __type field")
@@ -646,7 +648,7 @@ func TestErrorResponse_FormatFields(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
// Both "code" and "__type" must be present (SDK compatibility).
assert.Equal(t, tt.wantCodeKey, errResp["code"])
assert.Equal(t, tt.wantCodeKey, errResp["__type"])
@@ -732,8 +734,8 @@ func TestPagination_MarkerNotFound_EmptyList(t *testing.T) {
rec := doRequestWithHeaders(t, h, tt.method, tt.path, "", nil)
require.Equal(t, http.StatusOK, rec.Code)
- var resp map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ var resp map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
raw, ok := resp[tt.jsonKey]
assert.True(t, ok, "%s key must be present", tt.jsonKey)
assert.Equal(t, "[]", string(raw), "%s must be [] not null when marker not found", tt.jsonKey)
diff --git a/services/glacier/handler_vault_access_policy.go b/services/glacier/handler_vault_access_policy.go
index 3aae3be999..23ca7c2666 100644
--- a/services/glacier/handler_vault_access_policy.go
+++ b/services/glacier/handler_vault_access_policy.go
@@ -1,15 +1,17 @@
package glacier
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleSetVaultAccessPolicy(c *echo.Context, vaultName string, body []byte) error {
var req vaultAccessPolicy
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/glacier/handler_vault_access_policy_test.go b/services/glacier/handler_vault_access_policy_test.go
index f7a7293fd1..347c9dff80 100644
--- a/services/glacier/handler_vault_access_policy_test.go
+++ b/services/glacier/handler_vault_access_policy_test.go
@@ -1,13 +1,15 @@
package glacier_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestAccessPolicy(t *testing.T) {
@@ -87,7 +89,7 @@ func TestAccessPolicy_SetGetDelete(t *testing.T) {
"/"+testAccountID+"/vaults/access-policy-vault/access-policy", "", nil)
require.Equal(t, http.StatusOK, rec.Code)
var policyResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &policyResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &policyResp, jsonx.V1()))
assert.NotEmpty(t, policyResp["Policy"])
// DELETE.
diff --git a/services/glacier/handler_vault_lock.go b/services/glacier/handler_vault_lock.go
index 8fe7ed7509..d15a1784c7 100644
--- a/services/glacier/handler_vault_lock.go
+++ b/services/glacier/handler_vault_lock.go
@@ -1,10 +1,12 @@
package glacier
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleVaultLock(c *echo.Context, op, resource string, body []byte) error {
@@ -20,7 +22,7 @@ func (h *Handler) handleVaultLock(c *echo.Context, op, resource string, body []b
case opInitiateVaultLock:
var req vaultLockPolicyRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException",
"invalid request body: "+err.Error())
}
diff --git a/services/glacier/handler_vault_lock_test.go b/services/glacier/handler_vault_lock_test.go
index a0134caba4..8384329043 100644
--- a/services/glacier/handler_vault_lock_test.go
+++ b/services/glacier/handler_vault_lock_test.go
@@ -1,7 +1,7 @@
package glacier_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glacier"
)
@@ -65,7 +66,7 @@ func TestGetVaultLock(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantState, resp["State"])
}
})
@@ -276,7 +277,7 @@ func TestCompleteVaultLock_Success(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
lockID := resp["lockId"]
require.NotEmpty(t, lockID)
@@ -316,7 +317,7 @@ func TestInitiateVaultLock_ReadsPolicy(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["lockId"])
})
}
@@ -384,7 +385,7 @@ func TestVaultLock_CompleteClearsExpirationDate(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var lockResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lockResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lockResp, jsonx.V1()))
lockID := lockResp["lockId"].(string)
rec = doRequest(t, h, http.MethodPost,
@@ -396,7 +397,7 @@ func TestVaultLock_CompleteClearsExpirationDate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "Locked", getResp["State"])
// ExpirationDate must be absent after completing a lock per AWS spec.
_, hasExpiration := getResp["ExpirationDate"]
@@ -420,7 +421,7 @@ func TestVaultLock_LockedStateHasNoExpirationDate(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var initResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
lockID := initResp["lockId"].(string)
// InProgress state should have ExpirationDate.
@@ -429,7 +430,7 @@ func TestVaultLock_LockedStateHasNoExpirationDate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var inProgressResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &inProgressResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &inProgressResp, jsonx.V1()))
assert.Equal(t, "InProgress", inProgressResp["State"])
assert.NotEmpty(t, inProgressResp["ExpirationDate"], "InProgress lock should have ExpirationDate")
@@ -444,7 +445,7 @@ func TestVaultLock_LockedStateHasNoExpirationDate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var lockedResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lockedResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lockedResp, jsonx.V1()))
assert.Equal(t, "Locked", lockedResp["State"])
exp, hasExp := lockedResp["ExpirationDate"]
@@ -533,7 +534,7 @@ func TestVaultLock_FullLifecycle(t *testing.T) {
"/"+testAccountID+"/vaults/lock-lifecycle-vault/lock-policy", body, nil)
require.Equal(t, http.StatusCreated, rec.Code)
var initResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
lockID := initResp["lockId"]
require.NotEmpty(t, lockID)
@@ -542,7 +543,7 @@ func TestVaultLock_FullLifecycle(t *testing.T) {
"/"+testAccountID+"/vaults/lock-lifecycle-vault/lock-policy", "", nil)
require.Equal(t, http.StatusOK, rec.Code)
var lockState map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lockState))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lockState, jsonx.V1()))
assert.Equal(t, "InProgress", lockState["State"])
assert.NotEmpty(t, lockState["ExpirationDate"])
@@ -555,7 +556,7 @@ func TestVaultLock_FullLifecycle(t *testing.T) {
rec = doRequestWithHeaders(t, h, http.MethodGet,
"/"+testAccountID+"/vaults/lock-lifecycle-vault/lock-policy", "", nil)
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lockState))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lockState, jsonx.V1()))
assert.Equal(t, "Locked", lockState["State"], tt.name)
})
}
diff --git a/services/glacier/handler_vault_notifications.go b/services/glacier/handler_vault_notifications.go
index b582d32f86..7890decc67 100644
--- a/services/glacier/handler_vault_notifications.go
+++ b/services/glacier/handler_vault_notifications.go
@@ -1,10 +1,12 @@
package glacier
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleSetVaultNotifications(
@@ -13,7 +15,7 @@ func (h *Handler) handleSetVaultNotifications(
body []byte,
) error {
var req vaultNotificationConfig
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/glacier/handler_vault_notifications_test.go b/services/glacier/handler_vault_notifications_test.go
index c52373957e..68b2388ac3 100644
--- a/services/glacier/handler_vault_notifications_test.go
+++ b/services/glacier/handler_vault_notifications_test.go
@@ -1,13 +1,15 @@
package glacier_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestNotifications(t *testing.T) {
@@ -96,7 +98,7 @@ func TestVaultNotifications_EventValidation(t *testing.T) {
h := newTestHandler()
createVault(t, h, "notif-vault")
- eventsJSON, _ := json.Marshal(tt.events)
+ eventsJSON, _ := json.Marshal(tt.events, jsonx.V1())
body := fmt.Sprintf(`{"SNSTopic":"arn:aws:sns:us-east-1:000000000000:test","Events":%s}`, eventsJSON)
rec := doRequest(t, h, http.MethodPut,
"/"+testAccountID+"/vaults/notif-vault/notification-configuration", body)
@@ -195,7 +197,7 @@ func TestVaultNotifications_SetGetDelete(t *testing.T) {
assert.Equal(t, http.StatusNotFound, rec.Code)
// SET.
- eventsJSON, _ := json.Marshal(tt.events)
+ eventsJSON, _ := json.Marshal(tt.events, jsonx.V1())
body := `{"SNSTopic":"` + tt.topic + `","Events":` + string(eventsJSON) + `}`
rec = doRequestWithHeaders(t, h, http.MethodPut,
"/"+testAccountID+"/vaults/notif-vault-"+tt.name+"/notification-configuration", body, nil)
@@ -206,7 +208,7 @@ func TestVaultNotifications_SetGetDelete(t *testing.T) {
"/"+testAccountID+"/vaults/notif-vault-"+tt.name+"/notification-configuration", "", nil)
require.Equal(t, http.StatusOK, rec.Code)
var notifResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ¬ifResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ¬ifResp, jsonx.V1()))
assert.Equal(t, tt.topic, notifResp["SNSTopic"])
events, ok := notifResp["Events"].([]any)
require.True(t, ok)
diff --git a/services/glacier/handler_vaults_test.go b/services/glacier/handler_vaults_test.go
index d478f9a5b7..7372536fe1 100644
--- a/services/glacier/handler_vaults_test.go
+++ b/services/glacier/handler_vaults_test.go
@@ -2,7 +2,8 @@ package glacier_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glacier"
)
@@ -46,7 +48,7 @@ func TestCreateDescribeDeleteVault(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var describeResp map[string]any
- err := json.Unmarshal(rec.Body.Bytes(), &describeResp)
+ err := json.Unmarshal(rec.Body.Bytes(), &describeResp, jsonx.V1())
require.NoError(t, err)
assert.Equal(t, tt.vaultName, describeResp["VaultName"])
@@ -218,7 +220,7 @@ func TestListVaults_AfterReset(t *testing.T) {
VaultList []any `json:"VaultList"`
}
- err := json.NewDecoder(rec.Body).Decode(&resp)
+ err := json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1())
require.NoError(t, err)
assert.Len(t, resp.VaultList, tt.wantCount)
@@ -237,7 +239,7 @@ func TestAccountIDDash_SubstitutedInListVaults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
vaultList := resp["VaultList"].([]any)
assert.Len(t, vaultList, 1)
}
@@ -253,7 +255,7 @@ func TestAccountIDReal_WorksLikeDash(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
vaultList := resp["VaultList"].([]any)
assert.Len(t, vaultList, 1)
}
@@ -297,7 +299,7 @@ func TestListVaults_LimitValidation(t *testing.T) {
if tt.wantStatus == http.StatusOK && tt.wantLen > 0 {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
vaultList := resp["VaultList"].([]any)
assert.Len(t, vaultList, tt.wantLen)
}
@@ -320,7 +322,7 @@ func TestListVaults_MarkerPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
list1 := page1["VaultList"].([]any)
assert.Len(t, list1, 2)
@@ -334,7 +336,7 @@ func TestListVaults_MarkerPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
list2 := page2["VaultList"].([]any)
assert.Len(t, list2, 2)
@@ -465,7 +467,7 @@ func uploadArchiveHTTP(t *testing.T, h *glacier.Handler, vaultName string) strin
require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotEmpty(t, resp["archiveId"])
return resp["archiveId"]
@@ -495,7 +497,7 @@ func assertVaultDeleteRejected(t *testing.T, h *glacier.Handler, vaultName strin
require.Equal(t, http.StatusBadRequest, rec.Code, rec.Body.String())
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterValueException", errResp["code"])
descRec := doRequest(t, h, http.MethodGet, "/"+testAccountID+"/vaults/"+vaultName, "")
@@ -579,7 +581,7 @@ func TestDeleteVault_InventorySemantics(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterValueException", errResp["code"])
descRec := doRequest(t, h, http.MethodGet, "/"+testAccountID+"/vaults/"+vaultName, "")
@@ -720,7 +722,7 @@ func TestDeleteVault_WithActiveMultipartUploads(t *testing.T) {
"", map[string]string{"X-Amz-Part-Size": "1048576"})
require.Equal(t, http.StatusCreated, rec.Code)
var initResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &initResp, jsonx.V1()))
uploadID := initResp["uploadId"]
// Delete vault (empty archives = ok, multipart uploads get cleaned).
@@ -767,7 +769,7 @@ func TestCreateVault_Idempotent(t *testing.T) {
rec := doRequestWithHeaders(t, h, http.MethodGet, "/"+testAccountID+"/vaults", "", nil)
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
vl := listResp["VaultList"].([]any)
assert.Len(t, vl, 1)
})
@@ -847,7 +849,7 @@ func TestDescribeVault_ArchiveStatsAsOfInventory(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/"+testAccountID+"/vaults/"+vaultName, "")
require.Equal(t, http.StatusOK, rec.Code)
var v map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &v))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &v, jsonx.V1()))
if tt.wantAbsent {
_, hasArchives := v["NumberOfArchives"]
@@ -946,7 +948,7 @@ func TestVault_CrossAccountIsolation(t *testing.T) {
listRecB := doRequestWithHeaders(t, hB, http.MethodGet, "/account-bbb/vaults", "", nil)
require.Equal(t, http.StatusOK, listRecB.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRecB.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRecB.Body.Bytes(), &listResp, jsonx.V1()))
vl := listResp["VaultList"].([]any)
require.Len(t, vl, 1, tt.name)
v := vl[0].(map[string]any)
@@ -990,7 +992,7 @@ func TestVault_CrossRegionIsolation(t *testing.T) {
recW := doRequestWithHeaders(t, hW, http.MethodGet, "/same-acct/vaults", "", nil)
require.Equal(t, http.StatusOK, recW.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(recW.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(recW.Body.Bytes(), &listResp, jsonx.V1()))
vl := listResp["VaultList"].([]any)
assert.Empty(t, vl, tt.name)
})
@@ -1023,7 +1025,7 @@ func TestVaultARN_Format(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ := resp["VaultARN"].(string)
assert.NotEmpty(t, arn)
wantARN := "arn:aws:glacier:" + testRegion + ":" + testAccountID + ":vaults/" + tt.vaultName
@@ -1092,7 +1094,7 @@ func TestListVaults_LimitAndMarkerCombined(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list := resp["VaultList"].([]any)
require.Len(t, list, len(tt.wantNames))
for i, want := range tt.wantNames {
diff --git a/services/glacier/inventory_retrieval_test.go b/services/glacier/inventory_retrieval_test.go
index 80731a6bc9..e6fbb1123c 100644
--- a/services/glacier/inventory_retrieval_test.go
+++ b/services/glacier/inventory_retrieval_test.go
@@ -1,13 +1,14 @@
package glacier_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glacier"
)
@@ -51,7 +52,7 @@ func inventoryArchiveIDs(t *testing.T, body []byte) []string {
ArchiveID string `json:"ArchiveId"`
} `json:"ArchiveList"`
}
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
ids := make([]string, len(resp.ArchiveList))
for i, a := range resp.ArchiveList {
@@ -177,7 +178,7 @@ func TestInitiateJob_InventoryRetrieval_InvalidParameters(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code, rec.Body.String())
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterValueException", errResp["code"])
})
}
@@ -200,7 +201,7 @@ func TestDescribeJob_InventoryRetrieval_EchoesParameters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasTopLevelFormat := resp["Format"]
assert.False(t, hasTopLevelFormat, "top-level Format is not a real GlacierJobDescription field")
@@ -231,7 +232,7 @@ func TestDescribeJob_ArchiveRetrieval_NoInventoryRetrievalParameters(t *testing.
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, has := resp["InventoryRetrievalParameters"]
assert.False(t, has)
diff --git a/services/glacier/oldshape_restore_test.go b/services/glacier/oldshape_restore_test.go
index 8a4d015adc..cd1ded1d2d 100644
--- a/services/glacier/oldshape_restore_test.go
+++ b/services/glacier/oldshape_restore_test.go
@@ -1,12 +1,15 @@
package glacier_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// stripOldShapeVaultFields simulates a snapshot taken before gopherstack-x8em/
@@ -17,14 +20,14 @@ import (
func stripOldShapeVaultFields(t *testing.T, snap []byte) []byte {
t.Helper()
- var top map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &top))
+ var top map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &top, jsonx.V1()))
- var tables map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(top["tables"], &tables))
+ var tables map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(top["tables"], &tables, jsonx.V1()))
var vaults []map[string]any
- require.NoError(t, json.Unmarshal(tables["vaults"], &vaults))
+ require.NoError(t, json.Unmarshal(tables["vaults"], &vaults, jsonx.V1()))
for _, v := range vaults {
delete(v, "numberOfArchivesAtLastInventory")
@@ -32,15 +35,15 @@ func stripOldShapeVaultFields(t *testing.T, snap []byte) []byte {
delete(v, "writeSinceLastInventory")
}
- vaultsRaw, err := json.Marshal(vaults)
+ vaultsRaw, err := json.Marshal(vaults, jsonx.V1())
require.NoError(t, err)
tables["vaults"] = vaultsRaw
- tablesRaw, err := json.Marshal(tables)
+ tablesRaw, err := json.Marshal(tables, jsonx.V1())
require.NoError(t, err)
top["tables"] = tablesRaw
- out, err := json.Marshal(top)
+ out, err := json.Marshal(top, jsonx.V1())
require.NoError(t, err)
return out
@@ -75,7 +78,7 @@ func TestRestoreOldShapeSnapshot_DeleteVaultGuard(t *testing.T) {
t.Logf("DescribeVault on restored old-shape vault: %s", rec.Body.String())
var describeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &describeResp, jsonx.V1()))
assert.NotEmpty(t, describeResp["LastInventoryDate"], "LastInventoryDate must survive restore")
_, hasNumArchives := describeResp["NumberOfArchives"]
assert.False(t, hasNumArchives,
diff --git a/services/glacier/persistence.go b/services/glacier/persistence.go
index 5c35c7582b..07b1d5ab9d 100644
--- a/services/glacier/persistence.go
+++ b/services/glacier/persistence.go
@@ -2,7 +2,7 @@ package glacier
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -49,7 +49,7 @@ type provisionedCapacitySnapshot struct {
// persisted directly alongside Tables, exactly as they were before this
// conversion.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
DataRetrievalPolicies map[string]string `json:"dataRetrievalPolicies,omitempty"`
MultipartParts []multipartPartSnapshot `json:"multipartParts"`
ProvisionedCapacity []provisionedCapacitySnapshot `json:"provisionedCapacity"`
diff --git a/services/glacier/persistence_test.go b/services/glacier/persistence_test.go
index 901621a48f..7d23068436 100644
--- a/services/glacier/persistence_test.go
+++ b/services/glacier/persistence_test.go
@@ -1,13 +1,14 @@
package glacier_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glacier"
)
@@ -370,7 +371,7 @@ func TestPersistenceRoundTrip_DataRetrievalPolicy(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.NotEmpty(t, got)
})
}
@@ -425,7 +426,7 @@ func TestPersistenceRoundTrip_MultipartAndCapacity(t *testing.T) {
var uploadsResp struct {
UploadsList []any `json:"UploadsList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &uploadsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &uploadsResp, jsonx.V1()))
assert.Len(t, uploadsResp.UploadsList, 1)
// Verify provisioned capacity is restored
@@ -435,7 +436,7 @@ func TestPersistenceRoundTrip_MultipartAndCapacity(t *testing.T) {
var capsResp struct {
ProvisionedCapacityList []any `json:"ProvisionedCapacityList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &capsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &capsResp, jsonx.V1()))
assert.Len(t, capsResp.ProvisionedCapacityList, 1)
})
}
@@ -475,7 +476,7 @@ func TestPersistenceRoundTrip_SelectAndRangeInventoryJobs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var selectResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &selectResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &selectResp, jsonx.V1()))
assert.Equal(t, "Select", selectResp["Action"])
assert.NotEmpty(t, selectResp["JobOutputPath"])
@@ -502,7 +503,7 @@ func TestPersistenceRoundTrip_SelectAndRangeInventoryJobs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var invResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &invResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &invResp, jsonx.V1()))
invParams, ok := invResp["InventoryRetrievalParameters"].(map[string]any)
require.True(t, ok, "InventoryRetrievalParameters must survive restore: %#v", invResp)
diff --git a/services/glacier/select_output.go b/services/glacier/select_output.go
index def34b2eab..7256b088ee 100644
--- a/services/glacier/select_output.go
+++ b/services/glacier/select_output.go
@@ -27,11 +27,12 @@ package glacier
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"github.com/aws/aws-sdk-go-v2/aws"
s3sdk "github.com/aws/aws-sdk-go-v2/service/s3"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -155,7 +156,7 @@ func writeSelectS3Output(
// the (unreachable in practice, since callers pass only structs/maps of strings)
// marshal error -- a malformed manifest object is still safer than a panic.
func marshalSelectOutputJSON(v any) []byte {
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return []byte("{}")
}
diff --git a/services/glacier/select_output_test.go b/services/glacier/select_output_test.go
index 7fc148e1ef..e7c002bf09 100644
--- a/services/glacier/select_output_test.go
+++ b/services/glacier/select_output_test.go
@@ -2,7 +2,7 @@ package glacier_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glacier"
s3backend "github.com/blackbirdworks/gopherstack/services/s3"
)
@@ -210,7 +211,7 @@ func TestGetJobOutput_Select_S3ErrorManifestOnQueryFailure(t *testing.T) {
"OutputLocation": map[string]any{
"S3": map[string]any{"BucketName": testOutputBucket, "Prefix": "out/"},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
jobID := initiateJobWithBody(t, h, "select-s3-error-vault", string(body))
diff --git a/services/glacier/select_test.go b/services/glacier/select_test.go
index a7c885a340..17d798ef1d 100644
--- a/services/glacier/select_test.go
+++ b/services/glacier/select_test.go
@@ -1,13 +1,15 @@
package glacier_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const selectTestArchive = "1,alice,30\n2,bob,25\n3,carol,40\n"
@@ -31,7 +33,7 @@ func basicSelectBody(archiveID, expression string) string {
"OutputLocation": map[string]any{
"S3": map[string]any{"BucketName": "results-bucket", "Prefix": "out/"},
},
- })
+ }, jsonx.V1())
return string(body)
}
@@ -56,7 +58,7 @@ func TestInitiateJob_Select_Success(t *testing.T) {
assert.Contains(t, gotPathHeader, wantPath)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, gotPathHeader, resp["jobOutputPath"])
assert.NotEmpty(t, resp["jobId"])
}
@@ -208,7 +210,7 @@ func TestInitiateJob_Select_Validation(t *testing.T) {
maps.Copy(payload, tt.overrides)
- body, err := json.Marshal(payload)
+ body, err := json.Marshal(payload, jsonx.V1())
require.NoError(t, err)
rec := doRequest(
@@ -218,7 +220,7 @@ func TestInitiateJob_Select_Validation(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code, rec.Body.String())
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantCode, errResp["code"])
})
}
@@ -237,7 +239,7 @@ func TestInitiateJob_Select_ArchiveNotFound(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code, rec.Body.String())
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp["code"])
}
@@ -336,7 +338,7 @@ func TestGetJobOutput_Select_HeaderNames(t *testing.T) {
"OutputLocation": map[string]any{
"S3": map[string]any{"BucketName": "bucket"},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
jobID := initiateJobWithBody(t, h, "select-header-vault", string(body))
@@ -372,7 +374,7 @@ func TestGetJobOutput_Select_QuoteFieldsAlways(t *testing.T) {
"OutputLocation": map[string]any{
"S3": map[string]any{"BucketName": "bucket"},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
jobID := initiateJobWithBody(t, h, "select-quote-vault", string(body))
@@ -423,7 +425,7 @@ func TestDescribeJob_Select_EchoesParameters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "Select", resp["Action"])
assert.NotEmpty(t, resp["JobOutputPath"])
diff --git a/services/glacier/vault_lock_policy_eval.go b/services/glacier/vault_lock_policy_eval.go
index 3ba465a6a2..ee836c2944 100644
--- a/services/glacier/vault_lock_policy_eval.go
+++ b/services/glacier/vault_lock_policy_eval.go
@@ -1,10 +1,13 @@
package glacier
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Vault Lock and vault access policy evaluation is transcribed from AWS's
@@ -24,7 +27,7 @@ type vaultLockPolicyDocument struct {
type vaultLockPolicyStatement struct {
Condition map[string]map[string]glacierPolicyStringSet `json:"Condition"`
- Principal json.RawMessage `json:"Principal"`
+ Principal jsontext.Value `json:"Principal"`
Effect string `json:"Effect"`
Action glacierPolicyStringSet `json:"Action"`
Resource glacierPolicyStringSet `json:"Resource"`
@@ -35,23 +38,23 @@ type vaultLockPolicyStatement struct {
type glacierPolicyStringSet []string
func (s *glacierPolicyStringSet) UnmarshalJSON(data []byte) error {
- var multi []json.RawMessage
- if err := json.Unmarshal(data, &multi); err != nil {
- multi = []json.RawMessage{data}
+ var multi []jsontext.Value
+ if err := json.Unmarshal(data, &multi, jsonx.V1()); err != nil {
+ multi = []jsontext.Value{data}
}
out := make(glacierPolicyStringSet, 0, len(multi))
for _, raw := range multi {
var str string
- if err := json.Unmarshal(raw, &str); err == nil {
+ if err := json.Unmarshal(raw, &str, jsonx.V1()); err == nil {
out = append(out, str)
continue
}
- var num json.Number
- if err := json.Unmarshal(raw, &num); err != nil {
+ var num jsonx.Number
+ if err := json.Unmarshal(raw, &num, jsonx.V1()); err != nil {
return fmt.Errorf("unsupported policy value %s: %w", raw, err)
}
@@ -78,7 +81,7 @@ const (
// InitiateVaultLock time rather than silently never enforcing anything.
func parseVaultLockPolicyDocument(policy string) (*vaultLockPolicyDocument, error) {
var doc vaultLockPolicyDocument
- if err := json.Unmarshal([]byte(policy), &doc); err != nil {
+ if err := json.Unmarshal([]byte(policy), &doc, jsonx.V1()); err != nil {
return nil, fmt.Errorf("malformed vault lock policy: %w", err)
}
@@ -135,7 +138,7 @@ func evaluateVaultPolicyDeny(policy string, req *policyRequest) (bool, error) {
// principalMatches reports whether a statement's Principal covers the caller. A
// missing Principal is treated as the wildcard.
-func principalMatches(raw json.RawMessage, req *policyRequest) bool {
+func principalMatches(raw jsontext.Value, req *policyRequest) bool {
if len(raw) == 0 {
return true
}
@@ -143,11 +146,11 @@ func principalMatches(raw json.RawMessage, req *policyRequest) bool {
var set glacierPolicyStringSet
var single string
- if json.Unmarshal(raw, &single) == nil {
+ if json.Unmarshal(raw, &single, jsonx.V1()) == nil {
set = glacierPolicyStringSet{single}
} else {
var obj map[string]glacierPolicyStringSet
- if json.Unmarshal(raw, &obj) != nil {
+ if json.Unmarshal(raw, &obj, jsonx.V1()) != nil {
return false
}
diff --git a/services/glacier/writesince_writesites_test.go b/services/glacier/writesince_writesites_test.go
index 4089f8d181..523dd60f26 100644
--- a/services/glacier/writesince_writesites_test.go
+++ b/services/glacier/writesince_writesites_test.go
@@ -1,7 +1,7 @@
package glacier_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glacier"
)
@@ -107,7 +108,7 @@ func TestDeleteVault_WriteSinceLastInventory_CompleteMultipartUpload(t *testing.
require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
var completeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &completeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &completeResp, jsonx.V1()))
archiveID, _ := completeResp["archiveId"].(string)
require.NotEmpty(t, archiveID)
diff --git a/services/globalaccelerator/handler.go b/services/globalaccelerator/handler.go
index fd9443325e..4c270da8df 100644
--- a/services/globalaccelerator/handler.go
+++ b/services/globalaccelerator/handler.go
@@ -2,7 +2,7 @@ package globalaccelerator
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -16,6 +16,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -41,7 +42,7 @@ func bind[I, O any](fn func(*Handler, context.Context, *I) (*O, error)) opFunc {
var in I
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fail(ErrInvalidArgument, "malformed request body: %v", err)
}
}
diff --git a/services/globalaccelerator/handler_test.go b/services/globalaccelerator/handler_test.go
index f0446ed961..cb83d3e9d5 100644
--- a/services/globalaccelerator/handler_test.go
+++ b/services/globalaccelerator/handler_test.go
@@ -1,7 +1,7 @@
package globalaccelerator_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/globalaccelerator"
)
@@ -113,7 +114,7 @@ func TestHandler_ErrorEnvelope(t *testing.T) {
assert.NotEmpty(t, rec.Header().Get("X-Amz-Crc32"))
var got map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, tc.code, got["__type"])
assert.NotEmpty(t, got["message"])
assert.Equal(t, got["message"], got["Message"])
@@ -141,7 +142,7 @@ func TestHandler_EmptyBodyAndResponseShapes(t *testing.T) {
Accelerator map[string]any `json:"Accelerator"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.IsType(t, float64(0), out.Accelerator["CreatedTime"], "timestamps are epoch-seconds numbers")
assert.IsType(t, float64(0), out.Accelerator["LastModifiedTime"])
assert.Equal(t, true, out.Accelerator["Enabled"])
@@ -210,7 +211,7 @@ func TestErrorTable_CoversEverySentinel(t *testing.T) {
require.NoError(t, globalaccelerator.WriteErrorForTest(c, tc.err))
var got map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, tc.code, got["__type"])
})
}
diff --git a/services/globalaccelerator/persistence.go b/services/globalaccelerator/persistence.go
index c25b39eae7..eb869627db 100644
--- a/services/globalaccelerator/persistence.go
+++ b/services/globalaccelerator/persistence.go
@@ -2,7 +2,7 @@ package globalaccelerator
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,11 +12,11 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Seq int64 `json:"seq"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Seq int64 `json:"seq"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/glue/blueprint_include_flags_test.go b/services/glue/blueprint_include_flags_test.go
index c43ac1c685..ea69aca524 100644
--- a/services/glue/blueprint_include_flags_test.go
+++ b/services/glue/blueprint_include_flags_test.go
@@ -1,13 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -65,7 +66,7 @@ func TestGetBlueprint_IncludeFlags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
bp, _ := out["Blueprint"].(map[string]any)
if tt.action == "BatchGetBlueprints" {
diff --git a/services/glue/crawl_run.go b/services/glue/crawl_run.go
index 830955d049..0a2b83900c 100644
--- a/services/glue/crawl_run.go
+++ b/services/glue/crawl_run.go
@@ -1,7 +1,7 @@
package glue
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -11,6 +11,7 @@ import (
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -186,7 +187,7 @@ func configuredMergeNewColumns(cfg string) bool {
} `json:"CrawlerOutput"`
}
- if err := json.Unmarshal([]byte(cfg), &doc); err != nil {
+ if err := json.Unmarshal([]byte(cfg), &doc, jsonx.V1()); err != nil {
return false
}
diff --git a/services/glue/crawler_lake.go b/services/glue/crawler_lake.go
index 5fd96f178c..c70e077482 100644
--- a/services/glue/crawler_lake.go
+++ b/services/glue/crawler_lake.go
@@ -3,7 +3,8 @@ package glue
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -13,6 +14,8 @@ import (
"github.com/aws/aws-sdk-go-v2/aws"
sdk_s3 "github.com/aws/aws-sdk-go-v2/service/s3"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -163,5 +166,5 @@ func moveToPartitionKeys(cols []Column, names []string) ([]Column, []Column) {
}
func decodeJSON(data []byte, v any) error {
- return json.NewDecoder(bytes.NewReader(data)).Decode(v)
+ return json.UnmarshalDecode(jsontext.NewDecoder(bytes.NewReader(data)), v, jsonx.V1())
}
diff --git a/services/glue/crawler_s3.go b/services/glue/crawler_s3.go
index 5fce70e74f..39149ab9ea 100644
--- a/services/glue/crawler_s3.go
+++ b/services/glue/crawler_s3.go
@@ -4,7 +4,8 @@ import (
"bytes"
"context"
"encoding/csv"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"maps"
@@ -18,6 +19,7 @@ import (
"github.com/aws/aws-sdk-go-v2/aws"
sdk_s3 "github.com/aws/aws-sdk-go-v2/service/s3"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -523,40 +525,41 @@ func mergeJSONTypes(a, b string) string {
}
}
-func decodeJSONObject(line []byte) ([]string, []json.RawMessage, bool) {
- dec := json.NewDecoder(bytes.NewReader(line))
- if tok, err := dec.Token(); err != nil || tok != json.Delim('{') {
+func decodeJSONObject(line []byte) ([]string, []jsontext.Value, bool) {
+ dec := jsontext.NewDecoder(bytes.NewReader(line), jsonx.V1())
+ if tok, err := dec.ReadToken(); err != nil || tok.Kind() != '{' {
return nil, nil, false
}
var keys []string
- var vals []json.RawMessage
+ var vals []jsontext.Value
- for dec.More() {
- kt, err := dec.Token()
+ for dec.PeekKind() != '}' {
+ kt, err := dec.ReadToken()
if err != nil {
return nil, nil, false
}
- var raw json.RawMessage
- if err = dec.Decode(&raw); err != nil {
+ key := kt.String()
+
+ raw, err := dec.ReadValue()
+ if err != nil {
return nil, nil, false
}
- keys = append(keys, fmt.Sprint(kt))
- vals = append(vals, raw)
+ keys = append(keys, key)
+ vals = append(vals, raw.Clone())
}
return keys, vals, true
}
-func jsonType(raw json.RawMessage) string {
- dec := json.NewDecoder(bytes.NewReader(raw))
- dec.UseNumber()
+func jsonType(raw jsontext.Value) string {
+ dec := jsontext.NewDecoder(bytes.NewReader(raw))
var v any
- if err := dec.Decode(&v); err != nil {
+ if err := json.UnmarshalDecode(dec, &v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return avroString
}
@@ -569,7 +572,7 @@ func jsonValueType(v any) string {
return ""
case bool:
return avroBoolean
- case json.Number:
+ case jsonx.Number:
if _, err := x.Int64(); err == nil {
return typeBigint
}
diff --git a/services/glue/get_partitions_index_test.go b/services/glue/get_partitions_index_test.go
index bc03b9cad0..1563285533 100644
--- a/services/glue/get_partitions_index_test.go
+++ b/services/glue/get_partitions_index_test.go
@@ -1,13 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -122,7 +123,7 @@ func TestPagination_GetPartitions_RoundTripOrder(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out page
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
for _, p := range out.Partitions {
seen = append(seen, p.Values[0])
diff --git a/services/glue/get_partitions_segment_test.go b/services/glue/get_partitions_segment_test.go
index 8a5b8a780b..ca51a9a6a0 100644
--- a/services/glue/get_partitions_segment_test.go
+++ b/services/glue/get_partitions_segment_test.go
@@ -1,13 +1,15 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestGetPartitions_Segment(t *testing.T) {
@@ -57,7 +59,7 @@ func TestGetPartitions_Segment(t *testing.T) {
var out struct {
Partitions []map[string]any `json:"Partitions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Partitions, tt.wantCount)
})
}
@@ -107,7 +109,7 @@ func TestGetPartitions_ExcludeColumnSchema(t *testing.T) {
} `json:"StorageDescriptor"`
} `json:"Partitions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Partitions, 1)
assert.Len(t, out.Partitions[0].StorageDescriptor.Columns, tt.wantColumns)
assert.Equal(t, "s3://bucket/p", out.Partitions[0].StorageDescriptor.Location)
diff --git a/services/glue/glossary_client_token_test.go b/services/glue/glossary_client_token_test.go
index 1baa212713..eda8759ea8 100644
--- a/services/glue/glossary_client_token_test.go
+++ b/services/glue/glossary_client_token_test.go
@@ -1,12 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCreateGlossary_ClientToken(t *testing.T) {
@@ -34,7 +36,7 @@ func TestCreateGlossary_ClientToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["Id"].(string)
}
@@ -46,7 +48,7 @@ func TestCreateGlossary_ClientToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["Id"].(string)
}
diff --git a/services/glue/handler.go b/services/glue/handler.go
index de6085a5b5..3b480a63b8 100644
--- a/services/glue/handler.go
+++ b/services/glue/handler.go
@@ -2,7 +2,7 @@ package glue
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -175,7 +176,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
ConnectionName string `json:"ConnectionName"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
switch {
case req.ResourceArn != "":
@@ -232,7 +233,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// glueErrorCodes maps every sentinel this package returns to its AWS error
diff --git a/services/glue/handler_blueprints_test.go b/services/glue/handler_blueprints_test.go
index acd16a293b..74a024aecc 100644
--- a/services/glue/handler_blueprints_test.go
+++ b/services/glue/handler_blueprints_test.go
@@ -1,7 +1,7 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/glue/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestBlueprint_CRUD(t *testing.T) {
@@ -368,7 +370,7 @@ func TestBlueprint_Run_Lifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, startBpRec1.Code)
var startBpOut1 map[string]any
- require.NoError(t, json.Unmarshal(startBpRec1.Body.Bytes(), &startBpOut1))
+ require.NoError(t, json.Unmarshal(startBpRec1.Body.Bytes(), &startBpOut1, jsonx.V1()))
assert.NotEmpty(t, startBpOut1["RunId"])
startRec := doGlueRequest(t, h, "StartBlueprintRun", map[string]any{
@@ -377,7 +379,7 @@ func TestBlueprint_Run_Lifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
runID := startOut["RunId"].(string)
getBpRunRec := doGlueRequest(t, h, "GetBlueprintRun", map[string]any{
@@ -390,7 +392,7 @@ func TestBlueprint_Run_Lifecycle(t *testing.T) {
getBpRunsRec := doGlueRequest(t, h, "GetBlueprintRuns", map[string]any{"BlueprintName": "bp-run"})
require.Equal(t, http.StatusOK, getBpRunsRec.Code)
var getBpRunsOut map[string]any
- require.NoError(t, json.Unmarshal(getBpRunsRec.Body.Bytes(), &getBpRunsOut))
+ require.NoError(t, json.Unmarshal(getBpRunsRec.Body.Bytes(), &getBpRunsOut, jsonx.V1()))
bpRuns := getBpRunsOut["BlueprintRuns"].([]any)
assert.GreaterOrEqual(t, len(bpRuns), 1)
}
diff --git a/services/glue/handler_catalogs_test.go b/services/glue/handler_catalogs_test.go
index 8b1aa2ada9..5f777f3492 100644
--- a/services/glue/handler_catalogs_test.go
+++ b/services/glue/handler_catalogs_test.go
@@ -1,13 +1,15 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -19,7 +21,7 @@ func dispatchNewOp(t *testing.T, h *glue.Handler, op string, body any) map[strin
t.Fatalf("op %s: got status %d, body: %s", op, rr.Code, rr.Body.String())
}
var out map[string]any
- if err := json.NewDecoder(rr.Body).Decode(&out); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(rr.Body), &out, jsonx.V1()); err != nil {
t.Fatalf("decode response: %v", err)
}
@@ -202,7 +204,7 @@ func TestCatalogImport_Lifecycle(t *testing.T) {
ImportCompleted bool `json:"ImportCompleted"`
} `json:"ImportStatus"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tc.wantCompleted, out.ImportStatus.ImportCompleted)
if tc.wantCompleted {
@@ -231,7 +233,7 @@ func TestCatalogImport_Idempotent(t *testing.T) {
ImportCompleted bool `json:"ImportCompleted"`
} `json:"ImportStatus"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
assert.True(t, out.ImportStatus.ImportCompleted)
}
@@ -293,7 +295,7 @@ func TestCatalogImportStatus(t *testing.T) {
ImportCompleted bool `json:"ImportCompleted"`
} `json:"ImportStatus"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tc.wantCompleted, out.ImportStatus.ImportCompleted)
if tc.wantCompleted {
diff --git a/services/glue/handler_classifiers_test.go b/services/glue/handler_classifiers_test.go
index 1e2da3a96e..1e30293212 100644
--- a/services/glue/handler_classifiers_test.go
+++ b/services/glue/handler_classifiers_test.go
@@ -1,12 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestClassifier_CRUD(t *testing.T) {
@@ -148,7 +150,7 @@ func TestClassifier_GetClassifiers(t *testing.T) {
rec := doGlueRequest(t, h, "GetClassifiers", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
classifiers := out["Classifiers"].([]any)
assert.Len(t, classifiers, 2)
}
diff --git a/services/glue/handler_column_statistics_test.go b/services/glue/handler_column_statistics_test.go
index 521f3b8e84..408457fc6c 100644
--- a/services/glue/handler_column_statistics_test.go
+++ b/services/glue/handler_column_statistics_test.go
@@ -1,13 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -53,7 +54,7 @@ func TestStopColumnStatisticsTaskRun_NotFound(t *testing.T) {
var out struct {
RunID string `json:"ColumnStatisticsTaskRunId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.RunID, "expected ColumnStatisticsTaskRunId in response")
}
@@ -355,7 +356,7 @@ func TestColumnStatisticsTaskSettings(t *testing.T) {
})
require.Equal(t, http.StatusOK, startCSTRec.Code)
var startCSTOut map[string]any
- require.NoError(t, json.Unmarshal(startCSTRec.Body.Bytes(), &startCSTOut))
+ require.NoError(t, json.Unmarshal(startCSTRec.Body.Bytes(), &startCSTOut, jsonx.V1()))
assert.NotEmpty(t, startCSTOut["ColumnStatisticsTaskRunId"])
doGlueRequest(t, h, "StartColumnStatisticsTaskRun", map[string]any{
@@ -374,7 +375,7 @@ func TestColumnStatisticsTaskSettings(t *testing.T) {
})
require.Equal(t, http.StatusOK, startCSTRec2.Code)
var startCSTOut2 map[string]any
- require.NoError(t, json.Unmarshal(startCSTRec2.Body.Bytes(), &startCSTOut2))
+ require.NoError(t, json.Unmarshal(startCSTRec2.Body.Bytes(), &startCSTOut2, jsonx.V1()))
cstRunID := startCSTOut2["ColumnStatisticsTaskRunId"].(string)
getCSTRunRec := doGlueRequest(t, h, "GetColumnStatisticsTaskRun", map[string]any{
@@ -440,7 +441,7 @@ func TestColumnStatisticsTaskRunSchedule_RequiresExistingSettings(t *testing.T)
} `json:"Schedule"`
} `json:"ColumnStatisticsTaskSettings"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "SCHEDULED", out.ColumnStatisticsTaskSettings.Schedule.State)
rec = doGlueRequest(t, h, "StopColumnStatisticsTaskRunSchedule", map[string]any{
@@ -452,7 +453,7 @@ func TestColumnStatisticsTaskRunSchedule_RequiresExistingSettings(t *testing.T)
"DatabaseName": "db", "TableName": "tbl",
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "NOT_SCHEDULED", out.ColumnStatisticsTaskSettings.Schedule.State)
}
diff --git a/services/glue/handler_connection_types_sdk_test.go b/services/glue/handler_connection_types_sdk_test.go
index 73b8149286..b63bc711b4 100644
--- a/services/glue/handler_connection_types_sdk_test.go
+++ b/services/glue/handler_connection_types_sdk_test.go
@@ -1,7 +1,7 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -121,7 +122,7 @@ func TestConnectionTypeWireShape(t *testing.T) {
SupportedDataOperations []string `json:"SupportedDataOperations"`
} `json:"Capabilities"`
}
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
assert.ElementsMatch(t, []string{"READ", "WRITE"}, out.Capabilities.SupportedDataOperations)
},
},
@@ -133,7 +134,7 @@ func TestConnectionTypeWireShape(t *testing.T) {
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
assert.NotContains(t, out, "Category",
"DescribeConnectionTypeOutput has no Category member on the real wire")
},
@@ -151,7 +152,7 @@ func TestConnectionTypeWireShape(t *testing.T) {
Categories []string `json:"Categories"`
} `json:"ConnectionTypes"`
}
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
found := false
diff --git a/services/glue/handler_connection_types_test.go b/services/glue/handler_connection_types_test.go
index 27fabdd8ce..80fa171b3f 100644
--- a/services/glue/handler_connection_types_test.go
+++ b/services/glue/handler_connection_types_test.go
@@ -1,12 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDeleteConnectionType verifies real state mutation: built-in types are
@@ -75,7 +77,7 @@ func TestDeleteConnectionType_CustomRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, regRec.Code)
var regOut map[string]any
- require.NoError(t, json.Unmarshal(regRec.Body.Bytes(), ®Out))
+ require.NoError(t, json.Unmarshal(regRec.Body.Bytes(), ®Out, jsonx.V1()))
assert.NotEmpty(t, regOut["ConnectionTypeArn"], "RegisterConnectionType must return ConnectionTypeArn")
delRec := doGlueRequest(t, h, "DeleteConnectionType", map[string]any{"ConnectionType": "MyCustomConn"})
diff --git a/services/glue/handler_connections_test.go b/services/glue/handler_connections_test.go
index 9e26a7dfcc..6797de49fe 100644
--- a/services/glue/handler_connections_test.go
+++ b/services/glue/handler_connections_test.go
@@ -1,7 +1,7 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -41,7 +42,7 @@ func TestDescribeEntity_ValidConnection(t *testing.T) {
var out struct {
Fields []any `json:"Fields"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotNil(t, out.Fields)
}
@@ -77,7 +78,7 @@ func TestGetEntityRecords_ValidConnection(t *testing.T) {
NextToken string `json:"NextToken"`
Records []map[string]any `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.Records)
// Each record must conform to the Account schema.
for _, r := range out.Records {
@@ -286,7 +287,7 @@ func TestConnection_NetworkType(t *testing.T) {
getRec := doGlueRequest(t, h, "GetConnection", map[string]any{"Name": "network-conn"})
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
conn := out["Connection"].(map[string]any)
assert.Equal(t, "NETWORK", conn["ConnectionType"])
props := conn["ConnectionProperties"].(map[string]any)
@@ -323,7 +324,7 @@ func TestConnection_CredentialsPersistAfterUpdate(t *testing.T) {
getRec := doGlueRequest(t, h, "GetConnection", map[string]any{"Name": "cred-conn"})
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
conn := out["Connection"].(map[string]any)
props := conn["ConnectionProperties"].(map[string]any)
assert.Equal(t, "user2", props["USERNAME"])
@@ -405,7 +406,7 @@ func TestConnectionCRUD(t *testing.T) {
if tt.check != nil && rec.Code == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tt.check(t, out)
}
})
@@ -455,7 +456,7 @@ func TestConnectionGetFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
conn := out["Connection"].(map[string]any)
assert.Equal(t, tt.wantConnType, conn["ConnectionType"])
if tt.wantCreation {
@@ -500,7 +501,7 @@ func TestGetConnections(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
list, _ := out["ConnectionList"].([]any)
assert.Len(t, list, tt.wantLen)
})
@@ -553,7 +554,7 @@ func TestConnectionTagging(t *testing.T) {
rec := doGlueRequest(t, h, "GetTags", map[string]any{"ResourceArn": connARN})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tt.check(t, out)
}
@@ -567,7 +568,7 @@ func TestConnectionTagging(t *testing.T) {
rec2 := doGlueRequest(t, h, "GetTags", map[string]any{"ResourceArn": connARN})
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
tags, _ := out2["Tags"].(map[string]any)
_, hasFoo := tags["foo"]
assert.False(t, hasFoo)
@@ -684,7 +685,7 @@ func TestUpdateConnection_Updates(t *testing.T) {
ConnectionProperties map[string]string `json:"ConnectionProperties"`
} `json:"Connection"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "jdbc:mysql://newhost:3306/db", out.Connection.ConnectionProperties["JDBC_CONNECTION_URL"])
}
diff --git a/services/glue/handler_crawlers_test.go b/services/glue/handler_crawlers_test.go
index 336d135c6d..11c981e032 100644
--- a/services/glue/handler_crawlers_test.go
+++ b/services/glue/handler_crawlers_test.go
@@ -1,7 +1,7 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -72,7 +73,7 @@ func Test_Crawler_CreateOptions_RoundTrip(t *testing.T) {
} `json:"Targets"`
} `json:"Crawler"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "cron(0 12 * * ? *)", out.Crawler.Schedule.ScheduleExpression)
assert.Equal(t, `{"Version":1.0}`, out.Crawler.Configuration)
@@ -86,7 +87,7 @@ func Test_Crawler_CreateOptions_RoundTrip(t *testing.T) {
cat := out
cat.Crawler.Targets.CatalogTargets = nil
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cat))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cat, jsonx.V1()))
require.Len(t, cat.Crawler.Targets.CatalogTargets, 1)
assert.Equal(t, "db1", cat.Crawler.Targets.CatalogTargets[0].DatabaseName)
@@ -100,7 +101,7 @@ func Test_Crawler_CreateOptions_RoundTrip(t *testing.T) {
rec = doGlueRequest(t, h, "GetCrawler", map[string]any{"Name": "crawler1"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "curated_", out.Crawler.TablePrefix)
}
@@ -265,7 +266,7 @@ func TestCrawler_Lifecycle_States(t *testing.T) {
if tt.wantState != "" && tt.wantCode == http.StatusOK {
getRec := doGlueRequest(t, h, "GetCrawler", map[string]any{"Name": "cr-lifecycle"})
var out map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
cr := out["Crawler"].(map[string]any)
assert.Equal(t, tt.wantState, cr["State"])
}
@@ -412,7 +413,7 @@ func TestHTTPBatchGetCrawlers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
crawlers, ok := out["Crawlers"].([]any)
require.True(t, ok)
assert.Len(t, crawlers, 1)
@@ -453,7 +454,7 @@ func TestListCrawlers(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
names, _ := out["CrawlerNames"].([]any)
assert.Len(t, names, tt.wantLen)
@@ -515,7 +516,7 @@ func TestCrawlerErrors(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantErrorType, out["__type"])
})
}
@@ -582,7 +583,7 @@ func TestCrawlerSchedule(t *testing.T) {
if tt.check != nil && rec.Code == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tt.check(t, out)
}
})
@@ -660,7 +661,7 @@ func TestListCrawls_ReturnsRealCrawlHistory(t *testing.T) {
var before struct {
Crawls []map[string]any `json:"Crawls"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &before))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &before, jsonx.V1()))
assert.Empty(t, before.Crawls, "no crawl has started yet")
rec = doGlueRequest(t, h, "StartCrawler", map[string]any{"Name": "my-crawler"})
@@ -672,7 +673,7 @@ func TestListCrawls_ReturnsRealCrawlHistory(t *testing.T) {
var after struct {
Crawls []map[string]any `json:"Crawls"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &after))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &after, jsonx.V1()))
require.Len(t, after.Crawls, 1)
assert.NotEmpty(t, after.Crawls[0]["CrawlId"])
assert.NotEmpty(t, after.Crawls[0]["StartTime"])
diff --git a/services/glue/handler_custom_entity_types_test.go b/services/glue/handler_custom_entity_types_test.go
index 7ddd30f446..c3db87a363 100644
--- a/services/glue/handler_custom_entity_types_test.go
+++ b/services/glue/handler_custom_entity_types_test.go
@@ -1,13 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -181,7 +182,7 @@ func TestCustomEntityType_RegexAndContextWords_Persist(t *testing.T) {
rec := doGlueRequest(t, h, "GetCustomEntityType", map[string]any{"Name": "CreditCard"})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, `\d{4}-\d{4}-\d{4}-\d{4}`, out["RegexString"])
ctxWords, ok := out["ContextWords"].([]any)
require.True(t, ok)
@@ -271,7 +272,7 @@ func TestGetCustomEntityType_Found(t *testing.T) {
Name string `json:"Name"`
RegexString string `json:"RegexString"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "MY_TYPE", out.Name)
assert.Equal(t, `\d+`, out.RegexString)
}
diff --git a/services/glue/handler_data_quality_rulesets_test.go b/services/glue/handler_data_quality_rulesets_test.go
index ab49f9baa6..8a86a34dc9 100644
--- a/services/glue/handler_data_quality_rulesets_test.go
+++ b/services/glue/handler_data_quality_rulesets_test.go
@@ -1,7 +1,7 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -56,7 +57,7 @@ func TestCancelDQRecommendationRun_NotFound(t *testing.T) {
var out struct {
RunID string `json:"RunId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.RunID, "expected RunId in response")
runID = out.RunID
}
@@ -177,13 +178,13 @@ func TestDataQuality_RuleRecommendationRun(t *testing.T) {
startRec := doGlueRequest(t, h, "StartDataQualityRuleRecommendationRun", map[string]any{})
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
runID := startOut["RunId"].(string)
getRecommendRec := doGlueRequest(t, h, "GetDataQualityRuleRecommendationRun", map[string]any{"RunId": runID})
require.Equal(t, http.StatusOK, getRecommendRec.Code)
var getRecommendOut map[string]any
- require.NoError(t, json.Unmarshal(getRecommendRec.Body.Bytes(), &getRecommendOut))
+ require.NoError(t, json.Unmarshal(getRecommendRec.Body.Bytes(), &getRecommendOut, jsonx.V1()))
assert.NotEmpty(t, getRecommendOut["RunId"])
listRecommendRec := doGlueRequest(t, h, "ListDataQualityRuleRecommendationRuns", map[string]any{})
@@ -243,7 +244,7 @@ func TestDataQualityRulesets(t *testing.T) {
if tt.check != nil && rec.Code == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tt.check(t, out)
}
})
@@ -293,7 +294,7 @@ func TestDataQualityRulesetTagging(t *testing.T) {
if tt.check != nil {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
tt.check(t, out)
}
})
@@ -380,7 +381,7 @@ func TestHandlerDataQuality_GetDataQualityRuleset(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
if !tt.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "my-ruleset", out["Name"])
}
})
@@ -496,7 +497,7 @@ func TestHandlerDataQuality_ListDataQualityRulesets(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
rulesets, _ := out["Rulesets"].([]any)
assert.Len(t, rulesets, tt.wantLen)
})
@@ -540,7 +541,7 @@ func TestHandlerDataQuality_StartDataQualityRulesetEvaluationRun(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
if !tt.wantErr {
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["RunId"])
}
})
@@ -585,7 +586,7 @@ func TestHandlerDataQuality_GetDataQualityRulesetEvaluationRun(t *testing.T) {
)
require.Equal(t, http.StatusOK, startRec.Code)
var out map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &out, jsonx.V1()))
runID = out["RunId"]
}
@@ -594,7 +595,7 @@ func TestHandlerDataQuality_GetDataQualityRulesetEvaluationRun(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
if !tt.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotNil(t, out["RunId"])
}
})
@@ -618,7 +619,7 @@ func TestHandlerDataQuality_BatchGetDataQualityRulesetEvaluationRun(t *testing.T
)
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
runID := startOut["RunId"]
require.NotEmpty(t, runID)
@@ -631,7 +632,7 @@ func TestHandlerDataQuality_BatchGetDataQualityRulesetEvaluationRun(t *testing.T
Runs []map[string]any `json:"Runs"`
RunsNotFound []string `json:"RunsNotFound"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Runs, 1)
assert.Equal(t, runID, out.Runs[0]["RunId"])
assert.Equal(t, []string{"no-such-run"}, out.RunsNotFound)
@@ -671,7 +672,7 @@ func TestHandlerDataQuality_CancelDataQualityRulesetEvaluationRun(t *testing.T)
)
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
runID := startOut["RunId"]
if tt.preCancel {
diff --git a/services/glue/handler_data_quality_stats_test.go b/services/glue/handler_data_quality_stats_test.go
index 8889a62100..a757772d4f 100644
--- a/services/glue/handler_data_quality_stats_test.go
+++ b/services/glue/handler_data_quality_stats_test.go
@@ -1,13 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -23,7 +24,7 @@ func TestDataQualityRecommendationRun(t *testing.T) {
var startOut struct {
RunID string `json:"RunId"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
require.NotEmpty(t, startOut.RunID)
// List
@@ -104,13 +105,13 @@ func TestDataQuality_EvaluationRun_GetAndCancel(t *testing.T) {
)
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
runID := startOut["RunId"].(string)
getEvalRec := doGlueRequest(t, h, "GetDataQualityRulesetEvaluationRun", map[string]any{"RunId": runID})
require.Equal(t, http.StatusOK, getEvalRec.Code)
var getEvalOut map[string]any
- require.NoError(t, json.Unmarshal(getEvalRec.Body.Bytes(), &getEvalOut))
+ require.NoError(t, json.Unmarshal(getEvalRec.Body.Bytes(), &getEvalOut, jsonx.V1()))
assert.Equal(t, "RUNNING", getEvalOut["Status"])
cancelEvalRec := doGlueRequest(t, h, "CancelDataQualityRulesetEvaluationRun", map[string]any{"RunId": runID})
@@ -133,7 +134,7 @@ func TestGetDataQualityModel_RequiresProfileID(t *testing.T) {
var out struct {
Status string `json:"Status"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", out.Status)
}
@@ -155,7 +156,7 @@ func TestGetDataQualityModelResult_RequiresBothIDs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out, "Model")
assert.NotContains(t, out, "Status", "GetDataQualityModelResult has no Status field in the real API")
}
@@ -174,7 +175,7 @@ func TestDataQualityStatisticAnnotations_RealStore(t *testing.T) {
var empty struct {
Annotations []map[string]any `json:"Annotations"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &empty))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &empty, jsonx.V1()))
assert.Empty(t, empty.Annotations)
rec = doGlueRequest(t, h, "PutDataQualityProfileAnnotation", map[string]any{})
@@ -196,7 +197,7 @@ func TestDataQualityStatisticAnnotations_RealStore(t *testing.T) {
var batchOut struct {
FailedInclusionAnnotations []map[string]any `json:"FailedInclusionAnnotations"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &batchOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &batchOut, jsonx.V1()))
require.Len(t, batchOut.FailedInclusionAnnotations, 1)
assert.Equal(t, "stat-2", batchOut.FailedInclusionAnnotations[0]["StatisticId"])
@@ -206,14 +207,14 @@ func TestDataQualityStatisticAnnotations_RealStore(t *testing.T) {
var out struct {
Annotations []map[string]any `json:"Annotations"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Annotations, 2, "the profile-wide and the per-statistic annotation")
rec = doGlueRequest(t, h, "ListDataQualityStatisticAnnotations", map[string]any{
"ProfileId": "profile-1", "StatisticId": "stat-1",
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Annotations, 1)
assert.Equal(t, "stat-1", out.Annotations[0]["StatisticId"])
}
@@ -232,7 +233,7 @@ func TestDataQualityEvaluationRuns_Stateful(t *testing.T) {
var out struct {
Runs []any `json:"Runs"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.Runs)
})
@@ -260,7 +261,7 @@ func TestDataQualityEvaluationRuns_Stateful(t *testing.T) {
var out struct {
Runs []any `json:"Runs"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Runs, 1)
})
}
@@ -279,7 +280,7 @@ func TestListDataQualityResults_Stateful(t *testing.T) {
var out struct {
Results []any `json:"Results"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.Results)
})
@@ -298,7 +299,7 @@ func TestListDataQualityResults_Stateful(t *testing.T) {
var out struct {
Results []any `json:"Results"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Results, 2)
})
}
@@ -351,7 +352,7 @@ func TestDataQualityRecommendationRun_Stateful(t *testing.T) {
var startOut struct {
RunID string `json:"RunId"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
assert.NotEmpty(t, startOut.RunID)
getRec := doGlueRequest(t, h, "GetDataQualityRuleRecommendationRun", map[string]any{
@@ -363,7 +364,7 @@ func TestDataQualityRecommendationRun_Stateful(t *testing.T) {
RunID string `json:"RunId"`
Status string `json:"Status"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, startOut.RunID, getOut.RunID)
assert.NotEmpty(t, getOut.Status)
}
@@ -433,7 +434,7 @@ func TestGetDataQualityResult_Found(t *testing.T) {
ResultID string `json:"ResultId"`
Score float64 `json:"Score"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "result-abc", out.ResultID)
assert.InDelta(t, 0.95, out.Score, 0.001)
}
diff --git a/services/glue/handler_databases_test.go b/services/glue/handler_databases_test.go
index 6e4d77847b..41738babb1 100644
--- a/services/glue/handler_databases_test.go
+++ b/services/glue/handler_databases_test.go
@@ -1,7 +1,7 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -268,7 +269,7 @@ func TestTimestamps(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
resource := out
if tt.resourceKey != "" {
diff --git a/services/glue/handler_dev_endpoints_test.go b/services/glue/handler_dev_endpoints_test.go
index 651a1aeed0..606914b319 100644
--- a/services/glue/handler_dev_endpoints_test.go
+++ b/services/glue/handler_dev_endpoints_test.go
@@ -1,7 +1,7 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -140,7 +141,7 @@ func TestDevEndpoint_UpdateAndGet(t *testing.T) {
getRec := doGlueRequest(t, h, "GetDevEndpoint", map[string]any{"EndpointName": "dep-upd"})
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
dep := out["DevEndpoint"].(map[string]any)
args, ok := dep["Arguments"].(map[string]any)
require.True(t, ok)
@@ -197,7 +198,7 @@ func TestUpdateDevEndpoint_PublicKeys(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
dep := out["DevEndpoint"].(map[string]any)
keysRaw, ok := dep["PublicKeys"].([]any)
require.True(t, ok)
@@ -237,6 +238,6 @@ func TestDevEndpoint_ResourceNumberLimitExceeded(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "ResourceNumberLimitExceededException", out["__type"])
}
diff --git a/services/glue/handler_entities_test.go b/services/glue/handler_entities_test.go
index bd9197dee3..344cad5ac0 100644
--- a/services/glue/handler_entities_test.go
+++ b/services/glue/handler_entities_test.go
@@ -1,13 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -47,7 +48,7 @@ func TestHandler_DescribeEntity_Fields(t *testing.T) {
FieldType string `json:"FieldType"`
} `json:"Fields"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.Fields)
names := make([]string, 0, len(out.Fields))
@@ -129,7 +130,7 @@ func TestHandler_ListEntities(t *testing.T) {
EntityName string `json:"EntityName"`
} `json:"Entities"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.GreaterOrEqual(t, len(out.Entities), tc.wantMin)
})
}
@@ -154,7 +155,7 @@ func TestHandler_GetEntityRecords_Pagination(t *testing.T) {
NextToken string `json:"NextToken"`
Records []map[string]any `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
assert.Len(t, page1.Records, 2)
require.NotEmpty(t, page1.NextToken)
@@ -169,7 +170,7 @@ func TestHandler_GetEntityRecords_Pagination(t *testing.T) {
var page2 struct {
Records []map[string]any `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
require.NotEmpty(t, page2.Records)
assert.NotEqual(t, page1.Records[0]["Id"], page2.Records[0]["Id"])
}
@@ -201,7 +202,7 @@ func TestHandler_ConnectionTypeLifecycle(t *testing.T) {
var regOut struct {
ConnectionTypeArn string `json:"ConnectionTypeArn"`
}
- require.NoError(t, json.Unmarshal(reg.Body.Bytes(), ®Out))
+ require.NoError(t, json.Unmarshal(reg.Body.Bytes(), ®Out, jsonx.V1()))
assert.NotEmpty(t, regOut.ConnectionTypeArn, "RegisterConnectionType must return ConnectionTypeArn")
// It appears in ListConnectionTypes alongside built-ins.
@@ -213,7 +214,7 @@ func TestHandler_ConnectionTypeLifecycle(t *testing.T) {
ConnectionType string `json:"ConnectionType"`
} `json:"ConnectionTypes"`
}
- require.NoError(t, json.Unmarshal(list.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(list.Body.Bytes(), &listOut, jsonx.V1()))
names := make([]string, 0, len(listOut.ConnectionTypes))
for _, ct := range listOut.ConnectionTypes {
@@ -231,7 +232,7 @@ func TestHandler_ConnectionTypeLifecycle(t *testing.T) {
var descOut struct {
RestConfiguration map[string]any `json:"RestConfiguration"`
}
- require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(desc.Body.Bytes(), &descOut, jsonx.V1()))
require.NotNil(t, descOut.RestConfiguration)
validationCfg, _ := descOut.RestConfiguration["ValidationEndpointConfiguration"].(map[string]any)
require.NotNil(t, validationCfg)
@@ -386,7 +387,7 @@ func TestHandler_ListEntities_NativeCatalog(t *testing.T) {
var out struct {
Entities []glue.EntityDescriptor `json:"Entities"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.Entities)
})
@@ -402,7 +403,7 @@ func TestHandler_ListEntities_NativeCatalog(t *testing.T) {
var out struct {
Entities []glue.EntityDescriptor `json:"Entities"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Entities, 1)
assert.Equal(t, "salesdb", out.Entities[0].EntityName)
assert.Equal(t, "DATABASES", out.Entities[0].Category)
@@ -421,7 +422,7 @@ func TestHandler_ListEntities_NativeCatalog(t *testing.T) {
var out struct {
Entities []glue.EntityDescriptor `json:"Entities"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Entities, 1)
assert.Equal(t, "salesdb.orders", out.Entities[0].EntityName)
assert.Equal(t, "TABLES", out.Entities[0].Category)
@@ -451,7 +452,7 @@ func TestHandler_ListEntities_NativeCatalog(t *testing.T) {
var out struct {
Entities []glue.EntityDescriptor `json:"Entities"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
names := make([]string, 0, len(out.Entities))
for _, e := range out.Entities {
@@ -485,7 +486,7 @@ func TestHandler_GetEntityRecords_NativeCatalog(t *testing.T) {
var out struct {
Records []map[string]any `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Records, 3)
for _, r := range out.Records {
@@ -541,7 +542,7 @@ func TestHandler_GetEntityRecords_NativeCatalog(t *testing.T) {
NextToken string `json:"NextToken"`
Records []map[string]any `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
assert.Len(t, page1.Records, 2)
require.NotEmpty(t, page1.NextToken)
@@ -555,7 +556,7 @@ func TestHandler_GetEntityRecords_NativeCatalog(t *testing.T) {
var page2 struct {
Records []map[string]any `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
require.NotEmpty(t, page2.Records)
assert.NotEqual(t, page1.Records[0]["id"], page2.Records[0]["id"])
})
diff --git a/services/glue/handler_etl_test.go b/services/glue/handler_etl_test.go
index 3c3d28944a..5cb7cf391f 100644
--- a/services/glue/handler_etl_test.go
+++ b/services/glue/handler_etl_test.go
@@ -1,12 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestCreateScript verifies that CreateScript generates non-empty ETL code from DagNodes/DagEdges.
@@ -97,7 +99,7 @@ func TestCreateScript(t *testing.T) {
PythonScript string `json:"PythonScript"`
ScalaCode string `json:"ScalaCode"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tc.wantPythonNonEmpty {
assert.NotEmpty(t, out.PythonScript)
@@ -170,7 +172,7 @@ func TestGetDataflowGraph(t *testing.T) {
DagNodes []any `json:"DagNodes"`
DagEdges []any `json:"DagEdges"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.DagNodes, tc.wantNodeCount)
assert.Len(t, out.DagEdges, tc.wantEdgeCount)
})
@@ -213,7 +215,7 @@ func TestGetDataflowGraph_RoundTrip(t *testing.T) {
var scriptOut struct {
PythonScript string `json:"PythonScript"`
}
- require.NoError(t, json.Unmarshal(scriptRec.Body.Bytes(), &scriptOut))
+ require.NoError(t, json.Unmarshal(scriptRec.Body.Bytes(), &scriptOut, jsonx.V1()))
require.NotEmpty(t, scriptOut.PythonScript)
// Parse the script back to a DAG.
@@ -226,7 +228,7 @@ func TestGetDataflowGraph_RoundTrip(t *testing.T) {
DagNodes []any `json:"DagNodes"`
DagEdges []any `json:"DagEdges"`
}
- require.NoError(t, json.Unmarshal(graphRec.Body.Bytes(), &graphOut))
+ require.NoError(t, json.Unmarshal(graphRec.Body.Bytes(), &graphOut, jsonx.V1()))
// Should have recovered 2 nodes (DataSource + DataSink) and 1 edge.
assert.Len(t, graphOut.DagNodes, 2)
@@ -312,7 +314,7 @@ func TestGetMapping_DerivesFromRealSchema(t *testing.T) {
var out struct {
Mapping []map[string]any `json:"Mapping"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Mapping, tc.wantLen)
assert.Equal(t, "id", out.Mapping[0]["SourcePath"])
})
@@ -365,7 +367,7 @@ func TestGetPlan_Languages(t *testing.T) {
PythonScript string `json:"PythonScript"`
ScalaCode string `json:"ScalaCode"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tc.wantPython {
assert.NotEmpty(t, out.PythonScript)
@@ -430,7 +432,7 @@ func TestGetPlan(t *testing.T) {
PythonScript string `json:"PythonScript"`
ScalaCode string `json:"ScalaCode"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tc.wantPython {
assert.NotEmpty(t, out.PythonScript)
diff --git a/services/glue/handler_identity_center_test.go b/services/glue/handler_identity_center_test.go
index 920b6d3da9..1cdced9c1b 100644
--- a/services/glue/handler_identity_center_test.go
+++ b/services/glue/handler_identity_center_test.go
@@ -1,12 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestGlueIdentityCenter(t *testing.T) {
@@ -48,7 +50,7 @@ func TestGlueIdentityCenter_Stateful(t *testing.T) {
var getOut struct {
InstanceArn string `json:"InstanceArn"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "arn:aws:sso:::instance/ssoins-1234", getOut.InstanceArn)
updateRec := doGlueRequest(t, h, "UpdateGlueIdentityCenterConfiguration", map[string]any{
diff --git a/services/glue/handler_integrations_test.go b/services/glue/handler_integrations_test.go
index 6d7a8bc653..c1a13bd091 100644
--- a/services/glue/handler_integrations_test.go
+++ b/services/glue/handler_integrations_test.go
@@ -1,13 +1,15 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDescribeInboundIntegrations verifies integrations are returned from the backend.
@@ -63,7 +65,7 @@ func TestDescribeInboundIntegrations(t *testing.T) {
var out struct {
InboundIntegrations []any `json:"InboundIntegrations"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.GreaterOrEqual(t, len(out.InboundIntegrations), tc.wantMinCount)
})
}
@@ -192,7 +194,7 @@ func TestIntegration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
assert.Equal(t, "my-integration", createOut["IntegrationName"])
assert.Equal(t, "arn:aws:s3:::my-source-bucket", createOut["SourceArn"])
assert.Equal(t, "arn:aws:redshift:us-east-1:123456789012:cluster/my-target", createOut["TargetArn"])
@@ -213,7 +215,7 @@ func TestIntegration(t *testing.T) {
var inboundOut struct {
InboundIntegrations []any `json:"InboundIntegrations"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &inboundOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &inboundOut, jsonx.V1()))
assert.Len(t, inboundOut.InboundIntegrations, 1, "IntegrationArn filter should match the created integration")
// ModifyIntegration, addressed by ARN like a real client would (see
@@ -222,7 +224,7 @@ func TestIntegration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var modifyOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &modifyOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &modifyOut, jsonx.V1()))
assert.Equal(t, "my-integration", modifyOut["IntegrationName"])
assert.Equal(t, integrationARN, modifyOut["IntegrationArn"])
assert.Equal(t, "arn:aws:s3:::my-source-bucket", modifyOut["SourceArn"])
@@ -235,7 +237,7 @@ func TestIntegration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var deleteOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &deleteOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &deleteOut, jsonx.V1()))
assert.Equal(t, "my-integration", deleteOut["IntegrationName"])
assert.Equal(t, integrationARN, deleteOut["IntegrationArn"])
assert.Equal(t, "arn:aws:s3:::my-source-bucket", deleteOut["SourceArn"])
@@ -261,7 +263,7 @@ func TestIntegration_ReachesActive(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
assert.Equal(t, "CREATING", createOut["Status"])
require.Eventually(t, func() bool {
@@ -277,7 +279,7 @@ func TestIntegration_ReachesActive(t *testing.T) {
Status string `json:"Status"`
} `json:"Integrations"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
return len(out.Integrations) == 1 && out.Integrations[0].Status == "ACTIVE"
}, 2*time.Second, 10*time.Millisecond)
@@ -297,7 +299,7 @@ func TestListIntegrationResourceProperties_ReturnsStoredEntries(t *testing.T) {
var empty struct {
IntegrationResourcePropertyList []map[string]any `json:"IntegrationResourcePropertyList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &empty))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &empty, jsonx.V1()))
assert.Empty(t, empty.IntegrationResourcePropertyList)
doGlueRequest(t, h, "CreateIntegrationResourceProperty", map[string]any{
@@ -311,7 +313,7 @@ func TestListIntegrationResourceProperties_ReturnsStoredEntries(t *testing.T) {
var out struct {
IntegrationResourcePropertyList []map[string]any `json:"IntegrationResourcePropertyList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.IntegrationResourcePropertyList, 1)
assert.Equal(t, "arn:aws:glue:us-east-1:123456789012:connection/conn1",
out.IntegrationResourcePropertyList[0]["ResourceArn"])
@@ -348,7 +350,7 @@ func TestUpdateIntegrationResourceProperty_RequiresExistingEntry(t *testing.T) {
var out struct {
SourceProcessingProperties map[string]any `json:"SourceProcessingProperties"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "updated", out.SourceProcessingProperties["key"])
}
@@ -387,7 +389,7 @@ func TestUpdateIntegrationTableProperties_RequiresExistingEntry(t *testing.T) {
var out struct {
SourceTableConfig map[string]string `json:"SourceTableConfig"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "updated", out.SourceTableConfig["key"])
}
diff --git a/services/glue/handler_jobs.go b/services/glue/handler_jobs.go
index e5a8fb4203..4a69daf35e 100644
--- a/services/glue/handler_jobs.go
+++ b/services/glue/handler_jobs.go
@@ -2,7 +2,7 @@ package glue
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
gluetypes "github.com/aws/aws-sdk-go-v2/service/glue/types"
@@ -35,13 +35,13 @@ type createJobInput struct {
// jobExtraMembers are the CreateJob/JobUpdate members shared by both inputs.
type jobExtraMembers struct {
- NonOverridableArguments map[string]string `json:"NonOverridableArguments,omitempty"`
- CodeGenConfigurationNodes map[string]json.RawMessage `json:"CodeGenConfigurationNodes,omitempty"`
- JobRunQueuingEnabled *bool `json:"JobRunQueuingEnabled,omitempty"`
- LogURI string `json:"LogUri,omitempty"`
- SecurityConfiguration string `json:"SecurityConfiguration,omitempty"`
- ExecutionClass string `json:"ExecutionClass,omitempty"`
- MaintenanceWindow string `json:"MaintenanceWindow,omitempty"`
+ NonOverridableArguments map[string]string `json:"NonOverridableArguments,omitempty"`
+ CodeGenConfigurationNodes map[string]jsontext.Value `json:"CodeGenConfigurationNodes,omitempty"`
+ JobRunQueuingEnabled *bool `json:"JobRunQueuingEnabled,omitempty"`
+ LogURI string `json:"LogUri,omitempty"`
+ SecurityConfiguration string `json:"SecurityConfiguration,omitempty"`
+ ExecutionClass string `json:"ExecutionClass,omitempty"`
+ MaintenanceWindow string `json:"MaintenanceWindow,omitempty"`
}
func (m jobExtraMembers) apply(j *Job) {
diff --git a/services/glue/handler_jobs_test.go b/services/glue/handler_jobs_test.go
index 896478fe47..887531f3e6 100644
--- a/services/glue/handler_jobs_test.go
+++ b/services/glue/handler_jobs_test.go
@@ -1,7 +1,7 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -49,7 +50,7 @@ func Test_StartTrigger_OnDemand_StaysCreatedAndFiresJobRun(t *testing.T) {
JobRunState string `json:"JobRunState"`
} `json:"JobRuns"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &runsOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &runsOut, jsonx.V1()))
require.Len(t, runsOut.JobRuns, 1)
assert.NotEmpty(t, runsOut.JobRuns[0].JobRunState)
}
@@ -158,7 +159,7 @@ func Test_Job_MaxCapacity(t *testing.T) {
MaxCapacity float64 `json:"MaxCapacity"`
} `json:"Job"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.InDelta(t, tc.body["MaxCapacity"], out.Job.MaxCapacity, 0.0001)
})
}
@@ -448,7 +449,7 @@ func TestJobLifecycle(t *testing.T) {
if tt.check != nil && rec.Code == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tt.check(t, out)
}
})
@@ -534,7 +535,7 @@ func TestBatchStopJobRun(t *testing.T) {
runRec := doGlueRequest(t, h, "StartJobRun", map[string]any{"JobName": "j"})
var runOut map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &runOut, jsonx.V1()))
runID := runOut["JobRunId"].(string)
doGlueRequest(t, h, "BatchStopJobRun", map[string]any{
@@ -547,7 +548,7 @@ func TestBatchStopJobRun(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
errs, _ := out["Errors"].([]any)
assert.Len(t, errs, tt.wantErrors)
})
@@ -606,7 +607,7 @@ func TestUpdateJobFromSourceControl_ValidatesJob(t *testing.T) {
} `json:"SourceControlDetails"`
} `json:"Job"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "GITHUB", out.Job.SourceControlDetails.Provider)
assert.Equal(t, "my-repo", out.Job.SourceControlDetails.Repository)
assert.Equal(t, "main", out.Job.SourceControlDetails.Branch)
@@ -699,7 +700,7 @@ func TestHandlerJobRuns_StartJobRun(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
if !tt.wantErr {
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["JobRunId"])
}
})
@@ -749,7 +750,7 @@ func TestHandlerJobRuns_GetJobRun(t *testing.T) {
rec := doGlueRequest(t, h, "StartJobRun", map[string]any{"JobName": "my-job"})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
runID = out["JobRunId"]
}
@@ -758,7 +759,7 @@ func TestHandlerJobRuns_GetJobRun(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
if !tt.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotNil(t, out["JobRun"])
}
})
@@ -805,7 +806,7 @@ func TestHandlerJobRuns_GetJobRuns(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
runs, ok := out["JobRuns"].([]any)
require.True(t, ok)
assert.Len(t, runs, tt.numRuns)
@@ -855,7 +856,7 @@ func TestHandlerJobRuns_BatchStopJobRun(t *testing.T) {
startRec := doGlueRequest(t, h, "StartJobRun", map[string]any{"JobName": "my-job"})
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
realRunID := startOut["JobRunId"]
stopIDs := []string{realRunID}
@@ -867,7 +868,7 @@ func TestHandlerJobRuns_BatchStopJobRun(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
errs, _ := out["Errors"].([]any)
assert.Len(t, errs, tt.wantErrors)
})
@@ -910,7 +911,7 @@ func TestHandlerJobRuns_GetJobBookmark(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotNil(t, out["JobBookmarkEntry"])
})
}
diff --git a/services/glue/handler_materialized_views_test.go b/services/glue/handler_materialized_views_test.go
index 19c98be9bf..1df4994549 100644
--- a/services/glue/handler_materialized_views_test.go
+++ b/services/glue/handler_materialized_views_test.go
@@ -1,12 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestStopMaterializedViewRefreshTaskRun_NotFound verifies that
@@ -53,7 +55,7 @@ func TestStopMaterializedViewRefreshTaskRun_NotFound(t *testing.T) {
var out struct {
RunID string `json:"MaterializedViewRefreshTaskRunId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.RunID, "expected MaterializedViewRefreshTaskRunId in response")
}
@@ -99,7 +101,7 @@ func TestMaterializedViewRefreshTaskRun(t *testing.T) {
})
require.Equal(t, http.StatusOK, startRefreshRec1.Code)
var startRefreshOut1 map[string]any
- require.NoError(t, json.Unmarshal(startRefreshRec1.Body.Bytes(), &startRefreshOut1))
+ require.NoError(t, json.Unmarshal(startRefreshRec1.Body.Bytes(), &startRefreshOut1, jsonx.V1()))
assert.NotEmpty(t, startRefreshOut1["MaterializedViewRefreshTaskRunId"])
startRec := doGlueRequest(t, h, "StartMaterializedViewRefreshTaskRun", map[string]any{
@@ -108,7 +110,7 @@ func TestMaterializedViewRefreshTaskRun(t *testing.T) {
})
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
runID := startOut["MaterializedViewRefreshTaskRunId"].(string)
getRefreshRec := doGlueRequest(t, h, "GetMaterializedViewRefreshTaskRun", map[string]any{
@@ -145,7 +147,7 @@ func TestMaterializedViewRefresh_Stateful(t *testing.T) {
var startOut struct {
RunID string `json:"MaterializedViewRefreshTaskRunId"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
assert.NotEmpty(t, startOut.RunID)
getRec := doGlueRequest(t, h, "GetMaterializedViewRefreshTaskRun", map[string]any{
@@ -159,7 +161,7 @@ func TestMaterializedViewRefresh_Stateful(t *testing.T) {
Status string `json:"Status"`
} `json:"MaterializedViewRefreshTaskRun"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, startOut.RunID, getOut.Run.RunID)
assert.Equal(t, "RUNNING", getOut.Run.Status)
@@ -179,7 +181,7 @@ func TestMaterializedViewRefresh_Stateful(t *testing.T) {
Status string `json:"Status"`
} `json:"MaterializedViewRefreshTaskRun"`
}
- require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getOut2))
+ require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getOut2, jsonx.V1()))
assert.Equal(t, "STOPPED", getOut2.Run.Status)
})
diff --git a/services/glue/handler_ml_test.go b/services/glue/handler_ml_test.go
index 9026881424..d83a300070 100644
--- a/services/glue/handler_ml_test.go
+++ b/services/glue/handler_ml_test.go
@@ -1,7 +1,7 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -84,7 +85,7 @@ func TestMLTransform_CRUD(t *testing.T) {
createRec := doGlueRequest(t, h, "CreateMLTransform", createBody)
if createRec.Code == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out, jsonx.V1()))
transformID, _ = out["TransformId"].(string)
}
@@ -106,7 +107,7 @@ func TestMLTransform_Delete(t *testing.T) {
})
require.Equal(t, http.StatusOK, createRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out, jsonx.V1()))
transformID := out["TransformId"].(string)
rec := doGlueRequest(t, h, "DeleteMLTransform", map[string]any{"TransformId": transformID})
@@ -232,7 +233,7 @@ func createTestMLTransform(t *testing.T, h *glue.Handler, name string) string {
var out struct {
TransformID string `json:"TransformId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.TransformID)
return out.TransformID
@@ -296,7 +297,7 @@ func TestMLLabelingSetGenerationTaskRun(t *testing.T) {
var out struct {
TaskRunID string `json:"TaskRunId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.TaskRunID)
}
@@ -337,7 +338,7 @@ func TestMLExportImportLabels(t *testing.T) {
var out struct {
TaskRunID string `json:"TaskRunId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.TaskRunID)
})
}
@@ -380,7 +381,7 @@ func TestGetMLTaskRun(t *testing.T) {
var startOut struct {
TaskRunID string `json:"TaskRunId"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
getRec := doGlueRequest(t, h, "GetMLTaskRun", map[string]any{
"TransformId": transformID,
@@ -393,7 +394,7 @@ func TestGetMLTaskRun(t *testing.T) {
TaskRunID string `json:"TaskRunId"`
Status string `json:"Status"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, transformID, getOut.TransformID)
assert.Equal(t, startOut.TaskRunID, getOut.TaskRunID)
assert.Equal(t, "RUNNING", getOut.Status)
@@ -413,7 +414,7 @@ func TestGetMLTaskRun(t *testing.T) {
var getOut2 struct {
Status string `json:"Status"`
}
- require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getOut2))
+ require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getOut2, jsonx.V1()))
assert.Equal(t, "STOPPED", getOut2.Status)
})
}
@@ -432,7 +433,7 @@ func TestGetMLTaskRuns(t *testing.T) {
var out struct {
TaskRuns []any `json:"TaskRuns"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.TaskRuns)
})
@@ -457,7 +458,7 @@ func TestGetMLTaskRuns(t *testing.T) {
var out struct {
TaskRuns []any `json:"TaskRuns"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.TaskRuns, 3)
})
}
@@ -491,7 +492,7 @@ func TestGetMLTaskRuns_SDKPagination_TotalOrderNoTiesLost(t *testing.T) {
var out struct {
TaskRunID string `json:"TaskRunId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.TaskRunID)
wantIDs[out.TaskRunID] = true
}
@@ -543,7 +544,7 @@ func TestGetMLTaskRuns_SDKFilter_ByStatus(t *testing.T) {
var started struct {
TaskRunID string `json:"TaskRunId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &started))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &started, jsonx.V1()))
cancelRec := doGlueRequest(t, h, "CancelMLTaskRun", map[string]any{
"TransformId": transformID,
@@ -625,7 +626,7 @@ func TestListMLTransforms_Stateful(t *testing.T) {
var out struct {
TransformIDs []string `json:"TransformIds"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.TransformIDs, tc.wantLen)
})
}
diff --git a/services/glue/handler_pagination_test.go b/services/glue/handler_pagination_test.go
index e086978989..fe552b090c 100644
--- a/services/glue/handler_pagination_test.go
+++ b/services/glue/handler_pagination_test.go
@@ -1,12 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestPagination_GetDatabases(t *testing.T) {
@@ -90,7 +92,7 @@ func TestPagination_GetDatabases(t *testing.T) {
NextToken string `json:"NextToken"`
DatabaseList []any `json:"DatabaseList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.DatabaseList, tc.wantCount)
if tc.wantHasNext {
assert.NotEmpty(t, out.NextToken)
@@ -188,7 +190,7 @@ func TestPagination_GetTables(t *testing.T) {
NextToken string `json:"NextToken"`
TableList []any `json:"TableList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.TableList, tc.wantCount)
if tc.wantHasNext {
assert.NotEmpty(t, out.NextToken)
@@ -281,7 +283,7 @@ func TestPagination_GetPartitions(t *testing.T) {
NextToken string `json:"NextToken"`
Partitions []any `json:"Partitions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Partitions, tc.wantCount)
if tc.wantHasNext {
assert.NotEmpty(t, out.NextToken)
diff --git a/services/glue/handler_partition_indexes_test.go b/services/glue/handler_partition_indexes_test.go
index 56b271151a..e1b020a005 100644
--- a/services/glue/handler_partition_indexes_test.go
+++ b/services/glue/handler_partition_indexes_test.go
@@ -1,13 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -120,7 +121,7 @@ func TestPartitionIndexLifecycle(t *testing.T) {
} `json:"Keys"`
} `json:"PartitionIndexDescriptorList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Indexes, 2)
assert.Equal(t, "year_idx", out.Indexes[0].IndexName)
assert.Equal(t, "ACTIVE", out.Indexes[0].IndexStatus)
diff --git a/services/glue/handler_partitions_test.go b/services/glue/handler_partitions_test.go
index 16c0881527..5f1057c7ed 100644
--- a/services/glue/handler_partitions_test.go
+++ b/services/glue/handler_partitions_test.go
@@ -2,7 +2,7 @@ package glue_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -112,7 +113,7 @@ func Test_BatchGetPartition_ReturnsRealPartitions(t *testing.T) {
Values []string `json:"Values"`
} `json:"UnprocessedKeys"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Partitions, 1)
assert.Equal(t, []string{"2024-01-01"}, out.Partitions[0].Values)
@@ -303,7 +304,7 @@ func TestAuditGlue_GetPartitions_Expression(t *testing.T) {
Values []string `json:"Values"`
} `json:"Partitions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
got := make([][]string, len(out.Partitions))
for i, p := range out.Partitions {
@@ -390,7 +391,7 @@ func TestHTTPBatchCreatePartition(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotNil(t, out["Partitions"])
}
@@ -398,7 +399,7 @@ func TestHTTPBatchCreatePartition(t *testing.T) {
func postGlue(t *testing.T, h *glue.Handler, action string, body any) *httptest.ResponseRecorder {
t.Helper()
- bs, err := json.Marshal(body)
+ bs, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bs))
@@ -467,7 +468,7 @@ func TestGetPartition_Found(t *testing.T) {
Values []string `json:"Values"`
} `json:"Partition"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, []string{"2024", "01"}, out.Partition.Values)
}
@@ -503,7 +504,7 @@ func TestGetPartitions_List(t *testing.T) {
Values []string `json:"Values"`
} `json:"Partitions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Partitions, 2)
}
@@ -553,7 +554,7 @@ func TestUpdatePartition_Updates(t *testing.T) {
} `json:"StorageDescriptor"`
} `json:"Partition"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "s3://updated-bucket/tbl3", out.Partition.StorageDescriptor.Location)
}
@@ -608,7 +609,7 @@ func TestBatchUpdatePartition_Updates(t *testing.T) {
var out struct {
Errors []any `json:"Errors"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.Errors)
}
diff --git a/services/glue/handler_resource_limits_test.go b/services/glue/handler_resource_limits_test.go
index 5fe9433883..5ceba77b3b 100644
--- a/services/glue/handler_resource_limits_test.go
+++ b/services/glue/handler_resource_limits_test.go
@@ -1,7 +1,7 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -26,7 +27,7 @@ func assertResourceLimitExceeded(t *testing.T, h *glue.Handler, action string, b
assert.Equal(t, http.StatusBadRequest, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "ResourceNumberLimitExceededException", out["__type"])
}
@@ -327,7 +328,7 @@ func TestResourceNumberLimitExceeded_PartitionsPerTable(t *testing.T) {
} `json:"Errors"`
Partitions []map[string]any `json:"Partitions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Partitions, testResourceCap)
require.Len(t, out.Errors, 1)
diff --git a/services/glue/handler_resource_policies_test.go b/services/glue/handler_resource_policies_test.go
index bc41a2ee10..68fcdf8395 100644
--- a/services/glue/handler_resource_policies_test.go
+++ b/services/glue/handler_resource_policies_test.go
@@ -1,12 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func Test_PutResourcePolicy_ExistsConditions(t *testing.T) {
@@ -58,7 +60,7 @@ func Test_PutResourcePolicy_HashCondition(t *testing.T) {
var out struct {
PolicyHash string `json:"PolicyHash"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.PolicyHash)
// Stale hash -> ConditionCheckFailureException, and the policy is untouched.
@@ -90,7 +92,7 @@ func Test_DeleteResourcePolicy_HashCondition(t *testing.T) {
var out struct {
PolicyHash string `json:"PolicyHash"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// Wrong hash -> rejected, policy stays.
rec = doGlueRequest(t, h, "DeleteResourcePolicy", map[string]any{
@@ -126,7 +128,7 @@ func TestGetResourcePolicies_ReturnsStoredPolicies(t *testing.T) {
var empty struct {
GetResourcePoliciesResponseList []map[string]any `json:"GetResourcePoliciesResponseList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &empty))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &empty, jsonx.V1()))
assert.Empty(t, empty.GetResourcePoliciesResponseList)
doGlueRequest(t, h, "PutResourcePolicy", map[string]any{
@@ -143,7 +145,7 @@ func TestGetResourcePolicies_ReturnsStoredPolicies(t *testing.T) {
var out struct {
GetResourcePoliciesResponseList []map[string]any `json:"GetResourcePoliciesResponseList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.GetResourcePoliciesResponseList, 2)
for _, p := range out.GetResourcePoliciesResponseList {
diff --git a/services/glue/handler_schemas.go b/services/glue/handler_schemas.go
index 3cb0620f32..34d7f691df 100644
--- a/services/glue/handler_schemas.go
+++ b/services/glue/handler_schemas.go
@@ -2,7 +2,7 @@ package glue
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"sort"
@@ -11,6 +11,8 @@ import (
"time"
gluetypes "github.com/aws/aws-sdk-go-v2/service/glue/types"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// validateSchemaDefinition checks a schema definition string against its DataFormat.
@@ -611,7 +613,7 @@ type schemaVersionNumberInput struct {
func (s *schemaVersionNumberInput) UnmarshalJSON(data []byte) error {
var n int64
- if err := json.Unmarshal(data, &n); err == nil {
+ if err := json.Unmarshal(data, &n, jsonx.V1()); err == nil {
s.Number = n
return nil
@@ -621,7 +623,7 @@ func (s *schemaVersionNumberInput) UnmarshalJSON(data []byte) error {
VersionNumber int64 `json:"VersionNumber"`
}
- if err := json.Unmarshal(data, &v); err != nil {
+ if err := json.Unmarshal(data, &v, jsonx.V1()); err != nil {
return err
}
diff --git a/services/glue/handler_schemas_test.go b/services/glue/handler_schemas_test.go
index f11ff40776..c97b28113f 100644
--- a/services/glue/handler_schemas_test.go
+++ b/services/glue/handler_schemas_test.go
@@ -1,13 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -123,7 +124,7 @@ func TestCheckSchemaVersionValidity(t *testing.T) {
Error string `json:"Error"`
Valid bool `json:"Valid"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tc.wantValid, out.Valid)
if tc.wantError != "" {
@@ -187,7 +188,7 @@ func TestDeleteSchemaVersions(t *testing.T) {
VersionNumber int64 `json:"VersionNumber"`
} `json:"SchemaVersionErrors"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.SchemaVersionErrors, tc.wantErrCount)
})
}
@@ -219,7 +220,7 @@ func TestDeleteSchemaVersions_Stateful(t *testing.T) {
VersionNumber int64 `json:"VersionNumber"`
} `json:"SchemaVersionErrors"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// Version 9 doesn't exist → 1 error.
require.Len(t, out.SchemaVersionErrors, 1)
@@ -413,7 +414,7 @@ func TestSchemaRegistry_SchemaVersion_CRUD(t *testing.T) {
})
require.Equal(t, http.StatusOK, registerRec.Code)
var registerOut map[string]any
- require.NoError(t, json.Unmarshal(registerRec.Body.Bytes(), ®isterOut))
+ require.NoError(t, json.Unmarshal(registerRec.Body.Bytes(), ®isterOut, jsonx.V1()))
assert.NotEmpty(t, registerOut["SchemaVersionId"])
getSchemaVersionRec := doGlueRequest(t, h, "GetSchemaVersion", map[string]any{
@@ -422,7 +423,7 @@ func TestSchemaRegistry_SchemaVersion_CRUD(t *testing.T) {
})
require.Equal(t, http.StatusOK, getSchemaVersionRec.Code)
var getSchemaVersionOut map[string]any
- require.NoError(t, json.Unmarshal(getSchemaVersionRec.Body.Bytes(), &getSchemaVersionOut))
+ require.NoError(t, json.Unmarshal(getSchemaVersionRec.Body.Bytes(), &getSchemaVersionOut, jsonx.V1()))
assert.EqualValues(t, 1, getSchemaVersionOut["VersionNumber"])
listSchemaVersionsRec := doGlueRequest(t, h, "ListSchemaVersions", map[string]any{
@@ -437,7 +438,7 @@ func TestSchemaRegistry_SchemaVersion_CRUD(t *testing.T) {
})
require.Equal(t, http.StatusOK, checkValidityRec.Code)
var checkValidityOut map[string]any
- require.NoError(t, json.Unmarshal(checkValidityRec.Body.Bytes(), &checkValidityOut))
+ require.NoError(t, json.Unmarshal(checkValidityRec.Body.Bytes(), &checkValidityOut, jsonx.V1()))
assert.True(t, checkValidityOut["Valid"].(bool))
}
@@ -483,7 +484,7 @@ func TestCreateSchema_WithDefinition_PopulatesVersionFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.InDelta(t, tt.wantLatest, out["LatestSchemaVersion"], 0)
assert.InDelta(t, tt.wantNext, out["NextSchemaVersion"], 0)
@@ -554,7 +555,7 @@ func setupTestSchema(t *testing.T, h *glue.Handler) string {
var sv1Out struct {
SchemaVersionID string `json:"SchemaVersionId"`
}
- require.NoError(t, json.Unmarshal(reg1Rec.Body.Bytes(), &sv1Out))
+ require.NoError(t, json.Unmarshal(reg1Rec.Body.Bytes(), &sv1Out, jsonx.V1()))
reg2Rec := doGlueRequest(t, h, "RegisterSchemaVersion", map[string]any{
"SchemaId": map[string]any{"RegistryName": "reg1", "SchemaName": "schema1"},
@@ -565,7 +566,7 @@ func setupTestSchema(t *testing.T, h *glue.Handler) string {
var sv2Out struct {
SchemaVersionID string `json:"SchemaVersionId"`
}
- require.NoError(t, json.Unmarshal(reg2Rec.Body.Bytes(), &sv2Out))
+ require.NoError(t, json.Unmarshal(reg2Rec.Body.Bytes(), &sv2Out, jsonx.V1()))
return sv1Out.SchemaVersionID
}
@@ -632,7 +633,7 @@ func TestSchemaVersionMetadata_Lifecycle(t *testing.T) {
} `json:"MetadataInfoMap"`
SchemaVersionID string `json:"SchemaVersionId"`
}
- require.NoError(t, json.Unmarshal(queryRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(queryRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, v1ID, out.SchemaVersionID)
got := make(map[string]string, len(out.MetadataInfo))
@@ -659,7 +660,7 @@ func TestSchemaVersionMetadata_EmptyQuery(t *testing.T) {
var out struct {
MetadataInfo map[string]any `json:"MetadataInfoMap"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.MetadataInfo)
}
@@ -714,7 +715,7 @@ func TestGetSchemaByDefinition_Stateful(t *testing.T) {
SchemaVersionID string `json:"SchemaVersionId"`
Status string `json:"Status"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.SchemaVersionID)
assert.Equal(t, "AVAILABLE", out.Status)
}
@@ -764,7 +765,7 @@ func TestGetSchemaVersionsDiff_Stateful(t *testing.T) {
var out struct {
Diff string `json:"Diff"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tc.wantDiff {
assert.NotEmpty(t, out.Diff)
@@ -884,7 +885,7 @@ func TestGetSchemaVersionsDiff(t *testing.T) {
var out struct {
Diff string `json:"Diff"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tc.wantDiff {
assert.NotEmpty(t, out.Diff)
@@ -917,7 +918,7 @@ func TestListRegistries_OmitsGetOnlyFields(t *testing.T) {
var out struct {
Registries []map[string]any `json:"Registries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Registries, 1)
item := out.Registries[0]
@@ -950,7 +951,7 @@ func TestListSchemas_OmitsGetOnlyFields(t *testing.T) {
var out struct {
Schemas []map[string]any `json:"Schemas"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Schemas, 1)
item := out.Schemas[0]
@@ -990,7 +991,7 @@ func TestListSchemaVersions_OmitsGetOnlyFields(t *testing.T) {
var out struct {
Schemas []map[string]any `json:"Schemas"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Schemas, 1)
item := out.Schemas[0]
diff --git a/services/glue/handler_smoke_test.go b/services/glue/handler_smoke_test.go
index 63ef170d4d..8d4e1c97eb 100644
--- a/services/glue/handler_smoke_test.go
+++ b/services/glue/handler_smoke_test.go
@@ -2,7 +2,7 @@ package glue_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,13 +10,14 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
// doGlueOp is a helper to POST a JSON body to the Glue handler via echo.
func doGlueOp(t *testing.T, h *glue.Handler, op string, body any) *httptest.ResponseRecorder {
t.Helper()
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
if err != nil {
t.Fatalf("marshal request: %v", err)
}
diff --git a/services/glue/handler_table_optimizers_test.go b/services/glue/handler_table_optimizers_test.go
index ba5c7a0d33..247c94f20e 100644
--- a/services/glue/handler_table_optimizers_test.go
+++ b/services/glue/handler_table_optimizers_test.go
@@ -1,12 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestListTableOptimizerRuns_ReturnsRealHistory verifies ListTableOptimizerRuns
@@ -50,7 +52,7 @@ func TestListTableOptimizerRuns_ReturnsRealHistory(t *testing.T) {
var out struct {
TableOptimizerRuns []map[string]any `json:"TableOptimizerRuns"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.TableOptimizerRuns, 1)
assert.Equal(t, "completed", out.TableOptimizerRuns[0]["eventType"])
}
diff --git a/services/glue/handler_tables_test.go b/services/glue/handler_tables_test.go
index 00d754b65e..b23a365ebc 100644
--- a/services/glue/handler_tables_test.go
+++ b/services/glue/handler_tables_test.go
@@ -1,13 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -100,7 +101,7 @@ func Test_TableStorageDescriptor_RoundTrip(t *testing.T) {
Retention int `json:"Retention"`
} `json:"Table"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "hive", out.Table.Owner)
assert.Equal(t, 7, out.Table.Retention)
@@ -212,7 +213,7 @@ func TestAuditGlue_GetTables_Expression(t *testing.T) {
Name string `json:"Name"`
} `json:"TableList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
gotNames := make([]string, len(out.TableList))
for i, tbl := range out.TableList {
@@ -253,7 +254,7 @@ func TestHTTPBatchDeleteTable(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
errs, ok := out["Errors"].([]any)
require.True(t, ok)
assert.Empty(t, errs)
@@ -299,7 +300,7 @@ func TestCloneTableWithColumns(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
assert.NotNil(t, tbl)
@@ -356,7 +357,7 @@ func TestSearchTables_EmptyFilter(t *testing.T) {
Name string `json:"Name"`
} `json:"TableList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.TableList, 2)
}
@@ -382,7 +383,7 @@ func TestSearchTables_WithFilter(t *testing.T) {
Name string `json:"Name"`
} `json:"TableList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.TableList, 1)
assert.Equal(t, "orders", out.TableList[0].Name)
}
diff --git a/services/glue/handler_test.go b/services/glue/handler_test.go
index f11b70cd22..f5db9da77e 100644
--- a/services/glue/handler_test.go
+++ b/services/glue/handler_test.go
@@ -2,7 +2,7 @@ package glue_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -43,7 +44,7 @@ func dqEvalRunRequiredFields(rulesetNames []string) map[string]any {
func doGlueRequest(t *testing.T, h *glue.Handler, action string, body any) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -229,7 +230,7 @@ func TestGlue_DatabaseLifecycle(t *testing.T) {
CatalogID string `json:"CatalogId"`
} `json:"Database"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "mydb", getOut.Database.Name)
assert.Equal(t, testAccountID, getOut.Database.CatalogID)
@@ -242,7 +243,7 @@ func TestGlue_DatabaseLifecycle(t *testing.T) {
Name string `json:"Name"`
} `json:"DatabaseList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut.DatabaseList, 1)
// Delete
@@ -289,7 +290,7 @@ func TestGlue_TableLifecycle(t *testing.T) {
Name string `json:"Name"`
} `json:"Table"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "mytable", getOut.Table.Name)
// Update table
@@ -311,7 +312,7 @@ func TestGlue_TableLifecycle(t *testing.T) {
Name string `json:"Name"`
} `json:"TableList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut.TableList, 1)
// Delete table
@@ -364,7 +365,7 @@ func TestGlue_CrawlerLifecycle(t *testing.T) {
State string `json:"State"`
} `json:"Crawler"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "my-crawler", getOut.Crawler.Name)
assert.Equal(t, "READY", getOut.Crawler.State)
@@ -377,7 +378,7 @@ func TestGlue_CrawlerLifecycle(t *testing.T) {
Name string `json:"Name"`
} `json:"Crawlers"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut.Crawlers, 1)
// UpdateCrawler
@@ -420,7 +421,7 @@ func TestGlue_JobLifecycle(t *testing.T) {
var createOut struct {
Name string `json:"Name"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
assert.Equal(t, "my-job", createOut.Name)
// GetJob
@@ -432,7 +433,7 @@ func TestGlue_JobLifecycle(t *testing.T) {
Name string `json:"Name"`
} `json:"Job"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "my-job", getOut.Job.Name)
// GetJobs
@@ -444,7 +445,7 @@ func TestGlue_JobLifecycle(t *testing.T) {
Name string `json:"Name"`
} `json:"Jobs"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut.Jobs, 1)
// UpdateJob
@@ -460,7 +461,7 @@ func TestGlue_JobLifecycle(t *testing.T) {
var updateOut struct {
JobName string `json:"JobName"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateOut, jsonx.V1()))
assert.Equal(t, "my-job", updateOut.JobName)
// DeleteJob
@@ -502,7 +503,7 @@ func TestGlue_Tags(t *testing.T) {
var tagsOut struct {
Tags map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsOut, jsonx.V1()))
assert.Equal(t, "value1", tagsOut.Tags["key1"])
// TagResource
@@ -527,7 +528,7 @@ func TestGlue_Tags(t *testing.T) {
Tags map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &finalTagsOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &finalTagsOut, jsonx.V1()))
assert.NotContains(t, finalTagsOut.Tags, "key1")
assert.Equal(t, "value2", finalTagsOut.Tags["key2"])
}
@@ -635,7 +636,7 @@ func TestGlue_ExtractResource(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -716,7 +717,7 @@ func TestGlue_CrawlerTagsLifecycle(t *testing.T) {
var tagsOut struct {
Tags map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsOut, jsonx.V1()))
assert.Equal(t, "tag", tagsOut.Tags["extra"])
assert.Equal(t, "test", tagsOut.Tags["env"])
@@ -767,7 +768,7 @@ func TestGlue_JobTagsLifecycle(t *testing.T) {
var tagsOut struct {
Tags map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsOut, jsonx.V1()))
assert.Equal(t, "prod", tagsOut.Tags["env"])
assert.Equal(t, "data", tagsOut.Tags["team"])
@@ -882,7 +883,7 @@ func idFromResponse(t *testing.T, body []byte) string {
var out struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
require.NotEmpty(t, out.ID)
return out.ID
@@ -1202,7 +1203,7 @@ func TestGlue_AssociateGlossaryTerms_TableDriven(t *testing.T) {
var assocOut struct {
GlossaryTerms []string `json:"GlossaryTerms"`
}
- require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocOut))
+ require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocOut, jsonx.V1()))
require.Contains(t, assocOut.GlossaryTerms, termID)
return map[string]any{"AssetIdentifier": "asset-2", "GlossaryTermIdentifiers": []string{termID}}
@@ -1213,7 +1214,7 @@ func TestGlue_AssociateGlossaryTerms_TableDriven(t *testing.T) {
var out struct {
GlossaryTerms []string `json:"GlossaryTerms"`
}
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
assert.Empty(t, out.GlossaryTerms)
},
},
@@ -1246,7 +1247,7 @@ func TestGlue_AssociateGlossaryTerms_TableDriven(t *testing.T) {
var out struct {
GlossaryTerms []string `json:"GlossaryTerms"`
}
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
assert.Empty(t, out.GlossaryTerms, "deleted glossary term must be disassociated from the asset")
},
},
diff --git a/services/glue/handler_triggers_test.go b/services/glue/handler_triggers_test.go
index eaad998da5..a38b9cd53d 100644
--- a/services/glue/handler_triggers_test.go
+++ b/services/glue/handler_triggers_test.go
@@ -1,13 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -22,7 +23,7 @@ func triggerState(t *testing.T, h *glue.Handler, name string) string {
State string `json:"State"`
} `json:"Trigger"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.Trigger.State
}
@@ -174,7 +175,7 @@ func TestTrigger_GetTrigger(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
if !tt.wantErr {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
trig, ok := out["Trigger"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "my-trigger", trig["Name"])
@@ -278,7 +279,7 @@ func TestTrigger_StartStopActivate(t *testing.T) {
if tt.wantCode == http.StatusOK && tt.wantState != "" {
get := doGlueRequest(t, h, "GetTrigger", map[string]any{"Name": "t1"})
var out map[string]any
- require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out, jsonx.V1()))
trig := out["Trigger"].(map[string]any)
assert.Equal(t, tt.wantState, trig["State"])
}
@@ -305,7 +306,7 @@ func TestTrigger_BatchGetAndList(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
triggers := out["Triggers"].([]any)
assert.Len(t, triggers, 2)
missing := out["TriggersNotFound"].([]any)
@@ -352,7 +353,7 @@ func TestTrigger_WithActions(t *testing.T) {
getRec := doGlueRequest(t, h, "GetTrigger", map[string]any{"Name": "trig-actions"})
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
trig := out["Trigger"].(map[string]any)
actions := trig["Actions"].([]any)
assert.Len(t, actions, 2)
diff --git a/services/glue/handler_unfiltered_test.go b/services/glue/handler_unfiltered_test.go
index d5e73e56a6..e07b763d15 100644
--- a/services/glue/handler_unfiltered_test.go
+++ b/services/glue/handler_unfiltered_test.go
@@ -1,12 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestGetUnfilteredTableMetadata exercises the GetUnfilteredTableMetadata handler.
@@ -26,7 +28,7 @@ func TestGetUnfilteredTableMetadata(t *testing.T) {
var out struct {
AuthorizedColumns []string `json:"AuthorizedColumns"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotNil(t, out.AuthorizedColumns)
})
@@ -59,7 +61,7 @@ func TestGetUnfilteredTableMetadata(t *testing.T) {
} `json:"Table"`
AuthorizedColumns []string `json:"AuthorizedColumns"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "uftbl", out.Table.Name)
assert.NotNil(t, out.AuthorizedColumns)
})
@@ -79,7 +81,7 @@ func TestGetUnfilteredTableMetadata(t *testing.T) {
var out struct {
IsRegisteredWithLakeFormation bool `json:"IsRegisteredWithLakeFormation"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.False(t, out.IsRegisteredWithLakeFormation)
})
}
@@ -102,7 +104,7 @@ func TestGetUnfilteredPartitionMetadata(t *testing.T) {
var out struct {
AuthorizedColumns []string `json:"AuthorizedColumns"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotNil(t, out.AuthorizedColumns)
})
@@ -141,7 +143,7 @@ func TestGetUnfilteredPartitionMetadata(t *testing.T) {
AuthorizedColumns []string `json:"AuthorizedColumns"`
IsRegisteredWithLakeFormation bool `json:"IsRegisteredWithLakeFormation"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, []string{"2024", "01"}, out.Partition.Values)
assert.NotNil(t, out.AuthorizedColumns)
assert.False(t, out.IsRegisteredWithLakeFormation)
@@ -165,7 +167,7 @@ func TestGetUnfilteredPartitionsMetadata(t *testing.T) {
var out struct {
UnfilteredPartitions []any `json:"UnfilteredPartitions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.UnfilteredPartitions)
})
@@ -195,7 +197,7 @@ func TestGetUnfilteredPartitionsMetadata(t *testing.T) {
var out struct {
UnfilteredPartitions []any `json:"UnfilteredPartitions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.UnfilteredPartitions)
})
@@ -216,7 +218,7 @@ func TestGetUnfilteredPartitionsMetadata(t *testing.T) {
var out struct {
UnfilteredPartitions []any `json:"UnfilteredPartitions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.UnfilteredPartitions, 2)
})
@@ -241,7 +243,7 @@ func TestGetUnfilteredPartitionsMetadata(t *testing.T) {
AuthorizedColumns []string `json:"AuthorizedColumns"`
} `json:"UnfilteredPartitions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.UnfilteredPartitions, 1)
assert.Equal(t, []string{"2024"}, out.UnfilteredPartitions[0].Partition.Values)
assert.NotNil(t, out.UnfilteredPartitions[0].AuthorizedColumns)
@@ -287,7 +289,7 @@ func TestUnfilteredMetadata_IntegrationFlow(t *testing.T) {
Name string `json:"Name"`
} `json:"Table"`
}
- require.NoError(t, json.Unmarshal(tblMetaRec.Body.Bytes(), &tblMeta))
+ require.NoError(t, json.Unmarshal(tblMetaRec.Body.Bytes(), &tblMeta, jsonx.V1()))
assert.Equal(t, "integ-uf-tbl", tblMeta.Table.Name)
// GetUnfilteredPartitionsMetadata lists all partitions.
@@ -300,7 +302,7 @@ func TestUnfilteredMetadata_IntegrationFlow(t *testing.T) {
var partsMeta struct {
UnfilteredPartitions []any `json:"UnfilteredPartitions"`
}
- require.NoError(t, json.Unmarshal(partsMetaRec.Body.Bytes(), &partsMeta))
+ require.NoError(t, json.Unmarshal(partsMetaRec.Body.Bytes(), &partsMeta, jsonx.V1()))
assert.Len(t, partsMeta.UnfilteredPartitions, 1)
// GetUnfilteredPartitionMetadata retrieves the specific partition.
@@ -316,6 +318,6 @@ func TestUnfilteredMetadata_IntegrationFlow(t *testing.T) {
Values []string `json:"Values"`
} `json:"Partition"`
}
- require.NoError(t, json.Unmarshal(partMetaRec.Body.Bytes(), &partMeta))
+ require.NoError(t, json.Unmarshal(partMetaRec.Body.Bytes(), &partMeta, jsonx.V1()))
assert.Equal(t, []string{"2024", "06"}, partMeta.Partition.Values)
}
diff --git a/services/glue/handler_usage_profiles_test.go b/services/glue/handler_usage_profiles_test.go
index 451b071fc3..f0be268bd7 100644
--- a/services/glue/handler_usage_profiles_test.go
+++ b/services/glue/handler_usage_profiles_test.go
@@ -1,12 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDeleteUsageProfile_NotFound verifies that DeleteUsageProfile raises
@@ -203,7 +205,7 @@ func TestGetUsageProfile_FullFields(t *testing.T) {
CreatedOn float64 `json:"CreatedOn"`
LastModifiedOn float64 `json:"LastModifiedOn"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "full-profile", out.Name)
assert.Equal(t, "test description", out.Description)
assert.NotZero(t, out.CreatedOn)
diff --git a/services/glue/handler_user_defined_functions_test.go b/services/glue/handler_user_defined_functions_test.go
index 7352992ed1..4d6d5dd5ea 100644
--- a/services/glue/handler_user_defined_functions_test.go
+++ b/services/glue/handler_user_defined_functions_test.go
@@ -1,7 +1,8 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -139,7 +141,7 @@ func TestGetUserDefinedFunctions_Pattern(t *testing.T) {
FunctionName string `json:"FunctionName"`
} `json:"UserDefinedFunctions"`
}
- require.NoError(t, json.NewDecoder(rr.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rr.Body), &out, jsonx.V1()))
got := make([]string, 0, len(out.UserDefinedFunctions))
for _, u := range out.UserDefinedFunctions {
diff --git a/services/glue/handler_workflows_test.go b/services/glue/handler_workflows_test.go
index fcd4aa65cd..4a1d2bd030 100644
--- a/services/glue/handler_workflows_test.go
+++ b/services/glue/handler_workflows_test.go
@@ -1,7 +1,7 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -74,7 +75,7 @@ func TestWorkflow_CRUD(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
if tt.op == "GetWorkflow" && tt.wantCode == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
wf := out["Workflow"].(map[string]any)
assert.Equal(t, "wf1", wf["Name"])
}
@@ -131,7 +132,7 @@ func TestWorkflow_DeleteAndList(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
wfs := out["Workflows"].([]any)
assert.Len(t, wfs, 2)
missing := out["MissingWorkflows"].([]any)
@@ -150,7 +151,7 @@ func TestWorkflow_RunLifecycle(t *testing.T) {
rec := doGlueRequest(t, h, "StartWorkflowRun", map[string]any{"Name": "wf-run"})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["RunId"])
})
@@ -171,20 +172,20 @@ func TestWorkflow_GetRunAndProperties(t *testing.T) {
startRec := doGlueRequest(t, h, "StartWorkflowRun", map[string]any{"Name": "wf-props"})
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
runID := startOut["RunId"].(string)
getRunRec := doGlueRequest(t, h, "GetWorkflowRun", map[string]any{"Name": "wf-props", "RunId": runID})
require.Equal(t, http.StatusOK, getRunRec.Code)
var getRunOut map[string]any
- require.NoError(t, json.Unmarshal(getRunRec.Body.Bytes(), &getRunOut))
+ require.NoError(t, json.Unmarshal(getRunRec.Body.Bytes(), &getRunOut, jsonx.V1()))
run := getRunOut["Run"].(map[string]any)
assert.Equal(t, runID, run["WorkflowRunId"])
getRunsRec := doGlueRequest(t, h, "GetWorkflowRuns", map[string]any{"Name": "wf-props"})
require.Equal(t, http.StatusOK, getRunsRec.Code)
var getRunsOut map[string]any
- require.NoError(t, json.Unmarshal(getRunsRec.Body.Bytes(), &getRunsOut))
+ require.NoError(t, json.Unmarshal(getRunsRec.Body.Bytes(), &getRunsOut, jsonx.V1()))
runs := getRunsOut["Runs"].([]any)
assert.Len(t, runs, 1)
@@ -206,7 +207,7 @@ func TestWorkflow_GetRunAndProperties(t *testing.T) {
})
require.Equal(t, http.StatusOK, getPropsRec.Code)
var getPropsOut map[string]any
- require.NoError(t, json.Unmarshal(getPropsRec.Body.Bytes(), &getPropsOut))
+ require.NoError(t, json.Unmarshal(getPropsRec.Body.Bytes(), &getPropsOut, jsonx.V1()))
props := getPropsOut["RunProperties"].(map[string]any)
assert.Equal(t, "staging", props["env"])
@@ -235,7 +236,7 @@ func TestWorkflow_UpdateDefaultRunProperties(t *testing.T) {
getRec := doGlueRequest(t, h, "GetWorkflow", map[string]any{"Name": "wf-drp"})
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
wf, ok := out["Workflow"].(map[string]any)
require.True(t, ok)
props, ok := wf["DefaultRunProperties"].(map[string]any)
@@ -351,7 +352,7 @@ func TestResumeWorkflowRun_Stateful(t *testing.T) {
RunID string `json:"RunId"`
NodeIDs []string `json:"NodeIds"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.RunID)
assert.NotEqual(t, runID, out.RunID, "resume must mint a new RunId, not echo the original")
assert.Equal(t, []string{"node-a"}, out.NodeIDs)
@@ -373,7 +374,7 @@ func startedWorkflowRun(t *testing.T, h *glue.Handler, name string) (string, str
var startOut struct {
RunID string `json:"RunId"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
return name, startOut.RunID
}
@@ -407,7 +408,7 @@ func TestResumeWorkflowRun_EchoesRequestedNodes(t *testing.T) {
RunID string `json:"RunId"`
NodeIDs []string `json:"NodeIds"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, []string{"node-a", "node-b"}, out.NodeIDs)
assert.NotEqual(t, runID, out.RunID)
}
@@ -502,7 +503,7 @@ func TestWorkflow_Graph(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
wf := out["Workflow"].(map[string]any)
if !tt.includeGraph {
@@ -547,19 +548,19 @@ func TestWorkflow_LastRun(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotContains(t, out["Workflow"].(map[string]any), "LastRun")
startRec := doGlueRequest(t, h, "StartWorkflowRun", map[string]any{"Name": "graphwf"})
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
runID := startOut["RunId"].(string)
rec = doGlueRequest(t, h, "GetWorkflow", map[string]any{"Name": "graphwf"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
wf := out["Workflow"].(map[string]any)
assert.NotContains(t, wf, "Graph")
@@ -587,7 +588,7 @@ func TestStopWorkflowRun_StateGuard(t *testing.T) {
getRec := doGlueRequest(t, h, "GetWorkflowRun", map[string]any{"Name": wfName, "RunId": runID})
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "RUNNING", getOut["Run"].(map[string]any)["Status"])
stopRec := doGlueRequest(t, h, "StopWorkflowRun", map[string]any{"Name": wfName, "RunId": runID})
@@ -595,20 +596,20 @@ func TestStopWorkflowRun_StateGuard(t *testing.T) {
getRec = doGlueRequest(t, h, "GetWorkflowRun", map[string]any{"Name": wfName, "RunId": runID})
require.Equal(t, http.StatusOK, getRec.Code)
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "STOPPED", getOut["Run"].(map[string]any)["Status"])
// Stopping again must fail: the run is no longer RUNNING.
stopAgainRec := doGlueRequest(t, h, "StopWorkflowRun", map[string]any{"Name": wfName, "RunId": runID})
assert.Equal(t, http.StatusBadRequest, stopAgainRec.Code)
var errOut map[string]string
- require.NoError(t, json.Unmarshal(stopAgainRec.Body.Bytes(), &errOut))
+ require.NoError(t, json.Unmarshal(stopAgainRec.Body.Bytes(), &errOut, jsonx.V1()))
assert.Equal(t, "IllegalWorkflowStateException", errOut["__type"])
// Stopping a nonexistent run is EntityNotFoundException, not the state error.
missingRec := doGlueRequest(t, h, "StopWorkflowRun", map[string]any{"Name": wfName, "RunId": "no-such-run"})
assert.Equal(t, http.StatusBadRequest, missingRec.Code)
- require.NoError(t, json.Unmarshal(missingRec.Body.Bytes(), &errOut))
+ require.NoError(t, json.Unmarshal(missingRec.Body.Bytes(), &errOut, jsonx.V1()))
assert.Equal(t, "EntityNotFoundException", errOut["__type"])
}
@@ -667,7 +668,7 @@ func TestSDKRoundTrip_GetWorkflowRuns_Pagination(t *testing.T) {
var out struct {
RunID string `json:"RunId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
wantIDs[out.RunID] = true
}
require.Len(t, wantIDs, numRuns)
diff --git a/services/glue/models.go b/services/glue/models.go
index 54ce6dec3d..012eef0f24 100644
--- a/services/glue/models.go
+++ b/services/glue/models.go
@@ -1,8 +1,11 @@
package glue
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// DatabaseInput is the input for creating or updating a Glue database.
@@ -359,13 +362,13 @@ type Job struct {
// NonOverridableArguments are job arguments a run cannot override.
NonOverridableArguments map[string]string `json:"NonOverridableArguments,omitempty"`
// CodeGenConfigurationNodes is the visual-job DAG, stored verbatim.
- CodeGenConfigurationNodes map[string]json.RawMessage `json:"CodeGenConfigurationNodes,omitempty"`
- JobRunQueuingEnabled *bool `json:"JobRunQueuingEnabled,omitempty"`
- Command JobCommand `json:"Command,omitzero"`
- LogURI string `json:"LogUri,omitempty"`
- SecurityConfiguration string `json:"SecurityConfiguration,omitempty"`
- ExecutionClass string `json:"ExecutionClass,omitempty"`
- MaintenanceWindow string `json:"MaintenanceWindow,omitempty"`
+ CodeGenConfigurationNodes map[string]jsontext.Value `json:"CodeGenConfigurationNodes,omitempty"`
+ JobRunQueuingEnabled *bool `json:"JobRunQueuingEnabled,omitempty"`
+ Command JobCommand `json:"Command,omitzero"`
+ LogURI string `json:"LogUri,omitempty"`
+ SecurityConfiguration string `json:"SecurityConfiguration,omitempty"`
+ ExecutionClass string `json:"ExecutionClass,omitempty"`
+ MaintenanceWindow string `json:"MaintenanceWindow,omitempty"`
// JobMode describes how the job was created (SCRIPT/VISUAL/NOTEBOOK);
// missing or null defaults to SCRIPT (glue@v1.157.0 api_op_CreateJob.go).
JobMode string `json:"JobMode,omitempty"`
@@ -651,7 +654,7 @@ func (b *JobBookmark) UnmarshalJSON(data []byte) error {
ActiveRun string `json:"ActiveRun,omitempty"`
}{alias: (*alias)(b)}
- if err := json.Unmarshal(data, aux); err != nil {
+ if err := json.Unmarshal(data, aux, jsonx.V1()); err != nil {
return err
}
@@ -926,7 +929,7 @@ func (p *IntegrationResourceProperty) UnmarshalJSON(data []byte) error {
*alias
}{alias: (*alias)(p)}
- if err := json.Unmarshal(data, aux); err != nil {
+ if err := json.Unmarshal(data, aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/glue/multi_region_test.go b/services/glue/multi_region_test.go
index 3259d292f0..c7e76a9e1b 100644
--- a/services/glue/multi_region_test.go
+++ b/services/glue/multi_region_test.go
@@ -1,7 +1,7 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -40,7 +41,7 @@ func regionCall(t *testing.T, h *glue.Handler, region, op, body string) map[stri
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
out := map[string]any{}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/glue/persistence.go b/services/glue/persistence.go
index e33e76525b..615a28885e 100644
--- a/services/glue/persistence.go
+++ b/services/glue/persistence.go
@@ -2,7 +2,7 @@ package glue
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"strings"
@@ -47,7 +47,7 @@ const glueSnapshotVersion = 3
// singletons and JobRun*At timer maps, is not a map[string]*T at all) -- and
// so is still persisted directly, exactly as before this refactor.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
PartitionIndexes map[string]*PartitionIndex `json:"partitionIndexes"`
TableColumnStats map[string]*ColumnStatistics `json:"tableColumnStats"`
PartitionColumnStats map[string]*ColumnStatistics `json:"partitionColumnStats"`
diff --git a/services/glue/persistence_test.go b/services/glue/persistence_test.go
index 344d5b58fc..07b3cf72e9 100644
--- a/services/glue/persistence_test.go
+++ b/services/glue/persistence_test.go
@@ -1,13 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -367,11 +368,11 @@ func TestInMemoryBackend_Restore_VersionMismatch(t *testing.T) {
// Corrupt the version field so Restore hits the mismatch branch, without
// hardcoding the rest of the snapshot's shape.
var generic map[string]any
- require.NoError(t, json.Unmarshal(snap, &generic))
+ require.NoError(t, json.Unmarshal(snap, &generic, jsonx.V1()))
require.Contains(t, generic, "version")
generic["version"] = 999
- mutated, err := json.Marshal(generic)
+ mutated, err := json.Marshal(generic, jsonx.V1())
require.NoError(t, err)
restored := glue.NewInMemoryBackend("123456789012", "us-east-1")
diff --git a/services/glue/policy_shares.go b/services/glue/policy_shares.go
index 5fca62dc3f..8f46ad7094 100644
--- a/services/glue/policy_shares.go
+++ b/services/glue/policy_shares.go
@@ -1,10 +1,12 @@
package glue
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/collections"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// glueAccountIDLen is the number of digits in an AWS account ID.
@@ -34,14 +36,14 @@ type stringOrSlice []string
func (s *stringOrSlice) UnmarshalJSON(data []byte) error {
var str string
- if err := json.Unmarshal(data, &str); err == nil {
+ if err := json.Unmarshal(data, &str, jsonx.V1()); err == nil {
*s = []string{str}
return nil
}
var slice []string
- if err := json.Unmarshal(data, &slice); err != nil {
+ if err := json.Unmarshal(data, &slice, jsonx.V1()); err != nil {
return err
}
@@ -59,19 +61,19 @@ type policyPrincipal struct {
func (p *policyPrincipal) UnmarshalJSON(data []byte) error {
var str string
- if err := json.Unmarshal(data, &str); err == nil {
+ if err := json.Unmarshal(data, &str, jsonx.V1()); err == nil {
p.IsWildcard = str == "*"
return nil
}
- var m map[string]json.RawMessage
- if err := json.Unmarshal(data, &m); err != nil {
+ var m map[string]jsontext.Value
+ if err := json.Unmarshal(data, &m, jsonx.V1()); err != nil {
return err
}
if v, ok := m["AWS"]; ok {
- _ = json.Unmarshal(v, &p.AWS)
+ _ = json.Unmarshal(v, &p.AWS, jsonx.V1())
}
return nil
@@ -112,7 +114,7 @@ func isAllDigits(s string) bool {
// specific principals, so a public ("*") or same-account-only grant creates no share.
func crossAccountGrant(policyJSON, ownerAccountID string) ([]string, []string) {
var doc policyDoc
- if err := json.Unmarshal([]byte(policyJSON), &doc); err != nil {
+ if err := json.Unmarshal([]byte(policyJSON), &doc, jsonx.V1()); err != nil {
return nil, nil
}
diff --git a/services/glue/schema_avro.go b/services/glue/schema_avro.go
index b018904210..8248fb7ebc 100644
--- a/services/glue/schema_avro.go
+++ b/services/glue/schema_avro.go
@@ -1,10 +1,12 @@
package glue
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"slices"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errAvroSchema = errors.New("invalid AVRO schema")
@@ -64,7 +66,7 @@ func avroIsPrimitive(t string) bool {
func parseAvro(def string) (*avroNode, error) {
var v any
- if err := json.Unmarshal([]byte(def), &v); err != nil {
+ if err := json.Unmarshal([]byte(def), &v, jsonx.V1()); err != nil {
return nil, fmt.Errorf("schema is not valid JSON: %w", err)
}
diff --git a/services/glue/schema_json.go b/services/glue/schema_json.go
index 673c0e7829..1ba220af8e 100644
--- a/services/glue/schema_json.go
+++ b/services/glue/schema_json.go
@@ -1,13 +1,15 @@
package glue
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
"reflect"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errJSONSchema = errors.New("invalid JSON schema")
@@ -34,7 +36,7 @@ type jsonSchemaDoc map[string]any
func parseJSONSchema(def string) (jsonSchemaDoc, error) {
var v any
- if err := json.Unmarshal([]byte(def), &v); err != nil {
+ if err := json.Unmarshal([]byte(def), &v, jsonx.V1()); err != nil {
return nil, fmt.Errorf("schema is not valid JSON: %w", err)
}
diff --git a/services/glue/search_tables_sort_test.go b/services/glue/search_tables_sort_test.go
index b6bc5a9beb..61b060b765 100644
--- a/services/glue/search_tables_sort_test.go
+++ b/services/glue/search_tables_sort_test.go
@@ -1,12 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestSearchTables_SortCriteria(t *testing.T) {
@@ -76,7 +78,7 @@ func TestSearchTables_SortCriteria(t *testing.T) {
Name string `json:"Name"`
} `json:"TableList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
got := make([]string, 0, len(out.TableList))
for _, tbl := range out.TableList {
diff --git a/services/glue/session_docker.go b/services/glue/session_docker.go
index a113b7473b..e5f70d5ac1 100644
--- a/services/glue/session_docker.go
+++ b/services/glue/session_docker.go
@@ -3,7 +3,7 @@ package glue
import (
"context"
_ "embed"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"os"
@@ -11,6 +11,8 @@ import (
"strconv"
"sync/atomic"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
//go:embed session_repl.pyrepl
@@ -95,12 +97,12 @@ func (e *dockerSessionEngine) image(glueVersion string) string {
func sparkCommand(name string) bool { return name == "glueetl" || name == "gluestreaming" }
func sessionEnv(spec SessionSpec) ([]string, error) {
- args, err := json.Marshal(spec.DefaultArguments)
+ args, err := json.Marshal(spec.DefaultArguments, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("encode default arguments: %w", err)
}
- conf, err := json.Marshal(parseSparkConf(spec.DefaultArguments["--conf"]))
+ conf, err := json.Marshal(parseSparkConf(spec.DefaultArguments["--conf"]), jsonx.V1())
if err != nil {
return nil, fmt.Errorf("encode spark conf: %w", err)
}
@@ -222,7 +224,7 @@ func (p *dockerSession) awaitReady(ctx context.Context, timeout time.Duration) e
Status string `json:"status"`
Message string `json:"message"`
}
- if json.Unmarshal(data, &r) == nil && r.Status == "ok" {
+ if json.Unmarshal(data, &r, jsonx.V1()) == nil && r.Status == "ok" {
return nil
} else if r.Status == "error" {
return fmt.Errorf("%w: %s", errReplStart, r.Message)
@@ -273,7 +275,7 @@ func (p *dockerSession) Run(ctx context.Context, code string) (StatementResult,
p.seq++
n := p.seq
- body, err := json.Marshal(map[string]string{"code": code})
+ body, err := json.Marshal(map[string]string{"code": code}, jsonx.V1())
if err != nil {
return StatementResult{}, fmt.Errorf("encode statement: %w", err)
}
@@ -306,7 +308,7 @@ func (p *dockerSession) awaitResponse(ctx context.Context, n int) (replResponse,
for {
if data, err := os.ReadFile(path); err == nil {
var r replResponse
- if err = json.Unmarshal(data, &r); err != nil {
+ if err = json.Unmarshal(data, &r, jsonx.V1()); err != nil {
return r, fmt.Errorf("decode interpreter response: %w", err)
}
diff --git a/services/glue/timestamp_wire_shape_test.go b/services/glue/timestamp_wire_shape_test.go
index 94f79c7c86..743d0dbfa0 100644
--- a/services/glue/timestamp_wire_shape_test.go
+++ b/services/glue/timestamp_wire_shape_test.go
@@ -1,12 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestStartedOn_IsEpochSecondsNumber locks down a fix for a systemic bug
@@ -37,7 +39,7 @@ func TestStartedOn_IsEpochSecondsNumber(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
runID, ok := startOut["RunId"].(string)
require.True(t, ok)
@@ -48,7 +50,7 @@ func TestStartedOn_IsEpochSecondsNumber(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
run, ok := getOut["BlueprintRun"].(map[string]any)
require.True(t, ok)
@@ -79,7 +81,7 @@ func TestStartedOn_IsEpochSecondsNumber(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
runID, ok := startOut["ColumnStatisticsTaskRunId"].(string)
require.True(t, ok)
@@ -89,7 +91,7 @@ func TestStartedOn_IsEpochSecondsNumber(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
run, ok := getOut["ColumnStatisticsTaskRun"].(map[string]any)
require.True(t, ok)
diff --git a/services/glue/trigger_conditional_test.go b/services/glue/trigger_conditional_test.go
index 3dcd9465a7..c5fb701f96 100644
--- a/services/glue/trigger_conditional_test.go
+++ b/services/glue/trigger_conditional_test.go
@@ -1,7 +1,7 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"testing/synctest"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -172,7 +173,7 @@ func TestConditionalTrigger_Fires(t *testing.T) {
PredecessorRuns []pred `json:"PredecessorRuns"`
} `json:"JobRun"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
var got []string
for _, p := range out.JobRun.PredecessorRuns {
diff --git a/services/glue/wire_arn_no_leak_test.go b/services/glue/wire_arn_no_leak_test.go
index 95da48ec01..41fbbb70e9 100644
--- a/services/glue/wire_arn_no_leak_test.go
+++ b/services/glue/wire_arn_no_leak_test.go
@@ -1,7 +1,7 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -21,7 +22,7 @@ func assertRawObjectHasNoArn(t *testing.T, body []byte, path ...string) map[stri
t.Helper()
var decoded map[string]any
- require.NoError(t, json.Unmarshal(body, &decoded))
+ require.NoError(t, json.Unmarshal(body, &decoded, jsonx.V1()))
obj := decoded
for i, key := range path {
@@ -50,7 +51,7 @@ func assertNoArnInArray(t *testing.T, body []byte, containerKey string) {
t.Helper()
var decoded map[string]any
- require.NoError(t, json.Unmarshal(body, &decoded))
+ require.NoError(t, json.Unmarshal(body, &decoded, jsonx.V1()))
arr, ok := decoded[containerKey].([]any)
require.Truef(t, ok, "missing array key %q in response", containerKey)
diff --git a/services/glue/wire_field_fixes_test.go b/services/glue/wire_field_fixes_test.go
index a8e4044c95..bb8fb80f0f 100644
--- a/services/glue/wire_field_fixes_test.go
+++ b/services/glue/wire_field_fixes_test.go
@@ -1,7 +1,7 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/glue"
)
@@ -128,7 +129,7 @@ func TestCancelDataQualityRulesetEvaluationRun_StatusIsLegalEnumMember(t *testin
)
require.Equal(t, 200, startRec.Code)
var startOut map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
runID := startOut["RunId"]
cancelRec := doGlueRequest(t, h, "CancelDataQualityRulesetEvaluationRun", map[string]any{"RunId": runID})
@@ -140,7 +141,7 @@ func TestCancelDataQualityRulesetEvaluationRun_StatusIsLegalEnumMember(t *testin
var getOut struct {
Status string `json:"Status"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, string(types.TaskStatusTypeStopped), getOut.Status)
}
diff --git a/services/glue/workflow_run_options_test.go b/services/glue/workflow_run_options_test.go
index 57283d20f8..0a3f96a392 100644
--- a/services/glue/workflow_run_options_test.go
+++ b/services/glue/workflow_run_options_test.go
@@ -1,12 +1,14 @@
package glue_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestStartWorkflowRun_RunProperties(t *testing.T) {
@@ -47,7 +49,7 @@ func TestStartWorkflowRun_RunProperties(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var start map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &start))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &start, jsonx.V1()))
rec = doGlueRequest(t, h, "GetWorkflowRunProperties", map[string]any{
"Name": "wf", "RunId": start["RunId"],
@@ -55,7 +57,7 @@ func TestStartWorkflowRun_RunProperties(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, tt.want, got["RunProperties"])
})
}
@@ -86,7 +88,7 @@ func TestGetWorkflowRun_IncludeGraph(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var start map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &start))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &start, jsonx.V1()))
body := map[string]any{"Name": "graphwf", "IncludeGraph": tt.includeGraph}
if tt.action == "GetWorkflowRun" {
@@ -97,7 +99,7 @@ func TestGetWorkflowRun_IncludeGraph(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
run, _ := out["Run"].(map[string]any)
if tt.action == "GetWorkflowRuns" {
diff --git a/services/grafana/handler.go b/services/grafana/handler.go
index b348173ece..9d3f4692d2 100644
--- a/services/grafana/handler.go
+++ b/services/grafana/handler.go
@@ -2,7 +2,7 @@ package grafana
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -378,7 +379,7 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
status, errType = http.StatusNotFound, "ResourceNotFoundException"
}
- payload, _ := json.Marshal(body)
+ payload, _ := json.Marshal(body, jsonx.V1())
c.Response().Header().Set("X-Amzn-Errortype", errType)
diff --git a/services/grafana/handler_api_keys.go b/services/grafana/handler_api_keys.go
index e7810bbf69..cffd003af5 100644
--- a/services/grafana/handler_api_keys.go
+++ b/services/grafana/handler_api_keys.go
@@ -2,8 +2,10 @@ package grafana
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// createWorkspaceAPIKeyRequest mirrors CreateWorkspaceApiKeyInput's JSON body.
@@ -17,7 +19,7 @@ func (h *Handler) handleCreateWorkspaceAPIKey(_ context.Context, r *http.Request
segs := rawPathSegments(r)
var req createWorkspaceAPIKeyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, validationError("invalid request body: " + err.Error())
}
@@ -30,7 +32,7 @@ func (h *Handler) handleCreateWorkspaceAPIKey(_ context.Context, r *http.Request
"key": key.Key,
"keyName": key.KeyName,
keyWorkspaceID: key.WorkspaceID,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleDeleteWorkspaceAPIKey(_ context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -44,5 +46,5 @@ func (h *Handler) handleDeleteWorkspaceAPIKey(_ context.Context, r *http.Request
return json.Marshal(map[string]any{
"keyName": keyName,
keyWorkspaceID: workspaceID,
- })
+ }, jsonx.V1())
}
diff --git a/services/grafana/handler_authentication.go b/services/grafana/handler_authentication.go
index 8280c044b2..0168993276 100644
--- a/services/grafana/handler_authentication.go
+++ b/services/grafana/handler_authentication.go
@@ -2,8 +2,10 @@ package grafana
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDescribeWorkspaceAuthentication(_ context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -14,14 +16,14 @@ func (h *Handler) handleDescribeWorkspaceAuthentication(_ context.Context, r *ht
return nil, err
}
- return json.Marshal(map[string]any{"authentication": toAuthenticationDescriptionWire(w)})
+ return json.Marshal(map[string]any{"authentication": toAuthenticationDescriptionWire(w)}, jsonx.V1())
}
func (h *Handler) handleUpdateWorkspaceAuthentication(_ context.Context, r *http.Request, body []byte) ([]byte, error) {
segs := rawPathSegments(r)
var req updateWorkspaceAuthenticationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, validationError("invalid request body: " + err.Error())
}
@@ -30,5 +32,5 @@ func (h *Handler) handleUpdateWorkspaceAuthentication(_ context.Context, r *http
return nil, err
}
- return json.Marshal(map[string]any{"authentication": toAuthenticationDescriptionWire(w)})
+ return json.Marshal(map[string]any{"authentication": toAuthenticationDescriptionWire(w)}, jsonx.V1())
}
diff --git a/services/grafana/handler_configuration.go b/services/grafana/handler_configuration.go
index 7fae1d95b0..fe720481b2 100644
--- a/services/grafana/handler_configuration.go
+++ b/services/grafana/handler_configuration.go
@@ -2,8 +2,10 @@ package grafana
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDescribeWorkspaceConfiguration(_ context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -17,14 +19,14 @@ func (h *Handler) handleDescribeWorkspaceConfiguration(_ context.Context, r *htt
return json.Marshal(map[string]any{
"configuration": configuration,
"grafanaVersion": grafanaVersion,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleUpdateWorkspaceConfiguration(_ context.Context, r *http.Request, body []byte) ([]byte, error) {
segs := rawPathSegments(r)
var req updateWorkspaceConfigurationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, validationError("invalid request body: " + err.Error())
}
@@ -32,5 +34,5 @@ func (h *Handler) handleUpdateWorkspaceConfiguration(_ context.Context, r *http.
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
diff --git a/services/grafana/handler_license.go b/services/grafana/handler_license.go
index 4a80cb6095..5b698bf4db 100644
--- a/services/grafana/handler_license.go
+++ b/services/grafana/handler_license.go
@@ -2,8 +2,10 @@ package grafana
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// grafanaTokenHeader is the HTTP header AssociateLicenseInput.GrafanaToken is
@@ -24,7 +26,7 @@ func (h *Handler) handleAssociateLicense(_ context.Context, r *http.Request, _ [
return nil, err
}
- return json.Marshal(map[string]any{keyWorkspace: toWorkspaceDescriptionWire(w)})
+ return json.Marshal(map[string]any{keyWorkspace: toWorkspaceDescriptionWire(w)}, jsonx.V1())
}
func (h *Handler) handleDisassociateLicense(_ context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -36,5 +38,5 @@ func (h *Handler) handleDisassociateLicense(_ context.Context, r *http.Request,
return nil, err
}
- return json.Marshal(map[string]any{keyWorkspace: toWorkspaceDescriptionWire(w)})
+ return json.Marshal(map[string]any{keyWorkspace: toWorkspaceDescriptionWire(w)}, jsonx.V1())
}
diff --git a/services/grafana/handler_permissions.go b/services/grafana/handler_permissions.go
index 8c1018978b..4473dfa450 100644
--- a/services/grafana/handler_permissions.go
+++ b/services/grafana/handler_permissions.go
@@ -2,9 +2,10 @@ package grafana
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -31,14 +32,14 @@ func (h *Handler) handleListPermissions(_ context.Context, r *http.Request, _ []
out["nextToken"] = p.Next
}
- return json.Marshal(out)
+ return json.Marshal(out, jsonx.V1())
}
func (h *Handler) handleUpdatePermissions(ctx context.Context, r *http.Request, body []byte) ([]byte, error) {
segs := rawPathSegments(r)
var req updatePermissionsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, validationError("invalid request body: " + err.Error())
}
@@ -51,5 +52,5 @@ func (h *Handler) handleUpdatePermissions(ctx context.Context, r *http.Request,
errs = []updateErrorWire{}
}
- return json.Marshal(map[string]any{"errors": errs})
+ return json.Marshal(map[string]any{"errors": errs}, jsonx.V1())
}
diff --git a/services/grafana/handler_service_account_tokens.go b/services/grafana/handler_service_account_tokens.go
index 58ec308346..9c4e0ab87d 100644
--- a/services/grafana/handler_service_account_tokens.go
+++ b/services/grafana/handler_service_account_tokens.go
@@ -2,9 +2,10 @@ package grafana
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -22,7 +23,7 @@ func (h *Handler) handleCreateWorkspaceServiceAccountToken(
workspaceID, serviceAccountID := segs[1], segs[3]
var req createWorkspaceServiceAccountTokenRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, validationError("invalid request body: " + err.Error())
}
@@ -39,7 +40,7 @@ func (h *Handler) handleCreateWorkspaceServiceAccountToken(
Name: tok.Name,
Key: tok.Key,
},
- })
+ }, jsonx.V1())
}
func (h *Handler) handleDeleteWorkspaceServiceAccountToken(
@@ -56,7 +57,7 @@ func (h *Handler) handleDeleteWorkspaceServiceAccountToken(
keyServiceAccountID: serviceAccountID,
"tokenId": tokenID,
keyWorkspaceID: workspaceID,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleListWorkspaceServiceAccountTokens(
@@ -89,5 +90,5 @@ func (h *Handler) handleListWorkspaceServiceAccountTokens(
out["nextToken"] = p.Next
}
- return json.Marshal(out)
+ return json.Marshal(out, jsonx.V1())
}
diff --git a/services/grafana/handler_service_accounts.go b/services/grafana/handler_service_accounts.go
index 39e45561f3..de6440d641 100644
--- a/services/grafana/handler_service_accounts.go
+++ b/services/grafana/handler_service_accounts.go
@@ -2,9 +2,10 @@ package grafana
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -19,7 +20,7 @@ func (h *Handler) handleCreateWorkspaceServiceAccount(_ context.Context, r *http
segs := rawPathSegments(r)
var req createWorkspaceServiceAccountRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, validationError("invalid request body: " + err.Error())
}
@@ -33,7 +34,7 @@ func (h *Handler) handleCreateWorkspaceServiceAccount(_ context.Context, r *http
"name": sa.Name,
"grafanaRole": sa.GrafanaRole,
keyWorkspaceID: sa.WorkspaceID,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleDeleteWorkspaceServiceAccount(_ context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -47,7 +48,7 @@ func (h *Handler) handleDeleteWorkspaceServiceAccount(_ context.Context, r *http
return json.Marshal(map[string]any{
keyServiceAccountID: serviceAccountID,
keyWorkspaceID: workspaceID,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleListWorkspaceServiceAccounts(_ context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -74,5 +75,5 @@ func (h *Handler) handleListWorkspaceServiceAccounts(_ context.Context, r *http.
out["nextToken"] = p.Next
}
- return json.Marshal(out)
+ return json.Marshal(out, jsonx.V1())
}
diff --git a/services/grafana/handler_tags.go b/services/grafana/handler_tags.go
index ffaed0350c..4f8ce3c970 100644
--- a/services/grafana/handler_tags.go
+++ b/services/grafana/handler_tags.go
@@ -2,8 +2,10 @@ package grafana
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// tagResourceRequest mirrors TagResourceInput's JSON body.
@@ -20,7 +22,7 @@ func (h *Handler) handleListTagsForResource(_ context.Context, r *http.Request,
return nil, err
}
- return json.Marshal(map[string]any{"tags": tags})
+ return json.Marshal(map[string]any{"tags": tags}, jsonx.V1())
}
func (h *Handler) handleTagResource(ctx context.Context, r *http.Request, body []byte) ([]byte, error) {
@@ -28,7 +30,7 @@ func (h *Handler) handleTagResource(ctx context.Context, r *http.Request, body [
resourceArn := segs[1]
var req tagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, validationError("invalid request body: " + err.Error())
}
@@ -36,7 +38,7 @@ func (h *Handler) handleTagResource(ctx context.Context, r *http.Request, body [
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
func (h *Handler) handleUntagResource(ctx context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -48,5 +50,5 @@ func (h *Handler) handleUntagResource(ctx context.Context, r *http.Request, _ []
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
diff --git a/services/grafana/handler_versions.go b/services/grafana/handler_versions.go
index 56d8221680..659e630094 100644
--- a/services/grafana/handler_versions.go
+++ b/services/grafana/handler_versions.go
@@ -2,9 +2,10 @@ package grafana
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -34,5 +35,5 @@ func (h *Handler) handleListVersions(_ context.Context, r *http.Request, _ []byt
out["nextToken"] = p.Next
}
- return json.Marshal(out)
+ return json.Marshal(out, jsonx.V1())
}
diff --git a/services/grafana/handler_workspaces.go b/services/grafana/handler_workspaces.go
index d84fb08e10..79adfa8f22 100644
--- a/services/grafana/handler_workspaces.go
+++ b/services/grafana/handler_workspaces.go
@@ -2,9 +2,10 @@ package grafana
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -12,7 +13,7 @@ const grafanaDefaultPageSize = 100
func (h *Handler) handleCreateWorkspace(_ context.Context, _ *http.Request, body []byte) ([]byte, error) {
var req createWorkspaceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, validationError("invalid request body: " + err.Error())
}
@@ -21,7 +22,7 @@ func (h *Handler) handleCreateWorkspace(_ context.Context, _ *http.Request, body
return nil, err
}
- return json.Marshal(map[string]any{keyWorkspace: toWorkspaceDescriptionWire(w)})
+ return json.Marshal(map[string]any{keyWorkspace: toWorkspaceDescriptionWire(w)}, jsonx.V1())
}
func (h *Handler) handleDescribeWorkspace(_ context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -32,14 +33,14 @@ func (h *Handler) handleDescribeWorkspace(_ context.Context, r *http.Request, _
return nil, err
}
- return json.Marshal(map[string]any{keyWorkspace: toWorkspaceDescriptionWire(w)})
+ return json.Marshal(map[string]any{keyWorkspace: toWorkspaceDescriptionWire(w)}, jsonx.V1())
}
func (h *Handler) handleUpdateWorkspace(_ context.Context, r *http.Request, body []byte) ([]byte, error) {
segs := rawPathSegments(r)
var req updateWorkspaceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, validationError("invalid request body: " + err.Error())
}
@@ -48,7 +49,7 @@ func (h *Handler) handleUpdateWorkspace(_ context.Context, r *http.Request, body
return nil, err
}
- return json.Marshal(map[string]any{keyWorkspace: toWorkspaceDescriptionWire(w)})
+ return json.Marshal(map[string]any{keyWorkspace: toWorkspaceDescriptionWire(w)}, jsonx.V1())
}
func (h *Handler) handleDeleteWorkspace(_ context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -59,7 +60,7 @@ func (h *Handler) handleDeleteWorkspace(_ context.Context, r *http.Request, _ []
return nil, err
}
- return json.Marshal(map[string]any{keyWorkspace: toWorkspaceDescriptionWire(w)})
+ return json.Marshal(map[string]any{keyWorkspace: toWorkspaceDescriptionWire(w)}, jsonx.V1())
}
func (h *Handler) handleListWorkspaces(_ context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -81,5 +82,5 @@ func (h *Handler) handleListWorkspaces(_ context.Context, r *http.Request, _ []b
out["nextToken"] = p.Next
}
- return json.Marshal(out)
+ return json.Marshal(out, jsonx.V1())
}
diff --git a/services/grafana/persistence.go b/services/grafana/persistence.go
index 8425a6c15e..717c4eb000 100644
--- a/services/grafana/persistence.go
+++ b/services/grafana/persistence.go
@@ -2,7 +2,7 @@ package grafana
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -19,12 +19,12 @@ const grafanaSnapshotVersion = 1
// backendSnapshot is the top-level on-disk shape for the Grafana backend.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountId"`
- Region string `json:"region"`
- Version int `json:"version"`
- NextServiceAccountID uint64 `json:"nextServiceAccountId"`
- NextTokenID uint64 `json:"nextTokenId"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountId"`
+ Region string `json:"region"`
+ Version int `json:"version"`
+ NextServiceAccountID uint64 `json:"nextServiceAccountId"`
+ NextTokenID uint64 `json:"nextTokenId"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/guardduty/coverage_statistics_test.go b/services/guardduty/coverage_statistics_test.go
index f799cec38a..bc1241fafa 100644
--- a/services/guardduty/coverage_statistics_test.go
+++ b/services/guardduty/coverage_statistics_test.go
@@ -1,13 +1,14 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -30,7 +31,7 @@ func TestCoverage(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.NotNil(t, listResp["resources"])
// GetCoverageStatistics requires statisticsType (gopherstack-h910: this
@@ -45,7 +46,7 @@ func TestCoverage(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var byTypeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &byTypeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &byTypeResp, jsonx.V1()))
stats, ok := byTypeResp["coverageStatistics"].(map[string]any)
require.True(t, ok)
assert.Contains(t, stats, "countByResourceType")
@@ -57,7 +58,7 @@ func TestCoverage(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var byStatusResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &byStatusResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &byStatusResp, jsonx.V1()))
stats, ok = byStatusResp["coverageStatistics"].(map[string]any)
require.True(t, ok)
assert.Contains(t, stats, "countByCoverageStatus")
diff --git a/services/guardduty/cross_service_test.go b/services/guardduty/cross_service_test.go
index 2176fbc93f..d68cffab95 100644
--- a/services/guardduty/cross_service_test.go
+++ b/services/guardduty/cross_service_test.go
@@ -1,12 +1,13 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/guardduty"
organizationsbackend "github.com/blackbirdworks/gopherstack/services/organizations"
@@ -97,7 +98,7 @@ func TestMemberOps_AutoEnableOrganizationMembersAll_AllowedAfterAccountLeavesOrg
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Empty(t, out["unprocessedAccounts"], "account should have been processed, not left unprocessed")
})
}
diff --git a/services/guardduty/detectors_test.go b/services/guardduty/detectors_test.go
index 5a041c1cc8..2259eea74e 100644
--- a/services/guardduty/detectors_test.go
+++ b/services/guardduty/detectors_test.go
@@ -1,7 +1,7 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"regexp"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -40,7 +41,7 @@ func TestDetector_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotEmpty(t, resp["detectorId"])
},
},
@@ -54,7 +55,7 @@ func TestDetector_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ENABLED", resp["status"])
assert.NotEmpty(t, resp["serviceRole"])
assert.Equal(t, "SIX_HOURS", resp["findingPublishingFrequency"])
@@ -73,7 +74,7 @@ func TestDetector_Lifecycle(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/detector/"+id, nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "FIFTEEN_MINUTES", resp["findingPublishingFrequency"])
},
},
@@ -90,7 +91,7 @@ func TestDetector_Lifecycle(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/detector/"+id, nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "DISABLED", resp["status"])
},
},
@@ -117,7 +118,7 @@ func TestDetector_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ids, ok := resp["detectorIds"].([]any)
require.True(t, ok)
require.Len(t, ids, 1)
@@ -155,12 +156,12 @@ func TestDetector_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id := resp["detectorId"].(string)
rec = doRequest(t, h, http.MethodGet, "/detector/"+id, nil)
var det map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &det))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &det, jsonx.V1()))
tags, ok := det["tags"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "test", tags["env"])
@@ -218,7 +219,7 @@ func TestDetector_Timestamps_Present(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for _, field := range []string{"createdAt", "updatedAt"} {
raw, ok := resp[field]
@@ -241,7 +242,7 @@ func TestDetector_UpdatedAt_Advances(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var before map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &before))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &before, jsonx.V1()))
createdAt := before["createdAt"].(string)
updatedAt1 := before["updatedAt"].(string)
@@ -256,7 +257,7 @@ func TestDetector_UpdatedAt_Advances(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var after map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &after))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &after, jsonx.V1()))
createdAt2 := after["createdAt"].(string)
updatedAt2 := after["updatedAt"].(string)
@@ -279,7 +280,7 @@ func TestDetector_Tags_EmptyMap_Not_Null(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
raw, exists := resp["tags"]
require.True(t, exists, "GetDetector response must include 'tags' key")
@@ -299,7 +300,7 @@ func TestListDetectors_Empty_State(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
raw, exists := resp["detectorIds"]
require.True(t, exists, "ListDetectors must include 'detectorIds' key")
diff --git a/services/guardduty/entity_sets_test.go b/services/guardduty/entity_sets_test.go
index 88108bde72..da9e2c28de 100644
--- a/services/guardduty/entity_sets_test.go
+++ b/services/guardduty/entity_sets_test.go
@@ -1,13 +1,14 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -36,7 +37,7 @@ func TestThreatEntitySets(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
setID, _ := createResp["threatEntitySetId"].(string)
require.NotEmpty(t, setID)
@@ -45,7 +46,7 @@ func TestThreatEntitySets(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "my-threat-set", getResp["name"])
assert.Equal(t, "ACTIVE", getResp["status"])
@@ -61,7 +62,7 @@ func TestThreatEntitySets(t *testing.T) {
// Verify update
rec = doRequest(t, h, http.MethodGet, "/detector/"+id+"/threatentityset/"+setID, nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "updated-threat-set", getResp["name"])
assert.Equal(t, "INACTIVE", getResp["status"])
@@ -70,7 +71,7 @@ func TestThreatEntitySets(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
ids, _ := listResp["threatEntitySetIds"].([]any)
assert.Len(t, ids, 1)
assert.Equal(t, setID, ids[0])
@@ -81,7 +82,7 @@ func TestThreatEntitySets(t *testing.T) {
// List after delete
rec = doRequest(t, h, http.MethodGet, "/detector/"+id+"/threatentityset", nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
idsAfter, _ := listResp["threatEntitySetIds"].([]any)
assert.Empty(t, idsAfter)
},
@@ -133,7 +134,7 @@ func TestTrustedEntitySets(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
setID, _ := createResp["trustedEntitySetId"].(string)
require.NotEmpty(t, setID)
@@ -142,7 +143,7 @@ func TestTrustedEntitySets(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "my-trusted-set", getResp["name"])
assert.Equal(t, "ACTIVE", getResp["status"])
@@ -156,7 +157,7 @@ func TestTrustedEntitySets(t *testing.T) {
// Verify update
rec = doRequest(t, h, http.MethodGet, "/detector/"+id+"/trustedentityset/"+setID, nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "updated-trusted-set", getResp["name"])
assert.Equal(t, "INACTIVE", getResp["status"])
@@ -165,7 +166,7 @@ func TestTrustedEntitySets(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
ids, _ := listResp["trustedEntitySetIds"].([]any)
assert.Len(t, ids, 1)
assert.Equal(t, setID, ids[0])
@@ -176,7 +177,7 @@ func TestTrustedEntitySets(t *testing.T) {
// List after delete
rec = doRequest(t, h, http.MethodGet, "/detector/"+id+"/trustedentityset", nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
idsAfter, _ := listResp["trustedEntitySetIds"].([]any)
assert.Empty(t, idsAfter)
},
diff --git a/services/guardduty/filters_test.go b/services/guardduty/filters_test.go
index 3449d9e22f..1e71382880 100644
--- a/services/guardduty/filters_test.go
+++ b/services/guardduty/filters_test.go
@@ -1,13 +1,14 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -31,14 +32,14 @@ func TestFilter_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
assert.Equal(t, "my-filter", cr["name"])
rec = doRequest(t, h, http.MethodGet, "/detector/"+detectorID+"/filter/my-filter", nil)
require.Equal(t, http.StatusOK, rec.Code)
var gr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr, jsonx.V1()))
assert.Equal(t, "ARCHIVE", gr["action"])
assert.InDelta(t, 1.0, gr["rank"], 0.01)
assert.Equal(t, "test filter", gr["description"])
@@ -62,7 +63,7 @@ func TestFilter_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/detector/"+detectorID+"/filter/upd-filter", nil)
var gr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr, jsonx.V1()))
assert.Equal(t, "ARCHIVE", gr["action"])
assert.InDelta(t, 2.0, gr["rank"], 0.01)
},
@@ -96,7 +97,7 @@ func TestFilter_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
names, ok := resp["filterNames"].([]any)
require.True(t, ok)
assert.Len(t, names, 3)
@@ -132,7 +133,7 @@ func TestListFilters_Empty_State(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
raw, exists := resp["filterNames"]
require.True(t, exists, "ListFilters must include 'filterNames' key")
@@ -160,7 +161,7 @@ func TestFilter_Tags_EmptyMap_Not_Null(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
raw, exists := resp["tags"]
require.True(t, exists, "GetFilter response must include 'tags' key")
diff --git a/services/guardduty/finding_criteria.go b/services/guardduty/finding_criteria.go
index 7134ff90f9..266d64761b 100644
--- a/services/guardduty/finding_criteria.go
+++ b/services/guardduty/finding_criteria.go
@@ -1,12 +1,14 @@
package guardduty
import (
- "encoding/json"
+ "encoding/json/v2"
"regexp"
"slices"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Condition represents a single finding-criteria condition, matching
@@ -45,13 +47,13 @@ func conditionsFromRaw(raw map[string]any) map[string]Condition {
return nil
}
- data, err := json.Marshal(raw)
+ data, err := json.Marshal(raw, jsonx.V1())
if err != nil {
return nil
}
var fc FindingCriteria
- if unmarshalErr := json.Unmarshal(data, &fc); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(data, &fc, jsonx.V1()); unmarshalErr != nil {
return nil
}
@@ -71,13 +73,13 @@ type SortCriteria struct {
// resolved the same way a real GuardDuty backend would resolve them against
// the finding document.
func findingToFieldMap(f *Finding) map[string]any {
- data, marshalErr := json.Marshal(f)
+ data, marshalErr := json.Marshal(f, jsonx.V1())
if marshalErr != nil {
return map[string]any{}
}
var m map[string]any
- if unmarshalErr := json.Unmarshal(data, &m); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(data, &m, jsonx.V1()); unmarshalErr != nil {
return map[string]any{}
}
diff --git a/services/guardduty/finding_criteria_test.go b/services/guardduty/finding_criteria_test.go
index 05dd1293cd..f2f531c127 100644
--- a/services/guardduty/finding_criteria_test.go
+++ b/services/guardduty/finding_criteria_test.go
@@ -1,13 +1,14 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -20,7 +21,7 @@ func listFindings(t *testing.T, h *guardduty.Handler, detectorID string, body an
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
diff --git a/services/guardduty/finding_statistics_test.go b/services/guardduty/finding_statistics_test.go
index 026eaa1cc0..74429a52fe 100644
--- a/services/guardduty/finding_statistics_test.go
+++ b/services/guardduty/finding_statistics_test.go
@@ -1,12 +1,14 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestGetFindingsStatistics_GroupBy(t *testing.T) {
@@ -67,7 +69,7 @@ func TestGetFindingsStatistics_GroupBy(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fs, ok := resp["findingStatistics"].(map[string]any)
require.True(t, ok)
@@ -109,7 +111,7 @@ func TestGetFindingsStatistics_GroupBy_Unset_ReturnsCountBySeverity(t *testing.T
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fs := resp["findingStatistics"].(map[string]any)
assert.Contains(t, fs, "countBySeverity")
@@ -139,7 +141,7 @@ func TestGetFindingsStatistics_FindingCriteria(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fs := resp["findingStatistics"].(map[string]any)
groups, ok := fs["groupedByFindingType"].([]any)
diff --git a/services/guardduty/findings_test.go b/services/guardduty/findings_test.go
index e0fd0bff5c..20efdb8eff 100644
--- a/services/guardduty/findings_test.go
+++ b/services/guardduty/findings_test.go
@@ -1,7 +1,7 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -32,7 +33,7 @@ func TestFindings(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var lr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lr, jsonx.V1()))
ids, ok := lr["findingIds"].([]any)
require.True(t, ok)
assert.Len(t, ids, 1)
@@ -49,7 +50,7 @@ func TestFindings(t *testing.T) {
// List to get IDs
rec := doRequest(t, h, http.MethodPost, "/detector/"+detectorID+"/findings", nil)
var lr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lr, jsonx.V1()))
ids := lr["findingIds"].([]any)
// Get by ID
@@ -59,7 +60,7 @@ func TestFindings(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var gr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr, jsonx.V1()))
findings, ok := gr["findings"].([]any)
require.True(t, ok)
require.Len(t, findings, 1)
@@ -75,7 +76,7 @@ func TestFindings(t *testing.T) {
rec := doRequest(t, h, http.MethodPost, "/detector/"+detectorID+"/findings", nil)
var lr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lr, jsonx.V1()))
ids := lr["findingIds"].([]any)
fid := ids[0].(string)
@@ -90,7 +91,7 @@ func TestFindings(t *testing.T) {
"findingIds": []string{fid},
})
var gr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr, jsonx.V1()))
findings := gr["findings"].([]any)
f := findings[0].(map[string]any)
svc := f["service"].(map[string]any)
@@ -105,7 +106,7 @@ func TestFindings(t *testing.T) {
rec = doRequest(t, h, http.MethodPost, "/detector/"+detectorID+"/findings/get", map[string]any{
"findingIds": []string{fid},
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr, jsonx.V1()))
findings = gr["findings"].([]any)
f = findings[0].(map[string]any)
svc = f["service"].(map[string]any)
@@ -127,7 +128,7 @@ func TestFindings(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["findingStatistics"])
},
},
@@ -141,7 +142,7 @@ func TestFindings(t *testing.T) {
rec := doRequest(t, h, http.MethodPost, "/detector/"+detectorID+"/findings", nil)
var lr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lr, jsonx.V1()))
ids := lr["findingIds"].([]any)
rec = doRequest(t, h, http.MethodPost, "/detector/"+detectorID+"/findings/feedback", map[string]any{
@@ -173,7 +174,7 @@ func TestListFindings_Empty_State(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
raw, exists := resp["findingIds"]
require.True(t, exists, "ListFindings must include 'findingIds' key")
@@ -195,7 +196,7 @@ func TestGetFindings_Empty_Input(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
raw, exists := resp["findings"]
require.True(t, exists, "GetFindings must include 'findings' key")
@@ -425,7 +426,7 @@ func TestGetFindingsStatistics_Handler(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fs, ok := resp["findingStatistics"].(map[string]any)
require.True(t, ok)
diff --git a/services/guardduty/handler_coverage_statistics.go b/services/guardduty/handler_coverage_statistics.go
index ee29e46cb5..3a12dd45be 100644
--- a/services/guardduty/handler_coverage_statistics.go
+++ b/services/guardduty/handler_coverage_statistics.go
@@ -1,8 +1,10 @@
package guardduty
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleGetCoverageStatistics(detectorID string, body []byte) (any, int, error) {
@@ -11,7 +13,7 @@ func (h *Handler) handleGetCoverageStatistics(detectorID string, body []byte) (a
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
@@ -35,7 +37,7 @@ func (h *Handler) handleListCoverage(detectorID string, body []byte) (any, int,
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
diff --git a/services/guardduty/handler_detectors.go b/services/guardduty/handler_detectors.go
index f3d49e70b6..6b6486f787 100644
--- a/services/guardduty/handler_detectors.go
+++ b/services/guardduty/handler_detectors.go
@@ -1,8 +1,10 @@
package guardduty
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) dispatchDetectorOps(op, path, query string, body []byte) (any, int, bool, error) {
@@ -49,7 +51,7 @@ func (h *Handler) handleCreateDetector(body []byte) (any, int, error) {
Features []DetectorFeature `json:"features"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -105,7 +107,7 @@ func (h *Handler) handleUpdateDetector(detectorID string, body []byte) (int, err
Features []DetectorFeature `json:"features"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
diff --git a/services/guardduty/handler_entity_sets.go b/services/guardduty/handler_entity_sets.go
index 52b81316b7..ae4e515b69 100644
--- a/services/guardduty/handler_entity_sets.go
+++ b/services/guardduty/handler_entity_sets.go
@@ -1,10 +1,11 @@
package guardduty
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) dispatchEntitySetOps(op, path, query string, body []byte) (any, int, bool, error) {
@@ -87,7 +88,7 @@ func (h *Handler) handleCreateThreatEntitySet( //nolint:dupl // existing issue.
ClientToken string `json:"clientToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -176,7 +177,7 @@ func (h *Handler) handleUpdateThreatEntitySet(detectorID, setID string, body []b
ExpectedBucketOwner string `json:"expectedBucketOwner"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -212,7 +213,7 @@ func (h *Handler) handleCreateTrustedEntitySet( //nolint:dupl // existing issue.
ClientToken string `json:"clientToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -298,7 +299,7 @@ func (h *Handler) handleUpdateTrustedEntitySet(detectorID, setID string, body []
ExpectedBucketOwner string `json:"expectedBucketOwner"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
diff --git a/services/guardduty/handler_filters.go b/services/guardduty/handler_filters.go
index d02fceb97b..402cb3270e 100644
--- a/services/guardduty/handler_filters.go
+++ b/services/guardduty/handler_filters.go
@@ -1,10 +1,11 @@
package guardduty
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) dispatchFilterOps(op, path, query string, body []byte) (any, int, bool, error) {
@@ -54,7 +55,7 @@ func (h *Handler) handleCreateFilter(detectorID string, body []byte) (any, int,
Rank int32 `json:"rank"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -122,7 +123,7 @@ func (h *Handler) handleUpdateFilter(detectorID, filterName string, body []byte)
Rank int32 `json:"rank"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
diff --git a/services/guardduty/handler_findings.go b/services/guardduty/handler_findings.go
index afc5307b33..10b05f6d92 100644
--- a/services/guardduty/handler_findings.go
+++ b/services/guardduty/handler_findings.go
@@ -1,8 +1,10 @@
package guardduty
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) dispatchFindingOps(op, path string, body []byte) (any, int, bool, error) {
@@ -54,7 +56,7 @@ func (h *Handler) handleGetFindings(detectorID string, body []byte) (any, int, e
FindingIDs []string `json:"findingIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -79,7 +81,7 @@ func (h *Handler) handleListFindings(detectorID string, body []byte) (any, int,
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
@@ -116,7 +118,7 @@ func (h *Handler) handleArchiveFindings(detectorID string, body []byte) (int, er
FindingIDs []string `json:"findingIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -132,7 +134,7 @@ func (h *Handler) handleUnarchiveFindings(detectorID string, body []byte) (int,
FindingIDs []string `json:"findingIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -149,7 +151,7 @@ func (h *Handler) handleCreateSampleFindings(detectorID string, body []byte) (in
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
}
@@ -170,7 +172,7 @@ func (h *Handler) handleGetFindingsStatistics(detectorID string, body []byte) (a
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
@@ -195,7 +197,7 @@ func (h *Handler) handleUpdateFindingsFeedback(detectorID string, body []byte) (
FindingIDs []string `json:"findingIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
diff --git a/services/guardduty/handler_investigations.go b/services/guardduty/handler_investigations.go
index 61388fbc18..e6a40ba0d6 100644
--- a/services/guardduty/handler_investigations.go
+++ b/services/guardduty/handler_investigations.go
@@ -1,10 +1,11 @@
package guardduty
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) dispatchInvestigationOps(op, path string, body []byte) (any, int, bool, error) {
@@ -37,7 +38,7 @@ func (h *Handler) handleCreateInvestigation(detectorID string, body []byte) (any
ClientToken string `json:"clientToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -80,7 +81,7 @@ func (h *Handler) handleListInvestigations(detectorID string, body []byte) (any,
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
diff --git a/services/guardduty/handler_ip_and_threatintel_sets.go b/services/guardduty/handler_ip_and_threatintel_sets.go
index 5f58e7e2d2..93f6a31efb 100644
--- a/services/guardduty/handler_ip_and_threatintel_sets.go
+++ b/services/guardduty/handler_ip_and_threatintel_sets.go
@@ -1,9 +1,11 @@
package guardduty
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// validSetFormats are the enum values IpSetFormat and ThreatIntelSetFormat
@@ -60,7 +62,7 @@ func (h *Handler) handleCreateIPSet(detectorID string, body []byte) (any, int, e
ClientToken string `json:"clientToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -126,7 +128,7 @@ func (h *Handler) handleUpdateIPSet(detectorID, ipSetID string, body []byte) (in
ExpectedBucketOwner string `json:"expectedBucketOwner"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -210,7 +212,7 @@ func (h *Handler) handleCreateThreatIntelSet(detectorID string, body []byte) (an
ClientToken string `json:"clientToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -276,7 +278,7 @@ func (h *Handler) handleUpdateThreatIntelSet(detectorID, setID string, body []by
ExpectedBucketOwner string `json:"expectedBucketOwner"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
diff --git a/services/guardduty/handler_ip_and_threatintel_sets_test.go b/services/guardduty/handler_ip_and_threatintel_sets_test.go
index eb264ecfe3..ee8744b8d9 100644
--- a/services/guardduty/handler_ip_and_threatintel_sets_test.go
+++ b/services/guardduty/handler_ip_and_threatintel_sets_test.go
@@ -1,12 +1,14 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestGuardDuty_CreateIPSet_FormatValidation(t *testing.T) {
@@ -44,7 +46,7 @@ func TestGuardDuty_CreateIPSet_FormatValidation(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code, rec.Body.String())
var errOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errOut, jsonx.V1()))
assert.Equal(t, "BadRequestException", errOut["__type"])
return
@@ -90,7 +92,7 @@ func TestGuardDuty_CreateThreatIntelSet_FormatValidation(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code, rec.Body.String())
var errOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errOut, jsonx.V1()))
assert.Equal(t, "BadRequestException", errOut["__type"])
return
diff --git a/services/guardduty/handler_malware_protection.go b/services/guardduty/handler_malware_protection.go
index 6fb7710bcf..3dcc6cb70d 100644
--- a/services/guardduty/handler_malware_protection.go
+++ b/services/guardduty/handler_malware_protection.go
@@ -1,12 +1,13 @@
package guardduty
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"sync"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -188,7 +189,7 @@ func (h *Handler) handleDescribeMalwareScans(detectorID string, body []byte) (an
var req malwareScanFilterRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
@@ -210,7 +211,7 @@ func (h *Handler) handleListMalwareScans(query string, body []byte) (any, int, e
var req malwareScanFilterRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
@@ -239,7 +240,7 @@ func (h *Handler) handleStartMalwareScan(body []byte) (any, int, error) {
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
@@ -343,7 +344,7 @@ func (h *Handler) handleUpdateMalwareScanSettings(detectorID string, body []byte
EbsSnapshotPreservation string `json:"ebsSnapshotPreservation"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -368,7 +369,7 @@ func (h *Handler) handleCreateMalwareProtectionPlan(body []byte) (any, int, erro
ClientToken string `json:"clientToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -479,7 +480,7 @@ func (h *Handler) handleUpdateMalwareProtectionPlan(planID string, body []byte)
Role string `json:"role"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -508,7 +509,7 @@ func (h *Handler) handleSendObjectMalwareScan(body []byte) (any, int, error) {
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
diff --git a/services/guardduty/handler_members.go b/services/guardduty/handler_members.go
index dc2b230250..ad02a3ecf7 100644
--- a/services/guardduty/handler_members.go
+++ b/services/guardduty/handler_members.go
@@ -1,9 +1,11 @@
package guardduty
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// keyMembers is the real required response key for GetMembers, ListMembers,
@@ -157,7 +159,7 @@ func (h *Handler) handleCreateMembers(detectorID string, body []byte) (any, int,
AccountDetails []map[string]any `json:"accountDetails"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -173,7 +175,7 @@ func (h *Handler) handleDeleteMembers(detectorID string, body []byte) (any, int,
AccountIDs []string `json:"accountIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -190,7 +192,7 @@ func (h *Handler) handleGetMembers(detectorID string, body []byte) (any, int, er
AccountIDs []string `json:"accountIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -215,7 +217,7 @@ func (h *Handler) handleInviteMembers(detectorID string, body []byte) (any, int,
AccountIDs []string `json:"accountIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -253,7 +255,7 @@ func (h *Handler) handleStartMonitoringMembers(detectorID string, body []byte) (
AccountIDs []string `json:"accountIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -270,7 +272,7 @@ func (h *Handler) handleStopMonitoringMembers(detectorID string, body []byte) (a
AccountIDs []string `json:"accountIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -287,7 +289,7 @@ func (h *Handler) handleDisassociateMembers(detectorID string, body []byte) (any
AccountIDs []string `json:"accountIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -304,7 +306,7 @@ func (h *Handler) handleGetMemberDetectors(detectorID string, body []byte) (any,
AccountIDs []string `json:"accountIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -326,7 +328,7 @@ func (h *Handler) handleUpdateMemberDetectors(detectorID string, body []byte) (a
Features []DetectorFeature `json:"features"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -346,7 +348,7 @@ func (h *Handler) handleAcceptAdministratorInvitation(detectorID string, body []
InvitationID string `json:"invitationId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -363,7 +365,7 @@ func (h *Handler) handleAcceptInvitation(detectorID string, body []byte) (int, e
InvitationID string `json:"invitationId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -379,7 +381,7 @@ func (h *Handler) handleDeclineInvitations(body []byte) (any, int, error) {
AccountIDs []string `json:"accountIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -393,7 +395,7 @@ func (h *Handler) handleDeleteInvitations(body []byte) (any, int, error) {
AccountIDs []string `json:"accountIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
diff --git a/services/guardduty/handler_organization.go b/services/guardduty/handler_organization.go
index 9172447775..ab580c6313 100644
--- a/services/guardduty/handler_organization.go
+++ b/services/guardduty/handler_organization.go
@@ -1,8 +1,10 @@
package guardduty
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) dispatchOrgOps(op, path, query string, body []byte) (any, int, bool, error) {
@@ -70,7 +72,7 @@ func (h *Handler) handleEnableOrganizationAdminAccount(body []byte) (int, error)
AdminAccountID string `json:"adminAccountId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -86,7 +88,7 @@ func (h *Handler) handleDisableOrganizationAdminAccount(body []byte) (int, error
AdminAccountID string `json:"adminAccountId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -160,7 +162,7 @@ func (h *Handler) handleUpdateOrganizationConfiguration(detectorID string, body
Features []OrgFeature `json:"features"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
diff --git a/services/guardduty/handler_publishing_destinations.go b/services/guardduty/handler_publishing_destinations.go
index 261572bde8..1287f2effe 100644
--- a/services/guardduty/handler_publishing_destinations.go
+++ b/services/guardduty/handler_publishing_destinations.go
@@ -1,8 +1,10 @@
package guardduty
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) dispatchPublishingDestOps(op, path, query string, body []byte) (any, int, bool, error) {
@@ -49,7 +51,7 @@ func (h *Handler) handleCreatePublishingDestination(detectorID string, body []by
ClientToken string `json:"clientToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -137,7 +139,7 @@ func (h *Handler) handleUpdatePublishingDestination(detectorID, destID string, b
DestinationProperties DestinationProperties `json:"destinationProperties"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
diff --git a/services/guardduty/handler_tags.go b/services/guardduty/handler_tags.go
index b23eebb641..b9eac5640a 100644
--- a/services/guardduty/handler_tags.go
+++ b/services/guardduty/handler_tags.go
@@ -1,10 +1,12 @@
package guardduty
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) dispatchTagOps(op, path, query string, body []byte) (any, int, error) {
@@ -44,7 +46,7 @@ func (h *Handler) handleTagResource(resourceARN string, body []byte) (int, error
Tags map[string]string `json:"tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
diff --git a/services/guardduty/handler_tagsync_test.go b/services/guardduty/handler_tagsync_test.go
index 9f50e7dfe9..7702bc45b1 100644
--- a/services/guardduty/handler_tagsync_test.go
+++ b/services/guardduty/handler_tagsync_test.go
@@ -14,7 +14,7 @@ package guardduty_test
// regression coverage before now.
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -39,7 +40,7 @@ func doJSON(t *testing.T, h *guardduty.Handler, method, path string, body any) m
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
diff --git a/services/guardduty/handler_test.go b/services/guardduty/handler_test.go
index 8549e3718c..a0139b9f15 100644
--- a/services/guardduty/handler_test.go
+++ b/services/guardduty/handler_test.go
@@ -2,7 +2,7 @@ package guardduty_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -30,7 +31,7 @@ func doRequest(t *testing.T, h *guardduty.Handler, method, path string, body any
if body != nil {
var err error
- b, err = json.Marshal(body)
+ b, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -54,7 +55,7 @@ func createTestDetector(t *testing.T, h *guardduty.Handler) string {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id, ok := resp["detectorId"].(string)
require.True(t, ok)
require.NotEmpty(t, id)
diff --git a/services/guardduty/handler_usage.go b/services/guardduty/handler_usage.go
index e0354339af..16b3c694b3 100644
--- a/services/guardduty/handler_usage.go
+++ b/services/guardduty/handler_usage.go
@@ -1,8 +1,10 @@
package guardduty
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleGetUsageStatistics(detectorID string, body []byte) (any, int, error) {
@@ -18,7 +20,7 @@ func (h *Handler) handleGetUsageStatistics(detectorID string, body []byte) (any,
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
@@ -85,7 +87,7 @@ func (h *Handler) handleGetRemainingFreeTrialDays(detectorID string, body []byte
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
diff --git a/services/guardduty/handler_wireshape_test.go b/services/guardduty/handler_wireshape_test.go
index 9a4b8a9a32..8fbd5f418b 100644
--- a/services/guardduty/handler_wireshape_test.go
+++ b/services/guardduty/handler_wireshape_test.go
@@ -19,13 +19,15 @@ package guardduty_test
// scanStartedAt/scanCompletedAt epoch fields.
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -113,8 +115,8 @@ func TestWireShape_DescribePublishingDestination_UsesRealKeysAndTags(t *testing.
rec := doRequest(t, h, http.MethodGet, "/detector/"+detID+"/publishingDestination/"+destID, nil)
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
_, hasWrongKey := raw["publishingFailureStartedAt"]
assert.False(t, hasWrongKey, "must not use the made-up publishingFailureStartedAt wire key")
@@ -123,7 +125,7 @@ func TestWireShape_DescribePublishingDestination_UsesRealKeysAndTags(t *testing.
assert.True(t, hasRightKey, "must use the real publishingFailureStartTimestamp wire key")
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
tags, _ := got["tags"].(map[string]any)
assert.Equal(t, "prod", tags["env"], "tags supplied at creation must be returned by DescribePublishingDestination")
}
@@ -149,8 +151,8 @@ func TestWireShape_GetMalwareScan_UsesRealFieldNames(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/malware-scan/"+scanID, nil)
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
for _, badKey := range []string{"accountId", "resourceDetails", "findings", "scanStartTime", "scanEndTime"} {
_, present := raw[badKey]
@@ -171,7 +173,7 @@ func TestWireShape_GetMalwareScan_UsesRealFieldNames(t *testing.T) {
}
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, detID, got["detectorId"], "detectorId must resolve to the account's own detector")
assert.Equal(t, "EC2_INSTANCE", got["resourceType"], "resourceType must be inferred from the resource ARN")
@@ -267,14 +269,14 @@ func TestWireShape_Investigation_NoFabricatedAnalysis(t *testing.T) {
getRec := doRequest(t, h, http.MethodGet, "/detector/"+detID+"/investigation/"+invID, nil)
require.Equal(t, http.StatusOK, getRec.Code, getRec.Body.String())
- var getRaw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getRaw))
+ var getRaw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getRaw, jsonx.V1()))
invRaw, ok := getRaw["investigation"]
require.True(t, ok, "GetInvestigationOutput must have an investigation member")
- var inv map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(invRaw, &inv))
+ var inv map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(invRaw, &inv, jsonx.V1()))
for _, fabricatable := range []string{
"cloud", "confidence", "endTime", "error", "metadata", "risk", "riskLevel", "summary", "title",
@@ -285,18 +287,18 @@ func TestWireShape_Investigation_NoFabricatedAnalysis(t *testing.T) {
}
var got map[string]any
- require.NoError(t, json.Unmarshal(invRaw, &got))
+ require.NoError(t, json.Unmarshal(invRaw, &got, jsonx.V1()))
assert.Equal(t, "RUNNING", got["status"], "an investigation this backend cannot analyze must stay RUNNING")
assert.Equal(t, "Investigate finding in this account", got["triggerPrompt"])
listRec := doRequest(t, h, http.MethodPost, "/detector/"+detID+"/investigation/list", nil)
require.Equal(t, http.StatusOK, listRec.Code, listRec.Body.String())
- var listRaw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listRaw))
+ var listRaw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listRaw, jsonx.V1()))
- var summaries []map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(listRaw["investigations"], &summaries))
+ var summaries []map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(listRaw["investigations"], &summaries, jsonx.V1()))
require.Len(t, summaries, 1)
for _, fabricatable := range []string{"confidence", "endTime", "riskLevel", "title"} {
@@ -305,7 +307,7 @@ func TestWireShape_Investigation_NoFabricatedAnalysis(t *testing.T) {
}
var summary map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &summary))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &summary, jsonx.V1()))
invList, ok := summary["investigations"].([]any)
require.True(t, ok)
require.Len(t, invList, 1)
diff --git a/services/guardduty/ip_sets_test.go b/services/guardduty/ip_sets_test.go
index 5b8d1eead4..e07332274e 100644
--- a/services/guardduty/ip_sets_test.go
+++ b/services/guardduty/ip_sets_test.go
@@ -1,13 +1,14 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -31,7 +32,7 @@ func TestIPSet_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
ipSetID := cr["ipSetId"].(string)
require.NotEmpty(t, ipSetID)
@@ -39,7 +40,7 @@ func TestIPSet_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var gr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr, jsonx.V1()))
assert.Equal(t, "my-ipset", gr["name"])
assert.Equal(t, "TXT", gr["format"])
assert.Equal(t, "ACTIVE", gr["status"])
@@ -53,7 +54,7 @@ func TestIPSet_CRUD(t *testing.T) {
"name": "upd-ipset", "format": "TXT", "location": "s3://b/f.txt",
})
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
ipSetID := cr["ipSetId"].(string)
rec = doRequest(t, h, http.MethodPost, "/detector/"+detectorID+"/ipset/"+ipSetID, map[string]any{
@@ -64,7 +65,7 @@ func TestIPSet_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/detector/"+detectorID+"/ipset/"+ipSetID, nil)
var gr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr, jsonx.V1()))
assert.Equal(t, "updated-ipset", gr["name"])
assert.Equal(t, "ACTIVE", gr["status"])
},
@@ -77,7 +78,7 @@ func TestIPSet_CRUD(t *testing.T) {
"name": "del-ipset", "format": "TXT", "location": "s3://b/f.txt",
})
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
ipSetID := cr["ipSetId"].(string)
rec = doRequest(t, h, http.MethodDelete, "/detector/"+detectorID+"/ipset/"+ipSetID, nil)
@@ -87,7 +88,7 @@ func TestIPSet_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var gr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr, jsonx.V1()))
assert.Equal(t, "DELETED", gr["status"])
rec = doRequest(t, h, http.MethodGet, "/detector/"+detectorID+"/ipset", nil)
@@ -111,7 +112,7 @@ func TestIPSet_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ids, ok := resp["ipSetIds"].([]any)
require.True(t, ok)
assert.Len(t, ids, 3)
@@ -125,12 +126,12 @@ func TestIPSet_CRUD(t *testing.T) {
"name": "inactive-ipset", "format": "TXT", "location": "s3://b/f.txt",
})
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
ipSetID := cr["ipSetId"].(string)
rec = doRequest(t, h, http.MethodGet, "/detector/"+detectorID+"/ipset/"+ipSetID, nil)
var gr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr, jsonx.V1()))
assert.Equal(t, "INACTIVE", gr["status"])
},
},
@@ -156,7 +157,7 @@ func TestListIPSets_Empty_State(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
raw, exists := resp["ipSetIds"]
require.True(t, exists, "ListIPSets must include 'ipSetIds' key")
@@ -182,14 +183,14 @@ func TestIPSet_Tags_EmptyMap_Not_Null(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
ipSetID := createResp["ipSetId"].(string)
rec = doRequest(t, h, http.MethodGet, "/detector/"+id+"/ipset/"+ipSetID, nil)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
raw, exists := resp["tags"]
require.True(t, exists, "GetIPSet response must include 'tags' key")
diff --git a/services/guardduty/malware_protection_plan_schema.go b/services/guardduty/malware_protection_plan_schema.go
index 06032c603e..d9027ce7a3 100644
--- a/services/guardduty/malware_protection_plan_schema.go
+++ b/services/guardduty/malware_protection_plan_schema.go
@@ -1,6 +1,10 @@
package guardduty
-import "encoding/json"
+import (
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
// ProtectedResourceInput mirrors types.CreateProtectedResource (also reused
// verbatim by GetMalwareProtectionPlanOutput.ProtectedResource -- Create and
@@ -56,13 +60,13 @@ func decodeProtectedResource(raw map[string]any) (*ProtectedResourceInput, error
return &ProtectedResourceInput{}, nil
}
- data, marshalErr := json.Marshal(raw)
+ data, marshalErr := json.Marshal(raw, jsonx.V1())
if marshalErr != nil {
return nil, marshalErr
}
var pr ProtectedResourceInput
- if unmarshalErr := json.Unmarshal(data, &pr); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(data, &pr, jsonx.V1()); unmarshalErr != nil {
return nil, unmarshalErr
}
@@ -74,13 +78,13 @@ func decodeMalwareProtectionPlanActions(raw map[string]any) (*MalwareProtectionP
return &MalwareProtectionPlanActionsInput{}, nil
}
- data, marshalErr := json.Marshal(raw)
+ data, marshalErr := json.Marshal(raw, jsonx.V1())
if marshalErr != nil {
return nil, marshalErr
}
var a MalwareProtectionPlanActionsInput
- if unmarshalErr := json.Unmarshal(data, &a); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(data, &a, jsonx.V1()); unmarshalErr != nil {
return nil, unmarshalErr
}
diff --git a/services/guardduty/malware_protection_test.go b/services/guardduty/malware_protection_test.go
index bcfdf87788..4ec144e155 100644
--- a/services/guardduty/malware_protection_test.go
+++ b/services/guardduty/malware_protection_test.go
@@ -1,13 +1,14 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -30,7 +31,7 @@ func TestMalwareScanning(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
scanID, _ := startResp["scanId"].(string)
require.NotEmpty(t, scanID)
@@ -39,7 +40,7 @@ func TestMalwareScanning(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, scanID, getResp["scanId"])
// ListMalwareScans
@@ -47,7 +48,7 @@ func TestMalwareScanning(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
scans, _ := listResp["scans"].([]any)
assert.Len(t, scans, 1)
},
@@ -77,7 +78,7 @@ func TestMalwareScanning(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["scans"])
},
},
@@ -133,7 +134,7 @@ func TestMalwareScanning(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var settingsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &settingsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &settingsResp, jsonx.V1()))
assert.Equal(t, "RETENTION_WITH_FINDING", settingsResp["ebsSnapshotPreservation"])
},
},
@@ -167,7 +168,7 @@ func TestMalwareScanning(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["scanId"])
},
},
@@ -375,7 +376,7 @@ func TestMalwareProtectionPlans(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
planID, _ := createResp["malwareProtectionPlanId"].(string)
require.NotEmpty(t, planID)
@@ -384,7 +385,7 @@ func TestMalwareProtectionPlans(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, planID, getResp["malwareProtectionPlanId"])
// UpdateMalwareProtectionPlan uses PATCH on the real wire --
@@ -401,7 +402,7 @@ func TestMalwareProtectionPlans(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
plans, _ := listResp["malwareProtectionPlans"].([]any)
assert.Len(t, plans, 1)
@@ -411,7 +412,7 @@ func TestMalwareProtectionPlans(t *testing.T) {
// List after delete
rec = doRequest(t, h, http.MethodGet, "/malware-protection-plan", nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
plansAfter, _ := listResp["malwareProtectionPlans"].([]any)
assert.Empty(t, plansAfter)
},
@@ -461,7 +462,7 @@ func TestUpdateMalwareProtectionPlan_PreservesBucketName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
planID, _ := createResp["malwareProtectionPlanId"].(string)
require.NotEmpty(t, planID)
@@ -481,7 +482,7 @@ func TestUpdateMalwareProtectionPlan_PreservesBucketName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
protectedResource, ok := getResp["protectedResource"].(map[string]any)
require.True(t, ok, "protectedResource must survive the update")
diff --git a/services/guardduty/members_test.go b/services/guardduty/members_test.go
index 864eb9cc13..0ec28fdb60 100644
--- a/services/guardduty/members_test.go
+++ b/services/guardduty/members_test.go
@@ -1,13 +1,14 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
organizationsbackend "github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -36,7 +37,7 @@ func TestMembers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Empty(t, createResp["unprocessedAccounts"])
// ListMembers
@@ -44,7 +45,7 @@ func TestMembers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
members, _ := listResp["members"].([]any)
assert.Len(t, members, 2)
@@ -55,7 +56,7 @@ func TestMembers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
got, _ := getResp["members"].([]any)
assert.Len(t, got, 1)
@@ -68,7 +69,7 @@ func TestMembers(t *testing.T) {
// ListMembers after delete
rec = doRequest(t, h, http.MethodGet, "/detector/"+id+"/member", nil)
var listAfter map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listAfter))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listAfter, jsonx.V1()))
membersAfter, _ := listAfter["members"].([]any)
assert.Empty(t, membersAfter)
},
@@ -99,7 +100,7 @@ func TestMembers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
members, _ := resp["members"].([]any)
require.Len(t, members, 1)
@@ -310,7 +311,7 @@ func TestMemberOps_AutoEnableOrganizationMembersAll_Rejected(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code, rec.Body.String())
var errOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errOut, jsonx.V1()))
assert.Equal(t, "BadRequestException", errOut["__type"])
})
}
@@ -382,7 +383,7 @@ func TestInvitations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var countResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &countResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &countResp, jsonx.V1()))
assert.NotNil(t, countResp["invitationsCount"])
// ListInvitations
@@ -390,7 +391,7 @@ func TestInvitations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.NotNil(t, listResp["invitations"])
},
},
@@ -431,7 +432,7 @@ func TestInvitations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var adminResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &adminResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &adminResp, jsonx.V1()))
assert.NotNil(t, adminResp["administrator"])
// DisassociateFromAdministratorAccount
@@ -458,7 +459,7 @@ func TestInvitations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var masterResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &masterResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &masterResp, jsonx.V1()))
assert.NotNil(t, masterResp["master"])
// DisassociateFromMasterAccount
diff --git a/services/guardduty/multi_region_test.go b/services/guardduty/multi_region_test.go
index 463e56d8fa..42983a102c 100644
--- a/services/guardduty/multi_region_test.go
+++ b/services/guardduty/multi_region_test.go
@@ -2,7 +2,7 @@ package guardduty_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -41,7 +42,7 @@ func decode(t *testing.T, body string) map[string]any {
out := map[string]any{}
if body != "" {
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
}
return out
diff --git a/services/guardduty/organization_test.go b/services/guardduty/organization_test.go
index 65dc7147ed..2e5a71b027 100644
--- a/services/guardduty/organization_test.go
+++ b/services/guardduty/organization_test.go
@@ -1,13 +1,14 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -34,7 +35,7 @@ func TestOrganization(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
accounts, _ := listResp["adminAccounts"].([]any)
assert.Len(t, accounts, 1)
@@ -46,7 +47,7 @@ func TestOrganization(t *testing.T) {
// List after disable
rec = doRequest(t, h, http.MethodGet, "/admin", nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
accounts2, _ := listResp["adminAccounts"].([]any)
assert.Empty(t, accounts2)
},
@@ -63,7 +64,7 @@ func TestOrganization(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.NotNil(t, descResp["autoEnable"])
// UpdateOrganizationConfiguration
@@ -74,7 +75,7 @@ func TestOrganization(t *testing.T) {
// Verify update
rec = doRequest(t, h, http.MethodGet, "/detector/"+id+"/admin", nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, true, descResp["autoEnable"])
},
},
diff --git a/services/guardduty/persistence.go b/services/guardduty/persistence.go
index cb0ae8037f..65e27f9826 100644
--- a/services/guardduty/persistence.go
+++ b/services/guardduty/persistence.go
@@ -2,7 +2,7 @@ package guardduty
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -110,7 +110,7 @@ func buildPersistenceDTORegistry() persistenceDTOTables {
// identity of their own -- see store_setup.go) and is persisted here
// directly, as before. MemberSeq is a scalar counter, also carried directly.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
AccountID string `json:"accountId"`
Region string `json:"region"`
@@ -308,7 +308,7 @@ func unwrapByDetectorDTOs[V any](dtos *store.Table[byDetectorDTO[V]], setDetecto
// restoreDirtyTables rebuilds every "dirty" store.Table on b from tables via
// the ephemeral DTO registry, factored out of Restore to keep Restore's own
// cognitive complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreDirtyTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTables(tables map[string]jsontext.Value) error {
dtos := buildPersistenceDTORegistry()
if err := dtos.registry.RestoreAll(tables); err != nil {
return fmt.Errorf("guardduty: restore snapshot DTO tables: %w", err)
diff --git a/services/guardduty/publishing_destinations_test.go b/services/guardduty/publishing_destinations_test.go
index 5414bca43f..6ae3fe265f 100644
--- a/services/guardduty/publishing_destinations_test.go
+++ b/services/guardduty/publishing_destinations_test.go
@@ -1,13 +1,14 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -36,7 +37,7 @@ func TestPublishingDestinations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
destID, _ := createResp["destinationId"].(string)
require.NotEmpty(t, destID)
@@ -45,7 +46,7 @@ func TestPublishingDestinations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "S3", descResp["destinationType"])
// UpdatePublishingDestination
@@ -62,7 +63,7 @@ func TestPublishingDestinations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
dests, _ := listResp["destinations"].([]any)
assert.Len(t, dests, 1)
@@ -72,7 +73,7 @@ func TestPublishingDestinations(t *testing.T) {
// List after delete
rec = doRequest(t, h, http.MethodGet, "/detector/"+id+"/publishingDestination", nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
destsAfter, _ := listResp["destinations"].([]any)
assert.Empty(t, destsAfter)
},
diff --git a/services/guardduty/tags_test.go b/services/guardduty/tags_test.go
index e838b42fe9..f6f53eda4e 100644
--- a/services/guardduty/tags_test.go
+++ b/services/guardduty/tags_test.go
@@ -1,7 +1,7 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -34,7 +35,7 @@ func TestTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags, ok := resp["tags"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "prod", tags["env"])
@@ -56,7 +57,7 @@ func TestTags(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/tags/"+arn, nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags := resp["tags"].(map[string]any)
assert.NotContains(t, tags, "k1")
assert.Equal(t, "v2", tags["k2"])
@@ -72,7 +73,7 @@ func TestTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags, ok := resp["tags"].(map[string]any)
require.True(t, ok)
assert.Empty(t, tags)
@@ -101,7 +102,7 @@ func TestTagResource_Merges_Creation_Tags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["detectorId"].(string)
arn := fmt.Sprintf("arn:aws:guardduty:us-east-1:123456789012:detector/%s", id)
@@ -115,7 +116,7 @@ func TestTagResource_Merges_Creation_Tags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags := tagsResp["tags"].(map[string]any)
assert.Equal(t, "test", tags["env"], "creation-time tag must be preserved")
@@ -134,7 +135,7 @@ func TestUntagResource_Removes_Only_Specified_Key(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["detectorId"].(string)
arn := fmt.Sprintf("arn:aws:guardduty:us-east-1:123456789012:detector/%s", id)
@@ -146,7 +147,7 @@ func TestUntagResource_Removes_Only_Specified_Key(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags := tagsResp["tags"].(map[string]any)
assert.Equal(t, "v1", tags["k1"], "k1 must be preserved")
diff --git a/services/guardduty/threat_intel_sets_test.go b/services/guardduty/threat_intel_sets_test.go
index ba34e2cb9e..5dc1b25b06 100644
--- a/services/guardduty/threat_intel_sets_test.go
+++ b/services/guardduty/threat_intel_sets_test.go
@@ -1,13 +1,14 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/guardduty"
)
@@ -31,7 +32,7 @@ func TestThreatIntelSet_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
setID := cr["threatIntelSetId"].(string)
require.NotEmpty(t, setID)
@@ -39,7 +40,7 @@ func TestThreatIntelSet_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var gr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr, jsonx.V1()))
assert.Equal(t, "my-threatset", gr["name"])
assert.Equal(t, "ACTIVE", gr["status"])
},
@@ -52,7 +53,7 @@ func TestThreatIntelSet_CRUD(t *testing.T) {
"name": "upd-threat", "format": "TXT", "location": "s3://b/t.txt", "activate": true,
})
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
setID := cr["threatIntelSetId"].(string)
activate := false
@@ -63,7 +64,7 @@ func TestThreatIntelSet_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/detector/"+detectorID+"/threatintelset/"+setID, nil)
var gr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr, jsonx.V1()))
assert.Equal(t, "INACTIVE", gr["status"])
},
},
@@ -75,7 +76,7 @@ func TestThreatIntelSet_CRUD(t *testing.T) {
"name": "del-threat", "format": "TXT", "location": "s3://b/t.txt",
})
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
setID := cr["threatIntelSetId"].(string)
rec = doRequest(t, h, http.MethodDelete, "/detector/"+detectorID+"/threatintelset/"+setID, nil)
@@ -85,7 +86,7 @@ func TestThreatIntelSet_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var gr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gr, jsonx.V1()))
assert.Equal(t, "DELETED", gr["status"])
rec = doRequest(t, h, http.MethodGet, "/detector/"+detectorID+"/threatintelset", nil)
@@ -109,7 +110,7 @@ func TestThreatIntelSet_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ids, ok := resp["threatIntelSetIds"].([]any)
require.True(t, ok)
assert.Len(t, ids, 2)
@@ -137,7 +138,7 @@ func TestListThreatIntelSets_Empty_State(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
raw, exists := resp["threatIntelSetIds"]
require.True(t, exists, "ListThreatIntelSets must include 'threatIntelSetIds' key")
@@ -163,14 +164,14 @@ func TestThreatIntelSet_Tags_EmptyMap_Not_Null(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
setID := createResp["threatIntelSetId"].(string)
rec = doRequest(t, h, http.MethodGet, "/detector/"+id+"/threatintelset/"+setID, nil)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
raw, exists := resp["tags"]
require.True(t, exists, "GetThreatIntelSet response must include 'tags' key")
diff --git a/services/guardduty/usage_test.go b/services/guardduty/usage_test.go
index f9d4df400f..979d9e80ca 100644
--- a/services/guardduty/usage_test.go
+++ b/services/guardduty/usage_test.go
@@ -1,12 +1,14 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestGetUsageStatistics_WireShape locks the GetUsageStatistics fix: the
@@ -24,7 +26,7 @@ func TestGetUsageStatistics_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
us, ok := resp["usageStatistics"].(map[string]any)
require.True(t, ok, "response must be wrapped under usageStatistics")
@@ -66,7 +68,7 @@ func TestGetUsageStatistics_UsageStatisticType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
us := resp["usageStatistics"].(map[string]any)
assert.Contains(t, us, "sumByAccount")
@@ -93,7 +95,7 @@ func TestGetUsageStatistics_SumByFeature_ReflectsEnabledFeatures(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
detID := createResp["detectorId"].(string)
rec = doRequest(t, h, http.MethodPost, "/detector/"+detID+"/usage/statistics", map[string]any{
@@ -102,7 +104,7 @@ func TestGetUsageStatistics_SumByFeature_ReflectsEnabledFeatures(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
us := resp["usageStatistics"].(map[string]any)
features, ok := us["sumByFeature"].([]any)
@@ -140,7 +142,7 @@ func TestGetRemainingFreeTrialDays_HonorsAccountIDs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
accounts, ok := resp["accounts"].([]any)
require.True(t, ok)
diff --git a/services/guardduty/wire_field_fixes_test.go b/services/guardduty/wire_field_fixes_test.go
index 889c77444b..e9d3b40e2d 100644
--- a/services/guardduty/wire_field_fixes_test.go
+++ b/services/guardduty/wire_field_fixes_test.go
@@ -1,7 +1,7 @@
package guardduty_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/guardduty"
@@ -366,7 +367,7 @@ func TestListMalwareProtectionPlans_NoInventedArn(t *testing.T) {
var raw struct {
MalwareProtectionPlans []map[string]any `json:"malwareProtectionPlans"`
}
- require.NoError(t, json.Unmarshal(body, &raw))
+ require.NoError(t, json.Unmarshal(body, &raw, jsonx.V1()))
require.NotEmpty(t, raw.MalwareProtectionPlans)
for _, entry := range raw.MalwareProtectionPlans {
diff --git a/services/iam/accounts.go b/services/iam/accounts.go
index 8ff7b4fb8f..95cd9aabff 100644
--- a/services/iam/accounts.go
+++ b/services/iam/accounts.go
@@ -2,7 +2,7 @@ package iam
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"sort"
"strings"
@@ -223,13 +223,13 @@ func (h *Handler) forAccount(root *InMemoryBackend, acct string) *Handler {
return sh
}
-func (h *Handler) accountSnapshots(ctx context.Context) map[string]json.RawMessage {
+func (h *Handler) accountSnapshots(ctx context.Context) map[string]jsontext.Value {
root, ok := h.Backend.(*InMemoryBackend)
if !ok {
return nil
}
- var out map[string]json.RawMessage
+ var out map[string]jsontext.Value
for _, id := range root.Accounts() {
data := h.forAccount(root, id).Snapshot(ctx)
@@ -238,7 +238,7 @@ func (h *Handler) accountSnapshots(ctx context.Context) map[string]json.RawMessa
}
if out == nil {
- out = make(map[string]json.RawMessage)
+ out = make(map[string]jsontext.Value)
}
out[id] = data
@@ -247,7 +247,7 @@ func (h *Handler) accountSnapshots(ctx context.Context) map[string]json.RawMessa
return out
}
-func (h *Handler) restoreAccounts(ctx context.Context, snaps map[string]json.RawMessage) error {
+func (h *Handler) restoreAccounts(ctx context.Context, snaps map[string]jsontext.Value) error {
root, ok := h.Backend.(*InMemoryBackend)
if !ok {
return nil
diff --git a/services/iam/aws_managed.go b/services/iam/aws_managed.go
index d5b1ed86a7..0015de82c5 100644
--- a/services/iam/aws_managed.go
+++ b/services/iam/aws_managed.go
@@ -1,16 +1,18 @@
package iam
import (
- "bytes"
"crypto/sha256"
_ "embed"
"encoding/base32"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"sort"
"strings"
"sync"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
//go:embed data/aws_managed_policies.json
@@ -23,13 +25,13 @@ type awsManagedFile struct {
}
type awsManagedEntry struct {
- Name string `json:"name"`
- Path string `json:"path"`
- PolicyID string `json:"policyId"`
- DefaultVersionID string `json:"defaultVersionId"`
- CreateDate time.Time `json:"createDate"`
- UpdateDate time.Time `json:"updateDate"`
- Document json.RawMessage `json:"document"`
+ Name string `json:"name"`
+ Path string `json:"path"`
+ PolicyID string `json:"policyId"`
+ DefaultVersionID string `json:"defaultVersionId"`
+ CreateDate time.Time `json:"createDate"`
+ UpdateDate time.Time `json:"updateDate"`
+ Document jsontext.Value `json:"document"`
}
type awsManagedCatalog struct {
@@ -49,7 +51,7 @@ var loadAWSManagedCatalog = sync.OnceValue(func() *awsManagedCatalog {
func parseAWSManagedCatalog(raw []byte) (*awsManagedCatalog, error) {
var file awsManagedFile
- if err := json.Unmarshal(raw, &file); err != nil {
+ if err := json.Unmarshal(raw, &file, jsonx.V1()); err != nil {
return nil, fmt.Errorf("iam: parse AWS managed policy catalog: %w", err)
}
@@ -59,8 +61,8 @@ func parseAWSManagedCatalog(raw []byte) (*awsManagedCatalog, error) {
}
for _, e := range file.Policies {
- var doc bytes.Buffer
- if err := json.Compact(&doc, e.Document); err != nil {
+ doc := e.Document.Clone()
+ if err := doc.Compact(jsonx.V1()); err != nil {
return nil, fmt.Errorf("iam: AWS managed policy %q document: %w", e.Name, err)
}
@@ -74,7 +76,7 @@ func parseAWSManagedCatalog(raw []byte) (*awsManagedCatalog, error) {
PolicyID: e.PolicyID,
Arn: arn,
Path: e.Path,
- PolicyDocument: doc.String(),
+ PolicyDocument: string(doc),
DefaultVersionID: e.DefaultVersionID,
CreateDate: e.CreateDate.UTC(),
UpdateDate: e.UpdateDate.UTC(),
diff --git a/services/iam/aws_managed_test.go b/services/iam/aws_managed_test.go
index 4e6f6b0595..604de57460 100644
--- a/services/iam/aws_managed_test.go
+++ b/services/iam/aws_managed_test.go
@@ -1,7 +1,8 @@
package iam_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io/fs"
"net/url"
"os"
@@ -17,6 +18,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iam"
)
@@ -475,13 +477,13 @@ func TestAWSManagedPolicyCatalog_Integrity(t *testing.T) {
var file struct {
Policies []struct {
- Name string `json:"name"`
- Path string `json:"path"`
- DefaultVersionID string `json:"defaultVersionId"`
- Document json.RawMessage `json:"document"`
+ Name string `json:"name"`
+ Path string `json:"path"`
+ DefaultVersionID string `json:"defaultVersionId"`
+ Document jsontext.Value `json:"document"`
} `json:"policies"`
}
- require.NoError(t, json.Unmarshal(raw, &file))
+ require.NoError(t, json.Unmarshal(raw, &file, jsonx.V1()))
seen := map[string]bool{}
@@ -496,7 +498,7 @@ func TestAWSManagedPolicyCatalog_Integrity(t *testing.T) {
Version string `json:"Version"`
Statement []any `json:"Statement"`
}
- require.NoError(t, json.Unmarshal(p.Document, &d), key)
+ require.NoError(t, json.Unmarshal(p.Document, &d, jsonx.V1()), key)
assert.Equal(t, "2012-10-17", d.Version, key)
assert.NotEmpty(t, d.Statement, key)
}
diff --git a/services/iam/conditions_test.go b/services/iam/conditions_test.go
index e9dd5387ba..d0fd241e7f 100644
--- a/services/iam/conditions_test.go
+++ b/services/iam/conditions_test.go
@@ -2,11 +2,12 @@ package iam_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iam"
)
@@ -1058,7 +1059,7 @@ func TestConditionTagKeyFallsThroughToExtra(t *testing.T) {
// mustJSON marshals v to a compact JSON string, panicking on error (test-only).
func mustJSON(v any) string {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
panic("marshal policy: " + err.Error())
}
diff --git a/services/iam/enforcement_integration_test.go b/services/iam/enforcement_integration_test.go
index b3e00d0241..da088c1515 100644
--- a/services/iam/enforcement_integration_test.go
+++ b/services/iam/enforcement_integration_test.go
@@ -2,7 +2,7 @@ package iam_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -19,6 +19,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iam"
)
@@ -202,7 +203,7 @@ func TestEnforcement_ConditionOperators_SDKIntegration(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- policyBytes, marshalErr := json.Marshal(tt.policy)
+ policyBytes, marshalErr := json.Marshal(tt.policy, jsonx.V1())
require.NoError(t, marshalErr)
const (
@@ -260,7 +261,7 @@ func TestEnforcement_MultiServiceSDKIntegration(t *testing.T) {
},
},
}
- analystPolicyBytes, marshalErr := json.Marshal(analystPolicy)
+ analystPolicyBytes, marshalErr := json.Marshal(analystPolicy, jsonx.V1())
require.NoError(t, marshalErr)
analystPolicyDoc := string(analystPolicyBytes)
diff --git a/services/iam/evaluator.go b/services/iam/evaluator.go
index 7c7b2f870d..2970367b72 100644
--- a/services/iam/evaluator.go
+++ b/services/iam/evaluator.go
@@ -1,11 +1,12 @@
package iam
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"slices"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/safemap"
)
@@ -68,7 +69,7 @@ func parsePolicyDocumentCached(expanded string) (PolicyDocument, bool) {
var pd PolicyDocument
- err := json.Unmarshal([]byte(expanded), &pd)
+ err := json.Unmarshal([]byte(expanded), &pd, jsonx.V1())
entry := &cachedPolicyDoc{doc: pd, ok: err == nil}
parsedPolicyCache.Set(expanded, entry)
diff --git a/services/iam/handler_inline_policies.go b/services/iam/handler_inline_policies.go
index d0797fd8a2..93ca66236f 100644
--- a/services/iam/handler_inline_policies.go
+++ b/services/iam/handler_inline_policies.go
@@ -1,11 +1,14 @@
package iam
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"net/url"
"sort"
"strconv"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) iamInlinePolicyDispatchTable() map[string]iamActionFn {
@@ -284,10 +287,10 @@ func contextKeysFromPolicyDocuments(docs []string) []string {
}
var parsed struct {
- Statement json.RawMessage `json:"Statement"`
+ Statement jsontext.Value `json:"Statement"`
}
- if err := json.Unmarshal([]byte(doc), &parsed); err != nil {
+ if err := json.Unmarshal([]byte(doc), &parsed, jsonx.V1()); err != nil {
// Malformed documents are skipped; GetContextKeys is best-effort.
continue
}
diff --git a/services/iam/middleware.go b/services/iam/middleware.go
index 547735ed14..a34c961c07 100644
--- a/services/iam/middleware.go
+++ b/services/iam/middleware.go
@@ -3,7 +3,7 @@ package iam
import (
"cmp"
"context"
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"net/http"
"strings"
@@ -15,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/iampolicystream"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -752,7 +753,7 @@ func writeJSONRPCAccessDenied(c *echo.Context, action, msg string) error {
Message: cmp.Or(msg, "User is not authorized to perform: "+action),
}
- body, err := json.Marshal(resp)
+ body, err := json.Marshal(resp, jsonx.V1())
if err != nil {
return c.String(http.StatusBadRequest, `{"__type":"AccessDeniedException"}`)
}
diff --git a/services/iam/persistence.go b/services/iam/persistence.go
index 6d65e24168..fca1c9f695 100644
--- a/services/iam/persistence.go
+++ b/services/iam/persistence.go
@@ -2,7 +2,7 @@ package iam
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -32,7 +32,7 @@ type backendSnapshot struct {
PolicyVersions map[string][]StoredPolicyVersion `json:"policyVersions,omitempty"`
RoleByARN map[string]string `json:"roleByARN,omitempty"`
PolicyByARN map[string]string `json:"policyByARN,omitempty"`
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
PolicyAttachments map[string]policyAttachmentRefs `json:"policyAttachments,omitempty"`
DeletedV1Policies map[string]bool `json:"deletedV1Policies,omitempty"`
AccountProperties map[string]string `json:"accountProperties,omitempty"`
@@ -329,8 +329,8 @@ func parseVersionNum(id string) int {
type handlerSnapshot struct {
Tags map[string]map[string]string `json:"tags,omitempty"`
// Accounts holds each non-default account's handler snapshot.
- Accounts map[string]json.RawMessage `json:"accounts,omitempty"`
- Backend json.RawMessage `json:"backend,omitempty"`
+ Accounts map[string]jsontext.Value `json:"accounts,omitempty"`
+ Backend jsontext.Value `json:"backend,omitempty"`
}
// Snapshot implements persistence.Persistable. It combines the backend's own
@@ -344,7 +344,7 @@ func (h *Handler) Snapshot(ctx context.Context) []byte {
if s, ok := h.Backend.(snapshotter); ok {
if b := s.Snapshot(ctx); len(b) > 0 {
- snap.Backend = json.RawMessage(b)
+ snap.Backend = jsontext.Value(b)
}
}
diff --git a/services/iam/policy_validation.go b/services/iam/policy_validation.go
index d8fb1f883f..8cc7a0511d 100644
--- a/services/iam/policy_validation.go
+++ b/services/iam/policy_validation.go
@@ -1,9 +1,12 @@
package iam
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// IAM policy-grammar validation.
@@ -46,14 +49,14 @@ func isSupportedPolicyVersion(v string) bool {
// rawStatement is a statement decoded with field presence preserved so that
// "absent" can be distinguished from "present but empty".
type rawStatement struct {
- Effect *string `json:"Effect"`
- Condition map[string]map[string]json.RawMessage `json:"Condition"`
- Action json.RawMessage `json:"Action"`
- NotAction json.RawMessage `json:"NotAction"`
- Resource json.RawMessage `json:"Resource"`
- NotResource json.RawMessage `json:"NotResource"`
- Principal json.RawMessage `json:"Principal"`
- NotPrincipal json.RawMessage `json:"NotPrincipal"`
+ Effect *string `json:"Effect"`
+ Condition map[string]map[string]jsontext.Value `json:"Condition"`
+ Action jsontext.Value `json:"Action"`
+ NotAction jsontext.Value `json:"NotAction"`
+ Resource jsontext.Value `json:"Resource"`
+ NotResource jsontext.Value `json:"NotResource"`
+ Principal jsontext.Value `json:"Principal"`
+ NotPrincipal jsontext.Value `json:"NotPrincipal"`
}
// validateIdentityPolicyDocument validates an identity-based policy document
@@ -66,8 +69,8 @@ func validateIdentityPolicyDocument(policyDocument string) error {
}
// Top level must be a JSON object.
- var top map[string]json.RawMessage
- if err := json.Unmarshal([]byte(policyDocument), &top); err != nil {
+ var top map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(policyDocument), &top, jsonx.V1()); err != nil {
return fmt.Errorf("%w: policy document must be a JSON object", ErrMalformedPolicyDocument)
}
@@ -99,13 +102,13 @@ func validateIdentityPolicyDocument(policyDocument string) error {
}
// validatePolicyVersion checks the optional Version element.
-func validatePolicyVersion(raw json.RawMessage) error {
+func validatePolicyVersion(raw jsontext.Value) error {
if raw == nil {
return nil
}
var version string
- if err := json.Unmarshal(raw, &version); err != nil {
+ if err := json.Unmarshal(raw, &version, jsonx.V1()); err != nil {
return fmt.Errorf("%w: Version must be a string", ErrMalformedPolicyDocument)
}
@@ -121,11 +124,11 @@ func validatePolicyVersion(raw json.RawMessage) error {
// decodeStatements accepts either a single statement object or an array of
// statement objects, matching AWS's tolerance of both shapes.
-func decodeStatements(raw json.RawMessage) ([]rawStatement, error) {
+func decodeStatements(raw jsontext.Value) ([]rawStatement, error) {
trimmed := strings.TrimSpace(string(raw))
if strings.HasPrefix(trimmed, "[") {
var arr []rawStatement
- if err := json.Unmarshal(raw, &arr); err != nil {
+ if err := json.Unmarshal(raw, &arr, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: Statement array is malformed", ErrMalformedPolicyDocument)
}
@@ -133,7 +136,7 @@ func decodeStatements(raw json.RawMessage) ([]rawStatement, error) {
}
var single rawStatement
- if err := json.Unmarshal(raw, &single); err != nil {
+ if err := json.Unmarshal(raw, &single, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: Statement must be an object or array of objects", ErrMalformedPolicyDocument)
}
diff --git a/services/iam/request_scope.go b/services/iam/request_scope.go
index 02e81702b2..3fddcddeec 100644
--- a/services/iam/request_scope.go
+++ b/services/iam/request_scope.go
@@ -2,7 +2,7 @@ package iam
import (
"cmp"
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -148,7 +149,7 @@ func filterPrincipalDocs(docs []string, condCtx ConditionContext) []string {
func filterPrincipalDoc(doc string, condCtx ConditionContext) string {
var raw map[string]any
- if json.Unmarshal([]byte(doc), &raw) != nil {
+ if json.Unmarshal([]byte(doc), &raw, jsonx.V1()) != nil {
return doc
}
@@ -162,7 +163,7 @@ func filterPrincipalDoc(doc string, condCtx ConditionContext) string {
raw["Statement"] = kept
- out, err := json.Marshal(raw)
+ out, err := json.Marshal(raw, jsonx.V1())
if err != nil {
return doc
}
diff --git a/services/iam/resource_arn.go b/services/iam/resource_arn.go
index 60d2c5fbdd..8b1bcaf65a 100644
--- a/services/iam/resource_arn.go
+++ b/services/iam/resource_arn.go
@@ -2,7 +2,7 @@ package iam
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net"
"net/http"
"net/url"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -230,7 +231,7 @@ func extractJSONTargetResourceARN(r *http.Request, target, accountID, region str
}
var payload jsonTargetResourcePayload
- if jsonErr := json.Unmarshal(body, &payload); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &payload, jsonx.V1()); jsonErr != nil {
return ""
}
diff --git a/services/iam/role_templates.go b/services/iam/role_templates.go
index 2db30fd155..288582ab74 100644
--- a/services/iam/role_templates.go
+++ b/services/iam/role_templates.go
@@ -1,13 +1,14 @@
package iam
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"strconv"
"strings"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func roleTemplateVersionKey(templateArn string, minorVersion int32) string {
@@ -196,7 +197,7 @@ type acquiredRoleSpec struct {
// returning an error without having mutated any backend state.
func buildAcquiredRoleSpec(tv *RoleTemplateVersion, values map[string]string) (*acquiredRoleSpec, error) {
assumeRolePolicy := substituteTemplateParams(tv.AssumeRolePolicyDocumentTemplate, values)
- if assumeRolePolicy != "" && !json.Valid([]byte(assumeRolePolicy)) {
+ if assumeRolePolicy != "" && !jsontext.Value([]byte(assumeRolePolicy)).IsValid(jsonx.V1()) {
return nil, fmt.Errorf("%w: invalid JSON in AssumeRolePolicyDocument", ErrMalformedPolicyDocument)
}
diff --git a/services/iam/roles.go b/services/iam/roles.go
index 9fb017549e..2a13d95d57 100644
--- a/services/iam/roles.go
+++ b/services/iam/roles.go
@@ -1,7 +1,8 @@
package iam
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
@@ -11,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/collections"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -31,7 +33,7 @@ func (b *InMemoryBackend) CreateRole(
return nil, quotaExceeded("RolesPerAccount", limit)
}
- if assumeRolePolicyDocument != "" && !json.Valid([]byte(assumeRolePolicyDocument)) {
+ if assumeRolePolicyDocument != "" && !jsontext.Value([]byte(assumeRolePolicyDocument)).IsValid(jsonx.V1()) {
return nil, fmt.Errorf(
"%w: invalid JSON in AssumeRolePolicyDocument",
ErrMalformedPolicyDocument,
@@ -403,7 +405,7 @@ func (b *InMemoryBackend) UpdateAssumeRolePolicy(roleName, policyDocument string
return fmt.Errorf("%w: role %q not found", ErrRoleNotFound, roleName)
}
- if policyDocument != "" && !json.Valid([]byte(policyDocument)) {
+ if policyDocument != "" && !jsontext.Value([]byte(policyDocument)).IsValid(jsonx.V1()) {
return fmt.Errorf(
"%w: invalid JSON in AssumeRolePolicyDocument",
ErrMalformedPolicyDocument,
@@ -451,7 +453,7 @@ func (b *InMemoryBackend) UpdateRole(roleName, description string) error {
// parseJSON is a thin wrapper around json.Unmarshal for readability.
func parseJSON(s string, v any) error {
- return json.Unmarshal([]byte(s), v) //nolint:wrapcheck // thin delegation; callers handle the error
+ return json.Unmarshal([]byte(s), v, jsonx.V1()) //nolint:wrapcheck // thin delegation; callers handle the error
}
// TagRole merges the given key-value pairs into the role's Tags field.
@@ -522,7 +524,7 @@ type trustPrincipal struct {
func (p *trustPrincipal) UnmarshalJSON(data []byte) error {
// Try string first.
var s string
- if err := json.Unmarshal(data, &s); err == nil {
+ if err := json.Unmarshal(data, &s, jsonx.V1()); err == nil {
if s == "*" {
p.AllowAll = true
} else {
@@ -533,8 +535,8 @@ func (p *trustPrincipal) UnmarshalJSON(data []byte) error {
}
// Try typed map.
- var m map[string]json.RawMessage
- if err := json.Unmarshal(data, &m); err != nil {
+ var m map[string]jsontext.Value
+ if err := json.Unmarshal(data, &m, jsonx.V1()); err != nil {
return err //nolint:wrapcheck // UnmarshalJSON interface; wrapping changes the type
}
@@ -543,10 +545,10 @@ func (p *trustPrincipal) UnmarshalJSON(data []byte) error {
// Value can be a string or []string.
var single string
- if err := json.Unmarshal(v, &single); err == nil {
+ if err := json.Unmarshal(v, &single, jsonx.V1()); err == nil {
vals = []string{single}
} else {
- if err2 := json.Unmarshal(v, &vals); err2 != nil {
+ if err2 := json.Unmarshal(v, &vals, jsonx.V1()); err2 != nil {
continue
}
}
@@ -685,11 +687,11 @@ func (b *InMemoryBackend) validateTrustPolicyPrincipal(policyJSON string) error
var doc struct {
Statement []struct {
- Principal json.RawMessage `json:"Principal"`
+ Principal jsontext.Value `json:"Principal"`
} `json:"Statement"`
}
- if err := json.Unmarshal([]byte(policyJSON), &doc); err != nil {
+ if err := json.Unmarshal([]byte(policyJSON), &doc, jsonx.V1()); err != nil {
return nil //nolint:nilerr // JSON validity confirmed upstream; this path handles schema mismatch
}
@@ -699,7 +701,7 @@ func (b *InMemoryBackend) validateTrustPolicyPrincipal(policyJSON string) error
}
var p trustPrincipal
- if err := json.Unmarshal(stmt.Principal, &p); err != nil {
+ if err := json.Unmarshal(stmt.Principal, &p, jsonx.V1()); err != nil {
return fmt.Errorf(
"%w: statement[%d] Principal has unsupported shape",
ErrMalformedPolicyDocument, i,
diff --git a/services/identitystore/epoch_time_test.go b/services/identitystore/epoch_time_test.go
index e3705776cd..73a5895e81 100644
--- a/services/identitystore/epoch_time_test.go
+++ b/services/identitystore/epoch_time_test.go
@@ -1,12 +1,14 @@
package identitystore //nolint:testpackage // epochTime is unexported; white-box unit tests of its wire encoding.
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestEpochTimeMarshalJSON verifies epochTime renders as an AWS
@@ -43,7 +45,7 @@ func TestEpochTimeMarshalJSON(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- got, err := json.Marshal(epochTime(tt.in))
+ got, err := json.Marshal(epochTime(tt.in), jsonx.V1())
require.NoError(t, err)
assert.Equal(t, tt.want, string(got))
})
@@ -87,7 +89,7 @@ func TestEpochTimeUnmarshalJSON(t *testing.T) {
var got epochTime
- err := json.Unmarshal([]byte(tt.in), &got)
+ err := json.Unmarshal([]byte(tt.in), &got, jsonx.V1())
if tt.wantErr {
require.Error(t, err)
@@ -110,11 +112,11 @@ func TestEpochTimeRoundTrip(t *testing.T) {
original := time.Now().UTC()
- data, err := json.Marshal(epochTime(original))
+ data, err := json.Marshal(epochTime(original), jsonx.V1())
require.NoError(t, err)
var restored epochTime
- require.NoError(t, json.Unmarshal(data, &restored))
+ require.NoError(t, json.Unmarshal(data, &restored, jsonx.V1()))
assert.WithinDuration(t, original, time.Time(restored), time.Millisecond)
}
diff --git a/services/identitystore/guard_sentinel_test.go b/services/identitystore/guard_sentinel_test.go
index f54ff33709..cd7615a537 100644
--- a/services/identitystore/guard_sentinel_test.go
+++ b/services/identitystore/guard_sentinel_test.go
@@ -2,7 +2,7 @@ package identitystore //nolint:testpackage // pins errResponseWritten + unexport
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const internalTestStoreID = "d-1234567890"
@@ -34,7 +35,7 @@ func newInternalTestContext(t *testing.T, body []byte) (*echo.Context, *httptest
func mustJSON(t *testing.T, v any) []byte {
t.Helper()
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
return b
diff --git a/services/identitystore/handler.go b/services/identitystore/handler.go
index 98477ab150..796a197084 100644
--- a/services/identitystore/handler.go
+++ b/services/identitystore/handler.go
@@ -3,7 +3,7 @@ package identitystore
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -129,7 +130,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
IdentityStoreID string `json:"IdentityStoreId"`
}
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
return ""
}
@@ -306,7 +307,7 @@ func (h *Handler) parseAlternateIDRequest(c *echo.Context, body []byte) (alterna
IdentityStoreID string `json:"IdentityStoreId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
_ = h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
return alternateIDResult{}, errResponseWritten
@@ -444,7 +445,7 @@ func validNextToken(body []byte) bool {
NextToken string `json:"NextToken"`
}
- if json.Unmarshal(body, &req) != nil || req.NextToken == "" {
+ if json.Unmarshal(body, &req, jsonx.V1()) != nil || req.NextToken == "" {
return true
}
diff --git a/services/identitystore/handler_group_memberships.go b/services/identitystore/handler_group_memberships.go
index d6c1bd6b4f..6c1f343f42 100644
--- a/services/identitystore/handler_group_memberships.go
+++ b/services/identitystore/handler_group_memberships.go
@@ -2,12 +2,14 @@ package identitystore
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// maxIsMemberInGroupsIDs is the maximum number of GroupIds allowed per IsMemberInGroups request.
@@ -74,7 +76,7 @@ type listGroupMembershipsRequest struct {
func (h *Handler) handleCreateGroupMembership(ctx context.Context, c *echo.Context, body []byte) error {
var req createGroupMembershipRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -103,7 +105,7 @@ func (h *Handler) handleCreateGroupMembership(ctx context.Context, c *echo.Conte
func (h *Handler) handleDescribeGroupMembership(ctx context.Context, c *echo.Context, body []byte) error {
var req describeGroupMembershipRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -125,7 +127,7 @@ func (h *Handler) handleDescribeGroupMembership(ctx context.Context, c *echo.Con
func (h *Handler) handleListGroupMemberships(ctx context.Context, c *echo.Context, body []byte) error {
var req listGroupMembershipsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -152,7 +154,7 @@ func (h *Handler) handleListGroupMemberships(ctx context.Context, c *echo.Contex
func (h *Handler) handleDeleteGroupMembership(ctx context.Context, c *echo.Context, body []byte) error {
var req deleteGroupMembershipRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -173,7 +175,7 @@ func (h *Handler) handleDeleteGroupMembership(ctx context.Context, c *echo.Conte
func (h *Handler) handleGetGroupMembershipID(ctx context.Context, c *echo.Context, body []byte) error {
var req getGroupMembershipIDRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -202,7 +204,7 @@ func (h *Handler) handleGetGroupMembershipID(ctx context.Context, c *echo.Contex
func (h *Handler) handleListGroupMembershipsForMember(ctx context.Context, c *echo.Context, body []byte) error {
var req listGroupMembershipsForMemberRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -229,7 +231,7 @@ func (h *Handler) handleListGroupMembershipsForMember(ctx context.Context, c *ec
func (h *Handler) handleIsMemberInGroups(ctx context.Context, c *echo.Context, body []byte) error {
var req isMemberInGroupsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
diff --git a/services/identitystore/handler_groups.go b/services/identitystore/handler_groups.go
index 23253dd6e0..68fc6cbb66 100644
--- a/services/identitystore/handler_groups.go
+++ b/services/identitystore/handler_groups.go
@@ -2,11 +2,13 @@ package identitystore
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ----------------------------------------
@@ -56,7 +58,7 @@ type deleteGroupRequest struct {
func (h *Handler) handleCreateGroup(ctx context.Context, c *echo.Context, body []byte) error {
var req createGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -84,7 +86,7 @@ func (h *Handler) handleCreateGroup(ctx context.Context, c *echo.Context, body [
func (h *Handler) handleDescribeGroup(ctx context.Context, c *echo.Context, body []byte) error {
var req describeGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -107,7 +109,7 @@ func (h *Handler) handleDescribeGroup(ctx context.Context, c *echo.Context, body
//nolint:dupl // structurally parallel to handleListUsers; both validate MaxResults, filter, paginate
func (h *Handler) handleListGroups(ctx context.Context, c *echo.Context, body []byte) error {
var req listGroupsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -135,7 +137,7 @@ func (h *Handler) handleListGroups(ctx context.Context, c *echo.Context, body []
func (h *Handler) handleUpdateGroup(ctx context.Context, c *echo.Context, body []byte) error {
var req updateGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -160,7 +162,7 @@ func (h *Handler) handleUpdateGroup(ctx context.Context, c *echo.Context, body [
func (h *Handler) handleDeleteGroup(ctx context.Context, c *echo.Context, body []byte) error {
var req deleteGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
diff --git a/services/identitystore/handler_guard_test.go b/services/identitystore/handler_guard_test.go
index fbd0dce956..180b04eba9 100644
--- a/services/identitystore/handler_guard_test.go
+++ b/services/identitystore/handler_guard_test.go
@@ -1,13 +1,14 @@
package identitystore_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/identitystore"
)
@@ -303,7 +304,7 @@ func TestRequireIdentityStoreIDGuardReadsSingleWellFormedBody(t *testing.T) {
var resp map[string]any
require.NoError(
t,
- json.Unmarshal(rec.Body.Bytes(), &resp),
+ json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()),
"response body must be a single well-formed JSON document, not two writes concatenated: %s",
rec.Body.String(),
)
diff --git a/services/identitystore/handler_test.go b/services/identitystore/handler_test.go
index 024d1261e2..6acc68ac91 100644
--- a/services/identitystore/handler_test.go
+++ b/services/identitystore/handler_test.go
@@ -2,7 +2,7 @@ package identitystore_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/identitystore"
)
@@ -37,7 +38,7 @@ func doRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -58,7 +59,7 @@ func parseResponse(t *testing.T, rec *httptest.ResponseRecorder) map[string]any
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
@@ -179,7 +180,7 @@ func TestExtractOperationAndResource(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
diff --git a/services/identitystore/handler_users.go b/services/identitystore/handler_users.go
index 7b60c439a2..98b812f4fb 100644
--- a/services/identitystore/handler_users.go
+++ b/services/identitystore/handler_users.go
@@ -2,11 +2,13 @@ package identitystore
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ----------------------------------------
@@ -82,7 +84,7 @@ type listUsersRequest struct {
func (h *Handler) handleCreateUser(ctx context.Context, c *echo.Context, body []byte) error {
var req createUserRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -121,7 +123,7 @@ func (h *Handler) handleCreateUser(ctx context.Context, c *echo.Context, body []
func (h *Handler) handleDescribeUser(ctx context.Context, c *echo.Context, body []byte) error {
var req describeUserRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -144,7 +146,7 @@ func (h *Handler) handleDescribeUser(ctx context.Context, c *echo.Context, body
//nolint:dupl // structurally parallel to handleListGroups; both validate MaxResults, filter, paginate
func (h *Handler) handleListUsers(ctx context.Context, c *echo.Context, body []byte) error {
var req listUsersRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -172,7 +174,7 @@ func (h *Handler) handleListUsers(ctx context.Context, c *echo.Context, body []b
func (h *Handler) handleUpdateUser(ctx context.Context, c *echo.Context, body []byte) error {
var req updateUserRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -197,7 +199,7 @@ func (h *Handler) handleUpdateUser(ctx context.Context, c *echo.Context, body []
func (h *Handler) handleDeleteUser(ctx context.Context, c *echo.Context, body []byte) error {
var req deleteUserRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
diff --git a/services/identitystore/models.go b/services/identitystore/models.go
index 09872ab585..f0b52becb9 100644
--- a/services/identitystore/models.go
+++ b/services/identitystore/models.go
@@ -1,10 +1,11 @@
package identitystore
import (
- "encoding/json"
+ "encoding/json/v2"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ----------------------------------------
@@ -23,13 +24,13 @@ type epochTime time.Time
// MarshalJSON renders t as AWS JSON-protocol epoch seconds.
func (t epochTime) MarshalJSON() ([]byte, error) {
- return json.Marshal(awstime.Epoch(time.Time(t)))
+ return json.Marshal(awstime.Epoch(time.Time(t)), jsonx.V1())
}
// UnmarshalJSON parses AWS JSON-protocol epoch seconds back into t.
func (t *epochTime) UnmarshalJSON(data []byte) error {
var seconds float64
- if err := json.Unmarshal(data, &seconds); err != nil {
+ if err := json.Unmarshal(data, &seconds, jsonx.V1()); err != nil {
return err
}
diff --git a/services/identitystore/persistence.go b/services/identitystore/persistence.go
index f3eee1f31c..2baeda14db 100644
--- a/services/identitystore/persistence.go
+++ b/services/identitystore/persistence.go
@@ -2,7 +2,7 @@ package identitystore
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -49,11 +49,11 @@ func regionalDTOKeyFn[V any](d *regionalDTO[V]) string { return regionKey(d.Regi
// from an incompatible (older or newer) build of this backend as though it
// were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Counter int `json:"counter"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Counter int `json:"counter"`
+ Version int `json:"version"`
}
// persistenceDTOTables groups every ephemeral DTO table
@@ -169,7 +169,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreResourceTables rebuilds every store.Table on b from snap's
// per-table JSON, factored out of Restore to keep Restore's own cognitive
// complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
dtos := buildPersistenceDTORegistry()
if err := dtos.registry.RestoreAll(tables); err != nil {
diff --git a/services/inspector2/handler.go b/services/inspector2/handler.go
index 7c8c6b3237..c11209e6ea 100644
--- a/services/inspector2/handler.go
+++ b/services/inspector2/handler.go
@@ -1,7 +1,7 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -390,7 +391,7 @@ func decodeFilterListRequest(c *echo.Context, maxResultsLimit int32) (filterList
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
_ = c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
return req, false
diff --git a/services/inspector2/handler_cis_scans.go b/services/inspector2/handler_cis_scans.go
index 7b282ed8b5..bc812b670b 100644
--- a/services/inspector2/handler_cis_scans.go
+++ b/services/inspector2/handler_cis_scans.go
@@ -1,13 +1,14 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -58,7 +59,7 @@ func (h *Handler) handleCreateCisScanConfiguration(c *echo.Context) error {
SecurityLevel string `json:"securityLevel"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -89,7 +90,7 @@ func (h *Handler) handleDeleteCisScanConfiguration(c *echo.Context) error {
ScanConfigurationArn string `json:"scanConfigurationArn"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -114,7 +115,7 @@ func (h *Handler) handleUpdateCisScanConfiguration(c *echo.Context) error {
SecurityLevel string `json:"securityLevel"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -171,7 +172,7 @@ func (h *Handler) handleStartCisSession(c *echo.Context) error {
ScanJobID string `json:"scanJobId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -199,7 +200,7 @@ func (h *Handler) handleStopCisSession(c *echo.Context) error {
ScanJobID string `json:"scanJobId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -221,7 +222,7 @@ func (h *Handler) handleSendCisSessionHealth(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
@@ -255,7 +256,7 @@ func (h *Handler) handleSendCisSessionTelemetry(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
@@ -281,7 +282,7 @@ func (h *Handler) handleGetCisScanReport(c *echo.Context) error {
ReportFormat string `json:"reportFormat"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -314,7 +315,7 @@ func (h *Handler) handleGetCisScanResultDetails(c *echo.Context) error {
MaxResults int32 `json:"maxResults"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -415,7 +416,7 @@ func (h *Handler) listCisAggregation(c *echo.Context, l cisAggregationListing) e
MaxResults int32 `json:"maxResults"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
diff --git a/services/inspector2/handler_cis_scans_test.go b/services/inspector2/handler_cis_scans_test.go
index c5b6175dc6..3101596ac8 100644
--- a/services/inspector2/handler_cis_scans_test.go
+++ b/services/inspector2/handler_cis_scans_test.go
@@ -5,13 +5,14 @@ package inspector2_test
// canned data, and that unknown scan ARNs degrade benignly.
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/inspector2"
)
@@ -27,7 +28,7 @@ func createCisConfig(t *testing.T, h *inspector2.Handler, name string, accountID
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, ok := resp["scanConfigurationArn"].(string)
require.True(t, ok)
require.NotEmpty(t, arn)
@@ -43,7 +44,7 @@ func firstScanArn(t *testing.T, h *inspector2.Handler, cfgARN string) string {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
scans, _ := resp["scans"].([]any)
for _, raw := range scans {
@@ -71,7 +72,7 @@ func TestCisScans_CreatedConfigMaterializesScan(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var empty map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &empty))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &empty, jsonx.V1()))
scans, _ := empty["scans"].([]any)
assert.Empty(t, scans)
@@ -79,7 +80,7 @@ func TestCisScans_CreatedConfigMaterializesScan(t *testing.T) {
cfgARN := createCisConfig(t, h, "scan-a", []string{"111111111111"})
rec = auditDo(t, h, http.MethodPost, "/cis/scan/list", map[string]any{})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &empty))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &empty, jsonx.V1()))
scans, _ = empty["scans"].([]any)
require.Len(t, scans, 1)
@@ -102,7 +103,7 @@ func TestCisScans_ResultDetailsReflectConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results, _ := resp["scanResultDetails"].([]any)
// Two target accounts x catalog checks => non-empty, and every result is
@@ -137,7 +138,7 @@ func TestCisScans_ReportReflectsScan(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", resp["status"])
assert.Equal(t, scanARN, resp["scanArn"])
// totalChecks reported as a JSON number.
@@ -160,7 +161,7 @@ func TestCisScans_AggregationsConsistent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var byCheck map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &byCheck))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &byCheck, jsonx.V1()))
checks, _ := byCheck["checkAggregations"].([]any)
require.NotEmpty(t, checks)
@@ -178,7 +179,7 @@ func TestCisScans_AggregationsConsistent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var byTarget map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &byTarget))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &byTarget, jsonx.V1()))
targets, _ := byTarget["targetResourceAggregations"].([]any)
assert.Len(t, targets, 2)
}
@@ -198,7 +199,7 @@ func TestCisScans_DeleteConfigRemovesScans(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
scans, _ := resp["scans"].([]any)
assert.Empty(t, scans)
}
@@ -227,7 +228,7 @@ func TestCisScans_UnknownScanArnDegradesBenignly(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp[tc.key]
assert.True(t, ok)
})
@@ -323,7 +324,7 @@ func TestCisScanConfigurationLifecycle(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["scanConfigurationArn"])
},
},
@@ -336,7 +337,7 @@ func TestCisScanConfigurationLifecycle(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
cfgs, _ := resp["scanConfigurations"].([]any)
assert.Len(t, cfgs, 1)
},
@@ -415,7 +416,7 @@ func TestCisScanConfigurationFullCycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
cfgARN, _ := createResp["scanConfigurationArn"].(string)
require.NotEmpty(t, cfgARN)
@@ -434,7 +435,7 @@ func TestCisScanConfigurationFullCycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
cfgs, _ := listResp["scanConfigurations"].([]any)
assert.Empty(t, cfgs)
}
@@ -559,7 +560,7 @@ func TestCisSessionOps(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", resp["status"])
},
},
@@ -572,7 +573,7 @@ func TestCisSessionOps(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
_, ok := resp["scanResultDetails"]
assert.True(t, ok)
},
@@ -586,7 +587,7 @@ func TestCisSessionOps(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
scans, _ := resp["scans"].([]any)
assert.Empty(t, scans)
},
@@ -600,7 +601,7 @@ func TestCisSessionOps(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
_, ok := resp["checkAggregations"]
assert.True(t, ok)
},
@@ -614,7 +615,7 @@ func TestCisSessionOps(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
_, ok := resp["targetResourceAggregations"]
assert.True(t, ok)
},
diff --git a/services/inspector2/handler_code_security.go b/services/inspector2/handler_code_security.go
index 534a6cdced..d4daf8d4dd 100644
--- a/services/inspector2/handler_code_security.go
+++ b/services/inspector2/handler_code_security.go
@@ -1,13 +1,14 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -61,7 +62,7 @@ func (h *Handler) handleCreateCodeSecurityIntegration(c *echo.Context) error {
Type string `json:"type"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -91,7 +92,7 @@ func (h *Handler) handleDeleteCodeSecurityIntegration(c *echo.Context) error {
IntegrationArn string `json:"integrationArn"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -115,7 +116,7 @@ func (h *Handler) handleGetCodeSecurityIntegration(c *echo.Context) error {
IntegrationArn string `json:"integrationArn"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -164,7 +165,7 @@ func (h *Handler) handleUpdateCodeSecurityIntegration(c *echo.Context) error {
IntegrationArn string `json:"integrationArn"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -226,7 +227,7 @@ func (h *Handler) handleCreateCodeSecurityScanConfiguration(c *echo.Context) err
} `json:"configuration"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -252,7 +253,7 @@ func (h *Handler) handleDeleteCodeSecurityScanConfiguration(c *echo.Context) err
ScanConfigurationArn string `json:"scanConfigurationArn"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -277,7 +278,7 @@ func (h *Handler) handleGetCodeSecurityScanConfiguration(c *echo.Context) error
ScanConfigurationArn string `json:"scanConfigurationArn"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -354,7 +355,7 @@ func (h *Handler) handleUpdateCodeSecurityScanConfiguration(c *echo.Context) err
} `json:"configuration"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -483,7 +484,7 @@ func (h *Handler) handleBatchAssociateCodeSecurityScanConfiguration(
AssociateConfigurationRequests []codeSecurityBatchItemRequest `json:"associateConfigurationRequests"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -554,7 +555,7 @@ func (h *Handler) handleBatchDisassociateCodeSecurityScanConfiguration(
DisassociateConfigurationRequests []codeSecurityBatchItemRequest `json:"disassociateConfigurationRequests"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -594,7 +595,7 @@ func (h *Handler) handleListCodeSecurityScanConfigurationAssociations(c *echo.Co
MaxResults int32 `json:"maxResults"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -637,7 +638,7 @@ func (h *Handler) handleStartCodeSecurityScan(c *echo.Context) error {
} `json:"resource"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -667,7 +668,7 @@ func (h *Handler) handleGetCodeSecurityScan(c *echo.Context) error {
ScanID string `json:"scanId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
diff --git a/services/inspector2/handler_code_security_test.go b/services/inspector2/handler_code_security_test.go
index a93d07dbd0..43ec4ac15d 100644
--- a/services/inspector2/handler_code_security_test.go
+++ b/services/inspector2/handler_code_security_test.go
@@ -1,13 +1,14 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/inspector2"
)
@@ -25,7 +26,7 @@ func TestCodeSecurityIntegrationLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
integARN, _ := createResp["integrationArn"].(string)
require.NotEmpty(t, integARN)
assert.Equal(t, "ACTIVE", createResp["status"])
@@ -37,7 +38,7 @@ func TestCodeSecurityIntegrationLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, integARN, getResp["integrationArn"])
// List
@@ -45,7 +46,7 @@ func TestCodeSecurityIntegrationLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
integrations, _ := listResp["integrations"].([]any)
assert.Len(t, integrations, 1)
@@ -132,7 +133,7 @@ func TestCodeSecurityScanConfigurationLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
cfgARN, _ := createResp["scanConfigurationArn"].(string)
require.NotEmpty(t, cfgARN)
@@ -143,7 +144,7 @@ func TestCodeSecurityScanConfigurationLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "ACCOUNT", getResp["level"])
assert.NotContains(t, getResp, "status")
configuration, ok := getResp["configuration"].(map[string]any)
@@ -155,7 +156,7 @@ func TestCodeSecurityScanConfigurationLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
cfgs, _ := listResp["configurations"].([]any)
require.Len(t, cfgs, 1)
summary, ok := cfgs[0].(map[string]any)
@@ -191,7 +192,7 @@ func TestCodeSecurityScanConfigurationLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var batchAssocResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &batchAssocResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &batchAssocResp, jsonx.V1()))
successful, _ := batchAssocResp["successfulAssociations"].([]any)
assert.Len(t, successful, 1)
@@ -202,7 +203,7 @@ func TestCodeSecurityScanConfigurationLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var assocResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &assocResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &assocResp, jsonx.V1()))
assocs, _ := assocResp["associations"].([]any)
assert.Len(t, assocs, 1)
@@ -224,7 +225,7 @@ func TestCodeSecurityScanConfigurationLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var assocResp2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &assocResp2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &assocResp2, jsonx.V1()))
assocs2, _ := assocResp2["associations"].([]any)
assert.Empty(t, assocs2)
@@ -380,7 +381,7 @@ func TestCodeSecurityScanConfigurationValidation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
cfgARN, _ := createResp["scanConfigurationArn"].(string)
require.NotEmpty(t, cfgARN)
@@ -403,7 +404,7 @@ func TestCodeSecurityScanLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
scanID, _ := startResp["scanId"].(string)
require.NotEmpty(t, scanID)
@@ -414,7 +415,7 @@ func TestCodeSecurityScanLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, scanID, getResp["scanId"])
}
@@ -505,7 +506,7 @@ func TestConnectorLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["items"].([]any)
require.True(t, ok)
require.Len(t, items, 1)
@@ -537,7 +538,7 @@ func TestConnectorLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["items"].([]any)
assert.Len(t, items, 1)
@@ -553,7 +554,7 @@ func TestConnectorLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code, rec2.Body.String())
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
items2, _ := resp2["items"].([]any)
assert.Empty(t, items2)
},
@@ -572,12 +573,12 @@ func TestConnectorLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, connectorArn, resp["connectorArn"])
listRec := auditDo(t, h, http.MethodPost, "/connector/list", map[string]any{})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
items := listResp["items"].([]any)
require.Len(t, items, 1)
@@ -612,7 +613,7 @@ func TestConnectorLifecycle(t *testing.T) {
listRec := auditDo(t, h, http.MethodPost, "/connector/list", map[string]any{})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Empty(t, listResp["items"].([]any))
},
},
@@ -693,7 +694,7 @@ func TestConnectorScanConfigurationLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code, listRec.Body.String())
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
cfgs, ok := listResp["scanConfigurations"].([]any)
require.True(t, ok)
require.Len(t, cfgs, 1)
@@ -739,7 +740,7 @@ func TestConnectorScanConfigurationLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cfgs, _ := resp["scanConfigurations"].([]any)
require.Len(t, cfgs, 1)
assert.Equal(t, arnA, cfgs[0].(map[string]any)["awsConfigConnectorArn"])
diff --git a/services/inspector2/handler_connectors.go b/services/inspector2/handler_connectors.go
index 6861ff880d..b238162dff 100644
--- a/services/inspector2/handler_connectors.go
+++ b/services/inspector2/handler_connectors.go
@@ -1,13 +1,14 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -98,7 +99,7 @@ func (h *Handler) handleCreateConnector(c *echo.Context) error {
}
var req createConnectorRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse(errValidation, "invalid JSON"))
}
@@ -152,7 +153,7 @@ func (h *Handler) handleUpdateConnector(c *echo.Context) error {
}
var req updateConnectorRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse(errValidation, "invalid JSON"))
}
@@ -190,7 +191,7 @@ func (h *Handler) handleDeleteConnector(c *echo.Context) error {
ConnectorArn string `json:"connectorArn"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse(errValidation, "invalid JSON"))
}
@@ -405,7 +406,7 @@ func decodeListConnectorsRequest(c *echo.Context) (listConnectorsRequest, bool)
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
_ = c.JSON(http.StatusBadRequest, errorResponse(errValidation, "invalid JSON"))
return req, false
@@ -479,7 +480,7 @@ func (h *Handler) handleListConnectorScanConfigurations(c *echo.Context) error {
var req listConnectorScanConfigurationsRequest
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse(errValidation, "invalid JSON"))
}
}
@@ -533,7 +534,7 @@ func (h *Handler) handleUpdateConnectorScanConfiguration(c *echo.Context) error
}
var req updateConnectorScanConfigurationRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse(errValidation, "invalid JSON"))
}
diff --git a/services/inspector2/handler_coverage_reporting.go b/services/inspector2/handler_coverage_reporting.go
index 2ab081bcec..81142c5cdd 100644
--- a/services/inspector2/handler_coverage_reporting.go
+++ b/services/inspector2/handler_coverage_reporting.go
@@ -1,13 +1,14 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -90,7 +91,7 @@ func (h *Handler) handleListCoverageStatistics(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
diff --git a/services/inspector2/handler_coverage_reporting_test.go b/services/inspector2/handler_coverage_reporting_test.go
index 9da706e235..7e98228367 100644
--- a/services/inspector2/handler_coverage_reporting_test.go
+++ b/services/inspector2/handler_coverage_reporting_test.go
@@ -1,7 +1,7 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/inspector2"
)
@@ -20,7 +21,7 @@ func TestListCoverageEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
resources, ok := resp["coveredResources"].([]any)
require.True(t, ok)
assert.Empty(t, resources)
@@ -53,7 +54,7 @@ func TestListCoverageSeeded(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
resources, ok := resp["coveredResources"].([]any)
require.True(t, ok)
require.Len(t, resources, 2)
@@ -71,7 +72,7 @@ func TestListCoverageSeeded(t *testing.T) {
},
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
resources, ok = resp["coveredResources"].([]any)
require.True(t, ok)
require.Len(t, resources, 1)
@@ -106,7 +107,7 @@ func TestListCoverageStatistics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.InDelta(t, float64(3), resp["totalCounts"], 0)
groups, ok := resp["countsByGroup"].([]any)
require.True(t, ok)
@@ -117,7 +118,7 @@ func TestListCoverageStatistics(t *testing.T) {
"groupBy": "RESOURCE_TYPE",
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.InDelta(t, float64(3), resp["totalCounts"], 0)
groups, ok = resp["countsByGroup"].([]any)
@@ -231,7 +232,7 @@ func TestListCoverage_FilterCriteriaFacets(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
resources, ok := resp["coveredResources"].([]any)
require.True(t, ok)
require.Len(t, resources, 1)
diff --git a/services/inspector2/handler_ec2_configuration.go b/services/inspector2/handler_ec2_configuration.go
index 55a53aa2b9..c5d2d04398 100644
--- a/services/inspector2/handler_ec2_configuration.go
+++ b/services/inspector2/handler_ec2_configuration.go
@@ -1,12 +1,13 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -49,7 +50,7 @@ func (h *Handler) handleUpdateEc2DeepInspectionConfiguration(c *echo.Context) er
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
@@ -85,7 +86,7 @@ func (h *Handler) handleUpdateOrgEc2DeepInspectionConfiguration(c *echo.Context)
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
@@ -111,7 +112,7 @@ func (h *Handler) handleBatchGetMemberEc2DeepInspectionStatus(c *echo.Context) e
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
@@ -141,7 +142,7 @@ func (h *Handler) handleBatchUpdateMemberEc2DeepInspectionStatus(c *echo.Context
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
diff --git a/services/inspector2/handler_ec2_configuration_test.go b/services/inspector2/handler_ec2_configuration_test.go
index 935b035207..77b8c98d0d 100644
--- a/services/inspector2/handler_ec2_configuration_test.go
+++ b/services/inspector2/handler_ec2_configuration_test.go
@@ -1,12 +1,14 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestEc2DeepInspectionConfiguration(t *testing.T) {
@@ -36,7 +38,7 @@ func TestEc2DeepInspectionConfiguration(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
_, ok := resp["status"]
assert.True(t, ok)
},
@@ -50,7 +52,7 @@ func TestEc2DeepInspectionConfiguration(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
paths, _ := resp["packagePaths"].([]any)
assert.Len(t, paths, 2)
},
@@ -79,7 +81,7 @@ func TestEc2DeepInspectionConfiguration(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
members, _ := resp["accountIds"].([]any)
assert.Len(t, members, 1)
},
@@ -100,7 +102,7 @@ func TestEc2DeepInspectionConfiguration(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
accounts, _ := resp["accountIds"].([]any)
assert.Len(t, accounts, 1)
},
diff --git a/services/inspector2/handler_ecr_configuration.go b/services/inspector2/handler_ecr_configuration.go
index 47e53c0f36..aa2d71d0cc 100644
--- a/services/inspector2/handler_ecr_configuration.go
+++ b/services/inspector2/handler_ecr_configuration.go
@@ -1,12 +1,13 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -32,7 +33,7 @@ func (h *Handler) handleGetClustersForImage(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
diff --git a/services/inspector2/handler_ecr_configuration_test.go b/services/inspector2/handler_ecr_configuration_test.go
index fab46cc0f3..811c78b1ae 100644
--- a/services/inspector2/handler_ecr_configuration_test.go
+++ b/services/inspector2/handler_ecr_configuration_test.go
@@ -1,12 +1,14 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestGetClustersForImage exercises the fixed request shape: real
@@ -24,7 +26,7 @@ func TestGetClustersForImage(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp["cluster"]
assert.True(t, ok)
}
diff --git a/services/inspector2/handler_enablement.go b/services/inspector2/handler_enablement.go
index 1bc1177837..1d07e0857c 100644
--- a/services/inspector2/handler_enablement.go
+++ b/services/inspector2/handler_enablement.go
@@ -1,7 +1,7 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"regexp"
"slices"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -35,7 +36,7 @@ func (h *Handler) handleToggle(c *echo.Context, enable bool) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
@@ -123,7 +124,7 @@ func (h *Handler) handleBatchGetAccountStatus(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
}
@@ -196,7 +197,7 @@ func (h *Handler) handleGetConfiguration(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
}
@@ -245,7 +246,7 @@ func (h *Handler) handleUpdateConfiguration(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
@@ -293,7 +294,7 @@ func (h *Handler) handleListAccountPermissions(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
diff --git a/services/inspector2/handler_enablement_test.go b/services/inspector2/handler_enablement_test.go
index e577802608..fefed7d48f 100644
--- a/services/inspector2/handler_enablement_test.go
+++ b/services/inspector2/handler_enablement_test.go
@@ -1,13 +1,14 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/inspector2"
)
@@ -29,7 +30,7 @@ func TestEnableDisable(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
accounts, ok := resp["accounts"].([]any)
require.True(t, ok)
require.Len(t, accounts, 1)
@@ -54,7 +55,7 @@ func TestEnableDisable(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
accounts, ok := resp["accounts"].([]any)
require.True(t, ok)
require.Len(t, accounts, 1)
@@ -98,7 +99,7 @@ func TestBatchGetAccountStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
accounts, ok := resp["accounts"].([]any)
require.True(t, ok)
require.Len(t, accounts, 1)
@@ -119,7 +120,7 @@ func TestBatchGetAccountStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
accounts := resp["accounts"].([]any)
acc := accounts[0].(map[string]any)
state := acc["state"].(map[string]any)
@@ -152,7 +153,7 @@ func TestConfigurationGetUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasEC2 := resp["ec2Configuration"]
assert.True(t, hasEC2)
_, hasECR := resp["ecrConfiguration"]
@@ -177,7 +178,7 @@ func TestConfigurationGetUpdate(t *testing.T) {
// Verify via get
getRec := auditDo(t, h, http.MethodPost, "/configuration/get", nil)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
ec2Cfg := getResp["ec2Configuration"].(map[string]any)
scanModeState := ec2Cfg["scanModeState"].(map[string]any)
@@ -219,7 +220,7 @@ func TestAccountPermissions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
perms, ok := resp["permissions"].([]any)
require.True(t, ok)
@@ -244,7 +245,7 @@ func TestAccountPermissionsFilteredByService(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
perms, ok := resp["permissions"].([]any)
require.True(t, ok)
@@ -367,7 +368,7 @@ func TestEnableDisablePerResourceType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
accounts := resp["accounts"].([]any)
require.Len(t, accounts, 1)
diff --git a/services/inspector2/handler_encryption.go b/services/inspector2/handler_encryption.go
index 6f7c9c0a1f..cacb372695 100644
--- a/services/inspector2/handler_encryption.go
+++ b/services/inspector2/handler_encryption.go
@@ -1,12 +1,13 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -47,7 +48,7 @@ func (h *Handler) handleResetEncryptionKey(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
@@ -74,7 +75,7 @@ func (h *Handler) handleUpdateEncryptionKey(c *echo.Context) error {
ScanType string `json:"scanType"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
diff --git a/services/inspector2/handler_encryption_test.go b/services/inspector2/handler_encryption_test.go
index fd1f3ed103..4f249b92c8 100644
--- a/services/inspector2/handler_encryption_test.go
+++ b/services/inspector2/handler_encryption_test.go
@@ -1,12 +1,14 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestEncryptionKeyLifecycle(t *testing.T) {
@@ -36,7 +38,7 @@ func TestEncryptionKeyLifecycle(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "AWS_OWNED_KEY", resp["kmsKeyId"])
},
},
diff --git a/services/inspector2/handler_filters.go b/services/inspector2/handler_filters.go
index 0d7f8f6522..d7ed28d9c0 100644
--- a/services/inspector2/handler_filters.go
+++ b/services/inspector2/handler_filters.go
@@ -1,13 +1,14 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleCreateFilter handles POST /filters/create.
@@ -26,7 +27,7 @@ func (h *Handler) handleCreateFilter(c *echo.Context) error {
Reason string `json:"reason"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -72,7 +73,7 @@ func (h *Handler) handleUpdateFilter(c *echo.Context) error {
Name string `json:"name"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -109,7 +110,7 @@ func (h *Handler) handleDeleteFilter(c *echo.Context) error {
Arn string `json:"arn"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -142,7 +143,7 @@ func (h *Handler) handleListFilters(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
diff --git a/services/inspector2/handler_filters_test.go b/services/inspector2/handler_filters_test.go
index 3a807a37ac..f0590cb04b 100644
--- a/services/inspector2/handler_filters_test.go
+++ b/services/inspector2/handler_filters_test.go
@@ -1,7 +1,7 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/inspector2"
)
@@ -66,7 +67,7 @@ func TestFilterLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
filters, ok := resp["filters"].([]any)
require.True(t, ok)
require.Len(t, filters, 1)
@@ -89,7 +90,7 @@ func TestFilterLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
filters := resp["filters"].([]any)
require.Len(t, filters, 1)
@@ -116,7 +117,7 @@ func TestFilterLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
filters := resp["filters"].([]any)
require.Len(t, filters, 1)
@@ -139,13 +140,13 @@ func TestFilterLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, filterARN, resp["arn"])
// Verify via list
listRec := auditDo(t, h, http.MethodPost, "/filters/list", map[string]any{})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
filters := listResp["filters"].([]any)
require.Len(t, filters, 1)
@@ -176,7 +177,7 @@ func TestFilterLifecycle(t *testing.T) {
// Use a list check to verify filter was created
listRec := auditDo(t, h, http.MethodPost, "/filters/list", map[string]any{})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["filters"].([]any), 1)
rec := auditDo(t, h, http.MethodPost, "/filters/delete", map[string]any{
@@ -186,7 +187,7 @@ func TestFilterLifecycle(t *testing.T) {
listRec2 := auditDo(t, h, http.MethodPost, "/filters/list", map[string]any{})
var listResp2 map[string]any
- require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp2))
+ require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp2, jsonx.V1()))
assert.Empty(t, listResp2["filters"].([]any))
},
},
@@ -226,7 +227,7 @@ func TestFilterLifecycle(t *testing.T) {
listRec := auditDo(t, h, http.MethodPost, "/filters/list", map[string]any{})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
filters := listResp["filters"].([]any)
require.Len(t, filters, 1)
diff --git a/services/inspector2/handler_findings.go b/services/inspector2/handler_findings.go
index 5299e98cb2..52bbba360b 100644
--- a/services/inspector2/handler_findings.go
+++ b/services/inspector2/handler_findings.go
@@ -1,13 +1,14 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -173,6 +174,7 @@ func resourceToWire(r FindingResource) map[string]any {
return res
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) handleCreateFindingsReport(c *echo.Context) error {
body, err := httputils.ReadBody(c.Request())
if err != nil {
@@ -186,7 +188,7 @@ func (h *Handler) handleCreateFindingsReport(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
@@ -215,7 +217,7 @@ func (h *Handler) handleCancelFindingsReport(c *echo.Context) error {
ReportID string `json:"reportId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -237,7 +239,7 @@ func (h *Handler) handleGetFindingsReportStatus(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
@@ -286,7 +288,7 @@ func (h *Handler) handleListFindingAggregations(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
@@ -316,7 +318,7 @@ func (h *Handler) handleSearchVulnerabilities(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
@@ -404,7 +406,7 @@ func (h *Handler) handleBatchGetCodeSnippet(c *echo.Context) error {
FindingArns []string `json:"findingArns"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -430,7 +432,7 @@ func (h *Handler) handleBatchGetFindingDetails(c *echo.Context) error {
FindingArns []string `json:"findingArns"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
diff --git a/services/inspector2/handler_findings_core_test.go b/services/inspector2/handler_findings_core_test.go
index a0cbb60c19..76c62c5ec7 100644
--- a/services/inspector2/handler_findings_core_test.go
+++ b/services/inspector2/handler_findings_core_test.go
@@ -1,13 +1,14 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/inspector2"
)
@@ -27,7 +28,7 @@ func TestListFindingsBasic(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
findings, ok := resp["findings"]
require.True(t, ok)
require.NotNil(t, findings)
@@ -379,7 +380,7 @@ func TestListFindingsResponseStructure(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "findings", "response must have findings key")
}
diff --git a/services/inspector2/handler_findings_reports_test.go b/services/inspector2/handler_findings_reports_test.go
index 62438032ad..eb3d1b275e 100644
--- a/services/inspector2/handler_findings_reports_test.go
+++ b/services/inspector2/handler_findings_reports_test.go
@@ -1,13 +1,14 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/inspector2"
)
@@ -28,7 +29,7 @@ func TestFindingsReportLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
reportID, _ := createResp["reportId"].(string)
require.NotEmpty(t, reportID)
@@ -39,7 +40,7 @@ func TestFindingsReportLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var statusResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &statusResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &statusResp, jsonx.V1()))
assert.Equal(t, reportID, statusResp["reportId"])
assert.Equal(t, "SUCCEEDED", statusResp["status"])
@@ -74,7 +75,7 @@ func TestFindingAggregations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp["responses"]
assert.True(t, ok)
}
@@ -106,7 +107,7 @@ func TestSearchVulnerabilities(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
vulns, ok := resp["vulnerabilities"].([]any)
require.True(t, ok)
@@ -134,7 +135,7 @@ func TestSearchVulnerabilitiesNoMatches(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
vulns, ok := resp["vulnerabilities"].([]any)
require.True(t, ok)
@@ -161,7 +162,7 @@ func TestBatchGetCodeSnippet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results, ok := resp["codeSnippetResults"].([]any)
require.True(t, ok)
@@ -207,7 +208,7 @@ func TestBatchGetFindingDetails(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
details, ok := resp["findingDetails"].([]any)
require.True(t, ok)
diff --git a/services/inspector2/handler_members.go b/services/inspector2/handler_members.go
index aa25e8fd2c..2eaf30b7bc 100644
--- a/services/inspector2/handler_members.go
+++ b/services/inspector2/handler_members.go
@@ -1,13 +1,14 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -32,7 +33,7 @@ func (h *Handler) handleAssociateMember(c *echo.Context) error {
AccountID string `json:"accountId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -56,7 +57,7 @@ func (h *Handler) handleDisassociateMember(c *echo.Context) error {
AccountID string `json:"accountId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -77,7 +78,7 @@ func (h *Handler) handleGetMember(c *echo.Context) error {
AccountID string `json:"accountId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -116,7 +117,7 @@ func (h *Handler) handleListMembers(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
diff --git a/services/inspector2/handler_members_test.go b/services/inspector2/handler_members_test.go
index ba92c735ed..9e52bb1dd1 100644
--- a/services/inspector2/handler_members_test.go
+++ b/services/inspector2/handler_members_test.go
@@ -1,12 +1,14 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestMembersLifecycle(t *testing.T) {
@@ -36,7 +38,7 @@ func TestMembersLifecycle(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "111111111111", resp["accountId"])
},
},
@@ -64,7 +66,7 @@ func TestMembersLifecycle(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
m, _ := resp["member"].(map[string]any)
assert.Equal(t, "222222222222", m["accountId"])
},
@@ -78,7 +80,7 @@ func TestMembersLifecycle(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
members, _ := resp["members"].([]any)
assert.Len(t, members, 1)
},
diff --git a/services/inspector2/handler_organization.go b/services/inspector2/handler_organization.go
index a6dbb3d0cb..23f1d0a9f7 100644
--- a/services/inspector2/handler_organization.go
+++ b/services/inspector2/handler_organization.go
@@ -1,12 +1,13 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -40,7 +41,7 @@ func (h *Handler) handleEnableDelegatedAdminAccount(c *echo.Context) error {
ClientToken string `json:"clientToken"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -73,7 +74,7 @@ func (h *Handler) handleDisableDelegatedAdminAccount(c *echo.Context) error {
DelegatedAdminAccountID string `json:"delegatedAdminAccountId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -148,7 +149,7 @@ func (h *Handler) handleUpdateOrganizationConfiguration(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
diff --git a/services/inspector2/handler_organization_test.go b/services/inspector2/handler_organization_test.go
index 7c36eed66c..a386c90c6d 100644
--- a/services/inspector2/handler_organization_test.go
+++ b/services/inspector2/handler_organization_test.go
@@ -1,12 +1,14 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDelegatedAdminAccountLifecycle(t *testing.T) {
@@ -36,7 +38,7 @@ func TestDelegatedAdminAccountLifecycle(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "333333333333", resp["delegatedAdminAccountId"])
assert.Equal(t, "ENABLED", resp["status"])
},
@@ -50,7 +52,7 @@ func TestDelegatedAdminAccountLifecycle(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
d, _ := resp["delegatedAdmin"].(map[string]any)
assert.Equal(t, "333333333333", d["accountId"])
},
@@ -64,7 +66,7 @@ func TestDelegatedAdminAccountLifecycle(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
admins, _ := resp["delegatedAdminAccounts"].([]any)
assert.Len(t, admins, 1)
},
@@ -150,7 +152,7 @@ func TestOrganizationConfiguration(t *testing.T) {
t.Helper()
assert.Equal(t, http.StatusOK, code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
_, ok := resp["autoEnable"]
assert.True(t, ok)
},
diff --git a/services/inspector2/handler_sbom.go b/services/inspector2/handler_sbom.go
index a40b3346cb..1ec38d2ee4 100644
--- a/services/inspector2/handler_sbom.go
+++ b/services/inspector2/handler_sbom.go
@@ -1,12 +1,13 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -19,6 +20,7 @@ const (
pathSbomExportGet = "/sbomexport/get"
)
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) handleCreateSbomExport(c *echo.Context) error {
body, err := httputils.ReadBody(c.Request())
if err != nil {
@@ -36,7 +38,7 @@ func (h *Handler) handleCreateSbomExport(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
@@ -62,7 +64,7 @@ func (h *Handler) handleCancelSbomExport(c *echo.Context) error {
ReportID string `json:"reportId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
@@ -83,7 +85,7 @@ func (h *Handler) handleGetSbomExport(c *echo.Context) error {
ReportID string `json:"reportId"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
diff --git a/services/inspector2/handler_sbom_test.go b/services/inspector2/handler_sbom_test.go
index 3989d0f6f7..b0a4e0e53e 100644
--- a/services/inspector2/handler_sbom_test.go
+++ b/services/inspector2/handler_sbom_test.go
@@ -1,12 +1,14 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestSbomExportLifecycle(t *testing.T) {
@@ -28,7 +30,7 @@ func TestSbomExportLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
reportID, _ := createResp["reportId"].(string)
require.NotEmpty(t, reportID)
@@ -39,7 +41,7 @@ func TestSbomExportLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", getResp["status"])
assert.Equal(t, "CYCLONEDX_1_4", getResp["format"])
diff --git a/services/inspector2/handler_tags.go b/services/inspector2/handler_tags.go
index d97c85c755..3a75aa2584 100644
--- a/services/inspector2/handler_tags.go
+++ b/services/inspector2/handler_tags.go
@@ -1,12 +1,13 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleListTagsForResource handles GET /tags/{resourceArn}.
@@ -50,7 +51,7 @@ func (h *Handler) handleTagResource(c *echo.Context) error {
Tags map[string]string `json:"tags"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
diff --git a/services/inspector2/handler_tags_test.go b/services/inspector2/handler_tags_test.go
index 155a0ee66f..455e81a6a2 100644
--- a/services/inspector2/handler_tags_test.go
+++ b/services/inspector2/handler_tags_test.go
@@ -1,7 +1,7 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/inspector2"
)
@@ -40,7 +41,7 @@ func TestTagsLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code, listRec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
tags, ok := resp["tags"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "security", tags["team"])
@@ -72,7 +73,7 @@ func TestTagsLifecycle(t *testing.T) {
listRec := auditDo(t, h, http.MethodGet, "/tags/"+filterARN, nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
tags := resp["tags"].(map[string]any)
assert.Equal(t, "yes", tags["keep"])
_, hasRemove := tags["remove"]
@@ -122,7 +123,7 @@ func TestTagsLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code, listRec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
tags := resp["tags"].(map[string]any)
assert.Empty(t, tags)
},
diff --git a/services/inspector2/handler_test.go b/services/inspector2/handler_test.go
index eed8c2e36a..956e160b76 100644
--- a/services/inspector2/handler_test.go
+++ b/services/inspector2/handler_test.go
@@ -8,7 +8,7 @@ package inspector2_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/inspector2"
)
@@ -56,7 +57,7 @@ func auditDo(t *testing.T, h *inspector2.Handler, method, path string, body any)
if body != nil {
var err error
- b, err = json.Marshal(body)
+ b, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -82,7 +83,7 @@ func auditCreateFilter(t *testing.T, h *inspector2.Handler, name string) string
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, ok := resp["arn"].(string)
require.True(t, ok)
require.NotEmpty(t, arn)
@@ -95,7 +96,7 @@ func auditCreateFilter(t *testing.T, h *inspector2.Handler, name string) string
func inspector2UnmarshalError(t *testing.T, data []byte, v any) error {
t.Helper()
- return json.Unmarshal(data, v)
+ return json.Unmarshal(data, v, jsonx.V1())
}
// auditCreateConnector creates an Azure connector named name, associated
@@ -116,7 +117,7 @@ func auditCreateConnector(t *testing.T, h *inspector2.Handler, name, awsConfigCo
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, ok := resp["connectorArn"].(string)
require.True(t, ok)
require.NotEmpty(t, arn)
@@ -149,7 +150,7 @@ func parityDo(t *testing.T, h *inspector2.Handler, method, path string, body any
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -185,7 +186,7 @@ func parityListFindings(t *testing.T, h *inspector2.Handler, body map[string]any
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
findings, ok := resp["findings"].([]any)
if !ok {
@@ -205,7 +206,7 @@ func parityListFindingsWithToken(
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
findings, _ := resp["findings"].([]any)
nextToken, _ := resp["nextToken"].(string)
diff --git a/services/inspector2/handler_usage.go b/services/inspector2/handler_usage.go
index 4fea58a369..c520724591 100644
--- a/services/inspector2/handler_usage.go
+++ b/services/inspector2/handler_usage.go
@@ -1,12 +1,13 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -32,7 +33,7 @@ func (h *Handler) handleListUsageTotals(c *echo.Context) error {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("ValidationException", "invalid JSON"),
@@ -60,7 +61,7 @@ func (h *Handler) handleBatchGetFreeTrialInfo(c *echo.Context) error {
AccountIDs []string `json:"accountIds"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return c.JSON(http.StatusBadRequest, errorResponse("ValidationException", "invalid JSON"))
}
diff --git a/services/inspector2/handler_usage_test.go b/services/inspector2/handler_usage_test.go
index 3677fda903..798e80f8cd 100644
--- a/services/inspector2/handler_usage_test.go
+++ b/services/inspector2/handler_usage_test.go
@@ -1,13 +1,14 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/inspector2"
)
@@ -21,7 +22,7 @@ func TestUsageTotals(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
totals, ok := resp["totals"].([]any)
require.True(t, ok)
require.Len(t, totals, 1)
@@ -56,7 +57,7 @@ func TestUsageTotalsReflectsEnabledCoverage(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
totals, ok := resp["totals"].([]any)
require.True(t, ok)
require.Len(t, totals, 1)
@@ -84,7 +85,7 @@ func TestBatchGetFreeTrialInfo(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
accounts, _ := resp["accounts"].([]any)
assert.Len(t, accounts, 1)
}
diff --git a/services/inspector2/paging.go b/services/inspector2/paging.go
index 297a3f6741..0683290e8b 100644
--- a/services/inspector2/paging.go
+++ b/services/inspector2/paging.go
@@ -1,12 +1,13 @@
package inspector2
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const defaultPageSize = 100
@@ -30,7 +31,7 @@ func readPageRequest(c *echo.Context) (pageRequest, error) {
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return req, fmt.Errorf("%w: invalid JSON", ErrValidation)
}
}
diff --git a/services/inspector2/persistence.go b/services/inspector2/persistence.go
index c091672cfe..67fbdc2dbd 100644
--- a/services/inspector2/persistence.go
+++ b/services/inspector2/persistence.go
@@ -2,10 +2,12 @@ package inspector2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -79,7 +81,7 @@ const findingsTableName = "findings"
// incompatible (older or newer) build of this backend as though it were the
// current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
EnabledTypes map[string]bool `json:"enabledTypes"`
MemberEnabled map[string]map[string]bool `json:"memberEnabled,omitempty"`
@@ -121,7 +123,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
dtos[i] = toFindingSnapshot(f)
}
- findingsData, err := json.Marshal(dtos)
+ findingsData, err := json.Marshal(dtos, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "inspector2: snapshot findings table marshal failed", "error", err)
@@ -194,7 +196,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
if hasFindings {
var dtos []*findingSnapshot
- if err := json.Unmarshal(findingsRaw, &dtos); err != nil {
+ if err := json.Unmarshal(findingsRaw, &dtos, jsonx.V1()); err != nil {
return fmt.Errorf("inspector2: restore findings table: %w", err)
}
diff --git a/services/inspector2/required_output_members_test.go b/services/inspector2/required_output_members_test.go
index 122e316d00..43a23682fd 100644
--- a/services/inspector2/required_output_members_test.go
+++ b/services/inspector2/required_output_members_test.go
@@ -1,7 +1,7 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/inspector2/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestUpdateOrganizationConfiguration_AutoEnableAlwaysPresent proves
@@ -53,6 +55,6 @@ func TestUpdateOrganizationConfiguration_MissingAutoEnableRejected(t *testing.T)
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
diff --git a/services/inspector2/wire_field_fixes_y1zn_test.go b/services/inspector2/wire_field_fixes_y1zn_test.go
index b9ccda981b..94cde9bb57 100644
--- a/services/inspector2/wire_field_fixes_y1zn_test.go
+++ b/services/inspector2/wire_field_fixes_y1zn_test.go
@@ -1,13 +1,14 @@
package inspector2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/inspector2"
)
@@ -76,7 +77,7 @@ func TestListFindingAggregations_NoLowKey_RealClient(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
responses, ok := out["responses"].([]any)
require.True(t, ok)
require.NotEmpty(t, responses)
diff --git a/services/iot/authorizer_invoke.go b/services/iot/authorizer_invoke.go
index 8300b8a44a..3ebfda334f 100644
--- a/services/iot/authorizer_invoke.go
+++ b/services/iot/authorizer_invoke.go
@@ -7,7 +7,8 @@ import (
"crypto/sha256"
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"encoding/pem"
"errors"
"fmt"
@@ -15,6 +16,7 @@ import (
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/roleauth"
)
@@ -99,11 +101,11 @@ type authorizerMQTTData struct {
}
type authorizerResponse struct {
- DisconnectAfterInSeconds *int64 `json:"disconnectAfterInSeconds"`
- RefreshAfterInSeconds *int64 `json:"refreshAfterInSeconds"`
- PrincipalID string `json:"principalId"`
- PolicyDocuments []json.RawMessage `json:"policyDocuments"`
- IsAuthenticated bool `json:"isAuthenticated"`
+ DisconnectAfterInSeconds *int64 `json:"disconnectAfterInSeconds"`
+ RefreshAfterInSeconds *int64 `json:"refreshAfterInSeconds"`
+ PrincipalID string `json:"principalId"`
+ PolicyDocuments []jsontext.Value `json:"policyDocuments"`
+ IsAuthenticated bool `json:"isAuthenticated"`
}
// TestInvokeAuthorizer invokes the authorizer's Lambda function with the request.
@@ -166,7 +168,7 @@ func (b *InMemoryBackend) InvokeAuthorizer(
return nil, fmt.Errorf("%w: %w", errRuleActionDenied, err)
}
- payload, err := json.Marshal(buildAuthorizerEvent(req, verified))
+ payload, err := json.Marshal(buildAuthorizerEvent(req, verified), jsonx.V1())
if err != nil {
return nil, fmt.Errorf("authorizer event: %w", err)
}
@@ -247,7 +249,7 @@ func verifyTokenSignature(keys map[string]string, req *AuthorizerRequest) bool {
func parseAuthorizerResponse(raw []byte) (*TestInvokeAuthorizerOutput, error) {
var resp authorizerResponse
- if err := json.Unmarshal(raw, &resp); err != nil {
+ if err := json.Unmarshal(raw, &resp, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: not a JSON object", ErrInvalidAuthorizerResponse)
}
@@ -269,7 +271,7 @@ func parseAuthorizerResponse(raw []byte) (*TestInvokeAuthorizerOutput, error) {
doc := string(d)
var asString string
- if json.Unmarshal(d, &asString) == nil {
+ if json.Unmarshal(d, &asString, jsonx.V1()) == nil {
doc = asString
}
diff --git a/services/iot/broker_auth.go b/services/iot/broker_auth.go
index 08c8e28526..0b991fecbe 100644
--- a/services/iot/broker_auth.go
+++ b/services/iot/broker_auth.go
@@ -9,7 +9,7 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -23,6 +23,7 @@ import (
"github.com/mochi-mqtt/server/v2/packets"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iam"
)
@@ -325,7 +326,7 @@ func (b *InMemoryBackend) thingGroupARNsLocked(thingName string) []string {
}
func jsonEscape(v string) string {
- raw, err := json.Marshal(v)
+ raw, err := json.Marshal(v, jsonx.V1())
if err != nil || len(raw) < 2 {
return ""
}
diff --git a/services/iot/broker_commands.go b/services/iot/broker_commands.go
index d5927f45b1..30a90515ba 100644
--- a/services/iot/broker_commands.go
+++ b/services/iot/broker_commands.go
@@ -1,9 +1,11 @@
package iot
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -77,7 +79,7 @@ func (b *Broker) handleCommandResponse(r commandResponse, payload []byte) {
}
reply := func(outcome string, body map[string]any) {
- out, err := json.Marshal(body)
+ out, err := json.Marshal(body, jsonx.V1())
if err != nil {
return
}
@@ -101,7 +103,7 @@ func (b *Broker) handleCommandResponse(r commandResponse, payload []byte) {
}
var body commandResponseBody
- if err := json.Unmarshal(payload, &body); err != nil {
+ if err := json.Unmarshal(payload, &body, jsonx.V1()); err != nil {
reject(jobsCodeInvalidJSON, "payload is not valid JSON", "")
return
diff --git a/services/iot/broker_custom_auth_test.go b/services/iot/broker_custom_auth_test.go
index e0dad7d18a..ac8326faef 100644
--- a/services/iot/broker_custom_auth_test.go
+++ b/services/iot/broker_custom_auth_test.go
@@ -9,7 +9,7 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"fmt"
"net/url"
@@ -24,6 +24,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -40,7 +41,7 @@ type fakeAuthLambda struct {
func (f *fakeAuthLambda) RequestLambda(_ context.Context, _, _ string, payload []byte) ([]byte, error) {
var ev map[string]any
- if err := json.Unmarshal(payload, &ev); err != nil {
+ if err := json.Unmarshal(payload, &ev, jsonx.V1()); err != nil {
return nil, err
}
@@ -49,7 +50,7 @@ func (f *fakeAuthLambda) RequestLambda(_ context.Context, _, _ string, payload [
respond := f.respond
f.mu.Unlock()
- return json.Marshal(respond(ev))
+ return json.Marshal(respond(ev), jsonx.V1())
}
func (f *fakeAuthLambda) calls() []map[string]any {
@@ -68,7 +69,7 @@ func (f *fakeAuthLambda) setRespond(r func(map[string]any) any) {
func authzResponse(principal string, refresh, disconnect int, statements ...string) map[string]any {
var doc any
- _ = json.Unmarshal([]byte(authPolicy(statements...)), &doc)
+ _ = json.Unmarshal([]byte(authPolicy(statements...)), &doc, jsonx.V1())
out := map[string]any{"isAuthenticated": true, "principalId": principal, "policyDocuments": []any{doc}}
if refresh > 0 {
diff --git a/services/iot/broker_events.go b/services/iot/broker_events.go
index 71f20dab72..2b9e614e16 100644
--- a/services/iot/broker_events.go
+++ b/services/iot/broker_events.go
@@ -1,13 +1,15 @@
package iot
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
"sync/atomic"
"github.com/google/uuid"
mqtt "github.com/mochi-mqtt/server/v2"
"github.com/mochi-mqtt/server/v2/packets"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -41,7 +43,7 @@ func lifecycleEligible(clientID string) bool {
}
func (b *Broker) publishLifecycle(topic string, payload map[string]any) {
- raw, err := json.Marshal(payload)
+ raw, err := json.Marshal(payload, jsonx.V1())
if err != nil {
return
}
diff --git a/services/iot/broker_events_test.go b/services/iot/broker_events_test.go
index 5df3d3e3cc..6172551ccc 100644
--- a/services/iot/broker_events_test.go
+++ b/services/iot/broker_events_test.go
@@ -1,7 +1,7 @@
package iot_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"testing"
"time"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -79,7 +80,7 @@ func (w *eventWatcher) next(t *testing.T, topic string) map[string]any {
}
var out map[string]any
- require.NoError(t, json.Unmarshal(m.Payload(), &out))
+ require.NoError(t, json.Unmarshal(m.Payload(), &out, jsonx.V1()))
return out
case <-timeout:
@@ -174,7 +175,7 @@ func TestBrokerLifecycleEvents(t *testing.T) {
select {
case m := <-w.msgs:
var p map[string]any
- require.NoError(t, json.Unmarshal(m.Payload(), &p))
+ require.NoError(t, json.Unmarshal(m.Payload(), &p, jsonx.V1()))
switch {
case p["clientId"] != "dev-dup":
@@ -645,7 +646,7 @@ func TestBrokerConnectFailedEvent(t *testing.T) {
assert.Equal(t, "$aws/events/presence/connect_failed/intruder", m.Topic())
var p map[string]any
- require.NoError(t, json.Unmarshal(m.Payload(), &p))
+ require.NoError(t, json.Unmarshal(m.Payload(), &p, jsonx.V1()))
assert.Equal(t, "connect_failed", p["eventType"])
assert.Equal(t, "AUTHORIZATION_FAILED", p["connectFailureReason"])
assert.Equal(t, "intruder", p["clientId"])
diff --git a/services/iot/broker_jobs.go b/services/iot/broker_jobs.go
index 8e8f9b55e9..7c52d227ba 100644
--- a/services/iot/broker_jobs.go
+++ b/services/iot/broker_jobs.go
@@ -1,11 +1,14 @@
package iot
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -85,7 +88,7 @@ type jobsExecutionWire struct {
JobID string `json:"jobId"`
ThingName string `json:"thingName,omitempty"`
Status JobExecutionStatus `json:"status,omitempty"`
- JobDocument json.RawMessage `json:"jobDocument,omitempty"`
+ JobDocument jsontext.Value `json:"jobDocument,omitempty"`
ApproxSecondsBeforeTimedOut int64 `json:"approximateSecondsBeforeTimedOut,omitempty"`
QueuedAt int64 `json:"queuedAt"`
StartedAt int64 `json:"startedAt,omitempty"`
@@ -111,16 +114,16 @@ func executionWire(e *DeviceJobExecution) *jobsExecutionWire {
}
// documentRaw embeds a JSON job document as an object; MQTT carries it unquoted, unlike the HTTP API.
-func documentRaw(doc string) json.RawMessage {
+func documentRaw(doc string) jsontext.Value {
if doc == "" {
return nil
}
- if json.Valid([]byte(doc)) {
- return json.RawMessage(doc)
+ if jsontext.Value([]byte(doc)).IsValid(jsonx.V1()) {
+ return jsontext.Value(doc)
}
- quoted, _ := json.Marshal(doc)
+ quoted, _ := json.Marshal(doc, jsonx.V1())
return quoted
}
@@ -140,7 +143,7 @@ type jobsRequestBody struct {
func (b *Broker) handleJobsRequest(topic string, req jobsRequest, payload []byte) {
var body jobsRequestBody
if len(payload) > 0 {
- if err := json.Unmarshal(payload, &body); err != nil {
+ if err := json.Unmarshal(payload, &body, jsonx.V1()); err != nil {
b.publishJobs(topic+jobsSuffixReject, map[string]any{
"code": jobsCodeInvalidJSON,
jobsKeyMessage: "payload is not valid JSON",
@@ -343,7 +346,7 @@ func (b *Broker) rejectJobs(topic, clientToken, code, message string, state map[
}
func (b *Broker) publishJobs(topic string, body map[string]any) {
- payload, err := json.Marshal(body)
+ payload, err := json.Marshal(body, jsonx.V1())
if err != nil {
return
}
diff --git a/services/iot/broker_jobs_test.go b/services/iot/broker_jobs_test.go
index fa049cee80..3096075281 100644
--- a/services/iot/broker_jobs_test.go
+++ b/services/iot/broker_jobs_test.go
@@ -2,7 +2,7 @@ package iot_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"testing"
"time"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -113,7 +114,7 @@ func (e *jobsMQTTEnv) expect(t *testing.T, suffix string) map[string]any {
}
var out map[string]any
- require.NoError(t, json.Unmarshal(m.Payload(), &out))
+ require.NoError(t, json.Unmarshal(m.Payload(), &out, jsonx.V1()))
return out
case <-timer.C:
diff --git a/services/iot/command_executions_test.go b/services/iot/command_executions_test.go
index 5b6f143eb8..6c621e1669 100644
--- a/services/iot/command_executions_test.go
+++ b/services/iot/command_executions_test.go
@@ -2,7 +2,7 @@ package iot_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http/httptest"
@@ -24,6 +24,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -401,7 +402,7 @@ func TestCommandExecutionDeviceLifecycle(t *testing.T) {
e.respond(t, id, `{"status":"IN_PROGRESS","clientToken":"b"}`)
var rejected map[string]string
- require.NoError(t, json.Unmarshal(e.expect(t, base+"rejected/json"), &rejected))
+ require.NoError(t, json.Unmarshal(e.expect(t, base+"rejected/json"), &rejected, jsonx.V1()))
assert.Equal(t, "TerminalStateReached", rejected["error"])
assert.Equal(t, "b", rejected["clientToken"])
@@ -457,7 +458,7 @@ func TestCommandExecutionDeviceRejections(t *testing.T) {
t,
json.Unmarshal(
e.expect(t, "$aws/commands/things/"+cmdThing+"/executions/"+id+"/response/rejected/json"),
- &got,
+ &got, jsonx.V1(),
),
)
assert.Equal(t, tt.wantErr, got["error"])
@@ -475,7 +476,7 @@ func TestCommandExecutionDeviceRejections(t *testing.T) {
t,
json.Unmarshal(
e.expect(t, "$aws/commands/things/"+cmdThing+"/executions/ghost/response/rejected/json"),
- &got,
+ &got, jsonx.V1(),
),
)
assert.Equal(t, "ResourceNotFound", got["error"])
diff --git a/services/iot/events.go b/services/iot/events.go
index ab6b70ee94..5e2c7c63c0 100644
--- a/services/iot/events.go
+++ b/services/iot/events.go
@@ -1,13 +1,15 @@
package iot
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"slices"
"strings"
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// UpdateEventConfigurations keys that gate registry events.
@@ -92,7 +94,7 @@ func (b *InMemoryBackend) flushEvents() {
}
for _, ev := range events {
- raw, err := json.Marshal(ev.payload)
+ raw, err := json.Marshal(ev.payload, jsonx.V1())
if err != nil {
continue
}
diff --git a/services/iot/handler.go b/services/iot/handler.go
index 975a598aef..4665c3b240 100644
--- a/services/iot/handler.go
+++ b/services/iot/handler.go
@@ -2,7 +2,8 @@ package iot
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -14,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -322,7 +324,7 @@ func (h *Handler) handleCreateThing(c *echo.Context) error {
BillingGroupName string `json:"billingGroupName"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
@@ -549,7 +551,7 @@ func (h *Handler) handleUpdateThing(c *echo.Context) error {
var body UpdateThingInput
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
diff --git a/services/iot/handler_audit_suppressions_list_test.go b/services/iot/handler_audit_suppressions_list_test.go
index 47718618e0..3d026cae91 100644
--- a/services/iot/handler_audit_suppressions_list_test.go
+++ b/services/iot/handler_audit_suppressions_list_test.go
@@ -1,12 +1,14 @@
package iot_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestListAuditSuppressions_FilterOrderAndPagination proves
@@ -38,7 +40,7 @@ func TestListAuditSuppressions_FilterOrderAndPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
suppressions, _ := out["suppressions"].([]any)
require.Len(t, suppressions, 1)
})
@@ -51,7 +53,7 @@ func TestListAuditSuppressions_FilterOrderAndPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
suppressions, _ := out["suppressions"].([]any)
require.Len(t, suppressions, 1)
})
@@ -63,7 +65,7 @@ func TestListAuditSuppressions_FilterOrderAndPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
suppressions, _ := out["suppressions"].([]any)
require.Len(t, suppressions, 3)
@@ -79,7 +81,7 @@ func TestListAuditSuppressions_FilterOrderAndPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
suppressions, _ := out["suppressions"].([]any)
require.Len(t, suppressions, 3)
@@ -95,7 +97,7 @@ func TestListAuditSuppressions_FilterOrderAndPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
suppressions, _ := out["suppressions"].([]any)
require.Len(t, suppressions, 2)
assert.NotEmpty(t, out["nextToken"])
diff --git a/services/iot/handler_billing_groups.go b/services/iot/handler_billing_groups.go
index b39aa2e97a..babc149f45 100644
--- a/services/iot/handler_billing_groups.go
+++ b/services/iot/handler_billing_groups.go
@@ -1,19 +1,22 @@
package iot
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleAddThingToBillingGroup(c *echo.Context) error {
var body AddThingToBillingGroupInput
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
diff --git a/services/iot/handler_billing_groups_test.go b/services/iot/handler_billing_groups_test.go
index 7546d6289c..b8cac09850 100644
--- a/services/iot/handler_billing_groups_test.go
+++ b/services/iot/handler_billing_groups_test.go
@@ -1,12 +1,14 @@
package iot_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestNewOps_BillingGroup tests BillingGroup CRUD.
@@ -75,7 +77,7 @@ func TestBillingGroup_UpdateVersionConflict_Rejected(t *testing.T) {
var errBody struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "VersionConflictException", errBody.Type)
out := iotOK(t, h, http.MethodGet, "/billing-groups/ver-group", nil)
diff --git a/services/iot/handler_certificates.go b/services/iot/handler_certificates.go
index e9ab84ba71..0016760a5a 100644
--- a/services/iot/handler_certificates.go
+++ b/services/iot/handler_certificates.go
@@ -1,7 +1,8 @@
package iot
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -11,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -141,7 +143,7 @@ func (h *Handler) handleCreateCertificateFromCsr(c *echo.Context) error {
var body struct {
CertificateSigningRequest string `json:"certificateSigningRequest"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
@@ -174,7 +176,7 @@ func (h *Handler) handleRegisterCertificate(c *echo.Context) error {
CACertificatePem string `json:"caCertificatePem"`
Status string `json:"status"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
@@ -199,7 +201,7 @@ func (h *Handler) handleRegisterCertificateWithoutCA(c *echo.Context) error {
CertificatePem string `json:"certificatePem"`
Status string `json:"status"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
@@ -372,7 +374,7 @@ func (h *Handler) handleCreateCertificateProvider(c *echo.Context) error {
Tags []tags.KV `json:"tags,omitempty"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
@@ -450,7 +452,7 @@ func (h *Handler) handleUpdateCertificateProvider(c *echo.Context) error {
AccountDefaultForOperations []string `json:"accountDefaultForOperations"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
diff --git a/services/iot/handler_certificates_test.go b/services/iot/handler_certificates_test.go
index 5ac7896aad..78d92457b7 100644
--- a/services/iot/handler_certificates_test.go
+++ b/services/iot/handler_certificates_test.go
@@ -1,7 +1,7 @@
package iot_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -121,7 +122,7 @@ func TestListCertificatesByCA(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
certs, _ := out["certificates"].([]any)
require.Len(t, certs, 1)
@@ -632,7 +633,7 @@ func TestDescribeCertificate_ReturnsPem(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
certID, _ := cr["certificateId"].(string)
require.NotEmpty(t, certID)
@@ -640,7 +641,7 @@ func TestDescribeCertificate_ReturnsPem(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var desc map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &desc, jsonx.V1()))
certDesc, _ := desc["certificateDescription"].(map[string]any)
pem, _ := certDesc["certificatePem"].(string)
assert.NotEmpty(t, pem, "certificatePem must be present in DescribeCertificate response")
@@ -737,7 +738,7 @@ func TestTransferCertificate_ReturnsArn(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
certID, _ := cr["certificateId"].(string)
certARN, _ := cr["certificateArn"].(string)
require.NotEmpty(t, certID)
@@ -748,7 +749,7 @@ func TestTransferCertificate_ReturnsArn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
transferredARN, _ := resp["transferredCertificateArn"].(string)
assert.Equal(t, certARN, transferredARN, "transferredCertificateArn must be the cert ARN, not ID")
}
@@ -795,7 +796,7 @@ func TestListOutgoingCertificates_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
certs, _ := out["outgoingCertificates"].([]any)
assert.LessOrEqual(t, len(certs), 1)
assert.NotEmpty(t, out["nextMarker"])
diff --git a/services/iot/handler_helpers.go b/services/iot/handler_helpers.go
index fb29116175..ee5d7ce73a 100644
--- a/services/iot/handler_helpers.go
+++ b/services/iot/handler_helpers.go
@@ -1,7 +1,8 @@
package iot
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -9,6 +10,8 @@ import (
"strconv"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// errTypeInvalidRequest is the AWS IoT restjson1 __type for a malformed or
@@ -20,7 +23,12 @@ const errTypeInvalidRequest = "InvalidRequestException"
const maxTopicRuleBodyBytes int64 = 1 << 20
func readBody(c *echo.Context, dst any) error {
- if err := json.NewDecoder(c.Request().Body).Decode(dst); err != nil && !errors.Is(err, io.EOF) {
+ if err := json.UnmarshalDecode(
+ jsontext.NewDecoder(c.Request().Body),
+ dst,
+ jsonx.V1(),
+ ); err != nil &&
+ !errors.Is(err, io.EOF) {
if jsonErr := c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()}); jsonErr != nil {
return jsonErr
}
diff --git a/services/iot/handler_helpers_test.go b/services/iot/handler_helpers_test.go
index fe9e4c9ff5..39c0f689ff 100644
--- a/services/iot/handler_helpers_test.go
+++ b/services/iot/handler_helpers_test.go
@@ -2,7 +2,8 @@ package iot_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -48,15 +50,15 @@ func TestReadBodyMalformedWritesExactlyOneDocument(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
- dec := json.NewDecoder(rec.Body)
+ dec := jsontext.NewDecoder(rec.Body)
var first map[string]any
- require.NoError(t, dec.Decode(&first))
+ require.NoError(t, json.UnmarshalDecode(dec, &first, jsonx.V1()))
require.Contains(t, first, "__type")
require.Contains(t, first, "message")
var second map[string]any
- err := dec.Decode(&second)
+ err := json.UnmarshalDecode(dec, &second, jsonx.V1())
require.ErrorIs(
t,
err,
@@ -95,7 +97,7 @@ func TestReadBodyMalformed_RealSDKClient_DecodesInvalidRequestException(t *testi
require.Equal(t, http.StatusBadRequest, rec.Code)
- errorType, message, err := restjson.GetErrorInfo(json.NewDecoder(rec.Body))
+ errorType, message, err := restjson.GetErrorInfo(jsonx.SDKDecoder(rec.Body))
require.NoError(t, err)
assert.Equal(t, "InvalidRequestException", errorType,
"a real client must decode a modeled error code, not fall through to UnknownError")
diff --git a/services/iot/handler_indexing.go b/services/iot/handler_indexing.go
index 6ab042b8dc..60e89ad97b 100644
--- a/services/iot/handler_indexing.go
+++ b/services/iot/handler_indexing.go
@@ -1,13 +1,16 @@
package iot
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// resolveIndexOps maps fleet-indexing REST paths to their operation names.
@@ -96,7 +99,12 @@ func (h *Handler) dispatchIndexOps(c *echo.Context, op string) (bool, error) {
const keyCount = "count"
func decodeJSONBody(c *echo.Context, v any) error {
- if err := json.NewDecoder(c.Request().Body).Decode(v); err != nil && !errors.Is(err, io.EOF) {
+ if err := json.UnmarshalDecode(
+ jsontext.NewDecoder(c.Request().Body),
+ v,
+ jsonx.V1(),
+ ); err != nil &&
+ !errors.Is(err, io.EOF) {
return err
}
diff --git a/services/iot/handler_jobs.go b/services/iot/handler_jobs.go
index c783d29b74..b9eb838756 100644
--- a/services/iot/handler_jobs.go
+++ b/services/iot/handler_jobs.go
@@ -1,7 +1,8 @@
package iot
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -9,6 +10,8 @@ import (
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleAssociateTargetsWithJob(c *echo.Context) error {
@@ -22,7 +25,7 @@ func (h *Handler) handleAssociateTargetsWithJob(c *echo.Context) error {
Targets []string `json:"targets"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
@@ -379,7 +382,12 @@ func (h *Handler) handleCancelJobExecution(c *echo.Context) error {
ExpectedVersion int64 `json:"expectedVersion"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil && !errors.Is(err, io.EOF) {
+ if err := json.UnmarshalDecode(
+ jsontext.NewDecoder(c.Request().Body),
+ &body,
+ jsonx.V1(),
+ ); err != nil &&
+ !errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
diff --git a/services/iot/handler_jobs_test.go b/services/iot/handler_jobs_test.go
index 12cb7a44e6..12ea1231b0 100644
--- a/services/iot/handler_jobs_test.go
+++ b/services/iot/handler_jobs_test.go
@@ -2,7 +2,7 @@ package iot_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -233,7 +234,7 @@ func TestJobExecution_RoutingAndStateGuards(t *testing.T) {
assert.Equal(t, http.StatusConflict, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidStateTransitionException", body["__type"])
},
},
@@ -278,7 +279,7 @@ func TestJobExecution_RoutingAndStateGuards(t *testing.T) {
assert.Equal(t, http.StatusConflict, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "VersionConflictException", body["__type"])
},
},
@@ -888,7 +889,7 @@ func TestHandler_ListJobsNamespaceFilter(t *testing.T) {
JobID string `json:"jobId"`
} `json:"jobs"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
got := make([]string, 0, len(out.Jobs))
for _, j := range out.Jobs {
got = append(got, j.JobID)
diff --git a/services/iot/handler_packages.go b/services/iot/handler_packages.go
index e8c94ce54b..be0ee8cb82 100644
--- a/services/iot/handler_packages.go
+++ b/services/iot/handler_packages.go
@@ -1,13 +1,16 @@
package iot
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// maxPackagePathSegments is the number of segments in /packages/{pkg}/versions/{ver}/sbom.
@@ -35,7 +38,7 @@ func (h *Handler) handleAssociateSbomWithPackageVersion(c *echo.Context) error {
Sbom *SbomDocument `json:"sbom"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
diff --git a/services/iot/handler_policies.go b/services/iot/handler_policies.go
index d0459553fe..494f147463 100644
--- a/services/iot/handler_policies.go
+++ b/services/iot/handler_policies.go
@@ -1,7 +1,8 @@
package iot
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -11,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -189,7 +191,7 @@ func (h *Handler) handleCreatePolicy(c *echo.Context) error {
Tags []tags.KV `json:"tags,omitempty"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
@@ -218,7 +220,7 @@ func (h *Handler) handleAttachPolicy(c *echo.Context) error {
Target string `json:"target"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
@@ -307,7 +309,7 @@ func (h *Handler) handleDetachPolicy(c *echo.Context) error {
Target string `json:"target"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
@@ -343,7 +345,7 @@ func (h *Handler) handleListAttachedPolicies(c *echo.Context) error {
Target string `json:"target"`
Recursive bool `json:"recursive"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
@@ -408,7 +410,7 @@ func (h *Handler) handleCreatePolicyVersion(c *echo.Context) error {
PolicyDocument string `json:"policyDocument"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
diff --git a/services/iot/handler_policies_test.go b/services/iot/handler_policies_test.go
index 2291fe87cb..9e3c611522 100644
--- a/services/iot/handler_policies_test.go
+++ b/services/iot/handler_policies_test.go
@@ -1,7 +1,8 @@
package iot_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -105,7 +107,7 @@ func TestListPrincipalThingsV2_WireShapeAndFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
objs, _ := out["principalThingObjects"].([]any)
require.Len(t, objs, 1)
entry, ok := objs[0].(map[string]any)
@@ -186,7 +188,7 @@ func TestPolicyPrincipalListing_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
policies, _ := out["policies"].([]any)
assert.LessOrEqual(t, len(policies), 1)
assert.NotEmpty(t, out["nextMarker"])
@@ -209,7 +211,7 @@ func TestPolicyPrincipalListing_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
princs, _ := out["principals"].([]any)
assert.LessOrEqual(t, len(princs), 1)
assert.NotEmpty(t, out["nextMarker"])
@@ -229,7 +231,7 @@ func TestPolicyPrincipalListing_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
targets, _ := out["targets"].([]any)
assert.LessOrEqual(t, len(targets), 1)
assert.NotEmpty(t, out["nextMarker"])
@@ -252,7 +254,7 @@ func TestPolicyPrincipalListing_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
things, _ := out["things"].([]any)
assert.LessOrEqual(t, len(things), 1)
assert.NotEmpty(t, out["nextToken"])
@@ -456,7 +458,7 @@ func TestHandler_ListPolicies_Response(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
policies, ok := out["policies"]
require.True(t, ok, "response must have 'policies' key")
@@ -480,7 +482,7 @@ func TestHandler_GetPolicy_HTTP(t *testing.T) {
// creationDate/lastModifiedDate are JSON numbers (epoch seconds), not
// RFC3339 strings, so this must decode into map[string]any.
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
assert.Equal(t, "http-policy", out["policyName"])
assert.NotEmpty(t, out["policyArn"])
@@ -1011,7 +1013,7 @@ func TestGetPolicyVersion_ReturnsPolicyArnAndName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "parb-pol2", resp["policyName"], "policyName must be in GetPolicyVersion response")
policyARN, _ := resp["policyArn"].(string)
assert.NotEmpty(t, policyARN, "policyArn must be in GetPolicyVersion response")
diff --git a/services/iot/handler_routing_test.go b/services/iot/handler_routing_test.go
index 4e5604d5f7..d0df4bf156 100644
--- a/services/iot/handler_routing_test.go
+++ b/services/iot/handler_routing_test.go
@@ -2,7 +2,7 @@ package iot_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -40,7 +41,7 @@ func TestHandler_Operations(t *testing.T) {
validateOutput: func(t *testing.T, body []byte) {
t.Helper()
var out map[string]string
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
assert.Equal(t, "my-thing", out["thingName"])
},
},
@@ -101,7 +102,7 @@ func TestHandler_Operations(t *testing.T) {
var reqBody []byte
if tt.body != nil {
var err error
- reqBody, err = json.Marshal(tt.body)
+ reqBody, err = json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
}
@@ -372,7 +373,7 @@ func TestHandler_NewOperations(t *testing.T) {
var reqBody []byte
if tt.body != nil {
var err error
- reqBody, err = json.Marshal(tt.body)
+ reqBody, err = json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
}
diff --git a/services/iot/handler_tags_wire_test.go b/services/iot/handler_tags_wire_test.go
index dfde622508..2732824dad 100644
--- a/services/iot/handler_tags_wire_test.go
+++ b/services/iot/handler_tags_wire_test.go
@@ -1,7 +1,7 @@
package iot_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -38,7 +39,7 @@ func decodeTagList(t *testing.T, body []byte) map[string]string {
Value string `json:"Value"`
} `json:"tags"`
}
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
out := make(map[string]string, len(resp.Tags))
for _, item := range resp.Tags {
out[item.Key] = item.Value
@@ -435,7 +436,7 @@ func TestCreateThenListTagsForResourceRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code, "body: %s", createRec.Body.String())
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
resourceARN, ok := created[tt.arnField].(string)
require.Truef(t, ok && resourceARN != "", "response missing %q: %s", tt.arnField, createRec.Body.String())
@@ -463,7 +464,7 @@ func TestTagUntagResourceAfterCreationTags(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code, "body: %s", createRec.Body.String())
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
resourceARN, _ := created["thingGroupArn"].(string)
require.NotEmpty(t, resourceARN)
diff --git a/services/iot/handler_test.go b/services/iot/handler_test.go
index 5b6dd7c9d8..c31c73dcc6 100644
--- a/services/iot/handler_test.go
+++ b/services/iot/handler_test.go
@@ -2,7 +2,8 @@ package iot_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -27,7 +29,7 @@ func doIoTRequest(t *testing.T, h *iot.Handler, method, path string, body any) *
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -66,7 +68,7 @@ func iotRequest(
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
if err != nil {
t.Fatalf("marshal request body: %v", err)
}
@@ -93,7 +95,7 @@ func iotOK(t *testing.T, h *iot.Handler, method, path string, body any) map[stri
return map[string]any{}
}
var out map[string]any
- if err := json.Unmarshal(rec.Body.Bytes(), &out); err != nil {
+ if err := json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()); err != nil {
t.Fatalf("decode response: %v", err)
}
@@ -134,7 +136,7 @@ func doRefRequest(
if body != nil {
var err error
- raw, err = json.Marshal(body)
+ raw, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -166,7 +168,7 @@ func doRequest(t *testing.T, h *iot.Handler, method, path string, body any) *htt
if body != nil {
var err error
- reqBody, err = json.Marshal(body)
+ reqBody, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -204,7 +206,7 @@ func r3JSON(t *testing.T, h *iot.Handler, method, path string, body any, out any
t.Helper()
code, raw := r3Req(t, h, method, path, body)
if out != nil {
- require.NoError(t, json.Unmarshal(raw, out))
+ require.NoError(t, json.Unmarshal(raw, out, jsonx.V1()))
}
return code
@@ -541,7 +543,7 @@ func TestHandler_ListThings_ResponseFormat(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
things, ok := out["things"]
require.True(t, ok, "response must have 'things' key")
@@ -587,7 +589,7 @@ func TestHandler_ListThingPrincipals_HTTP(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
principals, ok := out["principals"].([]any)
require.True(t, ok)
require.Len(t, principals, 1)
@@ -641,7 +643,7 @@ func TestDescribeThing_DefaultClientId(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clientID, _ := resp["defaultClientId"].(string)
assert.Equal(t, tt.thingName, clientID, "defaultClientId must equal thingName")
})
@@ -680,7 +682,7 @@ func TestListThings_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp listResp
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.LessOrEqual(t, len(resp.Things), 2, "page exceeds maxResults")
for _, th := range resp.Things {
@@ -733,7 +735,7 @@ func TestListThingGroupsForThing_WireShapeAndPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
groups, ok := out["thingGroups"].([]any)
require.True(t, ok)
require.Len(t, groups, total)
@@ -751,7 +753,7 @@ func TestListThingGroupsForThing_WireShapeAndPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
groups, _ := out["thingGroups"].([]any)
assert.LessOrEqual(t, len(groups), 2)
assert.NotEmpty(t, out["nextToken"])
@@ -780,7 +782,7 @@ func TestListThingPrincipals_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
principals, _ := out["principals"].([]any)
assert.LessOrEqual(t, len(principals), 2)
assert.NotEmpty(t, out["nextToken"])
@@ -815,7 +817,7 @@ func TestListThingPrincipalsV2_ThingPrincipalTypeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
objs, _ := out["thingPrincipalObjects"].([]any)
require.Len(t, objs, 1, "EXCLUSIVE_THING filter must return only the EXCLUSIVE_THING attachment")
entry, _ := objs[0].(map[string]any)
@@ -827,7 +829,7 @@ func TestListThingPrincipalsV2_ThingPrincipalTypeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
objs2, _ := out2["thingPrincipalObjects"].([]any)
require.Len(t, objs2, 1)
entry2, _ := objs2[0].(map[string]any)
diff --git a/services/iot/handler_thing_groups.go b/services/iot/handler_thing_groups.go
index 4ab50661d4..5a9434550d 100644
--- a/services/iot/handler_thing_groups.go
+++ b/services/iot/handler_thing_groups.go
@@ -1,7 +1,8 @@
package iot
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -10,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -90,7 +92,7 @@ func (h *Handler) dispatchThingGroupOps(c *echo.Context, op string) (bool, error
func (h *Handler) handleAddThingToThingGroup(c *echo.Context) error {
var body AddThingToThingGroupInput
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
@@ -115,7 +117,7 @@ func (h *Handler) handleCreateThingGroup(c *echo.Context) error {
Tags []tags.KV `json:"tags,omitempty"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
@@ -222,7 +224,7 @@ func (h *Handler) handleUpdateThingGroup(c *echo.Context) error {
ExpectedVersion *int64 `json:"expectedVersion"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
@@ -266,7 +268,7 @@ func (h *Handler) handleDeleteThingGroup(c *echo.Context) error {
func (h *Handler) handleRemoveThingFromThingGroup(c *echo.Context) error {
var body RemoveThingFromThingGroupInput
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
diff --git a/services/iot/handler_thing_types.go b/services/iot/handler_thing_types.go
index f10681ff2d..2eabbddabe 100644
--- a/services/iot/handler_thing_types.go
+++ b/services/iot/handler_thing_types.go
@@ -1,7 +1,8 @@
package iot
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -10,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -72,7 +74,7 @@ func (h *Handler) handleCreateThingType(c *echo.Context) error {
Tags []tags.KV `json:"tags,omitempty"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
@@ -170,7 +172,7 @@ func (h *Handler) handleDeprecateThingType(c *echo.Context) error {
UndoDeprecate bool `json:"undoDeprecate"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
diff --git a/services/iot/handler_thing_types_test.go b/services/iot/handler_thing_types_test.go
index 75843409e7..f330c9a2ce 100644
--- a/services/iot/handler_thing_types_test.go
+++ b/services/iot/handler_thing_types_test.go
@@ -1,7 +1,8 @@
package iot_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -149,7 +151,7 @@ func TestHandler_DescribeThing_IncludesThingTypeName(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
assert.Equal(t, "SensorType", out["thingTypeName"])
}
diff --git a/services/iot/handler_thingtype_searchable_test.go b/services/iot/handler_thingtype_searchable_test.go
index 318c84eff6..a0a3b0fb2d 100644
--- a/services/iot/handler_thingtype_searchable_test.go
+++ b/services/iot/handler_thingtype_searchable_test.go
@@ -1,12 +1,14 @@
package iot_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestIoT_ThingTypes_SearchableAttributes validates that searchableAttributes
@@ -65,7 +67,7 @@ func TestIoT_ThingTypes_SearchableAttributes(t *testing.T) {
} `json:"thingTypeProperties"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantSearchable, resp.ThingTypeProperties.SearchableAttributes)
})
}
diff --git a/services/iot/handler_topic_rules.go b/services/iot/handler_topic_rules.go
index b520dd13a0..6ce0ca07c9 100644
--- a/services/iot/handler_topic_rules.go
+++ b/services/iot/handler_topic_rules.go
@@ -1,7 +1,8 @@
package iot
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -12,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// resolveTopicRuleDestinationOps resolves the topic-rule-destination op
@@ -148,14 +150,14 @@ func (h *Handler) handleCreateTopicRule(c *echo.Context) error {
var wrapped struct {
TopicRulePayload *TopicRulePayload `json:"topicRulePayload"`
}
- if jsonErr := json.Unmarshal(rawBody, &wrapped); jsonErr != nil && !errors.Is(jsonErr, io.EOF) {
+ if jsonErr := json.Unmarshal(rawBody, &wrapped, jsonx.V1()); jsonErr != nil && !errors.Is(jsonErr, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, jsonErr.Error()})
}
payload := wrapped.TopicRulePayload
if payload == nil {
var flat TopicRulePayload
- if jsonErr := json.Unmarshal(rawBody, &flat); jsonErr == nil && flat.SQL != "" {
+ if jsonErr := json.Unmarshal(rawBody, &flat, jsonx.V1()); jsonErr == nil && flat.SQL != "" {
payload = &flat
}
}
@@ -329,14 +331,14 @@ func (h *Handler) handleReplaceTopicRule(c *echo.Context) error {
var wrapped struct {
TopicRulePayload *TopicRulePayload `json:"topicRulePayload"`
}
- if jsonErr := json.Unmarshal(rawBody, &wrapped); jsonErr != nil && !errors.Is(jsonErr, io.EOF) {
+ if jsonErr := json.Unmarshal(rawBody, &wrapped, jsonx.V1()); jsonErr != nil && !errors.Is(jsonErr, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, jsonErr.Error()})
}
payload := wrapped.TopicRulePayload
if payload == nil {
var flat TopicRulePayload
- if jsonErr := json.Unmarshal(rawBody, &flat); jsonErr == nil && flat.SQL != "" {
+ if jsonErr := json.Unmarshal(rawBody, &flat, jsonx.V1()); jsonErr == nil && flat.SQL != "" {
payload = &flat
}
}
@@ -416,7 +418,7 @@ func (h *Handler) handleCreateTopicRuleDestination(c *echo.Context) error {
var body struct {
DestinationConfiguration *TopicRuleDestinationConfiguration `json:"destinationConfiguration"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
@@ -472,7 +474,7 @@ func (h *Handler) handleUpdateTopicRuleDestination(c *echo.Context) error {
ARN string `json:"arn"`
Status string `json:"status"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
!errors.Is(err, io.EOF) {
return c.JSON(http.StatusBadRequest, awsErrBody{errTypeInvalidRequest, err.Error()})
}
diff --git a/services/iot/handler_topic_rules_test.go b/services/iot/handler_topic_rules_test.go
index d1480c0ed0..f860e33841 100644
--- a/services/iot/handler_topic_rules_test.go
+++ b/services/iot/handler_topic_rules_test.go
@@ -1,7 +1,8 @@
package iot_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -102,7 +104,7 @@ func TestGetTopicRule_RuleWrapper(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "ruleArn")
assert.Contains(t, resp, "rule")
})
@@ -169,7 +171,7 @@ func TestCreateAndGetTopicRule_Handler(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "ruleArn")
assert.Contains(t, resp, "rule")
})
@@ -356,7 +358,7 @@ func TestHandler_ListTopicRules_ResponseFormat(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
rules, ok := out["rules"]
require.True(t, ok, "response must have 'rules' key")
@@ -411,7 +413,7 @@ func TestHandler_GetTopicRule_IncludesAWSSQLVersion(t *testing.T) {
require.Equal(t, http.StatusOK, resp.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
rule, ok := out["rule"].(map[string]any)
require.True(t, ok)
diff --git a/services/iot/jobs_data_plane_test.go b/services/iot/jobs_data_plane_test.go
index a97badcc56..30de4839d5 100644
--- a/services/iot/jobs_data_plane_test.go
+++ b/services/iot/jobs_data_plane_test.go
@@ -2,7 +2,8 @@ package iot_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -16,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/iot"
@@ -55,7 +57,7 @@ func newJobsDataEnv(t *testing.T) jobsDataEnv {
func (e jobsDataEnv) device(t *testing.T, method, path string, body any) (int, map[string]any) {
t.Helper()
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(t.Context(), method, e.url+path, bytes.NewReader(raw))
@@ -69,7 +71,7 @@ func (e jobsDataEnv) device(t *testing.T, method, path string, body any) (int, m
defer resp.Body.Close()
out := map[string]any{}
- _ = json.NewDecoder(resp.Body).Decode(&out)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1())
return resp.StatusCode, out
}
diff --git a/services/iot/multi_region_test.go b/services/iot/multi_region_test.go
index 9eb2ae4ba1..bf3891c4b3 100644
--- a/services/iot/multi_region_test.go
+++ b/services/iot/multi_region_test.go
@@ -2,7 +2,7 @@ package iot_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -41,7 +42,7 @@ func regionCall(t *testing.T, h *iot.Handler, region, method, path, body string)
out := map[string]any{}
if rec.Body.Len() > 0 {
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
}
return out
diff --git a/services/iot/names.go b/services/iot/names.go
index 4cc7a69bb4..ae7987385c 100644
--- a/services/iot/names.go
+++ b/services/iot/names.go
@@ -1,10 +1,13 @@
package iot
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"regexp"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const maxEntityNameLen = 128
@@ -41,13 +44,13 @@ func malformedPolicy(format string, args ...any) error {
// validatePolicyDocument checks the document is JSON with a Version and well-formed Statement entries.
func validatePolicyDocument(doc string) error {
- var v map[string]json.RawMessage
- if err := json.Unmarshal([]byte(doc), &v); err != nil {
+ var v map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(doc), &v, jsonx.V1()); err != nil {
return malformedPolicy("policy document is not valid JSON")
}
var version string
- if raw, ok := v["Version"]; !ok || json.Unmarshal(raw, &version) != nil || version == "" {
+ if raw, ok := v["Version"]; !ok || json.Unmarshal(raw, &version, jsonx.V1()) != nil || version == "" {
return malformedPolicy("policy document must have a string Version")
}
@@ -56,15 +59,15 @@ func validatePolicyDocument(doc string) error {
return malformedPolicy("policy document must have a Statement")
}
- var statements []map[string]json.RawMessage
+ var statements []map[string]jsontext.Value
- if err := json.Unmarshal(raw, &statements); err != nil {
- var single map[string]json.RawMessage
- if json.Unmarshal(raw, &single) != nil {
+ if err := json.Unmarshal(raw, &statements, jsonx.V1()); err != nil {
+ var single map[string]jsontext.Value
+ if json.Unmarshal(raw, &single, jsonx.V1()) != nil {
return malformedPolicy("Statement must be an object or an array of objects")
}
- statements = []map[string]json.RawMessage{single}
+ statements = []map[string]jsontext.Value{single}
}
for _, st := range statements {
@@ -76,9 +79,10 @@ func validatePolicyDocument(doc string) error {
return nil
}
-func validatePolicyStatement(st map[string]json.RawMessage) error {
+func validatePolicyStatement(st map[string]jsontext.Value) error {
var effect string
- if raw, ok := st["Effect"]; !ok || json.Unmarshal(raw, &effect) != nil || (effect != "Allow" && effect != "Deny") {
+ if raw, ok := st["Effect"]; !ok || json.Unmarshal(raw, &effect, jsonx.V1()) != nil ||
+ (effect != "Allow" && effect != "Deny") {
return malformedPolicy("statement Effect must be Allow or Deny")
}
@@ -91,17 +95,17 @@ func validatePolicyStatement(st map[string]json.RawMessage) error {
return nil
}
-func stringOrList(raw json.RawMessage) bool {
+func stringOrList(raw jsontext.Value) bool {
if len(raw) == 0 {
return false
}
var one string
- if json.Unmarshal(raw, &one) == nil {
+ if json.Unmarshal(raw, &one, jsonx.V1()) == nil {
return one != ""
}
var many []string
- return json.Unmarshal(raw, &many) == nil && len(many) > 0
+ return json.Unmarshal(raw, &many, jsonx.V1()) == nil && len(many) > 0
}
diff --git a/services/iot/ota_options.go b/services/iot/ota_options.go
index 62227d1f10..d20d7d2478 100644
--- a/services/iot/ota_options.go
+++ b/services/iot/ota_options.go
@@ -1,9 +1,11 @@
package iot
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -63,10 +65,10 @@ func otaJobInput(opts OTAUpdateOptions, roleARN string) *CreateJobInput {
// remapJSON copies the keys src shares with dst's JSON tags; an unmarshal failure leaves dst as is.
func remapJSON(src map[string]any, dst any) {
- raw, err := json.Marshal(src)
+ raw, err := json.Marshal(src, jsonx.V1())
if err != nil {
return
}
- _ = json.Unmarshal(raw, dst)
+ _ = json.Unmarshal(raw, dst, jsonx.V1())
}
diff --git a/services/iot/ota_updates.go b/services/iot/ota_updates.go
index 9ce95dafd1..7f28e482fe 100644
--- a/services/iot/ota_updates.go
+++ b/services/iot/ota_updates.go
@@ -1,12 +1,13 @@
package iot
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// OTAUpdate represents an AWS IoT OTA update.
@@ -74,7 +75,7 @@ func (b *InMemoryBackend) CreateOTAUpdate(
now := float64(time.Now().Unix())
jobID := "AFR_OTA-" + id
- doc, docErr := json.Marshal(map[string]any{"afr_ota": map[string]any{"files": files}})
+ doc, docErr := json.Marshal(map[string]any{"afr_ota": map[string]any{"files": files}}, jsonx.V1())
if docErr != nil {
return nil, fmt.Errorf("building OTA job document: %w", docErr)
}
diff --git a/services/iot/persistence.go b/services/iot/persistence.go
index e2dc6a04f6..6e05fe0a12 100644
--- a/services/iot/persistence.go
+++ b/services/iot/persistence.go
@@ -2,11 +2,13 @@ package iot
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -35,7 +37,7 @@ import (
const iotSnapshotVersion = 3
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
AuditTasks map[string]string `json:"auditTasks"`
ClientRequestTokens map[string]string `json:"clientRequestTokens,omitempty"`
OpReplay []string `json:"opReplay,omitempty"`
@@ -175,7 +177,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
DefaultAuthorizer: cfgSnap.DefaultAuthorizer,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "iot: failed to snapshot backend state", "error", err)
@@ -456,7 +458,7 @@ func fromTopicRuleDestSnap(s *topicRuleDestSnap) *TopicRuleDestination {
// out-of-band), so registry.SnapshotAll's generic encoding would drop it;
// the topicRuleDestSnap DTO carries it through instead. Must be called with
// b.mu held (read or write).
-func (b *InMemoryBackend) snapshotTopicRuleDestinationsTable() (map[string]json.RawMessage, error) {
+func (b *InMemoryBackend) snapshotTopicRuleDestinationsTable() (map[string]jsontext.Value, error) {
destDTOReg := store.NewRegistry()
destDTOs := store.Register(destDTOReg, topicRuleDestinationsTableName, store.New(topicRuleDestSnapKey))
@@ -476,7 +478,7 @@ func (b *InMemoryBackend) snapshotTopicRuleDestinationsTable() (map[string]json.
// DTO entry in tables (the inverse of snapshotTopicRuleDestinationsTable).
// Extracted from Restore to keep it within the repo's funlen limit. Must be
// called with b.mu held (write).
-func (b *InMemoryBackend) restoreTopicRuleDestinationsTable(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreTopicRuleDestinationsTable(tables map[string]jsontext.Value) error {
destDTOReg := store.NewRegistry()
destDTOs := store.Register(destDTOReg, topicRuleDestinationsTableName, store.New(topicRuleDestSnapKey))
diff --git a/services/iot/policy_evaluation.go b/services/iot/policy_evaluation.go
index 2ee476f19c..a5c404bf27 100644
--- a/services/iot/policy_evaluation.go
+++ b/services/iot/policy_evaluation.go
@@ -1,11 +1,13 @@
package iot
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// AuthInfo describes one action/resources pair to evaluate authorization for.
@@ -237,14 +239,14 @@ type flexStringList []string
func (f *flexStringList) UnmarshalJSON(data []byte) error {
var single string
- if err := json.Unmarshal(data, &single); err == nil {
+ if err := json.Unmarshal(data, &single, jsonx.V1()); err == nil {
*f = []string{single}
return nil
}
var list []string
- if err := json.Unmarshal(data, &list); err != nil {
+ if err := json.Unmarshal(data, &list, jsonx.V1()); err != nil {
return err
}
@@ -264,7 +266,7 @@ func parseIoTPolicyDocument(doc string) ([]iamStatement, error) {
} `json:"Statement"`
}
- if err := json.Unmarshal([]byte(doc), &raw); err != nil {
+ if err := json.Unmarshal([]byte(doc), &raw, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/iot/realism_test.go b/services/iot/realism_test.go
index bb61a6470b..719c263798 100644
--- a/services/iot/realism_test.go
+++ b/services/iot/realism_test.go
@@ -1,13 +1,15 @@
package iot_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func iotErr(t *testing.T, body []byte) (string, string) {
@@ -17,7 +19,7 @@ func iotErr(t *testing.T, body []byte) (string, string) {
Type string `json:"__type"`
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
return out.Type, out.Message
}
diff --git a/services/iot/rule_actions.go b/services/iot/rule_actions.go
index 67dd71abd6..32d645b2e9 100644
--- a/services/iot/rule_actions.go
+++ b/services/iot/rule_actions.go
@@ -3,7 +3,8 @@ package iot
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
@@ -14,6 +15,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/roleauth"
)
@@ -30,7 +32,7 @@ const (
opDelete = "DELETE"
)
-type actionRunner func(h *ruleHook, rule *TopicRule, msg *ruleMessage, raw json.RawMessage) error
+type actionRunner func(h *ruleHook, rule *TopicRule, msg *ruleMessage, raw jsontext.Value) error
type otherRunner struct {
run actionRunner
@@ -83,8 +85,8 @@ func (h *ruleHook) allow(roleARN, resource string, actions ...string) error {
return nil
}
-func decodeAction(raw json.RawMessage, dst any) error {
- if err := json.Unmarshal(raw, dst); err != nil {
+func decodeAction(raw jsontext.Value, dst any) error {
+ if err := json.Unmarshal(raw, dst, jsonx.V1()); err != nil {
return fmt.Errorf("decode action: %w", err)
}
@@ -132,7 +134,7 @@ type kinesisWire struct {
PartitionKey string `json:"partitionKey"`
}
-func (h *ruleHook) runKinesis(_ *TopicRule, msg *ruleMessage, raw json.RawMessage) error {
+func (h *ruleHook) runKinesis(_ *TopicRule, msg *ruleMessage, raw jsontext.Value) error {
var w kinesisWire
if err := decodeAction(raw, &w); err != nil {
return err
@@ -166,7 +168,7 @@ type firehoseWire struct {
BatchMode bool `json:"batchMode"`
}
-func (h *ruleHook) runFirehose(_ *TopicRule, msg *ruleMessage, raw json.RawMessage) error {
+func (h *ruleHook) runFirehose(_ *TopicRule, msg *ruleMessage, raw jsontext.Value) error {
var w firehoseWire
if err := decodeAction(raw, &w); err != nil {
return err
@@ -214,8 +216,8 @@ func (m *ruleMessage) batchElements(batch bool) ([][]byte, error) {
return [][]byte{m.payload}, nil
}
- var elems []json.RawMessage
- if err := json.Unmarshal(m.payload, &elems); err != nil {
+ var elems []jsontext.Value
+ if err := json.Unmarshal(m.payload, &elems, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: payload is not a valid JSON array", errTemplate)
}
@@ -226,7 +228,7 @@ func (m *ruleMessage) batchElements(batch bool) ([][]byte, error) {
out := make([][]byte, len(elems))
for i, e := range elems {
var s string
- if json.Unmarshal(e, &s) == nil {
+ if json.Unmarshal(e, &s, jsonx.V1()) == nil {
out[i] = []byte(s)
continue
@@ -251,7 +253,7 @@ type dynamoDBWire struct {
Operation string `json:"operation"`
}
-func (h *ruleHook) runDynamoDB(_ *TopicRule, msg *ruleMessage, raw json.RawMessage) error {
+func (h *ruleHook) runDynamoDB(_ *TopicRule, msg *ruleMessage, raw jsontext.Value) error {
var w dynamoDBWire
if err := decodeAction(raw, &w); err != nil {
return err
@@ -324,10 +326,9 @@ func keyAttribute(keyType, value string) map[string]any {
func payloadAttribute(payload []byte) map[string]any {
var obj map[string]any
- dec := json.NewDecoder(bytes.NewReader(payload))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(payload))
- if err := dec.Decode(&obj); err == nil && obj != nil {
+ if err := json.UnmarshalDecode(dec, &obj, jsonx.V1(), jsonx.UseNumber()); err == nil && obj != nil {
return jsonToAttribute(obj)
}
@@ -350,7 +351,7 @@ func jsonToAttribute(v any) map[string]any {
}
return map[string]any{"L": l}
- case json.Number:
+ case jsonx.Number:
return map[string]any{"N": t.String()}
case string:
return map[string]any{"S": t}
@@ -368,7 +369,7 @@ type dynamoDBv2Wire struct {
} `json:"putItem"`
}
-func (h *ruleHook) runDynamoDBv2(_ *TopicRule, msg *ruleMessage, raw json.RawMessage) error {
+func (h *ruleHook) runDynamoDBv2(_ *TopicRule, msg *ruleMessage, raw jsontext.Value) error {
var w dynamoDBv2Wire
if err := decodeAction(raw, &w); err != nil {
return err
@@ -405,7 +406,7 @@ type s3Wire struct {
CannedACL string `json:"cannedAcl"`
}
-func (h *ruleHook) runS3(_ *TopicRule, msg *ruleMessage, raw json.RawMessage) error {
+func (h *ruleHook) runS3(_ *TopicRule, msg *ruleMessage, raw jsontext.Value) error {
var w s3Wire
if err := decodeAction(raw, &w); err != nil {
return err
@@ -436,7 +437,7 @@ type cloudwatchMetricWire struct {
MetricTimestamp string `json:"metricTimestamp"`
}
-func (h *ruleHook) runCloudwatchMetric(_ *TopicRule, msg *ruleMessage, raw json.RawMessage) error {
+func (h *ruleHook) runCloudwatchMetric(_ *TopicRule, msg *ruleMessage, raw jsontext.Value) error {
var w cloudwatchMetricWire
if err := decodeAction(raw, &w); err != nil {
return err
@@ -481,7 +482,7 @@ type cloudwatchAlarmWire struct {
StateValue string `json:"stateValue"`
}
-func (h *ruleHook) runCloudwatchAlarm(_ *TopicRule, msg *ruleMessage, raw json.RawMessage) error {
+func (h *ruleHook) runCloudwatchAlarm(_ *TopicRule, msg *ruleMessage, raw jsontext.Value) error {
var w cloudwatchAlarmWire
if err := decodeAction(raw, &w); err != nil {
return err
@@ -515,7 +516,7 @@ type batchedLogEvent struct {
Message *string `json:"message"`
}
-func (h *ruleHook) runCloudwatchLogs(rule *TopicRule, msg *ruleMessage, raw json.RawMessage) error {
+func (h *ruleHook) runCloudwatchLogs(rule *TopicRule, msg *ruleMessage, raw jsontext.Value) error {
var w cloudwatchLogsWire
if err := decodeAction(raw, &w); err != nil {
return err
@@ -552,7 +553,7 @@ func (m *ruleMessage) logEvents(batch bool) ([]LogEvent, error) {
}
var elems []batchedLogEvent
- if err := json.Unmarshal(m.payload, &elems); err != nil {
+ if err := json.Unmarshal(m.payload, &elems, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: batchMode needs an array of {timestamp,message}", errTemplate)
}
@@ -579,7 +580,7 @@ type stepFunctionsWire struct {
ExecutionNamePrefix string `json:"executionNamePrefix"`
}
-func (h *ruleHook) runStepFunctions(_ *TopicRule, msg *ruleMessage, raw json.RawMessage) error {
+func (h *ruleHook) runStepFunctions(_ *TopicRule, msg *ruleMessage, raw jsontext.Value) error {
var w stepFunctionsWire
if err := decodeAction(raw, &w); err != nil {
return err
@@ -611,7 +612,7 @@ type iotAnalyticsWire struct {
BatchMode bool `json:"batchMode"`
}
-func (h *ruleHook) runIoTAnalytics(_ *TopicRule, msg *ruleMessage, raw json.RawMessage) error {
+func (h *ruleHook) runIoTAnalytics(_ *TopicRule, msg *ruleMessage, raw jsontext.Value) error {
var w iotAnalyticsWire
if err := decodeAction(raw, &w); err != nil {
return err
diff --git a/services/iot/rule_authz.go b/services/iot/rule_authz.go
index 09d99a536f..f782868565 100644
--- a/services/iot/rule_authz.go
+++ b/services/iot/rule_authz.go
@@ -2,12 +2,13 @@ package iot
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/roleauth"
)
@@ -122,7 +123,7 @@ func (h *ruleHook) runErrorAction(
"base64OriginalPayload": base64.StdEncoding.EncodeToString(msg.original),
"failedAction": failedAction,
"failedActionReason": "Failed to run action. Message: " + cause.Error(),
- })
+ }, jsonx.V1())
if err != nil {
return
}
diff --git a/services/iot/rule_http.go b/services/iot/rule_http.go
index cbb06ced9b..da5975123c 100644
--- a/services/iot/rule_http.go
+++ b/services/iot/rule_http.go
@@ -5,7 +5,7 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
"io"
@@ -18,6 +18,7 @@ import (
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -60,7 +61,7 @@ type httpWire struct {
}
// runHTTP posts the projected payload to a URL covered by an ENABLED destination (iot https-rule-action).
-func (h *ruleHook) runHTTP(_ *TopicRule, msg *ruleMessage, raw json.RawMessage) error {
+func (h *ruleHook) runHTTP(_ *TopicRule, msg *ruleMessage, raw jsontext.Value) error {
var w httpWire
if err := decodeAction(raw, &w); err != nil {
return err
@@ -141,7 +142,7 @@ func (b *InMemoryBackend) requireHTTPDestination(rawURL string) error {
}
func contentTypeFor(payload []byte) string {
- if json.Valid(payload) {
+ if jsontext.Value(payload).IsValid(jsonx.V1()) {
return "application/json"
}
diff --git a/services/iot/rule_http_destinations.go b/services/iot/rule_http_destinations.go
index f1c9ce0c6a..10dc0ad5bf 100644
--- a/services/iot/rule_http_destinations.go
+++ b/services/iot/rule_http_destinations.go
@@ -3,7 +3,7 @@ package iot
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/url"
@@ -13,6 +13,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -93,7 +94,7 @@ func sendConfirmation(c confirmation) error {
body, err := json.Marshal(map[string]string{
keyArn: c.destARN, "confirmationToken": c.token, "enableUrl": c.enableURL, "messageType": msgTypeConfirmation,
- })
+ }, jsonx.V1())
if err != nil {
return err
}
@@ -148,7 +149,7 @@ func httpActionsOf(actions []RuleAction, errAction *RuleAction) ([]httpWire, err
}
var w httpWire
- if err := json.Unmarshal(raw, &w); err != nil {
+ if err := json.Unmarshal(raw, &w, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: http action: %w", ErrValidation, err)
}
diff --git a/services/iot/rule_lookups.go b/services/iot/rule_lookups.go
index 8ef4ef9d18..3687a7cd7a 100644
--- a/services/iot/rule_lookups.go
+++ b/services/iot/rule_lookups.go
@@ -2,12 +2,13 @@ package iot
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/roleauth"
)
@@ -175,7 +176,7 @@ func describeThingJSON(bk *InMemoryBackend, thing string) ([]byte, error) {
return nil, err
}
- return json.Marshal(thingDescription(t))
+ return json.Marshal(thingDescription(t), jsonx.V1())
}
func thingGroupsJSON(bk *InMemoryBackend, thing string) ([]byte, error) {
@@ -190,5 +191,5 @@ func thingGroupsJSON(bk *InMemoryBackend, thing string) ([]byte, error) {
groups = append(groups, entry)
}
- return json.Marshal(map[string]any{keyThingGroups: groups})
+ return json.Marshal(map[string]any{keyThingGroups: groups}, jsonx.V1())
}
diff --git a/services/iot/rule_republish.go b/services/iot/rule_republish.go
index fcf26ef1c7..7cb9fb3fbd 100644
--- a/services/iot/rule_republish.go
+++ b/services/iot/rule_republish.go
@@ -2,7 +2,7 @@ package iot
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"slices"
"strconv"
@@ -38,7 +38,7 @@ type republishWire struct {
Qos int32 `json:"qos"`
}
-func (h *ruleHook) runRepublish(_ *TopicRule, msg *ruleMessage, raw json.RawMessage) error {
+func (h *ruleHook) runRepublish(_ *TopicRule, msg *ruleMessage, raw jsontext.Value) error {
var w republishWire
if err := decodeAction(raw, &w); err != nil {
return err
diff --git a/services/iot/sql_value.go b/services/iot/sql_value.go
index 8b04e773ce..d954c35441 100644
--- a/services/iot/sql_value.go
+++ b/services/iot/sql_value.go
@@ -2,12 +2,15 @@ package iot
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"math"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// sqlUndefined is the SQL Undefined value: omitted from JSON output.
@@ -77,52 +80,55 @@ var errBadJSON = errors.New("invalid json")
// decodeJSONValue parses a complete JSON document.
func decodeJSONValue(data []byte, v2016 bool) (any, error) {
- dec := json.NewDecoder(bytes.NewReader(data))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(data), jsonx.V1())
v, err := decodeToken(dec, v2016)
if err != nil {
return nil, err
}
- if _, err = dec.Token(); !errors.Is(err, io.EOF) {
+ if _, err = dec.ReadToken(); !errors.Is(err, io.EOF) {
return nil, errBadJSON
}
return v, nil
}
-func decodeToken(dec *json.Decoder, v2016 bool) (any, error) {
- tok, err := dec.Token()
+func decodeToken(dec *jsontext.Decoder, v2016 bool) (any, error) {
+ tok, err := dec.ReadToken()
if err != nil {
return nil, errBadJSON
}
- switch t := tok.(type) {
- case json.Delim:
- if t == '{' {
- return decodeObject(dec, v2016)
- }
-
+ switch tok.Kind() {
+ case '{':
+ return decodeObject(dec, v2016)
+ case '[':
return decodeArray(dec, v2016)
- case json.Number:
- return sqlNumber(t.String(), v2016), nil
+ case '0':
+ return sqlNumber(tok.String(), v2016), nil
+ case '"':
+ return tok.String(), nil
+ case 't', 'f':
+ return tok.Bool(), nil
default:
- return t, nil
+ var null any
+
+ return null, nil
}
}
-func decodeObject(dec *json.Decoder, v2016 bool) (any, error) {
+func decodeObject(dec *jsontext.Decoder, v2016 bool) (any, error) {
obj := newSQLObject()
- for dec.More() {
- kt, err := dec.Token()
-
- key, ok := kt.(string)
- if err != nil || !ok {
+ for dec.PeekKind() != '}' {
+ kt, err := dec.ReadToken()
+ if err != nil || kt.Kind() != '"' {
return nil, errBadJSON
}
+ key := kt.String()
+
v, verr := decodeToken(dec, v2016)
if verr != nil {
return nil, verr
@@ -131,17 +137,17 @@ func decodeObject(dec *json.Decoder, v2016 bool) (any, error) {
obj.set(key, v)
}
- if _, err := dec.Token(); err != nil {
+ if _, err := dec.ReadToken(); err != nil {
return nil, errBadJSON
}
return obj, nil
}
-func decodeArray(dec *json.Decoder, v2016 bool) (any, error) {
+func decodeArray(dec *jsontext.Decoder, v2016 bool) (any, error) {
arr := []any{}
- for dec.More() {
+ for dec.PeekKind() != ']' {
v, err := decodeToken(dec, v2016)
if err != nil {
return nil, err
@@ -150,7 +156,7 @@ func decodeArray(dec *json.Decoder, v2016 bool) (any, error) {
arr = append(arr, v)
}
- if _, err := dec.Token(); err != nil {
+ if _, err := dec.ReadToken(); err != nil {
return nil, errBadJSON
}
@@ -163,7 +169,7 @@ func writeJSONValue(buf *bytes.Buffer, v any) {
case nil:
buf.WriteString(jsonNull)
case bool, string:
- b, _ := json.Marshal(t)
+ b, _ := json.Marshal(t, jsonx.V1())
buf.Write(b)
case int64:
buf.WriteString(strconv.FormatInt(t, 10))
@@ -174,7 +180,7 @@ func writeJSONValue(buf *bytes.Buffer, v any) {
case *sqlObject:
writeJSONObject(buf, t)
case sqlRaw:
- b, _ := json.Marshal(string(t))
+ b, _ := json.Marshal(string(t), jsonx.V1())
buf.Write(b)
default:
buf.WriteString(jsonNull)
@@ -212,7 +218,7 @@ func writeJSONObject(buf *bytes.Buffer, o *sqlObject) {
first = false
- kb, _ := json.Marshal(k)
+ kb, _ := json.Marshal(k, jsonx.V1())
buf.Write(kb)
buf.WriteByte(':')
writeJSONValue(buf, v)
diff --git a/services/iot/things_test.go b/services/iot/things_test.go
index b2fa7b2de8..3ec6255e3c 100644
--- a/services/iot/things_test.go
+++ b/services/iot/things_test.go
@@ -1,7 +1,7 @@
package iot_test
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iot"
)
@@ -705,8 +706,8 @@ func TestUpdateThing_MergeFalse_ReplacesAttributes(t *testing.T) {
require.NoError(t, err)
var bodyAttr iot.AttributePayload
- raw, _ := json.Marshal(tt.updateBody["attributePayload"])
- require.NoError(t, json.Unmarshal(raw, &bodyAttr))
+ raw, _ := json.Marshal(tt.updateBody["attributePayload"], jsonx.V1())
+ require.NoError(t, json.Unmarshal(raw, &bodyAttr, jsonx.V1()))
err = b.UpdateThing(&iot.UpdateThingInput{
ThingName: "merge-test",
diff --git a/services/iot/topic_rules.go b/services/iot/topic_rules.go
index af4c0ca34d..b6563239e9 100644
--- a/services/iot/topic_rules.go
+++ b/services/iot/topic_rules.go
@@ -1,7 +1,7 @@
package iot
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -26,9 +26,9 @@ func cloneRuleAction(a RuleAction) RuleAction {
out.SNS = &cp
}
if len(a.Other) > 0 {
- out.Other = make(map[string]json.RawMessage, len(a.Other))
+ out.Other = make(map[string]jsontext.Value, len(a.Other))
for k, v := range a.Other {
- out.Other[k] = append(json.RawMessage(nil), v...)
+ out.Other[k] = append(jsontext.Value(nil), v...)
}
}
diff --git a/services/iot/types.go b/services/iot/types.go
index dfbec94c99..7ac37530b2 100644
--- a/services/iot/types.go
+++ b/services/iot/types.go
@@ -3,9 +3,12 @@
package iot
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Thing represents an AWS IoT Thing.
@@ -54,15 +57,15 @@ type RuleAction struct {
Lambda *LambdaAction `json:"lambda,omitempty"`
SNS *SNSAction `json:"sns,omitempty"`
// Other holds action types with no typed model (s3, dynamoDB, ...), kept verbatim.
- Other map[string]json.RawMessage `json:"-"`
+ Other map[string]jsontext.Value `json:"-"`
}
const typedRuleActionKeys = 3
// UnmarshalJSON decodes the typed actions and keeps every other key verbatim.
func (a *RuleAction) UnmarshalJSON(data []byte) error {
- var raw map[string]json.RawMessage
- if err := json.Unmarshal(data, &raw); err != nil {
+ var raw map[string]jsontext.Value
+ if err := json.Unmarshal(data, &raw, jsonx.V1()); err != nil {
return err
}
*a = RuleAction{}
@@ -73,14 +76,14 @@ func (a *RuleAction) UnmarshalJSON(data []byte) error {
var err error
switch k {
case "sqs":
- err = json.Unmarshal(v, &a.SQS)
+ err = json.Unmarshal(v, &a.SQS, jsonx.V1())
case "lambda":
- err = json.Unmarshal(v, &a.Lambda)
+ err = json.Unmarshal(v, &a.Lambda, jsonx.V1())
case "sns":
- err = json.Unmarshal(v, &a.SNS)
+ err = json.Unmarshal(v, &a.SNS, jsonx.V1())
default:
if a.Other == nil {
- a.Other = map[string]json.RawMessage{}
+ a.Other = map[string]jsontext.Value{}
}
a.Other[k] = v
}
@@ -106,10 +109,10 @@ func (a RuleAction) MarshalJSON() ([]byte, error) {
out["sns"] = a.SNS
}
- return json.Marshal(out)
+ return json.Marshal(out, jsonx.V1())
}
-func castRaw(in map[string]json.RawMessage) map[string]any {
+func castRaw(in map[string]jsontext.Value) map[string]any {
out := make(map[string]any, len(in))
for k, v := range in {
out[k] = v
diff --git a/services/iotanalytics/handler_channels.go b/services/iotanalytics/handler_channels.go
index 256e18006c..2ac6b4881a 100644
--- a/services/iotanalytics/handler_channels.go
+++ b/services/iotanalytics/handler_channels.go
@@ -1,16 +1,18 @@
package iotanalytics
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateChannel(c *echo.Context, body []byte) error {
var req createChannelRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -114,7 +116,7 @@ func (h *Handler) handleUpdateChannel(c *echo.Context, name string, body []byte)
var req updateChannelRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/iotanalytics/handler_channels_test.go b/services/iotanalytics/handler_channels_test.go
index 09fb7bd4b0..147fece4f6 100644
--- a/services/iotanalytics/handler_channels_test.go
+++ b/services/iotanalytics/handler_channels_test.go
@@ -1,13 +1,15 @@
package iotanalytics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateAndDescribeChannel(t *testing.T) {
@@ -85,7 +87,7 @@ func TestHandler_ListChannels(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- err := json.Unmarshal(rec.Body.Bytes(), &resp)
+ err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
require.NoError(t, err)
summaries, ok := resp["channelSummaries"].([]any)
@@ -270,7 +272,7 @@ func TestHandler_SampleChannelData_WithMessages(t *testing.T) {
require.Equal(t, http.StatusOK, sampleRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(sampleRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(sampleRec.Body.Bytes(), &resp, jsonx.V1()))
payloads, ok := resp["payloads"].([]any)
require.True(t, ok)
assert.Len(t, payloads, 2)
@@ -394,7 +396,7 @@ func TestHandler_DescribeChannel_NewFields(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
for _, field := range tt.wantFields {
assert.Contains(t, resp, field, "describe response must contain field %q", field)
}
@@ -435,7 +437,7 @@ func TestHandler_CreateChannel_RetentionPeriodInResponse(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasRP := resp["retentionPeriod"]
assert.Equal(t, tt.wantRetention, hasRP)
})
@@ -484,7 +486,7 @@ func TestHandler_DescribeChannel_IncludeStatistics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasStats := resp["statistics"]
assert.Equal(t, tt.wantStatistics, hasStats)
})
@@ -508,7 +510,7 @@ func TestHandler_ListChannels_ChannelStorage(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["channelSummaries"].([]any)
require.Len(t, summaries, 1)
summary, _ := summaries[0].(map[string]any)
diff --git a/services/iotanalytics/handler_datasets.go b/services/iotanalytics/handler_datasets.go
index a2a53967d1..7376eeb4dc 100644
--- a/services/iotanalytics/handler_datasets.go
+++ b/services/iotanalytics/handler_datasets.go
@@ -1,12 +1,14 @@
package iotanalytics
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// parseQueryDateTime parses a scheduledBefore/scheduledOnOrAfter query parameter into epoch
@@ -61,7 +63,7 @@ func filterDatasetContentsBySchedule(contents []*DatasetContent, c *echo.Context
func (h *Handler) handleCreateDataset(c *echo.Context, body []byte) error {
var req createDatasetRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -190,7 +192,7 @@ func (h *Handler) handleUpdateDataset(c *echo.Context, name string, body []byte)
var req updateDatasetRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -222,7 +224,7 @@ func (h *Handler) handleCreateDatasetContent(c *echo.Context, datasetName string
var req createDatasetContentRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/iotanalytics/handler_datasets_test.go b/services/iotanalytics/handler_datasets_test.go
index 44fe78cca5..ade737561a 100644
--- a/services/iotanalytics/handler_datasets_test.go
+++ b/services/iotanalytics/handler_datasets_test.go
@@ -1,7 +1,7 @@
package iotanalytics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateAndDescribeDataset(t *testing.T) {
@@ -101,7 +103,7 @@ func TestHandler_DatasetContentLifecycle(t *testing.T) {
if tt.wantList == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["datasetContentSummaries"].([]any)
require.True(t, ok)
assert.Len(t, summaries, 1)
@@ -140,7 +142,7 @@ func TestHandler_CreateDatasetContent_ExplicitVersionID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "my-custom-version", resp["versionId"])
getRec := doRequest(t, h, http.MethodGet, "/datasets/explicit_ver_ds/content?versionId=my-custom-version", nil)
@@ -189,7 +191,7 @@ func TestHandler_GetDatasetContent_MagicVersionStrings(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
status, _ := resp["status"].(map[string]any)
assert.Equal(t, "SUCCEEDED", status["state"])
@@ -214,7 +216,7 @@ func TestHandler_DeleteDatasetContent_OmittedVersionDeletesOnlyLatest(t *testing
if i == 0 {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
firstVersion, _ = resp["versionId"].(string)
require.NotEmpty(t, firstVersion)
}
@@ -227,7 +229,7 @@ func TestHandler_DeleteDatasetContent_OmittedVersionDeletesOnlyLatest(t *testing
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
summaries, _ := listResp["datasetContentSummaries"].([]any)
require.Len(t, summaries, 2, "omitted versionId must delete exactly one version")
@@ -259,7 +261,7 @@ func TestHandler_ListDatasetContents_PaginationAcrossPages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
vid, _ := resp["versionId"].(string)
require.NotEmpty(t, vid)
wantVersions[vid] = true
@@ -273,7 +275,7 @@ func TestHandler_ListDatasetContents_PaginationAcrossPages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["datasetContentSummaries"].([]any)
for _, s := range summaries {
@@ -315,7 +317,7 @@ func TestHandler_ListDatasetContents_ScheduleTime(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["datasetContentSummaries"].([]any)
require.Len(t, summaries, 1)
@@ -360,7 +362,7 @@ func TestHandler_ListDatasetContents_ScheduledFilters(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["datasetContentSummaries"].([]any)
assert.Len(t, summaries, tt.wantLen)
@@ -422,7 +424,7 @@ func TestHandler_Datasets(t *testing.T) {
listRec := doRequest(t, h, http.MethodGet, "/datasets", nil)
assert.Equal(t, tt.wantStatus, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["datasetSummaries"].([]any)
require.True(t, ok)
assert.Len(t, summaries, 1)
@@ -478,7 +480,7 @@ func TestHandler_DatasetContent_GetVersionSpecific(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
versionID, ok := createResp["versionId"].(string)
require.True(t, ok)
require.NotEmpty(t, versionID)
@@ -542,7 +544,7 @@ func TestHandler_DescribeDataset_NewFields(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
for _, field := range tt.wantFields {
assert.Contains(t, resp, field, "describe response must contain field %q", field)
}
@@ -566,7 +568,7 @@ func TestHandler_GetDatasetContent_VersionAndEntries(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasVersionID := resp["versionId"]
assert.False(t, hasVersionID, "GetDatasetContent must not return versionId -- AWS has no such member")
_, hasStatus := resp["status"]
@@ -608,7 +610,7 @@ func TestHandler_DeleteDatasetContent_RequiresVersionId(t *testing.T) {
deletePath := tt.path
if deletePath == "" {
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
vid, _ := cr["versionId"].(string)
require.NotEmpty(t, vid)
deletePath = "/datasets/delcontds/content?versionId=" + vid
@@ -635,7 +637,7 @@ func TestHandler_ListDatasetContents_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["nextToken"], "nextToken must be set when results are truncated")
summaries, _ := resp["datasetContentSummaries"].([]any)
assert.Len(t, summaries, 2)
diff --git a/services/iotanalytics/handler_datastores.go b/services/iotanalytics/handler_datastores.go
index bdc65b22ae..c8712dee56 100644
--- a/services/iotanalytics/handler_datastores.go
+++ b/services/iotanalytics/handler_datastores.go
@@ -1,15 +1,17 @@
package iotanalytics
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateDatastore(c *echo.Context, body []byte) error {
var req createDatastoreRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -135,7 +137,7 @@ func (h *Handler) handleUpdateDatastore(c *echo.Context, name string, body []byt
var req updateDatastoreRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/iotanalytics/handler_datastores_test.go b/services/iotanalytics/handler_datastores_test.go
index 4a9c9e205f..f0f66f40be 100644
--- a/services/iotanalytics/handler_datastores_test.go
+++ b/services/iotanalytics/handler_datastores_test.go
@@ -1,12 +1,14 @@
package iotanalytics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateAndDescribeDatastore(t *testing.T) {
@@ -125,7 +127,7 @@ func TestHandler_Datastores(t *testing.T) {
listRec := doRequest(t, h, http.MethodGet, "/datastores", nil)
assert.Equal(t, tt.wantStatus, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["datastoreSummaries"].([]any)
require.True(t, ok)
assert.Len(t, summaries, 1)
@@ -207,7 +209,7 @@ func TestHandler_DescribeDatastore_NewFields(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
for _, field := range tt.wantFields {
assert.Contains(t, resp, field, "describe response must contain field %q", field)
}
@@ -248,7 +250,7 @@ func TestHandler_CreateDatastore_RetentionPeriodInResponse(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasRP := resp["retentionPeriod"]
assert.Equal(t, tt.wantRetention, hasRP)
})
@@ -292,7 +294,7 @@ func TestHandler_DescribeDatastore_IncludeStatistics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasStats := resp["statistics"]
assert.Equal(t, tt.wantStatistics, hasStats)
})
@@ -316,7 +318,7 @@ func TestHandler_ListDatastores_DatastoreStorage(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["datastoreSummaries"].([]any)
require.Len(t, summaries, 1)
summary, _ := summaries[0].(map[string]any)
diff --git a/services/iotanalytics/handler_logging.go b/services/iotanalytics/handler_logging.go
index c10d9bd8d6..376cf5801b 100644
--- a/services/iotanalytics/handler_logging.go
+++ b/services/iotanalytics/handler_logging.go
@@ -1,10 +1,12 @@
package iotanalytics
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDescribeLoggingOptions(c *echo.Context) error {
@@ -24,7 +26,7 @@ func (h *Handler) handleDescribeLoggingOptions(c *echo.Context) error {
func (h *Handler) handlePutLoggingOptions(c *echo.Context, body []byte) error {
var req putLoggingOptionsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
diff --git a/services/iotanalytics/handler_logging_test.go b/services/iotanalytics/handler_logging_test.go
index 46508535ff..402b97ec90 100644
--- a/services/iotanalytics/handler_logging_test.go
+++ b/services/iotanalytics/handler_logging_test.go
@@ -1,12 +1,14 @@
package iotanalytics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_LoggingOptions(t *testing.T) {
@@ -64,7 +66,7 @@ func TestHandler_LoggingOptions(t *testing.T) {
if tt.wantDescribe == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
opts, ok := resp["loggingOptions"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "ERROR", opts["level"])
diff --git a/services/iotanalytics/handler_messages.go b/services/iotanalytics/handler_messages.go
index e9da65197c..5be56842c3 100644
--- a/services/iotanalytics/handler_messages.go
+++ b/services/iotanalytics/handler_messages.go
@@ -1,16 +1,18 @@
package iotanalytics
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleBatchPutMessage(c *echo.Context, body []byte) error {
var req batchPutMessageRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
diff --git a/services/iotanalytics/handler_messages_test.go b/services/iotanalytics/handler_messages_test.go
index 2b532ff521..9b5ada8821 100644
--- a/services/iotanalytics/handler_messages_test.go
+++ b/services/iotanalytics/handler_messages_test.go
@@ -1,13 +1,15 @@
package iotanalytics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_BatchPutMessage(t *testing.T) {
@@ -67,7 +69,7 @@ func TestHandler_BatchPutMessage(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
entries, _ := resp["batchPutMessageErrorEntries"].([]any)
assert.Len(t, entries, tt.wantErrors)
}
@@ -149,7 +151,7 @@ func TestHandler_BatchPutMessage_NoChannelNameInErrorEntry(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
entries, _ := resp["batchPutMessageErrorEntries"].([]any)
require.Len(t, entries, 1)
entry, _ := entries[0].(map[string]any)
diff --git a/services/iotanalytics/handler_pipelines.go b/services/iotanalytics/handler_pipelines.go
index 5897b777a0..ddb13bf253 100644
--- a/services/iotanalytics/handler_pipelines.go
+++ b/services/iotanalytics/handler_pipelines.go
@@ -1,16 +1,18 @@
package iotanalytics
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreatePipeline(c *echo.Context, body []byte) error {
var req createPipelineRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -97,7 +99,7 @@ func (h *Handler) handleUpdatePipeline(c *echo.Context, name string, body []byte
var req updatePipelineRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -126,7 +128,7 @@ func (h *Handler) handleStartPipelineReprocessing(c *echo.Context, pipelineName
var req startPipelineReprocessingRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -162,7 +164,7 @@ func (h *Handler) handleCancelPipelineReprocessing(c *echo.Context, resource str
func (h *Handler) handleRunPipelineActivity(c *echo.Context, body []byte) error {
var req runPipelineActivityRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
diff --git a/services/iotanalytics/handler_pipelines_test.go b/services/iotanalytics/handler_pipelines_test.go
index 4a99b42814..0e98f62387 100644
--- a/services/iotanalytics/handler_pipelines_test.go
+++ b/services/iotanalytics/handler_pipelines_test.go
@@ -1,12 +1,14 @@
package iotanalytics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateAndDescribePipeline(t *testing.T) {
@@ -105,7 +107,7 @@ func TestHandler_StartAndCancelPipelineReprocessing(t *testing.T) {
if tt.wantStart == http.StatusCreated {
var resp map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &resp, jsonx.V1()))
reprocessingID, ok := resp["reprocessingId"].(string)
require.True(t, ok)
assert.NotEmpty(t, reprocessingID)
@@ -169,7 +171,7 @@ func TestHandler_RunPipelineActivity(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
payloads, _ := resp["payloads"].([]any)
assert.Len(t, payloads, tt.wantLen)
}
@@ -233,7 +235,7 @@ func TestHandler_Pipelines(t *testing.T) {
listRec := doRequest(t, h, http.MethodGet, "/pipelines", nil)
assert.Equal(t, tt.wantStatus, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["pipelineSummaries"].([]any)
require.True(t, ok)
assert.Len(t, summaries, 1)
@@ -350,7 +352,7 @@ func TestHandler_DescribePipeline_NewFields(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
for _, field := range tt.wantFields {
assert.Contains(t, resp, field, "describe response must contain field %q", field)
}
@@ -452,7 +454,7 @@ func TestHandler_ReprocessingSummaries_Sorted(t *testing.T) {
require.Equal(t, http.StatusCreated, startRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &resp, jsonx.V1()))
ids[i], _ = resp["reprocessingId"].(string)
require.NotEmpty(t, ids[i])
}
@@ -462,7 +464,7 @@ func TestHandler_ReprocessingSummaries_Sorted(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
pipeline, ok := resp["pipeline"].(map[string]any)
require.True(t, ok)
summaries, ok := pipeline["reprocessingSummaries"].([]any)
@@ -510,7 +512,7 @@ func TestHandler_PipelineReprocessingSummary_NoStartEndTimeLeak(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
pl, _ := resp["pipeline"].(map[string]any)
summaries, _ := pl["reprocessingSummaries"].([]any)
require.Len(t, summaries, 1)
diff --git a/services/iotanalytics/handler_sdk_route_table_test.go b/services/iotanalytics/handler_sdk_route_table_test.go
index ca841ecf3e..944ad8cf44 100644
--- a/services/iotanalytics/handler_sdk_route_table_test.go
+++ b/services/iotanalytics/handler_sdk_route_table_test.go
@@ -1,7 +1,7 @@
package iotanalytics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http/httptest"
"strings"
"testing"
@@ -9,6 +9,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// sdkRouteCases is the authoritative method+path for every real IoT
@@ -113,7 +115,7 @@ func TestExtractOperation_SDKRouteTable(t *testing.T) {
var resp struct {
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEqual(t, "not found", resp.Message,
"method=%s path=%s op=%s: dispatched to the unmatched-route default", tc.method, tc.path, tc.op)
})
diff --git a/services/iotanalytics/handler_tags.go b/services/iotanalytics/handler_tags.go
index 4009a59beb..5db478f222 100644
--- a/services/iotanalytics/handler_tags.go
+++ b/services/iotanalytics/handler_tags.go
@@ -1,10 +1,12 @@
package iotanalytics
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleListTagsForResource(c *echo.Context) error {
@@ -38,7 +40,7 @@ func (h *Handler) handleTagResource(c *echo.Context, body []byte) error {
}
var req tagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
diff --git a/services/iotanalytics/handler_tags_test.go b/services/iotanalytics/handler_tags_test.go
index cc2fedb9b6..a4366d8936 100644
--- a/services/iotanalytics/handler_tags_test.go
+++ b/services/iotanalytics/handler_tags_test.go
@@ -1,13 +1,15 @@
package iotanalytics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_TagOperations covers ListTagsForResource/TagResource/UntagResource.
@@ -63,7 +65,7 @@ func TestHandler_TagOperations(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
arn, _ := createResp["channelArn"].(string)
require.NotEmpty(t, arn)
@@ -152,7 +154,7 @@ func TestHandler_CreateResource_InvalidTagsRejected(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", errResp["__type"])
okRec := doRequest(t, h, http.MethodPost, tt.path, tt.body(validTags))
@@ -191,7 +193,7 @@ func TestHandler_TagResource_Returns204(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ := resp["channelArn"].(string)
require.NotEmpty(t, arn)
@@ -252,7 +254,7 @@ func TestHandler_TagValidation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ := resp["channelArn"].(string)
require.NotEmpty(t, arn)
@@ -306,7 +308,7 @@ func TestHandler_ListTagsForResource_EmptyNotError(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
arn, _ := createResp[tt.arnKey].(string)
require.NotEmpty(t, arn)
@@ -314,7 +316,7 @@ func TestHandler_ListTagsForResource_EmptyNotError(t *testing.T) {
assert.Equal(t, http.StatusOK, tagRec.Code)
var tagResp map[string]any
- require.NoError(t, json.Unmarshal(tagRec.Body.Bytes(), &tagResp))
+ require.NoError(t, json.Unmarshal(tagRec.Body.Bytes(), &tagResp, jsonx.V1()))
tags, ok := tagResp["tags"].([]any)
require.True(t, ok, "tags field must be array")
assert.Empty(t, tags, "newly created resource must have empty tag array")
diff --git a/services/iotanalytics/handler_test.go b/services/iotanalytics/handler_test.go
index 0f0b1dbc52..5ab9ab69ab 100644
--- a/services/iotanalytics/handler_test.go
+++ b/services/iotanalytics/handler_test.go
@@ -2,7 +2,7 @@ package iotanalytics_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strconv"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotanalytics"
)
@@ -47,7 +48,7 @@ func doRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -321,7 +322,7 @@ func TestHandler_AlreadyExistsErrorType(t *testing.T) {
require.Equal(t, http.StatusConflict, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceAlreadyExistsException", resp["__type"])
})
}
@@ -387,7 +388,7 @@ func TestHandler_ErrorResponseFormat(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"], "error response must have __type field")
assert.NotEmpty(t, resp["message"], "error response must have message field")
})
@@ -626,7 +627,7 @@ func TestHandler_Pagination(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp[summaryKey].([]any)
require.True(t, ok)
assert.Len(t, summaries, tt.wantLen)
@@ -658,7 +659,7 @@ func TestHandler_PaginationNextToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
summaries1, ok := resp1["channelSummaries"].([]any)
require.True(t, ok)
assert.Len(t, summaries1, 2)
@@ -671,7 +672,7 @@ func TestHandler_PaginationNextToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
summaries2, ok := resp2["channelSummaries"].([]any)
require.True(t, ok)
assert.Len(t, summaries2, 1, "page 2 should have the remaining 1 channel")
diff --git a/services/iotanalytics/persistence.go b/services/iotanalytics/persistence.go
index 8f03b63400..e6919698f2 100644
--- a/services/iotanalytics/persistence.go
+++ b/services/iotanalytics/persistence.go
@@ -2,7 +2,7 @@ package iotanalytics
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
@@ -42,7 +42,7 @@ const iotanalyticsSnapshotVersion = 2
// decoding a snapshot from an incompatible (older or newer) build of this
// backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
ChannelMessages map[string][]ChannelMessage `json:"channelMessages"`
DatasetContents map[string][]*DatasetContent `json:"datasetContents"`
diff --git a/services/iotanalytics/pipeline_expr_test.go b/services/iotanalytics/pipeline_expr_test.go
index 795cf6ad4a..89022c7885 100644
--- a/services/iotanalytics/pipeline_expr_test.go
+++ b/services/iotanalytics/pipeline_expr_test.go
@@ -1,12 +1,13 @@
package iotanalytics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotanalytics"
)
@@ -246,7 +247,7 @@ func TestInMemoryBackend_RunPipelineActivity_Math(t *testing.T) {
}
var decoded map[string]any
- require.NoError(t, json.Unmarshal(out[0], &decoded))
+ require.NoError(t, json.Unmarshal(out[0], &decoded, jsonx.V1()))
assert.InDelta(t, tt.wantValue, decoded[tt.attribute], 1e-9)
})
}
diff --git a/services/iotanalytics/pipeline_lambda_iot_test.go b/services/iotanalytics/pipeline_lambda_iot_test.go
index 63d4a9d439..9037db1c84 100644
--- a/services/iotanalytics/pipeline_lambda_iot_test.go
+++ b/services/iotanalytics/pipeline_lambda_iot_test.go
@@ -2,13 +2,14 @@ package iotanalytics_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotanalytics"
)
@@ -43,7 +44,7 @@ func echoAddFieldLambda(calls *[]string) *fakeLambdaInvoker {
calls: calls,
invoke: func(_ context.Context, _, _ string, payload []byte) ([]byte, int, error) {
var msgs []map[string]any
- if err := json.Unmarshal(payload, &msgs); err != nil {
+ if err := json.Unmarshal(payload, &msgs, jsonx.V1()); err != nil {
return nil, 0, err
}
@@ -51,7 +52,7 @@ func echoAddFieldLambda(calls *[]string) *fakeLambdaInvoker {
m["seen"] = true
}
- out, err := json.Marshal(msgs)
+ out, err := json.Marshal(msgs, jsonx.V1())
return out, 200, err
},
@@ -105,7 +106,7 @@ func TestInMemoryBackend_RunPipelineActivity_Lambda(t *testing.T) {
require.Len(t, out, 1)
var decoded map[string]any
- require.NoError(t, json.Unmarshal(out[0], &decoded))
+ require.NoError(t, json.Unmarshal(out[0], &decoded, jsonx.V1()))
assert.Equal(t, true, decoded["seen"])
assert.InDelta(t, 1.0, decoded["x"], 1e-9)
})
@@ -130,11 +131,11 @@ func TestInMemoryBackend_RunPipelineActivity_Lambda(t *testing.T) {
require.Len(t, calls, 2, "3 payloads at batchSize=2 must invoke twice (2 then 1)")
var firstBatch []map[string]any
- require.NoError(t, json.Unmarshal([]byte(calls[0]), &firstBatch))
+ require.NoError(t, json.Unmarshal([]byte(calls[0]), &firstBatch, jsonx.V1()))
assert.Len(t, firstBatch, 2)
var secondBatch []map[string]any
- require.NoError(t, json.Unmarshal([]byte(calls[1]), &secondBatch))
+ require.NoError(t, json.Unmarshal([]byte(calls[1]), &secondBatch, jsonx.V1()))
assert.Len(t, secondBatch, 1)
})
@@ -218,7 +219,7 @@ func TestInMemoryBackend_RunPipelineActivity_DeviceRegistryEnrich(t *testing.T)
require.Len(t, out, 1)
var decoded map[string]any
- require.NoError(t, json.Unmarshal(out[0], &decoded))
+ require.NoError(t, json.Unmarshal(out[0], &decoded, jsonx.V1()))
registry, ok := decoded["registry"].(map[string]any)
require.True(t, ok)
@@ -270,7 +271,7 @@ func TestInMemoryBackend_RunPipelineActivity_DeviceShadowEnrich(t *testing.T) {
require.Len(t, out, 1)
var decoded map[string]any
- require.NoError(t, json.Unmarshal(out[0], &decoded))
+ require.NoError(t, json.Unmarshal(out[0], &decoded, jsonx.V1()))
shadow, ok := decoded["shadow"].(map[string]any)
require.True(t, ok)
diff --git a/services/iotanalytics/pipelines.go b/services/iotanalytics/pipelines.go
index e4fbc43467..dccf1d6074 100644
--- a/services/iotanalytics/pipelines.go
+++ b/services/iotanalytics/pipelines.go
@@ -3,13 +3,15 @@ package iotanalytics
import (
"cmp"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// clonePipelineActivities deep-copies a slice of PipelineActivity.
@@ -371,7 +373,7 @@ func (b *InMemoryBackend) invokeLambdaBatch(ctx context.Context, lambdaName stri
return nil
}
- reqBody, err := json.Marshal(msgs)
+ reqBody, err := json.Marshal(msgs, jsonx.V1())
if err != nil {
return fmt.Errorf("%w: marshal lambda batch for %q: %w", ErrPipelineActivityFailed, lambdaName, err)
}
@@ -382,7 +384,7 @@ func (b *InMemoryBackend) invokeLambdaBatch(ctx context.Context, lambdaName stri
}
var results []map[string]any
- if jsonErr := json.Unmarshal(respBody, &results); jsonErr != nil || len(results) != len(msgs) {
+ if jsonErr := json.Unmarshal(respBody, &results, jsonx.V1()); jsonErr != nil || len(results) != len(msgs) {
return fmt.Errorf(
"%w: lambda activity %q did not return a JSON object array of the same length",
ErrPipelineActivityFailed, lambdaName,
@@ -464,7 +466,7 @@ func enrichPayloads(attribute string, data map[string]any, payloads [][]byte) []
// decodeMessage attempts to unmarshal a message payload as a JSON object.
func decodeMessage(payload []byte) (map[string]any, bool) {
var m map[string]any
- if err := json.Unmarshal(payload, &m); err != nil {
+ if err := json.Unmarshal(payload, &m, jsonx.V1()); err != nil {
return nil, false
}
@@ -475,7 +477,7 @@ func decodeMessage(payload []byte) (map[string]any, bool) {
// marshaling somehow fails (m came from a successful Unmarshal, so this is unreachable in
// practice, but encodeMessage must never panic or drop data on error).
func encodeMessage(m map[string]any, fallback []byte) []byte {
- b, err := json.Marshal(m)
+ b, err := json.Marshal(m, jsonx.V1())
if err != nil {
return fallback
}
diff --git a/services/iotanalytics/pipelines_test.go b/services/iotanalytics/pipelines_test.go
index 05cd8cd9b4..78e24be74d 100644
--- a/services/iotanalytics/pipelines_test.go
+++ b/services/iotanalytics/pipelines_test.go
@@ -2,12 +2,13 @@ package iotanalytics_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotanalytics"
)
@@ -230,7 +231,7 @@ func TestInMemoryBackend_RunPipelineActivity_AttributeActivities(t *testing.T) {
}
var decoded map[string]any
- require.NoError(t, json.Unmarshal(out[0], &decoded))
+ require.NoError(t, json.Unmarshal(out[0], &decoded, jsonx.V1()))
assert.Len(t, decoded, len(tt.wantKeys))
for k, v := range tt.wantKeys {
diff --git a/services/iotanalytics/wire_shape_sdk_roundtrip_test.go b/services/iotanalytics/wire_shape_sdk_roundtrip_test.go
index b0ff62716e..462c973a08 100644
--- a/services/iotanalytics/wire_shape_sdk_roundtrip_test.go
+++ b/services/iotanalytics/wire_shape_sdk_roundtrip_test.go
@@ -1,7 +1,7 @@
package iotanalytics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotanalytics"
)
@@ -226,7 +227,7 @@ func TestListSummaries_NoFabricatedARN(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp[tt.summariesK].([]any)
require.True(t, ok)
@@ -273,7 +274,7 @@ func TestDatastore_IotSiteWiseCustomerManagedS3Storage_NoRoleArn(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
datastore, ok := resp["datastore"].(map[string]any)
require.True(t, ok)
diff --git a/services/iotdataplane/connections_test.go b/services/iotdataplane/connections_test.go
index 1b0b74ba1f..0aee54e023 100644
--- a/services/iotdataplane/connections_test.go
+++ b/services/iotdataplane/connections_test.go
@@ -2,7 +2,7 @@ package iotdataplane_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotdataplane"
)
@@ -209,7 +210,7 @@ func Test_ListConnections_Empty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
conns := resp["connections"].([]any)
assert.Empty(t, conns)
}
@@ -257,7 +258,7 @@ func Test_ListConnections_ShowsRegistered(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
conns := resp["connections"].([]any)
assert.Len(t, conns, 2)
}
@@ -323,7 +324,7 @@ func Test_AdminConnectionsPath_BasicFlow(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/_admin/connections", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["connections"].([]any), 1)
// Delete.
@@ -393,7 +394,7 @@ func Test_Connection_Register_DuplicateShape(t *testing.T) {
require.Equal(t, http.StatusConflict, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ConflictException", resp["error"])
}
func Test_Connection_EmptyClientID_Rejected(t *testing.T) {
@@ -414,7 +415,7 @@ func Test_Connection_DollarPrefix_Rejected(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", resp["error"])
}
func Test_Connection_DeleteUnknown_NotFound_DollarStillRejected(t *testing.T) {
@@ -457,7 +458,7 @@ func Test_DeleteConnection_UnknownClient_HTTPShape(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["error"])
})
}
@@ -568,7 +569,7 @@ func TestHandler_GetConnection_WireShape(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.clientID, resp["clientId"])
assert.Equal(t, true, resp["connected"])
assert.NotZero(t, resp["connectedSince"])
@@ -661,7 +662,7 @@ func TestHandler_ListSubscriptions_WireShape(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
subs, ok := resp["subscriptions"].([]any)
require.True(t, ok)
assert.Len(t, subs, len(tt.brokerSubs))
@@ -753,7 +754,7 @@ func TestHandler_ListSubscriptions_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
subs, ok := resp["subscriptions"].([]any)
require.True(t, ok)
@@ -985,7 +986,7 @@ func TestHandler_SendDirectMessage_WireShape(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "OK", resp["message"])
assert.NotEmpty(t, resp["traceId"])
@@ -1081,7 +1082,7 @@ func Test_GetConnection_AfterDelete(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, false, resp["connected"])
assert.Equal(t, "SERVER_INITIATED_DISCONNECT", resp["disconnectReason"])
assert.NotZero(t, resp["disconnectedSince"])
diff --git a/services/iotdataplane/export_test.go b/services/iotdataplane/export_test.go
index 0dab9f5829..ddbca34063 100644
--- a/services/iotdataplane/export_test.go
+++ b/services/iotdataplane/export_test.go
@@ -1,9 +1,11 @@
package iotdataplane
-import "encoding/json"
+import (
+ "encoding/json/jsontext"
+)
// MergeStateFields exposes mergeStateFields for white-box testing.
-func MergeStateFields(base, patch map[string]json.RawMessage) map[string]json.RawMessage {
+func MergeStateFields(base, patch map[string]jsontext.Value) map[string]jsontext.Value {
return mergeStateFields(base, patch)
}
@@ -80,7 +82,7 @@ func ForceSetShadowVersion(b *InMemoryBackend, thingName, shadowName string, ver
}
// GetShadowDesired returns the desired state map for white-box testing.
-func GetShadowDesired(b *InMemoryBackend, thingName, shadowName string) map[string]json.RawMessage {
+func GetShadowDesired(b *InMemoryBackend, thingName, shadowName string) map[string]jsontext.Value {
b.mu.RLock("GetShadowDesired")
defer b.mu.RUnlock()
@@ -93,7 +95,7 @@ func GetShadowDesired(b *InMemoryBackend, thingName, shadowName string) map[stri
}
// GetShadowReported returns the reported state map for white-box testing.
-func GetShadowReported(b *InMemoryBackend, thingName, shadowName string) map[string]json.RawMessage {
+func GetShadowReported(b *InMemoryBackend, thingName, shadowName string) map[string]jsontext.Value {
b.mu.RLock("GetShadowReported")
defer b.mu.RUnlock()
diff --git a/services/iotdataplane/handler_publish.go b/services/iotdataplane/handler_publish.go
index 9d6747495b..d4dee28cf0 100644
--- a/services/iotdataplane/handler_publish.go
+++ b/services/iotdataplane/handler_publish.go
@@ -1,7 +1,8 @@
package iotdataplane
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -11,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -39,12 +41,12 @@ func unwrapPublishPayload(body []byte, contentType string) []byte {
}
var wrapper struct {
- Payload *json.RawMessage `json:"payload"`
+ Payload *jsontext.Value `json:"payload"`
}
- if jsonErr := json.Unmarshal(body, &wrapper); jsonErr == nil && wrapper.Payload != nil {
+ if jsonErr := json.Unmarshal(body, &wrapper, jsonx.V1()); jsonErr == nil && wrapper.Payload != nil {
var payloadStr string
- if unmarshalErr := json.Unmarshal(*wrapper.Payload, &payloadStr); unmarshalErr == nil {
+ if unmarshalErr := json.Unmarshal(*wrapper.Payload, &payloadStr, jsonx.V1()); unmarshalErr == nil {
return []byte(payloadStr)
}
}
diff --git a/services/iotdataplane/handler_sdk_route_table_test.go b/services/iotdataplane/handler_sdk_route_table_test.go
index 7f8385d37f..3a695d2f08 100644
--- a/services/iotdataplane/handler_sdk_route_table_test.go
+++ b/services/iotdataplane/handler_sdk_route_table_test.go
@@ -1,7 +1,7 @@
package iotdataplane_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http/httptest"
"strings"
"testing"
@@ -9,6 +9,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// sdkRouteCases is the authoritative method+path for every real IoT Data
@@ -96,7 +98,7 @@ func TestExtractOperation_SDKRouteTable(t *testing.T) {
var decoded struct {
Error string `json:"error"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &decoded))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &decoded, jsonx.V1()))
assert.NotEqual(t, "not found", decoded.Error,
"method=%s path=%s op=%s: dispatched to the unmatched-route default", tc.method, tc.path, tc.op)
})
diff --git a/services/iotdataplane/handler_test.go b/services/iotdataplane/handler_test.go
index 77639ec0aa..270972c7c7 100644
--- a/services/iotdataplane/handler_test.go
+++ b/services/iotdataplane/handler_test.go
@@ -2,7 +2,7 @@ package iotdataplane_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotdataplane"
)
@@ -609,7 +610,7 @@ func Test_ErrorShapes_AllTypes(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code, "unexpected status for %s %s", tt.method, tt.path)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantError, resp["error"],
"unexpected error type for %s %s", tt.method, tt.path)
_, hasMsg := resp["message"]
@@ -629,7 +630,7 @@ func Test_VersionConflict_ErrorShape(t *testing.T) {
require.Equal(t, http.StatusConflict, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ConflictException", resp["error"])
assert.NotContains(t, resp, "code", "a numeric code breaks the SDK's string-typed body decode")
}
diff --git a/services/iotdataplane/models.go b/services/iotdataplane/models.go
index e90c08b904..d598d57729 100644
--- a/services/iotdataplane/models.go
+++ b/services/iotdataplane/models.go
@@ -1,7 +1,7 @@
package iotdataplane
import (
- "encoding/json"
+ "encoding/json/jsontext"
"regexp"
"time"
)
@@ -57,10 +57,10 @@ var _ StorageBackend = (*InMemoryBackend)(nil)
// never JSON-marshaled directly; shadowEntrySnap (persistence.go) is its serialisable form.
type shadowEntry struct {
updatedAt time.Time
- desired map[string]json.RawMessage // nil = not set
- reported map[string]json.RawMessage // nil = not set
- metaDesired map[string]int64 // field → epoch seconds of last update
- metaReported map[string]int64 // field → epoch seconds of last update
+ desired map[string]jsontext.Value // nil = not set
+ reported map[string]jsontext.Value // nil = not set
+ metaDesired map[string]int64 // field → epoch seconds of last update
+ metaReported map[string]int64 // field → epoch seconds of last update
thingName string
shadowName string
version int
diff --git a/services/iotdataplane/persistence.go b/services/iotdataplane/persistence.go
index c8e70f0571..6bcba90ca0 100644
--- a/services/iotdataplane/persistence.go
+++ b/services/iotdataplane/persistence.go
@@ -2,7 +2,7 @@ package iotdataplane
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
"maps"
@@ -32,14 +32,14 @@ const iotdataplaneSnapshotVersion = 1
// unexported identity fields (see store_setup.go) -- shadowEntry itself
// carries no exported fields.
type shadowEntrySnap struct {
- UpdatedAt time.Time `json:"updatedAt"`
- Desired map[string]json.RawMessage `json:"desired,omitempty"`
- Reported map[string]json.RawMessage `json:"reported,omitempty"`
- MetaDesired map[string]int64 `json:"metaDesired,omitempty"`
- MetaReported map[string]int64 `json:"metaReported,omitempty"`
- ThingName string `json:"thingName"`
- ShadowName string `json:"shadowName"`
- Version int `json:"version"`
+ UpdatedAt time.Time `json:"updatedAt"`
+ Desired map[string]jsontext.Value `json:"desired,omitempty"`
+ Reported map[string]jsontext.Value `json:"reported,omitempty"`
+ MetaDesired map[string]int64 `json:"metaDesired,omitempty"`
+ MetaReported map[string]int64 `json:"metaReported,omitempty"`
+ ThingName string `json:"thingName"`
+ ShadowName string `json:"shadowName"`
+ Version int `json:"version"`
// Deleted round-trips the tombstone flag (see shadowEntry.deleted) so a
// restored snapshot preserves delete-then-recreate version continuity.
// Omitted (defaults to false) for pre-existing snapshots, which is the
@@ -80,14 +80,14 @@ func newDirtyDTORegistry() (
}
// copyRawMessages returns a deep copy of a map[string]json.RawMessage.
-func copyRawMessages(src map[string]json.RawMessage) map[string]json.RawMessage {
+func copyRawMessages(src map[string]jsontext.Value) map[string]jsontext.Value {
if src == nil {
return nil
}
- dst := make(map[string]json.RawMessage, len(src))
+ dst := make(map[string]jsontext.Value, len(src))
for k, v := range src {
- cp := make(json.RawMessage, len(v))
+ cp := make(jsontext.Value, len(v))
copy(cp, v)
dst[k] = cp
}
@@ -147,8 +147,8 @@ func snapToEntry(es *shadowEntrySnap) *shadowEntry {
// guards against decoding a snapshot from an incompatible (older or newer)
// build of this backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serialises backend state to JSON.
@@ -236,7 +236,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreDirtyTablesLocked restores the "dirty" tables (shadows,
// connections) from tables via the ephemeral DTO registry, converting each
// DTO back to its live type. The caller MUST hold b.mu for writing.
-func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]jsontext.Value) error {
dtoReg, shadowDTOs, connDTOs := newDirtyDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
diff --git a/services/iotdataplane/persistence_test.go b/services/iotdataplane/persistence_test.go
index ccf80ea9b2..5e83db3757 100644
--- a/services/iotdataplane/persistence_test.go
+++ b/services/iotdataplane/persistence_test.go
@@ -1,13 +1,14 @@
package iotdataplane_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotdataplane"
)
@@ -377,7 +378,7 @@ func Test_Persistence_ShadowWithMetadata(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
state := resp["state"].(map[string]any)
desired := state["desired"].(map[string]any)
@@ -412,7 +413,7 @@ func Test_Persistence_NullWipeSurvivesRoundTrip(t *testing.T) {
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(resp, &doc))
+ require.NoError(t, json.Unmarshal(resp, &doc, jsonx.V1()))
state := doc["state"].(map[string]any)
_, hasDesired := state["desired"]
diff --git a/services/iotdataplane/publish.go b/services/iotdataplane/publish.go
index 6eba6a8703..f19ddf593e 100644
--- a/services/iotdataplane/publish.go
+++ b/services/iotdataplane/publish.go
@@ -2,11 +2,13 @@ package iotdataplane
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// payloadFormatIndicatorUnspecifiedBytes and payloadFormatIndicatorUTF8Data
@@ -108,7 +110,7 @@ func parseUserProperties(raw []byte) ([]MQTT5UserProperty, error) {
}
var entries []map[string]string
- if err := json.Unmarshal(raw, &entries); err != nil {
+ if err := json.Unmarshal(raw, &entries, jsonx.V1()); err != nil {
return nil, fmt.Errorf(
"%w: userProperties must be a JSON array of single-key objects",
ErrValidation,
diff --git a/services/iotdataplane/retained_messages_test.go b/services/iotdataplane/retained_messages_test.go
index 5208022651..927627ca26 100644
--- a/services/iotdataplane/retained_messages_test.go
+++ b/services/iotdataplane/retained_messages_test.go
@@ -2,7 +2,7 @@ package iotdataplane_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotdataplane"
)
@@ -212,7 +213,7 @@ func Test_ListRetainedMessages_Pagination(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
topics1 := page1["retainedTopics"].([]any)
assert.Len(t, topics1, 2)
@@ -225,7 +226,7 @@ func Test_ListRetainedMessages_Pagination(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var page2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
topics2 := page2["retainedTopics"].([]any)
assert.Len(t, topics2, 2)
}
@@ -237,7 +238,7 @@ func Test_ListRetainedMessages_NonNilWhenEmpty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
topics, ok := resp["retainedTopics"]
require.True(t, ok)
@@ -318,7 +319,7 @@ func Test_ListRetainedMessages_PageSizeParam(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
topics := resp["retainedTopics"].([]any)
assert.Len(t, topics, tt.wantPageCount, "query: %s", tt.query)
})
@@ -339,7 +340,7 @@ func Test_ListRetainedMessages_PaginationOffByOneFixed(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
topics1 := page1["retainedTopics"].([]any)
require.Len(t, topics1, 2)
@@ -354,7 +355,7 @@ func Test_ListRetainedMessages_PaginationOffByOneFixed(t *testing.T) {
// Page 2: cursor=c/3 → page starts at c/3 (nextToken IS the first item of the page).
rec = doRequest(t, h, http.MethodGet, "/retainedMessage?pageSize=2&nextToken="+nextToken, nil)
var page2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
topics2 := page2["retainedTopics"].([]any)
require.Len(t, topics2, 2)
@@ -373,7 +374,7 @@ func Test_ListRetainedMessages_SummaryIncludesQos(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
topics := resp["retainedTopics"].([]any)
require.Len(t, topics, 1)
@@ -401,7 +402,7 @@ func Test_GetRetainedMessage_StillHasQos(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "qos", "GetRetainedMessage full response must include qos")
}
@@ -433,7 +434,7 @@ func Test_GetRetainedMessage_UserPropertiesField(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Contains(t, resp, "userProperties")
if tt.wantNull {
@@ -489,7 +490,7 @@ func Test_RetainedMessage_GetNonexistent_404Shape(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["error"])
}
func Test_RetainedMessage_Lifecycle(t *testing.T) {
@@ -550,7 +551,7 @@ func Test_ListRetainedMessages_MaxResultsAlias(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
topics := resp["retainedTopics"].([]any)
assert.Len(t, topics, 3)
diff --git a/services/iotdataplane/shadows.go b/services/iotdataplane/shadows.go
index c17825ed6c..49773de1bf 100644
--- a/services/iotdataplane/shadows.go
+++ b/services/iotdataplane/shadows.go
@@ -1,7 +1,8 @@
package iotdataplane
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -9,6 +10,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/collections"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// validateThingName checks that a thing name meets AWS IoT naming rules.
@@ -83,8 +85,8 @@ func validateShadowDocument(doc []byte) error {
return fmt.Errorf("%w: The payload exceeds the maximum size allowed", ErrRequestTooLarge)
}
- var obj map[string]json.RawMessage
- if err := json.Unmarshal(doc, &obj); err != nil || obj == nil {
+ var obj map[string]jsontext.Value
+ if err := json.Unmarshal(doc, &obj, jsonx.V1()); err != nil || obj == nil {
return fmt.Errorf("%w: Invalid JSON", ErrValidation)
}
@@ -92,16 +94,16 @@ func validateShadowDocument(doc []byte) error {
}
// isJSONNull reports whether v is the JSON null literal.
-func isJSONNull(v json.RawMessage) bool {
+func isJSONNull(v jsontext.Value) bool {
var x any
- return json.Unmarshal(v, &x) == nil && x == nil
+ return json.Unmarshal(v, &x, jsonx.V1()) == nil && x == nil
}
// mergeStateFields deep-merges patch into base: null deletes a key, nested
// objects merge recursively, other values replace. base and patch are unchanged.
-func mergeStateFields(base, patch map[string]json.RawMessage) map[string]json.RawMessage {
- result := make(map[string]json.RawMessage, len(base))
+func mergeStateFields(base, patch map[string]jsontext.Value) map[string]jsontext.Value {
+ result := make(map[string]jsontext.Value, len(base))
maps.Copy(result, base)
for k, v := range patch {
@@ -116,7 +118,7 @@ func mergeStateFields(base, patch map[string]json.RawMessage) map[string]json.Ra
return result
}
-func mergeJSONValue(existing, patch json.RawMessage) json.RawMessage {
+func mergeJSONValue(existing, patch jsontext.Value) jsontext.Value {
patchObj, ok := decodeJSONObject(patch)
if !ok {
return patch
@@ -127,7 +129,7 @@ func mergeJSONValue(existing, patch json.RawMessage) json.RawMessage {
existingObj = nil
}
- merged, err := json.Marshal(mergeStateFields(existingObj, patchObj))
+ merged, err := json.Marshal(mergeStateFields(existingObj, patchObj), jsonx.V1())
if err != nil {
return patch
}
@@ -135,9 +137,9 @@ func mergeJSONValue(existing, patch json.RawMessage) json.RawMessage {
return merged
}
-func decodeJSONObject(raw json.RawMessage) (map[string]json.RawMessage, bool) {
- var obj map[string]json.RawMessage
- if len(raw) == 0 || json.Unmarshal(raw, &obj) != nil || obj == nil {
+func decodeJSONObject(raw jsontext.Value) (map[string]jsontext.Value, bool) {
+ var obj map[string]jsontext.Value
+ if len(raw) == 0 || json.Unmarshal(raw, &obj, jsonx.V1()) != nil || obj == nil {
return nil, false
}
@@ -146,7 +148,7 @@ func decodeJSONObject(raw json.RawMessage) (map[string]json.RawMessage, bool) {
// updateMetaFields returns a copy of meta with timestamps updated for keys present in patch.
// Null-deleted keys are removed from the returned meta map.
-func updateMetaFields(meta map[string]int64, patch map[string]json.RawMessage, ts int64) map[string]int64 {
+func updateMetaFields(meta map[string]int64, patch map[string]jsontext.Value, ts int64) map[string]int64 {
result := make(map[string]int64, len(meta)+len(patch))
maps.Copy(result, meta)
@@ -163,8 +165,8 @@ func updateMetaFields(meta map[string]int64, patch map[string]json.RawMessage, t
// computeDelta returns the fields where desired differs from reported, recursing
// into nested objects so only the differing members appear. nil means no delta.
-func computeDelta(desired, reported map[string]json.RawMessage) map[string]json.RawMessage {
- delta := make(map[string]json.RawMessage)
+func computeDelta(desired, reported map[string]jsontext.Value) map[string]jsontext.Value {
+ delta := make(map[string]jsontext.Value)
for k, dv := range desired {
rv, ok := reported[k]
@@ -179,7 +181,7 @@ func computeDelta(desired, reported map[string]json.RawMessage) map[string]json.
if dIsObj && rIsObj {
if sub := computeDelta(dObj, rObj); sub != nil {
- if raw, err := json.Marshal(sub); err == nil {
+ if raw, err := json.Marshal(sub, jsonx.V1()); err == nil {
delta[k] = raw
}
}
@@ -199,14 +201,14 @@ func computeDelta(desired, reported map[string]json.RawMessage) map[string]json.
return delta
}
-func jsonRawEqual(a, b json.RawMessage) bool {
+func jsonRawEqual(a, b jsontext.Value) bool {
var av, bv any
- if json.Unmarshal(a, &av) != nil || json.Unmarshal(b, &bv) != nil {
+ if json.Unmarshal(a, &av, jsonx.V1()) != nil || json.Unmarshal(b, &bv, jsonx.V1()) != nil {
return string(a) == string(b)
}
- ab, errA := json.Marshal(av)
- bb, errB := json.Marshal(bv)
+ ab, errA := json.Marshal(av, jsonx.V1())
+ bb, errB := json.Marshal(bv, jsonx.V1())
return errA == nil && errB == nil && string(ab) == string(bb)
}
@@ -272,7 +274,7 @@ func buildShadowResponse(entry *shadowEntry, clientToken string) ([]byte, error)
resp["clientToken"] = clientToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// sortedKeys returns a sorted copy of the keys of a map[string]V.
@@ -303,8 +305,8 @@ func (b *InMemoryBackend) GetThingShadow(thingName, shadowName string) ([]byte,
type shadowUpdateInput struct {
Version *int
ClientToken string
- StateDesired json.RawMessage
- StateReported json.RawMessage
+ StateDesired jsontext.Value
+ StateReported jsontext.Value
}
// parseShadowUpdateDoc validates and parses an UpdateThingShadow request body.
@@ -312,12 +314,12 @@ type shadowUpdateInput struct {
func parseShadowUpdateDoc(document []byte) (*shadowUpdateInput, error) {
// Outer document uses RawMessage for State so we can detect absent vs null.
var outer struct {
- Version json.RawMessage `json:"version,omitempty"`
- ClientToken string `json:"clientToken,omitempty"`
- State json.RawMessage `json:"state"`
+ Version jsontext.Value `json:"version,omitempty"`
+ ClientToken string `json:"clientToken,omitempty"`
+ State jsontext.Value `json:"state"`
}
- if err := json.Unmarshal(document, &outer); err != nil {
+ if err := json.Unmarshal(document, &outer, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: Invalid JSON", ErrValidation)
}
@@ -325,7 +327,7 @@ func parseShadowUpdateDoc(document []byte) (*shadowUpdateInput, error) {
if len(outer.Version) > 0 && !isJSONNull(outer.Version) {
var v int
- if err := json.Unmarshal(outer.Version, &v); err != nil {
+ if err := json.Unmarshal(outer.Version, &v, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: Invalid version", ErrValidation)
}
@@ -345,11 +347,11 @@ func parseShadowUpdateDoc(document []byte) (*shadowUpdateInput, error) {
}
var stateDoc struct {
- Desired json.RawMessage `json:"desired"`
- Reported json.RawMessage `json:"reported"`
+ Desired jsontext.Value `json:"desired"`
+ Reported jsontext.Value `json:"reported"`
}
- if err := json.Unmarshal(outer.State, &stateDoc); err != nil {
+ if err := json.Unmarshal(outer.State, &stateDoc, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: State node must be an object", ErrValidation)
}
@@ -389,9 +391,9 @@ func jsonValueDepth(v any) int {
// validateShadowStateDepth checks that a state.desired/state.reported section
// does not exceed AWS IoT's documented maximum JSON nesting depth (see
// maxShadowStateDepth). raw must already be known non-empty and non-null.
-func validateShadowStateDepth(raw json.RawMessage, sectionName string) error {
+func validateShadowStateDepth(raw jsontext.Value, sectionName string) error {
var v any
- if err := json.Unmarshal(raw, &v); err != nil {
+ if err := json.Unmarshal(raw, &v, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %s node must be an object", ErrValidation, sectionLabel(sectionName))
}
@@ -406,12 +408,12 @@ func validateShadowStateDepth(raw json.RawMessage, sectionName string) error {
// applyShadowStateSection merges a raw state section into existing state.
// raw absent (nil) → keep existing; raw null → clear; raw object → merge patch.
func applyShadowStateSection(
- existing map[string]json.RawMessage,
+ existing map[string]jsontext.Value,
existingMeta map[string]int64,
- raw json.RawMessage,
+ raw jsontext.Value,
sectionName string,
ts int64,
-) (map[string]json.RawMessage, map[string]int64, error) {
+) (map[string]jsontext.Value, map[string]int64, error) {
if len(raw) == 0 {
return existing, existingMeta, nil
}
@@ -424,8 +426,8 @@ func applyShadowStateSection(
return nil, nil, err
}
- var patch map[string]json.RawMessage
- if err := json.Unmarshal(raw, &patch); err != nil {
+ var patch map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &patch, jsonx.V1()); err != nil {
return nil, nil, fmt.Errorf("%w: %s node must be an object", ErrValidation, sectionLabel(sectionName))
}
@@ -464,7 +466,7 @@ func (b *InMemoryBackend) UpdateThingShadow(thingName, shadowName string, docume
now := time.Now()
ts := now.Unix()
- var existingDesired, existingReported map[string]json.RawMessage
+ var existingDesired, existingReported map[string]jsontext.Value
var existingMetaDesired, existingMetaReported map[string]int64
if current != nil {
@@ -561,7 +563,7 @@ func (b *InMemoryBackend) DeleteThingShadow(thingName, shadowName string) ([]byt
payload, err := json.Marshal(map[string]any{
"version": entry.version,
keyTimestamp: entry.updatedAt.Unix(),
- })
+ }, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("iotdataplane: marshal delete response: %w", err)
}
@@ -635,12 +637,12 @@ func (b *InMemoryBackend) AddShadowInternal(thingName, shadowName string, docume
// Best-effort parse of state.desired / state.reported from the seeded document.
var doc struct {
State struct {
- Desired map[string]json.RawMessage `json:"desired"`
- Reported map[string]json.RawMessage `json:"reported"`
+ Desired map[string]jsontext.Value `json:"desired"`
+ Reported map[string]jsontext.Value `json:"reported"`
} `json:"state"`
}
- if json.Unmarshal(document, &doc) == nil {
+ if json.Unmarshal(document, &doc, jsonx.V1()) == nil {
if doc.State.Desired != nil {
entry.desired = doc.State.Desired
}
diff --git a/services/iotdataplane/shadows_merge_test.go b/services/iotdataplane/shadows_merge_test.go
index c838557587..4e83e86bf1 100644
--- a/services/iotdataplane/shadows_merge_test.go
+++ b/services/iotdataplane/shadows_merge_test.go
@@ -1,13 +1,15 @@
package iotdataplane_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotdataplane"
)
@@ -83,7 +85,7 @@ func TestBackend_ShadowVersioning(t *testing.T) {
require.NoError(t, err)
var result map[string]any
- require.NoError(t, json.Unmarshal(resp, &result))
+ require.NoError(t, json.Unmarshal(resp, &result, jsonx.V1()))
gotVersion, ok := result["version"].(float64)
require.True(t, ok, "response should contain numeric version field")
@@ -247,7 +249,7 @@ func Test_ShadowVersion_OverflowProtection(t *testing.T) {
require.NoError(t, err)
var result map[string]any
- require.NoError(t, json.Unmarshal(resp, &result))
+ require.NoError(t, json.Unmarshal(resp, &result, jsonx.V1()))
versionFloat, ok := result["version"].(float64)
require.True(t, ok, "version must be a number")
@@ -270,7 +272,7 @@ func Test_ShadowMerge_DesiredPartialUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
state := resp["state"].(map[string]any)
desired := state["desired"].(map[string]any)
@@ -292,7 +294,7 @@ func Test_ShadowMerge_NullDeletion(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/things/dev2/shadow", nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
state := resp["state"].(map[string]any)
desired := state["desired"].(map[string]any)
@@ -315,7 +317,7 @@ func Test_ShadowMerge_ReportedIndependent(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/things/dev3/shadow", nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
state := resp["state"].(map[string]any)
desired := state["desired"].(map[string]any)
@@ -331,12 +333,12 @@ func Test_ShadowMerge_VersionIncrements(t *testing.T) {
rec1 := doRequest(t, h, http.MethodPost, "/things/dev-ver/shadow",
[]byte(`{"state":{"desired":{"k":"v1"}}}`))
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1, jsonx.V1()))
rec2 := doRequest(t, h, http.MethodPost, "/things/dev-ver/shadow",
[]byte(`{"state":{"desired":{"k":"v2"}}}`))
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
v1 := r1["version"].(float64)
v2 := r2["version"].(float64)
@@ -355,7 +357,7 @@ func Test_ShadowDelta_ComputedOnGet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
state := resp["state"].(map[string]any)
delta, hasDelta := state["delta"]
@@ -377,7 +379,7 @@ func Test_ShadowDelta_AbsentWhenEqual(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/things/dev-nodelta/shadow", nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
state := resp["state"].(map[string]any)
_, hasDelta := state["delta"]
@@ -400,7 +402,7 @@ func Test_ShadowDelta_UpdatedAfterReported(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/things/dev-delta2/shadow", nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
state := resp["state"].(map[string]any)
_, hasDelta := state["delta"]
@@ -417,7 +419,7 @@ func Test_ShadowMetadata_PresentAfterUpdate(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/things/dev-meta/shadow", nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
meta, hasMeta := resp["metadata"]
require.True(t, hasMeta, "metadata must be present after update")
@@ -440,7 +442,7 @@ func Test_ShadowMetadata_UnchangedFieldTimestampPreserved(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/things/dev-meta2/shadow", nil)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp1, jsonx.V1()))
ts1 := resp1["metadata"].(map[string]any)["desired"].(map[string]any)["a"].(map[string]any)["timestamp"].(float64)
// Update only b.
@@ -450,7 +452,7 @@ func Test_ShadowMetadata_UnchangedFieldTimestampPreserved(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/things/dev-meta2/shadow", nil)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp2, jsonx.V1()))
ts2 := resp2["metadata"].(map[string]any)["desired"].(map[string]any)["a"].(map[string]any)["timestamp"].(float64)
assert.InDelta(t, ts1, ts2, 0, "timestamp for unmodified field must not change")
@@ -486,7 +488,7 @@ func Test_ClientToken_Echoed(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
token, hasToken := resp["clientToken"]
if tt.wantPresent {
@@ -545,9 +547,9 @@ func Test_MergeStateFields(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- var base, patch map[string]json.RawMessage
- require.NoError(t, json.Unmarshal([]byte(tt.base), &base))
- require.NoError(t, json.Unmarshal([]byte(tt.patch), &patch))
+ var base, patch map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal([]byte(tt.base), &base, jsonx.V1()))
+ require.NoError(t, json.Unmarshal([]byte(tt.patch), &patch, jsonx.V1()))
result := iotdataplane.MergeStateFields(base, patch)
diff --git a/services/iotdataplane/shadows_pagination_test.go b/services/iotdataplane/shadows_pagination_test.go
index 2191c43f4b..5105d40ed8 100644
--- a/services/iotdataplane/shadows_pagination_test.go
+++ b/services/iotdataplane/shadows_pagination_test.go
@@ -1,7 +1,7 @@
package iotdataplane_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotdataplane"
)
@@ -31,7 +32,7 @@ func Test_ListNamedShadows_ExcludesClassicShadow(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results, ok := resp["results"].([]any)
require.True(t, ok, "results must be an array")
@@ -85,7 +86,7 @@ func Test_ListNamedShadows_ResponseShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for _, field := range tt.wantFields {
_, ok := resp[field]
@@ -119,7 +120,7 @@ func Test_ListNamedShadows_SortedAlphabetically(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results, ok := resp["results"].([]any)
require.True(t, ok)
@@ -144,7 +145,7 @@ func Test_ListNamedShadows_IncludesTimestamp(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "results")
assert.Contains(t, resp, "timestamp")
}
@@ -171,7 +172,7 @@ func Test_ListThingsWithShadows_Empty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
things := resp["things"].([]any)
assert.Empty(t, things)
}
@@ -188,7 +189,7 @@ func Test_ListThingsWithShadows_Multiple(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
things := resp["things"].([]any)
assert.Len(t, things, 3)
// Must be sorted.
@@ -231,7 +232,7 @@ func Test_ListThingsWithShadows_PageSize(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
things := resp["things"].([]any)
assert.Len(t, things, 10)
assert.Contains(t, resp, "nextToken")
@@ -251,7 +252,7 @@ func Test_ListNamedShadows_PageSize(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results := resp["results"].([]any)
assert.Len(t, results, 10)
assert.Contains(t, resp, "nextToken")
@@ -270,7 +271,7 @@ func Test_ListThingsWithShadows_PaginationOffByOneFixed(t *testing.T) {
rec := doRequest(t, h, http.MethodGet,
"/api/things/shadow/ListThingsWithShadows?pageSize=2", nil)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
things1 := page1["things"].([]any)
require.Len(t, things1, 2)
@@ -287,7 +288,7 @@ func Test_ListThingsWithShadows_PaginationOffByOneFixed(t *testing.T) {
rec = doRequest(t, h, http.MethodGet,
"/api/things/shadow/ListThingsWithShadows?pageSize=2&nextToken="+nextToken, nil)
var page2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
things2 := page2["things"].([]any)
require.Len(t, things2, 2)
@@ -305,7 +306,7 @@ func Test_ListThingsWithShadows_IncludesTimestamp(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "things")
assert.Contains(t, resp, "timestamp")
@@ -329,7 +330,7 @@ func Test_ListThingsWithShadows_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
things1 := page1["things"].([]any)
assert.Len(t, things1, 2)
nextToken := page1["nextToken"].(string)
@@ -339,7 +340,7 @@ func Test_ListThingsWithShadows_Pagination(t *testing.T) {
paginatedURL := "/api/things/shadow/ListThingsWithShadows?pageSize=2&nextToken=" + nextToken
rec2 := doRequest(t, h, http.MethodGet, paginatedURL, nil)
var page2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
things2 := page2["things"].([]any)
assert.Len(t, things2, 2)
diff --git a/services/iotdataplane/shadows_state_test.go b/services/iotdataplane/shadows_state_test.go
index 14c980e0ec..497038f2c7 100644
--- a/services/iotdataplane/shadows_state_test.go
+++ b/services/iotdataplane/shadows_state_test.go
@@ -1,13 +1,14 @@
package iotdataplane_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotdataplane"
)
@@ -29,7 +30,7 @@ func Test_ShadowDesiredNull_WipesDesired(t *testing.T) {
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(resp, &doc))
+ require.NoError(t, json.Unmarshal(resp, &doc, jsonx.V1()))
state := doc["state"].(map[string]any)
_, hasDesired := state["desired"]
@@ -51,7 +52,7 @@ func Test_ShadowDesiredNull_LeavesReportedIntact(t *testing.T) {
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(resp, &doc))
+ require.NoError(t, json.Unmarshal(resp, &doc, jsonx.V1()))
state := doc["state"].(map[string]any)
_, hasDesired := state["desired"]
@@ -78,7 +79,7 @@ func Test_ShadowReportedNull_WipesReported(t *testing.T) {
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(resp, &doc))
+ require.NoError(t, json.Unmarshal(resp, &doc, jsonx.V1()))
state := doc["state"].(map[string]any)
_, hasReported := state["reported"]
@@ -105,7 +106,7 @@ func Test_ShadowBothNull_WipesBoth(t *testing.T) {
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(resp, &doc))
+ require.NoError(t, json.Unmarshal(resp, &doc, jsonx.V1()))
state := doc["state"].(map[string]any)
_, hasDesired := state["desired"]
@@ -133,7 +134,7 @@ func Test_ShadowDesiredNull_ThenResetDesired(t *testing.T) {
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(resp, &doc))
+ require.NoError(t, json.Unmarshal(resp, &doc, jsonx.V1()))
state := doc["state"].(map[string]any)
desired := state["desired"].(map[string]any)
@@ -150,12 +151,12 @@ func Test_ShadowDesiredNull_VersionStillIncrements(t *testing.T) {
resp1, err := b.UpdateThingShadow("dev", "", []byte(`{"state":{"desired":{"k":"v"}}}`))
require.NoError(t, err)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(resp1, &r1))
+ require.NoError(t, json.Unmarshal(resp1, &r1, jsonx.V1()))
resp2, err := b.UpdateThingShadow("dev", "", []byte(`{"state":{"desired":null}}`))
require.NoError(t, err)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(resp2, &r2))
+ require.NoError(t, json.Unmarshal(resp2, &r2, jsonx.V1()))
v1 := int(r1["version"].(float64))
v2 := int(r2["version"].(float64))
@@ -175,7 +176,7 @@ func Test_ShadowDesiredNull_ViaHTTP(t *testing.T) {
// Confirm desired absent in GET.
rec = doRequest(t, h, http.MethodGet, "/things/dev/shadow", nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
state := resp["state"].(map[string]any)
_, hasDesired := state["desired"]
@@ -193,7 +194,7 @@ func Test_ShadowResponse_OnlyDesiredSet_ReportedOmitted(t *testing.T) {
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(resp, &doc))
+ require.NoError(t, json.Unmarshal(resp, &doc, jsonx.V1()))
state := doc["state"].(map[string]any)
_, hasDesired := state["desired"]
@@ -213,7 +214,7 @@ func Test_ShadowResponse_OnlyReportedSet_DesiredOmitted(t *testing.T) {
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(resp, &doc))
+ require.NoError(t, json.Unmarshal(resp, &doc, jsonx.V1()))
state := doc["state"].(map[string]any)
_, hasReported := state["reported"]
@@ -233,7 +234,7 @@ func Test_ShadowResponse_BothSet_BothPresent(t *testing.T) {
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(resp, &doc))
+ require.NoError(t, json.Unmarshal(resp, &doc, jsonx.V1()))
state := doc["state"].(map[string]any)
_, hasDesired := state["desired"]
@@ -256,7 +257,7 @@ func Test_ShadowResponse_EmptyStateSection_StateObjectPresent(t *testing.T) {
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(resp, &doc))
+ require.NoError(t, json.Unmarshal(resp, &doc, jsonx.V1()))
// "state" key must still exist (as empty object {}).
_, hasState := doc["state"]
@@ -285,7 +286,7 @@ func Test_ShadowMetadata_ClearedOnDesiredNull(t *testing.T) {
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(resp, &doc))
+ require.NoError(t, json.Unmarshal(resp, &doc, jsonx.V1()))
meta, hasMeta := doc["metadata"].(map[string]any)
if hasMeta {
@@ -313,7 +314,7 @@ func Test_ShadowDelta_AfterDesiredWipe_NoDelta(t *testing.T) {
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(resp, &doc))
+ require.NoError(t, json.Unmarshal(resp, &doc, jsonx.V1()))
state := doc["state"].(map[string]any)
_, hasDelta := state["delta"]
@@ -332,7 +333,7 @@ func Test_ShadowUpdate_EmptyDesiredPatch_NoOpForKeys(t *testing.T) {
require.NoError(t, err)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(resp2, &r2))
+ require.NoError(t, json.Unmarshal(resp2, &r2, jsonx.V1()))
assert.InDelta(t, float64(2), r2["version"], 0)
state := r2["state"].(map[string]any)
@@ -361,7 +362,7 @@ func Test_ShadowUpdate_MultiplePatchesAccumulate(t *testing.T) {
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(resp, &doc))
+ require.NoError(t, json.Unmarshal(resp, &doc, jsonx.V1()))
state := doc["state"].(map[string]any)
desired := state["desired"].(map[string]any)
diff --git a/services/iotdataplane/shadows_test.go b/services/iotdataplane/shadows_test.go
index d0449acecc..08df3c6220 100644
--- a/services/iotdataplane/shadows_test.go
+++ b/services/iotdataplane/shadows_test.go
@@ -1,7 +1,7 @@
package iotdataplane_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotdataplane"
)
@@ -22,7 +23,7 @@ func TestHandler_ShadowCRUD(t *testing.T) {
"desired": map[string]any{"color": "red"},
},
}
- docBytes, err := json.Marshal(shadowDoc)
+ docBytes, err := json.Marshal(shadowDoc, jsonx.V1())
require.NoError(t, err)
// Step 1: shadow does not exist yet.
@@ -133,7 +134,7 @@ func TestHandler_ShadowResponseContainsVersionAndTimestamp(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "version")
assert.Contains(t, resp, "timestamp")
@@ -142,7 +143,7 @@ func TestHandler_ShadowResponseContainsVersionAndTimestamp(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Contains(t, getResp, "version")
assert.Contains(t, getResp, "timestamp")
}
@@ -174,7 +175,7 @@ func updateShadow(t *testing.T, h *iotdataplane.Handler, thingName, shadowName s
require.Equal(t, http.StatusOK, rec.Code, "UpdateThingShadow: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
@@ -194,7 +195,7 @@ func getShadow(t *testing.T, h *iotdataplane.Handler, thingName, shadowName stri
var resp map[string]any
if rec.Body.Len() > 0 {
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
}
return resp, rec.Code
@@ -315,7 +316,7 @@ func Test_DeleteThingShadow_ResponseOmitsState(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasState := resp["state"]
assert.False(t, hasState, "delete response must NOT include state (empty response state document)")
@@ -428,7 +429,7 @@ func Test_DeleteThingShadow_ReturnsPayload(t *testing.T) {
// Response must be a JSON object with version and timestamp (AWS contract).
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "version")
assert.Contains(t, resp, "timestamp")
}
@@ -608,7 +609,7 @@ func Test_CrossThing_ShadowIsolation(t *testing.T) {
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(resp, &doc))
+ require.NoError(t, json.Unmarshal(resp, &doc, jsonx.V1()))
state := doc["state"].(map[string]any)
desired := state["desired"].(map[string]any)
assert.Equal(t, "blue", desired["color"])
@@ -631,7 +632,7 @@ func Test_ClassicVsNamedShadow_Isolation(t *testing.T) {
resp, err := b.GetThingShadow("dev", "named-one")
require.NoError(t, err)
var doc map[string]any
- require.NoError(t, json.Unmarshal(resp, &doc))
+ require.NoError(t, json.Unmarshal(resp, &doc, jsonx.V1()))
state := doc["state"].(map[string]any)
desired := state["desired"].(map[string]any)
assert.Equal(t, "yes", desired["named"])
@@ -659,8 +660,8 @@ func Test_NamedShadow_IndependentVersions(t *testing.T) {
require.NoError(t, err)
var alpha, beta map[string]any
- require.NoError(t, json.Unmarshal(alphaResp, &alpha))
- require.NoError(t, json.Unmarshal(betaResp, &beta))
+ require.NoError(t, json.Unmarshal(alphaResp, &alpha, jsonx.V1()))
+ require.NoError(t, json.Unmarshal(betaResp, &beta, jsonx.V1()))
assert.InDelta(t, float64(3), alpha["version"], 0, "alpha must be at version 3")
assert.InDelta(t, float64(1), beta["version"], 0, "beta must be at version 1")
diff --git a/services/iotdataplane/shadows_validation_test.go b/services/iotdataplane/shadows_validation_test.go
index 7cf29ccc31..3793a41185 100644
--- a/services/iotdataplane/shadows_validation_test.go
+++ b/services/iotdataplane/shadows_validation_test.go
@@ -1,7 +1,7 @@
package iotdataplane_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotdataplane"
)
@@ -50,7 +51,7 @@ func TestBackend_ShadowUpdate_ObjectDocumentSucceeds(t *testing.T) {
require.NoError(t, err)
var result map[string]any
- require.NoError(t, json.Unmarshal(resp, &result), "response must be valid JSON")
+ require.NoError(t, json.Unmarshal(resp, &result, jsonx.V1()), "response must be valid JSON")
assert.Contains(t, result, "version", "response must contain version")
assert.Contains(t, result, "timestamp", "response must contain timestamp")
}
@@ -102,7 +103,7 @@ func Test_UpdateThingShadow_StateRequired(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", resp["error"],
"error type must be InvalidRequestException")
}
@@ -157,7 +158,7 @@ func Test_VersionConflict_RejectedWithCorrectError(t *testing.T) {
if tt.wantErrorType != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantErrorType, resp["error"],
"error type must be ConflictException")
}
@@ -421,7 +422,7 @@ func Test_ThingName_ValidationOnDelete(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "error")
}
func Test_ThingName_ValidationOnListNamedShadows(t *testing.T) {
@@ -493,7 +494,7 @@ func Test_ClientToken_TooLong_ViaHTTP(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", resp["error"])
}
func Test_ClientToken_Exactly64Chars_Accepted(t *testing.T) {
@@ -507,7 +508,7 @@ func Test_ClientToken_Exactly64Chars_Accepted(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, exact, resp["clientToken"])
}
func Test_ShadowUpdate_StateRequired(t *testing.T) {
@@ -584,7 +585,7 @@ func Test_ShadowUpdate_StateRequired_ErrorShape(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", resp["error"])
assert.Contains(t, resp["message"], "state")
}
diff --git a/services/iotdataplane/value_semantics_test.go b/services/iotdataplane/value_semantics_test.go
index 4fb1e694a8..4cec508776 100644
--- a/services/iotdataplane/value_semantics_test.go
+++ b/services/iotdataplane/value_semantics_test.go
@@ -1,13 +1,15 @@
package iotdataplane_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
iotdataplanesdk "github.com/aws/aws-sdk-go-v2/service/iotdataplane"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestShadow_NestedUpdatesMergeAndDeltaIsMinimal(t *testing.T) {
@@ -66,7 +68,7 @@ func TestShadow_NestedUpdatesMergeAndDeltaIsMinimal(t *testing.T) {
var doc struct {
State map[string]any `json:"state"`
}
- require.NoError(t, json.Unmarshal(got.Payload, &doc))
+ require.NoError(t, json.Unmarshal(got.Payload, &doc, jsonx.V1()))
if tt.wantDesired != "" {
assert.JSONEq(t, tt.wantDesired, mustJSON(t, doc.State["desired"]))
@@ -86,7 +88,7 @@ func TestShadow_NestedUpdatesMergeAndDeltaIsMinimal(t *testing.T) {
func mustJSON(t *testing.T, v any) string {
t.Helper()
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
return string(b)
diff --git a/services/iotdataplane/whitebox_test.go b/services/iotdataplane/whitebox_test.go
index 2b517ea9d5..6ae6042f7a 100644
--- a/services/iotdataplane/whitebox_test.go
+++ b/services/iotdataplane/whitebox_test.go
@@ -2,7 +2,7 @@ package iotdataplane
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -11,6 +11,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func doWhiteboxRequest(t *testing.T, h *Handler, method, path string, body []byte) *httptest.ResponseRecorder {
@@ -91,6 +93,6 @@ func Test_ShadowStateDepth_ViaHTTP(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", resp["error"])
}
diff --git a/services/iotdataplane/wire_sdk_roundtrip_test.go b/services/iotdataplane/wire_sdk_roundtrip_test.go
index 4eba4a5657..f143d70d93 100644
--- a/services/iotdataplane/wire_sdk_roundtrip_test.go
+++ b/services/iotdataplane/wire_sdk_roundtrip_test.go
@@ -1,7 +1,7 @@
package iotdataplane_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -14,6 +14,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/iotdataplane"
@@ -112,7 +113,7 @@ func TestShadowLifecycle_SDKRoundTrip(t *testing.T) {
Version int64 `json:"version"`
Timestamp int64 `json:"timestamp"`
}
- require.NoError(t, json.Unmarshal(deleted.Payload, &deleteBody))
+ require.NoError(t, json.Unmarshal(deleted.Payload, &deleteBody, jsonx.V1()))
_, err = client.GetThingShadow(t.Context(), &iotdataplanesdk.GetThingShadowInput{
ThingName: aws.String(thingName),
diff --git a/services/iotwireless/handler.go b/services/iotwireless/handler.go
index 4e3fa6a71b..df1af1d7c7 100644
--- a/services/iotwireless/handler.go
+++ b/services/iotwireless/handler.go
@@ -1,7 +1,8 @@
package iotwireless
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -14,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
@@ -975,7 +977,10 @@ func writeError(c *echo.Context, status int, message string) error {
c.Response().Header().Set("X-Amzn-Errortype", errType)
c.Response().WriteHeader(status)
- _ = json.NewEncoder(c.Response()).Encode(errorResponse{Type: errType, Message: message})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(c.Response(), jsonx.V1()),
+ errorResponse{Type: errType, Message: message},
+ )
return nil
}
@@ -985,7 +990,7 @@ func writeJSON(c *echo.Context, status int, v any) error {
c.Response().Header().Set("Content-Type", "application/json")
c.Response().WriteHeader(status)
- _ = json.NewEncoder(c.Response()).Encode(v)
+ _ = json.MarshalEncode(jsontext.NewEncoder(c.Response(), jsonx.V1()), v)
return nil
}
diff --git a/services/iotwireless/handler_certificates.go b/services/iotwireless/handler_certificates.go
index c87b08617c..32ddf21a8b 100644
--- a/services/iotwireless/handler_certificates.go
+++ b/services/iotwireless/handler_certificates.go
@@ -1,12 +1,13 @@
package iotwireless
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -124,7 +125,7 @@ type startSingleWirelessDeviceImportTaskResponse struct {
func (h *Handler) associateWirelessGatewayWithCertificate(c *echo.Context, gatewayID string, body []byte) error {
var req associateWirelessGatewayWithCertificateRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "invalid request body")
}
@@ -168,7 +169,7 @@ func (h *Handler) startWirelessDeviceImportTask(c *echo.Context) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
task, err := h.Backend.StartWirelessDeviceImportTask(
h.AccountID, h.DefaultRegion, req.DestinationName, req.Positioning, req.Sidewalk.toModel(),
@@ -202,7 +203,7 @@ func (h *Handler) startSingleWirelessDeviceImportTask(c *echo.Context) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
details := SingleImportDetails{DeviceName: req.DeviceName, Positioning: req.Positioning}
if req.Sidewalk != nil {
@@ -293,7 +294,7 @@ func (h *Handler) updateWirelessDeviceImportTask(c *echo.Context, id string) err
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := h.Backend.UpdateWirelessDeviceImportTask(id, req.Sidewalk.toModel()); err != nil {
return handleError(c, err)
diff --git a/services/iotwireless/handler_certificates_test.go b/services/iotwireless/handler_certificates_test.go
index 52f472d9bb..17fb29d617 100644
--- a/services/iotwireless/handler_certificates_test.go
+++ b/services/iotwireless/handler_certificates_test.go
@@ -1,12 +1,14 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_AssociateWirelessGatewayWithCertificate(t *testing.T) {
@@ -46,7 +48,7 @@ func TestHandler_AssociateWirelessGatewayWithCertificate(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
gwID = createResp["Id"].(string)
}
@@ -56,7 +58,7 @@ func TestHandler_AssociateWirelessGatewayWithCertificate(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.iotCertificateID, resp["IotCertificateId"])
}
})
@@ -73,7 +75,7 @@ func TestHandler_GatewayCertificateLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
gwID := createResp["Id"].(string)
// Associate certificate
@@ -86,7 +88,7 @@ func TestHandler_GatewayCertificateLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var certResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &certResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &certResp, jsonx.V1()))
assert.Equal(t, "cert-123", certResp["IotCertificateId"])
// Disassociate certificate
@@ -107,7 +109,7 @@ func TestHandler_ListDevicesForWirelessDeviceImportTask_ByID(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id, _ := createResp["Id"].(string)
require.NotEmpty(t, id)
@@ -115,7 +117,7 @@ func TestHandler_ListDevicesForWirelessDeviceImportTask_ByID(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Equal(t, "dest-import", listResp["DestinationName"])
rec = doIoTWRequest(t, h, http.MethodGet, "/wireless_device_import_task?id=does-not-exist", "")
@@ -159,7 +161,7 @@ func TestHandler_StartWirelessDeviceImportTask(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ := resp["Arn"].(string)
id, _ := resp["Id"].(string)
@@ -207,7 +209,7 @@ func TestHandler_GetWirelessDeviceImportTask(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
taskID = createResp["Id"].(string)
}
@@ -216,7 +218,7 @@ func TestHandler_GetWirelessDeviceImportTask(t *testing.T) {
if tt.wantFound {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.setupDest, resp["DestinationName"])
assert.Equal(t, "Initialized", resp["Status"])
}
@@ -256,7 +258,7 @@ func TestHandler_DeleteWirelessDeviceImportTask(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
taskID = createResp["Id"].(string)
}
@@ -310,7 +312,7 @@ func TestHandler_UpdateWirelessDeviceImportTask(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
taskID := createResp["Id"].(string)
rec = doIoTWRequest(t, h, http.MethodPatch, "/wireless_device_import_task/"+taskID, tt.updateBody)
@@ -321,7 +323,7 @@ func TestHandler_UpdateWirelessDeviceImportTask(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "dest-old", getResp["DestinationName"])
if tt.wantFileList == nil {
@@ -363,7 +365,7 @@ func TestHandler_ListWirelessDeviceImportTasks(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp["WirelessDeviceImportTaskList"].([]any)
require.True(t, ok, "WirelessDeviceImportTaskList should be array")
@@ -401,7 +403,7 @@ func TestHandler_StartSingleWirelessDeviceImportTask(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ := resp["Arn"].(string)
devID, _ := resp["WirelessDeviceId"].(string)
@@ -420,7 +422,7 @@ func TestHandler_ListDevicesForWirelessDeviceImportTask_NoIDReturnsEmpty(t *test
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp["ImportedWirelessDeviceList"].([]any)
require.True(t, ok, "ImportedWirelessDeviceList should be array")
diff --git a/services/iotwireless/handler_destinations.go b/services/iotwireless/handler_destinations.go
index 582fd9afd2..04886b3dee 100644
--- a/services/iotwireless/handler_destinations.go
+++ b/services/iotwireless/handler_destinations.go
@@ -1,11 +1,12 @@
package iotwireless
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -36,7 +37,7 @@ type listDestinationsResponse struct {
func (h *Handler) createDestination(c *echo.Context, body []byte) error {
var req createDestinationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "invalid request body")
}
@@ -114,7 +115,7 @@ func (h *Handler) updateDestination(c *echo.Context, name string) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := h.Backend.UpdateDestination(
h.AccountID, h.DefaultRegion, name,
diff --git a/services/iotwireless/handler_destinations_test.go b/services/iotwireless/handler_destinations_test.go
index 3194fba3ef..f4eb1ec4b0 100644
--- a/services/iotwireless/handler_destinations_test.go
+++ b/services/iotwireless/handler_destinations_test.go
@@ -1,12 +1,14 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateGetListDeleteDestination(t *testing.T) {
@@ -40,7 +42,7 @@ func TestHandler_CreateGetListDeleteDestination(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Equal(t, tt.destName, createResp["Name"])
// Get
@@ -48,7 +50,7 @@ func TestHandler_CreateGetListDeleteDestination(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, tt.destName, getResp["Name"])
assert.Equal(t, tt.expression, getResp["Expression"])
@@ -57,7 +59,7 @@ func TestHandler_CreateGetListDeleteDestination(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
dests, ok := listResp["DestinationList"].([]any)
require.True(t, ok)
assert.Len(t, dests, 1)
@@ -103,6 +105,6 @@ func TestHandler_UpdateDestination(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "rule/updated", getResp["Expression"])
}
diff --git a/services/iotwireless/handler_event_configurations.go b/services/iotwireless/handler_event_configurations.go
index 24308f0d35..46a3a4669d 100644
--- a/services/iotwireless/handler_event_configurations.go
+++ b/services/iotwireless/handler_event_configurations.go
@@ -1,10 +1,12 @@
package iotwireless
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type eventConfigDocResponse struct {
@@ -56,7 +58,7 @@ type listEventConfigurationsResponse struct {
func eventConfigDocFromBody(body []byte) *EventConfigDoc {
var doc EventConfigDoc
- _ = json.Unmarshal(body, &doc)
+ _ = json.Unmarshal(body, &doc, jsonx.V1())
return &doc
}
diff --git a/services/iotwireless/handler_event_configurations_test.go b/services/iotwireless/handler_event_configurations_test.go
index 92d0e22e2d..8eec2f19dd 100644
--- a/services/iotwireless/handler_event_configurations_test.go
+++ b/services/iotwireless/handler_event_configurations_test.go
@@ -1,12 +1,14 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_EventConfigurations(t *testing.T) {
@@ -19,7 +21,7 @@ func TestHandler_EventConfigurations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var defaultResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &defaultResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &defaultResp, jsonx.V1()))
assert.NotContains(t, defaultResp, "DeviceRegistrationState")
// Update event config by resource types (PATCH, not POST -- iotwireless@v1.59.4
@@ -32,7 +34,7 @@ func TestHandler_EventConfigurations(t *testing.T) {
// fabricated always-empty response.
rec = doIoTWRequest(t, h, http.MethodGet, "/event-configurations-resource-types", "")
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &defaultResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &defaultResp, jsonx.V1()))
drs, ok := defaultResp["DeviceRegistrationState"].(map[string]any)
require.True(t, ok)
sidewalk, ok := drs["Sidewalk"].(map[string]any)
@@ -44,7 +46,7 @@ func TestHandler_EventConfigurations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
configs, ok := listResp["EventConfigurationsList"].([]any)
require.True(t, ok)
assert.Empty(t, configs)
@@ -54,7 +56,7 @@ func TestHandler_EventConfigurations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.NotContains(t, getResp, "Join")
// Update resource event configuration
@@ -66,7 +68,7 @@ func TestHandler_EventConfigurations(t *testing.T) {
// Get resource event configuration must now reflect the stored config.
rec = doIoTWRequest(t, h, http.MethodGet, "/event-configurations/some-resource", "")
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
join, ok := getResp["Join"].(map[string]any)
require.True(t, ok)
loRaWAN, ok := join["LoRaWAN"].(map[string]any)
@@ -82,7 +84,7 @@ func TestHandler_EventConfigurations(t *testing.T) {
"",
)
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
configs, ok = listResp["EventConfigurationsList"].([]any)
require.True(t, ok)
require.Len(t, configs, 1)
@@ -124,7 +126,7 @@ func TestHandler_EventConfigurationByResourceTypes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp["DeviceRegistrationState"]
assert.True(t, ok, "persisted event config must be returned")
}
@@ -172,7 +174,7 @@ func TestHandler_ResourceEventConfiguration(t *testing.T) {
// field (it echoes only the event-type configuration); confirm the
// stored per-resource event config round-trips instead.
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp[tt.wantField]
assert.True(t, ok, "persisted event config field %q must be returned", tt.wantField)
})
@@ -189,7 +191,7 @@ func TestHandler_ListEventConfigurations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var pre map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pre))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pre, jsonx.V1()))
assert.Empty(t, pre["EventConfigurationsList"])
// Update config for two resources.
@@ -204,7 +206,7 @@ func TestHandler_ListEventConfigurations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var post map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &post))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &post, jsonx.V1()))
list, ok := post["EventConfigurationsList"].([]any)
require.True(t, ok)
assert.Len(t, list, 2)
@@ -265,7 +267,7 @@ func TestHandler_ListEventConfigurations_FilterByResourceType(t *testing.T) {
Identifier string `json:"Identifier"`
} `json:"EventConfigurationsList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ids := make([]string, 0, len(resp.EventConfigurationsList))
for _, e := range resp.EventConfigurationsList {
diff --git a/services/iotwireless/handler_fuota_tasks.go b/services/iotwireless/handler_fuota_tasks.go
index 83ac62874c..67c8bfc896 100644
--- a/services/iotwireless/handler_fuota_tasks.go
+++ b/services/iotwireless/handler_fuota_tasks.go
@@ -1,13 +1,14 @@
package iotwireless
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -84,7 +85,7 @@ type listMulticastGroupsByFuotaTaskResponse struct {
func (h *Handler) createFuotaTask(c *echo.Context, body []byte) error {
var req createFuotaTaskRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "invalid request body")
}
@@ -166,7 +167,7 @@ func (h *Handler) deleteFuotaTask(c *echo.Context, id string) error {
func (h *Handler) associateMulticastGroupWithFuotaTask(c *echo.Context, fuotaTaskID string, body []byte) error {
var req associateMulticastGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "invalid request body")
}
@@ -181,7 +182,7 @@ func (h *Handler) associateMulticastGroupWithFuotaTask(c *echo.Context, fuotaTas
func (h *Handler) associateWirelessDeviceWithFuotaTask(c *echo.Context, fuotaTaskID string, body []byte) error {
var req associateWirelessDeviceWithFuotaRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "invalid request body")
}
@@ -208,7 +209,7 @@ func (h *Handler) updateFuotaTask(c *echo.Context, id string) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := h.Backend.UpdateFuotaTask(
h.AccountID, h.DefaultRegion, id, req.Name, req.Description,
@@ -230,7 +231,7 @@ func (h *Handler) startFuotaTask(c *echo.Context, id string) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
var startTime *time.Time
diff --git a/services/iotwireless/handler_fuota_tasks_test.go b/services/iotwireless/handler_fuota_tasks_test.go
index 9b76d35e47..942ae0605a 100644
--- a/services/iotwireless/handler_fuota_tasks_test.go
+++ b/services/iotwireless/handler_fuota_tasks_test.go
@@ -1,13 +1,16 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_FuotaTask_LoRaWANRoundTrip verifies the LoRaWANFuotaTask
@@ -27,7 +30,7 @@ func TestHandler_FuotaTask_LoRaWANRoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id, _ := createResp["Id"].(string)
require.NotEmpty(t, id)
@@ -35,7 +38,7 @@ func TestHandler_FuotaTask_LoRaWANRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
loRaWAN, ok := getResp["LoRaWAN"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "US915", loRaWAN["RfRegion"])
@@ -48,7 +51,7 @@ func TestHandler_FuotaTask_LoRaWANRoundTrip(t *testing.T) {
rec = doIoTWRequest(t, h, http.MethodGet, "/fuota-tasks/"+id, "")
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
loRaWAN, ok = getResp["LoRaWAN"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "EU868", loRaWAN["RfRegion"])
@@ -114,7 +117,7 @@ func TestHandler_UpdateFuotaTask_Fields(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id, _ := createResp["Id"].(string)
require.NotEmpty(t, id)
@@ -125,7 +128,7 @@ func TestHandler_UpdateFuotaTask_Fields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, tt.wantValue, getResp[tt.wantField])
})
}
@@ -163,7 +166,7 @@ func TestHandler_StartFuotaTask_StartTime(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id, _ := createResp["Id"].(string)
require.NotEmpty(t, id)
@@ -174,7 +177,7 @@ func TestHandler_StartFuotaTask_StartTime(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
loRaWAN, hasLoRaWAN := getResp["LoRaWAN"].(map[string]any)
if tt.wantStartTime == "" {
@@ -225,7 +228,7 @@ func TestHandler_CreateFuotaTask(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Id"])
assert.NotEmpty(t, resp["Arn"])
})
@@ -310,7 +313,7 @@ func TestHandler_ListMulticastGroupsByFuotaTask(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var ftResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ftResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ftResp, jsonx.V1()))
ftID := ftResp["Id"].(string)
// Create a multicast group
@@ -318,7 +321,7 @@ func TestHandler_ListMulticastGroupsByFuotaTask(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var mgResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mgResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mgResp, jsonx.V1()))
mgID := mgResp["Id"].(string)
// Associate multicast group with FUOTA task; AssociateMulticastGroupWithFuotaTask
@@ -332,7 +335,7 @@ func TestHandler_ListMulticastGroupsByFuotaTask(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
groups, ok := listResp["MulticastGroupList"].([]any)
require.True(t, ok)
assert.Len(t, groups, 1)
@@ -343,7 +346,7 @@ func TestHandler_ListMulticastGroupsByFuotaTask(t *testing.T) {
// List again should return empty
rec = doIoTWRequest(t, h, http.MethodGet, "/fuota-tasks/"+ftID+"/multicast-groups", "")
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
groups = listResp["MulticastGroupList"].([]any)
assert.Empty(t, groups)
}
@@ -364,7 +367,7 @@ func TestHandler_FuotaTaskUpdate(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
// Update
@@ -377,7 +380,7 @@ func TestHandler_FuotaTaskUpdate(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "ft1-updated", getResp["Name"])
}
@@ -397,7 +400,7 @@ func TestHandler_StartFuotaTask(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
// Start the task
@@ -421,7 +424,7 @@ func TestHandler_DisassociateWirelessDeviceFromFuotaTask(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
ftID := createResp["Id"].(string)
// Create a device
@@ -435,7 +438,7 @@ func TestHandler_DisassociateWirelessDeviceFromFuotaTask(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var devResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &devResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &devResp, jsonx.V1()))
devID := devResp["Id"].(string)
// Associate device with FUOTA task; AssociateWirelessDeviceWithFuotaTask
@@ -482,7 +485,7 @@ func TestHandler_FuotaTask_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
require.NotEmpty(t, id)
@@ -491,7 +494,7 @@ func TestHandler_FuotaTask_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, tt.taskName, getResp["Name"])
assert.Equal(t, id, getResp["Id"])
@@ -500,7 +503,7 @@ func TestHandler_FuotaTask_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
list, ok := listResp["FuotaTaskList"].([]any)
require.True(t, ok)
assert.Len(t, list, 1)
@@ -543,7 +546,7 @@ func TestHandler_FuotaTasks_FullLifecycle(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
fuotaID, _ := createResp["Id"].(string)
require.NotEmpty(t, fuotaID, "CreateFuotaTask must return an Id")
diff --git a/services/iotwireless/handler_log_levels.go b/services/iotwireless/handler_log_levels.go
index 91ea8602da..4e64bc0f5d 100644
--- a/services/iotwireless/handler_log_levels.go
+++ b/services/iotwireless/handler_log_levels.go
@@ -1,10 +1,12 @@
package iotwireless
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// getResourceLogLevelResponse mirrors GetResourceLogLevelOutput, which
@@ -52,7 +54,7 @@ func (h *Handler) updateLogLevelsByResourceTypes(c *echo.Context) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
cfg := LogLevelsConfig{
DefaultLogLevel: req.DefaultLogLevel,
@@ -95,7 +97,7 @@ func (h *Handler) putResourceLogLevel(c *echo.Context, id string) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := h.Backend.PutResourceLogLevel(id, req.LogLevel); err != nil {
return handleError(c, err)
diff --git a/services/iotwireless/handler_log_levels_test.go b/services/iotwireless/handler_log_levels_test.go
index 839d6fdd8f..8522edb3ed 100644
--- a/services/iotwireless/handler_log_levels_test.go
+++ b/services/iotwireless/handler_log_levels_test.go
@@ -1,12 +1,14 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_LogLevels(t *testing.T) {
@@ -19,7 +21,7 @@ func TestHandler_LogLevels(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "INFO", getResp["DefaultLogLevel"])
// Update log levels
@@ -28,7 +30,7 @@ func TestHandler_LogLevels(t *testing.T) {
// Verify updated
rec = doIoTWRequest(t, h, http.MethodGet, "/log-levels", "")
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "DEBUG", getResp["DefaultLogLevel"])
// Put resource log level
@@ -45,7 +47,7 @@ func TestHandler_LogLevels(t *testing.T) {
rec = doIoTWRequest(t, h, http.MethodGet, "/log-levels/my-device", "")
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "ERROR", getResp["LogLevel"])
// Reset resource log level
@@ -54,7 +56,7 @@ func TestHandler_LogLevels(t *testing.T) {
// After reset, should return INFO default
rec = doIoTWRequest(t, h, http.MethodGet, "/log-levels/my-device", "")
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "INFO", getResp["LogLevel"])
// Reset all resource log levels
@@ -91,7 +93,7 @@ func TestHandler_LogLevels_OptionListsRoundTrip(t *testing.T) {
WirelessDeviceLogOptions []map[string]any `json:"WirelessDeviceLogOptions"`
WirelessGatewayLogOptions []map[string]any `json:"WirelessGatewayLogOptions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ERROR", resp.DefaultLogLevel)
require.Len(t, resp.WirelessDeviceLogOptions, 1)
diff --git a/services/iotwireless/handler_metrics.go b/services/iotwireless/handler_metrics.go
index bd2fe2b0ab..c6cf13a281 100644
--- a/services/iotwireless/handler_metrics.go
+++ b/services/iotwireless/handler_metrics.go
@@ -1,10 +1,12 @@
package iotwireless
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const stubServiceType = "CUPS"
@@ -65,7 +67,7 @@ func (h *Handler) updateMetricConfiguration(c *echo.Context) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := h.Backend.UpdateMetricConfigurationStatus(req.SummaryMetric.Status); err != nil {
return handleError(c, err)
@@ -117,7 +119,7 @@ func (h *Handler) getMetrics(c *echo.Context) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
results := make([]summaryMetricQueryResultResponse, 0, len(req.SummaryMetricQueries))
for _, q := range req.SummaryMetricQueries {
diff --git a/services/iotwireless/handler_metrics_test.go b/services/iotwireless/handler_metrics_test.go
index 8473561201..38e0d776ba 100644
--- a/services/iotwireless/handler_metrics_test.go
+++ b/services/iotwireless/handler_metrics_test.go
@@ -1,12 +1,14 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_GetMetricConfiguration(t *testing.T) {
@@ -19,7 +21,7 @@ func TestHandler_GetMetricConfiguration(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
summaryMetric, ok := getResp["SummaryMetric"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "Enabled", summaryMetric["Status"])
@@ -31,7 +33,7 @@ func TestHandler_GetMetricConfiguration(t *testing.T) {
rec = doIoTWRequest(t, h, http.MethodGet, "/metric-configuration", "")
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
summaryMetric, ok = getResp["SummaryMetric"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "Disabled", summaryMetric["Status"])
@@ -52,7 +54,7 @@ func TestHandler_GetMetrics_MapsQueriesToResults(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results, ok := resp["SummaryMetricQueryResults"].([]any)
require.True(t, ok)
require.Len(t, results, 2)
@@ -71,7 +73,7 @@ func TestHandler_GetServiceEndpoint_ExactURLsAndValidation(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "CUPS", resp["ServiceType"])
assert.Equal(t, "https://cups.lorawan.us-east-1.amazonaws.com", resp["ServiceEndpoint"])
@@ -80,7 +82,7 @@ func TestHandler_GetServiceEndpoint_ExactURLsAndValidation(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var lnsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lnsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lnsResp, jsonx.V1()))
assert.Equal(t, "LNS", lnsResp["ServiceType"])
assert.Equal(t, "https://lns.lorawan.us-east-1.amazonaws.com", lnsResp["ServiceEndpoint"])
@@ -126,7 +128,7 @@ func TestHandler_GetMetrics_ReturnsEmptyResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results, ok := resp["SummaryMetricQueryResults"].([]any)
require.True(t, ok)
@@ -168,7 +170,7 @@ func TestHandler_MetricConfiguration_TableDriven(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp[tt.wantKV]
assert.True(t, ok, "expected key %q in response", tt.wantKV)
})
@@ -214,7 +216,7 @@ func TestHandler_GetServiceEndpoint_TableDriven(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantServiceType, resp["ServiceType"])
endpoint, _ := resp["ServiceEndpoint"].(string)
assert.Contains(t, endpoint, tt.wantSubstr)
diff --git a/services/iotwireless/handler_multicast_groups.go b/services/iotwireless/handler_multicast_groups.go
index e025bdb73e..c156629de7 100644
--- a/services/iotwireless/handler_multicast_groups.go
+++ b/services/iotwireless/handler_multicast_groups.go
@@ -1,7 +1,8 @@
package iotwireless
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"time"
@@ -9,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -59,7 +61,7 @@ func (h *Handler) createMulticastGroup(c *echo.Context) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
mg, err := h.Backend.CreateMulticastGroup(
h.AccountID,
@@ -141,7 +143,7 @@ func (h *Handler) updateMulticastGroup(c *echo.Context, id string) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := h.Backend.UpdateMulticastGroup(
h.AccountID, h.DefaultRegion, id, req.Name, req.Description, req.LoRaWAN,
@@ -214,11 +216,11 @@ func (h *Handler) startMulticastGroupSession(c *echo.Context, id string) error {
// would, not answered with a fabricated success.
func (h *Handler) sendDataToMulticastGroup(c *echo.Context, _ string) error {
var req struct {
- PayloadData string `json:"PayloadData"`
- WirelessMetadata json.RawMessage `json:"WirelessMetadata"`
+ PayloadData string `json:"PayloadData"`
+ WirelessMetadata jsontext.Value `json:"WirelessMetadata"`
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if req.PayloadData == "" {
return writeError(c, http.StatusBadRequest, "ValidationException: PayloadData is required")
@@ -236,7 +238,7 @@ func (h *Handler) associateWirelessDeviceWithMulticastGroup(
c *echo.Context, multicastGroupID string, body []byte,
) error {
var req associateWirelessDeviceWithMulticastRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "invalid request body")
}
diff --git a/services/iotwireless/handler_multicast_groups_test.go b/services/iotwireless/handler_multicast_groups_test.go
index 49e0b43419..6914de8b6a 100644
--- a/services/iotwireless/handler_multicast_groups_test.go
+++ b/services/iotwireless/handler_multicast_groups_test.go
@@ -1,13 +1,16 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_AssociateWirelessDeviceWithMulticastGroup(t *testing.T) {
@@ -62,7 +65,7 @@ func TestHandler_MulticastGroup_LoRaWANRoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id, _ := createResp["Id"].(string)
require.NotEmpty(t, id)
@@ -70,7 +73,7 @@ func TestHandler_MulticastGroup_LoRaWANRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
loRaWAN, ok := getResp["LoRaWAN"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "US915", loRaWAN["RfRegion"])
@@ -92,7 +95,7 @@ func TestHandler_MulticastGroup_LoRaWANRoundTrip(t *testing.T) {
rec = doIoTWRequest(t, h, http.MethodGet, "/multicast-groups/"+id, "")
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
loRaWAN, ok = getResp["LoRaWAN"].(map[string]any)
require.True(t, ok)
assert.InDelta(t, float64(1), loRaWAN["NumberOfDevicesInGroup"], 0.001)
@@ -104,7 +107,7 @@ func TestHandler_MulticastGroup_LoRaWANRoundTrip(t *testing.T) {
rec = doIoTWRequest(t, h, http.MethodGet, "/multicast-groups/"+id, "")
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
loRaWAN, ok = getResp["LoRaWAN"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "EU868", loRaWAN["RfRegion"])
@@ -132,7 +135,7 @@ func TestHandler_CancelMulticastGroupSession(t *testing.T) {
if tt.create {
rec := doIoTWRequest(t, h, http.MethodPost, "/multicast-groups", `{"Name":"mg"}`)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id, _ = resp["Id"].(string)
}
@@ -153,7 +156,7 @@ func TestHandler_MulticastGroupCRUD(t *testing.T) {
assert.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id, ok := createResp["Id"].(string)
require.True(t, ok)
assert.NotEmpty(t, id)
@@ -166,7 +169,7 @@ func TestHandler_MulticastGroupCRUD(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "mg1", getResp["Name"])
assert.Equal(t, id, getResp["Id"])
@@ -175,7 +178,7 @@ func TestHandler_MulticastGroupCRUD(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
groups, ok := listResp["MulticastGroupList"].([]any)
require.True(t, ok)
assert.Len(t, groups, 1)
@@ -186,7 +189,7 @@ func TestHandler_MulticastGroupCRUD(t *testing.T) {
// Verify update
rec = doIoTWRequest(t, h, http.MethodGet, "/multicast-groups/"+id, "")
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "mg1-updated", getResp["Name"])
// Delete
@@ -208,7 +211,7 @@ func TestHandler_MulticastGroupSession(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
// Getting the session before one has been started must 404, matching real
@@ -225,7 +228,7 @@ func TestHandler_MulticastGroupSession(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var sessionResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &sessionResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &sessionResp, jsonx.V1()))
_, hasLoRaWAN := sessionResp["LoRaWAN"]
assert.True(t, hasLoRaWAN)
}
@@ -240,7 +243,7 @@ func TestHandler_DisassociateWirelessDeviceFromMulticastGroup(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var mgResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mgResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mgResp, jsonx.V1()))
mgID := mgResp["Id"].(string)
// Create wireless device
@@ -254,7 +257,7 @@ func TestHandler_DisassociateWirelessDeviceFromMulticastGroup(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var devResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &devResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &devResp, jsonx.V1()))
devID := devResp["Id"].(string)
// Associate device with multicast group; AssociateWirelessDeviceWithMulticastGroup
@@ -284,7 +287,7 @@ func TestHandler_SendDataToMulticastGroup_UniqueMessageID(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
mgID := createResp["Id"].(string)
seen := make(map[string]bool)
@@ -296,7 +299,7 @@ func TestHandler_SendDataToMulticastGroup_UniqueMessageID(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
msgID, _ := resp["MessageId"].(string)
assert.NotEmpty(t, msgID)
@@ -334,7 +337,7 @@ func TestHandler_SendDataToMulticastGroup_RequiresBody(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
mgID, _ := createResp["Id"].(string)
require.NotEmpty(t, mgID)
@@ -375,7 +378,7 @@ func TestHandler_MulticastGroup_BulkOps(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
mgID := createResp["Id"].(string)
path := "/multicast-groups/" + mgID + "/bulk"
@@ -397,7 +400,7 @@ func TestHandler_MulticastGroups_FullLifecycle(t *testing.T) {
assert.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
mgID, _ := createResp["Id"].(string)
require.NotEmpty(t, mgID)
diff --git a/services/iotwireless/handler_network_analyzer.go b/services/iotwireless/handler_network_analyzer.go
index 7dab9b41f5..ab7e043e87 100644
--- a/services/iotwireless/handler_network_analyzer.go
+++ b/services/iotwireless/handler_network_analyzer.go
@@ -1,11 +1,12 @@
package iotwireless
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -46,7 +47,7 @@ func (h *Handler) createNetworkAnalyzerConfiguration(c *echo.Context) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
nc, err := h.Backend.CreateNetworkAnalyzerConfig(
h.AccountID, h.DefaultRegion,
@@ -125,7 +126,7 @@ func (h *Handler) updateNetworkAnalyzerConfiguration(c *echo.Context, name strin
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := h.Backend.UpdateNetworkAnalyzerConfig(
h.AccountID, h.DefaultRegion, name,
diff --git a/services/iotwireless/handler_network_analyzer_test.go b/services/iotwireless/handler_network_analyzer_test.go
index 689e29244a..47d8fe5c65 100644
--- a/services/iotwireless/handler_network_analyzer_test.go
+++ b/services/iotwireless/handler_network_analyzer_test.go
@@ -1,12 +1,14 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_NetworkAnalyzerCRUD(t *testing.T) {
@@ -20,7 +22,7 @@ func TestHandler_NetworkAnalyzerCRUD(t *testing.T) {
assert.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
name, _ := createResp["Name"].(string)
assert.Equal(t, "na1", name)
@@ -29,7 +31,7 @@ func TestHandler_NetworkAnalyzerCRUD(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "na1", getResp["Name"])
assert.Equal(t, "test config", getResp["Description"])
@@ -38,7 +40,7 @@ func TestHandler_NetworkAnalyzerCRUD(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
configs, ok := listResp["NetworkAnalyzerConfigurationList"].([]any)
require.True(t, ok)
assert.Len(t, configs, 1)
@@ -50,7 +52,7 @@ func TestHandler_NetworkAnalyzerCRUD(t *testing.T) {
// Verify update
rec = doIoTWRequest(t, h, http.MethodGet, "/network-analyzer-configurations/na1", "")
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "updated", getResp["Description"])
// Delete
diff --git a/services/iotwireless/handler_partner_accounts.go b/services/iotwireless/handler_partner_accounts.go
index 57371546c7..2ac6d00b98 100644
--- a/services/iotwireless/handler_partner_accounts.go
+++ b/services/iotwireless/handler_partner_accounts.go
@@ -2,12 +2,13 @@ package iotwireless
import (
"cmp"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -47,7 +48,7 @@ type listPartnerAccountsResponse struct {
func (h *Handler) associateAwsAccountWithPartnerAccount(c *echo.Context, body []byte) error {
var req associatePartnerAccountRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "invalid request body")
}
diff --git a/services/iotwireless/handler_partner_accounts_test.go b/services/iotwireless/handler_partner_accounts_test.go
index c1e21d25cb..e372eaa804 100644
--- a/services/iotwireless/handler_partner_accounts_test.go
+++ b/services/iotwireless/handler_partner_accounts_test.go
@@ -1,12 +1,14 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_AssociateAwsAccountWithPartnerAccount(t *testing.T) {
@@ -42,7 +44,7 @@ func TestHandler_AssociateAwsAccountWithPartnerAccount(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Arn"])
sidewalk, ok := resp["Sidewalk"].(map[string]any)
require.True(t, ok)
@@ -63,7 +65,7 @@ func TestHandler_PartnerAccounts(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var assocResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &assocResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &assocResp, jsonx.V1()))
assert.NotEmpty(t, assocResp["Arn"])
// Get partner account
@@ -71,7 +73,7 @@ func TestHandler_PartnerAccounts(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, true, getResp["AccountLinked"])
sidewalk, ok := getResp["Sidewalk"].(map[string]any)
require.True(t, ok, "AccountLinked accounts must include Sidewalk info")
@@ -84,7 +86,7 @@ func TestHandler_PartnerAccounts(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var unlinkedResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &unlinkedResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &unlinkedResp, jsonx.V1()))
assert.Equal(t, false, unlinkedResp["AccountLinked"])
// List partner accounts
@@ -92,7 +94,7 @@ func TestHandler_PartnerAccounts(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
sidewalkList, ok := listResp["Sidewalk"].([]any)
require.True(t, ok)
require.Len(
diff --git a/services/iotwireless/handler_positioning.go b/services/iotwireless/handler_positioning.go
index fddad4332e..857837ada2 100644
--- a/services/iotwireless/handler_positioning.go
+++ b/services/iotwireless/handler_positioning.go
@@ -3,13 +3,15 @@ package iotwireless
import (
"encoding/base64"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net"
"net/http"
"net/netip"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// accuracyResponse mirrors types.Accuracy: a struct of two float fields, not
@@ -99,7 +101,7 @@ func (h *Handler) updatePosition(c *echo.Context, id string) error {
var req map[string]any
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
_ = h.Backend.UpdatePosition(id, req)
return stubNoContent(c)
@@ -126,7 +128,7 @@ func (h *Handler) putPositionConfiguration(c *echo.Context, id string) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := h.Backend.PutPositionConfiguration(id, resourceType, req.Destination, req.Solvers); err != nil {
return handleError(c, err)
@@ -172,7 +174,7 @@ type getPositionEstimateRequest struct {
func (h *Handler) getPositionEstimate(c *echo.Context) error {
var req getPositionEstimateRequest
- _ = json.Unmarshal(readStubBody(c), &req)
+ _ = json.Unmarshal(readStubBody(c), &req, jsonx.V1())
if err := validatePositionEstimate(&req); err != nil {
return handleError(c, err)
diff --git a/services/iotwireless/handler_positioning_test.go b/services/iotwireless/handler_positioning_test.go
index 1e33ae91cb..5334b18ca5 100644
--- a/services/iotwireless/handler_positioning_test.go
+++ b/services/iotwireless/handler_positioning_test.go
@@ -1,12 +1,14 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_Position(t *testing.T) {
@@ -19,7 +21,7 @@ func TestHandler_Position(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
pos, ok := getResp["Position"].([]any)
require.True(t, ok)
assert.Empty(t, pos)
@@ -39,7 +41,7 @@ func TestHandler_Position(t *testing.T) {
// coordinates, not a fabricated empty response.
rec = doIoTWRequest(t, h, http.MethodGet, "/positions/resource-123", "")
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
pos, ok = getResp["Position"].([]any)
require.True(t, ok)
assert.Equal(t, []any{47.6, -122.3, 100.0}, pos)
@@ -57,7 +59,7 @@ func TestHandler_Position(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getCfgResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getCfgResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getCfgResp, jsonx.V1()))
assert.NotContains(t, getCfgResp, "Destination")
// Put position configuration with a real ResourceType and Destination.
@@ -69,7 +71,7 @@ func TestHandler_Position(t *testing.T) {
// Get position configuration must now reflect the stored destination.
rec = doIoTWRequest(t, h, http.MethodGet, "/position-configurations/resource-123", "")
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getCfgResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getCfgResp, jsonx.V1()))
assert.Equal(t, "my-position-dest", getCfgResp["Destination"])
solvers, ok := getCfgResp["Solvers"].(map[string]any)
require.True(t, ok)
@@ -91,7 +93,7 @@ func TestHandler_Position(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
configs, ok := listResp["PositionConfigurationList"].([]any)
require.True(t, ok)
require.Len(t, configs, 1)
@@ -106,7 +108,7 @@ func TestHandler_Position(t *testing.T) {
"",
)
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
configs, ok = listResp["PositionConfigurationList"].([]any)
require.True(t, ok)
assert.Empty(t, configs)
@@ -228,7 +230,7 @@ func TestHandler_PositionConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp[tt.wantKey]
assert.True(t, ok, "expected key %q in position config response", tt.wantKey)
@@ -237,7 +239,7 @@ func TestHandler_PositionConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
cfgs, ok := listResp["PositionConfigurationList"].([]any)
require.True(t, ok)
require.Len(t, cfgs, 1)
diff --git a/services/iotwireless/handler_profiles.go b/services/iotwireless/handler_profiles.go
index b8248e35a3..0438b521a3 100644
--- a/services/iotwireless/handler_profiles.go
+++ b/services/iotwireless/handler_profiles.go
@@ -7,11 +7,12 @@ package iotwireless
// grouped in one cohesive file instead.
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -54,7 +55,7 @@ type listDeviceProfilesResponse struct {
func (h *Handler) createDeviceProfile(c *echo.Context, body []byte) error {
var req createDeviceProfileRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "invalid request body")
}
@@ -147,7 +148,7 @@ type listServiceProfilesResponse struct {
func (h *Handler) createServiceProfile(c *echo.Context, body []byte) error {
var req createServiceProfileRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "invalid request body")
}
diff --git a/services/iotwireless/handler_profiles_test.go b/services/iotwireless/handler_profiles_test.go
index c1889a8749..39451d1063 100644
--- a/services/iotwireless/handler_profiles_test.go
+++ b/services/iotwireless/handler_profiles_test.go
@@ -1,12 +1,14 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateDeviceProfile(t *testing.T) {
@@ -39,7 +41,7 @@ func TestHandler_CreateDeviceProfile(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Id"])
assert.NotEmpty(t, resp["Arn"])
})
@@ -71,7 +73,7 @@ func TestHandler_DeviceProfile_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
require.NotEmpty(t, id)
@@ -80,7 +82,7 @@ func TestHandler_DeviceProfile_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, tt.profileName, getResp["Name"])
assert.Equal(t, id, getResp["Id"])
@@ -89,7 +91,7 @@ func TestHandler_DeviceProfile_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
list, ok := listResp["DeviceProfileList"].([]any)
require.True(t, ok)
assert.Len(t, list, 1)
@@ -121,7 +123,7 @@ func TestHandler_ServiceProfile_LoRaWANGetShape(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id, _ := createResp["Id"].(string)
require.NotEmpty(t, id)
@@ -129,7 +131,7 @@ func TestHandler_ServiceProfile_LoRaWANGetShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
loRaWAN, ok := getResp["LoRaWAN"].(map[string]any)
require.True(t, ok, "LoRaWAN must be present on Get")
assert.InDelta(t, float64(15), loRaWAN["DrMax"], 0.001)
@@ -153,7 +155,7 @@ func TestHandler_DeviceProfile_SidewalkCreateShape(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id, _ := createResp["Id"].(string)
require.NotEmpty(t, id)
@@ -161,7 +163,7 @@ func TestHandler_DeviceProfile_SidewalkCreateShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
sidewalk, ok := getResp["Sidewalk"].(map[string]any)
require.True(t, ok, "an empty Sidewalk request object must still mark the profile as Sidewalk on Get")
assert.Empty(t, sidewalk, "AWS-assigned Sidewalk fields must not be fabricated")
@@ -205,7 +207,7 @@ func TestHandler_ListDeviceProfiles_FilterByType(t *testing.T) {
Name string `json:"Name"`
} `json:"DeviceProfileList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
names := make([]string, 0, len(resp.DeviceProfileList))
for _, dp := range resp.DeviceProfileList {
@@ -253,7 +255,7 @@ func TestHandler_CreateGetListDeleteServiceProfile(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id, ok := createResp["Id"].(string)
require.True(t, ok)
assert.NotEmpty(t, id)
@@ -263,7 +265,7 @@ func TestHandler_CreateGetListDeleteServiceProfile(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, tt.profileName, getResp["Name"])
// List
@@ -271,7 +273,7 @@ func TestHandler_CreateGetListDeleteServiceProfile(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
profiles, ok := listResp["ServiceProfileList"].([]any)
require.True(t, ok)
assert.Len(t, profiles, 1)
diff --git a/services/iotwireless/handler_tags.go b/services/iotwireless/handler_tags.go
index 901f81a2c7..66abb03e09 100644
--- a/services/iotwireless/handler_tags.go
+++ b/services/iotwireless/handler_tags.go
@@ -1,12 +1,13 @@
package iotwireless
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -46,7 +47,7 @@ func (h *Handler) tagResource(c *echo.Context, resourceArn string, body []byte)
}
var req tagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "invalid request body")
}
diff --git a/services/iotwireless/handler_tags_test.go b/services/iotwireless/handler_tags_test.go
index d86c12658d..1f3bb61064 100644
--- a/services/iotwireless/handler_tags_test.go
+++ b/services/iotwireless/handler_tags_test.go
@@ -1,7 +1,7 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -11,6 +11,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_ListTagsTagUntagResource(t *testing.T) {
@@ -51,7 +53,7 @@ func TestHandler_ListTagsTagUntagResource(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
arn := createResp["Arn"].(string)
require.NotEmpty(t, arn)
@@ -85,7 +87,7 @@ func TestHandler_ListTagsTagUntagResource(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1()))
tagList, ok := tagsResp["Tags"].([]any)
require.True(t, ok)
diff --git a/services/iotwireless/handler_test.go b/services/iotwireless/handler_test.go
index 2d8b687e8d..d95a6cdfa6 100644
--- a/services/iotwireless/handler_test.go
+++ b/services/iotwireless/handler_test.go
@@ -1,7 +1,7 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotwireless"
)
@@ -325,7 +326,7 @@ func TestHandler_Reset(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp["WirelessDeviceList"].([]any)
require.True(t, ok)
@@ -411,7 +412,7 @@ func TestHandler_ListEmpty_NonNil(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp[tt.listKey].([]any)
require.True(t, ok, "key %q should be a JSON array, got %T", tt.listKey, resp[tt.listKey])
@@ -482,7 +483,7 @@ func Test_RouteMatcher_TagOperations(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
resourceArn := created["Arn"].(string)
require.NotEmpty(t, resourceArn)
@@ -503,7 +504,7 @@ func Test_RouteMatcher_TagOperations(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listed))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listed, jsonx.V1()))
require.Len(t, listed.Tags, 1)
assert.Equal(t, "env", listed.Tags[0].Key)
assert.Equal(t, "prod", listed.Tags[0].Value)
@@ -531,7 +532,7 @@ func Test_RouteMatcher_AssociateAwsAccountWithPartnerAccount(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Arn"])
sidewalk, ok := resp["Sidewalk"].(map[string]any)
@@ -545,7 +546,7 @@ func Test_RouteMatcher_AssociateAwsAccountWithPartnerAccount(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, true, getResp["AccountLinked"])
}
@@ -594,7 +595,7 @@ func Test_ErrorResponse_SetsAmznErrortypeHeader(t *testing.T) {
assert.Equal(t, tt.wantErrType, rec.Header().Get("X-Amzn-Errortype"))
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, tt.wantErrType, body["__type"])
assert.NotEmpty(t, body["Message"])
})
diff --git a/services/iotwireless/handler_wireless_devices.go b/services/iotwireless/handler_wireless_devices.go
index 99677f6bdd..bf982a4242 100644
--- a/services/iotwireless/handler_wireless_devices.go
+++ b/services/iotwireless/handler_wireless_devices.go
@@ -1,13 +1,14 @@
package iotwireless
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -100,7 +101,7 @@ type testWirelessDeviceResponse struct {
func (h *Handler) createWirelessDevice(c *echo.Context, body []byte) error {
var req createWirelessDeviceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "invalid request body")
}
@@ -194,7 +195,7 @@ func (h *Handler) deleteWirelessDevice(c *echo.Context, id string) error {
func (h *Handler) associateWirelessDeviceWithThing(c *echo.Context, wirelessDeviceID string, body []byte) error {
var req associateWirelessDeviceWithThingRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "invalid request body")
}
@@ -219,7 +220,7 @@ func (h *Handler) updateWirelessDevice(c *echo.Context, id string) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := h.Backend.UpdateWirelessDevice(
h.AccountID, h.DefaultRegion, id,
@@ -283,7 +284,7 @@ func (h *Handler) sendDataToWirelessDevice(c *echo.Context, wirelessDeviceID str
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
messageID := uuid.NewString()
diff --git a/services/iotwireless/handler_wireless_devices_filter_test.go b/services/iotwireless/handler_wireless_devices_filter_test.go
index e0777de97d..69e2611d48 100644
--- a/services/iotwireless/handler_wireless_devices_filter_test.go
+++ b/services/iotwireless/handler_wireless_devices_filter_test.go
@@ -1,13 +1,14 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotwireless"
)
@@ -25,7 +26,7 @@ func listWirelessDevicesNames(t *testing.T, h *iotwireless.Handler, rawQuery str
Name string `json:"Name"`
} `json:"WirelessDeviceList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
names := make([]string, 0, len(resp.WirelessDeviceList))
for _, d := range resp.WirelessDeviceList {
diff --git a/services/iotwireless/handler_wireless_devices_test.go b/services/iotwireless/handler_wireless_devices_test.go
index 9216384928..27a826e1a7 100644
--- a/services/iotwireless/handler_wireless_devices_test.go
+++ b/services/iotwireless/handler_wireless_devices_test.go
@@ -1,13 +1,14 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotwireless"
)
@@ -41,7 +42,7 @@ func TestHandler_CreateGetListDeleteWirelessDevice(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id, ok := createResp["Id"].(string)
require.True(t, ok)
assert.NotEmpty(t, id)
@@ -51,7 +52,7 @@ func TestHandler_CreateGetListDeleteWirelessDevice(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, tt.deviceName, getResp["Name"])
// List
@@ -59,7 +60,7 @@ func TestHandler_CreateGetListDeleteWirelessDevice(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
devices, ok := listResp["WirelessDeviceList"].([]any)
require.True(t, ok)
assert.Len(t, devices, 1)
@@ -133,7 +134,7 @@ func TestHandler_AssociateWirelessDeviceWithThing(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
devID = createResp["Id"].(string)
}
@@ -155,7 +156,7 @@ func TestHandler_UpdateWirelessDevice(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
// Update
@@ -168,7 +169,7 @@ func TestHandler_UpdateWirelessDevice(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "dev1-updated", getResp["Name"])
}
@@ -188,7 +189,7 @@ func TestHandler_DeregisterWirelessDevice(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
// Deregister (should delete the device)
@@ -216,7 +217,7 @@ func TestHandler_DisassociateWirelessDeviceFromThing(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
devID := createResp["Id"].(string)
// Associate with thing
@@ -239,14 +240,14 @@ func TestHandler_GetWirelessDeviceStatistics(t *testing.T) {
require.Equal(t, http.StatusCreated, createResp.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &created, jsonx.V1()))
devID := created["Id"].(string)
rec := doIoTWRequest(t, h, http.MethodGet, "/wireless-devices/"+devID+"/statistics", "")
assert.Equal(t, http.StatusOK, rec.Code)
var stats map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stats))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stats, jsonx.V1()))
assert.Equal(t, devID, stats["WirelessDeviceId"])
}
@@ -269,7 +270,7 @@ func TestHandler_SendDataToWirelessDevice(t *testing.T) {
assert.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["MessageId"])
}
@@ -283,14 +284,14 @@ func TestHandler_TestWirelessDevice(t *testing.T) {
require.Equal(t, http.StatusCreated, createResp.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &created, jsonx.V1()))
devID := created["Id"].(string)
rec := doIoTWRequest(t, h, http.MethodPost, "/wireless-devices/"+devID+"/test", `{}`)
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "PASS", resp["Result"])
}
@@ -319,7 +320,7 @@ func TestHandler_QueuedMessages_SendListDelete(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var devResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &devResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &devResp, jsonx.V1()))
devID := devResp["Id"].(string)
// List queued messages (empty)
@@ -327,7 +328,7 @@ func TestHandler_QueuedMessages_SendListDelete(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
msgs, ok := listResp["DownlinkQueueMessagesList"].([]any)
require.True(t, ok)
assert.Empty(t, msgs)
@@ -339,14 +340,14 @@ func TestHandler_QueuedMessages_SendListDelete(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var sendResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &sendResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &sendResp, jsonx.V1()))
sentMessageID, _ := sendResp["MessageId"].(string)
require.NotEmpty(t, sentMessageID)
// List queued messages must now reflect the sent message.
rec = doIoTWRequest(t, h, http.MethodGet, "/wireless-devices/"+devID+"/data", "")
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
msgs, ok = listResp["DownlinkQueueMessagesList"].([]any)
require.True(t, ok)
require.Len(t, msgs, 1)
@@ -359,7 +360,7 @@ func TestHandler_QueuedMessages_SendListDelete(t *testing.T) {
// After deletion, the queue must be empty again.
rec = doIoTWRequest(t, h, http.MethodGet, "/wireless-devices/"+devID+"/data", "")
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
msgs, ok = listResp["DownlinkQueueMessagesList"].([]any)
require.True(t, ok)
assert.Empty(t, msgs)
@@ -381,7 +382,7 @@ func TestHandler_SendDataToWirelessDevice_UniqueMessageID(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
msgID, _ := resp["MessageId"].(string)
assert.NotEmpty(t, msgID, "MessageId should not be empty")
@@ -399,7 +400,7 @@ func setupWirelessDevice(t *testing.T, h *iotwireless.Handler, name, devType, de
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id, _ := resp["Id"].(string)
require.NotEmpty(t, id)
@@ -418,7 +419,7 @@ func TestHandler_QueuedMessages_MultipleSends(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
devID, ok := createResp["Id"].(string)
require.True(t, ok)
@@ -427,7 +428,7 @@ func TestHandler_QueuedMessages_MultipleSends(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var pre map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pre))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pre, jsonx.V1()))
assert.Empty(t, pre["DownlinkQueueMessagesList"])
// Send two messages (POST /wireless-devices/{id}/data).
@@ -438,7 +439,7 @@ func TestHandler_QueuedMessages_MultipleSends(t *testing.T) {
require.Equal(t, http.StatusCreated, r.Code)
var sr map[string]any
- require.NoError(t, json.Unmarshal(r.Body.Bytes(), &sr))
+ require.NoError(t, json.Unmarshal(r.Body.Bytes(), &sr, jsonx.V1()))
assert.NotEmpty(t, sr["MessageId"], "SendData must return a MessageId")
}
@@ -447,7 +448,7 @@ func TestHandler_QueuedMessages_MultipleSends(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var post map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &post))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &post, jsonx.V1()))
msgs, ok := post["DownlinkQueueMessagesList"].([]any)
require.True(t, ok)
assert.Len(t, msgs, 2, "two enqueued messages must appear in list")
@@ -472,7 +473,7 @@ func Test_GetWirelessDevice_ReflectsThingAssociation(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
id := created["Id"].(string)
// Before association, ThingArn/ThingName must be absent.
@@ -480,7 +481,7 @@ func Test_GetWirelessDevice_ReflectsThingAssociation(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var before map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &before))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &before, jsonx.V1()))
assert.Empty(t, before["ThingArn"])
assert.Empty(t, before["ThingName"])
@@ -493,7 +494,7 @@ func Test_GetWirelessDevice_ReflectsThingAssociation(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var after map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &after))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &after, jsonx.V1()))
assert.Equal(t, thingArn, after["ThingArn"])
assert.Equal(t, "my-thing", after["ThingName"])
@@ -504,7 +505,7 @@ func Test_GetWirelessDevice_ReflectsThingAssociation(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var afterDisassoc map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &afterDisassoc))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &afterDisassoc, jsonx.V1()))
assert.Empty(t, afterDisassoc["ThingArn"])
assert.Empty(t, afterDisassoc["ThingName"])
}
diff --git a/services/iotwireless/handler_wireless_devices_wireshape_test.go b/services/iotwireless/handler_wireless_devices_wireshape_test.go
index b872399a17..6519dd787f 100644
--- a/services/iotwireless/handler_wireless_devices_wireshape_test.go
+++ b/services/iotwireless/handler_wireless_devices_wireshape_test.go
@@ -1,12 +1,14 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_CreateGetWirelessDevice_LoRaWANSidewalkWireShape drives a real
@@ -35,7 +37,7 @@ func TestHandler_CreateGetWirelessDevice_LoRaWANSidewalkWireShape(t *testing.T)
var createResp struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
rec = doIoTWRequest(t, h, http.MethodGet, "/wireless-devices/"+createResp.ID, "")
require.Equal(t, http.StatusOK, rec.Code)
@@ -57,7 +59,7 @@ func TestHandler_CreateGetWirelessDevice_LoRaWANSidewalkWireShape(t *testing.T)
} `json:"AbpV1_0_x"`
} `json:"LoRaWAN"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "dp-1", got.LoRaWAN.DeviceProfileID)
assert.Equal(t, "addr-1", got.LoRaWAN.AbpV1_0X.DevAddr)
@@ -94,7 +96,7 @@ func TestHandler_NetworkAnalyzerConfig_TraceContentWireShape(t *testing.T) {
var got struct {
TraceContent traceContent `json:"TraceContent"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "INFO", got.TraceContent.LogLevel)
assert.Equal(t, "ENABLED", got.TraceContent.MulticastFrameInfo)
assert.Empty(t, got.TraceContent.WirelessDeviceFrameInfo)
@@ -109,7 +111,7 @@ func TestHandler_NetworkAnalyzerConfig_TraceContentWireShape(t *testing.T) {
var got2 struct {
TraceContent traceContent `json:"TraceContent"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got2, jsonx.V1()))
assert.Empty(t, got2.TraceContent.LogLevel, "update replaces TraceContent wholesale")
assert.Equal(t, "ENABLED", got2.TraceContent.WirelessDeviceFrameInfo)
}
@@ -137,7 +139,7 @@ func TestHandler_ListWirelessDevices_EntryShape(t *testing.T) {
var got struct {
WirelessDeviceList []map[string]any `json:"WirelessDeviceList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
require.Len(t, got.WirelessDeviceList, 1)
entry := got.WirelessDeviceList[0]
diff --git a/services/iotwireless/handler_wireless_gateway_tasks.go b/services/iotwireless/handler_wireless_gateway_tasks.go
index 7d5e5e1802..b7674f2c7e 100644
--- a/services/iotwireless/handler_wireless_gateway_tasks.go
+++ b/services/iotwireless/handler_wireless_gateway_tasks.go
@@ -1,11 +1,13 @@
package iotwireless
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createWirelessGatewayTaskResponse struct {
@@ -57,7 +59,7 @@ func (h *Handler) createWirelessGatewayTask(c *echo.Context, gatewayID string) e
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
task, err := h.Backend.CreateWirelessGatewayTask(gatewayID, req.WirelessGatewayTaskDefinitionID)
if err != nil {
@@ -103,7 +105,7 @@ func (h *Handler) createWirelessGatewayTaskDefinition(c *echo.Context) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
def, err := h.Backend.CreateWirelessGatewayTaskDefinition(
h.AccountID,
diff --git a/services/iotwireless/handler_wireless_gateway_tasks_test.go b/services/iotwireless/handler_wireless_gateway_tasks_test.go
index 1238329c32..b3eec2db46 100644
--- a/services/iotwireless/handler_wireless_gateway_tasks_test.go
+++ b/services/iotwireless/handler_wireless_gateway_tasks_test.go
@@ -1,13 +1,15 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_GatewayTaskLifecycle(t *testing.T) {
@@ -20,7 +22,7 @@ func TestHandler_GatewayTaskLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var gwResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gwResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &gwResp, jsonx.V1()))
gwID := gwResp["Id"].(string)
// Create task definition
@@ -29,7 +31,7 @@ func TestHandler_GatewayTaskLifecycle(t *testing.T) {
assert.Equal(t, http.StatusCreated, rec.Code)
var defResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &defResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &defResp, jsonx.V1()))
defID := defResp["Id"].(string)
assert.NotEmpty(t, defID)
@@ -39,7 +41,7 @@ func TestHandler_GatewayTaskLifecycle(t *testing.T) {
assert.Equal(t, http.StatusCreated, rec.Code)
var taskResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &taskResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &taskResp, jsonx.V1()))
assert.Equal(t, "PENDING", taskResp["Status"])
// Get task
@@ -47,7 +49,7 @@ func TestHandler_GatewayTaskLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getTaskResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getTaskResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getTaskResp, jsonx.V1()))
assert.Equal(t, "PENDING", getTaskResp["Status"])
// List task definitions
@@ -68,7 +70,7 @@ func TestHandler_GatewayTaskLifecycle(t *testing.T) {
assert.Equal(t, http.StatusNotFound, rec.Code)
var notFoundResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ¬FoundResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ¬FoundResp, jsonx.V1()))
msg, _ := notFoundResp["Message"].(string)
assert.NotContains(t, msg, "ResourceNotFoundException")
assert.Contains(t, msg, "task")
@@ -130,7 +132,7 @@ func TestHandler_ListWirelessGatewayTaskDefinitions_ReturnsRealData(t *testing.T
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id, _ := createResp["Id"].(string)
require.NotEmpty(t, id)
createdIDs = append(createdIDs, id)
@@ -140,7 +142,7 @@ func TestHandler_ListWirelessGatewayTaskDefinitions_ReturnsRealData(t *testing.T
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
defs, ok := resp["TaskDefinitions"].([]any)
require.True(t, ok, "TaskDefinitions should be array")
@@ -199,7 +201,7 @@ func TestHandler_GatewayTaskDefinitionCRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
arn := createResp["Arn"].(string)
require.NotEmpty(t, id)
@@ -210,7 +212,7 @@ func TestHandler_GatewayTaskDefinitionCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, tt.defName, getResp["Name"])
// Delete.
@@ -222,7 +224,7 @@ func TestHandler_GatewayTaskDefinitionCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
defs := listResp["TaskDefinitions"].([]any)
assert.Empty(t, defs)
})
@@ -252,7 +254,7 @@ func TestGetWirelessGatewayTaskDefinition_NotFound(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
msg, _ := resp["Message"].(string)
assert.NotContains(t, msg, "ResourceNotFoundException")
@@ -276,7 +278,7 @@ func TestGetWirelessGatewayTaskDefinition_Found(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
id, _ := created["Id"].(string)
require.NotEmpty(t, id)
@@ -284,7 +286,7 @@ func TestGetWirelessGatewayTaskDefinition_Found(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "real-taskdef", got["Name"])
assert.Equal(t, true, got["AutoCreateTasks"])
assert.NotEmpty(t, got["Arn"])
diff --git a/services/iotwireless/handler_wireless_gateways.go b/services/iotwireless/handler_wireless_gateways.go
index 3fae05c554..eab16d533c 100644
--- a/services/iotwireless/handler_wireless_gateways.go
+++ b/services/iotwireless/handler_wireless_gateways.go
@@ -1,12 +1,13 @@
package iotwireless
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -51,7 +52,7 @@ type getWirelessGatewayStatisticsResponse struct {
func (h *Handler) createWirelessGateway(c *echo.Context, body []byte) error {
var req createWirelessGatewayRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "invalid request body")
}
@@ -117,7 +118,7 @@ func (h *Handler) deleteWirelessGateway(c *echo.Context, id string) error {
func (h *Handler) associateWirelessGatewayWithThing(c *echo.Context, gatewayID string, body []byte) error {
var req associateWirelessGatewayWithThingRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "invalid request body")
}
@@ -141,7 +142,7 @@ func (h *Handler) updateWirelessGateway(c *echo.Context, id string) error {
}
body := readStubBody(c)
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if err := h.Backend.UpdateWirelessGateway(
h.AccountID, h.DefaultRegion, id, req.Name, req.Description,
diff --git a/services/iotwireless/handler_wireless_gateways_test.go b/services/iotwireless/handler_wireless_gateways_test.go
index 4d7d5e41ea..ddfe6ef589 100644
--- a/services/iotwireless/handler_wireless_gateways_test.go
+++ b/services/iotwireless/handler_wireless_gateways_test.go
@@ -1,12 +1,15 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateGetListDeleteWirelessGateway(t *testing.T) {
@@ -45,7 +48,7 @@ func TestHandler_CreateGetListDeleteWirelessGateway(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id, ok := createResp["Id"].(string)
require.True(t, ok)
assert.NotEmpty(t, id)
@@ -55,7 +58,7 @@ func TestHandler_CreateGetListDeleteWirelessGateway(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, tt.gatewayName, getResp["Name"])
// List
@@ -63,7 +66,7 @@ func TestHandler_CreateGetListDeleteWirelessGateway(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
gateways, ok := listResp["WirelessGatewayList"].([]any)
require.True(t, ok)
assert.Len(t, gateways, 1)
@@ -116,7 +119,7 @@ func TestHandler_AssociateWirelessGatewayWithThing(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
gwID = createResp["Id"].(string)
}
@@ -143,7 +146,7 @@ func TestHandler_UpdateWirelessGateway(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
// Update
@@ -161,7 +164,7 @@ func TestHandler_UpdateWirelessGateway(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "gw1-updated", getResp["Name"])
}
@@ -175,7 +178,7 @@ func TestHandler_DisassociateWirelessGatewayFromThing(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
gwID := createResp["Id"].(string)
// Associate with thing
@@ -198,7 +201,7 @@ func TestHandler_GetWirelessGatewayStatistics(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
gwID := createResp["Id"].(string)
// Get statistics
@@ -206,7 +209,7 @@ func TestHandler_GetWirelessGatewayStatistics(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var statsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &statsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &statsResp, jsonx.V1()))
assert.Equal(t, "Connected", statsResp["ConnectionStatus"])
assert.Equal(t, gwID, statsResp["WirelessGatewayId"])
}
@@ -229,14 +232,14 @@ func TestHandler_GetWirelessGatewayFirmwareInformation(t *testing.T) {
require.Equal(t, http.StatusCreated, createResp.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &created, jsonx.V1()))
gwID := created["Id"].(string)
rec := doIoTWRequest(t, h, http.MethodGet, "/wireless-gateways/"+gwID+"/firmware-information", "")
assert.Equal(t, http.StatusOK, rec.Code)
var info map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &info))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &info, jsonx.V1()))
loRaWAN, ok := info["LoRaWAN"].(map[string]any)
require.True(t, ok)
currentVersion, ok := loRaWAN["CurrentVersion"].(map[string]any)
@@ -264,7 +267,7 @@ func TestHandler_GatewayFirmwareInformation(t *testing.T) {
require.Equal(t, http.StatusCreated, gwRec.Code)
var gwResp map[string]any
- require.NoError(t, json.Unmarshal(gwRec.Body.Bytes(), &gwResp))
+ require.NoError(t, json.Unmarshal(gwRec.Body.Bytes(), &gwResp, jsonx.V1()))
gwID, ok := gwResp["Id"].(string)
require.True(t, ok)
@@ -274,7 +277,7 @@ func TestHandler_GatewayFirmwareInformation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
loRaWAN, ok := resp["LoRaWAN"].(map[string]any)
require.True(t, ok, "response must have LoRaWAN field")
_, ok = loRaWAN["CurrentVersion"]
@@ -292,7 +295,7 @@ func Test_GetWirelessGateway_ReflectsThingAssociation(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
id := created["Id"].(string)
thingArn := "arn:aws:iot:us-east-1:000000000000:thing/gw-thing"
@@ -304,7 +307,7 @@ func Test_GetWirelessGateway_ReflectsThingAssociation(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var after map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &after))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &after, jsonx.V1()))
assert.Equal(t, thingArn, after["ThingArn"])
assert.Equal(t, "gw-thing", after["ThingName"])
}
@@ -321,7 +324,7 @@ func TestHandler_WirelessGatewayAndDestinationUpdates(t *testing.T) {
require.True(t, rec.Code >= 200 && rec.Code < 300)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
gwID, _ := createResp["Id"].(string)
require.NotEmpty(t, gwID, "CreateWirelessGateway must return an Id")
diff --git a/services/iotwireless/idempotency.go b/services/iotwireless/idempotency.go
index 9d6d70a2fc..1f5d063aa0 100644
--- a/services/iotwireless/idempotency.go
+++ b/services/iotwireless/idempotency.go
@@ -3,13 +3,15 @@ package iotwireless
import (
"bytes"
"crypto/sha256"
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"net/url"
"sync"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const maxIdempotencyEntries = 1024
@@ -98,7 +100,7 @@ func (w *captureWriter) WriteHeader(status int) {
// other body member.
func requestTokenAndParams(body []byte) (string, [sha256.Size]byte, bool) {
var members map[string]any
- if err := json.Unmarshal(body, &members); err != nil {
+ if err := json.Unmarshal(body, &members, jsonx.V1()); err != nil {
return "", [sha256.Size]byte{}, false
}
@@ -108,7 +110,7 @@ func requestTokenAndParams(body []byte) (string, [sha256.Size]byte, bool) {
}
delete(members, "ClientRequestToken")
- canonical, err := json.Marshal(members)
+ canonical, err := json.Marshal(members, jsonx.V1())
if err != nil {
return "", [sha256.Size]byte{}, false
}
diff --git a/services/iotwireless/idempotency_persistence_test.go b/services/iotwireless/idempotency_persistence_test.go
index 2b38476260..7c70c7f27e 100644
--- a/services/iotwireless/idempotency_persistence_test.go
+++ b/services/iotwireless/idempotency_persistence_test.go
@@ -1,13 +1,14 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotwireless"
)
@@ -42,7 +43,7 @@ func TestClientRequestTokenSurvivesRestore(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
var first map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &first))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &first, jsonx.V1()))
snap := h.Backend.(*iotwireless.InMemoryBackend).Snapshot(t.Context())
require.NotEmpty(t, snap)
@@ -55,7 +56,7 @@ func TestClientRequestTokenSurvivesRestore(t *testing.T) {
if tt.wantSameID {
var again map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &again))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &again, jsonx.V1()))
assert.Equal(t, first["Id"], again["Id"])
}
})
diff --git a/services/iotwireless/import_task_single_test.go b/services/iotwireless/import_task_single_test.go
index e1e006d21e..7e94f03c0b 100644
--- a/services/iotwireless/import_task_single_test.go
+++ b/services/iotwireless/import_task_single_test.go
@@ -1,13 +1,15 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestSingleImportTaskRoundTrip(t *testing.T) {
@@ -44,7 +46,7 @@ func TestSingleImportTaskRoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var start map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &start))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &start, jsonx.V1()))
id, _ := start["Id"].(string)
arn, _ := start["Arn"].(string)
@@ -53,7 +55,7 @@ func TestSingleImportTaskRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "d1", got["DestinationName"])
assert.Equal(t, tt.wantPositioning, orEmpty(got["Positioning"]))
@@ -71,7 +73,7 @@ func TestSingleImportTaskRoundTrip(t *testing.T) {
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagResp, jsonx.V1()))
if tt.wantTagValue == "" {
assert.Empty(t, tagResp.Tags)
diff --git a/services/iotwireless/multi_region_test.go b/services/iotwireless/multi_region_test.go
index 0976e756e6..9a2bfda197 100644
--- a/services/iotwireless/multi_region_test.go
+++ b/services/iotwireless/multi_region_test.go
@@ -2,7 +2,8 @@ package iotwireless_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/iotwireless"
)
@@ -180,8 +182,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/iotwireless/multicast_bulk_test.go b/services/iotwireless/multicast_bulk_test.go
index 32cb47192a..e690e00082 100644
--- a/services/iotwireless/multicast_bulk_test.go
+++ b/services/iotwireless/multicast_bulk_test.go
@@ -1,12 +1,13 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotwireless"
)
@@ -86,7 +87,7 @@ func TestHandler_StartBulkAssociate_MutatesState(t *testing.T) {
var created struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
rec = doIoTWRequest(t, h, "POST", "/wireless-devices", `{"Name":"d1","Type":"LoRaWAN"}`)
require.Equal(t, 201, rec.Code)
diff --git a/services/iotwireless/pagination_test.go b/services/iotwireless/pagination_test.go
index db1b13b0fa..bbd3eb97af 100644
--- a/services/iotwireless/pagination_test.go
+++ b/services/iotwireless/pagination_test.go
@@ -1,12 +1,14 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_ListWirelessDevices_Pagination locks in real cursor
@@ -47,7 +49,7 @@ func TestHandler_ListWirelessDevices_Pagination(t *testing.T) {
require.Equal(t, 200, rec.Code)
var resp listResp
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.LessOrEqual(t, len(resp.WirelessDeviceList), 2, "page size must respect maxResults")
for _, d := range resp.WirelessDeviceList {
diff --git a/services/iotwireless/persistence.go b/services/iotwireless/persistence.go
index 037e563fec..f79d017b71 100644
--- a/services/iotwireless/persistence.go
+++ b/services/iotwireless/persistence.go
@@ -2,11 +2,13 @@ package iotwireless
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -229,7 +231,7 @@ func newDirtyDTORegistry() (
// snapshot from an incompatible (older or newer) build of this backend as
// though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ResourceTags map[string]map[string]string `json:"resourceTags,omitempty"`
PartnerAccounts map[string]string `json:"partnerAccounts,omitempty"`
FuotaTaskMulticast map[string]map[string]bool `json:"fuotaTaskMulticast,omitempty"`
@@ -296,7 +298,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Idempotency: idemEntries,
}
- data, err := json.Marshal(snap) //nolint:musttag // nested types lack tags
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "iotwireless: failed to marshal snapshot", "error", err)
@@ -309,7 +311,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
// snapshotDirtyTablesLocked builds the ephemeral DTO registry from the 8
// account/region-scoped tables and returns its SnapshotAll() output. Must be
// called with b.mu held for reading.
-func (b *InMemoryBackend) snapshotDirtyTablesLocked() (map[string]json.RawMessage, error) {
+func (b *InMemoryBackend) snapshotDirtyTablesLocked() (map[string]jsontext.Value, error) {
dtoReg, deviceDTOs, gatewayDTOs, serviceProfileDTOs, destinationDTOs,
deviceProfileDTOs, fuotaTaskDTOs, multicastGroupDTOs, networkAnalyzerConfigDTOs := newDirtyDTORegistry()
@@ -473,7 +475,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// ephemeral DTO registry, converting each DTO back to its live type by
// re-populating the AccountID/Region fields the live type excludes from
// JSON. The caller MUST hold b.mu for writing.
-func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]jsontext.Value) error {
dtoReg, deviceDTOs, gatewayDTOs, serviceProfileDTOs, destinationDTOs,
deviceProfileDTOs, fuotaTaskDTOs, multicastGroupDTOs, networkAnalyzerConfigDTOs := newDirtyDTORegistry()
diff --git a/services/iotwireless/wireless_gateway_tasks_test.go b/services/iotwireless/wireless_gateway_tasks_test.go
index 1025fc4bff..7aff86bd25 100644
--- a/services/iotwireless/wireless_gateway_tasks_test.go
+++ b/services/iotwireless/wireless_gateway_tasks_test.go
@@ -1,13 +1,14 @@
package iotwireless_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iotwireless"
)
@@ -115,7 +116,7 @@ func TestHandler_ListWirelessGatewayTaskDefinitions_EntryShape(t *testing.T) {
require.Equal(t, 200, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
entries, ok := resp["TaskDefinitions"].([]any)
require.True(t, ok)
diff --git a/services/kafka/handler.go b/services/kafka/handler.go
index 9207105890..f57fbfc811 100644
--- a/services/kafka/handler.go
+++ b/services/kafka/handler.go
@@ -3,7 +3,7 @@ package kafka
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -653,7 +654,7 @@ func decodeJSONBody(body []byte, v any) error {
return nil
}
- return json.Unmarshal(body, v)
+ return json.Unmarshal(body, v, jsonx.V1())
}
// ----------------------------------------
@@ -666,7 +667,7 @@ const defaultPageSize = 100
func encodeKafkaPageToken(offset int) string {
data, _ := json.Marshal(struct {
O int `json:"o"`
- }{O: offset})
+ }{O: offset}, jsonx.V1())
return base64.RawURLEncoding.EncodeToString(data)
}
@@ -689,7 +690,7 @@ func decodeKafkaPageToken(token string) int {
O int `json:"o"`
}
- err = json.Unmarshal(data, &t)
+ err = json.Unmarshal(data, &t, jsonx.V1())
if err != nil || t.O < 0 {
return 0
}
diff --git a/services/kafka/handler_channels.go b/services/kafka/handler_channels.go
index 114bcda87d..891f34bbfb 100644
--- a/services/kafka/handler_channels.go
+++ b/services/kafka/handler_channels.go
@@ -2,11 +2,13 @@ package kafka
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// splitChannelResource splits the handler's internal "clusterArn|channelArn"
@@ -53,7 +55,7 @@ func (h *Handler) handleCreateChannel(
body []byte,
) error {
var in createChannelInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "BadRequestException", "invalid request body: "+err.Error())
}
@@ -219,7 +221,7 @@ func (h *Handler) handleUpdateChannel(
}
var in updateChannelInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "BadRequestException", "invalid request body: "+err.Error())
}
diff --git a/services/kafka/handler_channels_test.go b/services/kafka/handler_channels_test.go
index d9d2e62426..767831b74b 100644
--- a/services/kafka/handler_channels_test.go
+++ b/services/kafka/handler_channels_test.go
@@ -2,7 +2,7 @@ package kafka_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kafka"
)
@@ -278,7 +279,7 @@ func TestKafka_ListChannelsPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
channels1, _ := resp1["channels"].([]any)
assert.Len(t, channels1, 3)
@@ -292,7 +293,7 @@ func TestKafka_ListChannelsPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
channels2, _ := resp2["channels"].([]any)
assert.Len(t, channels2, 2)
assert.Empty(t, resp2["nextToken"])
diff --git a/services/kafka/handler_cluster_operations_test.go b/services/kafka/handler_cluster_operations_test.go
index c84eba36fb..b72cfc4ec9 100644
--- a/services/kafka/handler_cluster_operations_test.go
+++ b/services/kafka/handler_cluster_operations_test.go
@@ -2,7 +2,7 @@ package kafka_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kafka"
)
@@ -44,7 +45,7 @@ func TestKafka_DescribeClusterOperation(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
clusterArn := createResp["clusterArn"].(string)
op := backend.AddClusterOperationInternal(clusterArn, "UPDATE_BROKER_COUNT")
@@ -63,7 +64,7 @@ func TestKafka_DescribeClusterOperation(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
opInfo, ok := resp["clusterOperationInfo"].(map[string]any)
require.True(t, ok)
@@ -145,7 +146,7 @@ func TestClusterOperationTracking_V1(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
opList, ok := listResp["clusterOperationInfoList"].([]any)
require.True(t, ok)
assert.GreaterOrEqual(t, len(opList), 2, "should have at least 2 operations")
@@ -167,7 +168,7 @@ func TestClusterOperationTracking_V1(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
opInfo, infoOK := descResp["clusterOperationInfo"].(map[string]any)
require.True(t, infoOK)
assert.Equal(t, opArn, opInfo["operationArn"])
@@ -195,7 +196,7 @@ func TestClusterOperationTracking_V2(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
opList, ok := listResp["clusterOperationInfoList"].([]any)
require.True(t, ok)
require.NotEmpty(t, opList)
@@ -205,7 +206,7 @@ func TestClusterOperationTracking_V2(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
opInfo, ok := descResp["clusterOperationInfo"].(map[string]any)
require.True(t, ok)
assert.Equal(t, opArn, opInfo["operationArn"])
@@ -242,7 +243,7 @@ func TestListClusterOperationsV2_OmitsDescribeOnlyFields(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
opList, ok := listResp["clusterOperationInfoList"].([]any)
require.True(t, ok)
require.NotEmpty(t, opList)
diff --git a/services/kafka/handler_cluster_policy.go b/services/kafka/handler_cluster_policy.go
index 8c22076d55..253d60166c 100644
--- a/services/kafka/handler_cluster_policy.go
+++ b/services/kafka/handler_cluster_policy.go
@@ -2,10 +2,12 @@ package kafka
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDeleteClusterPolicy(
@@ -54,7 +56,7 @@ func (h *Handler) handlePutClusterPolicy(
body []byte,
) error {
var in putClusterPolicyInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/kafka/handler_cluster_policy_test.go b/services/kafka/handler_cluster_policy_test.go
index c78902031f..58ddcf8898 100644
--- a/services/kafka/handler_cluster_policy_test.go
+++ b/services/kafka/handler_cluster_policy_test.go
@@ -2,7 +2,7 @@ package kafka_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kafka"
)
@@ -45,7 +46,7 @@ func TestKafka_DeleteClusterPolicy(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
var clusterArn string
if tt.useRealArn {
diff --git a/services/kafka/handler_cluster_updates.go b/services/kafka/handler_cluster_updates.go
index 14b0951e7a..15e429de47 100644
--- a/services/kafka/handler_cluster_updates.go
+++ b/services/kafka/handler_cluster_updates.go
@@ -2,10 +2,12 @@ package kafka
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type rebootBrokerInput struct {
@@ -23,7 +25,7 @@ func (h *Handler) handleRebootBroker(
body []byte,
) error {
var in rebootBrokerInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -116,7 +118,7 @@ func (h *Handler) handleUpdateBrokerCount(
body []byte,
) error {
var in updateBrokerCountInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -147,7 +149,7 @@ func (h *Handler) handleUpdateBrokerStorage(
body []byte,
) error {
var in updateBrokerStorageInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -183,7 +185,7 @@ func (h *Handler) handleUpdateBrokerType(
body []byte,
) error {
var in updateBrokerTypeInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -214,7 +216,7 @@ func (h *Handler) handleUpdateClusterConfiguration(
body []byte,
) error {
var in updateClusterConfigurationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -249,7 +251,7 @@ func (h *Handler) handleUpdateClusterKafkaVersion(
body []byte,
) error {
var in updateClusterKafkaVersionInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -361,7 +363,7 @@ func (h *Handler) handleUpdateRebalancing(
body []byte,
) error {
var in updateRebalancingInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/kafka/handler_cluster_updates_test.go b/services/kafka/handler_cluster_updates_test.go
index 95bf2799d1..c1d0ea8b83 100644
--- a/services/kafka/handler_cluster_updates_test.go
+++ b/services/kafka/handler_cluster_updates_test.go
@@ -2,7 +2,7 @@ package kafka_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kafka"
)
@@ -164,7 +165,7 @@ func TestUpdateOpsRequireCurrentVersion(t *testing.T) {
if tt.wantCode == http.StatusBadRequest {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "BadRequestException", errResp["__type"],
"wrong version must produce BadRequestException")
}
@@ -285,7 +286,7 @@ func TestUpdateBrokerCount_Persists(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
clusterInfo, ok := descResp["clusterInfo"].(map[string]any)
require.True(t, ok)
provisioned, ok := clusterInfo["provisioned"].(map[string]any)
@@ -323,7 +324,7 @@ func TestUpdateBrokerStorage_Persists(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
clusterInfo, _ := descResp["clusterInfo"].(map[string]any)
provisioned, _ := clusterInfo["provisioned"].(map[string]any)
bng, _ := provisioned["brokerNodeGroupInfo"].(map[string]any)
@@ -357,7 +358,7 @@ func TestUpdateBrokerType_Persists(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
clusterInfo, _ := descResp["clusterInfo"].(map[string]any)
provisioned, _ := clusterInfo["provisioned"].(map[string]any)
bng, _ := provisioned["brokerNodeGroupInfo"].(map[string]any)
@@ -389,7 +390,7 @@ func TestUpdateClusterKafkaVersion_Persists(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
clusterInfo, _ := descResp["clusterInfo"].(map[string]any)
provisioned, _ := clusterInfo["provisioned"].(map[string]any)
assert.Equal(
@@ -608,7 +609,7 @@ func TestUpdateClusterConfiguration_V2Path(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
clusterInfo, _ := descResp["clusterInfo"].(map[string]any)
provisioned, _ := clusterInfo["provisioned"].(map[string]any)
assert.NotContains(t, provisioned, "configurationInfo",
diff --git a/services/kafka/handler_clusters.go b/services/kafka/handler_clusters.go
index 80a9844d5a..663ed3da9e 100644
--- a/services/kafka/handler_clusters.go
+++ b/services/kafka/handler_clusters.go
@@ -2,13 +2,15 @@ package kafka
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// createClusterInput is CreateClusterInput (api_op_CreateCluster.go,
@@ -233,7 +235,7 @@ type createClusterV2Output struct {
func (h *Handler) handleCreateCluster(ctx context.Context, c *echo.Context, body []byte) error {
var in createClusterInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -264,7 +266,7 @@ func (h *Handler) handleCreateCluster(ctx context.Context, c *echo.Context, body
func (h *Handler) handleCreateClusterV2(ctx context.Context, c *echo.Context, body []byte) error {
var in createClusterV2Input
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/kafka/handler_clusters_fields_test.go b/services/kafka/handler_clusters_fields_test.go
index 4ff7c37be6..9e51d41ce5 100644
--- a/services/kafka/handler_clusters_fields_test.go
+++ b/services/kafka/handler_clusters_fields_test.go
@@ -2,7 +2,7 @@ package kafka_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kafka"
)
@@ -240,7 +241,7 @@ func TestServerlessCluster_HTTP(t *testing.T) {
if tt.wantType != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["clusterType"])
}
})
@@ -266,7 +267,7 @@ func TestDescribeClusterV2_ServerlessArm(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clInfo, ok := resp["clusterInfo"].(map[string]any)
require.True(t, ok)
@@ -287,7 +288,7 @@ func TestDescribeClusterV2_ProvisionedArm(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clInfo, ok := resp["clusterInfo"].(map[string]any)
require.True(t, ok)
@@ -392,7 +393,7 @@ func TestEncryptionInfo_InV1Response(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clInfo := resp["clusterInfo"].(map[string]any)
encInfo, ok := clInfo["encryptionInfo"].(map[string]any)
@@ -428,7 +429,7 @@ func TestEncryptionInfo_InV2Response(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clInfo := resp["clusterInfo"].(map[string]any)
provisioned, ok := clInfo["provisioned"].(map[string]any)
diff --git a/services/kafka/handler_clusters_shapes_test.go b/services/kafka/handler_clusters_shapes_test.go
index e12fc8cdd8..1903a276fc 100644
--- a/services/kafka/handler_clusters_shapes_test.go
+++ b/services/kafka/handler_clusters_shapes_test.go
@@ -2,7 +2,7 @@ package kafka_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kafka"
)
@@ -184,7 +185,7 @@ func TestListClustersV2_ClusterType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusterList := resp["clusterInfoList"].([]any)
require.Len(t, clusterList, 2)
@@ -271,7 +272,7 @@ func TestListClustersV2_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusterList, _ := resp["clusterInfoList"].([]any)
@@ -301,7 +302,7 @@ func TestListClusters_NameFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusterList, _ := resp["clusterInfoList"].([]any)
@@ -328,7 +329,7 @@ func TestListClustersV2_ServerlessHasNoProvisionedArm(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusterList := resp["clusterInfoList"].([]any)
require.Len(t, clusterList, 1)
@@ -411,7 +412,7 @@ func TestOpenMonitoring_InV2Response(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clInfo := resp["clusterInfo"].(map[string]any)
provisioned := clInfo["provisioned"].(map[string]any)
@@ -593,7 +594,7 @@ func TestCreateCluster_V1_HTTP_AuthModes(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["clusterArn"])
})
}
@@ -695,7 +696,7 @@ func TestGetBootstrapBrokers_Variants(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["bootstrapBrokerString"], "default ClientBroker is TLS: no plaintext listener")
assert.NotEmpty(t, resp["bootstrapBrokerStringTls"], "TLS broker string present for the default mode")
@@ -754,7 +755,7 @@ func TestGetBootstrapBrokers_ScramPublic(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["bootstrapBrokerStringSaslScram"])
assert.NotEmpty(t, resp["bootstrapBrokerStringPublicTls"])
@@ -773,7 +774,7 @@ func TestZookeeperConnectString(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clInfo := resp["clusterInfo"].(map[string]any)
zkStr, ok := clInfo["zookeeperConnectString"].(string)
@@ -810,7 +811,7 @@ func TestDescribeCluster_V1_IncludesNewFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clInfo := resp["clusterInfo"].(map[string]any)
assert.Equal(t, kafka.EnhancedMonitoringPerBroker, clInfo["enhancedMonitoring"])
diff --git a/services/kafka/handler_clusters_test.go b/services/kafka/handler_clusters_test.go
index 25d03dbb46..db05354409 100644
--- a/services/kafka/handler_clusters_test.go
+++ b/services/kafka/handler_clusters_test.go
@@ -3,7 +3,7 @@ package kafka_test
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kafka"
)
@@ -60,7 +61,7 @@ func TestKafka_CreateCluster(t *testing.T) {
var bodyBytes []byte
if tt.body != nil {
var err error
- bodyBytes, err = json.Marshal(tt.body)
+ bodyBytes, err = json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("not-json")
@@ -78,7 +79,7 @@ func TestKafka_CreateCluster(t *testing.T) {
if tt.wantFields != nil {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for k, v := range tt.wantFields {
assert.Equal(t, v, resp[k])
@@ -149,7 +150,7 @@ func TestKafka_ListClusters(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp["clusterInfoList"].([]any)
require.True(t, ok)
@@ -211,7 +212,7 @@ func TestKafka_DescribeAndDeleteCluster(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
var clusterArn string
if tt.useRealArn {
@@ -276,7 +277,7 @@ func TestKafka_GetBootstrapBrokers(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
var clusterArn string
if tt.useRealArn {
@@ -293,7 +294,7 @@ func TestKafka_GetBootstrapBrokers(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["bootstrapBrokerString"], "default ClientBroker is TLS: no plaintext listener")
assert.Len(t, strings.Split(resp["bootstrapBrokerStringTls"].(string), ","), 3)
}
@@ -340,7 +341,7 @@ func TestKafka_CreateClusterV2(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "v2-cluster", resp["clusterName"])
assert.Equal(t, "PROVISIONED", resp["clusterType"])
assert.NotEmpty(t, resp["clusterArn"])
@@ -380,7 +381,7 @@ func TestKafka_DescribeClusterV2(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
var clusterArn string
if tt.useRealArn {
@@ -395,7 +396,7 @@ func TestKafka_DescribeClusterV2(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusterInfo, ok := resp["clusterInfo"].(map[string]any)
require.True(t, ok)
@@ -456,7 +457,7 @@ func TestKafka_ListClustersV2(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusters, ok := resp["clusterInfoList"].([]any)
require.True(t, ok)
@@ -582,7 +583,7 @@ func TestListClustersPagination(t *testing.T) {
assert.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
list1, ok := resp1["clusterInfoList"].([]any)
require.True(t, ok, "clusterInfoList must be an array")
@@ -601,7 +602,7 @@ func TestListClustersPagination(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
list2, ok2 := resp2["clusterInfoList"].([]any)
require.True(t, ok2)
@@ -638,7 +639,7 @@ func TestListClusters_NegativeOffsetToken(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp["clusterInfoList"].([]any)
require.True(t, ok, "clusterInfoList must be an array")
@@ -662,7 +663,7 @@ func TestListClustersV2Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
list1 := resp1["clusterInfoList"].([]any)
assert.Len(t, list1, 2)
@@ -675,7 +676,7 @@ func TestListClustersV2Pagination(t *testing.T) {
var tok struct {
O int `json:"o"`
}
- require.NoError(t, json.Unmarshal(decoded, &tok))
+ require.NoError(t, json.Unmarshal(decoded, &tok, jsonx.V1()))
assert.Equal(t, 2, tok.O)
path2 := "/api/v2/clusters?maxResults=2&nextToken=" + url.QueryEscape(nextToken)
@@ -683,7 +684,7 @@ func TestListClustersV2Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
list2 := resp2["clusterInfoList"].([]any)
assert.Len(t, list2, 2)
assert.Empty(t, resp2["nextToken"])
@@ -727,7 +728,7 @@ func TestClusterCreatingToActive(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
clusterArn = createResp["clusterArn"].(string)
} else {
rec := doKafkaRequest(t, h, http.MethodPost, "/v1/clusters", map[string]any{
@@ -742,7 +743,7 @@ func TestClusterCreatingToActive(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
clusterArn = createResp["clusterArn"].(string)
assert.Equal(t, kafka.ClusterStateCreating, createResp["state"])
}
@@ -755,7 +756,7 @@ func TestClusterCreatingToActive(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
clusterInfo := descResp["clusterInfo"].(map[string]any)
assert.Equal(t, kafka.ClusterStateActive, clusterInfo["state"])
})
@@ -779,7 +780,7 @@ func TestInvalidNextTokenFallsBackToStart(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list := resp["clusterInfoList"].([]any)
assert.Len(t, list, 3)
}
@@ -803,7 +804,7 @@ func TestCreateClusterV2HTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "v2-cluster", resp["clusterName"])
assert.Equal(t, "PROVISIONED", resp["clusterType"])
}
diff --git a/services/kafka/handler_configurations.go b/services/kafka/handler_configurations.go
index a01c8f512b..71e4ccc8b0 100644
--- a/services/kafka/handler_configurations.go
+++ b/services/kafka/handler_configurations.go
@@ -2,12 +2,14 @@ package kafka
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createConfigurationInput struct {
@@ -54,7 +56,7 @@ func (h *Handler) handleCreateConfiguration(
body []byte,
) error {
var in createConfigurationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -231,7 +233,7 @@ func (h *Handler) handleUpdateConfiguration(
body []byte,
) error {
var in updateConfigurationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/kafka/handler_configurations_test.go b/services/kafka/handler_configurations_test.go
index 4618d610a1..b27c39e458 100644
--- a/services/kafka/handler_configurations_test.go
+++ b/services/kafka/handler_configurations_test.go
@@ -2,7 +2,7 @@ package kafka_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kafka"
)
@@ -56,7 +57,7 @@ func TestKafka_CreateAndDescribeConfiguration(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "my-config", resp["name"])
assert.NotEmpty(t, resp["arn"])
}
@@ -102,7 +103,7 @@ func TestKafka_ListConfigurations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
configs, ok := resp["configurations"].([]any)
require.True(t, ok)
@@ -144,7 +145,7 @@ func TestKafka_DescribeAndDeleteConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
var cfgArn string
if tt.useRealArn {
@@ -209,7 +210,7 @@ func TestCreateConfigurationResponseIncludesStateAndRevision(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["arn"], "arn must be present")
assert.NotEmpty(t, resp["name"], "name must be present")
@@ -269,7 +270,7 @@ func TestListConfigurationsPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
configs := resp["configurations"].([]any)
assert.Len(t, configs, tt.wantPage1)
@@ -301,7 +302,7 @@ func TestConfigRevision_DescribeAndList(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.InDelta(t, float64(1), descResp["revision"], 0)
// ListConfigurationRevisions.
@@ -310,7 +311,7 @@ func TestConfigRevision_DescribeAndList(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
revisions, _ := listResp["revisions"].([]any)
assert.NotEmpty(t, revisions, "should have at least one revision")
}
@@ -330,7 +331,7 @@ func TestConfigRevision_UpdateConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Equal(t, configArn, updateResp["arn"])
assert.Equal(t, "cfg-update-test", updateResp["name"])
}
diff --git a/services/kafka/handler_nodes_test.go b/services/kafka/handler_nodes_test.go
index 1b599896a7..442b5d11b0 100644
--- a/services/kafka/handler_nodes_test.go
+++ b/services/kafka/handler_nodes_test.go
@@ -2,7 +2,7 @@ package kafka_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"strings"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kafka"
)
@@ -110,7 +111,7 @@ func TestListKafkaVersions_HTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions, ok := resp["kafkaVersions"].([]any)
require.True(t, ok)
diff --git a/services/kafka/handler_replicators.go b/services/kafka/handler_replicators.go
index 8f1687cbfd..913c41434f 100644
--- a/services/kafka/handler_replicators.go
+++ b/services/kafka/handler_replicators.go
@@ -2,12 +2,14 @@ package kafka
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// amazonMskClusterDTO mirrors types.AmazonMskCluster.
@@ -338,7 +340,7 @@ type createReplicatorOutput struct {
func (h *Handler) handleCreateReplicator(ctx context.Context, c *echo.Context, body []byte) error {
var in createReplicatorInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -599,7 +601,7 @@ func (h *Handler) handleUpdateReplicationInfo(
body []byte,
) error {
var in updateReplicationInfoInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/kafka/handler_replicators_test.go b/services/kafka/handler_replicators_test.go
index 828b4ada3c..6be668964f 100644
--- a/services/kafka/handler_replicators_test.go
+++ b/services/kafka/handler_replicators_test.go
@@ -2,7 +2,7 @@ package kafka_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kafka"
)
@@ -48,7 +49,7 @@ func TestKafka_CreateReplicator(t *testing.T) {
var bodyBytes []byte
if tt.body != nil {
var err error
- bodyBytes, err = json.Marshal(tt.body)
+ bodyBytes, err = json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("not-json")
@@ -66,7 +67,7 @@ func TestKafka_CreateReplicator(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "my-replicator", resp["replicatorName"])
assert.NotEmpty(t, resp["replicatorArn"])
assert.Equal(t, kafka.ReplicatorStateRunning, resp["replicatorState"])
@@ -97,7 +98,7 @@ func TestKafka_DeleteReplicator(t *testing.T) {
bodyBytes, _ := json.Marshal(map[string]any{
"replicatorName": "my-replicator",
"serviceExecutionRoleArn": "arn:aws:iam::000000000000:role/my-role",
- })
+ }, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/replication/v1/replicators", bytes.NewReader(bodyBytes))
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()
@@ -107,7 +108,7 @@ func TestKafka_DeleteReplicator(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
var replicatorArn string
if tt.useRealArn {
@@ -172,7 +173,7 @@ func TestListReplicatorsPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
reps := resp["replicators"].([]any)
assert.Len(t, reps, 2)
@@ -186,7 +187,7 @@ func TestListReplicatorsPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
reps2 := resp2["replicators"].([]any)
assert.Len(t, reps2, 1)
assert.Empty(t, resp2["nextToken"])
@@ -341,7 +342,7 @@ func TestReplicator_UpdateReplicationInfo(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code, updateRec.Body.String())
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Equal(t, replicatorArn, updateResp["replicatorArn"])
assert.NotEmpty(t, updateResp["replicatorState"])
}
diff --git a/services/kafka/handler_scram_secrets.go b/services/kafka/handler_scram_secrets.go
index 94118f7c09..b60a996776 100644
--- a/services/kafka/handler_scram_secrets.go
+++ b/services/kafka/handler_scram_secrets.go
@@ -2,10 +2,12 @@ package kafka
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type scramSecretInput struct {
@@ -24,7 +26,7 @@ func (h *Handler) handleBatchAssociateScramSecret(
body []byte,
) error {
var in scramSecretInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -48,7 +50,7 @@ func (h *Handler) handleBatchDisassociateScramSecret(
body []byte,
) error {
var in scramSecretInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/kafka/handler_scram_secrets_test.go b/services/kafka/handler_scram_secrets_test.go
index 611898c9b5..5c04a56063 100644
--- a/services/kafka/handler_scram_secrets_test.go
+++ b/services/kafka/handler_scram_secrets_test.go
@@ -2,7 +2,7 @@ package kafka_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -11,6 +11,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestKafka_BatchAssociateScramSecret(t *testing.T) {
@@ -53,14 +55,14 @@ func TestKafka_BatchAssociateScramSecret(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
clusterArn := createResp["clusterArn"].(string)
encodedArn := url.PathEscape(clusterArn)
var bodyBytes []byte
if tt.body != nil {
var err error
- bodyBytes, err = json.Marshal(tt.body)
+ bodyBytes, err = json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("not-json")
@@ -82,7 +84,7 @@ func TestKafka_BatchAssociateScramSecret(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "unprocessedScramSecrets")
}
})
@@ -117,14 +119,14 @@ func TestKafka_BatchDisassociateScramSecret(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
clusterArn := createResp["clusterArn"].(string)
encodedArn := url.PathEscape(clusterArn)
secretArn := "arn:aws:secretsmanager:us-east-1:000000000000:secret/my-secret"
// Associate first
- bodyBytes, _ := json.Marshal(map[string]any{"secretArnList": []string{secretArn}})
+ bodyBytes, _ := json.Marshal(map[string]any{"secretArnList": []string{secretArn}}, jsonx.V1())
e := echo.New()
req := httptest.NewRequest(
http.MethodPost,
@@ -139,7 +141,7 @@ func TestKafka_BatchDisassociateScramSecret(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
// Disassociate
- bodyBytes2, _ := json.Marshal(map[string]any{"secretArnList": []string{secretArn}})
+ bodyBytes2, _ := json.Marshal(map[string]any{"secretArnList": []string{secretArn}}, jsonx.V1())
req2 := httptest.NewRequest(
http.MethodPatch,
"/v1/clusters/"+encodedArn+"/scram-secrets",
@@ -188,7 +190,7 @@ func TestScramSecret_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, assocRec.Code)
var assocResp map[string]any
- require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp))
+ require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp, jsonx.V1()))
unprocessed, _ := assocResp["unprocessedScramSecrets"].([]any)
assert.Empty(t, unprocessed, "no secrets should be unprocessed")
@@ -198,7 +200,7 @@ func TestScramSecret_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
secretList, _ := listResp["secretArnList"].([]any)
require.Len(t, secretList, 1)
assert.Equal(t, secretArn, secretList[0])
@@ -210,7 +212,7 @@ func TestScramSecret_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, disassocRec.Code)
var disassocResp map[string]any
- require.NoError(t, json.Unmarshal(disassocRec.Body.Bytes(), &disassocResp))
+ require.NoError(t, json.Unmarshal(disassocRec.Body.Bytes(), &disassocResp, jsonx.V1()))
unprocessed2, _ := disassocResp["unprocessedScramSecrets"].([]any)
assert.Empty(t, unprocessed2, "no secrets should be unprocessed after disassociate")
@@ -220,7 +222,7 @@ func TestScramSecret_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, listRec2.Code)
var listResp2 map[string]any
- require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp2))
+ require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp2, jsonx.V1()))
secretList2, _ := listResp2["secretArnList"].([]any)
assert.Empty(t, secretList2, "list should be empty after disassociate")
}
@@ -248,7 +250,7 @@ func TestScramSecret_AssociateMultiple(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
secretList, _ := listResp["secretArnList"].([]any)
assert.Len(t, secretList, 3)
}
@@ -292,7 +294,7 @@ func TestBatchAssociateThenDisassociate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
clusterArn := cr["clusterArn"].(string)
encoded := url.PathEscape(clusterArn)
diff --git a/services/kafka/handler_tags.go b/services/kafka/handler_tags.go
index f562abdbf4..acfd4d29ef 100644
--- a/services/kafka/handler_tags.go
+++ b/services/kafka/handler_tags.go
@@ -2,11 +2,13 @@ package kafka
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type listTagsOutput struct {
@@ -37,7 +39,7 @@ func (h *Handler) handleTagResource(
body []byte,
) error {
var in tagResourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/kafka/handler_tags_test.go b/services/kafka/handler_tags_test.go
index 50cabf4b2f..fdf6d0f9f4 100644
--- a/services/kafka/handler_tags_test.go
+++ b/services/kafka/handler_tags_test.go
@@ -1,7 +1,7 @@
package kafka_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestKafka_TagOperations(t *testing.T) {
@@ -43,7 +45,7 @@ func TestKafka_TagOperations(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
clusterArn := createResp["clusterArn"].(string)
encodedArn := url.PathEscape(clusterArn)
tagPath := "/v1/tags/" + encodedArn
@@ -98,7 +100,7 @@ func TestTag_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags, _ := listResp["tags"].(map[string]any)
assert.Equal(t, "test", tags["env"])
assert.Equal(t, "infra", tags["team"])
@@ -119,7 +121,7 @@ func TestTag_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, listRec2.Code)
var listResp2 map[string]any
- require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp2))
+ require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp2, jsonx.V1()))
tags2, _ := listResp2["tags"].(map[string]any)
assert.Equal(t, "test", tags2["env"])
_, hasTeam := tags2["team"]
@@ -138,7 +140,7 @@ func TestTag_ListEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags, _ := listResp["tags"].(map[string]any)
assert.Empty(t, tags, "freshly created cluster should have no tags")
}
diff --git a/services/kafka/handler_test.go b/services/kafka/handler_test.go
index 703e4b3934..1fda0d39e2 100644
--- a/services/kafka/handler_test.go
+++ b/services/kafka/handler_test.go
@@ -2,7 +2,7 @@ package kafka_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/kafka"
)
@@ -39,7 +40,7 @@ func doKafkaRequest(t *testing.T, h *kafka.Handler, method, path string, body an
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -68,7 +69,7 @@ func decodeJSONResponse(t *testing.T, rec *httptest.ResponseRecorder) map[string
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
@@ -88,7 +89,7 @@ func doKafkaRequestJSON(
var resp map[string]any
if rec.Body.Len() > 0 {
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
}
return resp, rec.Code
diff --git a/services/kafka/handler_topics.go b/services/kafka/handler_topics.go
index 3ebf1f8085..1b26b1a699 100644
--- a/services/kafka/handler_topics.go
+++ b/services/kafka/handler_topics.go
@@ -2,11 +2,13 @@ package kafka
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// createTopicInput mirrors aws-sdk-go-v2/service/kafka's CreateTopicInput
@@ -34,7 +36,7 @@ func (h *Handler) handleCreateTopic(
body []byte,
) error {
var in createTopicInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -275,7 +277,7 @@ func (h *Handler) handleUpdateTopic(
}
var in updateTopicInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/kafka/handler_topics_test.go b/services/kafka/handler_topics_test.go
index 9f8c3d9256..e26f6e14c5 100644
--- a/services/kafka/handler_topics_test.go
+++ b/services/kafka/handler_topics_test.go
@@ -3,7 +3,7 @@ package kafka_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestKafka_CreateTopic(t *testing.T) {
@@ -57,14 +59,14 @@ func TestKafka_CreateTopic(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
clusterArn := createResp["clusterArn"].(string)
encodedArn := url.PathEscape(clusterArn)
var bodyBytes []byte
if tt.body != nil {
var err error
- bodyBytes, err = json.Marshal(tt.body)
+ bodyBytes, err = json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("not-json")
@@ -86,7 +88,7 @@ func TestKafka_CreateTopic(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "my-topic", resp["topicName"])
}
})
@@ -123,14 +125,14 @@ func TestKafka_DeleteTopic(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
clusterArn := createResp["clusterArn"].(string)
encodedArn := url.PathEscape(clusterArn)
// Create topic
e := echo.New()
topicBody, _ := json.Marshal(
- map[string]any{"topicName": "my-topic", "replicationFactor": 1, "partitionCount": 3},
+ map[string]any{"topicName": "my-topic", "replicationFactor": 1, "partitionCount": 3}, jsonx.V1(),
)
reqCreate := httptest.NewRequest(
http.MethodPost,
@@ -236,7 +238,7 @@ func TestListTopicsPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
topics1 := resp1["topics"].([]any)
assert.Len(t, topics1, 3)
@@ -253,7 +255,7 @@ func TestListTopicsPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
topics2 := resp2["topics"].([]any)
assert.Len(t, topics2, 2)
assert.Empty(t, resp2["nextToken"])
diff --git a/services/kafka/handler_vpc_connections.go b/services/kafka/handler_vpc_connections.go
index 47e824e445..cb7d381f17 100644
--- a/services/kafka/handler_vpc_connections.go
+++ b/services/kafka/handler_vpc_connections.go
@@ -2,10 +2,12 @@ package kafka
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createVpcConnectionInput struct {
@@ -37,7 +39,7 @@ func (h *Handler) handleCreateVpcConnection(
body []byte,
) error {
var in createVpcConnectionInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
@@ -208,7 +210,7 @@ func (h *Handler) handleRejectClientVpcConnection(
body []byte,
) error {
var in rejectClientVpcConnectionInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(
c,
http.StatusBadRequest,
diff --git a/services/kafka/handler_vpc_connections_test.go b/services/kafka/handler_vpc_connections_test.go
index aced96d301..67add0f2c8 100644
--- a/services/kafka/handler_vpc_connections_test.go
+++ b/services/kafka/handler_vpc_connections_test.go
@@ -3,7 +3,7 @@ package kafka_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kafka"
)
@@ -58,7 +59,7 @@ func TestKafka_CreateVpcConnection(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
clusterArn := createResp["clusterArn"].(string)
body := tt.body
@@ -69,7 +70,7 @@ func TestKafka_CreateVpcConnection(t *testing.T) {
var bodyBytes []byte
if tt.body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("not-json")
@@ -87,7 +88,7 @@ func TestKafka_CreateVpcConnection(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["vpcConnectionArn"])
assert.Equal(t, kafka.VpcConnectionStateAvailable, resp["state"])
}
@@ -126,7 +127,7 @@ func TestKafka_DeleteVpcConnection(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
clusterArn := createResp["clusterArn"].(string)
// Create VPC connection
@@ -135,7 +136,7 @@ func TestKafka_DeleteVpcConnection(t *testing.T) {
"targetClusterArn": clusterArn,
"vpcId": "vpc-12345",
"authentication": "SASL_IAM",
- })
+ }, jsonx.V1())
reqVpc := httptest.NewRequest(http.MethodPost, "/v1/vpc-connection", bytes.NewReader(vpcBody))
reqVpc.Header.Set("Content-Type", "application/json")
recVpc := httptest.NewRecorder()
@@ -144,7 +145,7 @@ func TestKafka_DeleteVpcConnection(t *testing.T) {
require.Equal(t, http.StatusOK, recVpc.Code)
var vpcResp map[string]any
- require.NoError(t, json.Unmarshal(recVpc.Body.Bytes(), &vpcResp))
+ require.NoError(t, json.Unmarshal(recVpc.Body.Bytes(), &vpcResp, jsonx.V1()))
var vpcConnectionArn string
if tt.useRealArn {
@@ -213,7 +214,7 @@ func TestVpcConnection_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code, createRec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
vpcConnArn, _ := createResp["vpcConnectionArn"].(string)
require.NotEmpty(t, vpcConnArn)
// Real CreateVpcConnectionOutput has no targetClusterArn field (unlike
@@ -230,7 +231,7 @@ func TestVpcConnection_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, vpcConnArn, descResp["vpcConnectionArn"])
assert.Equal(t, clusterArn, descResp["targetClusterArn"])
@@ -241,7 +242,7 @@ func TestVpcConnection_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
conns, _ := listResp["vpcConnections"].([]any)
require.Len(t, conns, 1)
@@ -275,7 +276,7 @@ func TestVpcConnection_ListClientVpcConnections(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
// Real ListClientVpcConnectionsOutput carries its array under the
// distinct "clientVpcConnections" key (not "vpcConnections", which is
// ListVpcConnections' key -- a different operation with a different
@@ -303,7 +304,7 @@ func TestVpcConnection_RejectClientVpcConnection(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
vpcConnArn, _ := createResp["vpcConnectionArn"].(string)
encodedCluster := url.PathEscape(clusterArn)
diff --git a/services/kafka/iam_auth.go b/services/kafka/iam_auth.go
index aa725d3bf6..f23ac86ca7 100644
--- a/services/kafka/iam_auth.go
+++ b/services/kafka/iam_auth.go
@@ -4,13 +4,14 @@ import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/url"
"strconv"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iam"
"github.com/blackbirdworks/gopherstack/services/sts"
)
@@ -83,7 +84,7 @@ type iamPayload struct {
// parseIAMPayload decodes the JSON sent by the aws-msk-iam-auth clients (version 2020_10_22).
func parseIAMPayload(raw []byte) (*iamPayload, error) {
var kv map[string]string
- if err := json.Unmarshal(raw, &kv); err != nil {
+ if err := json.Unmarshal(raw, &kv, jsonx.V1()); err != nil {
return nil, errIAMMalformed
}
diff --git a/services/kafka/persistence.go b/services/kafka/persistence.go
index 01abe28bd1..ff1e7a18d5 100644
--- a/services/kafka/persistence.go
+++ b/services/kafka/persistence.go
@@ -2,7 +2,7 @@ package kafka
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"maps"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -32,13 +32,13 @@ const kafkaSnapshotVersion = 1
// ScramSecrets and ClusterPolicies persist the two fields left as plain maps
// (their values are not *T, so they don't fit store.Table) alongside Tables.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- ScramSecrets map[string][]string `json:"scramSecrets"`
- ClusterPolicies map[string]string `json:"clusterPolicies"`
- PolicyVersions map[string]string `json:"clusterPolicyVersions,omitempty"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ ScramSecrets map[string][]string `json:"scramSecrets"`
+ ClusterPolicies map[string]string `json:"clusterPolicies"`
+ PolicyVersions map[string]string `json:"clusterPolicyVersions,omitempty"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/kafka/routes_test.go b/services/kafka/routes_test.go
index 58d837a681..74a4519d5b 100644
--- a/services/kafka/routes_test.go
+++ b/services/kafka/routes_test.go
@@ -1,7 +1,7 @@
package kafka_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"net/url"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kafka"
)
@@ -333,7 +334,7 @@ func TestUpdateOpsLiveUnderV1ClustersPath(t *testing.T) {
tt.method, tt.suffix, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["clusterOperationArn"])
// The old (wrong) /api/v2 path must no longer resolve to the same op --
@@ -363,7 +364,7 @@ func TestRejectClientVpcConnectionRealPath(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
vpcConnArn, _ := createResp["vpcConnectionArn"].(string)
require.NotEmpty(t, vpcConnArn)
@@ -399,7 +400,7 @@ func TestListVpcConnectionsPluralPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
conns, _ := resp["vpcConnections"].([]any)
assert.Len(t, conns, 1)
@@ -425,7 +426,7 @@ func TestGetCompatibleKafkaVersionsTopLevelPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions, _ := resp["compatibleKafkaVersions"].([]any)
assert.NotEmpty(t, versions)
@@ -477,7 +478,7 @@ func TestUpdateReplicationInfoStripsSuffix(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, replicatorArn, resp["replicatorArn"],
"the resolved replicator ARN must not include the /replication-info suffix")
}
diff --git a/services/kafka/scram_sync.go b/services/kafka/scram_sync.go
index 5f13e55f9e..c23cd2e1c7 100644
--- a/services/kafka/scram_sync.go
+++ b/services/kafka/scram_sync.go
@@ -5,7 +5,7 @@ import (
"crypto/pbkdf2"
"crypto/rand"
"crypto/sha512"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -14,6 +14,7 @@ import (
"github.com/twmb/franz-go/pkg/kgo"
"github.com/twmb/franz-go/pkg/kmsg"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -249,8 +250,13 @@ func ParseScramSecret(secretString string) (ScramCredential, error) {
Password *string `json:"password"`
}
- if err := json.Unmarshal([]byte(secretString), &v); err != nil || v.Username == nil || v.Password == nil ||
- *v.Username == "" || *v.Password == "" {
+ if err := json.Unmarshal(
+ []byte(secretString),
+ &v,
+ jsonx.V1(),
+ ); err != nil || v.Username == nil || v.Password == nil ||
+ *v.Username == "" ||
+ *v.Password == "" {
return ScramCredential{}, errScramSecretShape
}
diff --git a/services/kafkaconnect/handler.go b/services/kafkaconnect/handler.go
index 4df86a8156..72b7305bff 100644
--- a/services/kafkaconnect/handler.go
+++ b/services/kafkaconnect/handler.go
@@ -1,7 +1,7 @@
package kafkaconnect
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"maps"
"net/http"
@@ -11,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -225,7 +226,7 @@ func decodeBody[T any](body []byte, out *T) error {
return nil
}
- return json.Unmarshal(body, out)
+ return json.Unmarshal(body, out, jsonx.V1())
}
// writeJSON writes a 200 JSON response.
diff --git a/services/kafkaconnect/multi_region_test.go b/services/kafkaconnect/multi_region_test.go
index d8dd0439c2..c36be1f356 100644
--- a/services/kafkaconnect/multi_region_test.go
+++ b/services/kafkaconnect/multi_region_test.go
@@ -2,7 +2,8 @@ package kafkaconnect_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/kafkaconnect"
)
@@ -185,8 +187,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/kafkaconnect/persistence.go b/services/kafkaconnect/persistence.go
index 2554cc270a..3f05a3021c 100644
--- a/services/kafkaconnect/persistence.go
+++ b/services/kafkaconnect/persistence.go
@@ -2,7 +2,7 @@ package kafkaconnect
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
@@ -30,8 +30,8 @@ const kafkaConnectSnapshotVersion = 1
// workerConfigurations, connectorOperations -- see store_setup.go), produced
// by b.registry.SnapshotAll().
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serializes backend state to JSON.
diff --git a/services/kafkaconnect/worker_engine.go b/services/kafkaconnect/worker_engine.go
index 860bb50c0b..21af36e50d 100644
--- a/services/kafkaconnect/worker_engine.go
+++ b/services/kafkaconnect/worker_engine.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -22,6 +22,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
"github.com/blackbirdworks/gopherstack/pkgs/container"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc"
)
@@ -620,7 +621,7 @@ func (e *workerEngine) call(ctx context.Context, method, url string, body any) (
var rdr io.Reader
if body != nil {
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
if err != nil {
return nil, 0, fmt.Errorf("encode request: %w", err)
}
@@ -670,7 +671,7 @@ func (e *workerEngine) readStatus(ctx context.Context, base, name string) (strin
}
var st connectStatus
- if err = json.Unmarshal(data, &st); err != nil {
+ if err = json.Unmarshal(data, &st, jsonx.V1()); err != nil {
return "", "", fmt.Errorf("decode status: %w", err)
}
diff --git a/services/kinesis/account_settings_test.go b/services/kinesis/account_settings_test.go
index 5c214b55ad..410186cb7c 100644
--- a/services/kinesis/account_settings_test.go
+++ b/services/kinesis/account_settings_test.go
@@ -2,7 +2,7 @@ package kinesis_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -104,7 +105,7 @@ func TestDescribeLimits_DynamicOpenShardCount(t *testing.T) {
ShardLimit int `json:"ShardLimit"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &before))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &before, jsonx.V1()))
assert.Equal(t, 500, before.ShardLimit)
// Create a stream with 3 shards.
@@ -117,7 +118,7 @@ func TestDescribeLimits_DynamicOpenShardCount(t *testing.T) {
OpenShardCount int `json:"OpenShardCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &after))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &after, jsonx.V1()))
assert.Equal(t, before.OpenShardCount+3, after.OpenShardCount)
}
@@ -142,7 +143,7 @@ func TestDescribeLimits(t *testing.T) {
OnDemandStreamCountLimit int `json:"OnDemandStreamCountLimit"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, 500, resp.ShardLimit)
assert.Equal(t, 0, resp.OpenShardCount)
assert.Equal(t, 0, resp.OnDemandStreamCount)
@@ -194,7 +195,7 @@ func TestDescribeAccountSettings(t *testing.T) {
} `json:"MinimumThroughputBillingCommitment"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "DISABLED", resp.MinimumThroughputBillingCommitment.Status)
assert.Zero(t, resp.MinimumThroughputBillingCommitment.StartedAt)
assert.Zero(t, resp.MinimumThroughputBillingCommitment.EndedAt)
@@ -256,7 +257,7 @@ func TestHandleDescribeLimits(t *testing.T) {
OpenShardCount int `json:"OpenShardCount"`
ShardLimit int `json:"ShardLimit"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, 0, resp.OpenShardCount)
assert.Equal(t, 500, resp.ShardLimit)
}
diff --git a/services/kinesis/cbor_test.go b/services/kinesis/cbor_test.go
index b9b879bf7a..142758295d 100644
--- a/services/kinesis/cbor_test.go
+++ b/services/kinesis/cbor_test.go
@@ -3,7 +3,7 @@ package kinesis_test
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -191,7 +192,7 @@ func TestKinesisCBOR_GetRecords(t *testing.T) {
"ShardId": "shardId-000000000000",
"ShardIteratorType": "TRIM_HORIZON",
}
- iterBytes, _ := json.Marshal(iterBody)
+ iterBytes, _ := json.Marshal(iterBody, jsonx.V1())
iterReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(iterBytes))
iterReq.Header.Set("Content-Type", "application/x-amz-json-1.1")
iterReq.Header.Set("X-Amz-Target", kinesisPrefix+"GetShardIterator")
@@ -203,7 +204,7 @@ func TestKinesisCBOR_GetRecords(t *testing.T) {
require.Equal(t, http.StatusOK, iterRec.Code)
var iterResp map[string]any
- require.NoError(t, json.Unmarshal(iterRec.Body.Bytes(), &iterResp))
+ require.NoError(t, json.Unmarshal(iterRec.Body.Bytes(), &iterResp, jsonx.V1()))
shardIter, ok := iterResp["ShardIterator"].(string)
require.True(t, ok, "must have ShardIterator")
@@ -253,7 +254,7 @@ func TestKinesisCBOR_DataRoundTrip(t *testing.T) {
"Data": b64Data,
"PartitionKey": "pk1",
}
- b, _ := json.Marshal(jsonPut)
+ b, _ := json.Marshal(jsonPut, jsonx.V1())
putReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(b))
putReq.Header.Set("Content-Type", "application/x-amz-json-1.1")
putReq.Header.Set("X-Amz-Target", kinesisPrefix+"PutRecord")
@@ -276,7 +277,7 @@ func TestKinesisCBOR_DataRoundTrip(t *testing.T) {
iterC := e.NewContext(iterReq, iterRec)
require.NoError(t, h.Handler()(iterC))
var iterResp map[string]any
- require.NoError(t, json.Unmarshal(iterRec.Body.Bytes(), &iterResp))
+ require.NoError(t, json.Unmarshal(iterRec.Body.Bytes(), &iterResp, jsonx.V1()))
shardIter := iterResp["ShardIterator"].(string)
// Read via CBOR.
@@ -376,7 +377,7 @@ func TestKinesisCBOR_JSONAndCBORCoexist(t *testing.T) {
"Data": b64,
"PartitionKey": "pk2",
}
- rawJSON, _ := json.Marshal(putJSON)
+ rawJSON, _ := json.Marshal(putJSON, jsonx.V1())
req2 := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(rawJSON))
req2.Header.Set("Content-Type", "application/x-amz-json-1.1")
req2.Header.Set("X-Amz-Target", kinesisPrefix+"PutRecord")
@@ -398,7 +399,7 @@ func TestKinesisCBOR_JSONAndCBORCoexist(t *testing.T) {
iterC := e.NewContext(iterReq, iterRec)
require.NoError(t, h.Handler()(iterC))
var iterResp map[string]any
- require.NoError(t, json.Unmarshal(iterRec.Body.Bytes(), &iterResp))
+ require.NoError(t, json.Unmarshal(iterRec.Body.Bytes(), &iterResp, jsonx.V1()))
shardIter := iterResp["ShardIterator"].(string)
getReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(mustMarshalBytes(t, map[string]any{
@@ -413,7 +414,7 @@ func TestKinesisCBOR_JSONAndCBORCoexist(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
recs := getResp["Records"].([]any)
assert.GreaterOrEqual(t, len(recs), 2, "both records must be visible via JSON")
})
@@ -421,7 +422,7 @@ func TestKinesisCBOR_JSONAndCBORCoexist(t *testing.T) {
func mustMarshalBytes(t *testing.T, v any) []byte {
t.Helper()
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
return b
diff --git a/services/kinesis/channel_delivery.go b/services/kinesis/channel_delivery.go
index 559a10e044..6244cc75f6 100644
--- a/services/kinesis/channel_delivery.go
+++ b/services/kinesis/channel_delivery.go
@@ -4,7 +4,8 @@ import (
"bytes"
"compress/gzip"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"regexp"
@@ -18,6 +19,7 @@ import (
"github.com/google/uuid"
"github.com/klauspost/compress/zstd"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -159,7 +161,7 @@ func validateChannelRecord(formatType string, data []byte) string {
return "record is not valid UTF-8 for RecordFormatType STRING"
}
case recordFormatTypeJSON:
- if !json.Valid(data) {
+ if !jsontext.Value(data).IsValid(jsonx.V1()) {
return "record is not valid JSON for RecordFormatType JSON"
}
}
@@ -399,7 +401,7 @@ func (b *InMemoryBackend) writeChannelDeadLetterQueue(
ShardID string `json:"shardID"`
SequenceNumber string `json:"sequenceNumber"`
ErrorMessage string `json:"errorMessage"`
- }{f.streamARN, f.shardID, f.sequenceNumber, f.errorMessage})
+ }{f.streamARN, f.shardID, f.sequenceNumber, f.errorMessage}, jsonx.V1())
if err != nil {
continue
}
diff --git a/services/kinesis/consumer_wire_shape_test.go b/services/kinesis/consumer_wire_shape_test.go
index ee1168b29a..65edd5a578 100644
--- a/services/kinesis/consumer_wire_shape_test.go
+++ b/services/kinesis/consumer_wire_shape_test.go
@@ -1,12 +1,14 @@
package kinesis_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestConsumerWireShape_StreamARN locks in that StreamARN only appears on
@@ -36,7 +38,7 @@ func TestConsumerWireShape_StreamARN(t *testing.T) {
StreamARN string `json:"StreamARN"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(setupRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(setupRec.Body.Bytes(), &descResp, jsonx.V1()))
streamARN := descResp.StreamDescription.StreamARN
require.NotEmpty(t, streamARN)
@@ -82,7 +84,7 @@ func TestConsumerWireShape_StreamARN(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
consumerObj := raw[tc.wrapperKey]
var obj map[string]any
diff --git a/services/kinesis/consumers_test.go b/services/kinesis/consumers_test.go
index 1c9f2e296c..7264950757 100644
--- a/services/kinesis/consumers_test.go
+++ b/services/kinesis/consumers_test.go
@@ -3,7 +3,7 @@ package kinesis_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strconv"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -34,7 +35,7 @@ func subscribeAndCollect(t *testing.T, h *kinesis.Handler, consumerARN, shardID
"StartingPosition": map[string]any{
"Type": "TRIM_HORIZON",
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
@@ -62,7 +63,7 @@ func getFirstShardID(t *testing.T, h *kinesis.Handler, streamName string) string
} `json:"Shards"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotEmpty(t, resp.StreamDescription.Shards)
return resp.StreamDescription.Shards[0].ShardID
@@ -82,7 +83,7 @@ func registerConsumerAndGetARN(t *testing.T, h *kinesis.Handler, streamARN, cons
ConsumerARN string `json:"ConsumerARN"`
} `json:"Consumer"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp.Consumer.ConsumerARN
}
@@ -220,7 +221,7 @@ func TestRegisterStreamConsumer_InvalidName(t *testing.T) {
var resp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidArgumentException", resp.Type)
})
}
@@ -349,7 +350,7 @@ func TestListStreamConsumers_MaxResultsPagination(t *testing.T) {
ConsumerName string `json:"ConsumerName"`
} `json:"Consumers"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
assert.Len(t, page1.Consumers, 2)
assert.NotEmpty(t, page1.NextToken)
@@ -367,7 +368,7 @@ func TestListStreamConsumers_MaxResultsPagination(t *testing.T) {
ConsumerName string `json:"ConsumerName"`
} `json:"Consumers"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
assert.Len(t, page2.Consumers, 2)
assert.NotEmpty(t, page2.NextToken)
@@ -385,7 +386,7 @@ func TestListStreamConsumers_MaxResultsPagination(t *testing.T) {
ConsumerName string `json:"ConsumerName"`
} `json:"Consumers"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page3))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page3, jsonx.V1()))
assert.Len(t, page3.Consumers, 1)
assert.Empty(t, page3.NextToken)
@@ -430,7 +431,7 @@ func TestListStreamConsumers_NoMaxResults_ReturnsAll(t *testing.T) {
ConsumerName string `json:"ConsumerName"`
} `json:"Consumers"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Consumers, 3)
assert.Empty(t, resp.NextToken)
}
@@ -741,7 +742,7 @@ func createStreamAndGetARN(t *testing.T, h *kinesis.Handler, streamName string)
StreamARN string `json:"StreamARN"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
require.NotEmpty(t, descResp.StreamDescription.StreamARN)
return descResp.StreamDescription.StreamARN
@@ -785,7 +786,7 @@ func TestConsumerLifecycle(t *testing.T) {
// only types.ConsumerDescription (returned by DescribeStreamConsumer)
// does. A raw map catches a StreamARN key resurfacing on this response.
var regRespRaw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®RespRaw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®RespRaw, jsonx.V1()))
consumerRaw, ok := regRespRaw["Consumer"].(map[string]any)
require.True(t, ok)
_, hasStreamARN := consumerRaw["StreamARN"]
@@ -799,7 +800,7 @@ func TestConsumerLifecycle(t *testing.T) {
ConsumerStatus string `json:"ConsumerStatus"`
} `json:"Consumer"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp, jsonx.V1()))
assert.Equal(t, tt.consumerName, regResp.Consumer.ConsumerName)
assert.Equal(t, tt.expectedStatus, regResp.Consumer.ConsumerStatus)
assert.NotEmpty(t, regResp.Consumer.ConsumerARN)
@@ -819,7 +820,7 @@ func TestConsumerLifecycle(t *testing.T) {
ConsumerARN string `json:"ConsumerARN"`
} `json:"ConsumerDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, tt.consumerName, descResp.ConsumerDescription.ConsumerName)
assert.Equal(t, consumerARN, descResp.ConsumerDescription.ConsumerARN)
@@ -841,7 +842,7 @@ func TestConsumerLifecycle(t *testing.T) {
ConsumerName string `json:"ConsumerName"`
} `json:"Consumers"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Consumers, 1)
assert.Equal(t, tt.consumerName, listResp.Consumers[0].ConsumerName)
@@ -860,7 +861,7 @@ func TestConsumerLifecycle(t *testing.T) {
var listResp2 struct {
Consumers []any `json:"Consumers"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp2, jsonx.V1()))
assert.Empty(t, listResp2.Consumers)
})
}
@@ -921,7 +922,7 @@ func TestConsumerErrors(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, errResp.Type)
}
})
@@ -951,7 +952,7 @@ func TestRegisterConsumerDuplicate(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceInUseException", errResp.Type)
}
@@ -975,7 +976,7 @@ func TestSubscribeToShard(t *testing.T) {
} `json:"Shards"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
require.Len(t, descResp.StreamDescription.Shards, 1)
shardID := descResp.StreamDescription.Shards[0].ShardID
@@ -998,7 +999,7 @@ func TestSubscribeToShard(t *testing.T) {
ConsumerARN string `json:"ConsumerARN"`
} `json:"Consumer"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp, jsonx.V1()))
consumerARN := regResp.Consumer.ConsumerARN
// SubscribeToShard.
@@ -1009,7 +1010,7 @@ func TestSubscribeToShard(t *testing.T) {
"StartingPosition": map[string]any{
"Type": "TRIM_HORIZON",
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
diff --git a/services/kinesis/errors_test.go b/services/kinesis/errors_test.go
index 4e597f0649..8eb75acd3c 100644
--- a/services/kinesis/errors_test.go
+++ b/services/kinesis/errors_test.go
@@ -1,12 +1,14 @@
package kinesis_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestErrValidation verifies ErrValidation is returned for invalid stream name.
@@ -39,7 +41,7 @@ func TestErrValidation(t *testing.T) {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ValidationException", errResp.Type)
})
}
diff --git a/services/kinesis/handler.go b/services/kinesis/handler.go
index 573b7493ef..459b8de3f2 100644
--- a/services/kinesis/handler.go
+++ b/services/kinesis/handler.go
@@ -2,7 +2,7 @@ package kinesis
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -269,7 +270,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
var req extractStreamNameInput
- if err = json.Unmarshal(body, &req); err != nil {
+ if err = json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return ""
}
@@ -378,7 +379,7 @@ func (h *Handler) kinesisRoute(ctx context.Context, r *http.Request, action stri
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// handleError writes a Kinesis error response using the standard error details mapping.
diff --git a/services/kinesis/handler_account_settings.go b/services/kinesis/handler_account_settings.go
index 0e40ec97bd..96960de77a 100644
--- a/services/kinesis/handler_account_settings.go
+++ b/services/kinesis/handler_account_settings.go
@@ -2,10 +2,11 @@ package kinesis
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// describeLimitsOutput mirrors kinesis@v1.46.4 DescribeLimitsOutput
@@ -106,7 +107,7 @@ func (h *Handler) handleUpdateAccountSettings(
body []byte,
) (any, error) {
var req jsonUpdateAccountSettingsReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -137,7 +138,7 @@ func (h *Handler) handleUpdateMaxRecordSize(
body []byte,
) (any, error) {
var req jsonUpdateMaxRecordSizeReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
diff --git a/services/kinesis/handler_channels.go b/services/kinesis/handler_channels.go
index 2640be03ad..65d8f9f085 100644
--- a/services/kinesis/handler_channels.go
+++ b/services/kinesis/handler_channels.go
@@ -2,10 +2,11 @@ package kinesis
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- wire types (field names verified against kinesis@v1.53.0 serializers.go/deserializers.go) ---
@@ -419,7 +420,7 @@ func channelSummaryToJSON(c *Channel) jsonChannelSummary {
func (h *Handler) handleCreateChannel(ctx context.Context, _ *http.Request, body []byte) (any, error) {
var req jsonCreateChannelReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -447,7 +448,7 @@ func (h *Handler) handleCreateChannel(ctx context.Context, _ *http.Request, body
func (h *Handler) handleDeleteChannel(ctx context.Context, _ *http.Request, body []byte) (any, error) {
var req jsonChannelARNReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -460,7 +461,7 @@ func (h *Handler) handleDeleteChannel(ctx context.Context, _ *http.Request, body
func (h *Handler) handleDescribeChannel(ctx context.Context, _ *http.Request, body []byte) (any, error) {
var req jsonChannelARNReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -483,7 +484,7 @@ func channelStreamFiltersFromJSON(filters []jsonStreamFilter) []ChannelStreamFil
func (h *Handler) handleListChannels(ctx context.Context, _ *http.Request, body []byte) (any, error) {
var req jsonListChannelsReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -511,7 +512,7 @@ func (h *Handler) handleListChannels(ctx context.Context, _ *http.Request, body
func (h *Handler) handleUpdateChannel(ctx context.Context, _ *http.Request, body []byte) (any, error) {
var req jsonUpdateChannelReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
diff --git a/services/kinesis/handler_consumers.go b/services/kinesis/handler_consumers.go
index 302c04e6f3..0976f694bd 100644
--- a/services/kinesis/handler_consumers.go
+++ b/services/kinesis/handler_consumers.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"hash/crc32"
"math"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -124,7 +125,7 @@ func (h *Handler) handleRegisterStreamConsumer(
body []byte,
) (any, error) {
var req jsonRegisterStreamConsumerReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -146,7 +147,7 @@ func (h *Handler) handleDescribeStreamConsumer(
body []byte,
) (any, error) {
var req jsonDescribeStreamConsumerReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -168,7 +169,7 @@ func (h *Handler) handleListStreamConsumers(
body []byte,
) (any, error) {
var req jsonListStreamConsumersReq
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
out, err := h.Backend.ListStreamConsumers(ctx, &ListStreamConsumersInput{
StreamARN: req.StreamARN,
@@ -193,7 +194,7 @@ func (h *Handler) handleDeregisterStreamConsumer(
body []byte,
) (any, error) {
var req jsonDeregisterStreamConsumerReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -351,7 +352,7 @@ func (h *Handler) parseSubscribeToShardRequest(
return req, StartingPosition{}, false, h.handleError(ctx, c, "SubscribeToShard", readErr)
}
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
return req, StartingPosition{}, false, h.handleError(ctx, c, "SubscribeToShard", ErrInvalidArgument)
}
@@ -552,7 +553,7 @@ func (h *Handler) pollSubscribeToShardTick(
Records: records,
ContinuationSequenceNumber: out.Event.ContinuationSequenceNumber,
MillisBehindLatest: out.Event.MillisBehindLatest,
- })
+ }, jsonx.V1())
if marshalErr != nil {
return nil, false, marshalErr
}
diff --git a/services/kinesis/handler_monitoring.go b/services/kinesis/handler_monitoring.go
index 73159c0dad..7f9c7a705b 100644
--- a/services/kinesis/handler_monitoring.go
+++ b/services/kinesis/handler_monitoring.go
@@ -2,8 +2,10 @@ package kinesis
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type jsonEnhancedMonitoringReq struct {
@@ -25,7 +27,7 @@ func (h *Handler) handleEnableEnhancedMonitoring(
body []byte,
) (any, error) {
var req jsonEnhancedMonitoringReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -53,7 +55,7 @@ func (h *Handler) handleDisableEnhancedMonitoring(
body []byte,
) (any, error) {
var req jsonEnhancedMonitoringReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
diff --git a/services/kinesis/handler_records.go b/services/kinesis/handler_records.go
index 6da6d760c0..fd408d7121 100644
--- a/services/kinesis/handler_records.go
+++ b/services/kinesis/handler_records.go
@@ -2,8 +2,10 @@ package kinesis
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type jsonPutRecordReq struct {
@@ -91,7 +93,7 @@ func (h *Handler) handlePutRecord(
body []byte,
) (any, error) {
var req jsonPutRecordReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -128,7 +130,7 @@ func (h *Handler) handlePutRecords(
body []byte,
) (any, error) {
var req jsonPutRecordsReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -168,7 +170,7 @@ func (h *Handler) handleGetRecords(
body []byte,
) (any, error) {
var req jsonGetRecordsReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
diff --git a/services/kinesis/handler_resharding.go b/services/kinesis/handler_resharding.go
index f7a22fe398..dec2a2f878 100644
--- a/services/kinesis/handler_resharding.go
+++ b/services/kinesis/handler_resharding.go
@@ -2,8 +2,10 @@ package kinesis
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type jsonMergeShardsReq struct {
@@ -40,7 +42,7 @@ func (h *Handler) handleMergeShards(
body []byte,
) (any, error) {
var req jsonMergeShardsReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -62,7 +64,7 @@ func (h *Handler) handleSplitShard(
body []byte,
) (any, error) {
var req jsonSplitShardReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -84,7 +86,7 @@ func (h *Handler) handleUpdateShardCount(
body []byte,
) (any, error) {
var req jsonUpdateShardCountReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
diff --git a/services/kinesis/handler_resource_policy.go b/services/kinesis/handler_resource_policy.go
index 378f0cfdf6..85e7aa011f 100644
--- a/services/kinesis/handler_resource_policy.go
+++ b/services/kinesis/handler_resource_policy.go
@@ -2,8 +2,10 @@ package kinesis
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type jsonResourceARNReq struct {
@@ -25,7 +27,7 @@ func (h *Handler) handlePutResourcePolicy(
body []byte,
) (any, error) {
var req jsonPutResourcePolicyReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -45,7 +47,7 @@ func (h *Handler) handleGetResourcePolicy(
body []byte,
) (any, error) {
var req jsonResourceARNReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -65,7 +67,7 @@ func (h *Handler) handleDeleteResourcePolicy(
body []byte,
) (any, error) {
var req jsonResourceARNReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
diff --git a/services/kinesis/handler_shard_iterators.go b/services/kinesis/handler_shard_iterators.go
index 7199843a64..0790a77d3b 100644
--- a/services/kinesis/handler_shard_iterators.go
+++ b/services/kinesis/handler_shard_iterators.go
@@ -2,9 +2,11 @@ package kinesis
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type jsonGetShardIteratorReq struct {
@@ -27,7 +29,7 @@ func (h *Handler) handleGetShardIterator(
body []byte,
) (any, error) {
var req jsonGetShardIteratorReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
diff --git a/services/kinesis/handler_shards.go b/services/kinesis/handler_shards.go
index 8d589ee07e..abbef6a97b 100644
--- a/services/kinesis/handler_shards.go
+++ b/services/kinesis/handler_shards.go
@@ -2,9 +2,11 @@ package kinesis
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type jsonShardFilter struct {
@@ -67,7 +69,7 @@ func (h *Handler) handleListShards(
body []byte,
) (any, error) {
var req jsonListShardsReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
diff --git a/services/kinesis/handler_stream_encryption.go b/services/kinesis/handler_stream_encryption.go
index a2fb0977f1..86445110f3 100644
--- a/services/kinesis/handler_stream_encryption.go
+++ b/services/kinesis/handler_stream_encryption.go
@@ -2,8 +2,10 @@ package kinesis
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type jsonEncryptionReq struct {
@@ -19,7 +21,7 @@ func (h *Handler) handleStartStreamEncryption(
body []byte,
) (any, error) {
var req jsonEncryptionReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -41,7 +43,7 @@ func (h *Handler) handleStopStreamEncryption(
body []byte,
) (any, error) {
var req jsonEncryptionReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
diff --git a/services/kinesis/handler_stream_modes.go b/services/kinesis/handler_stream_modes.go
index a84e903a35..4e46606446 100644
--- a/services/kinesis/handler_stream_modes.go
+++ b/services/kinesis/handler_stream_modes.go
@@ -2,8 +2,10 @@ package kinesis
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// jsonUpdateStreamWarmThroughputReq mirrors UpdateStreamWarmThroughputInput
@@ -35,7 +37,7 @@ func (h *Handler) handleUpdateStreamMode(ctx context.Context, _ *http.Request, b
WarmThroughputMiBps *int `json:"WarmThroughputMiBps"`
StreamARN string `json:"StreamARN"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
if req.StreamModeDetails == nil {
@@ -55,7 +57,7 @@ func (h *Handler) handleUpdateStreamWarmThroughput(
body []byte,
) (any, error) {
var req jsonUpdateStreamWarmThroughputReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
diff --git a/services/kinesis/handler_stream_retention.go b/services/kinesis/handler_stream_retention.go
index fb358338a9..a897b65d22 100644
--- a/services/kinesis/handler_stream_retention.go
+++ b/services/kinesis/handler_stream_retention.go
@@ -2,8 +2,10 @@ package kinesis
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type jsonRetentionPeriodReq struct {
@@ -18,7 +20,7 @@ func (h *Handler) handleIncreaseStreamRetentionPeriod(
body []byte,
) (any, error) {
var req jsonRetentionPeriodReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -40,7 +42,7 @@ func (h *Handler) handleDecreaseStreamRetentionPeriod(
body []byte,
) (any, error) {
var req jsonRetentionPeriodReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
diff --git a/services/kinesis/handler_streams.go b/services/kinesis/handler_streams.go
index 903e7f3552..4f41b2814b 100644
--- a/services/kinesis/handler_streams.go
+++ b/services/kinesis/handler_streams.go
@@ -2,10 +2,11 @@ package kinesis
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -109,7 +110,7 @@ func (h *Handler) handleCreateStream(
body []byte,
) (any, error) {
var req jsonCreateStreamReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -181,7 +182,7 @@ func (h *Handler) handleDeleteStream(
body []byte,
) (any, error) {
var req jsonDeleteStreamReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -224,7 +225,7 @@ func (h *Handler) handleDescribeStream(
body []byte,
) (any, error) {
var req jsonDescribeStreamReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -282,7 +283,7 @@ func (h *Handler) handleDescribeStreamSummary(
body []byte,
) (any, error) {
var req jsonDescribeStreamReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -343,7 +344,7 @@ func (h *Handler) handleListStreams(
body []byte,
) (any, error) {
var req jsonListStreamsReq
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
out, err := h.Backend.ListStreams(ctx, &ListStreamsInput{
Limit: req.Limit,
diff --git a/services/kinesis/handler_tags.go b/services/kinesis/handler_tags.go
index 009b54534e..e2b38c0c62 100644
--- a/services/kinesis/handler_tags.go
+++ b/services/kinesis/handler_tags.go
@@ -3,12 +3,13 @@ package kinesis
import (
"cmp"
"context"
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"slices"
"github.com/blackbirdworks/gopherstack/pkgs/collections"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -77,7 +78,7 @@ func (h *Handler) handleAddTagsToStream(
body []byte,
) (any, error) {
var req handleAddTagsToStreamInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -124,7 +125,7 @@ func (h *Handler) handleRemoveTagsFromStream(
body []byte,
) (any, error) {
var req handleRemoveTagsFromStreamInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -151,7 +152,7 @@ func (h *Handler) handleListTagsForStream(
body []byte,
) (any, error) {
var req listTagsForStreamReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -205,7 +206,7 @@ func (h *Handler) handleTagResource(
body []byte,
) (any, error) {
var req jsonTagResourceReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -243,7 +244,7 @@ func (h *Handler) handleUntagResource(
body []byte,
) (any, error) {
var req jsonUntagResourceReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
@@ -263,7 +264,7 @@ func (h *Handler) handleListTagsForResource(
body []byte,
) (any, error) {
var req jsonResourceARNReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrInvalidArgument
}
diff --git a/services/kinesis/handler_test.go b/services/kinesis/handler_test.go
index 5c1334b8ce..639c6bd532 100644
--- a/services/kinesis/handler_test.go
+++ b/services/kinesis/handler_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"crypto/md5"
- "encoding/json"
+ "encoding/json/v2"
"math/big"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -57,7 +58,7 @@ func doRequest(t *testing.T, h *kinesis.Handler, action string, body any) *httpt
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -159,7 +160,7 @@ func doParityRequest(t *testing.T, h *kinesis.Handler, action string, body any)
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -255,7 +256,7 @@ func TestKinesisHandler_ErrorResponses(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, errResp.Type)
}
})
@@ -312,7 +313,7 @@ func TestExtractResource(t *testing.T) {
e := echo.New()
// Valid body
- body, _ := json.Marshal(map[string]string{"StreamName": "my-stream"})
+ body, _ := json.Marshal(map[string]string{"StreamName": "my-stream"}, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
c := e.NewContext(req, httptest.NewRecorder())
diff --git a/services/kinesis/inspect_test.go b/services/kinesis/inspect_test.go
index 7af71a8125..88eff6fbe3 100644
--- a/services/kinesis/inspect_test.go
+++ b/services/kinesis/inspect_test.go
@@ -2,7 +2,7 @@ package kinesis_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -20,6 +20,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/sdktest"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
@@ -79,7 +80,7 @@ func (e *inspectEnv) get(t *testing.T, path string, out any) int {
require.NoError(t, err)
if out != nil && resp.StatusCode == http.StatusOK {
- require.NoError(t, json.Unmarshal(body, out))
+ require.NoError(t, json.Unmarshal(body, out, jsonx.V1()))
}
return resp.StatusCode
diff --git a/services/kinesis/monitoring_test.go b/services/kinesis/monitoring_test.go
index 3526c21a7e..527209fb73 100644
--- a/services/kinesis/monitoring_test.go
+++ b/services/kinesis/monitoring_test.go
@@ -1,12 +1,14 @@
package kinesis_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestEnhancedMonitoring(t *testing.T) {
@@ -33,7 +35,7 @@ func TestEnhancedMonitoring(t *testing.T) {
CurrentShardLevelMetrics []string `json:"CurrentShardLevelMetrics"`
DesiredShardLevelMetrics []string `json:"DesiredShardLevelMetrics"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &enableResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &enableResp, jsonx.V1()))
assert.Equal(t, streamName, enableResp.StreamName)
assert.Empty(t, enableResp.CurrentShardLevelMetrics)
assert.ElementsMatch(t, []string{"IncomingBytes", "OutgoingRecords"}, enableResp.DesiredShardLevelMetrics)
@@ -50,7 +52,7 @@ func TestEnhancedMonitoring(t *testing.T) {
CurrentShardLevelMetrics []string `json:"CurrentShardLevelMetrics"`
DesiredShardLevelMetrics []string `json:"DesiredShardLevelMetrics"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &disableResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &disableResp, jsonx.V1()))
assert.ElementsMatch(t, []string{"IncomingBytes", "OutgoingRecords"}, disableResp.CurrentShardLevelMetrics)
assert.Equal(t, []string{"OutgoingRecords"}, disableResp.DesiredShardLevelMetrics)
}
@@ -91,7 +93,7 @@ func TestEnhancedMonitoring_StreamARN(t *testing.T) {
var resp struct {
StreamARN string `json:"StreamARN"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.StreamARN)
assert.Contains(t, resp.StreamARN, streamName)
})
diff --git a/services/kinesis/page_token.go b/services/kinesis/page_token.go
index 3c38066e01..ef18ce197b 100644
--- a/services/kinesis/page_token.go
+++ b/services/kinesis/page_token.go
@@ -2,8 +2,10 @@ package kinesis
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// pageTokenLifetime is the documented lifetime of ListShards and
@@ -34,7 +36,7 @@ func encodePageToken(kind string, stream *Stream, cursor string, now time.Time)
Cursor: cursor,
IssuedAt: now.UnixNano(),
Created: stream.CreatedAt.UnixNano(),
- })
+ }, jsonx.V1())
return base64.RawURLEncoding.EncodeToString(raw)
}
@@ -48,7 +50,7 @@ func decodePageToken(kind, token string, now time.Time) (pageToken, error) {
}
var pt pageToken
- if err = json.Unmarshal(raw, &pt); err != nil || pt.Kind != kind || pt.Stream == "" {
+ if err = json.Unmarshal(raw, &pt, jsonx.V1()); err != nil || pt.Kind != kind || pt.Stream == "" {
return pageToken{}, ErrInvalidArgument
}
diff --git a/services/kinesis/persistence.go b/services/kinesis/persistence.go
index d1466392c5..11ef9137b7 100644
--- a/services/kinesis/persistence.go
+++ b/services/kinesis/persistence.go
@@ -2,11 +2,13 @@ package kinesis
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"slices"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -38,7 +40,7 @@ const kinesisSnapshotVersion = 1
// persisted the same way it always was.
type backendSnapshot struct {
MinimumThroughputBillingCommitment MinimumThroughputBillingCommitmentOutput `json:"minimumThroughputBillingCommitment"`
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ResourcePolicies map[string]map[string]string `json:"resourcePolicies,omitempty"`
ChannelBuffers map[string]channelBufferSnapshot `json:"channelBuffers,omitempty"`
AccountID string `json:"accountID"`
@@ -122,7 +124,7 @@ func (stream *Stream) MarshalJSON() ([]byte, error) {
stream.mu.RLock("Snapshot")
defer stream.mu.RUnlock()
- return json.Marshal((*streamAlias)(stream))
+ return json.Marshal((*streamAlias)(stream), jsonx.V1())
}
// Snapshot serialises the backend state to JSON.
@@ -150,7 +152,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
MinimumThroughputBillingCommitment: b.minimumThroughputBillingCommitment,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "kinesis: snapshot serialization failed", "error", err)
diff --git a/services/kinesis/persistence_roundtrip_test.go b/services/kinesis/persistence_roundtrip_test.go
index 42ba51fd65..9a3315c401 100644
--- a/services/kinesis/persistence_roundtrip_test.go
+++ b/services/kinesis/persistence_roundtrip_test.go
@@ -2,13 +2,16 @@ package kinesis //nolint:testpackage // reuses ctxRegion (isolation_test.go) for
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"testing/synctest"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestInMemoryBackend_FullStateSnapshotRestoreRoundTrip exercises every kind
@@ -185,12 +188,12 @@ func TestInMemoryBackend_Restore_IncompatibleVersion_ResetsEmpty(t *testing.T) {
// Corrupt just the version field to simulate a snapshot from an
// incompatible (older/newer) build.
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &raw))
- bumped, err := json.Marshal(kinesisSnapshotVersion + 1)
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &raw, jsonx.V1()))
+ bumped, err := json.Marshal(kinesisSnapshotVersion+1, jsonx.V1())
require.NoError(t, err)
raw["version"] = bumped
- mutated, err := json.Marshal(raw)
+ mutated, err := json.Marshal(raw, jsonx.V1())
require.NoError(t, err)
fresh := NewInMemoryBackendWithConfig("000000000000", "us-east-1")
diff --git a/services/kinesis/persistence_test.go b/services/kinesis/persistence_test.go
index 138f736761..7ae48b5f0f 100644
--- a/services/kinesis/persistence_test.go
+++ b/services/kinesis/persistence_test.go
@@ -2,7 +2,7 @@ package kinesis_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -318,7 +319,7 @@ func TestTags_SurvivePersistenceRestore(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
found := false
for _, kv := range resp.Tags {
diff --git a/services/kinesis/records_get_test.go b/services/kinesis/records_get_test.go
index 596fb9a82c..e0e93e0352 100644
--- a/services/kinesis/records_get_test.go
+++ b/services/kinesis/records_get_test.go
@@ -3,7 +3,7 @@ package kinesis_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -48,7 +49,7 @@ func TestGetRecords_SizeCap_ExcludesPartitionKey(t *testing.T) {
} `json:"Shards"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
require.NotEmpty(t, descResp.StreamDescription.Shards)
shardID := descResp.StreamDescription.Shards[0].ShardID
@@ -75,7 +76,7 @@ func TestGetRecords_SizeCap_ExcludesPartitionKey(t *testing.T) {
var putResp struct {
FailedRecordCount int `json:"FailedRecordCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
assert.Equal(t, 0, putResp.FailedRecordCount)
rec = doRequest(t, h, "GetShardIterator", map[string]any{
@@ -88,7 +89,7 @@ func TestGetRecords_SizeCap_ExcludesPartitionKey(t *testing.T) {
var iterResp struct {
ShardIterator string `json:"ShardIterator"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &iterResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &iterResp, jsonx.V1()))
require.NotEmpty(t, iterResp.ShardIterator)
rec = doRequest(t, h, "GetRecords", map[string]any{
@@ -101,7 +102,7 @@ func TestGetRecords_SizeCap_ExcludesPartitionKey(t *testing.T) {
PartitionKey string `json:"PartitionKey"`
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Len(
t,
getResp.Records,
@@ -134,7 +135,7 @@ func TestGetRecords_SizeCap_ExcludesPartitionKey(t *testing.T) {
} `json:"Shards"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
require.NotEmpty(t, descResp.StreamDescription.Shards)
shardID := descResp.StreamDescription.Shards[0].ShardID
@@ -166,7 +167,7 @@ func TestGetRecords_SizeCap_ExcludesPartitionKey(t *testing.T) {
var iterResp struct {
ShardIterator string `json:"ShardIterator"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &iterResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &iterResp, jsonx.V1()))
require.NotEmpty(t, iterResp.ShardIterator)
rec = doRequest(t, h, "GetRecords", map[string]any{
@@ -179,7 +180,7 @@ func TestGetRecords_SizeCap_ExcludesPartitionKey(t *testing.T) {
Data []byte `json:"Data"`
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.LessOrEqual(t, len(getResp.Records), 2,
"data cap (10 MiB) should stop GetRecords before the 3rd 4-MiB record")
},
@@ -367,7 +368,7 @@ func TestPutAndGetRecords(t *testing.T) {
} `json:"Shards"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
require.NotEmpty(t, descResp.StreamDescription.Shards)
shardID := descResp.StreamDescription.Shards[0].ShardID
@@ -383,7 +384,7 @@ func TestPutAndGetRecords(t *testing.T) {
ShardID string `json:"ShardId"`
SequenceNumber string `json:"SequenceNumber"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
assert.NotEmpty(t, putResp.ShardID)
assert.NotEmpty(t, putResp.SequenceNumber)
firstSeq := putResp.SequenceNumber
@@ -405,7 +406,7 @@ func TestPutAndGetRecords(t *testing.T) {
} `json:"Records"`
FailedRecordCount int `json:"FailedRecordCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &batchResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &batchResp, jsonx.V1()))
assert.Equal(t, 0, batchResp.FailedRecordCount)
assert.Len(t, batchResp.Records, 2)
@@ -420,7 +421,7 @@ func TestPutAndGetRecords(t *testing.T) {
var iterResp struct {
ShardIterator string `json:"ShardIterator"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &iterResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &iterResp, jsonx.V1()))
assert.NotEmpty(t, iterResp.ShardIterator)
// GetRecords
@@ -438,7 +439,7 @@ func TestPutAndGetRecords(t *testing.T) {
Data []byte `json:"Data"`
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Len(t, getResp.Records, 3) // 1 + 2 batch
assert.NotEmpty(t, getResp.NextShardIterator)
@@ -454,7 +455,7 @@ func TestPutAndGetRecords(t *testing.T) {
var atSeqIterResp struct {
ShardIterator string `json:"ShardIterator"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &atSeqIterResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &atSeqIterResp, jsonx.V1()))
rec = doRequest(t, h, "GetRecords", map[string]any{
"ShardIterator": atSeqIterResp.ShardIterator,
@@ -467,7 +468,7 @@ func TestPutAndGetRecords(t *testing.T) {
SequenceNumber string `json:"SequenceNumber"`
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &atSeqResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &atSeqResp, jsonx.V1()))
// AT_SEQUENCE_NUMBER starts at the given record (inclusive)
require.NotEmpty(t, atSeqResp.Records)
assert.Equal(t, firstSeq, atSeqResp.Records[0].SequenceNumber)
@@ -484,7 +485,7 @@ func TestPutAndGetRecords(t *testing.T) {
var afterIterResp struct {
ShardIterator string `json:"ShardIterator"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &afterIterResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &afterIterResp, jsonx.V1()))
rec = doRequest(t, h, "GetRecords", map[string]any{
"ShardIterator": afterIterResp.ShardIterator,
@@ -497,7 +498,7 @@ func TestPutAndGetRecords(t *testing.T) {
SequenceNumber string `json:"SequenceNumber"`
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &afterSeqResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &afterSeqResp, jsonx.V1()))
// AFTER_SEQUENCE_NUMBER skips the given record
assert.Len(t, afterSeqResp.Records, 2)
@@ -512,7 +513,7 @@ func TestPutAndGetRecords(t *testing.T) {
var latestIterResp struct {
ShardIterator string `json:"ShardIterator"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &latestIterResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &latestIterResp, jsonx.V1()))
rec = doRequest(t, h, "GetRecords", map[string]any{
"ShardIterator": latestIterResp.ShardIterator,
@@ -522,7 +523,7 @@ func TestPutAndGetRecords(t *testing.T) {
var latestResp struct {
Records []any `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &latestResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &latestResp, jsonx.V1()))
assert.Empty(t, latestResp.Records) // No new records since iterator was created
})
}
@@ -554,7 +555,7 @@ func TestSequenceNumberOrdering(t *testing.T) {
} `json:"Shards"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
shardID := descResp.StreamDescription.Shards[0].ShardID
// Put 5 records
@@ -570,7 +571,7 @@ func TestSequenceNumberOrdering(t *testing.T) {
var putResp struct {
SequenceNumber string `json:"SequenceNumber"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
seqNums[i] = putResp.SequenceNumber
}
@@ -591,7 +592,7 @@ func TestSequenceNumberOrdering(t *testing.T) {
var iterResp struct {
ShardIterator string `json:"ShardIterator"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &iterResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &iterResp, jsonx.V1()))
rec = doRequest(t, h, "GetRecords", map[string]any{
"ShardIterator": iterResp.ShardIterator,
@@ -604,7 +605,7 @@ func TestSequenceNumberOrdering(t *testing.T) {
SequenceNumber string `json:"SequenceNumber"`
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
require.Len(t, getResp.Records, 5)
for i, r := range getResp.Records {
@@ -1153,7 +1154,7 @@ func TestGetRecords_MillisBehindLatest_ViaHandler(t *testing.T) {
var iterResp struct {
ShardIterator string `json:"ShardIterator"`
}
- require.NoError(t, json.Unmarshal(iterRec.Body.Bytes(), &iterResp))
+ require.NoError(t, json.Unmarshal(iterRec.Body.Bytes(), &iterResp, jsonx.V1()))
// Fetch 1 record (leaving 2 behind).
rec = doRequest(t, h, "GetRecords", map[string]any{
@@ -1166,7 +1167,7 @@ func TestGetRecords_MillisBehindLatest_ViaHandler(t *testing.T) {
Records []any `json:"Records"`
MillisBehindLatest int64 `json:"MillisBehindLatest"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Len(t, getResp.Records, 1)
// Should be behind the last record, not just the next one.
assert.GreaterOrEqual(t, getResp.MillisBehindLatest, int64(0))
diff --git a/services/kinesis/records_test.go b/services/kinesis/records_test.go
index 62092defba..9cde186913 100644
--- a/services/kinesis/records_test.go
+++ b/services/kinesis/records_test.go
@@ -2,7 +2,7 @@ package kinesis_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"math/big"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -73,7 +74,7 @@ func TestPutRecord_ByARN(t *testing.T) {
ShardID string `json:"ShardId"`
SequenceNumber string `json:"SequenceNumber"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.ShardID)
assert.NotEmpty(t, resp.SequenceNumber)
})
@@ -133,7 +134,7 @@ func TestPutRecords_ByARN(t *testing.T) {
} `json:"Records"`
FailedRecordCount int `json:"FailedRecordCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, 0, resp.FailedRecordCount)
assert.Len(t, resp.Records, 2)
})
@@ -257,7 +258,7 @@ func TestPutRecordsNotFound(t *testing.T) {
var resp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp.Type)
}
@@ -288,7 +289,7 @@ func TestMultipleShardRouting(t *testing.T) {
var putResp struct {
ShardID string `json:"ShardId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
shardIDs[putResp.ShardID] = true
}
@@ -343,7 +344,7 @@ func TestPutRecords_OversizeRecordReturnsValidationException(t *testing.T) {
} `json:"Records"`
FailedRecordCount int `json:"FailedRecordCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, 1, resp.FailedRecordCount)
require.Len(t, resp.Records, 2)
@@ -386,7 +387,7 @@ func TestPutRecords_ThrottledRecordReturnsProvisionedThroughputException(t *test
} `json:"Records"`
FailedRecordCount int `json:"FailedRecordCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, 1, resp.FailedRecordCount)
assert.Equal(t, "ProvisionedThroughputExceededException", resp.Records[0].ErrorCode)
@@ -422,7 +423,7 @@ func TestPutRecords_AllValidRecordsSucceed(t *testing.T) {
} `json:"Records"`
FailedRecordCount int `json:"FailedRecordCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, 0, resp.FailedRecordCount)
for _, r := range resp.Records {
assert.Empty(t, r.ErrorCode)
diff --git a/services/kinesis/resharding_shard_count_test.go b/services/kinesis/resharding_shard_count_test.go
index 90c205befb..af8b9c98ac 100644
--- a/services/kinesis/resharding_shard_count_test.go
+++ b/services/kinesis/resharding_shard_count_test.go
@@ -2,7 +2,7 @@ package kinesis_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -70,7 +71,7 @@ func TestUpdateShardCount(t *testing.T) {
CurrentShardCount int `json:"CurrentShardCount"`
TargetShardCount int `json:"TargetShardCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, streamName, resp.StreamName)
assert.Equal(t, tt.wantCurrentCount, resp.CurrentShardCount)
assert.Equal(t, tt.wantTargetCount, resp.TargetShardCount)
@@ -82,7 +83,7 @@ func TestUpdateShardCount(t *testing.T) {
var shardsResp struct {
Shards []any `json:"Shards"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &shardsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &shardsResp, jsonx.V1()))
assert.Len(t, shardsResp.Shards, tt.targetShards)
})
})
@@ -296,7 +297,7 @@ func TestUpdateShardCount_ViaHandler_OpenShardsOnly(t *testing.T) {
CurrentShardCount int `json:"CurrentShardCount"`
TargetShardCount int `json:"TargetShardCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Equal(t, 2, updateResp.CurrentShardCount)
assert.Equal(t, 4, updateResp.TargetShardCount)
@@ -307,7 +308,7 @@ func TestUpdateShardCount_ViaHandler_OpenShardsOnly(t *testing.T) {
var listResp struct {
Shards []any `json:"Shards"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp.Shards, 4)
})
}
diff --git a/services/kinesis/resharding_test.go b/services/kinesis/resharding_test.go
index 05e132e016..4668eef711 100644
--- a/services/kinesis/resharding_test.go
+++ b/services/kinesis/resharding_test.go
@@ -2,7 +2,7 @@ package kinesis_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"math/big"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -193,7 +194,7 @@ func TestScalingCap_HandlerValidationException(t *testing.T) {
Type string `json:"__type"`
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp.Type)
assert.NotEmpty(t, resp.Message)
}
@@ -494,7 +495,7 @@ func TestMergeShards_ARNSupport(t *testing.T) {
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
require.Len(t, descResp.StreamDescription.Shards, 2)
streamARN := descResp.StreamDescription.StreamARN
@@ -539,7 +540,7 @@ func TestSplitShard_ARNSupport(t *testing.T) {
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
require.Len(t, descResp.StreamDescription.Shards, 1)
rec = doRequest(t, h, "SplitShard", map[string]any{
@@ -574,7 +575,7 @@ func TestMergeShards_OnDemandRejected(t *testing.T) {
var resp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidArgumentException", resp.Type)
})
}
@@ -602,7 +603,7 @@ func TestSplitShard_OnDemandRejected(t *testing.T) {
var resp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidArgumentException", resp.Type)
})
}
@@ -659,7 +660,7 @@ func TestMergeShards(t *testing.T) {
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
require.Len(t, descResp.StreamDescription.Shards, 2)
shard0 := descResp.StreamDescription.Shards[0].ShardID
@@ -690,7 +691,7 @@ func TestMergeShards(t *testing.T) {
} `json:"Shards"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp2, jsonx.V1()))
assert.Len(t, descResp2.StreamDescription.Shards, 3)
})
}
@@ -725,7 +726,7 @@ func TestMergeAndSplitShardIDs(t *testing.T) {
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(r.Body.Bytes(), &d))
+ require.NoError(t, json.Unmarshal(r.Body.Bytes(), &d, jsonx.V1()))
return d.StreamDescription.Shards
}
@@ -739,7 +740,7 @@ func TestMergeAndSplitShardIDs(t *testing.T) {
Shards []shardEntry `json:"Shards"`
}
- require.NoError(t, json.Unmarshal(r.Body.Bytes(), &d))
+ require.NoError(t, json.Unmarshal(r.Body.Bytes(), &d, jsonx.V1()))
return d.Shards
}
@@ -850,7 +851,7 @@ func TestMergeShards_Errors(t *testing.T) {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(got.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(got.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, errResp.Type)
}
})
@@ -884,7 +885,7 @@ func TestSplitShard(t *testing.T) {
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
require.Len(t, descResp.StreamDescription.Shards, 1)
shardID := descResp.StreamDescription.Shards[0].ShardID
@@ -903,7 +904,7 @@ func TestSplitShard(t *testing.T) {
// After splitting 1 → 2, expect 3 total: 1 closed parent + 2 open children.
rec = doRequest(t, h, "DescribeStream", map[string]any{"StreamName": "split-stream"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Len(t, descResp.StreamDescription.Shards, 3)
})
}
@@ -961,7 +962,7 @@ func TestSplitShard_Errors(t *testing.T) {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(got.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(got.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.errType, errResp.Type)
}
})
diff --git a/services/kinesis/resource_policy_test.go b/services/kinesis/resource_policy_test.go
index 9de4ed1341..aa5bed2fe8 100644
--- a/services/kinesis/resource_policy_test.go
+++ b/services/kinesis/resource_policy_test.go
@@ -1,13 +1,14 @@
package kinesis_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -78,7 +79,7 @@ func TestResourcePolicyLifecycle(t *testing.T) {
Policy string `json:"Policy"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &policyResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &policyResp, jsonx.V1()))
assert.Equal(t, tt.policy, policyResp.Policy)
// DeleteResourcePolicy.
@@ -110,6 +111,6 @@ func TestDeleteResourcePolicy_NotFound(t *testing.T) {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp.Type)
}
diff --git a/services/kinesis/ring_buffer.go b/services/kinesis/ring_buffer.go
index 3457ae6d7e..0b290e299d 100644
--- a/services/kinesis/ring_buffer.go
+++ b/services/kinesis/ring_buffer.go
@@ -1,9 +1,11 @@
package kinesis
import (
- "encoding/json"
+ "encoding/json/v2"
"sort"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// shardRecords is a bounded ring buffer for Kinesis shard records.
@@ -82,7 +84,7 @@ func (r *shardRecords) MarshalJSON() ([]byte, error) {
out[i] = r.at(i)
}
- return json.Marshal(out)
+ return json.Marshal(out, jsonx.V1())
}
// UnmarshalJSON restores the ring buffer from a JSON array.
@@ -90,7 +92,7 @@ func (r *shardRecords) MarshalJSON() ([]byte, error) {
// Any previously held pointers are cleared to allow GC.
func (r *shardRecords) UnmarshalJSON(data []byte) error {
var records []*Record
- if err := json.Unmarshal(data, &records); err != nil {
+ if err := json.Unmarshal(data, &records, jsonx.V1()); err != nil {
return err
}
diff --git a/services/kinesis/shard_iterators.go b/services/kinesis/shard_iterators.go
index 9530fd71a5..a23b4c5408 100644
--- a/services/kinesis/shard_iterators.go
+++ b/services/kinesis/shard_iterators.go
@@ -3,13 +3,15 @@ package kinesis
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// iteratorToken encodes a shard iterator as a base64 JSON token.
func encodeIterator(it *ShardIterator) (string, error) {
- data, err := json.Marshal(it)
+ data, err := json.Marshal(it, jsonx.V1())
if err != nil {
return "", err
}
@@ -25,7 +27,7 @@ func decodeIterator(token string, now time.Time) (*ShardIterator, error) {
}
var it ShardIterator
- if err = json.Unmarshal(data, &it); err != nil {
+ if err = json.Unmarshal(data, &it, jsonx.V1()); err != nil {
return nil, ErrShardIteratorExpired
}
diff --git a/services/kinesis/shard_iterators_test.go b/services/kinesis/shard_iterators_test.go
index 4c6cc159a2..1ad01efb86 100644
--- a/services/kinesis/shard_iterators_test.go
+++ b/services/kinesis/shard_iterators_test.go
@@ -3,7 +3,7 @@ package kinesis_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -60,7 +61,7 @@ func TestGetShardIterator_ByARN(t *testing.T) {
var resp struct {
ShardIterator string `json:"ShardIterator"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.ShardIterator)
})
}
@@ -203,7 +204,7 @@ func TestGetShardIteratorBadIteratorType(t *testing.T) {
} `json:"Shards"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
shardID := descResp.StreamDescription.Shards[0].ShardID
rec = doRequest(t, h, "GetShardIterator", map[string]any{
@@ -264,7 +265,7 @@ func testGetShardIteratorAtTimestamp(t *testing.T) {
} `json:"Shards"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
require.Len(t, descResp.StreamDescription.Shards, 1)
shardID := descResp.StreamDescription.Shards[0].ShardID
@@ -288,7 +289,7 @@ func testGetShardIteratorAtTimestamp(t *testing.T) {
var iterResp struct {
ShardIterator string `json:"ShardIterator"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &iterResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &iterResp, jsonx.V1()))
assert.NotEmpty(t, iterResp.ShardIterator)
// GetRecords should return the record.
@@ -302,7 +303,7 @@ func testGetShardIteratorAtTimestamp(t *testing.T) {
Data []byte `json:"Data"`
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Len(t, getResp.Records, 1)
assert.Equal(t, []byte("hello"), getResp.Records[0].Data)
}
@@ -328,7 +329,7 @@ func TestGetShardIterator_AtTimestampRequiresTimestamp(t *testing.T) {
} `json:"Shards"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
shardID := descResp.StreamDescription.Shards[0].ShardID
rec = doRequest(t, h, "GetShardIterator", map[string]any{
@@ -342,7 +343,7 @@ func TestGetShardIterator_AtTimestampRequiresTimestamp(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidArgumentException", errResp.Type)
}
@@ -423,7 +424,7 @@ func testGetRecordsNextShardIteratorHasExpiry(t *testing.T) {
require.NoError(t, err)
var next kinesis.ShardIterator
- require.NoError(t, json.Unmarshal(raw, &next))
+ require.NoError(t, json.Unmarshal(raw, &next, jsonx.V1()))
assert.False(t, next.CreatedAt.IsZero(),
"NextShardIterator must carry a CreatedAt so ExpiredIteratorException can ever fire on a chained token")
diff --git a/services/kinesis/shards_test.go b/services/kinesis/shards_test.go
index e201c65f92..a0fdfe8a23 100644
--- a/services/kinesis/shards_test.go
+++ b/services/kinesis/shards_test.go
@@ -2,7 +2,7 @@ package kinesis_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -31,7 +32,7 @@ func TestListShards_NextTokenStreamNameMutuallyExclusive(t *testing.T) {
var page1 struct {
NextToken string `json:"NextToken"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
require.NotEmpty(t, page1.NextToken, "expected NextToken for pagination")
tests := []struct {
@@ -65,7 +66,7 @@ func TestListShards_NextTokenStreamNameMutuallyExclusive(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidArgumentException", errResp.Type)
})
}
@@ -90,7 +91,7 @@ func TestListShards_NextTokenAlone_Succeeds(t *testing.T) {
ShardID string `json:"ShardId"`
} `json:"Shards"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
require.NotEmpty(t, page1.NextToken)
// Use NextToken alone (no StreamName) — should succeed because the token embeds stream context.
@@ -105,7 +106,7 @@ func TestListShards_NextTokenAlone_Succeeds(t *testing.T) {
ShardID string `json:"ShardId"`
} `json:"Shards"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
assert.NotEmpty(t, page2.Shards)
}
@@ -538,7 +539,7 @@ func TestListShards(t *testing.T) {
ShardID string `json:"ShardId"`
} `json:"Shards"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listShardsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listShardsResp, jsonx.V1()))
assert.Len(t, listShardsResp.Shards, 3)
}
@@ -639,7 +640,7 @@ func TestListShards_MaxResults_ViaHandler(t *testing.T) {
NextToken string `json:"NextToken"`
Shards []any `json:"Shards"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Shards, 2)
assert.NotEmpty(t, resp.NextToken)
@@ -654,7 +655,7 @@ func TestListShards_MaxResults_ViaHandler(t *testing.T) {
NextToken string `json:"NextToken"`
Shards []any `json:"Shards"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp2, jsonx.V1()))
assert.Len(t, resp2.Shards, 2)
assert.Empty(t, resp2.NextToken, "no more pages")
}
@@ -927,7 +928,7 @@ func TestListShards_ShardFilterType_TimestampRequired(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidArgumentException", errResp.Type)
})
}
diff --git a/services/kinesis/stream_encryption_test.go b/services/kinesis/stream_encryption_test.go
index 1b0078cc38..064c3b7c78 100644
--- a/services/kinesis/stream_encryption_test.go
+++ b/services/kinesis/stream_encryption_test.go
@@ -2,7 +2,7 @@ package kinesis_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -44,7 +45,7 @@ func testStartEncryptionARNSupport(t *testing.T) {
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
rec = doRequest(t, h, "StartStreamEncryption", map[string]any{
"StreamARN": descResp.StreamDescription.StreamARN,
@@ -85,7 +86,7 @@ func TestStopEncryption_ResetsFields(t *testing.T) {
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "NONE", resp.StreamDescription.EncryptionType)
}
@@ -208,7 +209,7 @@ func TestStreamEncryption_Errors(t *testing.T) {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(got.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(got.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, errResp.Type)
}
})
diff --git a/services/kinesis/stream_modes_test.go b/services/kinesis/stream_modes_test.go
index 480835ddef..6ec18ac82d 100644
--- a/services/kinesis/stream_modes_test.go
+++ b/services/kinesis/stream_modes_test.go
@@ -2,7 +2,7 @@ package kinesis_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -155,7 +156,7 @@ func TestUpdateStreamMode_Valid(t *testing.T) {
StreamARN string `json:"StreamARN"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp, jsonx.V1()))
rec3 := doRequest(t, h, "UpdateStreamMode", map[string]any{
"StreamARN": descResp.StreamDescription.StreamARN,
@@ -174,7 +175,7 @@ func TestUpdateStreamMode_Valid(t *testing.T) {
} `json:"StreamModeDetails"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &verifyResp))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &verifyResp, jsonx.V1()))
require.NotNil(t, verifyResp.StreamDescription.StreamModeDetails)
assert.Equal(t, tt.wantMode, verifyResp.StreamDescription.StreamModeDetails.StreamMode)
})
diff --git a/services/kinesis/stream_retention_test.go b/services/kinesis/stream_retention_test.go
index 2bf83bc98b..b782d71602 100644
--- a/services/kinesis/stream_retention_test.go
+++ b/services/kinesis/stream_retention_test.go
@@ -2,13 +2,14 @@ package kinesis_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -199,7 +200,7 @@ func TestHandleIncreaseStreamRetentionPeriod(t *testing.T) {
RetentionPeriodHours int `json:"RetentionPeriodHours"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, 48, descResp.StreamDescription.RetentionPeriodHours)
}
@@ -231,7 +232,7 @@ func TestHandleDecreaseStreamRetentionPeriod(t *testing.T) {
RetentionPeriodHours int `json:"RetentionPeriodHours"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, 24, descResp.StreamDescription.RetentionPeriodHours)
}
diff --git a/services/kinesis/streams_describe_test.go b/services/kinesis/streams_describe_test.go
index f589540cf9..ce54fe9c13 100644
--- a/services/kinesis/streams_describe_test.go
+++ b/services/kinesis/streams_describe_test.go
@@ -2,7 +2,8 @@ package kinesis_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -55,7 +57,7 @@ func TestDescribeStream_ByARN(t *testing.T) {
StreamARN string `json:"StreamARN"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "arn-describe-stream", resp.StreamDescription.StreamName)
assert.Equal(t, desc.StreamARN, resp.StreamDescription.StreamARN)
})
@@ -100,7 +102,7 @@ func TestDescribeStreamSummary_ByARN(t *testing.T) {
StreamARN string `json:"StreamARN"`
} `json:"StreamDescriptionSummary"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "arn-summary-stream", resp.StreamDescriptionSummary.StreamName)
assert.Equal(t, desc.StreamARN, resp.StreamDescriptionSummary.StreamARN)
})
@@ -142,7 +144,7 @@ func TestDescribeStream_EncryptionFields(t *testing.T) {
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "KMS", resp.StreamDescription.EncryptionType)
assert.Equal(t, "alias/my-key-id", resp.StreamDescription.KeyID)
})
@@ -181,7 +183,7 @@ func TestDescribeStreamSummary_EncryptionFields(t *testing.T) {
} `json:"StreamDescriptionSummary"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "KMS", resp.StreamDescriptionSummary.EncryptionType)
assert.Equal(t, "alias/summary-key-id", resp.StreamDescriptionSummary.KeyID)
})
@@ -208,7 +210,10 @@ func TestDescribeStreamSummary_OpenShardCountAndConsumerCount(t *testing.T) {
} `json:"StreamDescription"`
}
- require.NoError(t, json.NewDecoder(strings.NewReader(descRec.Body.String())).Decode(&descResp))
+ require.NoError(
+ t,
+ json.UnmarshalDecode(jsontext.NewDecoder(strings.NewReader(descRec.Body.String())), &descResp, jsonx.V1()),
+ )
regRec := doParityRequest(t, h, "RegisterStreamConsumer", map[string]any{
"StreamARN": descResp.StreamDescription.StreamARN,
@@ -228,7 +233,10 @@ func TestDescribeStreamSummary_OpenShardCountAndConsumerCount(t *testing.T) {
} `json:"StreamDescriptionSummary"`
}
- require.NoError(t, json.NewDecoder(strings.NewReader(sumRec.Body.String())).Decode(&sumResp))
+ require.NoError(
+ t,
+ json.UnmarshalDecode(jsontext.NewDecoder(strings.NewReader(sumRec.Body.String())), &sumResp, jsonx.V1()),
+ )
assert.Equal(t, 3, sumResp.StreamDescriptionSummary.OpenShardCount)
assert.Equal(t, 1, sumResp.StreamDescriptionSummary.ConsumerCount)
@@ -269,7 +277,7 @@ func TestDescribeStream_StreamCreationTimestamp(t *testing.T) {
StreamCreationTimestamp float64 `json:"StreamCreationTimestamp"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
assert.GreaterOrEqual(t, int64(resp.StreamDescription.StreamCreationTimestamp), before)
})
}
@@ -309,7 +317,7 @@ func TestDescribeStream_StreamModeDetails(t *testing.T) {
} `json:"StreamModeDetails"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
require.NotNil(t, resp.StreamDescription.StreamModeDetails)
assert.Equal(t, tt.wantMode, resp.StreamDescription.StreamModeDetails.StreamMode)
})
@@ -366,7 +374,7 @@ func TestDescribeStreamSummary_OpenShardCount_AfterMerge(t *testing.T) {
ShardID string `json:"ShardId"`
} `json:"Shards"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &shardsResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &shardsResp, jsonx.V1()))
require.Len(t, shardsResp.Shards, 2)
rec3 := doRequest(t, h, "MergeShards", map[string]any{
@@ -387,7 +395,7 @@ func TestDescribeStreamSummary_OpenShardCount_AfterMerge(t *testing.T) {
OpenShardCount int `json:"OpenShardCount"`
} `json:"StreamDescriptionSummary"`
}
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &summaryResp))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &summaryResp, jsonx.V1()))
assert.Equal(t, tt.wantOpenShards, summaryResp.StreamDescriptionSummary.OpenShardCount)
})
})
@@ -426,7 +434,7 @@ func TestDescribeStream_EncryptionTypeDefault(t *testing.T) {
EncryptionType string `json:"EncryptionType"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantEncryption, resp.StreamDescription.EncryptionType)
})
}
@@ -454,7 +462,7 @@ func TestDescribeStream_IncludesClosedShardsAfterMerge(t *testing.T) {
}
rec = doRequest(t, h, "DescribeStream", map[string]any{"StreamName": "describe-closed-merge"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
s0 := descResp.StreamDescription.Shards[0].ShardID
s1 := descResp.StreamDescription.Shards[1].ShardID
@@ -468,7 +476,7 @@ func TestDescribeStream_IncludesClosedShardsAfterMerge(t *testing.T) {
// After merge: 2 closed parents + 1 open merged = 3 total.
rec = doRequest(t, h, "DescribeStream", map[string]any{"StreamName": "describe-closed-merge"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Len(t, descResp.StreamDescription.Shards, 3)
})
}
@@ -495,7 +503,7 @@ func TestDescribeStream_IncludesClosedShardsAfterSplit(t *testing.T) {
}
rec = doRequest(t, h, "DescribeStream", map[string]any{"StreamName": "describe-closed-split"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
shardID := descResp.StreamDescription.Shards[0].ShardID
const splitKey = "170141183460469231731687303715884105728"
@@ -509,7 +517,7 @@ func TestDescribeStream_IncludesClosedShardsAfterSplit(t *testing.T) {
// After split: 1 closed parent + 2 open children = 3 total.
rec = doRequest(t, h, "DescribeStream", map[string]any{"StreamName": "describe-closed-split"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Len(t, descResp.StreamDescription.Shards, 3)
})
}
@@ -537,7 +545,7 @@ func TestDescribeStream_OpenShardNoEndingSequenceNumber(t *testing.T) {
}
rec = doRequest(t, h, "DescribeStream", map[string]any{"StreamName": "describe-open-shard"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
require.Len(t, descResp.StreamDescription.Shards, 1)
// Open shards have an empty EndingSequenceNumber.
assert.Empty(t, descResp.StreamDescription.Shards[0].SequenceNumberRange.EndingSequenceNumber)
@@ -585,7 +593,7 @@ func TestDescribeStream_OpenShardWithRecordsNoEndingSeq(t *testing.T) {
}
rec = doRequest(t, h, "DescribeStream", map[string]any{"StreamName": "open-shard-with-records"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
require.Len(t, descResp.StreamDescription.Shards, 1)
shard := descResp.StreamDescription.Shards[0]
@@ -625,7 +633,7 @@ func TestDescribeStream_UpdateShardCount_IncludesOldClosedShards(t *testing.T) {
}
rec = doRequest(t, h, "DescribeStream", map[string]any{"StreamName": "describe-usc-stream"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Len(t, descResp.StreamDescription.Shards, 5, "3 closed + 2 open = 5")
})
}
@@ -734,7 +742,7 @@ func TestDescribeStream_ClosedShardHasEndingSequenceNumber(t *testing.T) {
rec = doRequest(t, h, "DescribeStream", map[string]any{"StreamName": "closing-seqnum-stream"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
require.Len(t, descResp.StreamDescription.Shards, 1)
shardID := descResp.StreamDescription.Shards[0].ShardID
@@ -750,7 +758,7 @@ func TestDescribeStream_ClosedShardHasEndingSequenceNumber(t *testing.T) {
// Re-describe: closed shard should have a non-empty EndingSequenceNumber.
rec = doRequest(t, h, "DescribeStream", map[string]any{"StreamName": "closing-seqnum-stream"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
require.Len(t, descResp.StreamDescription.Shards, 3)
closedCount := 0
diff --git a/services/kinesis/streams_test.go b/services/kinesis/streams_test.go
index 23f7df41ed..683e8aaca6 100644
--- a/services/kinesis/streams_test.go
+++ b/services/kinesis/streams_test.go
@@ -2,7 +2,7 @@ package kinesis_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -72,7 +73,7 @@ func TestCreateStream_ShardCount_Validation(t *testing.T) {
var resp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp.Type)
}
})
@@ -160,7 +161,7 @@ func TestCreateStream_OnDemand_ShardCountZero(t *testing.T) {
} `json:"Shards"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
require.NotNil(t, resp.StreamDescription.StreamModeDetails)
assert.Equal(t, tt.wantMode, resp.StreamDescription.StreamModeDetails.StreamMode)
@@ -423,7 +424,7 @@ func TestCreateStream_DefaultsToProvisioned(t *testing.T) {
} `json:"StreamModeDetails"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
require.NotNil(t, resp.StreamDescription.StreamModeDetails)
assert.Equal(t, tt.wantMode, resp.StreamDescription.StreamModeDetails.StreamMode)
})
@@ -476,7 +477,7 @@ func TestCreateStream_WithOnDemand(t *testing.T) {
} `json:"StreamModeDetails"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
require.NotNil(t, resp.StreamDescription.StreamModeDetails)
assert.Equal(t, tt.wantMode, resp.StreamDescription.StreamModeDetails.StreamMode)
})
@@ -524,7 +525,7 @@ func TestCreateStream_WithTags(t *testing.T) {
StreamARN string `json:"StreamARN"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp, jsonx.V1()))
rec3 := doRequest(t, h, "ListTagsForResource", map[string]any{
"ResourceARN": descResp.StreamDescription.StreamARN,
@@ -537,7 +538,7 @@ func TestCreateStream_WithTags(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &tagsResp, jsonx.V1()))
found := false
for _, kv := range tagsResp.Tags {
@@ -572,7 +573,7 @@ func TestStreamLifecycle(t *testing.T) {
var listResp struct {
StreamNames []string `json:"StreamNames"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Contains(t, listResp.StreamNames, "my-stream")
// DescribeStream
@@ -590,7 +591,7 @@ func TestStreamLifecycle(t *testing.T) {
} `json:"Shards"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "my-stream", descResp.StreamDescription.StreamName)
assert.Equal(t, "ACTIVE", descResp.StreamDescription.StreamStatus)
assert.Len(t, descResp.StreamDescription.Shards, 2)
@@ -607,7 +608,7 @@ func TestStreamLifecycle(t *testing.T) {
OpenShardCount int `json:"OpenShardCount"`
} `json:"StreamDescriptionSummary"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &sumResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &sumResp, jsonx.V1()))
assert.Equal(t, "my-stream", sumResp.StreamDescriptionSummary.StreamName)
assert.Equal(t, 2, sumResp.StreamDescriptionSummary.OpenShardCount)
@@ -645,7 +646,7 @@ func TestCreateStreamAlreadyExists(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceInUseException", errResp.Type)
}
@@ -673,7 +674,7 @@ func TestHandleListStreamsEmpty(t *testing.T) {
var resp struct {
StreamNames []string `json:"StreamNames"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp.StreamNames)
assert.Empty(t, resp.StreamNames)
}
@@ -730,7 +731,7 @@ func TestCreateStream_OnDemandLimit_ViaHandler(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "LimitExceededException", errResp.Type)
}
diff --git a/services/kinesis/tags_test.go b/services/kinesis/tags_test.go
index 2afce221b4..7895d5d9c5 100644
--- a/services/kinesis/tags_test.go
+++ b/services/kinesis/tags_test.go
@@ -2,7 +2,7 @@ package kinesis_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesis"
)
@@ -88,7 +89,7 @@ func TestListTagsForResource_SortedOutput(t *testing.T) {
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Tags, 3)
assert.Equal(t, "apple", resp.Tags[0].Key)
assert.Equal(t, "a", resp.Tags[0].Value)
@@ -113,7 +114,7 @@ func TestAddTagsToStream_KeyTooLong(t *testing.T) {
var resp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidArgumentException", resp.Type)
}
@@ -133,7 +134,7 @@ func TestAddTagsToStream_EmptyKey(t *testing.T) {
var resp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidArgumentException", resp.Type)
}
@@ -154,7 +155,7 @@ func TestAddTagsToStream_ValueTooLong(t *testing.T) {
var resp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidArgumentException", resp.Type)
}
@@ -188,7 +189,7 @@ func TestAddTagsToStream_StreamNotFound(t *testing.T) {
var resp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp.Type)
}
@@ -213,7 +214,7 @@ func TestTagResource_KeyTooLong(t *testing.T) {
var resp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidArgumentException", resp.Type)
}
@@ -238,7 +239,7 @@ func TestTagResource_ValueTooLong(t *testing.T) {
var resp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidArgumentException", resp.Type)
}
@@ -321,7 +322,7 @@ func TestRemoveTagsFromStream_StreamNotFound(t *testing.T) {
var resp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp.Type)
}
@@ -338,7 +339,7 @@ func TestListTagsForStream_StreamNotFound(t *testing.T) {
var resp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp.Type)
}
@@ -372,7 +373,7 @@ func TestListTagsForStream_LimitRespected(t *testing.T) {
} `json:"Tags"`
HasMoreTags bool `json:"HasMoreTags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Tags, 5)
assert.True(t, resp.HasMoreTags)
}
@@ -406,7 +407,7 @@ func TestListTagsForStream_LimitDefault(t *testing.T) {
} `json:"Tags"`
HasMoreTags bool `json:"HasMoreTags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Tags, 10)
assert.True(t, resp.HasMoreTags)
}
@@ -455,7 +456,7 @@ func TestListTagsForResource(t *testing.T) {
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp.Tags)
}
})
@@ -494,7 +495,7 @@ func TestHandleAddTagsToStream(t *testing.T) {
} `json:"Tags"`
HasMoreTags bool `json:"HasMoreTags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.False(t, listResp.HasMoreTags)
assert.Len(t, listResp.Tags, 2)
@@ -543,7 +544,7 @@ func TestHandleRemoveTagsFromStream(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp.Tags, 1)
assert.Equal(t, "team", listResp.Tags[0].Key)
}
@@ -567,7 +568,7 @@ func TestHandleListTagsForStreamEmpty(t *testing.T) {
} `json:"Tags"`
HasMoreTags bool `json:"HasMoreTags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Empty(t, listResp.Tags)
assert.False(t, listResp.HasMoreTags)
}
@@ -616,7 +617,7 @@ func TestListTagsForResource_TagsFromCreate(t *testing.T) {
StreamARN string `json:"StreamARN"`
} `json:"StreamDescription"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp, jsonx.V1()))
rec3 := doRequest(t, h, "ListTagsForResource", map[string]any{
"ResourceARN": descResp.StreamDescription.StreamARN,
@@ -629,7 +630,7 @@ func TestListTagsForResource_TagsFromCreate(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &tagsResp, jsonx.V1()))
found := false
for _, kv := range tagsResp.Tags {
diff --git a/services/kinesisanalytics/application_cloudwatch_test.go b/services/kinesisanalytics/application_cloudwatch_test.go
index 8155d308a2..b961cfe8f7 100644
--- a/services/kinesisanalytics/application_cloudwatch_test.go
+++ b/services/kinesisanalytics/application_cloudwatch_test.go
@@ -2,13 +2,14 @@ package kinesisanalytics_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalytics"
)
@@ -237,7 +238,7 @@ func TestAddCWLRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
detail := resp["ApplicationDetail"].(map[string]any)
cwlList := detail["CloudWatchLoggingOptionDescriptions"].([]any)
assert.Len(t, cwlList, 1)
diff --git a/services/kinesisanalytics/application_inputs_test.go b/services/kinesisanalytics/application_inputs_test.go
index 9201033487..e8ff51a3a3 100644
--- a/services/kinesisanalytics/application_inputs_test.go
+++ b/services/kinesisanalytics/application_inputs_test.go
@@ -2,7 +2,7 @@ package kinesisanalytics_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalytics"
)
@@ -541,7 +542,7 @@ func TestHandler_DiscoverInputSchema(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantErrCode, resp["__type"])
})
}
diff --git a/services/kinesisanalytics/application_outputs_test.go b/services/kinesisanalytics/application_outputs_test.go
index 7d2501c1f6..2f2eb4cec3 100644
--- a/services/kinesisanalytics/application_outputs_test.go
+++ b/services/kinesisanalytics/application_outputs_test.go
@@ -2,13 +2,14 @@ package kinesisanalytics_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalytics"
)
@@ -237,7 +238,7 @@ func TestAddOutputRoundTrip(t *testing.T) {
rec2 := doRequest(t, h, "DescribeApplication", map[string]any{"ApplicationName": app.ApplicationName})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
detail := resp["ApplicationDetail"].(map[string]any)
outList := detail["OutputDescriptions"].([]any)
assert.Len(t, outList, 1)
diff --git a/services/kinesisanalytics/application_reference_data_test.go b/services/kinesisanalytics/application_reference_data_test.go
index c4e56ca60e..e646325571 100644
--- a/services/kinesisanalytics/application_reference_data_test.go
+++ b/services/kinesisanalytics/application_reference_data_test.go
@@ -2,7 +2,7 @@ package kinesisanalytics_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalytics"
)
@@ -256,7 +257,7 @@ func TestAddRefDataSourceRoundTrip(t *testing.T) {
rec2 := doRequest(t, h, "DescribeApplication", map[string]any{"ApplicationName": app.ApplicationName})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
detail := resp["ApplicationDetail"].(map[string]any)
refList := detail["ReferenceDataSourceDescriptions"].([]any)
assert.Len(t, refList, 1)
diff --git a/services/kinesisanalytics/application_update_test.go b/services/kinesisanalytics/application_update_test.go
index 84a3ac8482..45bbe33219 100644
--- a/services/kinesisanalytics/application_update_test.go
+++ b/services/kinesisanalytics/application_update_test.go
@@ -2,7 +2,7 @@ package kinesisanalytics_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalytics"
)
@@ -248,7 +249,7 @@ func TestHandler_UpdateApplication_NestedWireShapes(t *testing.T) {
require.Equal(t, http.StatusOK, describeRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &resp, jsonx.V1()))
detail := resp["ApplicationDetail"].(map[string]any)
inList := detail["InputDescriptions"].([]any)
@@ -353,7 +354,7 @@ func TestUpdateApplication_ReturnsApplicationDetail(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
detail, ok := resp["ApplicationDetail"].(map[string]any)
require.True(t, ok, "UpdateApplication response must contain ApplicationDetail, got: %s", rec.Body.String())
@@ -384,7 +385,7 @@ func TestUpdateApplication_CodeVisible(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
detail, ok := resp["ApplicationDetail"].(map[string]any)
require.True(t, ok)
@@ -424,6 +425,6 @@ func TestUpdateApplication_ConcurrentModificationException(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ConcurrentModificationException", errResp["__type"])
}
diff --git a/services/kinesisanalytics/applications_handler_test.go b/services/kinesisanalytics/applications_handler_test.go
index 53eb5774fa..2f69d0f93f 100644
--- a/services/kinesisanalytics/applications_handler_test.go
+++ b/services/kinesisanalytics/applications_handler_test.go
@@ -2,7 +2,7 @@ package kinesisanalytics_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalytics"
)
@@ -46,7 +47,7 @@ func TestHandler_CreateApplication(t *testing.T) {
if tt.wantKey != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, tt.wantKey)
}
})
@@ -160,7 +161,7 @@ func TestDeleteApplication_TimestampMismatch_ConcurrentModificationException(t *
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ConcurrentModificationException", resp["__type"],
"timestamp mismatch must return ConcurrentModificationException, not LimitExceededException")
}
@@ -225,7 +226,7 @@ func TestHandler_ListApplications(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["ApplicationSummaries"].([]any)
assert.Len(t, summaries, tt.wantCount)
diff --git a/services/kinesisanalytics/applications_test.go b/services/kinesisanalytics/applications_test.go
index 6c0e5e768c..db175091db 100644
--- a/services/kinesisanalytics/applications_test.go
+++ b/services/kinesisanalytics/applications_test.go
@@ -2,7 +2,7 @@ package kinesisanalytics_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalytics"
)
@@ -605,7 +606,7 @@ func TestResourceAlreadyExists_Returns400(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceInUseException", errResp["__type"])
}
@@ -620,6 +621,6 @@ func TestErrValidationMapping(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidArgumentException", errResp["__type"])
}
diff --git a/services/kinesisanalytics/discover_schema.go b/services/kinesisanalytics/discover_schema.go
index 7645c90d91..7b1a3cc33e 100644
--- a/services/kinesisanalytics/discover_schema.go
+++ b/services/kinesisanalytics/discover_schema.go
@@ -3,7 +3,8 @@ package kinesisanalytics
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"sort"
@@ -11,6 +12,8 @@ import (
"strings"
sdk_s3 "github.com/aws/aws-sdk-go-v2/service/s3"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -208,7 +211,7 @@ func sampleJSONLines(body []byte, limit int) []string {
for _, line := range lines {
trimmed := bytes.TrimSpace(line)
- if len(trimmed) == 0 || !json.Valid(trimmed) {
+ if len(trimmed) == 0 || !jsontext.Value(trimmed).IsValid(jsonx.V1()) {
continue
}
@@ -266,11 +269,10 @@ func decodeSampleRecords(raw []string) []map[string]any {
records := make([]map[string]any, 0, len(raw))
for _, r := range raw {
- dec := json.NewDecoder(strings.NewReader(r))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(strings.NewReader(r))
var m map[string]any
- if err := dec.Decode(&m); err != nil {
+ if err := json.UnmarshalDecode(dec, &m, jsonx.V1(), jsonx.UseNumber()); err != nil {
continue
}
@@ -318,7 +320,7 @@ func inferColumnType(records []map[string]any, key string) string {
switch val := v.(type) {
case bool:
sawBool = true
- case json.Number:
+ case jsonx.Number:
if _, err := strconv.ParseInt(val.String(), 10, 64); err == nil {
sawInt = true
} else {
@@ -330,7 +332,7 @@ func inferColumnType(records []map[string]any, key string) string {
default:
sawOther = true
- if b, err := json.Marshal(val); err == nil {
+ if b, err := json.Marshal(val, jsonx.V1()); err == nil {
maxLen = max(maxLen, len(b))
}
}
@@ -357,12 +359,12 @@ func stringifyValue(v any) string {
return ""
case string:
return val
- case json.Number:
+ case jsonx.Number:
return val.String()
case bool:
return strconv.FormatBool(val)
default:
- b, err := json.Marshal(val)
+ b, err := json.Marshal(val, jsonx.V1())
if err != nil {
return ""
}
diff --git a/services/kinesisanalytics/discover_schema_test.go b/services/kinesisanalytics/discover_schema_test.go
index 649f378b6d..7d4dfaef16 100644
--- a/services/kinesisanalytics/discover_schema_test.go
+++ b/services/kinesisanalytics/discover_schema_test.go
@@ -3,7 +3,7 @@ package kinesisanalytics_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalytics"
"github.com/blackbirdworks/gopherstack/services/s3"
)
@@ -80,7 +81,7 @@ func discoverSchemaBody(t *testing.T, rec *httptest.ResponseRecorder) map[string
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
diff --git a/services/kinesisanalytics/handler.go b/services/kinesisanalytics/handler.go
index 7ae78c3732..162971e23a 100644
--- a/services/kinesisanalytics/handler.go
+++ b/services/kinesisanalytics/handler.go
@@ -2,7 +2,7 @@ package kinesisanalytics
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -163,7 +164,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var req applicationNameInput
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return req.ApplicationName
}
@@ -198,13 +199,10 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
var code string
var status int
@@ -234,8 +232,8 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
status = http.StatusBadRequest
code = errLimitExceededException
case errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr),
- errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err),
+ jsonx.IsTypeError(err):
status = http.StatusBadRequest
code = errInvalidArgumentException
case errors.Is(err, errApplicationName),
diff --git a/services/kinesisanalytics/handler_test.go b/services/kinesisanalytics/handler_test.go
index b56a6d26ee..638e9b8e04 100644
--- a/services/kinesisanalytics/handler_test.go
+++ b/services/kinesisanalytics/handler_test.go
@@ -2,7 +2,7 @@ package kinesisanalytics_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalytics"
)
@@ -49,7 +50,7 @@ func doRequest(t *testing.T, h *kinesisanalytics.Handler, action string, body an
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -273,7 +274,7 @@ func TestErrorTypes(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
})
}
diff --git a/services/kinesisanalytics/persistence.go b/services/kinesisanalytics/persistence.go
index cf28ab3a45..b8784282e7 100644
--- a/services/kinesisanalytics/persistence.go
+++ b/services/kinesisanalytics/persistence.go
@@ -2,9 +2,11 @@ package kinesisanalytics
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
@@ -28,11 +30,11 @@ const kinesisanalyticsSnapshotVersion = 1
// Application is persisted directly (no DTO) because every field, including
// the additive Region, already round-trips through encoding/json.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- NextID int64 `json:"next_id"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ NextID int64 `json:"next_id"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
@@ -56,7 +58,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
NextID: b.nextID,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "kinesisanalytics: failed to marshal snapshot", "error", err)
diff --git a/services/kinesisanalytics/persistence_test.go b/services/kinesisanalytics/persistence_test.go
index 242c92e5f7..1ddfaadd79 100644
--- a/services/kinesisanalytics/persistence_test.go
+++ b/services/kinesisanalytics/persistence_test.go
@@ -2,11 +2,14 @@ package kinesisanalytics //nolint:testpackage // needs kaCtxRegion and the unexp
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestInMemoryBackend_FullStateSnapshotRestoreRoundTrip exercises every kind
@@ -158,12 +161,12 @@ func TestInMemoryBackend_Restore_IncompatibleVersion_ResetsEmpty(t *testing.T) {
// Corrupt just the version field to simulate a snapshot from an
// incompatible (older/newer) build.
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &raw))
- bumped, err := json.Marshal(kinesisanalyticsSnapshotVersion + 1)
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &raw, jsonx.V1()))
+ bumped, err := json.Marshal(kinesisanalyticsSnapshotVersion+1, jsonx.V1())
require.NoError(t, err)
raw["version"] = bumped
- mutated, err := json.Marshal(raw)
+ mutated, err := json.Marshal(raw, jsonx.V1())
require.NoError(t, err)
fresh := NewInMemoryBackend("us-east-1", "000000000000")
diff --git a/services/kinesisanalytics/tags_test.go b/services/kinesisanalytics/tags_test.go
index c3a4bc057f..beb6eff1ff 100644
--- a/services/kinesisanalytics/tags_test.go
+++ b/services/kinesisanalytics/tags_test.go
@@ -2,7 +2,7 @@ package kinesisanalytics_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalytics"
)
@@ -434,7 +435,7 @@ func TestSortedListTagsForResource(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Tags, 3)
assert.Equal(t, "a", resp.Tags[0].Key)
diff --git a/services/kinesisanalyticsv2/application_update_test.go b/services/kinesisanalyticsv2/application_update_test.go
index f653515151..344cca1e75 100644
--- a/services/kinesisanalyticsv2/application_update_test.go
+++ b/services/kinesisanalyticsv2/application_update_test.go
@@ -2,13 +2,14 @@ package kinesisanalyticsv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"math"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalyticsv2"
)
@@ -520,7 +521,7 @@ func appCreateEpochSeconds(t *testing.T, b *kinesisanalyticsv2.InMemoryBackend,
} `json:"ApplicationDetail"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.ApplicationDetail.CreateTimestamp
}
diff --git a/services/kinesisanalyticsv2/flink_engine_test.go b/services/kinesisanalyticsv2/flink_engine_test.go
index 80029494aa..986f747412 100644
--- a/services/kinesisanalyticsv2/flink_engine_test.go
+++ b/services/kinesisanalyticsv2/flink_engine_test.go
@@ -2,7 +2,8 @@ package kinesisanalyticsv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"mime"
"mime/multipart"
@@ -16,6 +17,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/services/kinesisanalyticsv2"
)
@@ -127,7 +129,7 @@ func (f *fakeFlinkREST) Do(req *http.Request) (*http.Response, error) {
case strings.HasSuffix(p, "/run"):
var body map[string]any
- _ = json.NewDecoder(req.Body).Decode(&body)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(req.Body), &body, jsonx.V1())
f.runs = append(f.runs, body)
return jsonResp(`{"jobid":"job1"}`), nil
diff --git a/services/kinesisanalyticsv2/flink_rest.go b/services/kinesisanalyticsv2/flink_rest.go
index fde5d06424..322bba6ae1 100644
--- a/services/kinesisanalyticsv2/flink_rest.go
+++ b/services/kinesisanalyticsv2/flink_rest.go
@@ -3,7 +3,7 @@ package kinesisanalyticsv2
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -11,6 +11,8 @@ import (
"net/http"
"net/textproto"
"path"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const flinkMaxBody = 1 << 20
@@ -89,7 +91,7 @@ func (c flinkREST) send(ctx context.Context, method, p, contentType string, body
return nil
}
- if err = json.Unmarshal(raw, out); err != nil {
+ if err = json.Unmarshal(raw, out, jsonx.V1()); err != nil {
return fmt.Errorf("decode flink response: %w", err)
}
@@ -100,7 +102,7 @@ func (c flinkREST) json(ctx context.Context, method, p string, in, out any) erro
var body io.Reader
if in != nil {
- raw, err := json.Marshal(in)
+ raw, err := json.Marshal(in, jsonx.V1())
if err != nil {
return fmt.Errorf("encode flink request: %w", err)
}
diff --git a/services/kinesisanalyticsv2/flink_run.go b/services/kinesisanalyticsv2/flink_run.go
index c4bf340f35..c39ae593e9 100644
--- a/services/kinesisanalyticsv2/flink_run.go
+++ b/services/kinesisanalyticsv2/flink_run.go
@@ -2,7 +2,7 @@ package kinesisanalyticsv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"os"
@@ -12,6 +12,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc"
)
@@ -77,7 +78,7 @@ func (e *flinkEngine) stage(in flinkLaunch) (string, error) {
props = []PropertyGroup{}
}
- raw, err := json.Marshal(props)
+ raw, err := json.Marshal(props, jsonx.V1())
if err == nil {
err = os.WriteFile(filepath.Join(dir, "application_properties.json"), raw, flinkFileMode)
}
diff --git a/services/kinesisanalyticsv2/handler.go b/services/kinesisanalyticsv2/handler.go
index 579e647fd6..e09653af34 100644
--- a/services/kinesisanalyticsv2/handler.go
+++ b/services/kinesisanalyticsv2/handler.go
@@ -2,7 +2,7 @@ package kinesisanalyticsv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -131,7 +132,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
ApplicationName string `json:"ApplicationName"`
}
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
return ""
}
diff --git a/services/kinesisanalyticsv2/handler_application_config.go b/services/kinesisanalyticsv2/handler_application_config.go
index 1813036a99..42ddc6fd51 100644
--- a/services/kinesisanalyticsv2/handler_application_config.go
+++ b/services/kinesisanalyticsv2/handler_application_config.go
@@ -2,10 +2,12 @@ package kinesisanalyticsv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type cwlOptionInput struct {
@@ -218,7 +220,7 @@ type deleteApplicationVpcConfigOutput struct {
func (h *Handler) handleAddApplicationCloudWatchLoggingOption(ctx context.Context, c *echo.Context, body []byte) error {
var in addApplicationCWLOptionInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -258,7 +260,7 @@ func (h *Handler) handleAddApplicationInput(
body []byte,
) error {
var in addApplicationInputInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -288,7 +290,7 @@ func (h *Handler) handleAddApplicationInputProcessingConfiguration(
ctx context.Context, c *echo.Context, body []byte,
) error {
var in addInputProcessingConfigInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -337,7 +339,7 @@ func (h *Handler) handleAddApplicationOutput(
body []byte,
) error {
var in addApplicationOutputInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -368,7 +370,7 @@ func (h *Handler) handleAddApplicationOutput(
//nolint:dupl // add input/reference-data-source handlers share structure but are semantically distinct operations
func (h *Handler) handleAddApplicationReferenceDataSource(ctx context.Context, c *echo.Context, body []byte) error {
var in addApplicationRefDataSourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -398,7 +400,7 @@ func (h *Handler) handleAddApplicationReferenceDataSource(ctx context.Context, c
func (h *Handler) handleAddApplicationVpcConfiguration(ctx context.Context, c *echo.Context, body []byte) error {
var in addApplicationVpcConfigInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -439,7 +441,7 @@ func (h *Handler) handleDeleteApplicationCloudWatchLoggingOption(
ctx context.Context, c *echo.Context, body []byte,
) error {
var in deleteApplicationCWLOptionInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -467,7 +469,7 @@ func (h *Handler) handleDeleteApplicationInputProcessingConfiguration(
ctx context.Context, c *echo.Context, body []byte,
) error {
var in deleteInputProcessingConfigInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -490,7 +492,7 @@ func (h *Handler) handleDeleteApplicationInputProcessingConfiguration(
func (h *Handler) handleDeleteApplicationOutput(ctx context.Context, c *echo.Context, body []byte) error {
var in deleteApplicationOutputInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -513,7 +515,7 @@ func (h *Handler) handleDeleteApplicationOutput(ctx context.Context, c *echo.Con
func (h *Handler) handleDeleteApplicationReferenceDataSource(ctx context.Context, c *echo.Context, body []byte) error {
var in deleteApplicationRefDataSourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -536,7 +538,7 @@ func (h *Handler) handleDeleteApplicationReferenceDataSource(ctx context.Context
func (h *Handler) handleDeleteApplicationVpcConfiguration(ctx context.Context, c *echo.Context, body []byte) error {
var in deleteApplicationVpcConfigInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
diff --git a/services/kinesisanalyticsv2/handler_application_config_delete_test.go b/services/kinesisanalyticsv2/handler_application_config_delete_test.go
index 9dcbd09aa4..aec4dc83e6 100644
--- a/services/kinesisanalyticsv2/handler_application_config_delete_test.go
+++ b/services/kinesisanalyticsv2/handler_application_config_delete_test.go
@@ -1,13 +1,14 @@
package kinesisanalyticsv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalyticsv2"
)
@@ -28,7 +29,7 @@ func TestKAV2_DeleteApplicationCloudWatchLoggingOption(t *testing.T) {
})
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
descs := out["CloudWatchLoggingOptionDescriptions"].([]any)
desc := descs[0].(map[string]any)
@@ -77,7 +78,7 @@ func TestKAV2_DeleteApplicationCloudWatchLoggingOption(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
descs, ok := out["CloudWatchLoggingOptionDescriptions"].([]any)
require.True(t, ok)
assert.Len(t, descs, tt.wantCWLCount)
@@ -125,7 +126,7 @@ func TestKAV2_DeleteApplicationInputProcessingConfiguration(t *testing.T) {
})
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
inputs := out["InputDescriptions"].([]any)
input := inputs[0].(map[string]any)
inputID := input["InputId"].(string)
@@ -226,7 +227,7 @@ func TestKAV2_DeleteApplicationOutput(t *testing.T) {
})
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
outputs := out["OutputDescriptions"].([]any)
output := outputs[0].(map[string]any)
@@ -287,7 +288,7 @@ func TestKAV2_DeleteApplicationOutput(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["ApplicationARN"])
}
})
diff --git a/services/kinesisanalyticsv2/handler_application_config_test.go b/services/kinesisanalyticsv2/handler_application_config_test.go
index ffc3374611..d105543cf0 100644
--- a/services/kinesisanalyticsv2/handler_application_config_test.go
+++ b/services/kinesisanalyticsv2/handler_application_config_test.go
@@ -1,13 +1,14 @@
package kinesisanalyticsv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalyticsv2"
)
@@ -73,7 +74,7 @@ func TestKAV2_AddApplicationCloudWatchLoggingOption(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
descriptions, ok := out["CloudWatchLoggingOptionDescriptions"].([]any)
require.True(t, ok)
assert.Len(t, descriptions, tt.wantCWLLen)
@@ -146,7 +147,7 @@ func TestKAV2_AddApplicationInput(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
inputs, ok := out["InputDescriptions"].([]any)
require.True(t, ok)
assert.Len(t, inputs, tt.wantInputs)
@@ -196,7 +197,7 @@ func TestKAV2_AddApplicationInputProcessingConfiguration(t *testing.T) {
})
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
inputs := out["InputDescriptions"].([]any)
input := inputs[0].(map[string]any)
@@ -328,7 +329,7 @@ func TestKAV2_AddApplicationOutput(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
outputs, ok := out["OutputDescriptions"].([]any)
require.True(t, ok)
assert.Len(t, outputs, tt.wantOutputs)
@@ -422,7 +423,7 @@ func TestKAV2_AddApplicationReferenceDataSource(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
refs, ok := out["ReferenceDataSourceDescriptions"].([]any)
require.True(t, ok)
assert.Len(t, refs, tt.wantRefs)
@@ -512,7 +513,7 @@ func TestKAV2_AddApplicationVpcConfiguration(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotNil(t, out["VpcConfigurationDescription"])
vpcDesc, ok := out["VpcConfigurationDescription"].(map[string]any)
require.True(t, ok)
@@ -545,7 +546,7 @@ func TestKAV2_VpcConfiguration_NonNilSlices(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
vpcDesc, ok := out["VpcConfigurationDescription"].(map[string]any)
require.True(t, ok)
@@ -612,7 +613,7 @@ func TestKAV2_AddOps_VersionBump(t *testing.T) {
respBody := tt.setupAdd(h, appName)
var out map[string]any
- require.NoError(t, json.Unmarshal(respBody, &out))
+ require.NoError(t, json.Unmarshal(respBody, &out, jsonx.V1()))
versionID, ok := out["ApplicationVersionId"].(float64)
require.True(t, ok)
assert.InEpsilon(t, 2.0, versionID, 1e-9)
diff --git a/services/kinesisanalyticsv2/handler_application_presigned_url.go b/services/kinesisanalyticsv2/handler_application_presigned_url.go
index 47c768cc48..66a4855264 100644
--- a/services/kinesisanalyticsv2/handler_application_presigned_url.go
+++ b/services/kinesisanalyticsv2/handler_application_presigned_url.go
@@ -2,10 +2,12 @@ package kinesisanalyticsv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createPresignedURLInput struct {
@@ -20,7 +22,7 @@ type createPresignedURLOutput struct {
func (h *Handler) handleCreateApplicationPresignedURL(ctx context.Context, c *echo.Context, body []byte) error {
var in createPresignedURLInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
diff --git a/services/kinesisanalyticsv2/handler_application_presigned_url_test.go b/services/kinesisanalyticsv2/handler_application_presigned_url_test.go
index 3ca738d99f..ac003d620f 100644
--- a/services/kinesisanalyticsv2/handler_application_presigned_url_test.go
+++ b/services/kinesisanalyticsv2/handler_application_presigned_url_test.go
@@ -1,13 +1,14 @@
package kinesisanalyticsv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalyticsv2"
)
@@ -59,7 +60,7 @@ func TestKAV2_CreateApplicationPresignedUrl(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
url, ok := out["AuthorizedUrl"].(string)
require.True(t, ok)
assert.Contains(t, url, "FLINK_DASHBOARD_URL")
diff --git a/services/kinesisanalyticsv2/handler_application_snapshots.go b/services/kinesisanalyticsv2/handler_application_snapshots.go
index 128de02e0b..a808178339 100644
--- a/services/kinesisanalyticsv2/handler_application_snapshots.go
+++ b/services/kinesisanalyticsv2/handler_application_snapshots.go
@@ -2,10 +2,12 @@ package kinesisanalyticsv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createSnapshotInput struct {
@@ -34,14 +36,14 @@ type listSnapshotsOutput struct {
}
type deleteSnapshotInput struct {
- ApplicationName string `json:"ApplicationName"`
- SnapshotName string `json:"SnapshotName"`
- SnapshotCreationTimestamp json.Number `json:"SnapshotCreationTimestamp,omitempty"`
+ ApplicationName string `json:"ApplicationName"`
+ SnapshotName string `json:"SnapshotName"`
+ SnapshotCreationTimestamp jsonx.Number `json:"SnapshotCreationTimestamp,omitempty"`
}
func (h *Handler) handleCreateApplicationSnapshot(ctx context.Context, c *echo.Context, body []byte) error {
var in createSnapshotInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -57,7 +59,7 @@ func (h *Handler) handleCreateApplicationSnapshot(ctx context.Context, c *echo.C
func (h *Handler) handleDescribeApplicationSnapshot(ctx context.Context, c *echo.Context, body []byte) error {
var in describeSnapshotInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -71,7 +73,7 @@ func (h *Handler) handleDescribeApplicationSnapshot(ctx context.Context, c *echo
func (h *Handler) handleListApplicationSnapshots(ctx context.Context, c *echo.Context, body []byte) error {
var in listSnapshotsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -90,7 +92,7 @@ func (h *Handler) handleListApplicationSnapshots(ctx context.Context, c *echo.Co
func (h *Handler) handleDeleteApplicationSnapshot(ctx context.Context, c *echo.Context, body []byte) error {
var in deleteSnapshotInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
diff --git a/services/kinesisanalyticsv2/handler_application_snapshots_test.go b/services/kinesisanalyticsv2/handler_application_snapshots_test.go
index cea70f4004..bf059c3bd7 100644
--- a/services/kinesisanalyticsv2/handler_application_snapshots_test.go
+++ b/services/kinesisanalyticsv2/handler_application_snapshots_test.go
@@ -1,12 +1,14 @@
package kinesisanalyticsv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestKAV2_SnapshotLifecycle(t *testing.T) {
@@ -38,7 +40,7 @@ func TestKAV2_SnapshotLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
snaps, ok := listOut["SnapshotSummaries"].([]any)
require.True(t, ok)
assert.Len(t, snaps, 1)
diff --git a/services/kinesisanalyticsv2/handler_application_update.go b/services/kinesisanalyticsv2/handler_application_update.go
index 4105dab3a6..5e38724265 100644
--- a/services/kinesisanalyticsv2/handler_application_update.go
+++ b/services/kinesisanalyticsv2/handler_application_update.go
@@ -2,10 +2,12 @@ package kinesisanalyticsv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// runConfigurationInput mirrors real AWS's RunConfiguration (StartApplication)
@@ -219,7 +221,7 @@ type updateApplicationOutput struct {
func (h *Handler) handleUpdateApplication(ctx context.Context, c *echo.Context, body []byte) error {
var in updateApplicationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
diff --git a/services/kinesisanalyticsv2/handler_application_update_test.go b/services/kinesisanalyticsv2/handler_application_update_test.go
index 1316c51b10..f8c17a07f1 100644
--- a/services/kinesisanalyticsv2/handler_application_update_test.go
+++ b/services/kinesisanalyticsv2/handler_application_update_test.go
@@ -1,12 +1,14 @@
package kinesisanalyticsv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestKAV2_CreateApplication_ExtendedInlineConfiguration verifies that
@@ -57,7 +59,7 @@ func TestKAV2_CreateApplication_ExtendedInlineConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
detail := out["ApplicationDetail"].(map[string]any)
appConfig := detail["ApplicationConfigurationDescription"].(map[string]any)
@@ -110,7 +112,7 @@ func TestKAV2_UpdateApplication_ConditionalTokenWireRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
token, ok := createOut["ApplicationDetail"].(map[string]any)["ConditionalToken"].(string)
require.True(t, ok, "expected ConditionalToken in CreateApplication response")
require.NotEmpty(t, token)
@@ -123,7 +125,7 @@ func TestKAV2_UpdateApplication_ConditionalTokenWireRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateOut map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut, jsonx.V1()))
assert.Equal(t, "FLINK-1_19", updateOut["ApplicationDetail"].(map[string]any)["RuntimeEnvironment"])
// A second call reusing the now-stale token must fail.
@@ -159,14 +161,14 @@ func TestKAV2_StartApplication_RunConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
assert.NotEmpty(t, startOut["OperationId"])
descRec := doKAV2Request(t, h, "DescribeApplication", map[string]any{"ApplicationName": "start-runcfg-wire-app"})
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
appConfig := descOut["ApplicationDetail"].(map[string]any)["ApplicationConfigurationDescription"].(map[string]any)
runConfig := appConfig["RunConfigurationDescription"].(map[string]any)
restoreConfig := runConfig["ApplicationRestoreConfigurationDescription"].(map[string]any)
@@ -236,7 +238,7 @@ func TestKAV2_AddDeleteVpcAndCWLOption_WireIncludesOperationID(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
detail := descOut["ApplicationDetail"].(map[string]any)
cwlOptions := detail["CloudWatchLoggingOptionDescriptions"].([]any)
cwlID := cwlOptions[0].(map[string]any)["CloudWatchLoggingOptionId"].(string)
@@ -262,7 +264,7 @@ func assertHasOperationID(t *testing.T, body []byte) {
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
opID, ok := out["OperationId"].(string)
require.True(t, ok, "expected OperationId in response")
assert.NotEmpty(t, opID)
@@ -298,7 +300,7 @@ func TestKAV2_StartApplication_SqlRunConfigurations(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
createAppConfig := createOut["ApplicationDetail"].(map[string]any)["ApplicationConfigurationDescription"].(map[string]any) //nolint:lll // test readability
createSQLConfig := createAppConfig["SqlApplicationConfigurationDescription"].(map[string]any)
inputID := createSQLConfig["InputDescriptions"].([]any)[0].(map[string]any)["InputId"].(string)
@@ -322,7 +324,7 @@ func TestKAV2_StartApplication_SqlRunConfigurations(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
appConfig := descOut["ApplicationDetail"].(map[string]any)["ApplicationConfigurationDescription"].(map[string]any)
sqlConfig := appConfig["SqlApplicationConfigurationDescription"].(map[string]any)
input := sqlConfig["InputDescriptions"].([]any)[0].(map[string]any)
@@ -452,7 +454,7 @@ func TestKAV2_DiscoverInputSchema(t *testing.T) {
}
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
inputSchema := out["InputSchema"].(map[string]any)
assert.Len(t, inputSchema["RecordColumns"], 3)
})
@@ -510,7 +512,7 @@ func TestKAV2_CreateApplication_ZeppelinConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
detail := out["ApplicationDetail"].(map[string]any)
appConfig := detail["ApplicationConfigurationDescription"].(map[string]any)
zepDesc := appConfig["ZeppelinApplicationConfigurationDescription"].(map[string]any)
@@ -576,7 +578,7 @@ func TestKAV2_UpdateApplication_ZeppelinConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
appConfig := descOut["ApplicationDetail"].(map[string]any)["ApplicationConfigurationDescription"].(map[string]any)
zepDesc := appConfig["ZeppelinApplicationConfigurationDescription"].(map[string]any)
diff --git a/services/kinesisanalyticsv2/handler_application_versions.go b/services/kinesisanalyticsv2/handler_application_versions.go
index a3a1e436c8..1a94ad0774 100644
--- a/services/kinesisanalyticsv2/handler_application_versions.go
+++ b/services/kinesisanalyticsv2/handler_application_versions.go
@@ -2,12 +2,13 @@ package kinesisanalyticsv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type describeApplicationOperationInput struct {
@@ -93,7 +94,7 @@ type rollbackApplicationOutput struct {
func (h *Handler) handleDescribeApplicationOperation(ctx context.Context, c *echo.Context, body []byte) error {
var in describeApplicationOperationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -114,7 +115,7 @@ func (h *Handler) handleDescribeApplicationOperation(ctx context.Context, c *ech
func (h *Handler) handleListApplicationOperations(ctx context.Context, c *echo.Context, body []byte) error {
var in listApplicationOperationsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -144,7 +145,7 @@ func (h *Handler) handleListApplicationOperations(ctx context.Context, c *echo.C
func (h *Handler) handleDescribeApplicationVersion(ctx context.Context, c *echo.Context, body []byte) error {
var in describeApplicationVersionInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -160,7 +161,7 @@ func (h *Handler) handleDescribeApplicationVersion(ctx context.Context, c *echo.
func (h *Handler) handleListApplicationVersions(ctx context.Context, c *echo.Context, body []byte) error {
var in listApplicationVersionsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -185,7 +186,7 @@ func (h *Handler) handleListApplicationVersions(ctx context.Context, c *echo.Con
func (h *Handler) handleRollbackApplication(ctx context.Context, c *echo.Context, body []byte) error {
var in rollbackApplicationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
diff --git a/services/kinesisanalyticsv2/handler_application_versions_test.go b/services/kinesisanalyticsv2/handler_application_versions_test.go
index 6312718009..f9707e57ce 100644
--- a/services/kinesisanalyticsv2/handler_application_versions_test.go
+++ b/services/kinesisanalyticsv2/handler_application_versions_test.go
@@ -1,12 +1,14 @@
package kinesisanalyticsv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestKAV2_ApplicationOperationsLifecycle exercises
@@ -29,7 +31,7 @@ func TestKAV2_ApplicationOperationsLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
opID, ok := startOut["OperationId"].(string)
require.True(t, ok)
require.NotEmpty(t, opID)
@@ -41,7 +43,7 @@ func TestKAV2_ApplicationOperationsLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
details, ok := descOut["ApplicationOperationInfoDetails"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "StartApplication", details["Operation"])
@@ -55,7 +57,7 @@ func TestKAV2_ApplicationOperationsLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
items, ok := listOut["ApplicationOperationInfoList"].([]any)
require.True(t, ok)
require.Len(t, items, 1)
@@ -103,7 +105,7 @@ func TestKAV2_RollbackApplication(t *testing.T) {
require.Equal(t, http.StatusOK, rollbackRec.Code)
var rollbackOut map[string]any
- require.NoError(t, json.Unmarshal(rollbackRec.Body.Bytes(), &rollbackOut))
+ require.NoError(t, json.Unmarshal(rollbackRec.Body.Bytes(), &rollbackOut, jsonx.V1()))
assert.NotEmpty(t, rollbackOut["OperationId"])
detail, ok := rollbackOut["ApplicationDetail"].(map[string]any)
diff --git a/services/kinesisanalyticsv2/handler_applications.go b/services/kinesisanalyticsv2/handler_applications.go
index 45dd23c4fc..8ef6638125 100644
--- a/services/kinesisanalyticsv2/handler_applications.go
+++ b/services/kinesisanalyticsv2/handler_applications.go
@@ -2,13 +2,14 @@ package kinesisanalyticsv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const maintenanceWindowDuration = 8 * time.Hour
@@ -227,8 +228,8 @@ type listApplicationsOutput struct {
}
type deleteApplicationInput struct {
- ApplicationName string `json:"ApplicationName"`
- CreateTimestamp json.Number `json:"CreateTimestamp,omitempty"`
+ ApplicationName string `json:"ApplicationName"`
+ CreateTimestamp jsonx.Number `json:"CreateTimestamp,omitempty"`
}
type startApplicationInput struct {
@@ -313,7 +314,7 @@ type discoverInputSchemaOutput struct {
func (h *Handler) handleCreateApplication(ctx context.Context, c *echo.Context, body []byte) error {
var in createApplicationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -575,7 +576,7 @@ func buildZeppelinConfigDesc(in *zeppelinApplicationConfigInput) *ZeppelinApplic
func (h *Handler) handleDescribeApplication(ctx context.Context, c *echo.Context, body []byte) error {
var in describeApplicationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -591,7 +592,7 @@ func (h *Handler) handleDescribeApplication(ctx context.Context, c *echo.Context
func (h *Handler) handleListApplications(ctx context.Context, c *echo.Context, body []byte) error {
var in listApplicationsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -612,7 +613,7 @@ func (h *Handler) handleListApplications(ctx context.Context, c *echo.Context, b
// DeleteApplication validates against. Returns nil (skip the check) when the
// field is absent or unparseable, matching the pre-existing "leniency, never
// causes a false accept/reject" note in PARITY.md.
-func deleteTimestampSeconds(n json.Number) *float64 {
+func deleteTimestampSeconds(n jsonx.Number) *float64 {
if n == "" {
return nil
}
@@ -627,7 +628,7 @@ func deleteTimestampSeconds(n json.Number) *float64 {
func (h *Handler) handleDeleteApplication(ctx context.Context, c *echo.Context, body []byte) error {
var in deleteApplicationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -641,7 +642,7 @@ func (h *Handler) handleDeleteApplication(ctx context.Context, c *echo.Context,
func (h *Handler) handleStartApplication(ctx context.Context, c *echo.Context, body []byte) error {
var in startApplicationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -662,7 +663,7 @@ func (h *Handler) handleStartApplication(ctx context.Context, c *echo.Context, b
func (h *Handler) handleStopApplication(ctx context.Context, c *echo.Context, body []byte) error {
var in startStopApplicationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -690,7 +691,7 @@ func (h *Handler) handleUpdateApplicationMaintenanceConfiguration(
ctx context.Context, c *echo.Context, body []byte,
) error {
var in updateMaintenanceConfigInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -711,7 +712,7 @@ func (h *Handler) handleUpdateApplicationMaintenanceConfiguration(
func (h *Handler) handleDiscoverInputSchema(ctx context.Context, c *echo.Context, body []byte) error {
var in discoverInputSchemaInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
diff --git a/services/kinesisanalyticsv2/handler_applications_test.go b/services/kinesisanalyticsv2/handler_applications_test.go
index 21840becdb..3542784ecb 100644
--- a/services/kinesisanalyticsv2/handler_applications_test.go
+++ b/services/kinesisanalyticsv2/handler_applications_test.go
@@ -1,7 +1,7 @@
package kinesisanalyticsv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalyticsv2"
)
@@ -16,7 +17,7 @@ func assertErrorType(t *testing.T, body []byte, wantType string) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, wantType, resp["__type"])
}
@@ -24,7 +25,7 @@ func assertAppStatus(t *testing.T, body []byte, wantStatus string) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
detail, ok := resp["ApplicationDetail"].(map[string]any)
require.True(t, ok, "response missing ApplicationDetail")
@@ -76,7 +77,7 @@ func TestKAV2_CreateApplication(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
detail, ok := out["ApplicationDetail"].(map[string]any)
require.True(t, ok)
@@ -184,7 +185,7 @@ func TestKAV2_CreateApplication_InlineConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
detail := out["ApplicationDetail"].(map[string]any)
// Inline config must not bump the version past 1 -- real AWS keeps a
@@ -222,7 +223,7 @@ func TestKAV2_NonNilSlices_InDetailOutput(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
detail := out["ApplicationDetail"].(map[string]any)
// Tags should be present (may be empty but not nil)
@@ -292,7 +293,7 @@ func TestKAV2_ListApplications(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries, ok := out["ApplicationSummaries"].([]any)
require.True(t, ok)
@@ -341,7 +342,7 @@ func TestKAV2_StartStopApplication(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
detail := out["ApplicationDetail"].(map[string]any)
assert.Equal(t, "RUNNING", detail["ApplicationStatus"])
@@ -369,14 +370,14 @@ func TestKAV2_StartStopUpdate_ReturnOperationId(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
assert.NotEmpty(t, startOut["OperationId"])
stopRec := doKAV2Request(t, h, "StopApplication", map[string]any{"ApplicationName": "opid-app"})
require.Equal(t, http.StatusOK, stopRec.Code)
var stopOut map[string]any
- require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopOut))
+ require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopOut, jsonx.V1()))
assert.NotEmpty(t, stopOut["OperationId"])
updateRec := doKAV2Request(t, h, "UpdateApplication", map[string]any{
@@ -387,7 +388,7 @@ func TestKAV2_StartStopUpdate_ReturnOperationId(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateOut map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut, jsonx.V1()))
assert.NotEmpty(t, updateOut["OperationId"])
}
@@ -466,7 +467,7 @@ func TestKAV2_UpdateApplication(t *testing.T) {
if tt.wantRole != "" {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
detail := out["ApplicationDetail"].(map[string]any)
assert.Equal(t, tt.wantRole, detail["ServiceExecutionRole"])
}
diff --git a/services/kinesisanalyticsv2/handler_tags.go b/services/kinesisanalyticsv2/handler_tags.go
index ac3e4eb4ae..16fca54a7f 100644
--- a/services/kinesisanalyticsv2/handler_tags.go
+++ b/services/kinesisanalyticsv2/handler_tags.go
@@ -2,10 +2,12 @@ package kinesisanalyticsv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type tagResourceInput struct {
@@ -28,7 +30,7 @@ type listTagsOutput struct {
func (h *Handler) handleTagResource(ctx context.Context, c *echo.Context, body []byte) error {
var in tagResourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -41,7 +43,7 @@ func (h *Handler) handleTagResource(ctx context.Context, c *echo.Context, body [
func (h *Handler) handleUntagResource(ctx context.Context, c *echo.Context, body []byte) error {
var in untagResourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
@@ -54,7 +56,7 @@ func (h *Handler) handleUntagResource(ctx context.Context, c *echo.Context, body
func (h *Handler) handleListTagsForResource(ctx context.Context, c *echo.Context, body []byte) error {
var in listTagsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body: "+err.Error())
}
diff --git a/services/kinesisanalyticsv2/handler_tags_test.go b/services/kinesisanalyticsv2/handler_tags_test.go
index ae6c329917..a01ed56d2e 100644
--- a/services/kinesisanalyticsv2/handler_tags_test.go
+++ b/services/kinesisanalyticsv2/handler_tags_test.go
@@ -1,12 +1,14 @@
package kinesisanalyticsv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestKAV2_TaggingOperations(t *testing.T) {
@@ -24,7 +26,7 @@ func TestKAV2_TaggingOperations(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
detail := createOut["ApplicationDetail"].(map[string]any)
appARN := detail["ApplicationARN"].(string)
@@ -36,7 +38,7 @@ func TestKAV2_TaggingOperations(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
tags, ok := listOut["Tags"].([]any)
require.True(t, ok)
assert.Len(t, tags, 1)
@@ -62,7 +64,7 @@ func TestKAV2_TaggingOperations(t *testing.T) {
require.Equal(t, http.StatusOK, listRec2.Code)
var listOut2 map[string]any
- require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listOut2))
+ require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listOut2, jsonx.V1()))
tags2, ok := listOut2["Tags"].([]any)
require.True(t, ok)
assert.Len(t, tags2, 1)
diff --git a/services/kinesisanalyticsv2/handler_test.go b/services/kinesisanalyticsv2/handler_test.go
index 65928d1933..aea291aae1 100644
--- a/services/kinesisanalyticsv2/handler_test.go
+++ b/services/kinesisanalyticsv2/handler_test.go
@@ -2,7 +2,7 @@ package kinesisanalyticsv2_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kinesisanalyticsv2"
)
@@ -34,7 +35,7 @@ func doKAV2Request(t *testing.T, h *kinesisanalyticsv2.Handler, op string, body
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
return doRawKAV2Request(t, h, op, bodyBytes)
diff --git a/services/kinesisanalyticsv2/persistence.go b/services/kinesisanalyticsv2/persistence.go
index f0b5d54854..bd92e73056 100644
--- a/services/kinesisanalyticsv2/persistence.go
+++ b/services/kinesisanalyticsv2/persistence.go
@@ -2,7 +2,7 @@ package kinesisanalyticsv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -239,7 +239,7 @@ func fromPersistedSnap(p *persistedSnapshot) *Snapshot {
// decoding a snapshot from an incompatible (older or newer) build of this
// backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
// FlinkStopSaves keeps each application's stop-with-savepoint location for RESTORE_FROM_LATEST_SNAPSHOT.
FlinkStopSaves map[string]persistedStopSave `json:"flink_stop_saves,omitempty"`
NextID int64 `json:"next_id"`
diff --git a/services/kinesisanalyticsv2/wire_field_fixes_test.go b/services/kinesisanalyticsv2/wire_field_fixes_test.go
index c783448136..e46dc5bdaa 100644
--- a/services/kinesisanalyticsv2/wire_field_fixes_test.go
+++ b/services/kinesisanalyticsv2/wire_field_fixes_test.go
@@ -1,12 +1,14 @@
package kinesisanalyticsv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestKAV2_UpdateApplication_ApplicationDescription_NotARealField proves
@@ -43,7 +45,7 @@ func TestKAV2_UpdateApplication_ApplicationDescription_NotARealField(t *testing.
require.Equal(t, http.StatusOK, updateRec.Code)
var updateOut map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut, jsonx.V1()))
detail, ok := updateOut["ApplicationDetail"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "original", detail["ApplicationDescription"],
@@ -53,7 +55,7 @@ func TestKAV2_UpdateApplication_ApplicationDescription_NotARealField(t *testing.
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
descDetail, ok := descOut["ApplicationDetail"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "original", descDetail["ApplicationDescription"])
diff --git a/services/kinesisvideo/handler.go b/services/kinesisvideo/handler.go
index cfc39c381b..36490c3185 100644
--- a/services/kinesisvideo/handler.go
+++ b/services/kinesisvideo/handler.go
@@ -1,7 +1,7 @@
package kinesisvideo
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"maps"
"net/http"
@@ -10,6 +10,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -249,7 +250,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var data map[string]any
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
diff --git a/services/kinesisvideo/handler_configs.go b/services/kinesisvideo/handler_configs.go
index 85011a8ac4..e03729a8d3 100644
--- a/services/kinesisvideo/handler_configs.go
+++ b/services/kinesisvideo/handler_configs.go
@@ -1,10 +1,12 @@
package kinesisvideo
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) buildConfigOps() map[string]handlerFunc {
@@ -18,7 +20,7 @@ func (h *Handler) buildConfigOps() map[string]handlerFunc {
func (h *Handler) handleDescribeImageGenerationConfiguration(c *echo.Context, body []byte) error {
var req describeImageGenerationConfigurationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -34,7 +36,7 @@ func (h *Handler) handleDescribeImageGenerationConfiguration(c *echo.Context, bo
func (h *Handler) handleUpdateImageGenerationConfiguration(c *echo.Context, body []byte) error {
var req updateImageGenerationConfigurationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -49,7 +51,7 @@ func (h *Handler) handleUpdateImageGenerationConfiguration(c *echo.Context, body
func (h *Handler) handleDescribeNotificationConfiguration(c *echo.Context, body []byte) error {
var req describeNotificationConfigurationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -65,7 +67,7 @@ func (h *Handler) handleDescribeNotificationConfiguration(c *echo.Context, body
func (h *Handler) handleUpdateNotificationConfiguration(c *echo.Context, body []byte) error {
var req updateNotificationConfigurationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
diff --git a/services/kinesisvideo/handler_edge.go b/services/kinesisvideo/handler_edge.go
index c902a64cec..839a155ec6 100644
--- a/services/kinesisvideo/handler_edge.go
+++ b/services/kinesisvideo/handler_edge.go
@@ -1,10 +1,12 @@
package kinesisvideo
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) buildEdgeOps() map[string]handlerFunc {
@@ -18,7 +20,7 @@ func (h *Handler) buildEdgeOps() map[string]handlerFunc {
func (h *Handler) handleStartEdgeConfigurationUpdate(c *echo.Context, body []byte) error {
var req edgeStreamRequest
- if err := json.Unmarshal(body, &req); err != nil || req.EdgeConfig == nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil || req.EdgeConfig == nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -32,7 +34,7 @@ func (h *Handler) handleStartEdgeConfigurationUpdate(c *echo.Context, body []byt
func (h *Handler) handleDescribeEdgeConfiguration(c *echo.Context, body []byte) error {
var req edgeStreamRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -46,7 +48,7 @@ func (h *Handler) handleDescribeEdgeConfiguration(c *echo.Context, body []byte)
func (h *Handler) handleDeleteEdgeConfiguration(c *echo.Context, body []byte) error {
var req edgeStreamRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -59,7 +61,7 @@ func (h *Handler) handleDeleteEdgeConfiguration(c *echo.Context, body []byte) er
func (h *Handler) handleListEdgeAgentConfigurations(c *echo.Context, body []byte) error {
var req listEdgeAgentConfigurationsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
diff --git a/services/kinesisvideo/handler_signaling.go b/services/kinesisvideo/handler_signaling.go
index 07b7344086..a9d5587384 100644
--- a/services/kinesisvideo/handler_signaling.go
+++ b/services/kinesisvideo/handler_signaling.go
@@ -1,10 +1,12 @@
package kinesisvideo
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) buildSignalingOps() map[string]handlerFunc {
@@ -19,7 +21,7 @@ func (h *Handler) buildSignalingOps() map[string]handlerFunc {
func (h *Handler) handleCreateSignalingChannel(c *echo.Context, body []byte) error {
var req createSignalingChannelRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -48,7 +50,7 @@ func (h *Handler) handleCreateSignalingChannel(c *echo.Context, body []byte) err
func (h *Handler) handleDescribeSignalingChannel(c *echo.Context, body []byte) error {
var req describeSignalingChannelRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -60,9 +62,10 @@ func (h *Handler) handleDescribeSignalingChannel(c *echo.Context, body []byte) e
return h.writeJSON(c, describeSignalingChannelResponse{ChannelInfo: channelInfoFromChannel(ch)})
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) handleListSignalingChannels(c *echo.Context, body []byte) error {
var req listSignalingChannelsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -89,7 +92,7 @@ func (h *Handler) handleListSignalingChannels(c *echo.Context, body []byte) erro
func (h *Handler) handleUpdateSignalingChannel(c *echo.Context, body []byte) error {
var req updateSignalingChannelRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -117,7 +120,7 @@ func (h *Handler) handleUpdateSignalingChannel(c *echo.Context, body []byte) err
func (h *Handler) handleDeleteSignalingChannel(c *echo.Context, body []byte) error {
var req deleteSignalingChannelRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
diff --git a/services/kinesisvideo/handler_storage.go b/services/kinesisvideo/handler_storage.go
index c14b2eed3d..cb307e5515 100644
--- a/services/kinesisvideo/handler_storage.go
+++ b/services/kinesisvideo/handler_storage.go
@@ -1,10 +1,12 @@
package kinesisvideo
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) buildStorageOps() map[string]handlerFunc {
@@ -19,7 +21,7 @@ func (h *Handler) buildStorageOps() map[string]handlerFunc {
func (h *Handler) handleDescribeStreamStorageConfiguration(c *echo.Context, body []byte) error {
var req streamStorageConfigRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -37,7 +39,7 @@ func (h *Handler) handleDescribeStreamStorageConfiguration(c *echo.Context, body
func (h *Handler) handleUpdateStreamStorageConfiguration(c *echo.Context, body []byte) error {
var req streamStorageConfigRequest
- if err := json.Unmarshal(body, &req); err != nil || req.StreamStorageConfiguration == nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil || req.StreamStorageConfiguration == nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -52,7 +54,7 @@ func (h *Handler) handleUpdateStreamStorageConfiguration(c *echo.Context, body [
func (h *Handler) handleDescribeMediaStorageConfiguration(c *echo.Context, body []byte) error {
var req mediaStorageConfigRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -71,7 +73,12 @@ func (h *Handler) handleDescribeMediaStorageConfiguration(c *echo.Context, body
func (h *Handler) handleUpdateMediaStorageConfiguration(c *echo.Context, body []byte) error {
var req mediaStorageConfigRequest
- if err := json.Unmarshal(body, &req); err != nil || req.MediaStorageConfiguration == nil || req.ChannelARN == "" {
+ if err := json.Unmarshal(
+ body,
+ &req,
+ jsonx.V1(),
+ ); err != nil || req.MediaStorageConfiguration == nil ||
+ req.ChannelARN == "" {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -89,7 +96,7 @@ func (h *Handler) handleUpdateMediaStorageConfiguration(c *echo.Context, body []
func (h *Handler) handleGetSignalingChannelEndpoint(c *echo.Context, body []byte) error {
var req getSignalingChannelEndpointRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
diff --git a/services/kinesisvideo/handler_streams.go b/services/kinesisvideo/handler_streams.go
index 6dfd939883..c533f93233 100644
--- a/services/kinesisvideo/handler_streams.go
+++ b/services/kinesisvideo/handler_streams.go
@@ -1,12 +1,13 @@
package kinesisvideo
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) buildStreamOps() map[string]handlerFunc {
@@ -23,7 +24,7 @@ func (h *Handler) buildStreamOps() map[string]handlerFunc {
func (h *Handler) handleCreateStream(c *echo.Context, body []byte) error {
var req createStreamRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -49,7 +50,7 @@ func (h *Handler) handleCreateStream(c *echo.Context, body []byte) error {
func (h *Handler) handleDescribeStream(c *echo.Context, body []byte) error {
var req describeStreamRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -61,9 +62,10 @@ func (h *Handler) handleDescribeStream(c *echo.Context, body []byte) error {
return h.writeJSON(c, describeStreamResponse{StreamInfo: streamInfoFromStream(s)})
}
+//nolint:dupl // structurally similar JSON-protocol handlers
func (h *Handler) handleListStreams(c *echo.Context, body []byte) error {
var req listStreamsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -90,7 +92,7 @@ func (h *Handler) handleListStreams(c *echo.Context, body []byte) error {
func (h *Handler) handleUpdateStream(c *echo.Context, body []byte) error {
var req updateStreamRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -113,7 +115,7 @@ func (h *Handler) handleUpdateStream(c *echo.Context, body []byte) error {
func (h *Handler) handleDeleteStream(c *echo.Context, body []byte) error {
var req deleteStreamRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -130,7 +132,7 @@ func (h *Handler) handleDeleteStream(c *echo.Context, body []byte) error {
func (h *Handler) handleUpdateDataRetention(c *echo.Context, body []byte) error {
var req updateDataRetentionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -154,7 +156,7 @@ func (h *Handler) handleUpdateDataRetention(c *echo.Context, body []byte) error
func (h *Handler) handleGetDataEndpoint(c *echo.Context, body []byte) error {
var req getDataEndpointRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
diff --git a/services/kinesisvideo/handler_tags.go b/services/kinesisvideo/handler_tags.go
index 7ca8bf6d15..1991b15159 100644
--- a/services/kinesisvideo/handler_tags.go
+++ b/services/kinesisvideo/handler_tags.go
@@ -1,10 +1,12 @@
package kinesisvideo
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) buildTagOps() map[string]handlerFunc {
@@ -20,7 +22,7 @@ func (h *Handler) buildTagOps() map[string]handlerFunc {
func (h *Handler) handleTagStream(c *echo.Context, body []byte) error {
var req tagStreamRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -33,7 +35,7 @@ func (h *Handler) handleTagStream(c *echo.Context, body []byte) error {
func (h *Handler) handleUntagStream(c *echo.Context, body []byte) error {
var req untagStreamRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -46,7 +48,7 @@ func (h *Handler) handleUntagStream(c *echo.Context, body []byte) error {
func (h *Handler) handleListTagsForStream(c *echo.Context, body []byte) error {
var req listTagsForStreamRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -60,7 +62,7 @@ func (h *Handler) handleListTagsForStream(c *echo.Context, body []byte) error {
func (h *Handler) handleTagResource(c *echo.Context, body []byte) error {
var req tagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -82,7 +84,7 @@ func (h *Handler) handleTagResource(c *echo.Context, body []byte) error {
func (h *Handler) handleUntagResource(c *echo.Context, body []byte) error {
var req untagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
@@ -99,7 +101,7 @@ func (h *Handler) handleUntagResource(c *echo.Context, body []byte) error {
func (h *Handler) handleListTagsForResource(c *echo.Context, body []byte) error {
var req listTagsForResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidArgumentException", "invalid request body")
}
diff --git a/services/kinesisvideo/multi_region_test.go b/services/kinesisvideo/multi_region_test.go
index 15609ee2b9..ba9f68a039 100644
--- a/services/kinesisvideo/multi_region_test.go
+++ b/services/kinesisvideo/multi_region_test.go
@@ -2,7 +2,8 @@ package kinesisvideo_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/kinesisvideo"
)
@@ -175,8 +177,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/kinesisvideo/persistence.go b/services/kinesisvideo/persistence.go
index a80dab1c03..b31210ac69 100644
--- a/services/kinesisvideo/persistence.go
+++ b/services/kinesisvideo/persistence.go
@@ -2,7 +2,7 @@ package kinesisvideo
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
@@ -29,8 +29,8 @@ const kinesisvideoSnapshotVersion = 1
// one JSON-encoded array per registered table name (streams, channels -- see
// store_setup.go), produced by b.registry.SnapshotAll().
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serializes backend state to JSON.
diff --git a/services/kms/aliases_test.go b/services/kms/aliases_test.go
index c9042ddcc2..f5ed225276 100644
--- a/services/kms/aliases_test.go
+++ b/services/kms/aliases_test.go
@@ -2,7 +2,7 @@ package kms_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
"strings"
@@ -631,7 +632,7 @@ func TestCreateAlias_InvalidName_WireErrorType(t *testing.T) {
var body struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidAliasNameException", body.Type)
assert.NotEqual(t, "ValidationException", body.Type)
}
diff --git a/services/kms/custom_key_stores_test.go b/services/kms/custom_key_stores_test.go
index 3f2baa09b8..0c69147653 100644
--- a/services/kms/custom_key_stores_test.go
+++ b/services/kms/custom_key_stores_test.go
@@ -2,7 +2,7 @@ package kms_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
)
@@ -558,7 +559,7 @@ func TestCustomKeyStore_StateGuards_WireErrorType(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut kms.CreateCustomKeyStoreOutput
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
connectBody := `{"CustomKeyStoreId":"` + createOut.CustomKeyStoreID + `"}`
require.Equal(t, http.StatusOK, doKMSRequest(t, h, "ConnectCustomKeyStore", connectBody).Code)
@@ -567,7 +568,7 @@ func TestCustomKeyStore_StateGuards_WireErrorType(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "CustomKeyStoreInvalidStateException", errResp.Type)
})
@@ -580,14 +581,14 @@ func TestCustomKeyStore_StateGuards_WireErrorType(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut kms.CreateCustomKeyStoreOutput
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
disconnectBody := `{"CustomKeyStoreId":"` + createOut.CustomKeyStoreID + `"}`
rec := doKMSRequest(t, h, "DisconnectCustomKeyStore", disconnectBody)
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "CustomKeyStoreInvalidStateException", errResp.Type)
})
@@ -600,7 +601,7 @@ func TestCustomKeyStore_StateGuards_WireErrorType(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut kms.CreateCustomKeyStoreOutput
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
idBody := `{"CustomKeyStoreId":"` + createOut.CustomKeyStoreID + `"}`
require.Equal(t, http.StatusOK, doKMSRequest(t, h, "ConnectCustomKeyStore", idBody).Code)
@@ -609,7 +610,7 @@ func TestCustomKeyStore_StateGuards_WireErrorType(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "CustomKeyStoreInvalidStateException", errResp.Type)
})
}
@@ -629,7 +630,7 @@ func TestDeleteCustomKeyStore_HasKeys_WireErrorType(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut kms.CreateCustomKeyStoreOutput
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
idBody := `{"CustomKeyStoreId":"` + createOut.CustomKeyStoreID + `"}`
require.Equal(t, http.StatusOK, doKMSRequest(t, h, "ConnectCustomKeyStore", idBody).Code)
@@ -643,7 +644,7 @@ func TestDeleteCustomKeyStore_HasKeys_WireErrorType(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "CustomKeyStoreHasCMKsException", errResp.Type)
}
@@ -662,7 +663,7 @@ func TestEncrypt_DisconnectedCustomKeyStore_WireErrorType(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut kms.CreateCustomKeyStoreOutput
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
idBody := `{"CustomKeyStoreId":"` + createOut.CustomKeyStoreID + `"}`
require.Equal(t, http.StatusOK, doKMSRequest(t, h, "ConnectCustomKeyStore", idBody).Code)
@@ -672,7 +673,7 @@ func TestEncrypt_DisconnectedCustomKeyStore_WireErrorType(t *testing.T) {
require.Equal(t, http.StatusOK, createKeyRec.Code)
var createKeyOut kms.CreateKeyOutput
- require.NoError(t, json.Unmarshal(createKeyRec.Body.Bytes(), &createKeyOut))
+ require.NoError(t, json.Unmarshal(createKeyRec.Body.Bytes(), &createKeyOut, jsonx.V1()))
require.Equal(t, http.StatusOK, doKMSRequest(t, h, "DisconnectCustomKeyStore", idBody).Code)
@@ -681,6 +682,6 @@ func TestEncrypt_DisconnectedCustomKeyStore_WireErrorType(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "KMSInvalidStateException", errResp.Type)
}
diff --git a/services/kms/describe_key_grant_tokens_test.go b/services/kms/describe_key_grant_tokens_test.go
index 42e3cf0d5d..3881e0af5a 100644
--- a/services/kms/describe_key_grant_tokens_test.go
+++ b/services/kms/describe_key_grant_tokens_test.go
@@ -16,12 +16,13 @@ package kms_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
)
@@ -31,7 +32,7 @@ func TestDescribeKey_GrantTokens_WireField(t *testing.T) {
// The field must deserialize from the AWS wire name "GrantTokens".
var input kms.DescribeKeyInput
require.NoError(t, json.Unmarshal(
- []byte(`{"KeyId":"k-123","GrantTokens":["tok-a","tok-b"]}`), &input))
+ []byte(`{"KeyId":"k-123","GrantTokens":["tok-a","tok-b"]}`), &input, jsonx.V1()))
assert.Equal(t, "k-123", input.KeyID)
assert.Equal(t, []string{"tok-a", "tok-b"}, input.GrantTokens)
}
diff --git a/services/kms/errtarget_findings_test.go b/services/kms/errtarget_findings_test.go
index fb0f0d9f3d..0bc62aded8 100644
--- a/services/kms/errtarget_findings_test.go
+++ b/services/kms/errtarget_findings_test.go
@@ -2,7 +2,7 @@ package kms_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
)
@@ -34,7 +35,7 @@ func TestCancelKeyDeletion_DisabledKey_NotDisabledException(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "KMSInvalidStateException", errResp.Type)
desc, err := h.Backend.DescribeKey(context.Background(), &kms.DescribeKeyInput{KeyID: keyID})
@@ -73,7 +74,7 @@ func TestDecrypt_DisabledKey_StillDisabledException(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "DisabledException", errResp.Type)
}
@@ -119,7 +120,7 @@ func TestImportKeyMaterial_DisabledKey_NotDisabledException(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "KMSInvalidStateException", errResp.Type)
require.NoError(t, h.Backend.EnableKey(context.Background(), &kms.EnableKeyInput{KeyID: keyID}))
@@ -169,7 +170,7 @@ func TestVerifyMac_Mismatch_IsInvalidMac_NotInvalidSignature(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "KMSInvalidMacException", errResp.Type)
}
@@ -211,6 +212,6 @@ func TestDecrypt_ExpiredImportedMaterial_KMSInvalidStateException(t *testing.T)
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "KMSInvalidStateException", errResp.Type)
}
diff --git a/services/kms/grant_authz.go b/services/kms/grant_authz.go
index 511503e250..19a8533290 100644
--- a/services/kms/grant_authz.go
+++ b/services/kms/grant_authz.go
@@ -2,13 +2,15 @@ package kms
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"slices"
"strings"
gopherarn "github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -162,16 +164,16 @@ func statementCoversAction(s *policyStatement, action string) bool {
func keyPolicyAllows(policy string, p *awsmeta.Principal, action string) bool {
var doc struct {
- Statement json.RawMessage `json:"Statement"`
+ Statement jsontext.Value `json:"Statement"`
}
- if json.Unmarshal([]byte(policy), &doc) != nil {
+ if json.Unmarshal([]byte(policy), &doc, jsonx.V1()) != nil {
return false
}
var stmts []policyStatement
- if json.Unmarshal(doc.Statement, &stmts) != nil {
+ if json.Unmarshal(doc.Statement, &stmts, jsonx.V1()) != nil {
var one policyStatement
- if json.Unmarshal(doc.Statement, &one) != nil {
+ if json.Unmarshal(doc.Statement, &one, jsonx.V1()) != nil {
return false
}
diff --git a/services/kms/handler.go b/services/kms/handler.go
index 12296e1dd3..b149104cab 100644
--- a/services/kms/handler.go
+++ b/services/kms/handler.go
@@ -2,7 +2,7 @@ package kms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -13,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -215,7 +216,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var data map[string]any
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
@@ -262,7 +263,7 @@ func (h *Handler) buildDispatchTable() map[string]kmsActionFn {
func unmarshalAction[T any](fn func(context.Context, *T) (any, error)) kmsActionFn {
return func(ctx context.Context, b []byte) (any, error) {
var input T
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -300,7 +301,7 @@ func (h *Handler) dispatch(ctx context.Context, r *http.Request, action string,
return nil, err
}
- return json.Marshal(response)
+ return json.Marshal(response, jsonx.V1())
}
// handleError writes a structured error response for a KMS operation failure.
@@ -389,7 +390,7 @@ func (h *Handler) handleError(ctx context.Context, c *echo.Context, action strin
payload, _ := json.Marshal(ErrorResponse{
Type: errorType,
Message: errorMessage(reqErr),
- })
+ }, jsonx.V1())
return c.JSONBlob(statusCode, payload)
}
diff --git a/services/kms/handler_alias_rotation.go b/services/kms/handler_alias_rotation.go
index 97a503ee54..0222fa60bc 100644
--- a/services/kms/handler_alias_rotation.go
+++ b/services/kms/handler_alias_rotation.go
@@ -2,7 +2,9 @@ package kms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// buildAliasRotationActions returns dispatch entries for alias management and key rotation.
@@ -10,7 +12,7 @@ func (h *Handler) buildAliasRotationActions() map[string]kmsActionFn {
return map[string]kmsActionFn{
"CreateAlias": func(ctx context.Context, b []byte) (any, error) {
var input CreateAliasInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -18,7 +20,7 @@ func (h *Handler) buildAliasRotationActions() map[string]kmsActionFn {
},
"UpdateAlias": func(ctx context.Context, b []byte) (any, error) {
var input UpdateAliasInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -26,7 +28,7 @@ func (h *Handler) buildAliasRotationActions() map[string]kmsActionFn {
},
"DeleteAlias": func(ctx context.Context, b []byte) (any, error) {
var input DeleteAliasInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -34,7 +36,7 @@ func (h *Handler) buildAliasRotationActions() map[string]kmsActionFn {
},
"ListAliases": func(ctx context.Context, b []byte) (any, error) {
var input ListAliasesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -42,7 +44,7 @@ func (h *Handler) buildAliasRotationActions() map[string]kmsActionFn {
},
"EnableKeyRotation": func(ctx context.Context, b []byte) (any, error) {
var input EnableKeyRotationInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -50,7 +52,7 @@ func (h *Handler) buildAliasRotationActions() map[string]kmsActionFn {
},
"DisableKeyRotation": func(ctx context.Context, b []byte) (any, error) {
var input DisableKeyRotationInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -58,7 +60,7 @@ func (h *Handler) buildAliasRotationActions() map[string]kmsActionFn {
},
"GetKeyRotationStatus": func(ctx context.Context, b []byte) (any, error) {
var input GetKeyRotationStatusInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/kms/handler_alias_rotation_test.go b/services/kms/handler_alias_rotation_test.go
index f3de8b48df..cc7dd8349b 100644
--- a/services/kms/handler_alias_rotation_test.go
+++ b/services/kms/handler_alias_rotation_test.go
@@ -2,7 +2,7 @@ package kms_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
"strings"
@@ -39,7 +40,7 @@ func TestHandler_ListAliases_FilterByKeyID_ViaHTTP(t *testing.T) {
AliasName string `json:"AliasName"`
} `json:"Aliases"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.Aliases)
for _, a := range resp.Aliases {
assert.Equal(t, "alias/filter-test", a.AliasName)
@@ -61,7 +62,7 @@ func TestHandler_RotateKeyOnDemand_ViaHTTP(t *testing.T) {
var resp struct {
KeyID string `json:"KeyId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, keyID, resp.KeyID)
}
@@ -86,7 +87,7 @@ func TestHandler_EnableKeyRotation_ViaHTTP(t *testing.T) {
KeyRotationEnabled bool `json:"KeyRotationEnabled"`
RotationPeriodInDays int32 `json:"RotationPeriodInDays"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &status))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &status, jsonx.V1()))
assert.True(t, status.KeyRotationEnabled)
assert.Equal(t, int32(90), status.RotationPeriodInDays)
}
@@ -107,14 +108,14 @@ func TestKMSHandlerAliasOperations(t *testing.T) {
require.NoError(t, h.Handler()(e.NewContext(createReq, createRec)))
var createOut kms.CreateKeyOutput
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut.KeyMetadata.KeyID
// CreateAlias
aliasBody, _ := json.Marshal(map[string]string{
"AliasName": "alias/test-alias",
"TargetKeyId": keyID,
- })
+ }, jsonx.V1())
aliasReq := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(aliasBody)))
aliasReq.Header.Set("X-Amz-Target", "TrentService.CreateAlias")
aliasRec := httptest.NewRecorder()
@@ -128,11 +129,11 @@ func TestKMSHandlerAliasOperations(t *testing.T) {
require.NoError(t, h.Handler()(e.NewContext(listReq, listRec)))
var listOut kms.ListAliasesOutput
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut.Aliases, 1)
// DeleteAlias
- deleteBody, _ := json.Marshal(map[string]string{"AliasName": "alias/test-alias"})
+ deleteBody, _ := json.Marshal(map[string]string{"AliasName": "alias/test-alias"}, jsonx.V1())
deleteReq := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(deleteBody)))
deleteReq.Header.Set("X-Amz-Target", "TrentService.DeleteAlias")
deleteRec := httptest.NewRecorder()
@@ -156,11 +157,11 @@ func TestKMSHandlerKeyRotation(t *testing.T) {
require.NoError(t, h.Handler()(e.NewContext(createReq, createRec)))
var createOut kms.CreateKeyOutput
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut.KeyMetadata.KeyID
// GetKeyRotationStatus
- statusBody, _ := json.Marshal(map[string]string{"KeyID": keyID})
+ statusBody, _ := json.Marshal(map[string]string{"KeyID": keyID}, jsonx.V1())
statusReq := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(statusBody)))
statusReq.Header.Set("X-Amz-Target", "TrentService.GetKeyRotationStatus")
@@ -168,11 +169,11 @@ func TestKMSHandlerKeyRotation(t *testing.T) {
require.NoError(t, h.Handler()(e.NewContext(statusReq, statusRec)))
var statusOut kms.GetKeyRotationStatusOutput
- require.NoError(t, json.Unmarshal(statusRec.Body.Bytes(), &statusOut))
+ require.NoError(t, json.Unmarshal(statusRec.Body.Bytes(), &statusOut, jsonx.V1()))
assert.False(t, statusOut.KeyRotationEnabled)
// EnableKeyRotation
- enableBody, _ := json.Marshal(map[string]string{"KeyID": keyID})
+ enableBody, _ := json.Marshal(map[string]string{"KeyID": keyID}, jsonx.V1())
enableReq := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(enableBody)))
enableReq.Header.Set("X-Amz-Target", "TrentService.EnableKeyRotation")
enableRec := httptest.NewRecorder()
@@ -180,7 +181,7 @@ func TestKMSHandlerKeyRotation(t *testing.T) {
assert.Equal(t, http.StatusOK, enableRec.Code)
// DisableKeyRotation
- disableBody, _ := json.Marshal(map[string]string{"KeyID": keyID})
+ disableBody, _ := json.Marshal(map[string]string{"KeyID": keyID}, jsonx.V1())
disableReq := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(disableBody)))
disableReq.Header.Set("X-Amz-Target", "TrentService.DisableKeyRotation")
disableRec := httptest.NewRecorder()
@@ -293,7 +294,7 @@ func TestKMSHandlerErrorCases(t *testing.T) {
assert.Equal(t, tt.expectedStatus, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.expectedErrTyp, errResp.Type)
})
}
@@ -321,7 +322,7 @@ func TestKMSListAliasesFiltered(t *testing.T) {
)
// Filter by key1
- body, _ := json.Marshal(map[string]string{"KeyId": key1.KeyMetadata.KeyID})
+ body, _ := json.Marshal(map[string]string{"KeyId": key1.KeyMetadata.KeyID}, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(body)))
req.Header.Set("X-Amz-Target", "TrentService.ListAliases")
rec := httptest.NewRecorder()
@@ -329,7 +330,7 @@ func TestKMSListAliasesFiltered(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out kms.ListAliasesOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Aliases, 1)
assert.Equal(t, "alias/key1", out.Aliases[0].AliasName)
}
@@ -354,7 +355,7 @@ func TestKMSUpdateAlias_Handler(t *testing.T) {
body, err := json.Marshal(kms.UpdateAliasInput{
AliasName: "alias/handler-update-test",
TargetKeyID: k2.KeyMetadata.KeyID,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doKMSHTTPRequest(t, h, "UpdateAlias", string(body))
@@ -377,7 +378,7 @@ func TestHandlerCreateAliasWhitespaceRejected(t *testing.T) {
require.Equal(t, http.StatusOK, keyRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut["KeyMetadata"].(map[string]any)["KeyId"].(string)
rec := sendKMSOp(t, h, "CreateAlias", `{"AliasName":"alias/my bad alias","TargetKeyId":"`+keyID+`"}`)
@@ -394,7 +395,7 @@ func TestHandlerListKeyRotationsViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, keyRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut["KeyMetadata"].(map[string]any)["KeyId"].(string)
sendKMSOp(t, h, "RotateKeyOnDemand", `{"KeyId":"`+keyID+`"}`)
@@ -403,7 +404,7 @@ func TestHandlerListKeyRotationsViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
rotations := out["Rotations"].([]any)
require.NotEmpty(t, rotations)
diff --git a/services/kms/handler_crypto.go b/services/kms/handler_crypto.go
index 5229e6a679..4180a76991 100644
--- a/services/kms/handler_crypto.go
+++ b/services/kms/handler_crypto.go
@@ -2,7 +2,9 @@ package kms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// buildCryptoActions returns dispatch entries for encrypt, decrypt, sign, verify, and data-key operations.
@@ -10,7 +12,7 @@ func (h *Handler) buildCryptoActions() map[string]kmsActionFn {
return map[string]kmsActionFn{
opEncrypt: func(ctx context.Context, b []byte) (any, error) {
var input EncryptInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -18,7 +20,7 @@ func (h *Handler) buildCryptoActions() map[string]kmsActionFn {
},
opDecrypt: func(ctx context.Context, b []byte) (any, error) {
var input DecryptInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -26,7 +28,7 @@ func (h *Handler) buildCryptoActions() map[string]kmsActionFn {
},
opGenerateDataKey: func(ctx context.Context, b []byte) (any, error) {
var input GenerateDataKeyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -34,7 +36,7 @@ func (h *Handler) buildCryptoActions() map[string]kmsActionFn {
},
opGenerateDataKeyWithoutPlaintext: func(ctx context.Context, b []byte) (any, error) {
var input GenerateDataKeyWithoutPlaintextInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -42,7 +44,7 @@ func (h *Handler) buildCryptoActions() map[string]kmsActionFn {
},
"ReEncrypt": func(ctx context.Context, b []byte) (any, error) {
var input ReEncryptInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -50,7 +52,7 @@ func (h *Handler) buildCryptoActions() map[string]kmsActionFn {
},
opSign: func(ctx context.Context, b []byte) (any, error) {
var input SignInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -58,7 +60,7 @@ func (h *Handler) buildCryptoActions() map[string]kmsActionFn {
},
opVerify: func(ctx context.Context, b []byte) (any, error) {
var input VerifyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -66,7 +68,7 @@ func (h *Handler) buildCryptoActions() map[string]kmsActionFn {
},
opGetPublicKey: func(ctx context.Context, b []byte) (any, error) {
var input GetPublicKeyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/kms/handler_crypto_test.go b/services/kms/handler_crypto_test.go
index 1cde50a4dc..e323e5b71b 100644
--- a/services/kms/handler_crypto_test.go
+++ b/services/kms/handler_crypto_test.go
@@ -1,7 +1,7 @@
package kms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
"encoding/base64"
@@ -32,7 +33,7 @@ func TestKMSHandlerEncryptDecrypt(t *testing.T) {
require.NoError(t, h.Handler()(e.NewContext(createReq, createRec)))
var createOut kms.CreateKeyOutput
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut.KeyMetadata.KeyID
@@ -40,7 +41,7 @@ func TestKMSHandlerEncryptDecrypt(t *testing.T) {
encBody, _ := json.Marshal(map[string]any{
"KeyID": keyID,
"Plaintext": []byte("my-secret"),
- })
+ }, jsonx.V1())
encReq := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(encBody)))
encReq.Header.Set("X-Amz-Target", "TrentService.Encrypt")
encRec := httptest.NewRecorder()
@@ -48,10 +49,10 @@ func TestKMSHandlerEncryptDecrypt(t *testing.T) {
assert.Equal(t, http.StatusOK, encRec.Code)
var encOut kms.EncryptOutput
- require.NoError(t, json.Unmarshal(encRec.Body.Bytes(), &encOut))
+ require.NoError(t, json.Unmarshal(encRec.Body.Bytes(), &encOut, jsonx.V1()))
// Decrypt
- decBody, _ := json.Marshal(map[string]any{"CiphertextBlob": encOut.CiphertextBlob})
+ decBody, _ := json.Marshal(map[string]any{"CiphertextBlob": encOut.CiphertextBlob}, jsonx.V1())
decReq := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(decBody)))
decReq.Header.Set("X-Amz-Target", "TrentService.Decrypt")
decRec := httptest.NewRecorder()
@@ -59,7 +60,7 @@ func TestKMSHandlerEncryptDecrypt(t *testing.T) {
assert.Equal(t, http.StatusOK, decRec.Code)
var decOut kms.DecryptOutput
- require.NoError(t, json.Unmarshal(decRec.Body.Bytes(), &decOut))
+ require.NoError(t, json.Unmarshal(decRec.Body.Bytes(), &decOut, jsonx.V1()))
assert.Equal(t, []byte("my-secret"), decOut.Plaintext)
}
@@ -79,7 +80,7 @@ func TestKMSHandlerReEncrypt(t *testing.T) {
require.NoError(t, h.Handler()(e.NewContext(createReq1, createRec1)))
var out1 kms.CreateKeyOutput
- require.NoError(t, json.Unmarshal(createRec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(createRec1.Body.Bytes(), &out1, jsonx.V1()))
createReq2 := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(`{}`))
createReq2.Header.Set("X-Amz-Target", "TrentService.CreateKey")
@@ -87,26 +88,26 @@ func TestKMSHandlerReEncrypt(t *testing.T) {
require.NoError(t, h.Handler()(e.NewContext(createReq2, createRec2)))
var out2 kms.CreateKeyOutput
- require.NoError(t, json.Unmarshal(createRec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(createRec2.Body.Bytes(), &out2, jsonx.V1()))
// Encrypt with key1
encBody, _ := json.Marshal(map[string]any{
"KeyId": out1.KeyMetadata.KeyID,
"Plaintext": []byte("reencrypt-me"),
- })
+ }, jsonx.V1())
encReq := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(encBody)))
encReq.Header.Set("X-Amz-Target", "TrentService.Encrypt")
encRec := httptest.NewRecorder()
require.NoError(t, h.Handler()(e.NewContext(encReq, encRec)))
var encOut kms.EncryptOutput
- require.NoError(t, json.Unmarshal(encRec.Body.Bytes(), &encOut))
+ require.NoError(t, json.Unmarshal(encRec.Body.Bytes(), &encOut, jsonx.V1()))
// ReEncrypt with key2
reEncBody, _ := json.Marshal(map[string]any{
"CiphertextBlob": encOut.CiphertextBlob,
"DestinationKeyId": out2.KeyMetadata.KeyID,
- })
+ }, jsonx.V1())
reEncReq := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(reEncBody)))
reEncReq.Header.Set("X-Amz-Target", "TrentService.ReEncrypt")
reEncRec := httptest.NewRecorder()
@@ -114,7 +115,7 @@ func TestKMSHandlerReEncrypt(t *testing.T) {
assert.Equal(t, http.StatusOK, reEncRec.Code)
var reEncOut kms.ReEncryptOutput
- require.NoError(t, json.Unmarshal(reEncRec.Body.Bytes(), &reEncOut))
+ require.NoError(t, json.Unmarshal(reEncRec.Body.Bytes(), &reEncOut, jsonx.V1()))
assert.Equal(t, out2.KeyMetadata.Arn, reEncOut.KeyID)
}
@@ -140,7 +141,7 @@ func TestKMSHandlerInternalError(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidCiphertextException", errResp.Type)
}
@@ -154,12 +155,12 @@ func TestKMSHandlerSignVerify(t *testing.T) {
createBody, _ := json.Marshal(map[string]any{
"KeyUsage": kms.KeyUsageSignVerify,
"KeySpec": "RSA_2048",
- })
+ }, jsonx.V1())
rec := doKMSHTTPRequest(t, h, "CreateKey", string(createBody))
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
keyMeta, ok := createResp["KeyMetadata"].(map[string]any)
require.True(t, ok)
keyID, ok := keyMeta["KeyId"].(string)
@@ -171,12 +172,12 @@ func TestKMSHandlerSignVerify(t *testing.T) {
"Message": message,
"MessageType": "RAW",
"SigningAlgorithm": "RSASSA_PSS_SHA_256",
- })
+ }, jsonx.V1())
rec = doKMSHTTPRequest(t, h, "Sign", string(signBody))
require.Equal(t, http.StatusOK, rec.Code)
var signResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &signResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &signResp, jsonx.V1()))
sigRaw, ok := signResp["Signature"]
require.True(t, ok)
assert.NotEmpty(t, sigRaw)
@@ -188,12 +189,12 @@ func TestKMSHandlerSignVerify(t *testing.T) {
"MessageType": "RAW",
"Signature": sigRaw,
"SigningAlgorithm": "RSASSA_PSS_SHA_256",
- })
+ }, jsonx.V1())
rec = doKMSHTTPRequest(t, h, "Verify", string(verifyBody))
require.Equal(t, http.StatusOK, rec.Code)
var verifyResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verifyResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verifyResp, jsonx.V1()))
assert.True(t, verifyResp["SignatureValid"].(bool))
}
@@ -206,21 +207,21 @@ func TestKMSHandlerGetPublicKey(t *testing.T) {
createBody, _ := json.Marshal(map[string]any{
"KeyUsage": kms.KeyUsageSignVerify,
"KeySpec": "ECC_NIST_P256",
- })
+ }, jsonx.V1())
rec := doKMSHTTPRequest(t, h, "CreateKey", string(createBody))
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
keyMeta := createResp["KeyMetadata"].(map[string]any)
keyID := keyMeta["KeyId"].(string)
- getKeyBody, _ := json.Marshal(map[string]any{"KeyId": keyID})
+ getKeyBody, _ := json.Marshal(map[string]any{"KeyId": keyID}, jsonx.V1())
rec = doKMSHTTPRequest(t, h, "GetPublicKey", string(getKeyBody))
require.Equal(t, http.StatusOK, rec.Code)
var pubResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pubResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pubResp, jsonx.V1()))
assert.NotEmpty(t, pubResp["PublicKey"])
assert.Equal(t, "ECC_NIST_P256", pubResp["KeySpec"])
}
@@ -234,12 +235,12 @@ func TestKMSHandlerInvalidSignatureError(t *testing.T) {
createBody, _ := json.Marshal(map[string]any{
"KeyUsage": kms.KeyUsageSignVerify,
"KeySpec": "RSA_2048",
- })
+ }, jsonx.V1())
rec := doKMSHTTPRequest(t, h, "CreateKey", string(createBody))
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
keyMeta := createResp["KeyMetadata"].(map[string]any)
keyID := keyMeta["KeyId"].(string)
@@ -250,13 +251,13 @@ func TestKMSHandlerInvalidSignatureError(t *testing.T) {
"MessageType": "RAW",
"Signature": badSig,
"SigningAlgorithm": "RSASSA_PSS_SHA_256",
- })
+ }, jsonx.V1())
rec = doKMSHTTPRequest(t, h, "Verify", string(verifyBody))
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "KMSInvalidSignatureException", errResp["__type"])
}
@@ -270,7 +271,7 @@ func TestHandlerEncryptReturnsAlgorithm(t *testing.T) {
require.Equal(t, http.StatusOK, keyRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut["KeyMetadata"].(map[string]any)["KeyId"].(string)
plaintext := base64.StdEncoding.EncodeToString([]byte("test-plaintext"))
@@ -278,6 +279,6 @@ func TestHandlerEncryptReturnsAlgorithm(t *testing.T) {
require.Equal(t, http.StatusOK, encRec.Code)
var encOut map[string]any
- require.NoError(t, json.Unmarshal(encRec.Body.Bytes(), &encOut))
+ require.NoError(t, json.Unmarshal(encRec.Body.Bytes(), &encOut, jsonx.V1()))
assert.Equal(t, "SYMMETRIC_DEFAULT", encOut["EncryptionAlgorithm"])
}
diff --git a/services/kms/handler_custom_key_stores.go b/services/kms/handler_custom_key_stores.go
index b82bb73e6f..c043e2ba79 100644
--- a/services/kms/handler_custom_key_stores.go
+++ b/services/kms/handler_custom_key_stores.go
@@ -2,7 +2,9 @@ package kms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// buildCustomKeyStoreActions returns dispatch entries for custom key store and ECDH operations.
@@ -10,7 +12,7 @@ func (h *Handler) buildCustomKeyStoreActions() map[string]kmsActionFn {
return map[string]kmsActionFn{
"CreateCustomKeyStore": func(ctx context.Context, b []byte) (any, error) {
var input CreateCustomKeyStoreInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -18,7 +20,7 @@ func (h *Handler) buildCustomKeyStoreActions() map[string]kmsActionFn {
},
"DeleteCustomKeyStore": func(ctx context.Context, b []byte) (any, error) {
var input DeleteCustomKeyStoreInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -26,7 +28,7 @@ func (h *Handler) buildCustomKeyStoreActions() map[string]kmsActionFn {
},
"DescribeCustomKeyStores": func(ctx context.Context, b []byte) (any, error) {
var input DescribeCustomKeyStoresInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -34,7 +36,7 @@ func (h *Handler) buildCustomKeyStoreActions() map[string]kmsActionFn {
},
"ConnectCustomKeyStore": func(ctx context.Context, b []byte) (any, error) {
var input ConnectCustomKeyStoreInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -42,7 +44,7 @@ func (h *Handler) buildCustomKeyStoreActions() map[string]kmsActionFn {
},
"DisconnectCustomKeyStore": func(ctx context.Context, b []byte) (any, error) {
var input DisconnectCustomKeyStoreInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -50,7 +52,7 @@ func (h *Handler) buildCustomKeyStoreActions() map[string]kmsActionFn {
},
opDeriveSharedSecret: func(ctx context.Context, b []byte) (any, error) {
var input DeriveSharedSecretInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/kms/handler_custom_key_stores_test.go b/services/kms/handler_custom_key_stores_test.go
index a570c0b2b0..96c22eba16 100644
--- a/services/kms/handler_custom_key_stores_test.go
+++ b/services/kms/handler_custom_key_stores_test.go
@@ -3,7 +3,7 @@ package kms_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
)
@@ -31,7 +32,7 @@ func TestHandler_XKS_CreateDescribeViaHTTP(t *testing.T) {
var createResp struct {
CustomKeyStoreID string `json:"CustomKeyStoreId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.NotEmpty(t, createResp.CustomKeyStoreID)
// Describe the XKS store
@@ -45,7 +46,7 @@ func TestHandler_XKS_CreateDescribeViaHTTP(t *testing.T) {
ConnectionState string `json:"ConnectionState"`
} `json:"CustomKeyStores"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp, jsonx.V1()))
require.Len(t, descResp.CustomKeyStores, 1)
assert.Equal(t, "EXTERNAL_KEY_STORE", descResp.CustomKeyStores[0].CustomKeyStoreType)
assert.Equal(t, kms.ConnectionStateDisconnected, descResp.CustomKeyStores[0].ConnectionState)
@@ -129,7 +130,7 @@ func TestHandlerDispatchesCustomKeyStoreAndCryptoOps(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out kms.CreateCustomKeyStoreOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.CustomKeyStoreID)
},
},
@@ -150,7 +151,7 @@ func TestHandlerDispatchesCustomKeyStoreAndCryptoOps(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out kms.DescribeCustomKeyStoresOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.CustomKeyStores, 1)
},
},
@@ -199,7 +200,7 @@ func TestHandlerDispatchesCustomKeyStoreAndCryptoOps(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out kms.GenerateRandomOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Plaintext, 32)
},
},
@@ -223,7 +224,7 @@ func TestHandlerDispatchesCustomKeyStoreAndCryptoOps(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out kms.GenerateMacOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.Mac)
},
},
@@ -241,7 +242,7 @@ func TestHandlerDispatchesCustomKeyStoreAndCryptoOps(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out kms.GenerateDataKeyPairOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.PublicKey)
assert.NotEmpty(t, out.PrivateKeyPlaintext)
},
@@ -260,7 +261,7 @@ func TestHandlerDispatchesCustomKeyStoreAndCryptoOps(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out kms.GenerateDataKeyPairWithoutPlaintextOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.PublicKey)
assert.NotEmpty(t, out.PrivateKeyCiphertextBlob)
},
@@ -282,7 +283,7 @@ func TestHandlerDispatchesCustomKeyStoreAndCryptoOps(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out kms.DeriveSharedSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.SharedSecret)
},
},
@@ -349,7 +350,7 @@ func TestHandlerResetClearsCustomKeyStores(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out kms.DescribeCustomKeyStoresOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.CustomKeyStores)
}
@@ -365,21 +366,21 @@ func TestCustomKeyStoreHandler_ViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut kms.CreateCustomKeyStoreOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
storeID := createOut.CustomKeyStoreID
// Connect via HTTP.
- connectBody, _ := json.Marshal(map[string]string{"CustomKeyStoreId": storeID})
+ connectBody, _ := json.Marshal(map[string]string{"CustomKeyStoreId": storeID}, jsonx.V1())
rec = postKMSOp(t, h, "ConnectCustomKeyStore", string(connectBody))
require.Equal(t, http.StatusOK, rec.Code)
// Describe via HTTP – should be CONNECTED.
- descBody, _ := json.Marshal(map[string]string{"CustomKeyStoreId": storeID})
+ descBody, _ := json.Marshal(map[string]string{"CustomKeyStoreId": storeID}, jsonx.V1())
rec = postKMSOp(t, h, "DescribeCustomKeyStores", string(descBody))
require.Equal(t, http.StatusOK, rec.Code)
var descOut kms.DescribeCustomKeyStoresOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descOut, jsonx.V1()))
require.Len(t, descOut.CustomKeyStores, 1)
assert.Equal(t, kms.ConnectionStateConnected, descOut.CustomKeyStores[0].ConnectionState)
@@ -388,7 +389,7 @@ func TestCustomKeyStoreHandler_ViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
// Delete via HTTP.
- deleteBody, _ := json.Marshal(map[string]string{"CustomKeyStoreId": storeID})
+ deleteBody, _ := json.Marshal(map[string]string{"CustomKeyStoreId": storeID}, jsonx.V1())
rec = postKMSOp(t, h, "DeleteCustomKeyStore", string(deleteBody))
require.Equal(t, http.StatusOK, rec.Code)
}
diff --git a/services/kms/handler_generate_and_mac.go b/services/kms/handler_generate_and_mac.go
index 4908a92a5f..b596e2a7e7 100644
--- a/services/kms/handler_generate_and_mac.go
+++ b/services/kms/handler_generate_and_mac.go
@@ -2,7 +2,9 @@ package kms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// buildGenerateAndMacActions returns dispatch entries for data key pair, MAC, and random operations.
@@ -10,7 +12,7 @@ func (h *Handler) buildGenerateAndMacActions() map[string]kmsActionFn {
return map[string]kmsActionFn{
opGenerateDataKeyPair: func(ctx context.Context, b []byte) (any, error) {
var input GenerateDataKeyPairInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -18,7 +20,7 @@ func (h *Handler) buildGenerateAndMacActions() map[string]kmsActionFn {
},
opGenerateDataKeyPairWithoutPlaintext: func(ctx context.Context, b []byte) (any, error) {
var input GenerateDataKeyPairWithoutPlaintextInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -26,7 +28,7 @@ func (h *Handler) buildGenerateAndMacActions() map[string]kmsActionFn {
},
opGenerateMac: func(ctx context.Context, b []byte) (any, error) {
var input GenerateMacInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -34,7 +36,7 @@ func (h *Handler) buildGenerateAndMacActions() map[string]kmsActionFn {
},
"GenerateRandom": func(ctx context.Context, b []byte) (any, error) {
var input GenerateRandomInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -42,7 +44,7 @@ func (h *Handler) buildGenerateAndMacActions() map[string]kmsActionFn {
},
opVerifyMac: func(ctx context.Context, b []byte) (any, error) {
var input VerifyMacInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/kms/handler_generate_and_mac_test.go b/services/kms/handler_generate_and_mac_test.go
index fa11021028..423ce7d907 100644
--- a/services/kms/handler_generate_and_mac_test.go
+++ b/services/kms/handler_generate_and_mac_test.go
@@ -2,7 +2,7 @@ package kms_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
"strings"
@@ -38,12 +39,12 @@ func TestVerifyMac_Handler(t *testing.T) {
"MacAlgorithm": "HMAC_SHA_256",
"Message": msg,
"Mac": macOut.Mac,
- })
+ }, jsonx.V1())
rec := sendKMSOp(t, h, "VerifyMac", string(bodyBytes))
require.Equal(t, http.StatusOK, rec.Code)
var verOut kms.VerifyMacOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verOut, jsonx.V1()))
assert.True(t, verOut.MacValid)
}
@@ -63,10 +64,10 @@ func TestKMSHandlerGenerateDataKey(t *testing.T) {
require.NoError(t, h.Handler()(e.NewContext(createReq, createRec)))
var createOut kms.CreateKeyOutput
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut.KeyMetadata.KeyID
- body, _ := json.Marshal(map[string]string{"KeyID": keyID, "KeySpec": "AES_256"})
+ body, _ := json.Marshal(map[string]string{"KeyID": keyID, "KeySpec": "AES_256"}, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(body)))
req.Header.Set("X-Amz-Target", "TrentService.GenerateDataKey")
rec := httptest.NewRecorder()
@@ -74,7 +75,7 @@ func TestKMSHandlerGenerateDataKey(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out kms.GenerateDataKeyOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Plaintext, 32)
assert.NotEmpty(t, out.CiphertextBlob)
}
@@ -90,11 +91,11 @@ func TestKMSGenerateDataKeyWithoutPlaintext(t *testing.T) {
require.NoError(t, err)
keyID := keyOut.KeyMetadata.KeyID
- body, _ := json.Marshal(map[string]string{"KeyId": keyID, "KeySpec": "AES_256"})
+ body, _ := json.Marshal(map[string]string{"KeyId": keyID, "KeySpec": "AES_256"}, jsonx.V1())
rec := doKMSHTTPRequest(t, h, "GenerateDataKeyWithoutPlaintext", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
var out kms.GenerateDataKeyWithoutPlaintextOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.CiphertextBlob)
assert.Equal(t, keyOut.KeyMetadata.Arn, out.KeyID)
}
@@ -129,7 +130,7 @@ func TestKMS_InvalidKeySpec_Returns400UnsupportedOperationException(t *testing.T
body, err := json.Marshal(map[string]string{
"KeyId": wrapKeyID,
"KeyPairSpec": "BOGUS_SPEC",
- })
+ }, jsonx.V1())
require.NoError(t, err)
return "TrentService.GenerateDataKeyPair", string(body)
@@ -165,7 +166,7 @@ func TestKMS_InvalidKeySpec_Returns400UnsupportedOperationException(t *testing.T
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "UnsupportedOperationException", errResp.Type)
})
}
diff --git a/services/kms/handler_grants_policies.go b/services/kms/handler_grants_policies.go
index 528f8b5531..b88bf70a7c 100644
--- a/services/kms/handler_grants_policies.go
+++ b/services/kms/handler_grants_policies.go
@@ -2,8 +2,10 @@ package kms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// buildGrantPolicyActions returns dispatch entries for grant and key policy operations.
@@ -11,7 +13,7 @@ func (h *Handler) buildGrantPolicyActions() map[string]kmsActionFn {
return map[string]kmsActionFn{
opCreateGrant: func(ctx context.Context, b []byte) (any, error) {
var input CreateGrantInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -19,7 +21,7 @@ func (h *Handler) buildGrantPolicyActions() map[string]kmsActionFn {
},
"ListGrants": func(ctx context.Context, b []byte) (any, error) {
var input ListGrantsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -27,7 +29,7 @@ func (h *Handler) buildGrantPolicyActions() map[string]kmsActionFn {
},
"RevokeGrant": func(ctx context.Context, b []byte) (any, error) {
var input RevokeGrantInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -35,7 +37,7 @@ func (h *Handler) buildGrantPolicyActions() map[string]kmsActionFn {
},
opRetireGrant: func(ctx context.Context, b []byte) (any, error) {
var input RetireGrantInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -43,7 +45,7 @@ func (h *Handler) buildGrantPolicyActions() map[string]kmsActionFn {
},
"ListRetirableGrants": func(ctx context.Context, b []byte) (any, error) {
var input ListRetirableGrantsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -59,7 +61,7 @@ func (h *Handler) buildGrantPolicyActions() map[string]kmsActionFn {
// function's cognitive complexity under the gocognit limit.
func (h *Handler) handlePutKeyPolicy(ctx context.Context, b []byte) (any, error) {
var input PutKeyPolicyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -85,7 +87,7 @@ func (h *Handler) handlePutKeyPolicy(ctx context.Context, b []byte) (any, error)
func (h *Handler) handleGetKeyPolicy(ctx context.Context, b []byte) (any, error) {
var input GetKeyPolicyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/kms/handler_grants_policies_test.go b/services/kms/handler_grants_policies_test.go
index 3823cc5ef2..69f1f5f2dc 100644
--- a/services/kms/handler_grants_policies_test.go
+++ b/services/kms/handler_grants_policies_test.go
@@ -2,7 +2,8 @@ package kms_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
)
@@ -34,7 +36,7 @@ func TestHandler_CreateGrant_WithRetiringPrincipal_ViaHTTP(t *testing.T) {
GrantID string `json:"GrantId"`
GrantToken string `json:"GrantToken"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.GrantID)
assert.NotEmpty(t, resp.GrantToken)
}
@@ -57,25 +59,25 @@ func TestHandler_CreateGrant_NameTooLong_ViaHTTP(t *testing.T) {
"GranteePrincipal": "arn:aws:iam::123456789012:role/grantee",
"Operations": []string{"Encrypt"},
"Name": strings.Repeat("x", 257),
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := b2postKMSOp(t, h, "CreateGrant", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "LimitExceededException", errResp.Type)
assert.NotEqual(t, "ValidationException", errResp.Type)
- listBody, err := json.Marshal(map[string]string{"KeyId": keyID})
+ listBody, err := json.Marshal(map[string]string{"KeyId": keyID}, jsonx.V1())
require.NoError(t, err)
listRec := b2postKMSOp(t, h, "ListGrants", string(listBody))
assert.Equal(t, http.StatusOK, listRec.Code)
var listOut kms.ListGrantsOutput
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Empty(t, listOut.Grants, "rejected CreateGrant must not create a grant")
}
@@ -102,14 +104,14 @@ func TestHandler_PutKeyPolicy_InvalidPolicyName_ViaHTTP(t *testing.T) {
"KeyId": keyID,
"PolicyName": "custom",
"Policy": `{"Version":"2012-10-17","Statement":[]}`,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := b2postKMSOp(t, h, "PutKeyPolicy", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "UnsupportedOperationException", errResp.Type)
assert.NotEqual(t, "ValidationException", errResp.Type)
@@ -141,7 +143,7 @@ func TestKMSGrantOperations(t *testing.T) {
rec := doKMSHTTPRequest(t, h, "CreateGrant", createBody)
assert.Equal(t, http.StatusOK, rec.Code)
var createOut kms.CreateGrantOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
assert.NotEmpty(t, createOut.GrantID)
assert.NotEmpty(t, createOut.GrantToken)
@@ -150,7 +152,7 @@ func TestKMSGrantOperations(t *testing.T) {
rec = doKMSHTTPRequest(t, h, "ListGrants", listBody)
assert.Equal(t, http.StatusOK, rec.Code)
var listOut kms.ListGrantsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.Grants, 1)
assert.Equal(t, createOut.GrantID, listOut.Grants[0].GrantID)
@@ -161,7 +163,7 @@ func TestKMSGrantOperations(t *testing.T) {
// ListGrants after revoke — should be empty
rec = doKMSHTTPRequest(t, h, "ListGrants", listBody)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Empty(t, listOut.Grants)
}
@@ -181,15 +183,15 @@ func TestKMSKeyPolicy(t *testing.T) {
"KeyId": keyID,
"PolicyName": "default",
"Policy": policy,
- })
+ }, jsonx.V1())
rec := doKMSHTTPRequest(t, h, "PutKeyPolicy", string(putBody))
assert.Equal(t, http.StatusOK, rec.Code)
- getBody, _ := json.Marshal(map[string]string{"KeyId": keyID, "PolicyName": "default"})
+ getBody, _ := json.Marshal(map[string]string{"KeyId": keyID, "PolicyName": "default"}, jsonx.V1())
rec = doKMSHTTPRequest(t, h, "GetKeyPolicy", string(getBody))
assert.Equal(t, http.StatusOK, rec.Code)
var getOut kms.GetKeyPolicyOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, policy, getOut.Policy)
}
@@ -211,30 +213,30 @@ func TestKMSRetireGrant(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var createOut kms.CreateGrantOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
grantToken := createOut.GrantToken
// ListRetirableGrants for the retiring principal
listBody, _ := json.Marshal(map[string]string{
"RetiringPrincipal": "arn:aws:iam::000000000000:role/retire-role",
- })
+ }, jsonx.V1())
rec = doKMSHTTPRequest(t, h, "ListRetirableGrants", string(listBody))
assert.Equal(t, http.StatusOK, rec.Code)
var listOut kms.ListGrantsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.Grants, 1)
assert.Equal(t, createOut.GrantID, listOut.Grants[0].GrantID)
// RetireGrant using grant token
- retireBody, _ := json.Marshal(map[string]string{"GrantToken": grantToken})
+ retireBody, _ := json.Marshal(map[string]string{"GrantToken": grantToken}, jsonx.V1())
rec = doKMSHTTPRequest(t, h, "RetireGrant", string(retireBody))
assert.Equal(t, http.StatusOK, rec.Code)
// ListGrants should now be empty
listGrantsBody := `{"KeyId":"` + keyID + `"}`
rec = doKMSHTTPRequest(t, h, "ListGrants", listGrantsBody)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Empty(t, listOut.Grants)
}
@@ -285,9 +287,9 @@ func TestListGrants_RawBody_NoGrantTokenLeak(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw struct {
- Grants []map[string]json.RawMessage `json:"Grants"`
+ Grants []map[string]jsontext.Value `json:"Grants"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
require.Len(t, raw.Grants, 1)
_, hasToken := raw.Grants[0]["GrantToken"]
diff --git a/services/kms/handler_keys.go b/services/kms/handler_keys.go
index 93a444e868..d55b652387 100644
--- a/services/kms/handler_keys.go
+++ b/services/kms/handler_keys.go
@@ -2,8 +2,9 @@ package kms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -57,7 +58,7 @@ func (h *Handler) buildKeyLifecycleActions() map[string]kmsActionFn {
// backend whenever the caller supplied a malformed tag.
func (h *Handler) createKeyAction(ctx context.Context, b []byte) (any, error) {
var input CreateKeyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/kms/handler_keys_test.go b/services/kms/handler_keys_test.go
index 02e4071552..62ff825d9b 100644
--- a/services/kms/handler_keys_test.go
+++ b/services/kms/handler_keys_test.go
@@ -2,7 +2,7 @@ package kms_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
"log/slog"
@@ -30,14 +31,14 @@ func TestHandler_CreateKey_DescriptionTooLong_ViaHTTP(t *testing.T) {
b := h.Backend.(*kms.InMemoryBackend)
longDescription := strings.Repeat("x", 8193)
- body, err := json.Marshal(map[string]string{"Description": longDescription})
+ body, err := json.Marshal(map[string]string{"Description": longDescription}, jsonx.V1())
require.NoError(t, err)
rec := b2postKMSOp(t, h, "CreateKey", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "LimitExceededException", errResp.Type)
assert.NotEqual(t, "ValidationException", errResp.Type)
@@ -62,7 +63,7 @@ func TestHandler_CreateKey_KeySpecKeyUsageMismatch_ViaHTTP(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "UnsupportedOperationException", errResp.Type)
assert.NotEqual(t, "InvalidKeyUsageException", errResp.Type)
@@ -91,7 +92,7 @@ func TestHandler_CreateKey_HMACMultiRegion_ViaHTTP(t *testing.T) {
MultiRegion bool `json:"MultiRegion"`
} `json:"KeyMetadata"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "HMAC_256", out.KeyMetadata.KeySpec)
assert.Equal(t, "GENERATE_VERIFY_MAC", out.KeyMetadata.KeyUsage)
assert.True(t, out.KeyMetadata.MultiRegion)
@@ -112,7 +113,7 @@ func TestHandler_ScheduleKeyDeletion_ViaHTTP(t *testing.T) {
var resp struct {
KeyState string `json:"KeyState"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, kms.KeyStatePendingDeletion, resp.KeyState)
}
@@ -137,7 +138,7 @@ func TestHandler_CancelKeyDeletion_ViaHTTP(t *testing.T) {
var resp struct {
KeyID string `json:"KeyId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, keyID, resp.KeyID)
desc, err := b.DescribeKey(context.Background(), &kms.DescribeKeyInput{KeyID: keyID})
@@ -180,7 +181,7 @@ func TestKMSHandler(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out kms.CreateKeyOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.KeyMetadata.KeyID)
},
},
@@ -192,7 +193,7 @@ func TestKMSHandler(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "UnknownOperationException", errResp.Type)
},
},
@@ -210,7 +211,7 @@ func TestKMSHandler(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var ops []string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops, jsonx.V1()))
assert.Contains(t, ops, "CreateKey")
},
},
@@ -222,7 +223,7 @@ func TestKMSHandler(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "NotFoundException", errResp.Type)
},
},
@@ -240,7 +241,7 @@ func TestKMSHandler(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out kms.ListKeysOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Keys, 1)
},
},
@@ -394,7 +395,7 @@ func TestKMSHandlerDisableEnableKey(t *testing.T) {
rec = doKMSReqLocal(t, h, "ScheduleKeyDeletion", `{"KeyId":"`+keyID+`","PendingWindowInDays":7}`)
assert.Equal(t, http.StatusOK, rec.Code)
var schedResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &schedResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &schedResp, jsonx.V1()))
assert.Equal(t, kms.KeyStatePendingDeletion, schedResp["KeyState"])
rec = doKMSReqLocal(t, h, "CancelKeyDeletion", body)
@@ -411,7 +412,7 @@ func TestHandlerCancelKeyDeletionReturnsBody(t *testing.T) {
require.Equal(t, http.StatusOK, keyRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut["KeyMetadata"].(map[string]any)["KeyId"].(string)
sendKMSOp(t, h, "ScheduleKeyDeletion", `{"KeyId":"`+keyID+`","PendingWindowInDays":7}`)
@@ -420,7 +421,7 @@ func TestHandlerCancelKeyDeletionReturnsBody(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, keyID, out["KeyId"])
assert.NotContains(t, out, "KeyState", "real CancelKeyDeletionOutput has only KeyId")
}
@@ -435,7 +436,7 @@ func TestHandlerUpdateKeyDescriptionMaxLengthRejected(t *testing.T) {
require.Equal(t, http.StatusOK, keyRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut["KeyMetadata"].(map[string]any)["KeyId"].(string)
longDesc := strings.Repeat("x", 8193)
diff --git a/services/kms/handler_replication_maintenance.go b/services/kms/handler_replication_maintenance.go
index 8f99e45d6a..fcd42a629a 100644
--- a/services/kms/handler_replication_maintenance.go
+++ b/services/kms/handler_replication_maintenance.go
@@ -2,8 +2,10 @@ package kms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sort"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// replicateKeyAction handles ReplicateKey dispatch, including tag validation and
@@ -13,7 +15,7 @@ import (
// leaving a real, untagged replica key behind.
func (h *Handler) replicateKeyAction(ctx context.Context, b []byte) (any, error) {
var input ReplicateKeyInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -66,7 +68,7 @@ func (h *Handler) buildReplicationAndMaintenanceActions() map[string]kmsActionFn
return map[string]kmsActionFn{
"GetParametersForImport": func(ctx context.Context, b []byte) (any, error) {
var input GetParametersForImportInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -74,7 +76,7 @@ func (h *Handler) buildReplicationAndMaintenanceActions() map[string]kmsActionFn
},
"ListKeyPolicies": func(ctx context.Context, b []byte) (any, error) {
var input ListKeyPoliciesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -82,7 +84,7 @@ func (h *Handler) buildReplicationAndMaintenanceActions() map[string]kmsActionFn
},
"ListKeyRotations": func(ctx context.Context, b []byte) (any, error) {
var input ListKeyRotationsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -91,7 +93,7 @@ func (h *Handler) buildReplicationAndMaintenanceActions() map[string]kmsActionFn
"ReplicateKey": h.replicateKeyAction,
"RotateKeyOnDemand": func(ctx context.Context, b []byte) (any, error) {
var input RotateKeyOnDemandInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -99,7 +101,7 @@ func (h *Handler) buildReplicationAndMaintenanceActions() map[string]kmsActionFn
},
"UpdateCustomKeyStore": func(ctx context.Context, b []byte) (any, error) {
var input UpdateCustomKeyStoreInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -107,7 +109,7 @@ func (h *Handler) buildReplicationAndMaintenanceActions() map[string]kmsActionFn
},
"UpdateKeyDescription": func(ctx context.Context, b []byte) (any, error) {
var input UpdateKeyDescriptionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -115,7 +117,7 @@ func (h *Handler) buildReplicationAndMaintenanceActions() map[string]kmsActionFn
},
"UpdatePrimaryRegion": func(ctx context.Context, b []byte) (any, error) {
var input UpdatePrimaryRegionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/kms/handler_replication_maintenance_test.go b/services/kms/handler_replication_maintenance_test.go
index f593d5af48..1573e0d151 100644
--- a/services/kms/handler_replication_maintenance_test.go
+++ b/services/kms/handler_replication_maintenance_test.go
@@ -3,7 +3,7 @@ package kms_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
"strings"
@@ -47,7 +48,7 @@ func TestHandler_GetParametersForImport_ViaHTTP(t *testing.T) {
KeyID string `json:"KeyId"`
ParametersValidTo float64 `json:"ParametersValidTo"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, keyID, resp.KeyID)
assert.Greater(t, resp.ParametersValidTo, float64(0))
}
@@ -98,7 +99,7 @@ func TestHandler_ImportKeyMaterial_WireFieldName_ViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut kms.DescribeKeyOutput
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, kms.KeyStateEnabled, descOut.KeyMetadata.KeyState)
}
@@ -110,7 +111,7 @@ func TestKMSHandlerImportKeyMaterial(t *testing.T) {
h := kms.NewHandler(kms.NewInMemoryBackend())
// Create EXTERNAL-origin key.
- createBody, err := json.Marshal(kms.CreateKeyInput{Origin: kms.KeyOriginExternal})
+ createBody, err := json.Marshal(kms.CreateKeyInput{Origin: kms.KeyOriginExternal}, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(createBody))
req.Header.Set("Content-Type", "application/x-amz-json-1.1")
@@ -123,7 +124,7 @@ func TestKMSHandlerImportKeyMaterial(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut kms.CreateKeyOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
assert.Equal(t, kms.KeyStatePendingImport, createOut.KeyMetadata.KeyState)
// Import key material.
@@ -135,7 +136,7 @@ func TestKMSHandlerImportKeyMaterial(t *testing.T) {
importBody, err := json.Marshal(kms.ImportKeyMaterialInput{
KeyID: createOut.KeyMetadata.KeyID,
KeyMaterial: mat,
- })
+ }, jsonx.V1())
require.NoError(t, err)
importReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(importBody))
importReq.Header.Set("Content-Type", "application/x-amz-json-1.1")
@@ -147,7 +148,7 @@ func TestKMSHandlerImportKeyMaterial(t *testing.T) {
assert.Equal(t, http.StatusOK, importRec.Code)
// Describe should show Enabled.
- descBody, err := json.Marshal(kms.DescribeKeyInput{KeyID: createOut.KeyMetadata.KeyID})
+ descBody, err := json.Marshal(kms.DescribeKeyInput{KeyID: createOut.KeyMetadata.KeyID}, jsonx.V1())
require.NoError(t, err)
descReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(descBody))
descReq.Header.Set("Content-Type", "application/x-amz-json-1.1")
@@ -159,11 +160,11 @@ func TestKMSHandlerImportKeyMaterial(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut kms.DescribeKeyOutput
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, kms.KeyStateEnabled, descOut.KeyMetadata.KeyState)
// DeleteImportedKeyMaterial.
- delBody, err := json.Marshal(kms.DeleteImportedKeyMaterialInput{KeyID: createOut.KeyMetadata.KeyID})
+ delBody, err := json.Marshal(kms.DeleteImportedKeyMaterialInput{KeyID: createOut.KeyMetadata.KeyID}, jsonx.V1())
require.NoError(t, err)
delReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(delBody))
delReq.Header.Set("Content-Type", "application/x-amz-json-1.1")
@@ -175,7 +176,7 @@ func TestKMSHandlerImportKeyMaterial(t *testing.T) {
assert.Equal(t, http.StatusOK, delRec.Code)
// Key should be back to PendingImport.
- descBody2, err := json.Marshal(kms.DescribeKeyInput{KeyID: createOut.KeyMetadata.KeyID})
+ descBody2, err := json.Marshal(kms.DescribeKeyInput{KeyID: createOut.KeyMetadata.KeyID}, jsonx.V1())
require.NoError(t, err)
descReq2 := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(descBody2))
descReq2.Header.Set("Content-Type", "application/x-amz-json-1.1")
@@ -187,7 +188,7 @@ func TestKMSHandlerImportKeyMaterial(t *testing.T) {
require.Equal(t, http.StatusOK, descRec2.Code)
var descOut2 kms.DescribeKeyOutput
- require.NoError(t, json.Unmarshal(descRec2.Body.Bytes(), &descOut2))
+ require.NoError(t, json.Unmarshal(descRec2.Body.Bytes(), &descOut2, jsonx.V1()))
assert.Equal(t, kms.KeyStatePendingImport, descOut2.KeyMetadata.KeyState)
}
@@ -270,7 +271,7 @@ func TestKMSHandlerNewMaintenanceOps(t *testing.T) {
}
var payload map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload, jsonx.V1()))
_, ok := payload[tt.wantField]
assert.True(t, ok)
})
@@ -316,7 +317,7 @@ func TestKMS_ErrorClassification_MissingTableEntries(t *testing.T) {
ValidTo: pastTS,
}))
- body, err := json.Marshal(kms.EncryptInput{KeyID: keyID, Plaintext: []byte("test")})
+ body, err := json.Marshal(kms.EncryptInput{KeyID: keyID, Plaintext: []byte("test")}, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(body)))
@@ -327,7 +328,7 @@ func TestKMS_ErrorClassification_MissingTableEntries(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "KMSInvalidStateException", errResp.Type)
})
@@ -349,7 +350,7 @@ func TestKMS_ErrorClassification_MissingTableEntries(t *testing.T) {
e := echo.New()
h := kms.NewHandler(restored)
- body, err := json.Marshal(kms.EncryptInput{KeyID: keyID, Plaintext: []byte("test")})
+ body, err := json.Marshal(kms.EncryptInput{KeyID: keyID, Plaintext: []byte("test")}, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(body)))
@@ -360,7 +361,7 @@ func TestKMS_ErrorClassification_MissingTableEntries(t *testing.T) {
assert.Equal(t, http.StatusInternalServerError, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "KeyUnavailableException", errResp.Type)
})
}
@@ -376,7 +377,7 @@ func TestHandlerReplicateKeyRequiresMultiRegion(t *testing.T) {
require.Equal(t, http.StatusOK, keyRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut["KeyMetadata"].(map[string]any)["KeyId"].(string)
rec := sendKMSOp(t, h, "ReplicateKey", `{"KeyId":"`+keyID+`","ReplicaRegion":"us-west-2"}`)
@@ -393,7 +394,7 @@ func TestHandlerReplicateKeyCopiesTags(t *testing.T) {
require.Equal(t, http.StatusOK, keyRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut["KeyMetadata"].(map[string]any)["KeyId"].(string)
tagRec := sendKMSOp(t, h, "TagResource", `{"KeyId":"`+keyID+`","Tags":[{"TagKey":"env","TagValue":"prod"}]}`)
@@ -403,14 +404,14 @@ func TestHandlerReplicateKeyCopiesTags(t *testing.T) {
require.Equal(t, http.StatusOK, repRec.Code)
var repOut map[string]any
- require.NoError(t, json.Unmarshal(repRec.Body.Bytes(), &repOut))
+ require.NoError(t, json.Unmarshal(repRec.Body.Bytes(), &repOut, jsonx.V1()))
replicaID := repOut["ReplicaKeyMetadata"].(map[string]any)["KeyId"].(string)
tagListRec := sendKMSOp(t, h, "ListResourceTags", `{"KeyId":"`+replicaID+`"}`)
require.Equal(t, http.StatusOK, tagListRec.Code)
var tagListOut map[string]any
- require.NoError(t, json.Unmarshal(tagListRec.Body.Bytes(), &tagListOut))
+ require.NoError(t, json.Unmarshal(tagListRec.Body.Bytes(), &tagListOut, jsonx.V1()))
tags := tagListOut["Tags"].([]any)
require.NotEmpty(t, tags)
@@ -435,7 +436,7 @@ func TestHandlerUpdatePrimaryRegionViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, keyRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut["KeyMetadata"].(map[string]any)["KeyId"].(string)
// Replicate to eu-central-1 first — UpdatePrimaryRegion requires an actual replica.
@@ -456,7 +457,7 @@ func TestHandlerReplicateKeyWithInputTags(t *testing.T) {
require.Equal(t, http.StatusOK, keyRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(keyRec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut["KeyMetadata"].(map[string]any)["KeyId"].(string)
// Replicate with explicit tags that should overlay the source tags.
@@ -465,14 +466,14 @@ func TestHandlerReplicateKeyWithInputTags(t *testing.T) {
require.Equal(t, http.StatusOK, repRec.Code)
var repOut map[string]any
- require.NoError(t, json.Unmarshal(repRec.Body.Bytes(), &repOut))
+ require.NoError(t, json.Unmarshal(repRec.Body.Bytes(), &repOut, jsonx.V1()))
replicaID := repOut["ReplicaKeyMetadata"].(map[string]any)["KeyId"].(string)
tagListRec := sendKMSOp(t, h, "ListResourceTags", `{"KeyId":"`+replicaID+`"}`)
require.Equal(t, http.StatusOK, tagListRec.Code)
var tagListOut map[string]any
- require.NoError(t, json.Unmarshal(tagListRec.Body.Bytes(), &tagListOut))
+ require.NoError(t, json.Unmarshal(tagListRec.Body.Bytes(), &tagListOut, jsonx.V1()))
tags := tagListOut["Tags"].([]any)
keys := make(map[string]string)
diff --git a/services/kms/handler_tags.go b/services/kms/handler_tags.go
index 713fd4aae2..a0e06cf602 100644
--- a/services/kms/handler_tags.go
+++ b/services/kms/handler_tags.go
@@ -2,12 +2,13 @@ package kms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
"strconv"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -147,7 +148,7 @@ func (h *Handler) getTags(resourceID string) map[string]string {
// listResourceTags handles the ListResourceTags operation.
func (h *Handler) listResourceTags(ctx context.Context, b []byte) (any, error) {
var input listResourceTagsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -202,7 +203,7 @@ func paginateTagList(tagList []kmsTagEntry, marker string, limit *int32) *listRe
// tagResource handles the TagResource operation, validating key existence and tag count.
func (h *Handler) tagResource(ctx context.Context, b []byte) (any, error) {
var input tagResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -291,7 +292,7 @@ func (h *Handler) validateTagCount(keyID string, newTags map[string]string) erro
// untagResource handles the UntagResource operation.
func (h *Handler) untagResource(ctx context.Context, b []byte) (any, error) {
var input untagResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/kms/handler_tags_persistence_test.go b/services/kms/handler_tags_persistence_test.go
index a303e274d8..16668aa475 100644
--- a/services/kms/handler_tags_persistence_test.go
+++ b/services/kms/handler_tags_persistence_test.go
@@ -16,12 +16,13 @@ package kms_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
)
@@ -95,7 +96,7 @@ func TestHandlerRestore_LegacyBackendOnlySnapshot(t *testing.T) {
require.NotEmpty(t, legacy)
var probe map[string]any
- require.NoError(t, json.Unmarshal(legacy, &probe))
+ require.NoError(t, json.Unmarshal(legacy, &probe, jsonx.V1()))
_, hasHandlerFormat := probe["handlerFormat"]
require.False(t, hasHandlerFormat, "precondition: a raw backend snapshot has no handlerFormat marker")
diff --git a/services/kms/handler_tags_test.go b/services/kms/handler_tags_test.go
index be7596b6c7..3585a15dd1 100644
--- a/services/kms/handler_tags_test.go
+++ b/services/kms/handler_tags_test.go
@@ -3,7 +3,7 @@ package kms_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
"strings"
@@ -29,7 +30,7 @@ func TestTagResource_PendingDeletion_Rejected(t *testing.T) {
body, err := json.Marshal(map[string]any{
"KeyId": keyID,
"Tags": []map[string]string{{"TagKey": "env", "TagValue": "test"}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doKMSRequest(t, h, "TagResource", string(body))
@@ -38,7 +39,7 @@ func TestTagResource_PendingDeletion_Rejected(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "KMSInvalidStateException", errResp.Type)
}
@@ -69,7 +70,7 @@ func TestTagResource_EnabledAndDisabled_Accepted(t *testing.T) {
body, merr := json.Marshal(map[string]any{
"KeyId": keyID,
"Tags": []map[string]string{{"TagKey": "env", "TagValue": "test"}},
- })
+ }, jsonx.V1())
require.NoError(t, merr)
rec := doKMSRequest(t, h, "TagResource", string(body))
@@ -90,7 +91,7 @@ func TestUntagResource_PendingDeletion_Rejected(t *testing.T) {
body, err := json.Marshal(map[string]any{
"KeyId": keyID,
"TagKeys": []string{"env"},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doKMSRequest(t, h, "UntagResource", string(body))
@@ -99,7 +100,7 @@ func TestUntagResource_PendingDeletion_Rejected(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "KMSInvalidStateException", errResp.Type)
}
@@ -130,7 +131,7 @@ func TestUntagResource_EnabledAndDisabled_Accepted(t *testing.T) {
body, merr := json.Marshal(map[string]any{
"KeyId": keyID,
"TagKeys": []string{"nonexistent-key"},
- })
+ }, jsonx.V1())
require.NoError(t, merr)
rec := doKMSRequest(t, h, "UntagResource", string(body))
@@ -219,7 +220,7 @@ func TestTagResource_AndListResourceTags_Roundtrip(t *testing.T) {
TagValue string `json:"TagValue"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagsResp, jsonx.V1()))
assert.Len(t, tagsResp.Tags, 2)
}
@@ -248,7 +249,7 @@ func TestUntagResource_RemovesSpecifiedTags(t *testing.T) {
TagKey string `json:"TagKey"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagsResp, jsonx.V1()))
keys := make([]string, 0, len(tagsResp.Tags))
for _, t := range tagsResp.Tags {
keys = append(keys, t.TagKey)
@@ -318,7 +319,7 @@ func TestListResourceTags_Pagination(t *testing.T) {
Tags []any `json:"Tags"`
Truncated bool `json:"Truncated"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Tags, 3)
assert.True(t, resp.Truncated)
}
@@ -353,12 +354,12 @@ func TestKMSTagOperations(t *testing.T) {
keyID := keyOut.KeyMetadata.KeyID
// ListResourceTags on a key with no tags
- listBody, _ := json.Marshal(map[string]string{"KeyId": keyID})
+ listBody, _ := json.Marshal(map[string]string{"KeyId": keyID}, jsonx.V1())
rec := doKMSHTTPRequest(t, h, "ListResourceTags", string(listBody))
assert.Equal(t, http.StatusOK, rec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Empty(t, listOut["Tags"])
assert.Equal(t, false, listOut["Truncated"])
@@ -369,7 +370,7 @@ func TestKMSTagOperations(t *testing.T) {
{"TagKey": "env", "TagValue": "prod"},
{"TagKey": "team", "TagValue": "backend"},
},
- })
+ }, jsonx.V1())
rec = doKMSHTTPRequest(t, h, "TagResource", string(tagBody))
assert.Equal(t, http.StatusOK, rec.Code)
@@ -377,7 +378,7 @@ func TestKMSTagOperations(t *testing.T) {
rec = doKMSHTTPRequest(t, h, "ListResourceTags", string(listBody))
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
tags, ok := listOut["Tags"].([]any)
require.True(t, ok)
assert.Len(t, tags, 2)
@@ -386,13 +387,13 @@ func TestKMSTagOperations(t *testing.T) {
untagBody, _ := json.Marshal(map[string]any{
"KeyId": keyID,
"TagKeys": []string{"team"},
- })
+ }, jsonx.V1())
rec = doKMSHTTPRequest(t, h, "UntagResource", string(untagBody))
assert.Equal(t, http.StatusOK, rec.Code)
// ListResourceTags should now return 1 tag
rec = doKMSHTTPRequest(t, h, "ListResourceTags", string(listBody))
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
tags, ok = listOut["Tags"].([]any)
require.True(t, ok)
assert.Len(t, tags, 1)
@@ -536,7 +537,7 @@ func TestKMSHandlerListResourceTagsPagination(t *testing.T) {
Tags []any `json:"Tags"`
Truncated bool `json:"Truncated"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Tags, tt.wantCount)
assert.Equal(t, tt.wantTruncated, out.Truncated)
@@ -590,7 +591,7 @@ func TestHandlerTagResourceMaxTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut["KeyMetadata"].(map[string]any)["KeyId"].(string)
// Build a tag list with 51 unique entries.
@@ -622,14 +623,14 @@ func TestHandlerCreateKeyWithTagsApplied(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut["KeyMetadata"].(map[string]any)["KeyId"].(string)
tagListRec := sendKMSOp(t, h, "ListResourceTags", `{"KeyId":"`+keyID+`"}`)
require.Equal(t, http.StatusOK, tagListRec.Code)
var tagListOut map[string]any
- require.NoError(t, json.Unmarshal(tagListRec.Body.Bytes(), &tagListOut))
+ require.NoError(t, json.Unmarshal(tagListRec.Body.Bytes(), &tagListOut, jsonx.V1()))
tags := tagListOut["Tags"].([]any)
require.NotEmpty(t, tags)
diff --git a/services/kms/handler_test.go b/services/kms/handler_test.go
index 7cbb043b55..71a13bc5a1 100644
--- a/services/kms/handler_test.go
+++ b/services/kms/handler_test.go
@@ -2,7 +2,7 @@ package kms_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
)
@@ -68,7 +69,10 @@ func TestErrValidation_Mapping(t *testing.T) {
if err != nil {
return `{}`
}
- body, _ := json.Marshal(map[string]any{"KeyId": key.KeyMetadata.KeyID, "PendingWindowInDays": 3})
+ body, _ := json.Marshal(
+ map[string]any{"KeyId": key.KeyMetadata.KeyID, "PendingWindowInDays": 3},
+ jsonx.V1(),
+ )
return string(body)
},
diff --git a/services/kms/helpers_test.go b/services/kms/helpers_test.go
index 3deba98564..da3bd5b120 100644
--- a/services/kms/helpers_test.go
+++ b/services/kms/helpers_test.go
@@ -3,7 +3,7 @@ package kms_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
"log/slog"
@@ -284,7 +285,7 @@ func buildBodyFromResourceID(operation, resourceID string) string {
switch operation {
case "ConnectCustomKeyStore", "DeleteCustomKeyStore", "DisconnectCustomKeyStore":
- body, _ := json.Marshal(map[string]string{"CustomKeyStoreId": resourceID})
+ body, _ := json.Marshal(map[string]string{"CustomKeyStoreId": resourceID}, jsonx.V1())
return string(body)
case "GenerateMac":
@@ -292,21 +293,21 @@ func buildBodyFromResourceID(operation, resourceID string) string {
"KeyId": resourceID,
"MacAlgorithm": "HMAC_SHA_256",
"Message": []byte("test message"),
- })
+ }, jsonx.V1())
return string(body)
case "GenerateDataKeyPair":
body, _ := json.Marshal(map[string]any{
"KeyId": resourceID,
"KeyPairSpec": "ECC_NIST_P256",
- })
+ }, jsonx.V1())
return string(body)
case "GenerateDataKeyPairWithoutPlaintext":
body, _ := json.Marshal(map[string]any{
"KeyId": resourceID,
"KeyPairSpec": "ECC_NIST_P256",
- })
+ }, jsonx.V1())
return string(body)
case "DeriveSharedSecret":
@@ -323,7 +324,7 @@ func buildBodyFromResourceID(operation, resourceID string) string {
"KeyId": resourceID,
"KeyAgreementAlgorithm": "ECDH",
"PublicKey": pubOut.PublicKey,
- })
+ }, jsonx.V1())
return string(body)
}
diff --git a/services/kms/import_error_wiring_test.go b/services/kms/import_error_wiring_test.go
index 64e34ff480..566f48bd2c 100644
--- a/services/kms/import_error_wiring_test.go
+++ b/services/kms/import_error_wiring_test.go
@@ -3,13 +3,14 @@ package kms_test
import (
"context"
"crypto/rand"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
)
@@ -46,7 +47,7 @@ func TestImportKeyMaterial_RSAUnwrapFailure_IsInvalidCiphertext(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidCiphertextException", errResp.Type)
assert.NotEqual(t, "InvalidKeyUsageException", errResp.Type)
@@ -81,7 +82,7 @@ func TestImportKeyMaterial_AsymmetricKeySpec_IsUnsupportedOperation(t *testing.T
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "UnsupportedOperationException", errResp.Type)
assert.NotEqual(t, "InvalidKeyUsageException", errResp.Type)
@@ -111,7 +112,7 @@ func TestImportKeyMaterial_EmptyMaterial_IsIncorrectKeyMaterial(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "IncorrectKeyMaterialException", errResp.Type)
assert.NotEqual(t, "InvalidKeyUsageException", errResp.Type)
assert.NotEqual(t, "ValidationException", errResp.Type)
@@ -139,7 +140,7 @@ func TestImportKeyMaterial_WrongLength_IsIncorrectKeyMaterial(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "IncorrectKeyMaterialException", errResp.Type)
assert.NotEqual(t, "InvalidKeyUsageException", errResp.Type)
@@ -175,7 +176,7 @@ func TestDecrypt_WrongKeyUsage_StillInvalidKeyUsageException(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidKeyUsageException", errResp.Type)
}
@@ -198,6 +199,6 @@ func TestVerifyMac_WrongKeyUsage_StillInvalidKeyUsageException(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidKeyUsageException", errResp.Type)
}
diff --git a/services/kms/incorrect_key_test.go b/services/kms/incorrect_key_test.go
index 968981f0ac..bde6d5fcd6 100644
--- a/services/kms/incorrect_key_test.go
+++ b/services/kms/incorrect_key_test.go
@@ -2,7 +2,7 @@ package kms_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
)
@@ -179,7 +180,7 @@ func TestIncorrectKeyExceptionWireType(t *testing.T) {
body, err := json.Marshal(map[string]any{
"CiphertextBlob": encOut.CiphertextBlob,
"KeyId": otherKey.KeyMetadata.KeyID,
- })
+ }, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(body)))
@@ -194,6 +195,6 @@ func TestIncorrectKeyExceptionWireType(t *testing.T) {
Type string `json:"__type"`
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "IncorrectKeyException", resp.Type)
}
diff --git a/services/kms/isolation_test.go b/services/kms/isolation_test.go
index 7d8fd839d5..f1385351fe 100644
--- a/services/kms/isolation_test.go
+++ b/services/kms/isolation_test.go
@@ -4,7 +4,7 @@ package kms_test
// keys and aliases created in one region are not visible in another.
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
)
@@ -48,14 +49,14 @@ func TestRegionIsolation_AliasInTwoRegions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var east1Key kms.CreateKeyOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &east1Key))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &east1Key, jsonx.V1()))
east1KeyID := east1Key.KeyMetadata.KeyID
// Create alias "alias/my-key" in us-east-1.
aliasBody, _ := json.Marshal(map[string]string{
"AliasName": "alias/my-key",
"TargetKeyId": east1KeyID,
- })
+ }, jsonx.V1())
rec = kmsCall(t, h, e, "CreateAlias", "us-east-1", string(aliasBody))
require.Equal(t, http.StatusOK, rec.Code)
@@ -64,14 +65,14 @@ func TestRegionIsolation_AliasInTwoRegions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var west2Key kms.CreateKeyOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &west2Key))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &west2Key, jsonx.V1()))
west2KeyID := west2Key.KeyMetadata.KeyID
// Create same alias "alias/my-key" in us-west-2 — should succeed (different region).
aliasBody2, _ := json.Marshal(map[string]string{
"AliasName": "alias/my-key",
"TargetKeyId": west2KeyID,
- })
+ }, jsonx.V1())
rec = kmsCall(t, h, e, "CreateAlias", "us-west-2", string(aliasBody2))
assert.Equal(t, http.StatusOK, rec.Code, "same alias name should succeed in a different region")
}
@@ -102,7 +103,7 @@ func TestRegionIsolation_ListKeys_RegionScoped(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var east1List kms.ListKeysOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &east1List))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &east1List, jsonx.V1()))
assert.Len(t, east1List.Keys, 3, "us-east-1 should have 3 keys")
// ListKeys in us-west-2 should return only 2 keys.
@@ -110,7 +111,7 @@ func TestRegionIsolation_ListKeys_RegionScoped(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var west2List kms.ListKeysOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &west2List))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &west2List, jsonx.V1()))
assert.Len(t, west2List.Keys, 2, "us-west-2 should have 2 keys")
}
@@ -129,18 +130,18 @@ func TestRegionIsolation_DescribeKey_CrossRegion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var east1Key kms.CreateKeyOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &east1Key))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &east1Key, jsonx.V1()))
east1KeyARN := east1Key.KeyMetadata.Arn
aliasBody, _ := json.Marshal(map[string]string{
"AliasName": "alias/region-test-key",
"TargetKeyId": east1Key.KeyMetadata.KeyID,
- })
+ }, jsonx.V1())
rec = kmsCall(t, h, e, "CreateAlias", "us-east-1", string(aliasBody))
require.Equal(t, http.StatusOK, rec.Code)
// DescribeKey via alias in us-east-1 should succeed.
- descBody, _ := json.Marshal(map[string]string{"KeyId": "alias/region-test-key"})
+ descBody, _ := json.Marshal(map[string]string{"KeyId": "alias/region-test-key"}, jsonx.V1())
rec = kmsCall(t, h, e, "DescribeKey", "us-east-1", string(descBody))
assert.Equal(t, http.StatusOK, rec.Code, "DescribeKey in same region via alias should succeed")
@@ -149,7 +150,7 @@ func TestRegionIsolation_DescribeKey_CrossRegion(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code, "DescribeKey via alias from wrong region should fail")
// DescribeKey via ARN from us-west-2 should succeed — ARN encodes the region.
- arnBody, _ := json.Marshal(map[string]string{"KeyId": east1KeyARN})
+ arnBody, _ := json.Marshal(map[string]string{"KeyId": east1KeyARN}, jsonx.V1())
rec = kmsCall(t, h, e, "DescribeKey", "us-west-2", string(arnBody))
assert.Equal(t, http.StatusOK, rec.Code, "DescribeKey via ARN should succeed from any region")
}
@@ -168,12 +169,12 @@ func TestRegionIsolation_AliasNotVisible_CrossRegion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var keyOut kms.CreateKeyOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &keyOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &keyOut, jsonx.V1()))
aliasBody, _ := json.Marshal(map[string]string{
"AliasName": "alias/east-only",
"TargetKeyId": keyOut.KeyMetadata.KeyID,
- })
+ }, jsonx.V1())
rec = kmsCall(t, h, e, "CreateAlias", "us-east-1", string(aliasBody))
require.Equal(t, http.StatusOK, rec.Code)
@@ -182,7 +183,7 @@ func TestRegionIsolation_AliasNotVisible_CrossRegion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var aliasList kms.ListAliasesOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &aliasList))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &aliasList, jsonx.V1()))
for _, a := range aliasList.Aliases {
assert.NotEqual(t, "alias/east-only", a.AliasName,
diff --git a/services/kms/key_policies.go b/services/kms/key_policies.go
index 7fd86737d4..8630b24c58 100644
--- a/services/kms/key_policies.go
+++ b/services/kms/key_policies.go
@@ -2,11 +2,12 @@ package kms
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strconv"
gopherarn "github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// PutKeyPolicy stores a key policy for a KMS key.
@@ -61,7 +62,7 @@ func validKeyPolicyDoc(s string) bool {
Statement any `json:"Statement"`
Version string `json:"Version"`
}
- if err := json.Unmarshal([]byte(s), &policyDoc); err != nil {
+ if err := json.Unmarshal([]byte(s), &policyDoc, jsonx.V1()); err != nil {
return false
}
diff --git a/services/kms/mac_algorithm_wiring_test.go b/services/kms/mac_algorithm_wiring_test.go
index bdb0eae4bb..4a6dc75c40 100644
--- a/services/kms/mac_algorithm_wiring_test.go
+++ b/services/kms/mac_algorithm_wiring_test.go
@@ -2,13 +2,14 @@ package kms_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
)
@@ -38,7 +39,7 @@ func TestGenerateMac_WrongAlgorithm_WireType_IsInvalidKeyUsage(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidKeyUsageException", errResp.Type)
}
@@ -64,6 +65,6 @@ func TestVerifyMac_WrongAlgorithm_WireType_IsInvalidKeyUsage(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp kms.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidKeyUsageException", errResp.Type)
}
diff --git a/services/kms/persistence.go b/services/kms/persistence.go
index 18b81f94b0..84b34eb244 100644
--- a/services/kms/persistence.go
+++ b/services/kms/persistence.go
@@ -2,10 +2,12 @@ package kms
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -33,7 +35,7 @@ const kmsSnapshotVersion = 1
// keyMaterialsStore/keyMaterialHistoryStore in backend.go), so neither can be
// expressed as a store.Table.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Policies map[string]map[string]string `json:"policies"`
KeyMaterials map[string]map[string]serializedKeyMaterial `json:"key_materials,omitempty"`
KeyMaterialHistory map[string]map[string][]serializedKeyMaterial `json:"key_material_history,omitempty"`
@@ -262,7 +264,7 @@ func warnMissingKeyMaterials(
// have no registered table for RestoreAll to find -- store.Registry.RestoreAll
// only restores tables already known to the registry, silently ignoring any
// snapshot entry whose name isn't registered.
-func (b *InMemoryBackend) preRegisterSnapshotTables(tables map[string]json.RawMessage) {
+func (b *InMemoryBackend) preRegisterSnapshotTables(tables map[string]jsontext.Value) {
for name := range tables {
kind, region, ok := strings.Cut(name, ":")
if !ok {
@@ -375,7 +377,7 @@ const handlerSnapshotFormat = 1
// `tags` attribute of `aws_kms_key` after a restart.
type handlerSnapshot struct {
Tags map[string]map[string]string `json:"tags,omitempty"`
- Backend json.RawMessage `json:"backend,omitempty"`
+ Backend jsontext.Value `json:"backend,omitempty"`
HandlerFormat int `json:"handlerFormat"`
}
@@ -469,7 +471,7 @@ func (h *Handler) Snapshot(ctx context.Context) []byte {
Tags: h.snapshotTags(),
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "kms: handler snapshot marshal failed", "error", err)
@@ -489,7 +491,7 @@ func (h *Handler) Restore(ctx context.Context, data []byte) error {
HandlerFormat int `json:"handlerFormat"`
}
- if err := json.Unmarshal(data, &peek); err != nil {
+ if err := json.Unmarshal(data, &peek, jsonx.V1()); err != nil {
return fmt.Errorf("kms: restore handler snapshot: %w", err)
}
@@ -500,7 +502,7 @@ func (h *Handler) Restore(ctx context.Context, data []byte) error {
}
var snap handlerSnapshot
- if err := json.Unmarshal(data, &snap); err != nil {
+ if err := json.Unmarshal(data, &snap, jsonx.V1()); err != nil {
return fmt.Errorf("kms: restore handler snapshot: %w", err)
}
diff --git a/services/kms/realism_validation_test.go b/services/kms/realism_validation_test.go
index 18807bdb38..17ed64270b 100644
--- a/services/kms/realism_validation_test.go
+++ b/services/kms/realism_validation_test.go
@@ -2,13 +2,15 @@ package kms_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func kmsErr(t *testing.T, body []byte) (string, string) {
@@ -19,7 +21,7 @@ func kmsErr(t *testing.T, body []byte) (string, string) {
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(body, &e))
+ require.NoError(t, json.Unmarshal(body, &e, jsonx.V1()))
return e.Type, e.Message
}
@@ -98,7 +100,10 @@ func TestKMSWire_ErrorRealism(t *testing.T) {
} `json:"KeyMetadata"`
}
- require.NoError(t, json.Unmarshal(doKMSRequest(t, h, "CreateKey", createBody).Body.Bytes(), &created))
+ require.NoError(
+ t,
+ json.Unmarshal(doKMSRequest(t, h, "CreateKey", createBody).Body.Bytes(), &created, jsonx.V1()),
+ )
keyID := created.KeyMetadata.KeyID
rec := doKMSRequest(t, h, tt.action, tt.body(keyID))
diff --git a/services/kms/replicate_key_policy_test.go b/services/kms/replicate_key_policy_test.go
index 16fd765643..ba6c5e9d34 100644
--- a/services/kms/replicate_key_policy_test.go
+++ b/services/kms/replicate_key_policy_test.go
@@ -21,7 +21,7 @@ package kms_test
// regional endpoint would.
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -29,6 +29,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
)
@@ -74,7 +75,7 @@ func Test_ReplicateKey_InlinePolicy(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code, createRec.Body.String())
var createOut kms.CreateKeyOutput
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
primaryID := createOut.KeyMetadata.KeyID
replicateBody := `{"KeyId":"` + primaryID + `","ReplicaRegion":"us-west-2","Policy":` +
@@ -89,7 +90,7 @@ func Test_ReplicateKey_InlinePolicy(t *testing.T) {
require.Equal(t, http.StatusOK, replicateRec.Code, replicateRec.Body.String())
var replicateOut kms.ReplicateKeyOutput
- require.NoError(t, json.Unmarshal(replicateRec.Body.Bytes(), &replicateOut))
+ require.NoError(t, json.Unmarshal(replicateRec.Body.Bytes(), &replicateOut, jsonx.V1()))
replicaID := replicateOut.ReplicaKeyMetadata.KeyID
require.NotEmpty(t, replicaID)
@@ -99,7 +100,7 @@ func Test_ReplicateKey_InlinePolicy(t *testing.T) {
require.Equal(t, http.StatusOK, polRec.Code, polRec.Body.String())
var polOut kms.GetKeyPolicyOutput
- require.NoError(t, json.Unmarshal(polRec.Body.Bytes(), &polOut))
+ require.NoError(t, json.Unmarshal(polRec.Body.Bytes(), &polOut, jsonx.V1()))
assert.Equal(t, "default", polOut.PolicyName)
if tc.wantPolicy != "" {
@@ -116,7 +117,7 @@ func Test_ReplicateKey_InlinePolicy(t *testing.T) {
require.Equal(t, http.StatusOK, primaryPolRec.Code)
var primaryPolOut kms.GetKeyPolicyOutput
- require.NoError(t, json.Unmarshal(primaryPolRec.Body.Bytes(), &primaryPolOut))
+ require.NoError(t, json.Unmarshal(primaryPolRec.Body.Bytes(), &primaryPolOut, jsonx.V1()))
assert.NotEqual(t, customPolicy, primaryPolOut.Policy)
})
}
diff --git a/services/kms/terraform_lifecycle_test.go b/services/kms/terraform_lifecycle_test.go
index 7875c7ca3b..bf8a68b6ac 100644
--- a/services/kms/terraform_lifecycle_test.go
+++ b/services/kms/terraform_lifecycle_test.go
@@ -20,7 +20,7 @@ package kms_test
// regression (e.g. "KeyId" vs "KeyID") would also be caught, not just a Go-side one.
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -28,6 +28,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
)
@@ -65,7 +66,7 @@ func TestTerraformLifecycle_KMSKey(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code, createRec.Body.String())
var createOut kms.CreateKeyOutput
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
keyID := createOut.KeyMetadata.KeyID
require.NotEmpty(t, keyID)
@@ -86,7 +87,7 @@ func TestTerraformLifecycle_KMSKey(t *testing.T) {
"repeated DescribeKey must be byte-identical -- any diff here is perpetual terraform plan drift")
var desc1 kms.DescribeKeyOutput
- require.NoError(t, json.Unmarshal(descRec1.Body.Bytes(), &desc1))
+ require.NoError(t, json.Unmarshal(descRec1.Body.Bytes(), &desc1, jsonx.V1()))
assert.Equal(t, "terraform-managed key", desc1.KeyMetadata.Description)
assert.Equal(t, "SYMMETRIC_DEFAULT", desc1.KeyMetadata.KeySpec)
assert.Equal(t, "SYMMETRIC_DEFAULT", desc1.KeyMetadata.CustomerMasterKeySpec,
@@ -107,7 +108,7 @@ func TestTerraformLifecycle_KMSKey(t *testing.T) {
"that made aws_kms_key poll to a 10-minute timeout")
var polOut kms.GetKeyPolicyOutput
- require.NoError(t, json.Unmarshal(polRec1.Body.Bytes(), &polOut))
+ require.NoError(t, json.Unmarshal(polRec1.Body.Bytes(), &polOut, jsonx.V1()))
assert.JSONEq(t, policy, polOut.Policy, "GetKeyPolicy must return exactly the policy supplied to CreateKey")
// --- ListResourceTags: must return exactly the tags supplied to CreateKey. ---
@@ -119,7 +120,7 @@ func TestTerraformLifecycle_KMSKey(t *testing.T) {
"repeated ListResourceTags must be byte-identical")
var tagsOut tagsWireOutput
- require.NoError(t, json.Unmarshal(tagsRec1.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(tagsRec1.Body.Bytes(), &tagsOut, jsonx.V1()))
gotTags := make(map[string]string, len(tagsOut.Tags))
for _, tg := range tagsOut.Tags {
@@ -136,7 +137,7 @@ func TestTerraformLifecycle_KMSKey(t *testing.T) {
assert.JSONEq(t, rotRec1.Body.String(), rotRec2.Body.String())
var rotOut kms.GetKeyRotationStatusOutput
- require.NoError(t, json.Unmarshal(rotRec1.Body.Bytes(), &rotOut))
+ require.NoError(t, json.Unmarshal(rotRec1.Body.Bytes(), &rotOut, jsonx.V1()))
assert.False(t, rotOut.KeyRotationEnabled, "key rotation must default to disabled")
// --- CreateAlias -> ListAliases must show it, resolving TargetKeyId correctly. ---
@@ -149,7 +150,7 @@ func TestTerraformLifecycle_KMSKey(t *testing.T) {
require.Equal(t, http.StatusOK, listAliasesRec.Code)
var aliasesOut kms.ListAliasesOutput
- require.NoError(t, json.Unmarshal(listAliasesRec.Body.Bytes(), &aliasesOut))
+ require.NoError(t, json.Unmarshal(listAliasesRec.Body.Bytes(), &aliasesOut, jsonx.V1()))
require.Len(t, aliasesOut.Aliases, 1)
assert.Equal(t, "alias/tf-lifecycle-test", aliasesOut.Aliases[0].AliasName)
assert.Equal(t, keyID, aliasesOut.Aliases[0].TargetKeyID)
@@ -162,7 +163,7 @@ func TestTerraformLifecycle_KMSKey(t *testing.T) {
require.Equal(t, http.StatusOK, scheduleRec.Code, scheduleRec.Body.String())
var scheduleOut kms.ScheduleKeyDeletionOutput
- require.NoError(t, json.Unmarshal(scheduleRec.Body.Bytes(), &scheduleOut))
+ require.NoError(t, json.Unmarshal(scheduleRec.Body.Bytes(), &scheduleOut, jsonx.V1()))
assert.Equal(t, kms.KeyStatePendingDeletion, scheduleOut.KeyState)
assert.Positive(t, scheduleOut.DeletionDate)
assert.Equal(t, 7, scheduleOut.PendingWindowInDays)
@@ -171,7 +172,7 @@ func TestTerraformLifecycle_KMSKey(t *testing.T) {
require.Equal(t, http.StatusOK, finalDescRec.Code)
var finalDesc kms.DescribeKeyOutput
- require.NoError(t, json.Unmarshal(finalDescRec.Body.Bytes(), &finalDesc))
+ require.NoError(t, json.Unmarshal(finalDescRec.Body.Bytes(), &finalDesc, jsonx.V1()))
assert.Equal(t, kms.KeyStatePendingDeletion, finalDesc.KeyMetadata.KeyState)
assert.Positive(t, finalDesc.KeyMetadata.DeletionDate)
assert.Equal(t, 7, finalDesc.KeyMetadata.PendingDeletionWindowInDays)
@@ -181,7 +182,7 @@ func TestTerraformLifecycle_KMSKey(t *testing.T) {
// strconvQuote JSON-quotes s (equivalent to strconv.Quote for the ASCII/simple policy
// strings this test uses, without pulling in strconv just for one call site).
func strconvQuote(s string) string {
- b, err := json.Marshal(s)
+ b, err := json.Marshal(s, jsonx.V1())
if err != nil {
panic(err)
}
diff --git a/services/kms/undeclared_members_test.go b/services/kms/undeclared_members_test.go
index bc197e250d..fcb04dbb27 100644
--- a/services/kms/undeclared_members_test.go
+++ b/services/kms/undeclared_members_test.go
@@ -1,7 +1,7 @@
package kms_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/kms/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_KeyMetadataOmitsUndeclaredMembers(t *testing.T) {
@@ -32,14 +34,14 @@ func TestHandler_KeyMetadataOmitsUndeclaredMembers(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotContains(t, out["KeyMetadata"], "PrimaryRegion")
assert.NotContains(t, out["KeyMetadata"], "MultiRegionKeyType")
id := out["KeyMetadata"]["KeyId"].(string)
rec = sendKMSOp(t, h, "DescribeKey", `{"KeyId":"`+id+`"}`)
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotContains(t, out["KeyMetadata"], "PrimaryRegion")
assert.NotContains(t, out["KeyMetadata"], "MultiRegionKeyType")
})
diff --git a/services/kms/validation.go b/services/kms/validation.go
index fd84aaaac4..2e6f0f18e4 100644
--- a/services/kms/validation.go
+++ b/services/kms/validation.go
@@ -1,12 +1,14 @@
package kms
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"regexp"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var keyIDShapeRe = regexp.MustCompile(
@@ -21,7 +23,7 @@ func validateListMarker(action string, body []byte) error {
Marker string `json:"Marker"`
}
- if json.Unmarshal(body, &in) == nil && in.Marker != "" {
+ if json.Unmarshal(body, &in, jsonx.V1()) == nil && in.Marker != "" {
if n, err := strconv.Atoi(in.Marker); err != nil || n < 0 {
return fmt.Errorf("%w: invalid marker", ErrInvalidMarker)
}
diff --git a/services/kms/wire_error_types_test.go b/services/kms/wire_error_types_test.go
index 2486b168f9..16f4394f30 100644
--- a/services/kms/wire_error_types_test.go
+++ b/services/kms/wire_error_types_test.go
@@ -2,7 +2,7 @@ package kms_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/kms"
)
@@ -226,7 +227,7 @@ func TestWireErrorTypes_NotValidationException(t *testing.T) {
var resp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantType, resp.Type)
assert.NotEqual(t, "ValidationException", resp.Type)
})
@@ -351,7 +352,7 @@ func TestWireErrorTypes_MalformedKeyIDArn(t *testing.T) {
var resp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantType, resp.Type)
})
}
@@ -360,7 +361,7 @@ func TestWireErrorTypes_MalformedKeyIDArn(t *testing.T) {
func mustJSON(t *testing.T, v any) string {
t.Helper()
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
return string(b)
diff --git a/services/lakeformation/handler_credentials.go b/services/lakeformation/handler_credentials.go
index 22ee1dab04..19d8a26d4f 100644
--- a/services/lakeformation/handler_credentials.go
+++ b/services/lakeformation/handler_credentials.go
@@ -2,16 +2,18 @@ package lakeformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleAssumeDecoratedRoleWithSAML(_ context.Context, c *echo.Context, body []byte) error {
var in assumeDecoratedRoleWithSAMLInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -40,7 +42,7 @@ func (h *Handler) handleGetDataLakePrincipal(ctx context.Context, c *echo.Contex
func (h *Handler) handleGetTemporaryDataLocationCredentials(_ context.Context, c *echo.Context, body []byte) error {
var in getTemporaryDataLocationCredentialsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
// GetTemporaryDataLocationCredentialsInput (lakeformation@v1.50.4
@@ -60,7 +62,7 @@ func (h *Handler) handleGetTemporaryDataLocationCredentials(_ context.Context, c
func (h *Handler) handleGetTemporaryGluePartitionCredentials(_ context.Context, c *echo.Context, body []byte) error {
var in getTemporaryGluePartitionCredentialsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
if strings.TrimSpace(in.TableArn) == "" {
@@ -78,7 +80,7 @@ func (h *Handler) handleGetTemporaryGluePartitionCredentials(_ context.Context,
func (h *Handler) handleGetTemporaryGlueTableCredentials(_ context.Context, c *echo.Context, body []byte) error {
var in getTemporaryGlueTableCredentialsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
if strings.TrimSpace(in.TableArn) == "" {
diff --git a/services/lakeformation/handler_credentials_test.go b/services/lakeformation/handler_credentials_test.go
index d49a94c033..10c1118bdb 100644
--- a/services/lakeformation/handler_credentials_test.go
+++ b/services/lakeformation/handler_credentials_test.go
@@ -1,13 +1,14 @@
package lakeformation_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lakeformation"
)
@@ -61,7 +62,7 @@ func TestHandler_AssumeDecoratedRoleWithSAML(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for _, field := range tt.wantFields {
assert.Contains(t, resp, field)
assert.NotEmpty(t, resp[field])
@@ -85,7 +86,7 @@ func TestAssumeDecoratedRoleWithSAML_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["AccessKeyId"])
assert.NotEmpty(t, out["SecretAccessKey"])
assert.NotEmpty(t, out["SessionToken"])
diff --git a/services/lakeformation/handler_data_cells_filter.go b/services/lakeformation/handler_data_cells_filter.go
index f9d0054a1e..be4fbe754e 100644
--- a/services/lakeformation/handler_data_cells_filter.go
+++ b/services/lakeformation/handler_data_cells_filter.go
@@ -2,16 +2,18 @@ package lakeformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateDataCellsFilter(_ context.Context, c *echo.Context, body []byte) error {
var in createDataCellsFilterInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -32,7 +34,7 @@ func (h *Handler) handleCreateDataCellsFilter(_ context.Context, c *echo.Context
func (h *Handler) handleDeleteDataCellsFilter(_ context.Context, c *echo.Context, body []byte) error {
var in deleteDataCellsFilterInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -46,7 +48,7 @@ func (h *Handler) handleDeleteDataCellsFilter(_ context.Context, c *echo.Context
func (h *Handler) handleListDataCellsFilter(_ context.Context, c *echo.Context, body []byte) error {
var in listDataCellsFilterInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
@@ -79,7 +81,7 @@ func (h *Handler) handleListDataCellsFilter(_ context.Context, c *echo.Context,
func (h *Handler) handleGetDataCellsFilter(_ context.Context, c *echo.Context, body []byte) error {
var in getDataCellsFilterInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
f, err := h.Backend.GetDataCellsFilter(in.TableCatalogID, in.DatabaseName, in.TableName, in.Name)
@@ -92,7 +94,7 @@ func (h *Handler) handleGetDataCellsFilter(_ context.Context, c *echo.Context, b
func (h *Handler) handleUpdateDataCellsFilter(_ context.Context, c *echo.Context, body []byte) error {
var in updateDataCellsFilterInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
if in.TableData == nil {
diff --git a/services/lakeformation/handler_data_lake_settings.go b/services/lakeformation/handler_data_lake_settings.go
index 712d6626dd..4497fd47af 100644
--- a/services/lakeformation/handler_data_lake_settings.go
+++ b/services/lakeformation/handler_data_lake_settings.go
@@ -2,16 +2,18 @@ package lakeformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleGetDataLakeSettings(_ context.Context, c *echo.Context, body []byte) error {
var in getDataLakeSettingsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
@@ -26,7 +28,7 @@ func (h *Handler) handleGetDataLakeSettings(_ context.Context, c *echo.Context,
func (h *Handler) handlePutDataLakeSettings(_ context.Context, c *echo.Context, body []byte) error {
var in putDataLakeSettingsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
diff --git a/services/lakeformation/handler_data_lake_settings_test.go b/services/lakeformation/handler_data_lake_settings_test.go
index 327f9d0c18..22ba6412b9 100644
--- a/services/lakeformation/handler_data_lake_settings_test.go
+++ b/services/lakeformation/handler_data_lake_settings_test.go
@@ -1,13 +1,14 @@
package lakeformation_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lakeformation"
)
@@ -41,7 +42,7 @@ func TestHandler_GetDataLakeSettings(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "DataLakeSettings")
})
}
diff --git a/services/lakeformation/handler_lake_formation_identity_center.go b/services/lakeformation/handler_lake_formation_identity_center.go
index 6e541e9f8e..9b5d4670d2 100644
--- a/services/lakeformation/handler_lake_formation_identity_center.go
+++ b/services/lakeformation/handler_lake_formation_identity_center.go
@@ -2,10 +2,12 @@ package lakeformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateLakeFormationIdentityCenterConfiguration(
@@ -14,7 +16,7 @@ func (h *Handler) handleCreateLakeFormationIdentityCenterConfiguration(
body []byte,
) error {
var in createLakeFormationIdentityCenterConfigurationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -42,7 +44,7 @@ func (h *Handler) handleDeleteLakeFormationIdentityCenterConfiguration(
) error {
var in deleteLakeFormationIdentityCenterConfigurationInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
@@ -62,7 +64,7 @@ func (h *Handler) handleDescribeLakeFormationIdentityCenterConfiguration(
) error {
var in describeLakeFormationIdentityCenterConfigurationInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
@@ -90,7 +92,7 @@ func (h *Handler) handleUpdateLakeFormationIdentityCenterConfiguration(
) error {
var in updateLakeFormationIdentityCenterConfigurationInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
diff --git a/services/lakeformation/handler_lake_formation_identity_center_test.go b/services/lakeformation/handler_lake_formation_identity_center_test.go
index d5c4948083..afffbce80f 100644
--- a/services/lakeformation/handler_lake_formation_identity_center_test.go
+++ b/services/lakeformation/handler_lake_formation_identity_center_test.go
@@ -1,13 +1,14 @@
package lakeformation_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lakeformation"
)
@@ -52,7 +53,7 @@ func TestHandler_CreateLakeFormationIdentityCenterConfiguration(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "ApplicationArn")
}
})
@@ -72,7 +73,7 @@ func TestIdentityCenterConfiguration_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["ApplicationArn"])
assert.Contains(t, out["ApplicationArn"].(string), "arn:aws:sso")
}
diff --git a/services/lakeformation/handler_lf_tag_expression.go b/services/lakeformation/handler_lf_tag_expression.go
index be036532c0..35e819a55d 100644
--- a/services/lakeformation/handler_lf_tag_expression.go
+++ b/services/lakeformation/handler_lf_tag_expression.go
@@ -2,16 +2,18 @@ package lakeformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateLFTagExpression(_ context.Context, c *echo.Context, body []byte) error {
var in createLFTagExpressionInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -32,7 +34,7 @@ func (h *Handler) handleCreateLFTagExpression(_ context.Context, c *echo.Context
func (h *Handler) handleDeleteLFTagExpression(_ context.Context, c *echo.Context, body []byte) error {
var in deleteLFTagExpressionInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -50,7 +52,7 @@ func (h *Handler) handleDeleteLFTagExpression(_ context.Context, c *echo.Context
func (h *Handler) handleListLFTagExpressions(_ context.Context, c *echo.Context, body []byte) error {
var in listLFTagExpressionsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
@@ -65,7 +67,7 @@ func (h *Handler) handleListLFTagExpressions(_ context.Context, c *echo.Context,
func (h *Handler) handleGetLFTagExpression(_ context.Context, c *echo.Context, body []byte) error {
var in getLFTagExpressionInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
expr, err := h.Backend.GetLFTagExpression(in.Name, in.CatalogID)
@@ -83,7 +85,7 @@ func (h *Handler) handleGetLFTagExpression(_ context.Context, c *echo.Context, b
func (h *Handler) handleUpdateLFTagExpression(_ context.Context, c *echo.Context, body []byte) error {
var in updateLFTagExpressionInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
if strings.TrimSpace(in.Name) == "" {
diff --git a/services/lakeformation/handler_lf_tags.go b/services/lakeformation/handler_lf_tags.go
index da7605c1d8..5aae9acc0a 100644
--- a/services/lakeformation/handler_lf_tags.go
+++ b/services/lakeformation/handler_lf_tags.go
@@ -2,15 +2,17 @@ package lakeformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateLFTag(_ context.Context, c *echo.Context, body []byte) error {
var in createLFTagInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -23,7 +25,7 @@ func (h *Handler) handleCreateLFTag(_ context.Context, c *echo.Context, body []b
func (h *Handler) handleDeleteLFTag(_ context.Context, c *echo.Context, body []byte) error {
var in deleteLFTagInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -36,7 +38,7 @@ func (h *Handler) handleDeleteLFTag(_ context.Context, c *echo.Context, body []b
func (h *Handler) handleGetLFTag(_ context.Context, c *echo.Context, body []byte) error {
var in getLFTagInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -54,7 +56,7 @@ func (h *Handler) handleGetLFTag(_ context.Context, c *echo.Context, body []byte
func (h *Handler) handleUpdateLFTag(_ context.Context, c *echo.Context, body []byte) error {
var in updateLFTagInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -68,7 +70,7 @@ func (h *Handler) handleUpdateLFTag(_ context.Context, c *echo.Context, body []b
func (h *Handler) handleListLFTags(_ context.Context, c *echo.Context, body []byte) error {
var in listLFTagsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
@@ -104,7 +106,7 @@ func (h *Handler) checkAddRemoveLFTagsInput(c *echo.Context, resource *Resource,
func (h *Handler) handleAddLFTagsToResource(_ context.Context, c *echo.Context, body []byte) error {
var in addLFTagsToResourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -122,7 +124,7 @@ func (h *Handler) handleAddLFTagsToResource(_ context.Context, c *echo.Context,
func (h *Handler) handleRemoveLFTagsFromResource(_ context.Context, c *echo.Context, body []byte) error {
var in removeLFTagsFromResourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -140,7 +142,7 @@ func (h *Handler) handleRemoveLFTagsFromResource(_ context.Context, c *echo.Cont
func (h *Handler) handleGetResourceLFTags(_ context.Context, c *echo.Context, body []byte) error {
var in getResourceLFTagsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -174,7 +176,7 @@ func (h *Handler) handleGetResourceLFTags(_ context.Context, c *echo.Context, bo
func (h *Handler) handleSearchDatabasesByLFTags(_ context.Context, c *echo.Context, body []byte) error {
var in searchDatabasesByLFTagsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
@@ -190,7 +192,7 @@ func (h *Handler) handleSearchDatabasesByLFTags(_ context.Context, c *echo.Conte
func (h *Handler) handleSearchTablesByLFTags(_ context.Context, c *echo.Context, body []byte) error {
var in searchTablesByLFTagsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
diff --git a/services/lakeformation/handler_lf_tags_test.go b/services/lakeformation/handler_lf_tags_test.go
index 1535d03534..56a3d89600 100644
--- a/services/lakeformation/handler_lf_tags_test.go
+++ b/services/lakeformation/handler_lf_tags_test.go
@@ -1,7 +1,7 @@
package lakeformation_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lakeformation"
)
@@ -49,7 +50,7 @@ func TestHandler_LFTagLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, tt.tagKey, getResp["TagKey"])
// Update
@@ -64,7 +65,7 @@ func TestHandler_LFTagLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags, _ := listResp["LFTags"].([]any)
assert.Len(t, tags, 1)
@@ -142,7 +143,7 @@ func TestHandler_AddLFTagsToResource(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
failures, _ := resp["Failures"].([]any)
assert.Len(t, failures, tt.wantFails)
}
@@ -234,7 +235,7 @@ func TestCreateLFTag_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "team", out["TagKey"])
}
@@ -279,7 +280,7 @@ func TestListLFTags_Sorted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tags := out["LFTags"].([]any)
require.Len(t, tags, 3)
@@ -306,7 +307,7 @@ func TestAddLFTagsToResource_PartialFailure(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
failures := out["Failures"].([]any)
assert.Len(t, failures, 1, "only nonexistent tag should fail")
assert.Equal(t, "nonexistent", failures[0].(map[string]any)["LFTag"].(map[string]any)["TagKey"])
@@ -335,7 +336,7 @@ func TestUpdateLFTag_AddAndRemove(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
vals := out["TagValues"].([]any)
strs := make([]string, len(vals))
@@ -368,7 +369,7 @@ func TestAddLFTagsToResource_TableResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out["Failures"])
}
diff --git a/services/lakeformation/handler_opt_ins.go b/services/lakeformation/handler_opt_ins.go
index 2524c95a0a..d1e658aad2 100644
--- a/services/lakeformation/handler_opt_ins.go
+++ b/services/lakeformation/handler_opt_ins.go
@@ -2,15 +2,17 @@ package lakeformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateLakeFormationOptIn(_ context.Context, c *echo.Context, body []byte) error {
var in createLakeFormationOptInInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -31,7 +33,7 @@ func (h *Handler) handleCreateLakeFormationOptIn(_ context.Context, c *echo.Cont
func (h *Handler) handleDeleteLakeFormationOptIn(_ context.Context, c *echo.Context, body []byte) error {
var in deleteLakeFormationOptInInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -53,7 +55,7 @@ func (h *Handler) handleDeleteLakeFormationOptIn(_ context.Context, c *echo.Cont
func (h *Handler) handleListLakeFormationOptIns(_ context.Context, c *echo.Context, body []byte) error {
var in listLakeFormationOptInsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
diff --git a/services/lakeformation/handler_permissions.go b/services/lakeformation/handler_permissions.go
index bc3e691a5c..39cf7aed98 100644
--- a/services/lakeformation/handler_permissions.go
+++ b/services/lakeformation/handler_permissions.go
@@ -3,16 +3,18 @@ package lakeformation
import (
"cmp"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleGrantPermissions(ctx context.Context, c *echo.Context, body []byte) error {
var in grantPermissionsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -33,7 +35,7 @@ func (h *Handler) handleGrantPermissions(ctx context.Context, c *echo.Context, b
func (h *Handler) handleRevokePermissions(ctx context.Context, c *echo.Context, body []byte) error {
var in revokePermissionsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -55,7 +57,7 @@ func (h *Handler) handleRevokePermissions(ctx context.Context, c *echo.Context,
func (h *Handler) handleListPermissions(_ context.Context, c *echo.Context, body []byte) error {
var in listPermissionsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
@@ -85,7 +87,7 @@ func (h *Handler) handleListPermissions(_ context.Context, c *echo.Context, body
func (h *Handler) handleBatchGrantPermissions(ctx context.Context, c *echo.Context, body []byte) error {
var in batchGrantPermissionsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -99,7 +101,7 @@ func (h *Handler) handleBatchGrantPermissions(ctx context.Context, c *echo.Conte
func (h *Handler) handleBatchRevokePermissions(ctx context.Context, c *echo.Context, body []byte) error {
var in batchRevokePermissionsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -139,7 +141,7 @@ func (h *Handler) runBatchPermissions(
func (h *Handler) handleGetEffectivePermissionsForPath(_ context.Context, c *echo.Context, body []byte) error {
var in getEffectivePermissionsForPathInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
diff --git a/services/lakeformation/handler_permissions_test.go b/services/lakeformation/handler_permissions_test.go
index d44d964d48..29b05f52c5 100644
--- a/services/lakeformation/handler_permissions_test.go
+++ b/services/lakeformation/handler_permissions_test.go
@@ -1,13 +1,14 @@
package lakeformation_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lakeformation"
)
@@ -45,7 +46,7 @@ func TestHandler_GrantRevokeListPermissions(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
perms, _ := listResp["PrincipalResourcePermissions"].([]any)
assert.Len(t, perms, 1)
@@ -82,7 +83,7 @@ func TestHandler_BatchGrantRevokePermissions(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
failures, _ := resp["Failures"].([]any)
assert.Empty(t, failures)
@@ -104,7 +105,7 @@ func TestBatchGrantPermissions_EmptyEntries(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out["Failures"])
}
@@ -126,7 +127,7 @@ func TestGrantAndListPermissions(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
perms := out["PrincipalResourcePermissions"].([]any)
assert.Len(t, perms, 1)
}
diff --git a/services/lakeformation/handler_resources.go b/services/lakeformation/handler_resources.go
index 1287564074..401ebe278a 100644
--- a/services/lakeformation/handler_resources.go
+++ b/services/lakeformation/handler_resources.go
@@ -2,16 +2,18 @@ package lakeformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleRegisterResource(_ context.Context, c *echo.Context, body []byte) error {
var in registerResourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -46,7 +48,7 @@ func (h *Handler) handleRegisterResource(_ context.Context, c *echo.Context, bod
func (h *Handler) handleDeregisterResource(_ context.Context, c *echo.Context, body []byte) error {
var in deregisterResourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -59,7 +61,7 @@ func (h *Handler) handleDeregisterResource(_ context.Context, c *echo.Context, b
func (h *Handler) handleDescribeResource(_ context.Context, c *echo.Context, body []byte) error {
var in describeResourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -74,7 +76,7 @@ func (h *Handler) handleDescribeResource(_ context.Context, c *echo.Context, bod
func (h *Handler) handleListResources(_ context.Context, c *echo.Context, body []byte) error {
var in listResourcesInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
@@ -94,7 +96,7 @@ func (h *Handler) handleListResources(_ context.Context, c *echo.Context, body [
func (h *Handler) handleUpdateResource(_ context.Context, c *echo.Context, body []byte) error {
var in updateResourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
diff --git a/services/lakeformation/handler_resources_test.go b/services/lakeformation/handler_resources_test.go
index 0fe4e21965..2f5e058ced 100644
--- a/services/lakeformation/handler_resources_test.go
+++ b/services/lakeformation/handler_resources_test.go
@@ -1,13 +1,14 @@
package lakeformation_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lakeformation"
)
@@ -49,7 +50,7 @@ func TestHandler_RegisterDeregisterDescribeResource(t *testing.T) {
assert.Equal(t, tt.wantDescStatus, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
ri, ok := descResp["ResourceInfo"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.resourceArn, ri["ResourceArn"])
@@ -123,7 +124,7 @@ func TestHandler_ListResources(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, _ := resp["ResourceInfoList"].([]any)
assert.Len(t, list, tt.wantCount)
})
@@ -157,7 +158,7 @@ func TestListResources_Sorted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
list := out["ResourceInfoList"].([]any)
require.Len(t, list, 3)
diff --git a/services/lakeformation/handler_table_storage.go b/services/lakeformation/handler_table_storage.go
index b6cf74e6a8..c80c080dc9 100644
--- a/services/lakeformation/handler_table_storage.go
+++ b/services/lakeformation/handler_table_storage.go
@@ -2,16 +2,18 @@ package lakeformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleGetTableObjects(_ context.Context, c *echo.Context, body []byte) error {
var in getTableObjectsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
@@ -25,7 +27,7 @@ func (h *Handler) handleGetTableObjects(_ context.Context, c *echo.Context, body
func (h *Handler) handleListTableStorageOptimizers(_ context.Context, c *echo.Context, body []byte) error {
var in listTableStorageOptimizersInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
opts, next := h.Backend.ListTableStorageOptimizers(
@@ -38,7 +40,7 @@ func (h *Handler) handleListTableStorageOptimizers(_ context.Context, c *echo.Co
func (h *Handler) handleUpdateTableObjects(_ context.Context, c *echo.Context, body []byte) error {
var in updateTableObjectsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
@@ -53,7 +55,7 @@ func (h *Handler) handleUpdateTableObjects(_ context.Context, c *echo.Context, b
func (h *Handler) handleUpdateTableStorageOptimizer(_ context.Context, c *echo.Context, body []byte) error {
var in updateTableStorageOptimizerInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
result := h.Backend.UpdateTableStorageOptimizer(
diff --git a/services/lakeformation/handler_test.go b/services/lakeformation/handler_test.go
index 7f31381a3c..0294685251 100644
--- a/services/lakeformation/handler_test.go
+++ b/services/lakeformation/handler_test.go
@@ -2,7 +2,8 @@ package lakeformation_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lakeformation"
)
@@ -62,7 +64,7 @@ func newTestContext(t *testing.T, path string, body any) (*echo.Context, *httpte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -91,7 +93,7 @@ func postJSON(t *testing.T, h *lakeformation.Handler, path string, body any) *ht
// jsonDecode is a helper to decode a response body into a map.
func jsonDecode(r io.Reader, v any) error {
- return json.NewDecoder(r).Decode(v)
+ return json.UnmarshalDecode(jsontext.NewDecoder(r), v, jsonx.V1())
}
func TestHandler_UnknownOperation(t *testing.T) {
@@ -515,7 +517,7 @@ func TestErrValidationMapping(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidInputException", resp["__type"])
})
}
diff --git a/services/lakeformation/handler_transactions.go b/services/lakeformation/handler_transactions.go
index 31cfa87fd1..ff3a2d1f32 100644
--- a/services/lakeformation/handler_transactions.go
+++ b/services/lakeformation/handler_transactions.go
@@ -2,16 +2,18 @@ package lakeformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCancelTransaction(_ context.Context, c *echo.Context, body []byte) error {
var in cancelTransactionInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -28,7 +30,7 @@ func (h *Handler) handleCancelTransaction(_ context.Context, c *echo.Context, bo
func (h *Handler) handleCommitTransaction(_ context.Context, c *echo.Context, body []byte) error {
var in commitTransactionInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -47,7 +49,7 @@ func (h *Handler) handleCommitTransaction(_ context.Context, c *echo.Context, bo
func (h *Handler) handleStartTransaction(_ context.Context, c *echo.Context, body []byte) error {
var in startTransactionInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
@@ -59,7 +61,7 @@ func (h *Handler) handleStartTransaction(_ context.Context, c *echo.Context, bod
func (h *Handler) handleDescribeTransaction(_ context.Context, c *echo.Context, body []byte) error {
var in describeTransactionInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -78,7 +80,7 @@ func (h *Handler) handleDescribeTransaction(_ context.Context, c *echo.Context,
func (h *Handler) handleListTransactions(_ context.Context, c *echo.Context, body []byte) error {
var in listTransactionsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
@@ -93,7 +95,7 @@ func (h *Handler) handleListTransactions(_ context.Context, c *echo.Context, bod
func (h *Handler) handleDeleteObjectsOnCancel(_ context.Context, c *echo.Context, body []byte) error {
var in deleteObjectsOnCancelInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
@@ -119,7 +121,7 @@ func (h *Handler) handleDeleteObjectsOnCancel(_ context.Context, c *echo.Context
func (h *Handler) handleExtendTransaction(_ context.Context, c *echo.Context, body []byte) error {
var in extendTransactionInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
}
diff --git a/services/lakeformation/handler_transactions_test.go b/services/lakeformation/handler_transactions_test.go
index 0afd7d991d..a2b2355754 100644
--- a/services/lakeformation/handler_transactions_test.go
+++ b/services/lakeformation/handler_transactions_test.go
@@ -1,13 +1,14 @@
package lakeformation_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lakeformation"
)
@@ -122,7 +123,7 @@ func TestHandler_CommitTransaction(t *testing.T) {
if tt.wantStatus == http.StatusOK && tt.wantStatus2 != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantStatus2, resp["TransactionStatus"])
}
})
@@ -198,7 +199,7 @@ func TestCommitTransaction_ReturnsStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "COMMITTED", out["TransactionStatus"])
}
diff --git a/services/lakeformation/handler_work_units.go b/services/lakeformation/handler_work_units.go
index 527ff73670..b95b35c684 100644
--- a/services/lakeformation/handler_work_units.go
+++ b/services/lakeformation/handler_work_units.go
@@ -2,16 +2,18 @@ package lakeformation
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleGetQueryState(_ context.Context, c *echo.Context, body []byte) error {
var in getQueryStateInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
state, err := h.Backend.GetQueryState(in.QueryID)
@@ -24,7 +26,7 @@ func (h *Handler) handleGetQueryState(_ context.Context, c *echo.Context, body [
func (h *Handler) handleGetQueryStatistics(_ context.Context, c *echo.Context, body []byte) error {
var in getQueryStatisticsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
exec, plan, err := h.Backend.GetQueryStatistics(in.QueryID)
@@ -37,7 +39,7 @@ func (h *Handler) handleGetQueryStatistics(_ context.Context, c *echo.Context, b
func (h *Handler) handleGetWorkUnitResults(_ context.Context, c *echo.Context, body []byte) error {
var in getWorkUnitResultsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
result, err := h.Backend.GetWorkUnitResults(in.QueryID, in.WorkUnitID, in.WorkUnitToken)
@@ -50,7 +52,7 @@ func (h *Handler) handleGetWorkUnitResults(_ context.Context, c *echo.Context, b
func (h *Handler) handleGetWorkUnits(_ context.Context, c *echo.Context, body []byte) error {
var in getWorkUnitsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
ranges, nextToken, err := h.Backend.GetWorkUnits(in.QueryID)
@@ -63,7 +65,7 @@ func (h *Handler) handleGetWorkUnits(_ context.Context, c *echo.Context, body []
func (h *Handler) handleStartQueryPlanning(_ context.Context, c *echo.Context, body []byte) error {
var in startQueryPlanningInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidInputException", err.Error())
}
if strings.TrimSpace(in.QueryString) == "" {
diff --git a/services/lakeformation/handler_work_units_test.go b/services/lakeformation/handler_work_units_test.go
index 310c8bc7d0..96afc053b0 100644
--- a/services/lakeformation/handler_work_units_test.go
+++ b/services/lakeformation/handler_work_units_test.go
@@ -1,13 +1,14 @@
package lakeformation_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lakeformation"
)
@@ -24,7 +25,7 @@ func TestStartQueryPlanning_RequiresDatabaseName(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out["message"].(string), "DatabaseName")
}
diff --git a/services/lakeformation/multi_region_test.go b/services/lakeformation/multi_region_test.go
index 8957d9bcac..a0798d3308 100644
--- a/services/lakeformation/multi_region_test.go
+++ b/services/lakeformation/multi_region_test.go
@@ -2,7 +2,8 @@ package lakeformation_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/lakeformation"
)
@@ -176,8 +178,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/lakeformation/pagination_validation.go b/services/lakeformation/pagination_validation.go
index 165b9547dc..039afba684 100644
--- a/services/lakeformation/pagination_validation.go
+++ b/services/lakeformation/pagination_validation.go
@@ -1,10 +1,12 @@
package lakeformation
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strconv"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -22,11 +24,11 @@ func validatePagination(op string, body []byte) error {
NextToken string `json:"NextToken"`
}
- if !json.Valid(body) {
+ if !jsontext.Value(body).IsValid(jsonx.V1()) {
return nil
}
- _ = json.Unmarshal(body, &in)
+ _ = json.Unmarshal(body, &in, jsonx.V1())
if in.MaxResults != nil && (*in.MaxResults < 1 || *in.MaxResults > maxListResults) {
return fmt.Errorf("MaxResults must be between 1 and %d: %w", maxListResults, ErrValidation)
diff --git a/services/lakeformation/persistence.go b/services/lakeformation/persistence.go
index a6575a115d..bc2d6fd285 100644
--- a/services/lakeformation/persistence.go
+++ b/services/lakeformation/persistence.go
@@ -2,10 +2,12 @@ package lakeformation
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -94,7 +96,7 @@ func newTransactionDTORegistry() (*store.Registry, *store.Table[transactionSnaps
// snapshot from an incompatible (older or newer) build of this backend as
// though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ResourceLFTags map[string][]LFTagPair `json:"resourceLFTags"`
Queries map[string]string `json:"queries,omitempty"`
TableStorageOptimizers map[string][]StorageOptimizer `json:"tableStorageOptimizers,omitempty"`
@@ -186,7 +188,7 @@ func (b *InMemoryBackend) Snapshot() ([]byte, error) {
// Marshal failure is returned to the caller (the Persistable wrapper logs
// it via the context-aware logger); do not log through slog.Default() here.
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("lakeformation: marshal snapshot: %w", err)
}
@@ -278,7 +280,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreTransactionsLocked restores the "dirty" transactions table from
// tables via the ephemeral DTO registry, converting each DTO back to its
// live type. The caller MUST hold b.mu for writing.
-func (b *InMemoryBackend) restoreTransactionsLocked(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreTransactionsLocked(tables map[string]jsontext.Value) error {
dtoReg, txDTOs := newTransactionDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
diff --git a/services/lakeformation/persistence_test.go b/services/lakeformation/persistence_test.go
index 21ee862a49..288414d41e 100644
--- a/services/lakeformation/persistence_test.go
+++ b/services/lakeformation/persistence_test.go
@@ -1,13 +1,14 @@
package lakeformation_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/services/lakeformation"
)
@@ -350,7 +351,7 @@ func TestPersistence_IncludesQueriesAndOptimizers(t *testing.T) {
// Verify queries survived restore (GetQueryState should work)
var qOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &qOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &qOut, jsonx.V1()))
queryID := qOut["QueryId"].(string)
state, err := b2.GetQueryState(queryID)
diff --git a/services/lambda/account_settings_test.go b/services/lambda/account_settings_test.go
index 8afa095077..961e1b89f1 100644
--- a/services/lambda/account_settings_test.go
+++ b/services/lambda/account_settings_test.go
@@ -1,7 +1,8 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -25,7 +27,7 @@ func TestAccountUsage_FunctionCount_Increments(t *testing.T) {
require.Equal(t, http.StatusOK, rec0.Code)
var s0 lambda.AccountSettingsOutput
- require.NoError(t, json.NewDecoder(rec0.Body).Decode(&s0))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec0.Body), &s0, jsonx.V1()))
require.NotNil(t, s0.AccountUsage)
assert.Equal(t, 0, s0.AccountUsage.FunctionCount)
@@ -36,7 +38,7 @@ func TestAccountUsage_FunctionCount_Increments(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var s2 lambda.AccountSettingsOutput
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&s2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &s2, jsonx.V1()))
require.NotNil(t, s2.AccountUsage)
assert.Equal(t, 2, s2.AccountUsage.FunctionCount)
}
@@ -50,7 +52,7 @@ func TestAccountUsage_AccountLimit_Fields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var s lambda.AccountSettingsOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&s))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &s, jsonx.V1()))
require.NotNil(t, s.AccountLimit)
assert.Positive(t, s.AccountLimit.TotalCodeSize)
assert.Positive(t, s.AccountLimit.ConcurrentExecutions)
@@ -110,7 +112,7 @@ func TestGetAccountSettings_UnreservedConcurrency(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
limit, ok := out["AccountLimit"].(map[string]any)
require.True(t, ok, "AccountLimit must be present")
@@ -133,7 +135,7 @@ func TestGetAccountSettings(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.AccountSettingsOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotNil(t, out.AccountLimit)
assert.Positive(t, out.AccountLimit.ConcurrentExecutions)
assert.Positive(t, out.AccountLimit.TotalCodeSize)
@@ -152,6 +154,6 @@ func TestGetAccountSettings_FunctionCount(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.AccountSettingsOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, 2, out.AccountUsage.FunctionCount)
}
diff --git a/services/lambda/async_destinations.go b/services/lambda/async_destinations.go
index b80c23d773..f4bddfecd9 100644
--- a/services/lambda/async_destinations.go
+++ b/services/lambda/async_destinations.go
@@ -3,9 +3,11 @@ package lambda
import (
"cmp"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -50,8 +52,8 @@ type asyncOutcome struct {
// asyncDestinationRecord is the JSON invocation record AWS Lambda delivers to
// OnSuccess/OnFailure destinations.
type asyncDestinationRecord struct {
- RequestPayload json.RawMessage `json:"requestPayload,omitempty"`
- ResponsePayload json.RawMessage `json:"responsePayload,omitempty"`
+ RequestPayload jsontext.Value `json:"requestPayload,omitempty"`
+ ResponsePayload jsontext.Value `json:"responsePayload,omitempty"`
Timestamp string `json:"timestamp"`
Version string `json:"version"`
RequestContext asyncRequestContext `json:"requestContext"`
@@ -100,7 +102,7 @@ func (b *InMemoryBackend) dispatchAsyncOutcome(ctx context.Context, out asyncOut
return
}
- body, marshalErr := json.Marshal(buildAsyncDestinationRecord(out, functionArn))
+ body, marshalErr := json.Marshal(buildAsyncDestinationRecord(out, functionArn), jsonx.V1())
if marshalErr != nil {
log.WarnContext(ctx, "lambda: failed to marshal destination record",
"function", out.functionName, "error", marshalErr)
@@ -197,18 +199,18 @@ func buildAsyncDestinationRecord(out asyncOutcome, functionArn string) asyncDest
// rawJSONOrNull returns b as raw JSON when it is valid JSON, otherwise a JSON
// string containing the raw bytes so the record always marshals cleanly.
-func rawJSONOrNull(b []byte) json.RawMessage {
+func rawJSONOrNull(b []byte) jsontext.Value {
if len(b) == 0 {
- return json.RawMessage("null")
+ return jsontext.Value("null")
}
- if json.Valid(b) {
- return json.RawMessage(b)
+ if jsontext.Value(b).IsValid(jsonx.V1()) {
+ return jsontext.Value(b)
}
- quoted, err := json.Marshal(string(b))
+ quoted, err := json.Marshal(string(b), jsonx.V1())
if err != nil {
- return json.RawMessage("null")
+ return jsontext.Value("null")
}
return quoted
diff --git a/services/lambda/capacity_providers_test.go b/services/lambda/capacity_providers_test.go
index e92eff569a..5e4b04234b 100644
--- a/services/lambda/capacity_providers_test.go
+++ b/services/lambda/capacity_providers_test.go
@@ -2,7 +2,8 @@ package lambda_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/sdktest"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/lambda"
@@ -231,7 +233,7 @@ func TestCapacityProvider_StateTransitionAndTags(t *testing.T) {
CapacityProviderArn string `json:"CapacityProviderArn"`
} `json:"CapacityProvider"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
rec = callInMemoryHandler(t, h, http.MethodGet, "/2025-11-30/capacity-providers/tagged", "")
require.Equal(t, http.StatusOK, rec.Code)
@@ -544,7 +546,7 @@ func TestCapacityProvider_GetDeleteUpdateList(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateOut lambda.UpdateCapacityProviderOutput
- require.NoError(t, json.NewDecoder(updateRec.Body).Decode(&updateOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updateRec.Body), &updateOut, jsonx.V1()))
require.NotNil(t, updateOut.CapacityProvider)
require.NotNil(t, updateOut.CapacityProvider.CapacityProviderScalingConfig)
require.NotNil(t, updateOut.CapacityProvider.CapacityProviderScalingConfig.MaxVCpuCount)
@@ -555,7 +557,7 @@ func TestCapacityProvider_GetDeleteUpdateList(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut lambda.ListCapacityProvidersOutput
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
assert.Len(t, listOut.CapacityProviders, 1)
// Delete — real AWS returns 200 with the deleted provider's state, not 204
@@ -564,7 +566,7 @@ func TestCapacityProvider_GetDeleteUpdateList(t *testing.T) {
require.Equal(t, http.StatusOK, delRec.Code)
var deleteOut lambda.DeleteCapacityProviderOutput
- require.NoError(t, json.NewDecoder(delRec.Body).Decode(&deleteOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(delRec.Body), &deleteOut, jsonx.V1()))
require.NotNil(t, deleteOut.CapacityProvider)
assert.True(t, strings.HasSuffix(deleteOut.CapacityProvider.CapacityProviderArn, "capacity-provider:test-cp"))
@@ -573,7 +575,7 @@ func TestCapacityProvider_GetDeleteUpdateList(t *testing.T) {
require.Equal(t, http.StatusOK, listRec2.Code)
var listOut2 lambda.ListCapacityProvidersOutput
- require.NoError(t, json.NewDecoder(listRec2.Body).Decode(&listOut2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec2.Body), &listOut2, jsonx.V1()))
assert.Empty(t, listOut2.CapacityProviders)
}
@@ -626,7 +628,7 @@ func TestListFunctionVersionsByCapacityProvider(t *testing.T) {
var out struct {
FunctionVersions []any `json:"FunctionVersions"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Empty(t, out.FunctionVersions)
}
})
@@ -667,7 +669,7 @@ func TestCapacityProvider_TelemetryConfig(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createOut lambda.CreateCapacityProviderOutput
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
require.NotNil(t, createOut.CapacityProvider.TelemetryConfig)
require.NotNil(t, createOut.CapacityProvider.TelemetryConfig.LoggingConfig)
assert.Equal(t, "WARN", createOut.CapacityProvider.TelemetryConfig.LoggingConfig.SystemLogLevel)
@@ -678,7 +680,7 @@ func TestCapacityProvider_TelemetryConfig(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateOut lambda.UpdateCapacityProviderOutput
- require.NoError(t, json.NewDecoder(updateRec.Body).Decode(&updateOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updateRec.Body), &updateOut, jsonx.V1()))
require.NotNil(t, updateOut.CapacityProvider.TelemetryConfig)
require.NotNil(t, updateOut.CapacityProvider.TelemetryConfig.LoggingConfig)
assert.Equal(t, "DEBUG", updateOut.CapacityProvider.TelemetryConfig.LoggingConfig.SystemLogLevel)
diff --git a/services/lambda/code_signing_test.go b/services/lambda/code_signing_test.go
index ddef6bae16..e6da21bc3a 100644
--- a/services/lambda/code_signing_test.go
+++ b/services/lambda/code_signing_test.go
@@ -1,7 +1,8 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -48,7 +50,7 @@ func TestCodeSigningConfig_Lifecycle(t *testing.T) {
if tt.wantStatus == http.StatusCreated {
var out lambda.CreateCodeSigningConfigOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotNil(t, out.CodeSigningConfig)
assert.NotEmpty(t, out.CodeSigningConfig.CodeSigningConfigArn)
assert.Equal(t, tt.wantDesc, out.CodeSigningConfig.Description)
@@ -73,7 +75,7 @@ func TestCodeSigningConfig_GetDeleteUpdate(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createOut lambda.CreateCodeSigningConfigOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createOut, jsonx.V1()))
cscARN := createOut.CodeSigningConfig.CodeSigningConfigArn
// Get
@@ -97,7 +99,7 @@ func TestCodeSigningConfig_GetDeleteUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut lambda.ListCodeSigningConfigsOutput
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
assert.Len(t, listOut.CodeSigningConfigs, 1)
// Delete
@@ -128,7 +130,7 @@ func TestFunctionCodeSigningConfig(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createOut lambda.CreateCodeSigningConfigOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createOut, jsonx.V1()))
cscARN := createOut.CodeSigningConfig.CodeSigningConfigArn
// Put function code signing config
@@ -138,7 +140,7 @@ func TestFunctionCodeSigningConfig(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var putOut lambda.PutFunctionCodeSigningConfigOutput
- require.NoError(t, json.NewDecoder(putRec.Body).Decode(&putOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(putRec.Body), &putOut, jsonx.V1()))
assert.Equal(t, cscARN, putOut.CodeSigningConfigArn)
assert.Equal(t, "csc-test-fn", putOut.FunctionName)
@@ -148,7 +150,7 @@ func TestFunctionCodeSigningConfig(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getOut lambda.GetFunctionCodeSigningConfigOutput
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&getOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &getOut, jsonx.V1()))
assert.Equal(t, cscARN, getOut.CodeSigningConfigArn)
// List functions by code signing config
@@ -157,7 +159,7 @@ func TestFunctionCodeSigningConfig(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut lambda.ListFunctionsByCodeSigningConfigOutput
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
assert.NotEmpty(t, listOut.FunctionArns)
// Delete function code signing config
@@ -171,7 +173,7 @@ func TestFunctionCodeSigningConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, getRec2.Code)
var getOut2 lambda.GetFunctionCodeSigningConfigOutput
- require.NoError(t, json.NewDecoder(getRec2.Body).Decode(&getOut2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec2.Body), &getOut2, jsonx.V1()))
assert.Empty(t, getOut2.CodeSigningConfigArn)
}
@@ -234,7 +236,7 @@ func TestCodeSigning(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var cscOut lambda.CreateCodeSigningConfigOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cscOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cscOut, jsonx.V1()))
require.NotNil(t, cscOut.CodeSigningConfig)
cscArn := cscOut.CodeSigningConfig.CodeSigningConfigArn
assert.NotEmpty(t, cscArn)
@@ -251,7 +253,7 @@ func TestCodeSigning(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listOut lambda.ListCodeSigningConfigsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.CodeSigningConfigs, 1)
// UpdateCodeSigningConfig.
@@ -280,7 +282,7 @@ func TestCodeSigning(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getCSCOut lambda.GetFunctionCodeSigningConfigOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getCSCOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getCSCOut, jsonx.V1()))
assert.Equal(t, cscArn, getCSCOut.CodeSigningConfigArn)
assert.Equal(t, "csc-fn", getCSCOut.FunctionName)
diff --git a/services/lambda/concurrency_test.go b/services/lambda/concurrency_test.go
index 4956b11798..c577ac50e9 100644
--- a/services/lambda/concurrency_test.go
+++ b/services/lambda/concurrency_test.go
@@ -1,13 +1,15 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -110,7 +112,7 @@ func TestPutFunctionConcurrency(t *testing.T) {
}
var out lambda.FunctionConcurrency
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantReserved, out.ReservedConcurrentExecutions)
})
}
@@ -199,14 +201,14 @@ func TestGetFunctionConcurrency(t *testing.T) {
if tt.wantAbsent {
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
assert.NotContains(t, raw, "ReservedConcurrentExecutions")
return
}
var out lambda.FunctionConcurrency
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantReserved, out.ReservedConcurrentExecutions)
})
}
@@ -448,7 +450,7 @@ func TestGetFunction_ReservedConcurrentExecutions(t *testing.T) {
var out struct {
Configuration *lambda.FunctionConfiguration `json:"Configuration"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.Configuration)
if tt.wantReserved == nil {
@@ -550,7 +552,7 @@ func TestConcurrency_PutGetDelete(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var conc lambda.FunctionConcurrency
- require.NoError(t, json.NewDecoder(putRec.Body).Decode(&conc))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(putRec.Body), &conc, jsonx.V1()))
assert.Equal(t, 50, conc.ReservedConcurrentExecutions)
// Get
@@ -559,7 +561,7 @@ func TestConcurrency_PutGetDelete(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getConc lambda.FunctionConcurrency
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&getConc))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &getConc, jsonx.V1()))
assert.Equal(t, 50, getConc.ReservedConcurrentExecutions)
// Delete
@@ -575,7 +577,7 @@ func TestConcurrency_PutGetDelete(t *testing.T) {
require.Equal(t, http.StatusOK, getRec2.Code)
var raw map[string]any
- require.NoError(t, json.NewDecoder(getRec2.Body).Decode(&raw))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec2.Body), &raw, jsonx.V1()))
assert.NotContains(t, raw, "ReservedConcurrentExecutions")
}
diff --git a/services/lambda/ddb_esm_test.go b/services/lambda/ddb_esm_test.go
index fd0ec94a3b..9c6921b85f 100644
--- a/services/lambda/ddb_esm_test.go
+++ b/services/lambda/ddb_esm_test.go
@@ -2,13 +2,14 @@ package lambda_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -260,7 +261,7 @@ func TestLambda_DDB_Poller_InvokesLambdaWithRecords(t *testing.T) {
AWSRegion string `json:"awsRegion"`
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(payloads[0], &event))
+ require.NoError(t, json.Unmarshal(payloads[0], &event, jsonx.V1()))
require.Len(t, event.Records, 2)
assert.Equal(t, "event-1", event.Records[0].EventID)
diff --git a/services/lambda/delete_function_cleanup_test.go b/services/lambda/delete_function_cleanup_test.go
index 1edf60ebad..dc79dab4b1 100644
--- a/services/lambda/delete_function_cleanup_test.go
+++ b/services/lambda/delete_function_cleanup_test.go
@@ -1,13 +1,15 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -61,7 +63,7 @@ func TestDeleteFunction_ClearsSideState(t *testing.T) {
require.Equal(t, http.StatusOK, aliasListRec.Code)
var aliasOut lambda.ListAliasesOutput
- require.NoError(t, json.NewDecoder(aliasListRec.Body).Decode(&aliasOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(aliasListRec.Body), &aliasOut, jsonx.V1()))
assert.Empty(t, aliasOut.Aliases,
"recreated function must not inherit the deleted function's aliases")
@@ -70,7 +72,7 @@ func TestDeleteFunction_ClearsSideState(t *testing.T) {
require.Equal(t, http.StatusOK, concGetRec.Code)
var concOut map[string]any
- require.NoError(t, json.NewDecoder(concGetRec.Body).Decode(&concOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(concGetRec.Body), &concOut, jsonx.V1()))
assert.NotContains(t, concOut, "ReservedConcurrentExecutions",
"recreated function must not inherit the deleted function's reserved concurrency")
}
diff --git a/services/lambda/dispatch_test.go b/services/lambda/dispatch_test.go
index be3fc2493b..1f6713c3e2 100644
--- a/services/lambda/dispatch_test.go
+++ b/services/lambda/dispatch_test.go
@@ -1,13 +1,15 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -223,7 +225,7 @@ func TestListFunctionURLConfigs_All(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut lambda.ListFunctionURLConfigsOutput
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
assert.Len(t, listOut.FunctionURLConfigs, 2)
}
@@ -250,7 +252,7 @@ func TestHandlerReset_ClearsState(t *testing.T) {
require.Equal(t, http.StatusOK, listCSC.Code)
var listOut lambda.ListCodeSigningConfigsOutput
- require.NoError(t, json.NewDecoder(listCSC.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listCSC.Body), &listOut, jsonx.V1()))
require.NotEmpty(t, listOut.CodeSigningConfigs)
// Reset
@@ -261,7 +263,7 @@ func TestHandlerReset_ClearsState(t *testing.T) {
require.Equal(t, http.StatusOK, listCSC2.Code)
var listOut2 lambda.ListCodeSigningConfigsOutput
- require.NoError(t, json.NewDecoder(listCSC2.Body).Decode(&listOut2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listCSC2.Body), &listOut2, jsonx.V1()))
assert.Empty(t, listOut2.CodeSigningConfigs)
// Verify capacity providers are cleared
@@ -269,6 +271,6 @@ func TestHandlerReset_ClearsState(t *testing.T) {
require.Equal(t, http.StatusOK, listCP.Code)
var listOutCP lambda.ListCapacityProvidersOutput
- require.NoError(t, json.NewDecoder(listCP.Body).Decode(&listOutCP))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listCP.Body), &listOutCP, jsonx.V1()))
assert.Empty(t, listOutCP.CapacityProviders)
}
diff --git a/services/lambda/durable_execution_test.go b/services/lambda/durable_execution_test.go
index cc2801c685..7954a01139 100644
--- a/services/lambda/durable_execution_test.go
+++ b/services/lambda/durable_execution_test.go
@@ -1,7 +1,7 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/url"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -49,7 +50,7 @@ func TestDurableExecution_GetHistory(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.GetDurableExecutionHistoryOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Events, 3)
assert.Equal(t, "ExecutionStarted", out.Events[0].EventType)
assert.Equal(t, "StepStarted", out.Events[1].EventType)
@@ -81,7 +82,7 @@ func TestDurableExecution_GetState(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.GetDurableExecutionStateOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Operations, 1)
assert.Equal(t, "execution", out.Operations[0].ID)
assert.Equal(t, lambda.DurableOperationTypeExecution, out.Operations[0].Type)
@@ -111,7 +112,7 @@ func TestDurableExecution_Stop(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var stopOut lambda.StopDurableExecutionOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stopOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stopOut, jsonx.V1()))
assert.Positive(t, stopOut.StopTimestamp)
rec = callInMemoryHandler(t, h, http.MethodGet, durableExecURL(""), "{}")
@@ -212,7 +213,7 @@ func TestDurableExecution_ListByFunction(t *testing.T) {
assert.Contains(t, rec.Body.String(), "DurableExecutions")
var out lambda.ListDurableExecutionsByFunctionOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// Neither exec-1 nor exec-2 is associated with "some-func": checkpoint-only
// auto-creation has no function-association entry point (see
// durableExecutionStore's doc comment) — the correctly wired, correctly
@@ -275,7 +276,7 @@ func TestDurableExecution_WireShapeFields(t *testing.T) {
rec := callInMemoryHandler(t, h, http.MethodGet, durableExecURL(""), "{}")
var out lambda.GetDurableExecutionOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, durableExecARN, out.DurableExecutionArn)
assert.Equal(t, "test-exec-1", out.DurableExecutionName)
assert.NotEqual(t, out.DurableExecutionArn, out.DurableExecutionName)
@@ -306,7 +307,7 @@ func TestDurableExecution_WireShapeFields(t *testing.T) {
rec := callInMemoryHandler(t, h, http.MethodGet, durableExecURL("")+"?IncludeExecutionData=false", "{}")
var out lambda.GetDurableExecutionOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.ExecutionDataIncluded)
assert.False(t, *out.ExecutionDataIncluded)
assert.Nil(t, out.InputPayload)
@@ -323,7 +324,7 @@ func TestDurableExecution_WireShapeFields(t *testing.T) {
rec := callInMemoryHandler(t, h, http.MethodGet, durableExecURL(""), "{}")
var out lambda.GetDurableExecutionOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.ExecutionDataIncluded)
assert.True(t, *out.ExecutionDataIncluded)
},
@@ -346,7 +347,7 @@ func TestDurableExecution_WireShapeFields(t *testing.T) {
check: func(t *testing.T, _ *lambda.Handler) {
t.Helper()
- b, err := json.Marshal(lambda.DurableExecutionStatusTimedOut)
+ b, err := json.Marshal(lambda.DurableExecutionStatusTimedOut, jsonx.V1())
require.NoError(t, err)
assert.JSONEq(t, `"TIMED_OUT"`, string(b))
},
@@ -400,7 +401,7 @@ func TestDurableExecution_CheckpointUpdatesOperations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var checkpointOut lambda.CheckpointDurableExecutionOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &checkpointOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &checkpointOut, jsonx.V1()))
require.NotNil(t, checkpointOut.NewExecutionState)
require.Len(t, checkpointOut.NewExecutionState.Operations, 1)
assert.Equal(t, opID, checkpointOut.NewExecutionState.Operations[0].ID)
@@ -411,7 +412,7 @@ func TestDurableExecution_CheckpointUpdatesOperations(t *testing.T) {
histRec := callInMemoryHandler(t, h, http.MethodGet, durableExecURL("/history"), "{}")
var histOut lambda.GetDurableExecutionHistoryOutput
- require.NoError(t, json.Unmarshal(histRec.Body.Bytes(), &histOut))
+ require.NoError(t, json.Unmarshal(histRec.Body.Bytes(), &histOut, jsonx.V1()))
eventTypes := make([]string, 0, len(histOut.Events))
for _, ev := range histOut.Events {
@@ -422,7 +423,7 @@ func TestDurableExecution_CheckpointUpdatesOperations(t *testing.T) {
stateRec := callInMemoryHandler(t, h, http.MethodGet, durableExecURL("/state"), "{}")
var stateOut lambda.GetDurableExecutionStateOutput
- require.NoError(t, json.Unmarshal(stateRec.Body.Bytes(), &stateOut))
+ require.NoError(t, json.Unmarshal(stateRec.Body.Bytes(), &stateOut, jsonx.V1()))
require.Len(t, stateOut.Operations, 2) // implicit root EXECUTION + this one
var found bool
@@ -506,7 +507,7 @@ func TestDurableExecution_ListByFunctionQueryParams(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.ListDurableExecutionsByFunctionOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
})
}
}
@@ -562,7 +563,7 @@ func TestDurableExecution_StopVariants(t *testing.T) {
if tt.name == "with_custom_error" {
var out lambda.GetDurableExecutionOutput
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.Error)
require.NotNil(t, out.Error.ErrorMessage)
assert.Equal(t, "cancelled by operator", *out.Error.ErrorMessage)
diff --git a/services/lambda/esm_config_test.go b/services/lambda/esm_config_test.go
index a36ae4362f..419d6edf70 100644
--- a/services/lambda/esm_config_test.go
+++ b/services/lambda/esm_config_test.go
@@ -1,7 +1,8 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/url"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -33,7 +35,7 @@ func TestESM_HTTPLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
uuid := created["UUID"].(string)
// List by function name
@@ -78,7 +80,7 @@ func TestESM_BatchWindow(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.InDelta(t, float64(60), out["MaximumBatchingWindowInSeconds"], 0)
}
@@ -98,7 +100,7 @@ func TestESM_TumblingWindow(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.InDelta(t, float64(300), out["TumblingWindowInSeconds"], 0)
}
@@ -117,7 +119,7 @@ func TestESM_FilterCriteria(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
fc, ok := out["FilterCriteria"].(map[string]any)
require.True(t, ok)
filters := fc["Filters"].([]any)
@@ -140,7 +142,7 @@ func TestESM_BisectBatchOnError(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, true, out["BisectBatchOnFunctionError"])
}
@@ -160,7 +162,7 @@ func TestESM_MaximumRecordAge(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.InDelta(t, float64(3600), out["MaximumRecordAgeInSeconds"], 0)
}
@@ -180,7 +182,7 @@ func TestESM_ParallelizationFactor(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.InDelta(t, float64(5), out["ParallelizationFactor"], 0)
}
@@ -200,7 +202,7 @@ func TestESM_DestinationConfig(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
dc, ok := out["DestinationConfig"].(map[string]any)
require.True(t, ok)
onFail := dc["OnFailure"].(map[string]any)
@@ -222,7 +224,7 @@ func TestESM_UpdateBatchWindow(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&created))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &created, jsonx.V1()))
uuid := created["UUID"].(string)
updRec := callInMemoryHandler(t, h, http.MethodPut,
@@ -231,7 +233,7 @@ func TestESM_UpdateBatchWindow(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var updated map[string]any
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&updated))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &updated, jsonx.V1()))
assert.InDelta(t, float64(30), updated["MaximumBatchingWindowInSeconds"], 0)
}
@@ -253,7 +255,7 @@ func TestESM_SQS_FullOptions(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.InDelta(t, float64(10), out["BatchSize"], 0)
assert.InDelta(t, float64(5), out["MaximumBatchingWindowInSeconds"], 0)
assert.NotNil(t, out["FilterCriteria"])
@@ -279,7 +281,7 @@ func TestESM_DDB_FullOptions(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.InDelta(t, float64(50), out["BatchSize"], 0)
assert.Equal(t, true, out["BisectBatchOnFunctionError"])
assert.InDelta(t, float64(3), out["MaximumRetryAttempts"], 0)
@@ -307,7 +309,7 @@ func TestESM_Kinesis_AllWindowOptions(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.InDelta(t, float64(200), out["BatchSize"], 0)
assert.InDelta(t, float64(120), out["MaximumBatchingWindowInSeconds"], 0)
assert.InDelta(t, float64(60), out["TumblingWindowInSeconds"], 0)
@@ -336,7 +338,7 @@ func TestESM_ListByFunction(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
mappings := out["EventSourceMappings"].([]any)
assert.Len(t, mappings, 3)
}
@@ -353,7 +355,7 @@ func TestESM_EnableDisable(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&created))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &created, jsonx.V1()))
uuid := created["UUID"].(string)
// Disable
@@ -365,7 +367,7 @@ func TestESM_EnableDisable(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var updOut map[string]any
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&updOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &updOut, jsonx.V1()))
assert.Equal(t, "Disabled", updOut["State"])
// Re-enable
@@ -375,7 +377,7 @@ func TestESM_EnableDisable(t *testing.T) {
require.Equal(t, http.StatusOK, enableRec.Code)
var enableOut map[string]any
- require.NoError(t, json.NewDecoder(enableRec.Body).Decode(&enableOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(enableRec.Body), &enableOut, jsonx.V1()))
assert.Equal(t, "Enabled", enableOut["State"])
}
@@ -391,7 +393,7 @@ func TestESM_DeleteReturnsMapping(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&created))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &created, jsonx.V1()))
uuid := created["UUID"].(string)
delRec := callInMemoryHandler(t, h, http.MethodDelete,
@@ -399,7 +401,7 @@ func TestESM_DeleteReturnsMapping(t *testing.T) {
require.Equal(t, http.StatusOK, delRec.Code)
var delOut map[string]any
- require.NoError(t, json.NewDecoder(delRec.Body).Decode(&delOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(delRec.Body), &delOut, jsonx.V1()))
assert.Equal(t, uuid, delOut["UUID"])
// Get after delete → 404
@@ -425,7 +427,7 @@ func TestESM_MSK_Topics_And_ConsumerGroup(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
topics, _ := out["Topics"].([]any)
require.Len(t, topics, 1)
@@ -452,7 +454,7 @@ func TestESM_MSK_Get_PreservesConfig(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&created))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &created, jsonx.V1()))
id, _ := created["UUID"].(string)
require.NotEmpty(t, id)
@@ -460,7 +462,7 @@ func TestESM_MSK_Get_PreservesConfig(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var got map[string]any
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&got))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &got, jsonx.V1()))
topics, _ := got["Topics"].([]any)
require.Len(t, topics, 1)
@@ -494,7 +496,7 @@ func TestESM_SelfManagedKafka_Create(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
topics, _ := out["Topics"].([]any)
require.Len(t, topics, 1)
@@ -533,7 +535,7 @@ func TestESM_SelfManagedKafka_UpdateSourceAccessConfigs(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&created))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &created, jsonx.V1()))
id := created["UUID"].(string)
updateBody := `{
@@ -546,7 +548,7 @@ func TestESM_SelfManagedKafka_UpdateSourceAccessConfigs(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(updateRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updateRec.Body), &out, jsonx.V1()))
sacs, _ := out["SourceAccessConfigurations"].([]any)
assert.Len(t, sacs, 2)
@@ -570,7 +572,7 @@ func TestESM_AmazonMQ_Queues(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
queues, _ := out["Queues"].([]any)
require.Len(t, queues, 1)
@@ -600,7 +602,7 @@ func TestESM_DocumentDB_Create(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
docdb, _ := out["DocumentDBEventSourceConfig"].(map[string]any)
require.NotNil(t, docdb)
@@ -627,14 +629,14 @@ func TestESM_DocumentDB_Get_PreservesConfig(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&created))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &created, jsonx.V1()))
id := created["UUID"].(string)
getRec := callInMemoryHandler(t, h, http.MethodGet, "/2015-03-31/event-source-mappings/"+id, "")
require.Equal(t, http.StatusOK, getRec.Code)
var got map[string]any
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&got))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &got, jsonx.V1()))
docdb, _ := got["DocumentDBEventSourceConfig"].(map[string]any)
require.NotNil(t, docdb)
@@ -657,7 +659,7 @@ func TestESM_DocumentDB_NoCollectionName_Omitted(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
docdb, _ := out["DocumentDBEventSourceConfig"].(map[string]any)
require.NotNil(t, docdb)
@@ -713,7 +715,7 @@ func TestSourceAccessConfiguration_TypeAndURI(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
sacs, _ := out["SourceAccessConfigurations"].([]any)
require.Len(t, sacs, 1)
@@ -779,7 +781,7 @@ func TestListESM_EventSourceArnFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
esms, _ := out["EventSourceMappings"].([]any)
assert.Len(t, esms, tc.wantCount,
"EventSourceMappings count mismatch for filter=%q", tc.filter)
diff --git a/services/lambda/esm_failure.go b/services/lambda/esm_failure.go
index 4a944f7fba..c1f9f0e447 100644
--- a/services/lambda/esm_failure.go
+++ b/services/lambda/esm_failure.go
@@ -2,7 +2,7 @@ package lambda
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"slices"
@@ -12,6 +12,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -211,7 +212,7 @@ func logDeliveryFailure(ctx context.Context, uuidStr, dest string, err error) {
}
func marshalFailure(rec failureRecord) ([]byte, error) {
- body, err := json.Marshal(rec)
+ body, err := json.Marshal(rec, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("marshal failure record: %w", err)
}
diff --git a/services/lambda/esm_failure_internal_test.go b/services/lambda/esm_failure_internal_test.go
index 5ed0c92b51..c31639d2c3 100644
--- a/services/lambda/esm_failure_internal_test.go
+++ b/services/lambda/esm_failure_internal_test.go
@@ -2,7 +2,8 @@ package lambda
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strconv"
"strings"
"sync"
@@ -12,6 +13,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type deliveredMsg struct {
@@ -130,10 +133,10 @@ func newFailureHarness(
defer mu.Unlock()
var ev struct {
- Records []json.RawMessage `json:"Records"`
+ Records []jsontext.Value `json:"Records"`
}
- require.NoError(t, json.Unmarshal(payload, &ev))
+ require.NoError(t, json.Unmarshal(payload, &ev, jsonx.V1()))
*h.calls = append(*h.calls, len(ev.Records))
if failOn(string(payload)) {
@@ -321,7 +324,7 @@ func TestStreamRetryAndOnFailureDestination(t *testing.T) {
} `json:"requestContext"`
}
- require.NoError(t, json.Unmarshal(body, &rec))
+ require.NoError(t, json.Unmarshal(body, &rec, jsonx.V1()))
assert.Equal(t, "RetryAttemptsExhausted", rec.RequestContext.Condition)
assert.Equal(t, testFnARN, rec.RequestContext.FunctionArn)
assert.Equal(t, tt.wantInvokeCnt, rec.RequestContext.Count)
@@ -470,7 +473,7 @@ func TestKafkaRetryExhaustionDestinations(t *testing.T) {
} `json:"KafkaBatchInfo"`
}
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
assert.Equal(t, "RetryAttemptsExhausted", out.RequestContext.Condition)
assert.Equal(t, "0", out.KafkaBatchInfo.RecordInfo.Offset)
assert.Equal(t, 1, out.KafkaBatchInfo.BatchSize)
@@ -642,7 +645,7 @@ func TestESMSystemLogLevels(t *testing.T) {
Processor string `json:"eventProcessorId"`
}
- require.NoError(t, json.Unmarshal([]byte(line), &rec))
+ require.NoError(t, json.Unmarshal([]byte(line), &rec, jsonx.V1()))
assert.Equal(t, "u-3/0", rec.Processor)
got = append(got, rec.EventType)
@@ -781,7 +784,7 @@ func TestDynamoDBStreamOnFailureDestination(t *testing.T) {
} `json:"requestContext"`
}
- require.NoError(t, json.Unmarshal(del.msgs[0].body, &out))
+ require.NoError(t, json.Unmarshal(del.msgs[0].body, &out, jsonx.V1()))
assert.Equal(t, "800000000003126276362", out.Info.Start)
assert.Equal(t, "2019-11-14T00:13:19Z", out.Info.First)
assert.Equal(t, streamARN, out.Info.StreamArn)
@@ -807,7 +810,7 @@ func TestESMMaximumRetryAttemptsWire(t *testing.T) {
t.Parallel()
m := &EventSourceMapping{FunctionARN: testFnARN, MaximumRetryAttempts: tt.in}
- body, err := json.Marshal(toJSONESMResponse(m))
+ body, err := json.Marshal(toJSONESMResponse(m), jsonx.V1())
require.NoError(t, err)
if tt.want == "" {
diff --git a/services/lambda/esm_kafka_failure.go b/services/lambda/esm_kafka_failure.go
index 7ce353dd11..4cf13e0a6e 100644
--- a/services/lambda/esm_kafka_failure.go
+++ b/services/lambda/esm_kafka_failure.go
@@ -2,7 +2,7 @@ package lambda
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
"strconv"
@@ -117,7 +117,7 @@ func (p *EventSourcePoller) deliverKafkaFailure(
switch {
case strings.HasPrefix(dest, kafkaDestinationPrefix):
- rec.Payload = json.RawMessage(event)
+ rec.Payload = jsontext.Value(event)
body, mErr := marshalFailure(rec)
if mErr != nil {
return mErr
diff --git a/services/lambda/esm_logging.go b/services/lambda/esm_logging.go
index 8bec9b6bab..95a45512ef 100644
--- a/services/lambda/esm_logging.go
+++ b/services/lambda/esm_logging.go
@@ -2,11 +2,12 @@ package lambda
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"maps"
"strconv"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -97,7 +98,7 @@ func (l esmLogger) emit(ctx context.Context, at, eventType string, extra map[str
maps.Copy(rec, extra)
- body, err := json.Marshal(rec)
+ body, err := json.Marshal(rec, jsonx.V1())
if err != nil {
return
}
diff --git a/services/lambda/esm_partial_batch_test.go b/services/lambda/esm_partial_batch_test.go
index 446428065b..032eab635f 100644
--- a/services/lambda/esm_partial_batch_test.go
+++ b/services/lambda/esm_partial_batch_test.go
@@ -2,13 +2,14 @@ package lambda_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -204,7 +205,7 @@ func TestLambda_ESM_FunctionResponseTypes_StoredAndRetrieved(t *testing.T) {
}
func mustMarshal(v any) []byte {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
panic(err)
}
diff --git a/services/lambda/esm_stream_retry.go b/services/lambda/esm_stream_retry.go
index 8330cc8f28..9b8a38ae00 100644
--- a/services/lambda/esm_stream_retry.go
+++ b/services/lambda/esm_stream_retry.go
@@ -3,13 +3,15 @@ package lambda
import (
"cmp"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"slices"
"strings"
"time"
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -18,7 +20,7 @@ type streamEvent struct {
arrival time.Time
seq string
key string
- raw json.RawMessage
+ raw jsontext.Value
}
// streamBatch is a batch awaiting a successful invocation.
@@ -39,14 +41,14 @@ type streamShardRef struct {
}
func streamEventPayload(events []streamEvent) ([]byte, error) {
- raws := make([]json.RawMessage, len(events))
+ raws := make([]jsontext.Value, len(events))
for i, e := range events {
raws[i] = e.raw
}
return json.Marshal(struct {
- Records []json.RawMessage `json:"Records"`
- }{Records: raws})
+ Records []jsontext.Value `json:"Records"`
+ }{Records: raws}, jsonx.V1())
}
func (p *EventSourcePoller) frontStreamBatch(key string) *streamBatch {
diff --git a/services/lambda/esm_stream_window.go b/services/lambda/esm_stream_window.go
index 13374b33d2..0c3531d931 100644
--- a/services/lambda/esm_stream_window.go
+++ b/services/lambda/esm_stream_window.go
@@ -3,7 +3,8 @@ package lambda
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"hash/fnv"
"math"
@@ -12,6 +13,8 @@ import (
"strings"
"sync"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -30,7 +33,7 @@ var (
type tumblingWindow struct {
start time.Time
end time.Time
- state json.RawMessage
+ state jsontext.Value
finalQueued bool
}
@@ -289,13 +292,13 @@ type windowRange struct {
}
type windowedPayload struct {
- State json.RawMessage `json:"state"`
- Window windowRange `json:"window"`
- ShardID string `json:"shardId"`
- EventSourceARN string `json:"eventSourceARN"`
- Records []json.RawMessage `json:"Records"`
- IsFinalInvokeForWindow bool `json:"isFinalInvokeForWindow"`
- IsWindowTerminatedEarly bool `json:"isWindowTerminatedEarly"`
+ State jsontext.Value `json:"state"`
+ Window windowRange `json:"window"`
+ ShardID string `json:"shardId"`
+ EventSourceARN string `json:"eventSourceARN"`
+ Records []jsontext.Value `json:"Records"`
+ IsFinalInvokeForWindow bool `json:"isFinalInvokeForWindow"`
+ IsWindowTerminatedEarly bool `json:"isWindowTerminatedEarly"`
}
func (p *EventSourcePoller) streamBatchPayload(
@@ -310,10 +313,10 @@ func (p *EventSourcePoller) streamBatchPayload(
p.mu.RUnlock()
if len(state) == 0 {
- state = json.RawMessage(emptyJSONObject)
+ state = jsontext.Value(emptyJSONObject)
}
- raws := make([]json.RawMessage, len(b.events))
+ raws := make([]jsontext.Value, len(b.events))
for i, e := range b.events {
raws[i] = e.raw
}
@@ -329,21 +332,21 @@ func (p *EventSourcePoller) streamBatchPayload(
EventSourceARN: m.EventSourceARN,
IsFinalInvokeForWindow: b.final,
IsWindowTerminatedEarly: b.early,
- })
+ }, jsonx.V1())
}
// windowResponseState extracts the required state property of a tumbling-window response.
-func windowResponseState(body []byte) (json.RawMessage, bool) {
+func windowResponseState(body []byte) (jsontext.Value, bool) {
var resp struct {
- State *json.RawMessage `json:"state"`
+ State *jsontext.Value `json:"state"`
}
- if err := json.Unmarshal(body, &resp); err != nil || resp.State == nil {
+ if err := json.Unmarshal(body, &resp, jsonx.V1()); err != nil || resp.State == nil {
return nil, false
}
if bytes.Equal(bytes.TrimSpace(*resp.State), []byte("null")) {
- return json.RawMessage(emptyJSONObject), true
+ return jsontext.Value(emptyJSONObject), true
}
return *resp.State, true
@@ -358,7 +361,8 @@ func batchFailureIndex(body []byte, events []streamEvent) int {
} `json:"batchItemFailures"`
}
- if len(bytes.TrimSpace(body)) == 0 || json.Unmarshal(body, &resp) != nil || len(resp.BatchItemFailures) == 0 {
+ if len(bytes.TrimSpace(body)) == 0 || json.Unmarshal(body, &resp, jsonx.V1()) != nil ||
+ len(resp.BatchItemFailures) == 0 {
return -1
}
diff --git a/services/lambda/esm_stream_window_internal_test.go b/services/lambda/esm_stream_window_internal_test.go
index cec3b22455..cc816d27b3 100644
--- a/services/lambda/esm_stream_window_internal_test.go
+++ b/services/lambda/esm_stream_window_internal_test.go
@@ -2,7 +2,8 @@ package lambda
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strconv"
"strings"
"sync"
@@ -11,6 +12,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type scriptedKinesisReader struct {
@@ -39,13 +42,13 @@ func (r *scriptedKinesisReader) GetRecords(string, int) ([]KinesisRecord, string
}
type streamCall struct {
- Window *windowRange `json:"window"`
- State map[string]any `json:"state"`
- ShardID string `json:"shardId"`
- EventSourceARN string `json:"eventSourceARN"`
- Records []json.RawMessage `json:"Records"`
- IsFinalInvokeForWindow bool `json:"isFinalInvokeForWindow"`
- IsWindowTerminatedEarly bool `json:"isWindowTerminatedEarly"`
+ Window *windowRange `json:"window"`
+ State map[string]any `json:"state"`
+ ShardID string `json:"shardId"`
+ EventSourceARN string `json:"eventSourceARN"`
+ Records []jsontext.Value `json:"Records"`
+ IsFinalInvokeForWindow bool `json:"isFinalInvokeForWindow"`
+ IsWindowTerminatedEarly bool `json:"isWindowTerminatedEarly"`
}
func (c streamCall) seqs(t *testing.T) []string {
@@ -60,7 +63,7 @@ func (c streamCall) seqs(t *testing.T) []string {
} `json:"kinesis"`
}
- require.NoError(t, json.Unmarshal(r, &rec))
+ require.NoError(t, json.Unmarshal(r, &rec, jsonx.V1()))
out = append(out, rec.Kinesis.Seq)
}
@@ -104,7 +107,7 @@ func newStreamHarness(
h.p.streamInvoker = func(_ context.Context, _ string, payload []byte) invokeOutcome {
var c streamCall
- require.NoError(t, json.Unmarshal(payload, &c))
+ require.NoError(t, json.Unmarshal(payload, &c, jsonx.V1()))
h.mu.Lock()
h.calls = append(h.calls, c)
@@ -229,7 +232,7 @@ func TestStreamParallelizationFactor(t *testing.T) {
} `json:"kinesis"`
}
- require.NoError(t, json.Unmarshal(r, &rec))
+ require.NoError(t, json.Unmarshal(r, &rec, jsonx.V1()))
l := laneIndex(rec.Kinesis.PK, tt.factor)
if laneOf >= 0 {
@@ -360,7 +363,7 @@ func TestStreamTumblingWindow(t *testing.T) {
assert.Equal(t, testKinesisARN, c.EventSourceARN)
require.NotNil(t, c.Window)
- state, err := json.Marshal(c.State)
+ state, err := json.Marshal(c.State, jsonx.V1())
require.NoError(t, err)
assert.JSONEq(t, w.state, string(state), "call %d", i)
}
@@ -676,7 +679,7 @@ func TestStreamDynamoDBParallelizationKeepsItemOrder(t *testing.T) {
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(payload, &ev))
+ require.NoError(t, json.Unmarshal(payload, &ev, jsonx.V1()))
got := make([]string, 0, len(ev.Records))
for _, r := range ev.Records {
diff --git a/services/lambda/esm_test.go b/services/lambda/esm_test.go
index 13062ac39a..ffa10c8e5e 100644
--- a/services/lambda/esm_test.go
+++ b/services/lambda/esm_test.go
@@ -2,7 +2,7 @@ package lambda_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -37,7 +38,7 @@ func doESMRequest(t *testing.T, h *lambda.Handler, method, path string, body any
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -87,7 +88,7 @@ func TestLambda_ESM_FilterCriteria_RoundTrip(t *testing.T) {
} `json:"Filters"`
} `json:"FilterCriteria"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
require.NotEmpty(t, createResp.UUID)
require.Len(t, createResp.FilterCriteria.Filters, 1)
assert.JSONEq(t, `{"data":{"orderType":["premium"]}}`, createResp.FilterCriteria.Filters[0].Pattern)
@@ -113,7 +114,7 @@ func TestLambda_ESM_FilterCriteria_RoundTrip(t *testing.T) {
} `json:"Filters"`
} `json:"FilterCriteria"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
require.Len(t, getResp.FilterCriteria.Filters, 1)
assert.JSONEq(t, `{"data":{"orderType":["standard"]}}`, getResp.FilterCriteria.Filters[0].Pattern)
}
@@ -144,7 +145,7 @@ func TestLambda_ESM_CRUD(t *testing.T) {
State string `json:"State"`
BatchSize int `json:"BatchSize"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.NotEmpty(t, createResp.UUID)
assert.Equal(t, streamARN, createResp.EventSourceARN)
assert.Equal(t, "Enabled", createResp.State)
@@ -160,7 +161,7 @@ func TestLambda_ESM_CRUD(t *testing.T) {
UUID string `json:"UUID"`
State string `json:"State"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, esmUUID, getResp.UUID)
// ListEventSourceMappings
@@ -172,7 +173,7 @@ func TestLambda_ESM_CRUD(t *testing.T) {
UUID string `json:"UUID"`
} `json:"EventSourceMappings"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.EventSourceMappings, 1)
assert.Equal(t, esmUUID, listResp.EventSourceMappings[0].UUID)
@@ -204,7 +205,7 @@ func TestLambda_ESM_CreateDisabled(t *testing.T) {
var resp struct {
State string `json:"State"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "Disabled", resp.State)
}
@@ -226,7 +227,7 @@ func TestLambda_ESM_CreateNilEnabled(t *testing.T) {
var resp struct {
State string `json:"State"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "Enabled", resp.State)
}
@@ -256,7 +257,7 @@ func TestLambda_ESM_ListByFunctionName(t *testing.T) {
var allResp struct {
EventSourceMappings []any `json:"EventSourceMappings"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &allResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &allResp, jsonx.V1()))
assert.Len(t, allResp.EventSourceMappings, 2)
// List for function-a only
@@ -268,7 +269,7 @@ func TestLambda_ESM_ListByFunctionName(t *testing.T) {
FunctionARN string `json:"FunctionArn"`
} `json:"EventSourceMappings"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &filtResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &filtResp, jsonx.V1()))
assert.Len(t, filtResp.EventSourceMappings, 1)
assert.Contains(t, filtResp.EventSourceMappings[0].FunctionARN, "function-a")
}
diff --git a/services/lambda/esm_tuning_validation_test.go b/services/lambda/esm_tuning_validation_test.go
index 408e4ce3f2..723acdf96b 100644
--- a/services/lambda/esm_tuning_validation_test.go
+++ b/services/lambda/esm_tuning_validation_test.go
@@ -1,12 +1,14 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestESM_TuningValidation(t *testing.T) {
@@ -124,7 +126,7 @@ func TestESM_UpdateTuningValidation(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
uuid, _ := created["UUID"].(string)
diff --git a/services/lambda/event_filter.go b/services/lambda/event_filter.go
index 063b34bf40..9b5927c447 100644
--- a/services/lambda/event_filter.go
+++ b/services/lambda/event_filter.go
@@ -2,10 +2,13 @@ package lambda
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"math"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// eventFilterMatches reports whether the given event source record satisfies the
@@ -29,8 +32,8 @@ func eventFilterMatches(fc *FilterCriteria, record map[string]any) bool {
continue
}
- var pattern map[string]json.RawMessage
- if err := json.Unmarshal([]byte(f.Pattern), &pattern); err != nil {
+ var pattern map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(f.Pattern), &pattern, jsonx.V1()); err != nil {
continue
}
@@ -47,7 +50,7 @@ func eventFilterMatches(fc *FilterCriteria, record map[string]any) bool {
// the "$or" combinator documented for "Or (multiple fields)": its value is an
// array of sibling pattern fragments evaluated against the same value, and the
// object matches only if at least one fragment does.
-func patternMatchesObject(pattern map[string]json.RawMessage, value map[string]any) bool {
+func patternMatchesObject(pattern map[string]jsontext.Value, value map[string]any) bool {
for key, rawRule := range pattern {
if key == "$or" {
if !orClauseMatches(rawRule, value) {
@@ -69,15 +72,15 @@ func patternMatchesObject(pattern map[string]json.RawMessage, value map[string]a
// orClauseMatches evaluates a "$or" array of sibling pattern-fragment objects
// against value, matching if any fragment matches.
-func orClauseMatches(rawRule json.RawMessage, value map[string]any) bool {
- var branches []json.RawMessage
- if err := json.Unmarshal(rawRule, &branches); err != nil {
+func orClauseMatches(rawRule jsontext.Value, value map[string]any) bool {
+ var branches []jsontext.Value
+ if err := json.Unmarshal(rawRule, &branches, jsonx.V1()); err != nil {
return false
}
for _, branch := range branches {
- var branchPattern map[string]json.RawMessage
- if err := json.Unmarshal(branch, &branchPattern); err != nil {
+ var branchPattern map[string]jsontext.Value
+ if err := json.Unmarshal(branch, &branchPattern, jsonx.V1()); err != nil {
continue
}
@@ -91,7 +94,7 @@ func orClauseMatches(rawRule json.RawMessage, value map[string]any) bool {
// fieldMatchesRule evaluates a single pattern field. rawRule is either a nested
// pattern object (recurse) or a JSON array of match rules (any of which may match).
-func fieldMatchesRule(rawRule json.RawMessage, fieldVal any, present bool) bool {
+func fieldMatchesRule(rawRule jsontext.Value, fieldVal any, present bool) bool {
trimmed := strings.TrimSpace(string(rawRule))
// Nested object pattern: recurse into the sub-document.
@@ -101,8 +104,8 @@ func fieldMatchesRule(rawRule json.RawMessage, fieldVal any, present bool) bool
return false
}
- var subPattern map[string]json.RawMessage
- if err := json.Unmarshal(rawRule, &subPattern); err != nil {
+ var subPattern map[string]jsontext.Value
+ if err := json.Unmarshal(rawRule, &subPattern, jsonx.V1()); err != nil {
return false
}
@@ -110,8 +113,8 @@ func fieldMatchesRule(rawRule json.RawMessage, fieldVal any, present bool) bool
}
// Rule array: the field matches if any element in the array matches.
- var rules []json.RawMessage
- if err := json.Unmarshal(rawRule, &rules); err != nil {
+ var rules []jsontext.Value
+ if err := json.Unmarshal(rawRule, &rules, jsonx.V1()); err != nil {
return false
}
@@ -126,7 +129,7 @@ func fieldMatchesRule(rawRule json.RawMessage, fieldVal any, present bool) bool
// ruleMatchesValue evaluates one rule element against the field value. A rule is
// either a scalar (exact match) or an operator object like {"prefix":"x"}.
-func ruleMatchesValue(rule json.RawMessage, fieldVal any, present bool) bool {
+func ruleMatchesValue(rule jsontext.Value, fieldVal any, present bool) bool {
trimmed := strings.TrimSpace(string(rule))
if strings.HasPrefix(trimmed, "{") {
@@ -137,13 +140,13 @@ func ruleMatchesValue(rule json.RawMessage, fieldVal any, present bool) bool {
}
// scalarMatches performs an exact comparison between a scalar rule and the field.
-func scalarMatches(rule json.RawMessage, fieldVal any, present bool) bool {
+func scalarMatches(rule jsontext.Value, fieldVal any, present bool) bool {
if !present {
return false
}
var ruleVal any
- if err := json.Unmarshal(rule, &ruleVal); err != nil {
+ if err := json.Unmarshal(rule, &ruleVal, jsonx.V1()); err != nil {
return false
}
@@ -169,9 +172,9 @@ func scalarMatches(rule json.RawMessage, fieldVal any, present bool) bool {
// operatorMatches evaluates an operator rule object such as {"prefix":"x"},
// {"exists":true}, {"anything-but":[...]}, or {"numeric":[">",5]}.
-func operatorMatches(rule json.RawMessage, fieldVal any, present bool) bool {
- var op map[string]json.RawMessage
- if err := json.Unmarshal(rule, &op); err != nil {
+func operatorMatches(rule jsontext.Value, fieldVal any, present bool) bool {
+ var op map[string]jsontext.Value
+ if err := json.Unmarshal(rule, &op, jsonx.V1()); err != nil {
return false
}
@@ -185,7 +188,7 @@ func operatorMatches(rule json.RawMessage, fieldVal any, present bool) bool {
}
// singleOperatorMatches dispatches one named operator.
-func singleOperatorMatches(name string, arg json.RawMessage, fieldVal any, present bool) bool {
+func singleOperatorMatches(name string, arg jsontext.Value, fieldVal any, present bool) bool {
switch name {
case "exists":
return existsMatches(arg, fieldVal, present)
@@ -209,9 +212,9 @@ func singleOperatorMatches(name string, arg json.RawMessage, fieldVal any, prese
// match intermediate nodes" -- a field present as a nested object is an
// intermediate node, not a leaf, so it never satisfies exists regardless of
// the requested polarity.
-func existsMatches(arg json.RawMessage, fieldVal any, present bool) bool {
+func existsMatches(arg jsontext.Value, fieldVal any, present bool) bool {
var want bool
- if err := json.Unmarshal(arg, &want); err != nil {
+ if err := json.Unmarshal(arg, &want, jsonx.V1()); err != nil {
return false
}
@@ -222,13 +225,13 @@ func existsMatches(arg json.RawMessage, fieldVal any, present bool) bool {
return want == present
}
-func affixMatches(arg json.RawMessage, fieldVal any, present bool, fn func(string, string) bool) bool {
+func affixMatches(arg jsontext.Value, fieldVal any, present bool, fn func(string, string) bool) bool {
if !present {
return false
}
var want string
- if err := json.Unmarshal(arg, &want); err != nil {
+ if err := json.Unmarshal(arg, &want, jsonx.V1()); err != nil {
return false
}
@@ -237,13 +240,13 @@ func affixMatches(arg json.RawMessage, fieldVal any, present bool, fn func(strin
return ok && fn(fv, want)
}
-func equalsIgnoreCaseMatches(arg json.RawMessage, fieldVal any, present bool) bool {
+func equalsIgnoreCaseMatches(arg jsontext.Value, fieldVal any, present bool) bool {
if !present {
return false
}
var want string
- if err := json.Unmarshal(arg, &want); err != nil {
+ if err := json.Unmarshal(arg, &want, jsonx.V1()); err != nil {
return false
}
@@ -254,7 +257,7 @@ func equalsIgnoreCaseMatches(arg json.RawMessage, fieldVal any, present bool) bo
// anythingButMatches matches when the field is present and NOT equal to any of
// the listed values. The argument may be a scalar or an array of scalars.
-func anythingButMatches(arg json.RawMessage, fieldVal any, present bool) bool {
+func anythingButMatches(arg jsontext.Value, fieldVal any, present bool) bool {
if !present {
return false
}
@@ -264,8 +267,8 @@ func anythingButMatches(arg json.RawMessage, fieldVal any, present bool) bool {
return !scalarMatches(arg, fieldVal, present)
}
- var values []json.RawMessage
- if err := json.Unmarshal(arg, &values); err != nil {
+ var values []jsontext.Value
+ if err := json.Unmarshal(arg, &values, jsonx.V1()); err != nil {
return false
}
@@ -279,7 +282,7 @@ func anythingButMatches(arg json.RawMessage, fieldVal any, present bool) bool {
}
// numericMatches evaluates a numeric rule like ["=",1] or [">",0,"<=",10].
-func numericMatches(arg json.RawMessage, fieldVal any, present bool) bool {
+func numericMatches(arg jsontext.Value, fieldVal any, present bool) bool {
if !present {
return false
}
@@ -289,19 +292,19 @@ func numericMatches(arg json.RawMessage, fieldVal any, present bool) bool {
return false
}
- var tokens []json.RawMessage
- if err := json.Unmarshal(arg, &tokens); err != nil {
+ var tokens []jsontext.Value
+ if err := json.Unmarshal(arg, &tokens, jsonx.V1()); err != nil {
return false
}
for i := 0; i+1 < len(tokens); i += 2 {
var operator string
- if err := json.Unmarshal(tokens[i], &operator); err != nil {
+ if err := json.Unmarshal(tokens[i], &operator, jsonx.V1()); err != nil {
return false
}
var operand float64
- if err := json.Unmarshal(tokens[i+1], &operand); err != nil {
+ if err := json.Unmarshal(tokens[i+1], &operand, jsonx.V1()); err != nil {
return false
}
@@ -339,7 +342,7 @@ func toFloat(v any) (float64, bool) {
switch n := v.(type) {
case float64:
return n, true
- case json.Number:
+ case jsonx.Number:
f, err := n.Float64()
return f, err == nil
@@ -440,7 +443,7 @@ func parseJSONObjectBytes(b []byte) (map[string]any, bool) {
}
var obj map[string]any
- if err := json.Unmarshal(b, &obj); err != nil {
+ if err := json.Unmarshal(b, &obj, jsonx.V1()); err != nil {
return nil, false
}
diff --git a/services/lambda/event_invoke_config_test.go b/services/lambda/event_invoke_config_test.go
index 2f669779b9..6f96052867 100644
--- a/services/lambda/event_invoke_config_test.go
+++ b/services/lambda/event_invoke_config_test.go
@@ -1,7 +1,8 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -150,7 +152,7 @@ func TestPutFunctionEventInvokeConfig(t *testing.T) {
if tt.wantRetries != nil || tt.wantAge != nil {
var cfg lambda.FunctionEventInvokeConfig
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cfg))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cfg, jsonx.V1()))
if tt.wantRetries != nil {
require.NotNil(t, cfg.MaximumRetryAttempts)
@@ -186,7 +188,7 @@ func TestFunctionEventInvokeConfig_LastModifiedIsEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
lastModified, ok := raw["LastModified"]
require.True(t, ok, "response must include LastModified")
@@ -358,7 +360,7 @@ func TestUpdateFunctionEventInvokeConfig(t *testing.T) {
if tt.wantRetries != nil {
var cfg lambda.FunctionEventInvokeConfig
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cfg))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cfg, jsonx.V1()))
require.NotNil(t, cfg.MaximumRetryAttempts)
assert.Equal(t, *tt.wantRetries, *cfg.MaximumRetryAttempts)
}
@@ -518,7 +520,7 @@ func TestListFunctionEventInvokeConfigs(t *testing.T) {
}
var out lambda.ListFunctionEventInvokeConfigsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.FunctionEventInvokeConfigs, tt.wantCount)
})
}
@@ -709,7 +711,7 @@ func TestEventInvokeConfig_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var cfg lambda.FunctionEventInvokeConfig
- require.NoError(t, json.NewDecoder(putRec.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(putRec.Body), &cfg, jsonx.V1()))
require.NotNil(t, cfg.MaximumRetryAttempts)
assert.Equal(t, maxRetry, *cfg.MaximumRetryAttempts)
require.NotNil(t, cfg.MaximumEventAgeInSeconds)
@@ -721,7 +723,7 @@ func TestEventInvokeConfig_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getCfg lambda.FunctionEventInvokeConfig
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&getCfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &getCfg, jsonx.V1()))
assert.Equal(t, maxRetry, *getCfg.MaximumRetryAttempts)
// Update config
@@ -736,7 +738,7 @@ func TestEventInvokeConfig_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut lambda.ListFunctionEventInvokeConfigsOutput
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
assert.NotEmpty(t, listOut.FunctionEventInvokeConfigs)
// Delete config
@@ -767,7 +769,7 @@ func TestEventInvokeConfig_WithDestinations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var cfg lambda.FunctionEventInvokeConfig
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &cfg, jsonx.V1()))
require.NotNil(t, cfg.DestinationConfig)
require.NotNil(t, cfg.DestinationConfig.OnSuccess)
assert.Contains(t, cfg.DestinationConfig.OnSuccess.Destination, "success-q")
@@ -802,7 +804,7 @@ func TestFunctionDestinations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.FunctionEventInvokeConfig
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.DestinationConfig)
require.NotNil(t, out.DestinationConfig.OnSuccess)
require.NotNil(t, out.DestinationConfig.OnFailure)
diff --git a/services/lambda/event_source_poller.go b/services/lambda/event_source_poller.go
index 2b0763b355..ee36106443 100644
--- a/services/lambda/event_source_poller.go
+++ b/services/lambda/event_source_poller.go
@@ -3,7 +3,7 @@ package lambda
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strings"
@@ -11,6 +11,7 @@ import (
"time"
arnpkg "github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -641,7 +642,7 @@ func (p *EventSourcePoller) invokeLambda(
InvokeIdentityArn: m.FunctionARN,
AWSRegion: p.lambdaBackend.region,
EventSourceARN: m.EventSourceARN,
- })
+ }, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "event source poller: failed to marshal event", "error", err)
@@ -863,7 +864,7 @@ func (p *EventSourcePoller) invokeLambdaForDDB(
NewImage: r.NewImage,
OldImage: r.OldImage,
},
- })
+ }, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "event source poller: failed to marshal DDB event", "error", err)
@@ -876,7 +877,7 @@ func (p *EventSourcePoller) invokeLambdaForDDB(
arrival = time.Time{}
}
- keyJSON, _ := json.Marshal(r.Keys)
+ keyJSON, _ := json.Marshal(r.Keys, jsonx.V1())
events = append(events, streamEvent{arrival: arrival, seq: r.SequenceNumber, key: string(keyJSON), raw: raw})
}
@@ -1068,7 +1069,7 @@ func buildSQSEventPayload(region, esmARN string, msgs []*SQSMessage) ([]byte, er
}
}
- payload, err := json.Marshal(sqsEvent{Records: records})
+ payload, err := json.Marshal(sqsEvent{Records: records}, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("marshal sqs event: %w", err)
}
@@ -1161,7 +1162,12 @@ func filterByBatchItemFailures(reportFailures bool, respBody []byte, msgs []*SQS
}
var failures batchItemFailures
- if jsonErr := json.Unmarshal(respBody, &failures); jsonErr != nil || len(failures.BatchItemFailures) == 0 {
+ if jsonErr := json.Unmarshal(
+ respBody,
+ &failures,
+ jsonx.V1(),
+ ); jsonErr != nil ||
+ len(failures.BatchItemFailures) == 0 {
return nil
}
diff --git a/services/lambda/event_source_poller_test.go b/services/lambda/event_source_poller_test.go
index 3a8ef8defc..4a9865edf3 100644
--- a/services/lambda/event_source_poller_test.go
+++ b/services/lambda/event_source_poller_test.go
@@ -2,7 +2,7 @@ package lambda_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"testing/synctest"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -248,7 +249,7 @@ func TestLambda_BuildSQSEventPayload(t *testing.T) {
EventSourceARN string `json:"eventSourceARN"`
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(payload, &event))
+ require.NoError(t, json.Unmarshal(payload, &event, jsonx.V1()))
require.Len(t, event.Records, 1)
rec := event.Records[0]
diff --git a/services/lambda/extensions_api.go b/services/lambda/extensions_api.go
index 906d1c3610..598fec872e 100644
--- a/services/lambda/extensions_api.go
+++ b/services/lambda/extensions_api.go
@@ -2,7 +2,8 @@ package lambda
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -12,6 +13,7 @@ import (
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -197,7 +199,7 @@ type registerResponse struct {
func writeExtError(w http.ResponseWriter, status int, typ, msg string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
- _ = json.NewEncoder(w).Encode(extensionError{Type: typ, Message: msg})
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), extensionError{Type: typ, Message: msg})
}
// register adds an extension; it returns the response, identifier or an HTTP failure.
@@ -290,7 +292,7 @@ func (r *extensionRegistry) fanoutInvoke(reqID string, deadline time.Time, fnARN
body["tracing"] = map[string]string{"type": "X-Amzn-Trace-Id", "value": trace}
}
- raw, _ := json.Marshal(body)
+ raw, _ := json.Marshal(body, jsonx.V1())
wait := &invokeWait{pending: map[string]struct{}{}, done: make(chan struct{}), deadline: deadline}
for id, ext := range r.exts {
@@ -433,7 +435,7 @@ func (r *extensionRegistry) shutdown(ctx context.Context, reason string) {
deadline := time.Now().Add(r.shutdownTimeout)
raw, _ := json.Marshal(map[string]any{
jsonKeyEventType: extEventShutdown, "shutdownReason": reason, "deadlineMs": deadline.UnixMilli(),
- })
+ }, jsonx.V1())
var taken []chan struct{}
@@ -488,7 +490,7 @@ func (s *runtimeServer) handleExtensionRegister(w http.ResponseWriter, r *http.R
body, _ := io.ReadAll(http.MaxBytesReader(w, r.Body, maxExtensionBodyBytes))
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
writeExtError(w, http.StatusBadRequest, "Extension.InvalidRequestBody", "malformed registration body")
return
@@ -512,7 +514,7 @@ func (s *runtimeServer) handleExtensionRegister(w http.ResponseWriter, r *http.R
w.Header().Set(headerExtensionID, id)
w.Header().Set("Content-Type", "application/json")
- _ = json.NewEncoder(w).Encode(resp)
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), resp)
}
// handleExtensionNext serves GET /2020-01-01/extension/event/next (long poll).
diff --git a/services/lambda/extensions_api_internal_test.go b/services/lambda/extensions_api_internal_test.go
index 428d36c629..ee6799a254 100644
--- a/services/lambda/extensions_api_internal_test.go
+++ b/services/lambda/extensions_api_internal_test.go
@@ -2,7 +2,7 @@ package lambda
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -82,7 +83,7 @@ func (h *extHarness) do(
func (h *extHarness) register(t *testing.T, name string, events ...string) string {
t.Helper()
- b, err := json.Marshal(map[string]any{"events": events})
+ b, err := json.Marshal(map[string]any{"events": events}, jsonx.V1())
require.NoError(t, err)
status, hdr, body := h.do(t, http.MethodPost, "/2020-01-01/extension/register",
@@ -177,7 +178,7 @@ func TestExtensionRegister(t *testing.T) {
if tt.wantStatus != http.StatusOK {
var e extensionError
- require.NoError(t, json.Unmarshal([]byte(body), &e))
+ require.NoError(t, json.Unmarshal([]byte(body), &e, jsonx.V1()))
assert.Equal(t, tt.wantType, e.Type)
return
@@ -187,7 +188,7 @@ func TestExtensionRegister(t *testing.T) {
var resp map[string]string
- require.NoError(t, json.Unmarshal([]byte(body), &resp))
+ require.NoError(t, json.Unmarshal([]byte(body), &resp, jsonx.V1()))
assert.Equal(t, "ext-fn", resp["functionName"])
assert.Equal(t, "$LATEST", resp["functionVersion"])
assert.Equal(t, "index.handler", resp["handler"])
@@ -250,7 +251,7 @@ func TestExtensionInvokeFanout(t *testing.T) {
var ev map[string]any
- require.NoError(t, json.Unmarshal([]byte(<-otherEv), &ev))
+ require.NoError(t, json.Unmarshal([]byte(<-otherEv), &ev, jsonx.V1()))
assert.Equal(t, "INVOKE", ev["eventType"])
assert.Equal(t, reqID, ev["requestId"])
assert.Equal(t, extTestFnARN, ev["invokedFunctionArn"])
@@ -337,7 +338,7 @@ func TestExtensionShutdown(t *testing.T) {
var got map[string]any
- require.NoError(t, json.Unmarshal([]byte(<-ev), &got))
+ require.NoError(t, json.Unmarshal([]byte(<-ev), &got, jsonx.V1()))
assert.Equal(t, "SHUTDOWN", got["eventType"])
assert.Equal(t, tt.wantReason, got["shutdownReason"])
assert.Positive(t, got["deadlineMs"])
diff --git a/services/lambda/fis.go b/services/lambda/fis.go
index 3558dc44fb..6dd891fe95 100644
--- a/services/lambda/fis.go
+++ b/services/lambda/fis.go
@@ -2,7 +2,7 @@ package lambda
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"math/rand/v2"
"net/http"
@@ -10,6 +10,7 @@ import (
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -338,7 +339,7 @@ func (b *InMemoryBackend) applyFISFaultToInvocation(
errPayload, _ := json.Marshal(map[string]string{
"errorMessage": faultErr.Error(),
"errorType": "FISFaultInjection",
- })
+ }, jsonx.V1())
return errPayload, http.StatusOK, nil
}
diff --git a/services/lambda/function_fields_test.go b/services/lambda/function_fields_test.go
index 46b7c2e995..aafbeab50b 100644
--- a/services/lambda/function_fields_test.go
+++ b/services/lambda/function_fields_test.go
@@ -1,7 +1,8 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -15,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -42,7 +44,7 @@ func TestVpcConfig_Ipv6AllowedForDualStack_Create(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
vpc, _ := out["VpcConfig"].(map[string]any)
require.NotNil(t, vpc)
@@ -63,7 +65,7 @@ func TestVpcConfig_Ipv6AllowedForDualStack_Update(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
vpc, _ := out["VpcConfig"].(map[string]any)
require.NotNil(t, vpc)
assert.Equal(t, true, vpc["Ipv6AllowedForDualStack"])
@@ -87,7 +89,7 @@ func TestVpcConfig_Ipv6AllowedForDualStack_NotSet_Omitted(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
vpc, _ := out["VpcConfig"].(map[string]any)
require.NotNil(t, vpc)
// omitempty: nil pointer → field absent in JSON
@@ -118,7 +120,7 @@ func TestVpcConfig_Ipv6AllowedForDualStack_GetConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &out, jsonx.V1()))
vpc, _ := out["VpcConfig"].(map[string]any)
require.NotNil(t, vpc)
assert.Equal(t, true, vpc["Ipv6AllowedForDualStack"])
@@ -142,7 +144,7 @@ func TestVpcConfig_CreateAndGet(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
var created lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&created))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &created, jsonx.V1()))
require.NotNil(t, created.VpcConfig)
assert.Equal(t, []string{"subnet-abc"}, created.VpcConfig.SubnetIDs)
assert.Equal(t, []string{"sg-xyz"}, created.VpcConfig.SecurityGroupIDs)
@@ -151,10 +153,10 @@ func TestVpcConfig_CreateAndGet(t *testing.T) {
rec2 := auditGetFunction(t, h, "vpc-fn")
require.Equal(t, http.StatusOK, rec2.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &out, jsonx.V1()))
var cfg lambda.FunctionConfiguration
- require.NoError(t, json.Unmarshal(out["Configuration"], &cfg))
+ require.NoError(t, json.Unmarshal(out["Configuration"], &cfg, jsonx.V1()))
require.NotNil(t, cfg.VpcConfig)
assert.Equal(t, []string{"subnet-abc"}, cfg.VpcConfig.SubnetIDs)
}
@@ -171,7 +173,7 @@ func TestVpcConfig_UpdateAndGet(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code, rec2.Body.String())
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.VpcConfig)
assert.Equal(t, []string{"subnet-new"}, fn.VpcConfig.SubnetIDs)
}
@@ -186,7 +188,7 @@ func TestTracingConfig_CreateDefault(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
// Default TracingConfig is PassThrough
require.NotNil(t, fn.TracingConfig)
assert.Equal(t, "PassThrough", fn.TracingConfig.Mode)
@@ -208,7 +210,7 @@ func TestTracingConfig_CreateActive(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.TracingConfig)
assert.Equal(t, "Active", fn.TracingConfig.Mode)
}
@@ -224,7 +226,7 @@ func TestTracingConfig_Update(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.TracingConfig)
assert.Equal(t, "Active", fn.TracingConfig.Mode)
}
@@ -249,7 +251,7 @@ func TestFileSystemConfigs_CreateAndGet(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
require.Len(t, fn.FileSystemConfigs, 1)
assert.Equal(t, "arn:aws:elasticfilesystem:us-east-1:000000000000:access-point/fsap-abc",
fn.FileSystemConfigs[0].Arn)
@@ -270,7 +272,7 @@ func TestFileSystemConfigs_Update(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &fn, jsonx.V1()))
require.Len(t, fn.FileSystemConfigs, 1)
assert.Equal(t, "/mnt/new", fn.FileSystemConfigs[0].LocalMountPath)
}
@@ -293,7 +295,7 @@ func TestDeadLetterConfig_CreateAndGet(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.DeadLetterConfig)
assert.Equal(t, "arn:aws:sqs:us-east-1:123:my-dlq", fn.DeadLetterConfig.TargetArn)
}
@@ -310,7 +312,7 @@ func TestDeadLetterConfig_Update(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.DeadLetterConfig)
assert.Equal(t, "arn:aws:sns:us-east-1:123:my-topic", fn.DeadLetterConfig.TargetArn)
}
@@ -339,7 +341,7 @@ func TestDurableConfig_CreateAndGet(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.DurableConfig)
require.NotNil(t, fn.DurableConfig.ExecutionTimeout)
assert.Equal(t, int32(3600), *fn.DurableConfig.ExecutionTimeout)
@@ -360,7 +362,7 @@ func TestDurableConfig_Update(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.DurableConfig)
require.NotNil(t, fn.DurableConfig.ExecutionTimeout)
assert.Equal(t, int32(7200), *fn.DurableConfig.ExecutionTimeout)
@@ -386,7 +388,7 @@ func TestDurableConfig_PublishedVersionCarriesConfig(t *testing.T) {
require.Equal(t, http.StatusCreated, pubRec.Code)
var ver lambda.FunctionVersion
- require.NoError(t, json.NewDecoder(pubRec.Body).Decode(&ver))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(pubRec.Body), &ver, jsonx.V1()))
require.NotNil(t, ver.DurableConfig)
require.NotNil(t, ver.DurableConfig.ExecutionTimeout)
assert.Equal(t, int32(1800), *ver.DurableConfig.ExecutionTimeout)
@@ -631,7 +633,7 @@ func TestEphemeralStorage_DefaultOn_Create(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.EphemeralStorage)
assert.Equal(t, int32(512), fn.EphemeralStorage.Size)
}
@@ -673,7 +675,7 @@ func TestEphemeralStorage_Create(t *testing.T) {
if tc.wantCode == http.StatusCreated {
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.EphemeralStorage)
assert.Equal(t, int32(tc.size), fn.EphemeralStorage.Size)
}
@@ -716,7 +718,7 @@ func TestEphemeralStorage_Update(t *testing.T) {
if tc.wantCode == http.StatusOK {
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.EphemeralStorage)
assert.Equal(t, int32(tc.size), fn.EphemeralStorage.Size)
}
@@ -742,7 +744,7 @@ func TestImageConfig_Persisted_Create(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.ImageConfigResponse)
require.NotNil(t, fn.ImageConfigResponse.ImageConfig)
assert.Equal(t, []string{"serve"}, fn.ImageConfigResponse.ImageConfig.Command)
@@ -768,10 +770,10 @@ func TestImageConfig_InGetFunction(t *testing.T) {
rec2 := auditGetFunction(t, h, "imgcfg-get-fn")
require.Equal(t, http.StatusOK, rec2.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &out, jsonx.V1()))
var cfg lambda.FunctionConfiguration
- require.NoError(t, json.Unmarshal(out["Configuration"], &cfg))
+ require.NoError(t, json.Unmarshal(out["Configuration"], &cfg, jsonx.V1()))
require.NotNil(t, cfg.ImageConfigResponse)
require.NotNil(t, cfg.ImageConfigResponse.ImageConfig)
assert.Equal(t, []string{"run"}, cfg.ImageConfigResponse.ImageConfig.Command)
@@ -785,7 +787,7 @@ func TestImageConfig_NotSetForZip(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
assert.Nil(t, fn.ImageConfigResponse)
}
@@ -928,7 +930,7 @@ func TestAllNewFields_PersistInGetConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.VpcConfig)
assert.Equal(t, []string{"subnet-1"}, fn.VpcConfig.SubnetIDs)
@@ -1049,14 +1051,14 @@ func TestVpcConfig_JSONShape(t *testing.T) {
rec := auditCreateFunction(t, h, body)
require.Equal(t, http.StatusCreated, rec.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &raw, jsonx.V1()))
vpcRaw, ok := raw["VpcConfig"]
require.True(t, ok, "VpcConfig key missing from response")
- var vpc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(vpcRaw, &vpc))
+ var vpc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(vpcRaw, &vpc, jsonx.V1()))
assert.Contains(t, vpc, "SubnetIds")
assert.Contains(t, vpc, "SecurityGroupIds")
}
@@ -1068,8 +1070,8 @@ func TestTracingConfig_JSONShape(t *testing.T) {
rec := auditCreateFunction(t, h, baseImageFn("tracing-shape-fn"))
require.Equal(t, http.StatusCreated, rec.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &raw, jsonx.V1()))
_, ok := raw["TracingConfig"]
assert.True(t, ok, "TracingConfig key missing from response")
@@ -1104,7 +1106,7 @@ func TestMultipleFields_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &fn, jsonx.V1()))
// VpcConfig updated
require.NotNil(t, fn.VpcConfig)
@@ -1153,7 +1155,7 @@ func TestGetFunctionConfiguration_IncludesNewFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.VpcConfig)
assert.Equal(t, []string{"subnet-gc"}, fn.VpcConfig.SubnetIDs)
@@ -1192,10 +1194,10 @@ func TestListFunctions_IncludesNewFields(t *testing.T) {
require.NoError(t, h.Handler()(c))
require.Equal(t, http.StatusOK, rec2.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &out, jsonx.V1()))
var fns []lambda.FunctionConfiguration
- require.NoError(t, json.Unmarshal(out["Functions"], &fns))
+ require.NoError(t, json.Unmarshal(out["Functions"], &fns, jsonx.V1()))
require.Len(t, fns, 1)
require.NotNil(t, fns[0].VpcConfig)
assert.Equal(t, []string{"subnet-list"}, fns[0].VpcConfig.SubnetIDs)
diff --git a/services/lambda/function_settings_test.go b/services/lambda/function_settings_test.go
index 9f83f62afc..e00c840113 100644
--- a/services/lambda/function_settings_test.go
+++ b/services/lambda/function_settings_test.go
@@ -2,7 +2,8 @@ package lambda_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -61,7 +63,7 @@ func TestFunctionScalingConfig_PutGet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.GetFunctionScalingConfigOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.AppliedFunctionScalingConfig)
require.NotNil(t, out.AppliedFunctionScalingConfig.MaxExecutionEnvironments)
assert.Equal(t, int32(maxEnv), *out.AppliedFunctionScalingConfig.MaxExecutionEnvironments)
@@ -96,7 +98,7 @@ func TestFunctionScalingConfig_DistinctPerQualifier(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.GetFunctionScalingConfigOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.AppliedFunctionScalingConfig)
require.NotNil(t, out.AppliedFunctionScalingConfig.MaxExecutionEnvironments)
@@ -147,7 +149,7 @@ func TestRuntimeManagementConfig_GetDefault(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var cfg lambda.RuntimeManagementConfig
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &cfg, jsonx.V1()))
assert.Equal(t, "Auto", cfg.UpdateRuntimeOn)
}
@@ -163,7 +165,7 @@ func TestRuntimeManagementConfig_Put(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var cfg lambda.RuntimeManagementConfig
- require.NoError(t, json.NewDecoder(putRec.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(putRec.Body), &cfg, jsonx.V1()))
assert.Equal(t, "FunctionUpdate", cfg.UpdateRuntimeOn)
}
@@ -180,7 +182,7 @@ func TestRuntimeManagementConfig_PutManualWithARN(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var cfg lambda.RuntimeManagementConfig
- require.NoError(t, json.NewDecoder(putRec.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(putRec.Body), &cfg, jsonx.V1()))
assert.Equal(t, "Manual", cfg.UpdateRuntimeOn)
assert.Equal(t, rva, cfg.RuntimeVersionArn)
}
@@ -196,7 +198,7 @@ func TestRuntimeManagementConfig_FunctionARNInResponse(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var cfg lambda.RuntimeManagementConfig
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &cfg, jsonx.V1()))
assert.Contains(t, cfg.FunctionArn, "rmc-arn-fn")
}
@@ -215,7 +217,7 @@ func TestRecursionConfig_GetDefault(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var cfg lambda.FunctionRecursionConfig
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &cfg, jsonx.V1()))
assert.Equal(t, "Terminate", cfg.RecursiveLoop)
}
@@ -231,7 +233,7 @@ func TestRecursionConfig_Put_Allow(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var cfg lambda.FunctionRecursionConfig
- require.NoError(t, json.NewDecoder(putRec.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(putRec.Body), &cfg, jsonx.V1()))
assert.Equal(t, "Allow", cfg.RecursiveLoop)
}
@@ -251,7 +253,7 @@ func TestRecursionConfig_Put_Terminate(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var cfg lambda.FunctionRecursionConfig
- require.NoError(t, json.NewDecoder(putRec.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(putRec.Body), &cfg, jsonx.V1()))
assert.Equal(t, "Terminate", cfg.RecursiveLoop)
}
diff --git a/services/lambda/function_tenancy_capacity_test.go b/services/lambda/function_tenancy_capacity_test.go
index fab249dfca..c29a7c0878 100644
--- a/services/lambda/function_tenancy_capacity_test.go
+++ b/services/lambda/function_tenancy_capacity_test.go
@@ -1,12 +1,14 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCreateFunction_TenancyAndCapacityProviderConfig(t *testing.T) {
@@ -52,7 +54,7 @@ func TestCreateFunction_TenancyAndCapacityProviderConfig(t *testing.T) {
}
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
assert.Equal(t, "PER_TENANT", created["TenancyConfig"].(map[string]any)["TenantIsolationMode"])
rec = callInMemoryHandler(t, h, http.MethodPost, "/2015-03-31/functions/tenant-fn/versions", `{}`)
@@ -68,7 +70,7 @@ func TestCreateFunction_TenancyAndCapacityProviderConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
cp, _ := got["CapacityProviderConfig"].(map[string]any)
require.NotNil(t, cp, "published version keeps the capacity provider config")
diff --git a/services/lambda/function_url_host_test.go b/services/lambda/function_url_host_test.go
index 2c21d7be27..5f046fa60c 100644
--- a/services/lambda/function_url_host_test.go
+++ b/services/lambda/function_url_host_test.go
@@ -5,7 +5,7 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"net"
@@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -298,7 +299,7 @@ func TestFunctionURLHost_EventShape(t *testing.T) {
Cookies []string `json:"cookies"`
IsBase64 bool `json:"isBase64Encoded"`
}
- require.NoError(t, json.Unmarshal(<-env.events, &ev))
+ require.NoError(t, json.Unmarshal(<-env.events, &ev, jsonx.V1()))
assert.Equal(t, "2.0", ev.Version)
assert.Equal(t, "/a%20b/c", ev.RawPath)
@@ -373,7 +374,7 @@ func TestFunctionURLHost_IAMAuthorizerContext(t *testing.T) {
} `json:"authorizer"`
} `json:"requestContext"`
}
- require.NoError(t, json.Unmarshal(<-env.events, &ev))
+ require.NoError(t, json.Unmarshal(<-env.events, &ev, jsonx.V1()))
assert.Equal(t, "AKIDEXAMPLE", ev.RequestContext.Authorizer.IAM.AccessKey)
assert.Equal(t, "000000000000", ev.RequestContext.Authorizer.IAM.AccountID)
}
@@ -501,7 +502,7 @@ func TestFunctionURLConfig_Validation(t *testing.T) {
if tt.want == http.StatusCreated {
var cfg lambda.FunctionURLConfig
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cfg))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cfg, jsonx.V1()))
assert.Regexp(t, `^https://[0-9a-f]{32}\.lambda-url\.us-east-1\.on\.aws/$`, cfg.FunctionURL)
up := callHandler(t, h, http.MethodPut, "/2021-10-31/functions/val-fn/url", `{"InvokeMode":"X"}`, nil)
diff --git a/services/lambda/function_url_http.go b/services/lambda/function_url_http.go
index b7e91f8ea6..c6d6935582 100644
--- a/services/lambda/function_url_http.go
+++ b/services/lambda/function_url_http.go
@@ -3,7 +3,7 @@ package lambda
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -20,6 +20,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -275,7 +276,7 @@ func writeFunctionURLError(w http.ResponseWriter, status int, message string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
- body, _ := json.Marshal(map[string]string{"Message": message})
+ body, _ := json.Marshal(map[string]string{"Message": message}, jsonx.V1())
_, _ = w.Write(body)
}
@@ -448,7 +449,7 @@ func (b *InMemoryBackend) buildURLEvent(r *http.Request, meta urlRequestMeta) ([
event.Body, event.IsBase64Encoded = encodeURLBody(bodyBytes, r.Header.Get("Content-Type"))
- return json.Marshal(event)
+ return json.Marshal(event, jsonx.V1())
}
func urlIAMAuthorizer(r *http.Request, accountID string) *lambdaURLAuthorizer {
@@ -590,7 +591,7 @@ func writeFunctionURLResult(w http.ResponseWriter, result []byte, streaming bool
if streaming {
if prelude, rest, ok := bytes.Cut(result, []byte(functionURLStreamSep)); ok &&
- json.Unmarshal(prelude, &resp) == nil {
+ json.Unmarshal(prelude, &resp, jsonx.V1()) == nil {
writeURLResponseHead(w, &resp)
writeFlushed(w, rest)
@@ -598,7 +599,7 @@ func writeFunctionURLResult(w http.ResponseWriter, result []byte, streaming bool
}
}
- if json.Unmarshal(result, &resp) != nil || resp.StatusCode == 0 {
+ if json.Unmarshal(result, &resp, jsonx.V1()) != nil || resp.StatusCode == 0 {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(http.StatusOK)
diff --git a/services/lambda/function_urls_test.go b/services/lambda/function_urls_test.go
index bdf9ac83ae..1f20db4ee4 100644
--- a/services/lambda/function_urls_test.go
+++ b/services/lambda/function_urls_test.go
@@ -1,7 +1,8 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -78,7 +80,7 @@ func TestFunctionURL_CORS(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var cfg lambda.FunctionURLConfig
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &cfg, jsonx.V1()))
require.NotNil(t, cfg.Cors)
assert.Equal(t, []string{"https://example.com"}, cfg.Cors.AllowOrigins)
assert.Equal(t, 300, cfg.Cors.MaxAge)
@@ -96,7 +98,7 @@ func TestFunctionURL_InvokeMode(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var cfg lambda.FunctionURLConfig
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &cfg, jsonx.V1()))
assert.Equal(t, "RESPONSE_STREAM", cfg.InvokeMode)
}
@@ -112,7 +114,7 @@ func TestFunctionURL_AWSIAMAuth(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var cfg lambda.FunctionURLConfig
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &cfg, jsonx.V1()))
assert.Equal(t, "AWS_IAM", cfg.AuthType)
}
@@ -128,7 +130,7 @@ func TestFunctionURL_FunctionURLIsSet(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var cfg lambda.FunctionURLConfig
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &cfg, jsonx.V1()))
assert.NotEmpty(t, cfg.FunctionURL)
}
@@ -177,7 +179,7 @@ func TestUpdateFunctionURLConfig_CORS(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var cfg lambda.FunctionURLConfig
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &cfg, jsonx.V1()))
assert.Equal(t, tt.wantAuthType, cfg.AuthType)
@@ -213,7 +215,7 @@ func TestFunctionURL_CorsAllowOrigins_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &out, jsonx.V1()))
cors, _ := out["Cors"].(map[string]any)
require.NotNil(t, cors)
origins, _ := cors["AllowOrigins"].([]any)
@@ -235,7 +237,7 @@ func TestFunctionURLConfig_2021(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var cfg lambda.FunctionURLConfig
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &cfg, jsonx.V1()))
assert.Equal(t, "NONE", cfg.AuthType)
// Get via 2021 path
@@ -249,7 +251,7 @@ func TestFunctionURLConfig_2021(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut lambda.ListFunctionURLConfigsOutput
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
assert.Len(t, listOut.FunctionURLConfigs, 1)
// Update via 2021 path
@@ -291,7 +293,7 @@ func TestFunctionURLConfig_2021_ListEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut lambda.ListFunctionURLConfigsOutput
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
assert.Empty(t, listOut.FunctionURLConfigs)
}
@@ -381,7 +383,7 @@ func TestLambda_FunctionURL_EventPayloadEnrichment(t *testing.T) {
Headers map[string]string `json:"headers"`
Cookies []string `json:"cookies"`
}
- require.NoError(t, json.Unmarshal(payload, &event))
+ require.NoError(t, json.Unmarshal(payload, &event, jsonx.V1()))
assert.ElementsMatch(t, []string{"session=abc", "theme=dark"}, event.Cookies)
assert.Equal(t, "bar", event.QueryStringParameters["foo"])
diff --git a/services/lambda/functions_test.go b/services/lambda/functions_test.go
index 849ccd9aaa..1a92663ad5 100644
--- a/services/lambda/functions_test.go
+++ b/services/lambda/functions_test.go
@@ -1,7 +1,8 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -80,7 +82,7 @@ func TestUpdateConfig_EnvVars(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.Environment)
assert.Equal(t, "val", fn.Environment.Variables["KEY"])
assert.Equal(t, "bar", fn.Environment.Variables["FOO"])
@@ -98,7 +100,7 @@ func TestUpdateConfig_TimeoutAndMemory(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &fn, jsonx.V1()))
assert.Equal(t, 30, fn.Timeout)
assert.Equal(t, 512, fn.MemorySize)
}
@@ -115,7 +117,7 @@ func TestUpdateConfig_VPC(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.VpcConfig)
assert.Equal(t, []string{"subnet-abc"}, fn.VpcConfig.SubnetIDs)
assert.Equal(t, []string{"sg-xyz"}, fn.VpcConfig.SecurityGroupIDs)
@@ -133,7 +135,7 @@ func TestUpdateConfig_DLQ(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.DeadLetterConfig)
assert.Equal(t, "arn:aws:sqs:us-east-1:000000000000:dlq", fn.DeadLetterConfig.TargetArn)
}
@@ -150,7 +152,7 @@ func TestUpdateConfig_Tracing(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.TracingConfig)
assert.Equal(t, "Active", fn.TracingConfig.Mode)
}
@@ -168,7 +170,7 @@ func TestUpdateConfig_FileSystem(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &fn, jsonx.V1()))
require.Len(t, fn.FileSystemConfigs, 1)
assert.Equal(t, "/mnt/data", fn.FileSystemConfigs[0].LocalMountPath)
}
@@ -186,7 +188,7 @@ func TestUpdateConfig_Layers(t *testing.T) {
require.Equal(t, http.StatusCreated, pubRec.Code)
var layerOut lambda.PublishLayerVersionOutput
- require.NoError(t, json.NewDecoder(pubRec.Body).Decode(&layerOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(pubRec.Body), &layerOut, jsonx.V1()))
layerARN := layerOut.LayerVersionArn
// Update function to use layer
@@ -196,7 +198,7 @@ func TestUpdateConfig_Layers(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &fn, jsonx.V1()))
require.Len(t, fn.Layers, 1)
assert.Contains(t, fn.Layers[0].Arn, "my-layer")
}
@@ -239,7 +241,7 @@ func TestGetFunctionConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
assert.Equal(t, 15, fn.Timeout)
assert.Equal(t, 256, fn.MemorySize)
}
@@ -259,7 +261,7 @@ func TestArchitectures_CreateArm64(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
require.Equal(t, []string{"arm64"}, fn.Architectures)
}
@@ -274,7 +276,7 @@ func TestArchitectures_CreateX86(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
assert.Equal(t, []string{"x86_64"}, fn.Architectures)
}
@@ -290,7 +292,7 @@ func TestArchitectures_UpdateCode(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
assert.Equal(t, []string{"arm64"}, fn.Architectures)
}
@@ -305,7 +307,7 @@ func TestArchitectures_DefaultIsX86(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.GetFunctionOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
// may be empty or x86_64 — just confirm it doesn't return arm64
for _, arch := range out.Configuration.Architectures {
assert.NotEqual(t, "arm64", arch)
@@ -328,7 +330,7 @@ func TestSnapStart_CreateWithPublishedVersions(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.SnapStart)
assert.Equal(t, "PublishedVersions", fn.SnapStart.ApplyOn)
assert.Equal(t, "On", fn.SnapStart.OptimizationStatus)
@@ -345,7 +347,7 @@ func TestSnapStart_CreateWithNone(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.SnapStart)
assert.Equal(t, "None", fn.SnapStart.ApplyOn)
assert.Equal(t, "Off", fn.SnapStart.OptimizationStatus)
@@ -363,7 +365,7 @@ func TestSnapStart_UpdateFunctionConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.SnapStart)
assert.Equal(t, "PublishedVersions", fn.SnapStart.ApplyOn)
assert.Equal(t, "On", fn.SnapStart.OptimizationStatus)
@@ -384,7 +386,7 @@ func TestSnapStart_DisableViaUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
require.NotNil(t, fn.SnapStart)
assert.Equal(t, "None", fn.SnapStart.ApplyOn)
assert.Equal(t, "Off", fn.SnapStart.OptimizationStatus)
@@ -403,7 +405,7 @@ func TestSnapStart_GetFunctionReflectsState(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.GetFunctionOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotNil(t, out.Configuration.SnapStart)
assert.Equal(t, "PublishedVersions", out.Configuration.SnapStart.ApplyOn)
}
@@ -423,7 +425,7 @@ func TestFunctionState_ActiveOnCreate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.GetFunctionOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, lambda.FunctionStateActive, out.Configuration.State)
}
@@ -445,7 +447,7 @@ func TestFunctionState_ListedFunctionsHaveState(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.ListFunctionsOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
for _, fn := range out.Functions {
assert.NotEmpty(t, string(fn.State), "function %s has empty State", fn.FunctionName)
}
@@ -462,7 +464,7 @@ func TestFunctionState_VersionHasState(t *testing.T) {
require.Equal(t, http.StatusCreated, pubRec.Code)
var v lambda.FunctionVersion
- require.NoError(t, json.NewDecoder(pubRec.Body).Decode(&v))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(pubRec.Body), &v, jsonx.V1()))
assert.NotEmpty(t, string(v.State))
}
@@ -488,7 +490,7 @@ func TestCreateFunction_AllFields(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
assert.Equal(t, "full-fn", fn.FunctionName)
assert.Equal(t, 1024, fn.MemorySize)
assert.Equal(t, 60, fn.Timeout)
@@ -590,7 +592,7 @@ func TestUpdateFunctionCode_Publish(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
assert.Equal(t, tt.wantVersion, fn.Version,
"UpdateFunctionCode Publish=%v: Version must be %q", tt.publish, tt.wantVersion)
@@ -647,7 +649,7 @@ func TestCreateFunction_Tags(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var out lambda.GetFunctionOutput
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &out, jsonx.V1()))
for k, v := range tt.wantTags {
assert.Equal(t, v, out.Tags[k],
@@ -706,7 +708,7 @@ func TestCreateFunction_MemorySizeNotDivisibleBy64(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var errResp map[string]string
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterValueException", errResp["__type"],
"error type must be InvalidParameterValueException, not ServiceException")
}
@@ -737,7 +739,7 @@ func TestSnapStart_GetConfiguration_OptimizationStatus(t *testing.T) {
require.Equal(t, http.StatusOK, cfgRec.Code)
var cfg map[string]any
- require.NoError(t, json.NewDecoder(cfgRec.Body).Decode(&cfg))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(cfgRec.Body), &cfg, jsonx.V1()))
snap, _ := cfg["SnapStart"].(map[string]any)
require.NotNil(t, snap)
assert.Equal(t, "PublishedVersions", snap["ApplyOn"])
@@ -834,7 +836,7 @@ func TestListFunctions_FunctionVersionAll(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
fns, _ := out["Functions"].([]any)
assert.GreaterOrEqual(t, len(fns), tc.wantMinCount,
"FunctionVersion=ALL must include $LATEST and all published versions")
@@ -876,7 +878,7 @@ func TestUpdateFunctionCodeArchitectures(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn, jsonx.V1()))
require.Equal(t, []string{"arm64"}, fn.Architectures)
// GetFunctionConfiguration returns the stored architectures.
@@ -885,7 +887,7 @@ func TestUpdateFunctionCodeArchitectures(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var fn2 lambda.FunctionConfiguration
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn2, jsonx.V1()))
assert.Equal(t, []string{"arm64"}, fn2.Architectures)
}
diff --git a/services/lambda/get_function_qualifier_test.go b/services/lambda/get_function_qualifier_test.go
index efb0ea5636..b91f0de193 100644
--- a/services/lambda/get_function_qualifier_test.go
+++ b/services/lambda/get_function_qualifier_test.go
@@ -2,7 +2,7 @@ package lambda_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -21,7 +22,7 @@ func doFnRequest(t *testing.T, h *lambda.Handler, method, path string, body any)
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -148,7 +149,7 @@ func TestLambda_GetFunction_ByQualifier(t *testing.T) {
Version string `json:"Version"`
} `json:"Configuration"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Equal(t, tc.wantVersion, out.Configuration.Version)
require.Contains(t, out.Configuration.FunctionArn, tc.wantArnSfx)
})
@@ -172,7 +173,7 @@ func TestLambda_GetFunctionConfiguration_ByQualifier(t *testing.T) {
FunctionArn string `json:"FunctionArn"`
Version string `json:"Version"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cfg))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cfg, jsonx.V1()))
require.Equal(t, "1", cfg.Version)
require.Contains(t, cfg.FunctionArn, ":function:cfgfn:1")
}
diff --git a/services/lambda/handler_capacity_providers.go b/services/lambda/handler_capacity_providers.go
index f8d487f860..d4b7a7422e 100644
--- a/services/lambda/handler_capacity_providers.go
+++ b/services/lambda/handler_capacity_providers.go
@@ -1,7 +1,7 @@
package lambda
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- Capacity provider handlers ---
@@ -66,7 +67,7 @@ func (h *Handler) handleCreateCapacityProvider(c *echo.Context, bk *InMemoryBack
var input CreateCapacityProviderInput
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
}
@@ -153,7 +154,7 @@ func (h *Handler) handleUpdateCapacityProvider(c *echo.Context, bk *InMemoryBack
var input UpdateCapacityProviderInput
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
}
diff --git a/services/lambda/handler_code_signing.go b/services/lambda/handler_code_signing.go
index 1e3707f632..e7c1d70d1f 100644
--- a/services/lambda/handler_code_signing.go
+++ b/services/lambda/handler_code_signing.go
@@ -1,7 +1,7 @@
package lambda
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handle2020FunctionRoute handles routes under /2020-06-30/functions/{name}/...
@@ -72,7 +73,7 @@ func (h *Handler) handlePutFunctionCodeSigningConfig(c *echo.Context, bk *InMemo
}
var input PutFunctionCodeSigningConfigInput
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
@@ -173,7 +174,7 @@ func (h *Handler) handleCreateCodeSigningConfig(c *echo.Context, bk *InMemoryBac
var input CreateCodeSigningConfigInput
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
}
@@ -234,7 +235,7 @@ func (h *Handler) handleUpdateCodeSigningConfig(c *echo.Context, bk *InMemoryBac
var input UpdateCodeSigningConfigInput
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
}
diff --git a/services/lambda/handler_concurrency.go b/services/lambda/handler_concurrency.go
index 9524e8c347..717df6b429 100644
--- a/services/lambda/handler_concurrency.go
+++ b/services/lambda/handler_concurrency.go
@@ -1,13 +1,15 @@
package lambda
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const opListProvisionedConcurrencyConfigs = "ListProvisionedConcurrencyConfigs"
@@ -29,8 +31,8 @@ func (h *Handler) handlePutFunctionConcurrency(c *echo.Context, name string) err
"ReservedConcurrentExecutions is required")
}
- var raw map[string]json.RawMessage
- if unmarshalErr := json.Unmarshal(body, &raw); unmarshalErr != nil {
+ var raw map[string]jsontext.Value
+ if unmarshalErr := json.Unmarshal(body, &raw, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
@@ -40,7 +42,7 @@ func (h *Handler) handlePutFunctionConcurrency(c *echo.Context, name string) err
}
var input PutFunctionConcurrencyInput
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
@@ -131,7 +133,7 @@ func (h *Handler) handlePutProvisionedConcurrencyConfig(c *echo.Context, name st
}
var input PutProvisionedConcurrencyConfigInput
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
diff --git a/services/lambda/handler_durable_execution.go b/services/lambda/handler_durable_execution.go
index 2c43335034..67e45bb4f0 100644
--- a/services/lambda/handler_durable_execution.go
+++ b/services/lambda/handler_durable_execution.go
@@ -1,7 +1,7 @@
package lambda
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
)
@@ -284,7 +285,7 @@ func replayClosedDurableExecution(ex *DurableExecution, invType string) ([]byte,
msg = ptrconv.String(ex.Error.ErrorMessage)
}
- payload, _ := json.Marshal(map[string]string{"errorMessage": msg})
+ payload, _ := json.Marshal(map[string]string{"errorMessage": msg}, jsonx.V1())
return payload, "Unhandled", http.StatusOK
}
@@ -300,7 +301,7 @@ func (h *Handler) handleCheckpointDurableExecution(c *echo.Context, path string)
var body CheckpointDurableExecutionInput
if raw, err := httputils.ReadBody(c.Request()); err == nil && len(raw) > 0 {
- _ = json.Unmarshal(raw, &body)
+ _ = json.Unmarshal(raw, &body, jsonx.V1())
}
return c.JSON(http.StatusOK, store.checkpoint(arn, body.Updates))
@@ -430,7 +431,7 @@ func (h *Handler) handleStopDurableExecution(c *echo.Context) error {
// struct here silently dropped every real client's Error every time.
var parsedErr ErrorObject
if raw, err := httputils.ReadBody(c.Request()); err == nil && len(raw) > 0 {
- _ = json.Unmarshal(raw, &parsedErr)
+ _ = json.Unmarshal(raw, &parsedErr, jsonx.V1())
}
var errObj *ErrorObject
@@ -479,7 +480,7 @@ func (h *Handler) handleSendDurableExecutionCallbackFailure(c *echo.Context) err
// the request's top-level JSON body, not {"Error": {...}}.
var parsedErr ErrorObject
if raw, err := httputils.ReadBody(c.Request()); err == nil && len(raw) > 0 {
- _ = json.Unmarshal(raw, &parsedErr)
+ _ = json.Unmarshal(raw, &parsedErr, jsonx.V1())
}
var errObj *ErrorObject
diff --git a/services/lambda/handler_event_invoke_config.go b/services/lambda/handler_event_invoke_config.go
index 14f5745016..50517ae11c 100644
--- a/services/lambda/handler_event_invoke_config.go
+++ b/services/lambda/handler_event_invoke_config.go
@@ -1,7 +1,7 @@
package lambda
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handlePutFunctionEventInvokeConfig handles PUT /2015-03-31/functions/{name}/event-invoke-config.
@@ -25,7 +26,7 @@ func (h *Handler) handlePutFunctionEventInvokeConfig(c *echo.Context, name strin
var input PutFunctionEventInvokeConfigInput
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
}
@@ -67,7 +68,7 @@ func (h *Handler) handleUpdateFunctionEventInvokeConfig(c *echo.Context, name st
var input PutFunctionEventInvokeConfigInput
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
}
diff --git a/services/lambda/handler_event_source_mappings.go b/services/lambda/handler_event_source_mappings.go
index 51030ca6d7..88cbd51d14 100644
--- a/services/lambda/handler_event_source_mappings.go
+++ b/services/lambda/handler_event_source_mappings.go
@@ -1,7 +1,7 @@
package lambda
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleESMRoute dispatches event-source-mapping REST API requests.
@@ -74,7 +75,7 @@ func (h *Handler) handleCreateESM(c *echo.Context) error {
var req handleCreateESMInput
- if err = json.Unmarshal(body, &req); err != nil {
+ if err = json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
@@ -216,7 +217,7 @@ func (h *Handler) handleUpdateESM(c *echo.Context, id string) error {
}
var req handleUpdateESMInput
- if err = json.Unmarshal(body, &req); err != nil {
+ if err = json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
diff --git a/services/lambda/handler_function_settings.go b/services/lambda/handler_function_settings.go
index 477b49004a..066c4e3332 100644
--- a/services/lambda/handler_function_settings.go
+++ b/services/lambda/handler_function_settings.go
@@ -1,7 +1,7 @@
package lambda
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleRuntimeMgmtRoute handles /2021-07-20/functions/{name}/runtime-management-config routes.
@@ -51,7 +52,7 @@ func (h *Handler) handleRuntimeMgmtRoute(c *echo.Context, path, method string) e
var input PutRuntimeManagementConfigInput
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
}
@@ -112,7 +113,7 @@ func (h *Handler) handleRecursionConfigRoute(c *echo.Context, path, method strin
var input PutFunctionRecursionConfigInput
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
}
@@ -179,7 +180,7 @@ func (h *Handler) handleScalingConfigRoute(c *echo.Context, path, method string)
var input PutFunctionScalingConfigInput
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
}
diff --git a/services/lambda/handler_function_urls.go b/services/lambda/handler_function_urls.go
index 1e29d68538..339f9cda4b 100644
--- a/services/lambda/handler_function_urls.go
+++ b/services/lambda/handler_function_urls.go
@@ -1,7 +1,7 @@
package lambda
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateFunctionURLConfig(c *echo.Context, name string) error {
@@ -24,7 +25,7 @@ func (h *Handler) handleCreateFunctionURLConfig(c *echo.Context, name string) er
var input CreateFunctionURLConfigInput
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
}
@@ -173,7 +174,7 @@ func (h *Handler) handleUpdateFunctionURLConfig(c *echo.Context, name string) er
var input UpdateFunctionURLConfigInput
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
}
diff --git a/services/lambda/handler_function_urls_test.go b/services/lambda/handler_function_urls_test.go
index 1e32de7aea..472adf2075 100644
--- a/services/lambda/handler_function_urls_test.go
+++ b/services/lambda/handler_function_urls_test.go
@@ -1,7 +1,7 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -62,7 +63,7 @@ func TestFunctionUrl_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var cfg lambda.FunctionURLConfig
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cfg))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cfg, jsonx.V1()))
assert.NotEmpty(t, cfg.FunctionURL)
assert.Equal(t, "NONE", cfg.AuthType)
assert.NotEmpty(t, cfg.FunctionArn)
@@ -72,7 +73,7 @@ func TestFunctionUrl_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getCfg lambda.FunctionURLConfig
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getCfg))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getCfg, jsonx.V1()))
assert.Equal(t, cfg.FunctionURL, getCfg.FunctionURL)
rec = callHandler(t, h, http.MethodDelete, "/2015-03-31/functions/"+tt.funcName+"/url", "", nil)
diff --git a/services/lambda/handler_functions.go b/services/lambda/handler_functions.go
index a5bd895e69..8fe2739bad 100644
--- a/services/lambda/handler_functions.go
+++ b/services/lambda/handler_functions.go
@@ -4,7 +4,7 @@ import (
"context"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -16,6 +16,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -371,7 +372,7 @@ func (h *Handler) handleCreateFunction(c *echo.Context) error {
}
var input CreateFunctionInput
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid request body")
}
@@ -589,7 +590,7 @@ func (h *Handler) handleUpdateFunctionCode(c *echo.Context, name string) error {
}
var input UpdateFunctionCodeInput
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid request body")
}
@@ -765,7 +766,7 @@ func (h *Handler) handleUpdateFunctionConfiguration(c *echo.Context, name string
}
var input UpdateFunctionConfigurationInput
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid request body")
}
diff --git a/services/lambda/handler_invocation.go b/services/lambda/handler_invocation.go
index 4bbb2aa1e2..5945ecd57e 100644
--- a/services/lambda/handler_invocation.go
+++ b/services/lambda/handler_invocation.go
@@ -4,7 +4,8 @@ import (
"bytes"
"context"
"encoding/binary"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"hash/crc32"
"math"
@@ -14,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// errInvalidInvocationType and errInvalidLogType are returned unwritten by
@@ -129,7 +131,7 @@ func writeInvokeResult(c *echo.Context, result []byte, functionError string, sta
}
func rejectNonJSONPayload(h *Handler, c *echo.Context, body []byte) bool {
- if len(body) == 0 || json.Valid(body) {
+ if len(body) == 0 || jsontext.Value(body).IsValid(jsonx.V1()) {
return false
}
@@ -326,10 +328,10 @@ func isLambdaFunctionErrorPayload(result []byte) bool {
}
var probe struct {
- ErrorMessage *json.RawMessage `json:"errorMessage"`
- ErrorType *json.RawMessage `json:"errorType"`
+ ErrorMessage *jsontext.Value `json:"errorMessage"`
+ ErrorType *jsontext.Value `json:"errorType"`
}
- if err := json.Unmarshal(trimmed, &probe); err != nil {
+ if err := json.Unmarshal(trimmed, &probe, jsonx.V1()); err != nil {
return false
}
diff --git a/services/lambda/handler_layers.go b/services/lambda/handler_layers.go
index 0e2104e4ac..42fec6da16 100644
--- a/services/lambda/handler_layers.go
+++ b/services/lambda/handler_layers.go
@@ -1,7 +1,7 @@
package lambda
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleLayersRoute dispatches Lambda Layers REST API requests.
@@ -149,7 +150,7 @@ func (h *Handler) handlePublishLayerVersion(c *echo.Context, bk *InMemoryBackend
var input PublishLayerVersionInput
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
}
@@ -233,7 +234,7 @@ func (h *Handler) handleAddLayerVersionPermission(
var input AddLayerVersionPermissionInput
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
}
diff --git a/services/lambda/handler_permissions.go b/services/lambda/handler_permissions.go
index ffad594aaa..f0c36c0dd3 100644
--- a/services/lambda/handler_permissions.go
+++ b/services/lambda/handler_permissions.go
@@ -1,13 +1,14 @@
package lambda
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- AddPermission handler ---
@@ -25,7 +26,7 @@ func (h *Handler) handleAddPermission(c *echo.Context, name string) error {
}
var input AddPermissionInput
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
diff --git a/services/lambda/handler_resource_policy.go b/services/lambda/handler_resource_policy.go
index 442ef00dd5..70317f2706 100644
--- a/services/lambda/handler_resource_policy.go
+++ b/services/lambda/handler_resource_policy.go
@@ -1,7 +1,7 @@
package lambda
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// resourcePolicyBody is PutResourcePolicy's real request body shape
@@ -60,7 +61,7 @@ func (h *Handler) handlePutResourcePolicy(c *echo.Context, b *InMemoryBackend, r
}
var input resourcePolicyBody
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
diff --git a/services/lambda/handler_runtime_test.go b/services/lambda/handler_runtime_test.go
index 487665403d..84ee5c501d 100644
--- a/services/lambda/handler_runtime_test.go
+++ b/services/lambda/handler_runtime_test.go
@@ -2,7 +2,7 @@ package lambda_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -17,6 +17,7 @@ import (
"github.com/blackbirdworks/gopherstack/internal/dockercompat/api/types/container"
"github.com/blackbirdworks/gopherstack/internal/dockercompat/api/types/image"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
gophercontainer "github.com/blackbirdworks/gopherstack/pkgs/container"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc"
@@ -500,7 +501,7 @@ func assertLambdaError(t *testing.T, rec *httptest.ResponseRecorder, errType str
t.Helper()
var lambdaErr lambda.Error
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lambdaErr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lambdaErr, jsonx.V1()))
assert.Equal(t, errType, lambdaErr.Type)
}
diff --git a/services/lambda/handler_tags.go b/services/lambda/handler_tags.go
index 5082eb4077..725d7052fb 100644
--- a/services/lambda/handler_tags.go
+++ b/services/lambda/handler_tags.go
@@ -2,7 +2,7 @@ package lambda
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -102,7 +103,7 @@ func (h *Handler) handleTagsRoute(c *echo.Context, method string) error {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "failed to read body")
}
var input lambdaTagsInput
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid body")
}
var kv map[string]string
diff --git a/services/lambda/handler_tags_iam_test.go b/services/lambda/handler_tags_iam_test.go
index b5c9f9cfea..3fc64525f4 100644
--- a/services/lambda/handler_tags_iam_test.go
+++ b/services/lambda/handler_tags_iam_test.go
@@ -1,7 +1,7 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -154,7 +155,7 @@ func TestHandler_TagsRoute(t *testing.T) {
if tt.wantTagsNotNil || len(tt.wantTagValues) > 0 {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tt.wantTagsNotNil {
assert.NotNil(t, resp["Tags"])
}
@@ -173,7 +174,7 @@ func TestHandler_TagsRoute(t *testing.T) {
if len(tt.verifyTagValues) > 0 || len(tt.verifyTagAbsent) > 0 {
var resp map[string]any
- require.NoError(t, json.Unmarshal(verifyRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(verifyRec.Body.Bytes(), &resp, jsonx.V1()))
tags, ok := resp["Tags"].(map[string]any)
require.True(t, ok)
for k, v := range tt.verifyTagValues {
diff --git a/services/lambda/handler_test.go b/services/lambda/handler_test.go
index 8bb26ea9fe..1923715af0 100644
--- a/services/lambda/handler_test.go
+++ b/services/lambda/handler_test.go
@@ -2,7 +2,7 @@ package lambda_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -294,7 +295,7 @@ func TestCreateFunction(t *testing.T) {
}
var fn lambda.FunctionConfiguration
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn, jsonx.V1()))
assert.Equal(t, tt.wantFunctionName, fn.FunctionName)
if tt.wantFunctionArn != "" {
assert.Equal(t, tt.wantFunctionArn, fn.FunctionArn)
@@ -375,7 +376,7 @@ func TestGetFunction(t *testing.T) {
if tt.wantCode == http.StatusOK {
var out lambda.GetFunctionOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.Configuration)
assert.Equal(t, tt.funcName, out.Configuration.FunctionName)
require.NotNil(t, out.Code)
@@ -421,7 +422,7 @@ func TestListFunctions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.ListFunctionsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Functions, tt.wantCount)
})
}
@@ -544,7 +545,7 @@ func TestUpdateFunctionCode(t *testing.T) {
if tt.wantImageURI != "" {
var fn lambda.FunctionConfiguration
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn, jsonx.V1()))
assert.Equal(t, tt.wantImageURI, fn.ImageURI)
assert.NotEmpty(t, fn.RevisionID)
if tt.wantLastUpdateStatus != "" {
@@ -645,7 +646,7 @@ func TestUpdateFunctionConfiguration(t *testing.T) {
}
var fn lambda.FunctionConfiguration
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn, jsonx.V1()))
if tt.wantDescription != "" {
assert.Equal(t, tt.wantDescription, fn.Description)
}
diff --git a/services/lambda/handler_versions_aliases.go b/services/lambda/handler_versions_aliases.go
index edac4b399c..31a5b1013f 100644
--- a/services/lambda/handler_versions_aliases.go
+++ b/services/lambda/handler_versions_aliases.go
@@ -1,7 +1,7 @@
package lambda
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
)
@@ -83,7 +84,7 @@ func (h *Handler) handlePublishVersion(c *echo.Context, name string) error {
var input publishVersionInput
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
}
@@ -160,7 +161,7 @@ func (h *Handler) handleCreateAlias(c *echo.Context, name string) error {
}
var input CreateAliasInput
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
@@ -264,7 +265,7 @@ func (h *Handler) handleUpdateAlias(c *echo.Context, name, aliasName string) err
}
var input UpdateAliasInput
- if unmarshalErr := json.Unmarshal(body, &input); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &input, jsonx.V1()); unmarshalErr != nil {
return h.writeError(c, http.StatusBadRequest, "InvalidParameterValueException", "invalid JSON")
}
diff --git a/services/lambda/handler_versions_aliases_test.go b/services/lambda/handler_versions_aliases_test.go
index acdcbc5f65..2a37e76891 100644
--- a/services/lambda/handler_versions_aliases_test.go
+++ b/services/lambda/handler_versions_aliases_test.go
@@ -1,13 +1,14 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -69,7 +70,7 @@ func TestPublishVersion(t *testing.T) {
if tt.wantVersion != "" {
var ver lambda.FunctionVersion
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ver))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ver, jsonx.V1()))
assert.Equal(t, tt.wantVersion, ver.Version)
assert.Equal(t, tt.wantDesc, ver.Description)
assert.Equal(t, tt.funcName, ver.FunctionName)
@@ -134,7 +135,7 @@ func TestListVersionsByFunction(t *testing.T) {
if tt.wantCount > 0 {
var out lambda.ListVersionsByFunctionOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Versions, tt.wantCount)
for i, v := range tt.wantVersions {
assert.Equal(t, v, out.Versions[i].Version)
@@ -238,7 +239,7 @@ func TestCreateAlias(t *testing.T) {
if tt.wantName != "" {
var alias lambda.FunctionAlias
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &alias))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &alias, jsonx.V1()))
assert.Equal(t, tt.wantName, alias.Name)
assert.Equal(t, tt.wantVersion, alias.FunctionVersion)
}
@@ -312,7 +313,7 @@ func TestGetAlias(t *testing.T) {
if tt.wantName != "" {
var alias lambda.FunctionAlias
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &alias))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &alias, jsonx.V1()))
assert.Equal(t, tt.wantName, alias.Name)
}
})
@@ -378,7 +379,7 @@ func TestListAliases(t *testing.T) {
if tt.wantCount > 0 {
var out lambda.ListAliasesOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Aliases, tt.wantCount)
}
})
@@ -456,7 +457,7 @@ func TestUpdateAlias(t *testing.T) {
if tt.wantVersion != "" {
var alias lambda.FunctionAlias
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &alias))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &alias, jsonx.V1()))
assert.Equal(t, tt.wantVersion, alias.FunctionVersion)
}
})
diff --git a/services/lambda/invocation_test.go b/services/lambda/invocation_test.go
index 75f6953721..9880c7aaff 100644
--- a/services/lambda/invocation_test.go
+++ b/services/lambda/invocation_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -18,6 +18,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -598,14 +599,14 @@ func TestHandleInvokeAsync_Returns202Immediately(t *testing.T) {
if tt.wantStatus == http.StatusAccepted {
var body map[string]int
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, http.StatusAccepted, body["Status"],
"InvokeAsync body must contain {\"Status\":202}")
}
if tt.wantErrType != "" {
var errBody map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Contains(t, errBody["__type"], tt.wantErrType)
}
})
diff --git a/services/lambda/kafka_event.go b/services/lambda/kafka_event.go
index 8537ab7b34..eb5a1f4e69 100644
--- a/services/lambda/kafka_event.go
+++ b/services/lambda/kafka_event.go
@@ -2,11 +2,13 @@ package lambda
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"strconv"
"strings"
"time"
"unicode/utf8"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -101,7 +103,7 @@ func buildKafkaEventPayload(eventSource, eventSourceARN, bootstrap string, recs
})
}
- return json.Marshal(ev)
+ return json.Marshal(ev, jsonx.V1())
}
// kafkaFilterView is the record shape FilterCriteria is matched against; only "value" is a data key.
diff --git a/services/lambda/kafka_event_internal_test.go b/services/lambda/kafka_event_internal_test.go
index e247ff7fdf..e5320a0961 100644
--- a/services/lambda/kafka_event_internal_test.go
+++ b/services/lambda/kafka_event_internal_test.go
@@ -2,12 +2,14 @@ package lambda
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func kafkaTestRecord(topic string, partition int32, offset int64, value string) KafkaRecord {
@@ -63,7 +65,7 @@ func TestBuildKafkaEventPayload(t *testing.T) {
var ev map[string]any
- require.NoError(t, json.Unmarshal(raw, &ev))
+ require.NoError(t, json.Unmarshal(raw, &ev, jsonx.V1()))
assert.Equal(t, tt.source, ev["eventSource"])
assert.Equal(t, "b1:9092,b2:9092", ev["bootstrapServers"])
diff --git a/services/lambda/kafka_integration_internal_test.go b/services/lambda/kafka_integration_internal_test.go
index d612bf3d87..086f42a6b2 100644
--- a/services/lambda/kafka_integration_internal_test.go
+++ b/services/lambda/kafka_integration_internal_test.go
@@ -3,7 +3,7 @@ package lambda
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net"
"net/netip"
"strconv"
@@ -17,6 +17,8 @@ import (
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
"github.com/twmb/franz-go/pkg/kgo"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -89,7 +91,7 @@ func awaitDelivery(t *testing.T, ch <-chan []byte) map[string]any {
case raw := <-ch:
var ev map[string]any
- require.NoError(t, json.Unmarshal(raw, &ev))
+ require.NoError(t, json.Unmarshal(raw, &ev, jsonx.V1()))
return ev
case <-time.After(kafkaDeliveryTimeout):
diff --git a/services/lambda/kafka_msk_internal_test.go b/services/lambda/kafka_msk_internal_test.go
index 785655c4db..74e9d8d5ac 100644
--- a/services/lambda/kafka_msk_internal_test.go
+++ b/services/lambda/kafka_msk_internal_test.go
@@ -2,12 +2,14 @@ package lambda
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const mskTestARN = "arn:aws:kafka:us-east-1:000000000000:cluster/c/uuid"
@@ -82,7 +84,7 @@ func TestMSKESMFollowsClusterBroker(t *testing.T) {
var ev map[string]any
- require.NoError(t, json.Unmarshal(<-deliveries, &ev))
+ require.NoError(t, json.Unmarshal(<-deliveries, &ev, jsonx.V1()))
assert.Equal(t, "aws:kafka", ev["eventSource"])
assert.Equal(t, mskTestARN, ev["eventSourceArn"])
assert.Equal(t, "127.0.0.1:19092", ev["bootstrapServers"])
diff --git a/services/lambda/kafka_worker_internal_test.go b/services/lambda/kafka_worker_internal_test.go
index e2e447af5d..290a5e692c 100644
--- a/services/lambda/kafka_worker_internal_test.go
+++ b/services/lambda/kafka_worker_internal_test.go
@@ -2,7 +2,7 @@ package lambda
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"sync"
"sync/atomic"
@@ -12,6 +12,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errKafkaTestFunction = errors.New("boom")
@@ -202,7 +204,7 @@ func TestKafkaWorkerBatching(t *testing.T) {
Records map[string][]struct{} `json:"records"`
}
- require.NoError(t, json.Unmarshal(payload, &ev))
+ require.NoError(t, json.Unmarshal(payload, &ev, jsonx.V1()))
n := 0
for _, rs := range ev.Records {
diff --git a/services/lambda/layer_permissions_test.go b/services/lambda/layer_permissions_test.go
index f7cee14828..1a4df24183 100644
--- a/services/lambda/layer_permissions_test.go
+++ b/services/lambda/layer_permissions_test.go
@@ -1,13 +1,15 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -166,7 +168,7 @@ func TestLayer_Policy_AddAndGet(t *testing.T) {
require.Equal(t, http.StatusCreated, addRec.Code)
var addOut lambda.AddLayerVersionPermissionOutput
- require.NoError(t, json.NewDecoder(addRec.Body).Decode(&addOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(addRec.Body), &addOut, jsonx.V1()))
assert.NotEmpty(t, addOut.Statement)
assert.NotEmpty(t, addOut.RevisionID)
@@ -175,7 +177,7 @@ func TestLayer_Policy_AddAndGet(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var pol lambda.LayerVersionPolicy
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&pol))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &pol, jsonx.V1()))
assert.NotEmpty(t, pol.Policy)
}
@@ -222,7 +224,7 @@ func TestLayerVersionPermission_RevisionId(t *testing.T) {
require.Equal(t, http.StatusCreated, addRec.Code)
var addOut lambda.AddLayerVersionPermissionOutput
- require.NoError(t, json.NewDecoder(addRec.Body).Decode(&addOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(addRec.Body), &addOut, jsonx.V1()))
rev1 := addOut.RevisionID
require.NotEmpty(t, rev1)
@@ -234,7 +236,7 @@ func TestLayerVersionPermission_RevisionId(t *testing.T) {
getRec := callInMemoryHandler(t, h, http.MethodGet, policyPath, "")
var pol lambda.LayerVersionPolicy
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&pol))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &pol, jsonx.V1()))
assert.NotContains(t, pol.Policy, "s2")
// Stale RevisionId on Remove is rejected and leaves the statement in place.
@@ -244,7 +246,7 @@ func TestLayerVersionPermission_RevisionId(t *testing.T) {
getRec2 := callInMemoryHandler(t, h, http.MethodGet, policyPath, "")
var pol2 lambda.LayerVersionPolicy
- require.NoError(t, json.NewDecoder(getRec2.Body).Decode(&pol2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec2.Body), &pol2, jsonx.V1()))
assert.Contains(t, pol2.Policy, "s1")
// The correct RevisionId succeeds.
diff --git a/services/lambda/layers.go b/services/lambda/layers.go
index c574aeced3..e1cd08b96e 100644
--- a/services/lambda/layers.go
+++ b/services/lambda/layers.go
@@ -3,7 +3,7 @@ package lambda
import (
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/collections"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -346,7 +347,7 @@ func (b *InMemoryBackend) AddLayerVersionPermission(
stmts[input.StatementID] = stmt
- stmtJSON, marshalErr := json.Marshal(stmt)
+ stmtJSON, marshalErr := json.Marshal(stmt, jsonx.V1())
if marshalErr != nil {
return nil, marshalErr
}
@@ -454,7 +455,7 @@ func buildLayerPolicy(stmts map[string]*LayerVersionStatement) (string, error) {
Statement: statements,
}
- data, err := json.Marshal(doc)
+ data, err := json.Marshal(doc, jsonx.V1())
if err != nil {
return "", err
}
diff --git a/services/lambda/layers_http_test.go b/services/lambda/layers_http_test.go
index 65c537e760..4f0b987f9c 100644
--- a/services/lambda/layers_http_test.go
+++ b/services/lambda/layers_http_test.go
@@ -1,7 +1,8 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/url"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -50,7 +52,7 @@ func TestLayer_PublishAndGet(t *testing.T) {
require.Equal(t, http.StatusCreated, pubRec.Code)
var pub lambda.PublishLayerVersionOutput
- require.NoError(t, json.NewDecoder(pubRec.Body).Decode(&pub))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(pubRec.Body), &pub, jsonx.V1()))
assert.Equal(t, int64(1), pub.Version)
assert.Equal(t, "v1", pub.Description)
assert.Equal(t, []string{"python3.12", "python3.11"}, pub.CompatibleRuntimes)
@@ -63,7 +65,7 @@ func TestLayer_PublishAndGet(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var got lambda.GetLayerVersionOutput
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&got))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &got, jsonx.V1()))
assert.Equal(t, int64(1), got.Version)
}
@@ -79,7 +81,7 @@ func TestLayer_MultipleVersions(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var pub lambda.PublishLayerVersionOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&pub))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &pub, jsonx.V1()))
assert.Equal(t, int64(i+1), pub.Version)
}
}
@@ -99,7 +101,7 @@ func TestLayer_ListVersions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.ListLayerVersionsOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.LayerVersions, 2)
}
@@ -134,7 +136,7 @@ func TestLayer_ListLayers(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.ListLayersOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.GreaterOrEqual(t, len(out.Layers), 2)
}
@@ -149,14 +151,14 @@ func TestLayer_GetVersionByArn(t *testing.T) {
require.Equal(t, http.StatusCreated, pubRec.Code)
var pub lambda.PublishLayerVersionOutput
- require.NoError(t, json.NewDecoder(pubRec.Body).Decode(&pub))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(pubRec.Body), &pub, jsonx.V1()))
rec := callInMemoryHandler(t, h, http.MethodGet,
"/2018-10-31/layers?find=LayerVersion&Arn="+pub.LayerVersionArn, "")
require.Equal(t, http.StatusOK, rec.Code)
var got lambda.GetLayerVersionOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&got))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &got, jsonx.V1()))
assert.Equal(t, pub.LayerVersionArn, got.LayerVersionArn)
}
@@ -229,7 +231,7 @@ func TestListLayers_CompatibleRuntimeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
layers, _ := out["Layers"].([]any)
assert.Len(t, layers, tc.wantCount, "layer count mismatch for runtime=%q", tc.compatibleRuntime)
})
diff --git a/services/lambda/layers_test.go b/services/lambda/layers_test.go
index 73ad3303c8..3c67b61683 100644
--- a/services/lambda/layers_test.go
+++ b/services/lambda/layers_test.go
@@ -3,7 +3,7 @@ package lambda_test
import (
"archive/zip"
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -591,7 +592,7 @@ func TestCreateFunctionWithLayers(t *testing.T) {
"Layers": tt.layers,
}
- bodyBytes, _ := json.Marshal(bodyMap)
+ bodyBytes, _ := json.Marshal(bodyMap, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/2015-03-31/functions", strings.NewReader(string(bodyBytes)))
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()
diff --git a/services/lambda/list_functions_master_region_test.go b/services/lambda/list_functions_master_region_test.go
index 83ab63e7ad..390b4c8eb4 100644
--- a/services/lambda/list_functions_master_region_test.go
+++ b/services/lambda/list_functions_master_region_test.go
@@ -1,12 +1,15 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestListFunctions_MasterRegion(t *testing.T) {
@@ -50,9 +53,9 @@ func TestListFunctions_MasterRegion(t *testing.T) {
}
var out struct {
- Functions []json.RawMessage `json:"Functions"`
+ Functions []jsontext.Value `json:"Functions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Functions, tt.wantCount)
})
}
diff --git a/services/lambda/mq_event.go b/services/lambda/mq_event.go
index 519b5d4586..6a2455f5f5 100644
--- a/services/lambda/mq_event.go
+++ b/services/lambda/mq_event.go
@@ -2,10 +2,12 @@ package lambda
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"strconv"
"time"
"unicode/utf8"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Event shapes follow https://docs.aws.amazon.com/lambda/latest/dg/with-mq.html.
@@ -113,10 +115,10 @@ func strPtr(s string) *string {
// buildMQEventPayload shapes msgs into the documented aws:mq or aws:rmq event.
func buildMQEventPayload(eventSource, eventSourceARN, vhost string, msgs []mqMessage) ([]byte, error) {
if eventSource == mqEventSourceRabbitMQ {
- return json.Marshal(buildRabbitMQEvent(eventSourceARN, vhost, msgs))
+ return json.Marshal(buildRabbitMQEvent(eventSourceARN, vhost, msgs), jsonx.V1())
}
- return json.Marshal(buildActiveMQEvent(eventSourceARN, msgs))
+ return json.Marshal(buildActiveMQEvent(eventSourceARN, msgs), jsonx.V1())
}
func buildActiveMQEvent(eventSourceARN string, msgs []mqMessage) activeMQEvent {
diff --git a/services/lambda/mq_event_internal_test.go b/services/lambda/mq_event_internal_test.go
index f4ef964725..07e5106a92 100644
--- a/services/lambda/mq_event_internal_test.go
+++ b/services/lambda/mq_event_internal_test.go
@@ -2,7 +2,7 @@ package lambda
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"sync"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -244,7 +246,7 @@ func TestMQESMPollsRealBrokerOnly(t *testing.T) {
var ev map[string]any
- require.NoError(t, json.Unmarshal(<-deliveries, &ev))
+ require.NoError(t, json.Unmarshal(<-deliveries, &ev, jsonx.V1()))
assert.Equal(t, "aws:rmq", ev["eventSource"])
assert.Equal(t, mqTestBrokerARN, ev["eventSourceArn"])
assert.Contains(t, ev["rmqMessagesByQueue"], "q::/")
diff --git a/services/lambda/mq_worker.go b/services/lambda/mq_worker.go
index e113c567da..8cd85afb81 100644
--- a/services/lambda/mq_worker.go
+++ b/services/lambda/mq_worker.go
@@ -2,11 +2,12 @@ package lambda
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"reflect"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -169,7 +170,7 @@ func (p *EventSourcePoller) mqCredentials(
}
raw, err := secrets.SecretString(ctx, secretARN)
- if err != nil || json.Unmarshal([]byte(raw), &creds) != nil {
+ if err != nil || json.Unmarshal([]byte(raw), &creds, jsonx.V1()) != nil {
return creds, false
}
diff --git a/services/lambda/mq_worker_internal_test.go b/services/lambda/mq_worker_internal_test.go
index 412554a10e..097c8bcbf2 100644
--- a/services/lambda/mq_worker_internal_test.go
+++ b/services/lambda/mq_worker_internal_test.go
@@ -2,7 +2,7 @@ package lambda
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"sync"
"sync/atomic"
@@ -12,6 +12,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errMQTestFunction = errors.New("boom")
@@ -203,7 +205,7 @@ func TestMQWorkerBatching(t *testing.T) {
Messages []struct{} `json:"messages"`
}
- require.NoError(t, json.Unmarshal(payload, &ev))
+ require.NoError(t, json.Unmarshal(payload, &ev, jsonx.V1()))
sizes = append(sizes, len(ev.Messages))
diff --git a/services/lambda/multi_region_test.go b/services/lambda/multi_region_test.go
index 48b3c9615b..9147477dd5 100644
--- a/services/lambda/multi_region_test.go
+++ b/services/lambda/multi_region_test.go
@@ -3,7 +3,8 @@ package lambda_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -48,7 +50,7 @@ func regionCall(t *testing.T, h *lambda.Handler, region, method, path string, bo
var buf bytes.Buffer
if body != nil {
- require.NoError(t, json.NewEncoder(&buf).Encode(body))
+ require.NoError(t, json.MarshalEncode(jsontext.NewEncoder(&buf, jsonx.V1()), body))
}
req := httptest.NewRequest(method, path, &buf).WithContext(regionCtx(region))
@@ -77,7 +79,7 @@ func createRegionFunction(t *testing.T, h *lambda.Handler, region, name string)
FunctionArn string `json:"FunctionArn"`
}
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
return out.FunctionArn
}
@@ -95,7 +97,7 @@ func regionFunctionNames(t *testing.T, h *lambda.Handler, region string) []strin
} `json:"Functions"`
}
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
names := make([]string, 0, len(out.Functions))
@@ -316,9 +318,9 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
+ var doc map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(snap, &doc))
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
_, hasRegions := doc["regions"]
assert.Equal(t, tc.remote, hasRegions)
diff --git a/services/lambda/permissions.go b/services/lambda/permissions.go
index f3e1efe019..a92e01a18c 100644
--- a/services/lambda/permissions.go
+++ b/services/lambda/permissions.go
@@ -4,13 +4,15 @@ import (
"bytes"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"sort"
"strconv"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// AddPermission adds a permission statement to a function's resource-based policy.
@@ -271,10 +273,9 @@ func buildPermissionStatementJSON(p *FunctionPermission, resourceArn string) str
var buf bytes.Buffer
- enc := json.NewEncoder(&buf)
- enc.SetEscapeHTML(false)
+ enc := jsontext.NewEncoder(&buf, jsonx.V1(), jsontext.EscapeForHTML(false))
- if err := enc.Encode(stmt); err != nil {
+ if err := json.MarshalEncode(enc, stmt); err != nil {
return ""
}
diff --git a/services/lambda/permissions_test.go b/services/lambda/permissions_test.go
index 7cac66ff08..9994e43bf0 100644
--- a/services/lambda/permissions_test.go
+++ b/services/lambda/permissions_test.go
@@ -1,7 +1,8 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -29,7 +31,7 @@ func TestPermission_FullLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, addRec.Code)
var addOut lambda.AddPermissionOutput
- require.NoError(t, json.NewDecoder(addRec.Body).Decode(&addOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(addRec.Body), &addOut, jsonx.V1()))
require.NotNil(t, addOut.Statement)
assert.NotEmpty(t, *addOut.Statement)
@@ -44,7 +46,7 @@ func TestPermission_FullLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var pol lambda.GetPolicyOutput
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&pol))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &pol, jsonx.V1()))
require.NotNil(t, pol.Policy)
assert.Contains(t, *pol.Policy, "AllowS3")
assert.Contains(t, *pol.Policy, "AllowSNS")
@@ -63,7 +65,7 @@ func TestPermission_FullLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, getRec2.Code)
var pol2 lambda.GetPolicyOutput
- require.NoError(t, json.NewDecoder(getRec2.Body).Decode(&pol2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec2.Body), &pol2, jsonx.V1()))
assert.NotContains(t, *pol2.Policy, "AllowS3")
assert.Contains(t, *pol2.Policy, "AllowSNS")
}
@@ -129,7 +131,7 @@ func TestPermission_SourceArn(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var addOut lambda.AddPermissionOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&addOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &addOut, jsonx.V1()))
require.NotNil(t, addOut.Statement)
assert.NotEmpty(t, *addOut.Statement)
// Statement must include the StatementId
@@ -274,7 +276,7 @@ func TestPolicyJSON_SourceArnCondition(t *testing.T) {
require.NotEmpty(t, policyStr)
var policy map[string]any
- require.NoError(t, json.Unmarshal([]byte(policyStr), &policy))
+ require.NoError(t, json.Unmarshal([]byte(policyStr), &policy, jsonx.V1()))
stmts, _ := policy["Statement"].([]any)
require.Len(t, stmts, 1)
stmt := stmts[0].(map[string]any)
@@ -368,7 +370,7 @@ func TestAddPermission(t *testing.T) {
if tt.wantStatus == http.StatusCreated {
var out lambda.AddPermissionOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotNil(t, out.Statement)
assert.NotEmpty(t, *out.Statement)
}
@@ -416,7 +418,7 @@ func TestAddPermission_FunctionUrlConditions(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out lambda.AddPermissionOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotNil(t, out.Statement)
assert.Contains(t, *out.Statement, `"StringEquals":{"lambda:FunctionUrlAuthType":"NONE"}`)
assert.Contains(t, *out.Statement, `"Bool":{"lambda:InvokedViaFunctionUrl":"true"}`)
@@ -425,7 +427,7 @@ func TestAddPermission_FunctionUrlConditions(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var pol lambda.GetPolicyOutput
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&pol))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &pol, jsonx.V1()))
require.NotNil(t, pol.Policy)
assert.Contains(t, *pol.Policy, "lambda:FunctionUrlAuthType")
assert.Contains(t, *pol.Policy, "lambda:InvokedViaFunctionUrl")
@@ -453,7 +455,7 @@ func TestAddPermission_RevisionId(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var pol lambda.GetPolicyOutput
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&pol))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &pol, jsonx.V1()))
require.NotNil(t, pol.RevisionID)
rev1 := *pol.RevisionID
assert.NotEmpty(t, rev1)
@@ -466,7 +468,7 @@ func TestAddPermission_RevisionId(t *testing.T) {
getRec2 := callInMemoryHandler(t, h, http.MethodGet, policyPath, "")
var pol2 lambda.GetPolicyOutput
- require.NoError(t, json.NewDecoder(getRec2.Body).Decode(&pol2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec2.Body), &pol2, jsonx.V1()))
assert.Equal(t, rev1, *pol2.RevisionID, "rejected AddPermission must not change the revision")
assert.NotContains(t, *pol2.Policy, "s2")
@@ -478,7 +480,7 @@ func TestAddPermission_RevisionId(t *testing.T) {
getRec3 := callInMemoryHandler(t, h, http.MethodGet, policyPath, "")
var pol3 lambda.GetPolicyOutput
- require.NoError(t, json.NewDecoder(getRec3.Body).Decode(&pol3))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec3.Body), &pol3, jsonx.V1()))
rev3 := *pol3.RevisionID
assert.NotEqual(t, rev1, rev3, "a real mutation must change the revision")
@@ -489,7 +491,7 @@ func TestAddPermission_RevisionId(t *testing.T) {
getRec4 := callInMemoryHandler(t, h, http.MethodGet, policyPath, "")
var pol4 lambda.GetPolicyOutput
- require.NoError(t, json.NewDecoder(getRec4.Body).Decode(&pol4))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec4.Body), &pol4, jsonx.V1()))
assert.Contains(t, *pol4.Policy, "s1", "rejected RemovePermission must not delete the statement")
// RemovePermission with the correct RevisionId succeeds.
@@ -519,7 +521,7 @@ func TestPermission_PolicyJSONEscapesValues(t *testing.T) {
reqBody, err := json.Marshal(map[string]string{
"StatementId": tt.sid, "Action": "lambda:InvokeFunction",
"Principal": "s3.amazonaws.com", "SourceArn": tt.sourceArn,
- })
+ }, jsonx.V1())
require.NoError(t, err)
addRec := callInMemoryHandler(t, h, http.MethodPost, "/2015-03-31/functions/fn/policy", string(reqBody))
@@ -529,7 +531,7 @@ func TestPermission_PolicyJSONEscapesValues(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var out lambda.GetPolicyOutput
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &out, jsonx.V1()))
require.NotNil(t, out.Policy)
type statement struct {
@@ -540,7 +542,7 @@ func TestPermission_PolicyJSONEscapesValues(t *testing.T) {
var doc struct {
Statement []statement `json:"Statement"`
}
- require.NoError(t, json.Unmarshal([]byte(*out.Policy), &doc))
+ require.NoError(t, json.Unmarshal([]byte(*out.Policy), &doc, jsonx.V1()))
require.Len(t, doc.Statement, 1)
assert.Equal(t, tt.sid, doc.Statement[0].Sid)
assert.Equal(t, tt.sourceArn, doc.Statement[0].Condition["ArnLike"]["AWS:SourceArn"])
diff --git a/services/lambda/persistence.go b/services/lambda/persistence.go
index 74141f40dc..b79df07bdc 100644
--- a/services/lambda/persistence.go
+++ b/services/lambda/persistence.go
@@ -2,10 +2,12 @@ package lambda
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -160,7 +162,7 @@ type backendSnapshot struct {
// b.ephemeralRegistry (codeSigningConfigs,
// provisionedConcurrencies) are deliberately NOT included here -- they
// were never persisted before this refactor and must stay that way.
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
// The fields below have no pure, self-contained identity (see
// store_setup.go's exclusion list) so they cannot become store.Tables, but
// they WERE persisted before this refactor and must remain so.
@@ -258,7 +260,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.region,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "persistence: snapshot marshal failed", "service", "lambda", "error", err)
@@ -382,7 +384,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreFunctionsFromDTO restores b.functions from its "functions" DTO
// entry in tables (gopherstack-rluhj; b.functions is not on b.registry, see
// store_setup.go's package doc and functionConfigurationSnapshot above).
-func (b *InMemoryBackend) restoreFunctionsFromDTO(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreFunctionsFromDTO(tables map[string]jsontext.Value) error {
funcDTOReg := store.NewRegistry()
funcDTOs := store.Register(funcDTOReg, "functions", store.New(functionConfigurationSnapshotKey))
@@ -403,7 +405,7 @@ func (b *InMemoryBackend) restoreFunctionsFromDTO(tables map[string]json.RawMess
// restorePermissionsFromDTO restores b.permissions from its "permissions"
// DTO entry in tables (b.permissions is not on b.registry, see
// store_setup.go's package doc).
-func (b *InMemoryBackend) restorePermissionsFromDTO(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restorePermissionsFromDTO(tables map[string]jsontext.Value) error {
permDTOReg := store.NewRegistry()
permDTOs := store.Register(permDTOReg, "permissions", store.New(permissionSnapshotKey))
diff --git a/services/lambda/provisioned_concurrency_test.go b/services/lambda/provisioned_concurrency_test.go
index f53503851f..584dfa3d4b 100644
--- a/services/lambda/provisioned_concurrency_test.go
+++ b/services/lambda/provisioned_concurrency_test.go
@@ -1,7 +1,8 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -139,7 +141,7 @@ func TestPutProvisionedConcurrencyConfig(t *testing.T) {
}
var out lambda.ProvisionedConcurrencyConfig
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantRequested, out.RequestedProvisionedConcurrentExecutions)
assert.Equal(t, tt.wantRequested, out.AllocatedProvisionedConcurrentExecutions)
assert.Equal(t, "READY", out.Status)
@@ -233,7 +235,7 @@ func TestGetProvisionedConcurrencyConfig(t *testing.T) {
}
var out lambda.ProvisionedConcurrencyConfig
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantRequested, out.RequestedProvisionedConcurrentExecutions)
assert.Equal(t, "READY", out.Status)
})
@@ -407,7 +409,7 @@ func TestListProvisionedConcurrencyConfigs(t *testing.T) {
}
var out lambda.ListProvisionedConcurrencyConfigsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.ProvisionedConcurrencyConfigs, tt.wantCount)
})
}
@@ -469,7 +471,7 @@ func TestProvisionedConcurrency_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, putRec.Code)
var pc lambda.ProvisionedConcurrencyConfig
- require.NoError(t, json.NewDecoder(putRec.Body).Decode(&pc))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(putRec.Body), &pc, jsonx.V1()))
assert.Equal(t, 5, pc.RequestedProvisionedConcurrentExecutions)
assert.Equal(t, "READY", pc.Status)
@@ -479,7 +481,7 @@ func TestProvisionedConcurrency_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getPC lambda.ProvisionedConcurrencyConfig
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&getPC))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &getPC, jsonx.V1()))
assert.Equal(t, 5, getPC.RequestedProvisionedConcurrentExecutions)
// List
@@ -488,7 +490,7 @@ func TestProvisionedConcurrency_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut lambda.ListProvisionedConcurrencyConfigsOutput
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
assert.Len(t, listOut.ProvisionedConcurrencyConfigs, 1)
// Delete
@@ -500,7 +502,7 @@ func TestProvisionedConcurrency_Lifecycle(t *testing.T) {
listRec2 := callInMemoryHandler(t, h, http.MethodGet,
"/2019-09-30/functions/pc-fn/provisioned-concurrency", "")
var listOut2 lambda.ListProvisionedConcurrencyConfigsOutput
- require.NoError(t, json.NewDecoder(listRec2.Body).Decode(&listOut2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec2.Body), &listOut2, jsonx.V1()))
assert.Empty(t, listOut2.ProvisionedConcurrencyConfigs)
}
@@ -549,7 +551,7 @@ func TestProvisionedConcurrency_StatusReady(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.InDelta(t, float64(5), out["RequestedProvisionedConcurrentExecutions"], 0)
assert.InDelta(t, float64(5), out["AllocatedProvisionedConcurrentExecutions"], 0)
assert.InDelta(t, float64(5), out["AvailableProvisionedConcurrentExecutions"], 0)
@@ -587,7 +589,7 @@ func TestProvisionedConcurrency_List_IncludesAllQualifiers(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var out lambda.ListProvisionedConcurrencyConfigsOutput
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &out, jsonx.V1()))
assert.Len(t, out.ProvisionedConcurrencyConfigs, 2)
}
@@ -658,7 +660,7 @@ func TestProvisionedConcurrency_FullCRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var pcOut lambda.ProvisionedConcurrencyConfig
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pcOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pcOut, jsonx.V1()))
assert.Equal(t, 5, pcOut.RequestedProvisionedConcurrentExecutions)
assert.Equal(t, "READY", pcOut.Status)
@@ -673,7 +675,7 @@ func TestProvisionedConcurrency_FullCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listOut lambda.ListProvisionedConcurrencyConfigsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.ProvisionedConcurrencyConfigs, 1)
// DeleteProvisionedConcurrencyConfig.
diff --git a/services/lambda/publish_latest_published_test.go b/services/lambda/publish_latest_published_test.go
index 0e787f689f..c243a051e3 100644
--- a/services/lambda/publish_latest_published_test.go
+++ b/services/lambda/publish_latest_published_test.go
@@ -1,7 +1,7 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestPublishVersion_PublishTo(t *testing.T) {
@@ -44,13 +46,13 @@ func TestPublishVersion_PublishTo(t *testing.T) {
}
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, tt.wantVer, got["Version"])
assert.Contains(t, got["FunctionArn"], ":"+tt.wantVer)
rec = callInMemoryHandler(t, h, http.MethodPost, "/2015-03-31/functions/pub-fn/versions", tt.body)
require.Equal(t, http.StatusCreated, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
if tt.wantVer == "$LATEST.PUBLISHED" {
assert.Equal(t, tt.wantVer, got["Version"], "republish keeps the single version")
diff --git a/services/lambda/snapstart_extra_test.go b/services/lambda/snapstart_extra_test.go
index 8a1baa6f02..ff80310a03 100644
--- a/services/lambda/snapstart_extra_test.go
+++ b/services/lambda/snapstart_extra_test.go
@@ -1,13 +1,15 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -62,7 +64,7 @@ func TestSnapStart_ReportedOnPublishedVersion(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var ver lambda.FunctionVersion
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&ver))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &ver, jsonx.V1()))
require.NotNil(t, ver.SnapStart)
assert.Equal(t, "PublishedVersions", ver.SnapStart.ApplyOn)
assert.Equal(t, "On", ver.SnapStart.OptimizationStatus)
@@ -85,6 +87,6 @@ func TestSnapStart_OmittedWhenUnset(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var ver lambda.FunctionVersion
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&ver))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &ver, jsonx.V1()))
assert.Nil(t, ver.SnapStart)
}
diff --git a/services/lambda/store_test.go b/services/lambda/store_test.go
index b9fa96403c..1ae5fbbaf3 100644
--- a/services/lambda/store_test.go
+++ b/services/lambda/store_test.go
@@ -3,7 +3,7 @@ package lambda_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -596,7 +597,7 @@ func TestBuildURLEventPayload(t *testing.T) {
require.NoError(t, err)
var event map[string]any
- require.NoError(t, json.Unmarshal(payload, &event))
+ require.NoError(t, json.Unmarshal(payload, &event, jsonx.V1()))
if tt.wantVersion != "" {
assert.Equal(t, tt.wantVersion, event["version"])
diff --git a/services/lambda/tags_test.go b/services/lambda/tags_test.go
index a10b54ac09..91903620ca 100644
--- a/services/lambda/tags_test.go
+++ b/services/lambda/tags_test.go
@@ -2,13 +2,15 @@ package lambda_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -26,7 +28,7 @@ func TestTags_TagAndListAndUntag(t *testing.T) {
getRec := callInMemoryHandler(t, h, http.MethodGet,
"/2015-03-31/functions/tags-fn", "")
var out lambda.GetFunctionOutput
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &out, jsonx.V1()))
fnARN := out.Configuration.FunctionArn
// Tag
@@ -41,7 +43,7 @@ func TestTags_TagAndListAndUntag(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var tagsOut map[string]any
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&tagsOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &tagsOut, jsonx.V1()))
tags := tagsOut["Tags"].(map[string]any)
assert.Equal(t, "prod", tags["env"])
assert.Equal(t, "platform", tags["team"])
@@ -57,7 +59,7 @@ func TestTags_TagAndListAndUntag(t *testing.T) {
require.Equal(t, http.StatusOK, listRec2.Code)
var tagsOut2 map[string]any
- require.NoError(t, json.NewDecoder(listRec2.Body).Decode(&tagsOut2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec2.Body), &tagsOut2, jsonx.V1()))
tags2, _ := tagsOut2["Tags"].(map[string]any)
_, hasEnv := tags2["env"]
assert.False(t, hasEnv)
@@ -85,7 +87,7 @@ func TestGetFunction_ReturnsTopLevelTags(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &out, jsonx.V1()))
rawTags, ok := out["Tags"].(map[string]any)
require.True(t, ok, "GetFunctionOutput must have a top-level Tags field, got: %v", out["Tags"])
@@ -103,7 +105,7 @@ func TestTags_CreateFunctionWithTags(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
// Tags may be in the function config or accessible separately
_ = fn
}
@@ -142,7 +144,7 @@ func TestTaggedFunctions_CreationTags(t *testing.T) {
h, _ := newInMemoryHandler(t)
- tagsJSON, err := json.Marshal(tt.tags)
+ tagsJSON, err := json.Marshal(tt.tags, jsonx.V1())
require.NoError(t, err)
body := `{"FunctionName":"` + tt.fnName + `","PackageType":"Image","Code":{"ImageUri":"x"},` +
diff --git a/services/lambda/test_helpers_test.go b/services/lambda/test_helpers_test.go
index 5550ebb40e..4a05416224 100644
--- a/services/lambda/test_helpers_test.go
+++ b/services/lambda/test_helpers_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"hash/crc32"
"io"
@@ -20,6 +20,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -163,7 +164,7 @@ func lambdaParseBody(t *testing.T, rec *httptest.ResponseRecorder) map[string]an
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/lambda/versions_aliases_test.go b/services/lambda/versions_aliases_test.go
index adc5a70e20..debae34d6b 100644
--- a/services/lambda/versions_aliases_test.go
+++ b/services/lambda/versions_aliases_test.go
@@ -1,7 +1,8 @@
package lambda_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -83,7 +85,7 @@ func TestPublishVersion_Basic(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var v lambda.FunctionVersion
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&v))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &v, jsonx.V1()))
assert.Equal(t, "1", v.Version)
assert.Equal(t, "ver-fn", v.FunctionName)
assert.NotEmpty(t, v.FunctionArn)
@@ -101,7 +103,7 @@ func TestPublishVersion_Increments(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var v lambda.FunctionVersion
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&v))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &v, jsonx.V1()))
assert.Equal(t, strconv.Itoa(i), v.Version)
}
}
@@ -117,7 +119,7 @@ func TestPublishVersion_WithDescription(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var v lambda.FunctionVersion
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&v))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &v, jsonx.V1()))
assert.Equal(t, "my-snapshot", v.Description)
assert.Equal(t, "1", v.Version)
}
@@ -143,7 +145,7 @@ func TestListVersionsByFunction_Empty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.ListVersionsByFunctionOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
// $LATEST is always present
require.NotEmpty(t, out.Versions)
assert.Equal(t, "$LATEST", out.Versions[0].Version)
@@ -163,7 +165,7 @@ func TestListVersionsByFunction_AfterPublish(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out lambda.ListVersionsByFunctionOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
// $LATEST + 2 numbered versions
assert.GreaterOrEqual(t, len(out.Versions), 2)
@@ -196,7 +198,7 @@ func TestPublishVersion_Publish_CreateFunction(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
assert.Equal(t, "1", fn.Version)
}
@@ -211,7 +213,7 @@ func TestFunctionVersion_ARNFormat(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var v lambda.FunctionVersion
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&v))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &v, jsonx.V1()))
// versioned ARN should end with :1
assert.True(t, strings.HasSuffix(v.FunctionArn, ":1"), "expected ARN to end with :1, got %s", v.FunctionArn)
}
@@ -235,7 +237,7 @@ func TestAlias_CreateGetDeleteList(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var alias lambda.FunctionAlias
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&alias))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &alias, jsonx.V1()))
assert.Equal(t, "live", alias.Name)
assert.Equal(t, "1", alias.FunctionVersion)
assert.Equal(t, "prod alias", alias.Description)
@@ -247,7 +249,7 @@ func TestAlias_CreateGetDeleteList(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getAlias lambda.FunctionAlias
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&getAlias))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &getAlias, jsonx.V1()))
assert.Equal(t, "live", getAlias.Name)
// List aliases
@@ -256,7 +258,7 @@ func TestAlias_CreateGetDeleteList(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listOut lambda.ListAliasesOutput
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
assert.Len(t, listOut.Aliases, 1)
// Delete alias
@@ -284,7 +286,7 @@ func TestAlias_RoutingConfig_WeightedTraffic(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var alias lambda.FunctionAlias
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&alias))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &alias, jsonx.V1()))
require.NotNil(t, alias.RoutingConfig)
assert.InDelta(t, 0.1, alias.RoutingConfig.AdditionalVersionWeights["2"], 0.001)
}
@@ -308,7 +310,7 @@ func TestAlias_Update(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var updated lambda.FunctionAlias
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&updated))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &updated, jsonx.V1()))
assert.Equal(t, "2", updated.FunctionVersion)
assert.Equal(t, "now v2", updated.Description)
}
@@ -331,7 +333,7 @@ func TestAlias_UpdateRoutingConfig(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var updated lambda.FunctionAlias
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&updated))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &updated, jsonx.V1()))
require.NotNil(t, updated.RoutingConfig)
assert.InDelta(t, 0.2, updated.RoutingConfig.AdditionalVersionWeights["2"], 0.001)
}
@@ -388,14 +390,14 @@ func TestAlias_RevisionID_Changes(t *testing.T) {
`{"Name":"rev","FunctionVersion":"1"}`)
require.Equal(t, http.StatusCreated, createRec.Code)
var a1 lambda.FunctionAlias
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&a1))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &a1, jsonx.V1()))
updRec := callInMemoryHandler(t, h, http.MethodPut,
"/2015-03-31/functions/alias-rev-fn/aliases/rev",
`{"FunctionVersion":"2"}`)
require.Equal(t, http.StatusOK, updRec.Code)
var a2 lambda.FunctionAlias
- require.NoError(t, json.NewDecoder(updRec.Body).Decode(&a2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(updRec.Body), &a2, jsonx.V1()))
assert.NotEqual(t, a1.RevisionID, a2.RevisionID)
}
@@ -418,7 +420,7 @@ func TestAlias_RevisionID_Precondition(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var a1 lambda.FunctionAlias
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&a1))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &a1, jsonx.V1()))
staleRec := callInMemoryHandler(t, h, http.MethodPut,
"/2015-03-31/functions/alias-precond-fn/aliases/pc",
@@ -428,7 +430,7 @@ func TestAlias_RevisionID_Precondition(t *testing.T) {
getRec := callInMemoryHandler(t, h, http.MethodGet,
"/2015-03-31/functions/alias-precond-fn/aliases/pc", "")
var got lambda.FunctionAlias
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&got))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &got, jsonx.V1()))
assert.Equal(t, "1", got.FunctionVersion, "rejected update must not change the alias")
okRec := callInMemoryHandler(t, h, http.MethodPut,
@@ -459,7 +461,7 @@ func TestPublishVersion_RevisionID_Precondition(t *testing.T) {
listRec := callInMemoryHandler(t, h, http.MethodGet,
"/2015-03-31/functions/publish-precond-fn/versions", "")
var list lambda.ListVersionsByFunctionOutput
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&list))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &list, jsonx.V1()))
assert.Len(t, list.Versions, 1, "rejected publish must not create a numbered version ($LATEST only)")
okRec := callInMemoryHandler(t, h, http.MethodPost,
@@ -502,7 +504,7 @@ func TestVersion_AlwaysLatestInCreateFunction(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
assert.Equal(t, tt.wantVersion, fn.Version,
"CreateFunction response must include Version=%q", tt.wantVersion)
})
@@ -541,7 +543,7 @@ func TestVersion_AlwaysLatestInGetFunctionConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
assert.Equal(t, "$LATEST", fn.Version,
"GetFunctionConfiguration must always return Version=$LATEST for live code")
})
@@ -578,7 +580,7 @@ func TestVersion_LiveFunctionStaysLatestAfterPublish(t *testing.T) {
`{"ImageUri":"x:v2","Publish":true}`)
require.Equal(t, http.StatusOK, rec.Code)
var upd lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&upd))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &upd, jsonx.V1()))
assert.Equal(t, "1", upd.Version,
"UpdateFunctionCode Publish=true response must show numbered version")
},
@@ -599,7 +601,7 @@ func TestVersion_LiveFunctionStaysLatestAfterPublish(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fn))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fn, jsonx.V1()))
assert.Equal(t, "$LATEST", fn.Version,
"GetFunctionConfiguration after publish must still return Version=$LATEST")
})
@@ -636,7 +638,7 @@ func TestAliasRouting(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var alias lambda.FunctionAlias
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &alias))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &alias, jsonx.V1()))
assert.Equal(t, "prod", alias.Name)
assert.Equal(t, v1.Version, alias.FunctionVersion)
@@ -651,7 +653,7 @@ func TestAliasRouting(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listOut lambda.ListAliasesOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.Aliases, 1)
// UpdateAlias — point to v2 with weighted routing 10% to v1.
@@ -664,7 +666,7 @@ func TestAliasRouting(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var updated lambda.FunctionAlias
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, v2.Version, updated.FunctionVersion)
require.NotNil(t, updated.RoutingConfig)
assert.InDelta(t, 0.1, updated.RoutingConfig.AdditionalVersionWeights[v1.Version], 0.001)
diff --git a/services/lambda/zip_handler_test.go b/services/lambda/zip_handler_test.go
index a4631874ca..f027588474 100644
--- a/services/lambda/zip_handler_test.go
+++ b/services/lambda/zip_handler_test.go
@@ -3,13 +3,14 @@ package lambda_test
import (
"archive/zip"
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lambda"
)
@@ -150,7 +151,7 @@ func TestZipHandler_CreateFunction(t *testing.T) {
h, _ := newHandler(t)
- body, err := json.Marshal(tt.input)
+ body, err := json.Marshal(tt.input, jsonx.V1())
require.NoError(t, err)
rec := callHandler(t, h, http.MethodPost, "/2015-03-31/functions", string(body), nil)
@@ -162,7 +163,7 @@ func TestZipHandler_CreateFunction(t *testing.T) {
}
var fn lambda.FunctionConfiguration
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn, jsonx.V1()))
if tt.wantFnName != "" {
assert.Equal(t, tt.wantFnName, fn.FunctionName)
@@ -192,7 +193,7 @@ func TestZipHandler_UpdateFunctionCode(t *testing.T) {
zipBody := func() string {
zipBytes := makeTestZip(t, `def handler(event, context): return "updated"`)
- b, err := json.Marshal(map[string]any{"ZipFile": zipBytes})
+ b, err := json.Marshal(map[string]any{"ZipFile": zipBytes}, jsonx.V1())
require.NoError(t, err)
return string(b)
@@ -244,7 +245,7 @@ func TestZipHandler_UpdateFunctionCode(t *testing.T) {
if tt.wantRevisionID {
var fn lambda.FunctionConfiguration
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn, jsonx.V1()))
assert.NotEmpty(t, fn.RevisionID)
}
})
@@ -292,7 +293,7 @@ func TestZipHandler_UpdateFunctionConfiguration(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var fn lambda.FunctionConfiguration
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &fn, jsonx.V1()))
assert.Equal(t, tt.wantRuntime, fn.Runtime)
assert.Equal(t, tt.wantHandler, fn.Handler)
})
@@ -348,7 +349,7 @@ func TestZipHandler_BuildCodeLocation(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var out lambda.GetFunctionOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.Code)
if tt.wantImageURI != "" {
diff --git a/services/lightsail/error_envelope_test.go b/services/lightsail/error_envelope_test.go
index 57df4b49cc..0b0810ffc0 100644
--- a/services/lightsail/error_envelope_test.go
+++ b/services/lightsail/error_envelope_test.go
@@ -2,7 +2,7 @@ package lightsail_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -16,6 +16,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/lightsail"
@@ -53,7 +54,7 @@ func TestErrorEnvelope_NotFoundDecodesToTypedError(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rawStatus)
var envelope map[string]any
- require.NoError(t, json.Unmarshal(rawBody, &envelope))
+ require.NoError(t, json.Unmarshal(rawBody, &envelope, jsonx.V1()))
require.Equal(t, "NotFoundException", envelope["__type"],
"raw response must carry the JSON body key __type the SDK's getProtocolErrorInfo reads")
msg, ok := envelope["message"]
diff --git a/services/lightsail/handler.go b/services/lightsail/handler.go
index e0d3052076..ff51a6b0cb 100644
--- a/services/lightsail/handler.go
+++ b/services/lightsail/handler.go
@@ -2,7 +2,8 @@ package lightsail
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -13,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -197,8 +199,8 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
return ""
}
- var m map[string]json.RawMessage
- if unmarshalErr := json.Unmarshal(body, &m); unmarshalErr != nil {
+ var m map[string]jsontext.Value
+ if unmarshalErr := json.Unmarshal(body, &m, jsonx.V1()); unmarshalErr != nil {
return ""
}
@@ -210,11 +212,11 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
)
}
-func extractFirstResourceByKeys(body map[string]json.RawMessage, keys ...string) string {
+func extractFirstResourceByKeys(body map[string]jsontext.Value, keys ...string) string {
for _, key := range keys {
if raw, ok := body[key]; ok {
var v string
- if err := json.Unmarshal(raw, &v); err == nil && v != "" {
+ if err := json.Unmarshal(raw, &v, jsonx.V1()); err == nil && v != "" {
return v
}
}
@@ -292,7 +294,7 @@ func (h *Handler) handleError(ctx context.Context, c *echo.Context, action strin
}
body := map[string]string{"__type": errType, "message": err.Error()}
- payload, _ := json.Marshal(body)
+ payload, _ := json.Marshal(body, jsonx.V1())
c.Response().Header().Set("Content-Type", "application/x-amz-json-1.1")
@@ -306,7 +308,7 @@ func decodeBody[T any](body []byte) (*T, error) {
var v T
if len(body) > 0 {
- if err := json.Unmarshal(body, &v); err != nil {
+ if err := json.Unmarshal(body, &v, jsonx.V1()); err != nil {
return nil, validationError("malformed request body: " + err.Error())
}
}
@@ -318,7 +320,7 @@ func decodeBody[T any](body []byte) (*T, error) {
// exception (marshaling our own well-typed wire structs should never
// fail).
func marshalResponse(v any) ([]byte, error) {
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return nil, serviceError("lightsail: marshal response: " + err.Error())
}
diff --git a/services/lightsail/multi_region_test.go b/services/lightsail/multi_region_test.go
index b8107c369f..615a342aed 100644
--- a/services/lightsail/multi_region_test.go
+++ b/services/lightsail/multi_region_test.go
@@ -1,7 +1,7 @@
package lightsail_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lightsail"
)
@@ -37,7 +38,7 @@ func lightsailCall(t *testing.T, h *lightsail.Handler, region, op, body string)
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
out := map[string]any{}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/lightsail/persistence.go b/services/lightsail/persistence.go
index ddd806cc0f..a0d9bebfd5 100644
--- a/services/lightsail/persistence.go
+++ b/services/lightsail/persistence.go
@@ -2,7 +2,7 @@ package lightsail
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -18,14 +18,14 @@ const lightsailSnapshotVersion = 1
// backendSnapshot is the top-level on-disk shape for the Lightsail backend.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- DefaultKeyPair *KeyPair `json:"defaultKeyPair,omitempty"`
- Partner *PartnerProfile `json:"partner,omitempty"`
- AccountID string `json:"accountId"`
- Region string `json:"region"`
- Version int `json:"version"`
- NextAccessKeySeq int `json:"nextAccessKeySeq"`
- VpcPeered bool `json:"vpcPeered"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ DefaultKeyPair *KeyPair `json:"defaultKeyPair,omitempty"`
+ Partner *PartnerProfile `json:"partner,omitempty"`
+ AccountID string `json:"accountId"`
+ Region string `json:"region"`
+ Version int `json:"version"`
+ NextAccessKeySeq int `json:"nextAccessKeySeq"`
+ VpcPeered bool `json:"vpcPeered"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/lightsail/sdk_roundtrip_field_fixes_test.go b/services/lightsail/sdk_roundtrip_field_fixes_test.go
index eeba85eb37..f20fa02f69 100644
--- a/services/lightsail/sdk_roundtrip_field_fixes_test.go
+++ b/services/lightsail/sdk_roundtrip_field_fixes_test.go
@@ -1,7 +1,8 @@
package lightsail_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"time"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/lightsail"
)
@@ -160,22 +162,22 @@ func testGetRelationalDatabaseEventsDuration(t *testing.T) {
func backdateOldestEvent(t *testing.T, backend *lightsail.InMemoryBackend, dbName string, when time.Time) {
t.Helper()
- type snapshot = map[string]json.RawMessage
+ type snapshot = map[string]jsontext.Value
raw := backend.Snapshot(t.Context())
require.NotEmpty(t, raw)
var snap snapshot
- require.NoError(t, json.Unmarshal(raw, &snap))
+ require.NoError(t, json.Unmarshal(raw, &snap, jsonx.V1()))
var tables snapshot
- require.NoError(t, json.Unmarshal(snap["tables"], &tables))
+ require.NoError(t, json.Unmarshal(snap["tables"], &tables, jsonx.V1()))
// Decoded as untyped maps, not lightsail.RelationalDatabase, so this
// edit doesn't need (and musttag doesn't demand) a json tag on an
// internal persistence struct that intentionally has none.
var dbs []map[string]any
- require.NoError(t, json.Unmarshal(tables["databases"], &dbs))
+ require.NoError(t, json.Unmarshal(tables["databases"], &dbs, jsonx.V1()))
require.Len(t, dbs, 1)
require.Equal(t, dbName, dbs[0]["Name"])
@@ -187,15 +189,15 @@ func backdateOldestEvent(t *testing.T, backend *lightsail.InMemoryBackend, dbNam
require.True(t, ok)
event["CreatedAt"] = when.Format(time.RFC3339Nano)
- newDBs, err := json.Marshal(dbs)
+ newDBs, err := json.Marshal(dbs, jsonx.V1())
require.NoError(t, err)
tables["databases"] = newDBs
- newTables, err := json.Marshal(tables)
+ newTables, err := json.Marshal(tables, jsonx.V1())
require.NoError(t, err)
snap["tables"] = newTables
- newSnap, err := json.Marshal(snap)
+ newSnap, err := json.Marshal(snap, jsonx.V1())
require.NoError(t, err)
require.NoError(t, backend.Restore(t.Context(), newSnap))
diff --git a/services/macie2/handler_administrator_test.go b/services/macie2/handler_administrator_test.go
index 5874cae533..4fdab48fe8 100644
--- a/services/macie2/handler_administrator_test.go
+++ b/services/macie2/handler_administrator_test.go
@@ -1,13 +1,14 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -27,7 +28,7 @@ func TestAdministratorMaster(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Nil(t, resp["administrator"])
},
},
@@ -44,7 +45,7 @@ func TestAdministratorMaster(t *testing.T) {
// GetAdministratorAccount
rec := doRequest(t, h, http.MethodGet, "/administrator", nil)
var adminResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &adminResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &adminResp, jsonx.V1()))
assert.NotNil(t, adminResp["administrator"])
// DisassociateFromAdministratorAccount
@@ -54,7 +55,7 @@ func TestAdministratorMaster(t *testing.T) {
// Verify cleared
rec = doRequest(t, h, http.MethodGet, "/administrator", nil)
var afterResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &afterResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &afterResp, jsonx.V1()))
assert.Nil(t, afterResp["administrator"])
},
},
@@ -72,7 +73,7 @@ func TestAdministratorMaster(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
master, _ := resp["master"].(map[string]any)
assert.Equal(t, "legacy-admin", master["accountId"])
},
@@ -92,7 +93,7 @@ func TestAdministratorMaster(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/master", nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Nil(t, resp["master"])
},
},
diff --git a/services/macie2/handler_allow_lists.go b/services/macie2/handler_allow_lists.go
index 343219a293..23b9150b2f 100644
--- a/services/macie2/handler_allow_lists.go
+++ b/services/macie2/handler_allow_lists.go
@@ -1,13 +1,14 @@
package macie2
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
"strconv"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseAllowListPath(method string, parts []string) (string, string) {
@@ -77,7 +78,7 @@ func (h *Handler) handleCreateAllowList(body []byte) (any, int, error) {
Name string `json:"name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -132,7 +133,7 @@ func (h *Handler) handleUpdateAllowList(id string, body []byte) (any, int, error
Name string `json:"name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
diff --git a/services/macie2/handler_allow_lists_test.go b/services/macie2/handler_allow_lists_test.go
index 63f55e96e3..ee4b6d594e 100644
--- a/services/macie2/handler_allow_lists_test.go
+++ b/services/macie2/handler_allow_lists_test.go
@@ -1,7 +1,7 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"regexp"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -37,7 +38,7 @@ func TestMacie2_AllowLists(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]string
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["arn"])
assert.NotEmpty(t, resp["id"])
assert.Contains(t, resp["arn"], "arn:aws:macie2:")
@@ -54,7 +55,7 @@ func TestMacie2_AllowLists(t *testing.T) {
"description": "my desc",
})
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["id"]
},
@@ -63,7 +64,7 @@ func TestMacie2_AllowLists(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "my-list", resp["name"])
assert.Equal(t, "my desc", resp["description"])
assert.NotNil(t, resp["criteria"])
@@ -89,7 +90,7 @@ func TestMacie2_AllowLists(t *testing.T) {
"criteria": map[string]any{"regex": "orig"},
})
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["id"]
},
@@ -103,7 +104,7 @@ func TestMacie2_AllowLists(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]string
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["arn"])
assert.NotEmpty(t, resp["id"])
},
@@ -116,7 +117,7 @@ func TestMacie2_AllowLists(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp, "allowLists")
},
},
@@ -130,7 +131,7 @@ func TestMacie2_AllowLists(t *testing.T) {
"criteria": map[string]any{"regex": ".+"},
})
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["id"]
},
@@ -173,7 +174,7 @@ func createTestAllowListARN(t *testing.T, h *macie2.Handler) string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["arn"]
}
@@ -195,7 +196,7 @@ func TestAllowListARNShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp["arn"]
require.NotEmpty(t, arn, "CreateAllowList must return arn")
@@ -213,7 +214,7 @@ func TestListAllowListsEmptyNotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
v, ok := resp["allowLists"]
require.True(t, ok, "response must contain allowLists key")
diff --git a/services/macie2/handler_automated_discovery.go b/services/macie2/handler_automated_discovery.go
index 6d31bbfee6..a364156b10 100644
--- a/services/macie2/handler_automated_discovery.go
+++ b/services/macie2/handler_automated_discovery.go
@@ -1,10 +1,12 @@
package macie2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"slices"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseAutomatedDiscoveryPath(method string, parts []string) (string, string) {
@@ -75,7 +77,7 @@ func (h *Handler) handleUpdateAutomatedDiscoveryConfiguration(body []byte) (int,
Status string `json:"status"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -111,7 +113,7 @@ func (h *Handler) handleBatchUpdateAutomatedDiscoveryAccounts(body []byte) (int,
Accounts []AutoDiscoveryAccountUpdate `json:"accounts"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
diff --git a/services/macie2/handler_automated_discovery_test.go b/services/macie2/handler_automated_discovery_test.go
index ed67b3a651..86d2751d07 100644
--- a/services/macie2/handler_automated_discovery_test.go
+++ b/services/macie2/handler_automated_discovery_test.go
@@ -1,13 +1,14 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -28,7 +29,7 @@ func TestAutomatedDiscovery(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var cfg map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cfg))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cfg, jsonx.V1()))
assert.Equal(t, "DISABLED", cfg["status"])
// UpdateAutomatedDiscoveryConfiguration
@@ -41,7 +42,7 @@ func TestAutomatedDiscovery(t *testing.T) {
// Verify
rec = doRequest(t, h, http.MethodGet, "/automated-discovery/configuration", nil)
var updated map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "ENABLED", updated["status"])
assert.Equal(t, "NEW", updated["autoEnableOrganizationMembers"])
},
@@ -65,7 +66,7 @@ func TestAutomatedDiscovery(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
items, _ := listResp["items"].([]any)
assert.Len(t, items, 2)
},
diff --git a/services/macie2/handler_buckets.go b/services/macie2/handler_buckets.go
index a9740f9b53..fe0150adb2 100644
--- a/services/macie2/handler_buckets.go
+++ b/services/macie2/handler_buckets.go
@@ -1,8 +1,10 @@
package macie2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseDatasourcesPath(method string, parts []string) (string, string) {
@@ -71,7 +73,7 @@ func (h *Handler) handleDescribeBuckets(body []byte) (any, int, error) {
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
@@ -104,7 +106,7 @@ func (h *Handler) handleGetBucketStatistics(body []byte) (any, int, error) {
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
@@ -208,7 +210,7 @@ func (h *Handler) handleSearchResources(body []byte) (any, int, error) {
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
diff --git a/services/macie2/handler_buckets_test.go b/services/macie2/handler_buckets_test.go
index 3e7b8f0e3a..7b43425e5a 100644
--- a/services/macie2/handler_buckets_test.go
+++ b/services/macie2/handler_buckets_test.go
@@ -1,7 +1,7 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -30,7 +31,7 @@ func TestBucketsAndBatchCustomDataIdentifiers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
buckets, _ := resp["buckets"].([]any)
assert.Empty(t, buckets)
},
@@ -44,7 +45,7 @@ func TestBucketsAndBatchCustomDataIdentifiers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
count, _ := resp["bucketCount"].(float64)
assert.InDelta(t, 0, count, 0.0001)
},
@@ -62,7 +63,7 @@ func TestBucketsAndBatchCustomDataIdentifiers(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
cdiID := createResp["customDataIdentifierId"]
// BatchGetCustomDataIdentifiers
@@ -72,7 +73,7 @@ func TestBucketsAndBatchCustomDataIdentifiers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var batchResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &batchResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &batchResp, jsonx.V1()))
items, _ := batchResp["customDataIdentifiers"].([]any)
assert.Len(t, items, 1)
},
@@ -88,7 +89,7 @@ func TestBucketsAndBatchCustomDataIdentifiers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results, _ := resp["matchingResources"].([]any)
assert.Empty(t, results)
},
@@ -156,7 +157,7 @@ func describeBuckets(t *testing.T, h *macie2.Handler, criteria map[string]any) [
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
buckets, ok := resp["buckets"].([]any)
if !ok {
@@ -173,7 +174,7 @@ func getBucketStatistics(t *testing.T, h *macie2.Handler) map[string]any {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
diff --git a/services/macie2/handler_classification_jobs.go b/services/macie2/handler_classification_jobs.go
index d270b5d1b2..b7b6284620 100644
--- a/services/macie2/handler_classification_jobs.go
+++ b/services/macie2/handler_classification_jobs.go
@@ -1,13 +1,14 @@
package macie2
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
"slices"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseJobPath(method string, parts []string) (string, string) {
@@ -148,7 +149,7 @@ func (h *Handler) handleCreateClassificationJob(body []byte) (any, int, error) {
InitialRun bool `json:"initialRun"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -211,7 +212,7 @@ func (h *Handler) handleListClassificationJobs(body []byte) (any, int, error) {
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
@@ -241,7 +242,7 @@ func (h *Handler) handleUpdateClassificationJob(jobID string, body []byte) (int,
JobStatus string `json:"jobStatus"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -278,7 +279,7 @@ func (h *Handler) handlePutClassificationExportConfiguration(body []byte) (int,
Configuration *ClassificationExportConfig `json:"configuration"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -321,7 +322,7 @@ func (h *Handler) handleUpdateClassificationScope(scopeID string, body []byte) (
S3 *ClassificationScopeS3Update `json:"s3"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
diff --git a/services/macie2/handler_classification_jobs_test.go b/services/macie2/handler_classification_jobs_test.go
index dc01b1354f..f381f910f4 100644
--- a/services/macie2/handler_classification_jobs_test.go
+++ b/services/macie2/handler_classification_jobs_test.go
@@ -1,13 +1,14 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -26,7 +27,7 @@ func TestUpdateClassificationScope_ExcludedBucketsSurviveIndependentAdds(t *test
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
scopes, _ := listResp["classificationScopes"].([]any)
require.Len(t, scopes, 1)
scope0, _ := scopes[0].(map[string]any)
@@ -59,7 +60,7 @@ func TestUpdateClassificationScope_ExcludedBucketsSurviveIndependentAdds(t *test
require.Equal(t, http.StatusOK, rec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
s3, ok := got["s3"].(map[string]any)
require.True(t, ok, "got %#v", got)
excludes, ok := s3["excludes"].(map[string]any)
@@ -79,7 +80,7 @@ func TestUpdateClassificationScope_ExcludedBucketsSurviveIndependentAdds(t *test
require.Equal(t, http.StatusOK, rec.Code)
rec = doRequest(t, h, http.MethodGet, "/classification-scopes/"+scopeID, nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
s3, ok = got["s3"].(map[string]any)
require.True(t, ok)
excludes, ok = s3["excludes"].(map[string]any)
@@ -111,7 +112,7 @@ func TestClassificationJobs(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
jobID := createResp["jobId"]
assert.NotEmpty(t, jobID)
assert.Equal(t, "RUNNING", createResp["jobStatus"])
@@ -123,7 +124,7 @@ func TestClassificationJobs(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, jobID, descResp["jobId"])
assert.Equal(t, "ONE_TIME", descResp["jobType"])
assert.Equal(t, "test-job", descResp["name"])
@@ -135,7 +136,7 @@ func TestClassificationJobs(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
items, _ := listResp["items"].([]any)
require.Len(t, items, 1)
item0 := items[0].(map[string]any)
@@ -155,7 +156,7 @@ func TestClassificationJobs(t *testing.T) {
// Verify update
rec = doRequest(t, h, http.MethodGet, "/jobs/"+jobID, nil)
var updated map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "CANCELLED", updated["jobStatus"])
},
},
@@ -184,11 +185,11 @@ func TestClassificationJobs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rec = doRequest(t, h, http.MethodGet, "/jobs/"+resp["jobId"], nil)
var desc map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc, jsonx.V1()))
assert.Equal(t, "IDLE", desc["jobStatus"])
},
},
@@ -219,7 +220,7 @@ func TestClassificationConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
cfg, _ := getResp["configuration"].(map[string]any)
assert.Nil(t, cfg["s3Destination"])
@@ -238,7 +239,7 @@ func TestClassificationConfig(t *testing.T) {
// Verify
rec = doRequest(t, h, http.MethodGet, "/classification-export-configuration", nil)
var updated map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
updCfg, _ := updated["configuration"].(map[string]any)
s3Dest, _ := updCfg["s3Destination"].(map[string]any)
assert.Equal(t, "my-export-bucket", s3Dest["bucketName"])
@@ -254,7 +255,7 @@ func TestClassificationConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
scopes, _ := listResp["classificationScopes"].([]any)
require.Len(t, scopes, 1)
@@ -267,7 +268,7 @@ func TestClassificationConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var scopeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &scopeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &scopeResp, jsonx.V1()))
assert.Equal(t, scopeID, scopeResp["id"])
// UpdateClassificationScope
@@ -321,7 +322,7 @@ func createTestJob(t *testing.T, h *macie2.Handler, name, jobType string) (strin
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["jobId"], resp["jobArn"]
}
@@ -351,7 +352,7 @@ func TestListClassificationJobsFilterAndPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["items"].([]any)
require.True(t, ok)
require.Len(t, items, 1)
@@ -374,7 +375,7 @@ func TestListClassificationJobsFilterAndPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["items"].([]any)
require.True(t, ok)
@@ -393,7 +394,7 @@ func TestListClassificationJobsFilterAndPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["items"].([]any)
require.True(t, ok)
require.Len(t, items, 1)
@@ -406,7 +407,7 @@ func TestListClassificationJobsFilterAndPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
items2, ok := resp2["items"].([]any)
require.True(t, ok)
require.Len(t, items2, 1)
@@ -431,7 +432,7 @@ func TestClassificationJobUserPausedDetails(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var paused map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &paused))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &paused, jsonx.V1()))
details, ok := paused["userPausedDetails"].(map[string]any)
require.True(t, ok, "userPausedDetails must be present while USER_PAUSED")
assert.NotEmpty(t, details["jobPausedAt"])
@@ -444,7 +445,7 @@ func TestClassificationJobUserPausedDetails(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resumed map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resumed))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resumed, jsonx.V1()))
assert.Nil(t, resumed["userPausedDetails"], "userPausedDetails must clear once no longer USER_PAUSED")
}
@@ -544,7 +545,7 @@ func TestClassificationJobTagging(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags, ok := resp["tags"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "test", tags["env"])
@@ -567,13 +568,13 @@ func TestClassificationJobCreateFieldsRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var al map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &al))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &al, jsonx.V1()))
rec = doRequest(t, h, http.MethodPost, "/custom-data-identifiers", map[string]any{"name": "cdi", "regex": `.+`})
require.Equal(t, http.StatusOK, rec.Code)
var cdi map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cdi))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cdi, jsonx.V1()))
rec = doRequest(t, h, http.MethodPost, "/jobs", map[string]any{
"name": "full-field-job",
@@ -589,13 +590,13 @@ func TestClassificationJobCreateFieldsRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
rec = doRequest(t, h, http.MethodGet, "/jobs/"+created["jobId"], nil)
require.Equal(t, http.StatusOK, rec.Code)
var desc map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc, jsonx.V1()))
allowListIDs, ok := desc["allowListIds"].([]any)
require.True(t, ok)
diff --git a/services/macie2/handler_custom_data_identifiers.go b/services/macie2/handler_custom_data_identifiers.go
index 24ded90b5b..bf1e9fee8a 100644
--- a/services/macie2/handler_custom_data_identifiers.go
+++ b/services/macie2/handler_custom_data_identifiers.go
@@ -1,11 +1,12 @@
package macie2
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseCustomDataIDPath(method string, parts []string) (string, string) {
@@ -98,7 +99,7 @@ func (h *Handler) handleCreateCustomDataID(body []byte) (any, int, error) {
SeverityLevels []SeverityLevel `json:"severityLevels"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -152,7 +153,7 @@ func (h *Handler) handleListCustomDataIDs(body []byte) (any, int, error) {
MaxResults *int32 `json:"maxResults"`
NextToken string `json:"nextToken"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
limit := 0
if req.MaxResults != nil {
@@ -181,7 +182,7 @@ func (h *Handler) handleTestCustomDataID(body []byte) (any, int, error) {
Keywords []string `json:"keywords"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -214,7 +215,7 @@ func (h *Handler) handleBatchGetCustomDataIdentifiers(body []byte) (any, int, er
IDs []string `json:"ids"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -250,7 +251,7 @@ func (h *Handler) handleListManagedDataIdentifiers(body []byte) (any, int, error
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
diff --git a/services/macie2/handler_custom_data_identifiers_test.go b/services/macie2/handler_custom_data_identifiers_test.go
index fa424d00ab..467ab93b49 100644
--- a/services/macie2/handler_custom_data_identifiers_test.go
+++ b/services/macie2/handler_custom_data_identifiers_test.go
@@ -1,13 +1,14 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -35,7 +36,7 @@ func TestMacie2_CustomDataIdentifiers(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]string
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["customDataIdentifierId"])
},
},
@@ -50,7 +51,7 @@ func TestMacie2_CustomDataIdentifiers(t *testing.T) {
"keywords": []string{"phone", "tel"},
})
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["customDataIdentifierId"]
},
@@ -59,7 +60,7 @@ func TestMacie2_CustomDataIdentifiers(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "phone-detector", resp["name"])
assert.Equal(t, "detects phone numbers", resp["description"])
assert.NotEmpty(t, resp["arn"])
@@ -78,7 +79,7 @@ func TestMacie2_CustomDataIdentifiers(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.InDelta(t, float64(2), resp["matchCount"], 0.001)
},
},
@@ -91,7 +92,7 @@ func TestMacie2_CustomDataIdentifiers(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp, "items")
},
},
@@ -104,7 +105,7 @@ func TestMacie2_CustomDataIdentifiers(t *testing.T) {
"regex": `.+`,
})
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["customDataIdentifierId"]
},
@@ -208,7 +209,7 @@ func TestMacie2_CustomDataIdentifierWireShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id := resp["customDataIdentifierId"]
doRequest(t, h, http.MethodDelete, "/custom-data-identifiers/"+id, nil)
@@ -267,14 +268,14 @@ func TestCustomDataIdentifierDeletedField(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
id := created["customDataIdentifierId"]
rec2 := doRequest(t, h, http.MethodGet, "/custom-data-identifiers/"+id, nil)
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
deleted, hasDeleted := resp["deleted"]
require.True(t, hasDeleted, "GetCustomDataIdentifier must include a 'deleted' field")
assert.Equal(t, false, deleted)
@@ -286,7 +287,7 @@ func TestCustomDataIdentifierDeletedField(t *testing.T) {
require.Equal(t, http.StatusOK, rec4.Code, "Get on a soft-deleted identifier must not 404")
var afterDelete map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &afterDelete))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &afterDelete, jsonx.V1()))
assert.Equal(t, true, afterDelete["deleted"])
// BatchGetCustomDataIdentifiers must also return the soft-deleted
@@ -297,7 +298,7 @@ func TestCustomDataIdentifierDeletedField(t *testing.T) {
require.Equal(t, http.StatusOK, batchRec.Code)
var batchResp map[string]any
- require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batchResp))
+ require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batchResp, jsonx.V1()))
items, ok := batchResp["customDataIdentifiers"].([]any)
require.True(t, ok)
require.Len(t, items, 1)
@@ -328,13 +329,13 @@ func TestCustomDataIdentifierSeverityLevels(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
rec2 := doRequest(t, h, http.MethodGet, "/custom-data-identifiers/"+created["customDataIdentifierId"], nil)
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
levels, ok := resp["severityLevels"].([]any)
require.True(t, ok)
require.Len(t, levels, 2)
@@ -355,7 +356,7 @@ func TestListCustomDataIDsEmptyNotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
v, ok := resp["items"]
require.True(t, ok, "response must contain items key")
diff --git a/services/macie2/handler_enablement.go b/services/macie2/handler_enablement.go
index f5900897e5..3930578674 100644
--- a/services/macie2/handler_enablement.go
+++ b/services/macie2/handler_enablement.go
@@ -1,12 +1,13 @@
package macie2
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseMaciePath(method string, parts []string) (string, string) {
@@ -78,7 +79,7 @@ func (h *Handler) handleEnableMacie(body []byte) (int, error) {
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
}
@@ -117,7 +118,7 @@ func (h *Handler) handleUpdateMacieSession(body []byte) (any, int, error) {
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
diff --git a/services/macie2/handler_enablement_test.go b/services/macie2/handler_enablement_test.go
index 0832390b60..6d791bed22 100644
--- a/services/macie2/handler_enablement_test.go
+++ b/services/macie2/handler_enablement_test.go
@@ -1,7 +1,7 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -54,7 +55,7 @@ func TestMacie2_Session(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "ONE_HOUR", resp["findingPublishingFrequency"])
assert.Equal(t, "ENABLED", resp["status"])
assert.NotEmpty(t, resp["serviceRole"])
@@ -132,7 +133,7 @@ func TestDisableMacie_DeletesResources(t *testing.T) {
require.Equal(t, http.StatusOK, jobRec.Code)
var jobResp map[string]string
- require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &jobResp))
+ require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &jobResp, jsonx.V1()))
jobID := jobResp["jobId"]
require.NotEmpty(t, jobID)
@@ -143,7 +144,7 @@ func TestDisableMacie_DeletesResources(t *testing.T) {
require.Equal(t, http.StatusOK, cdiRec.Code)
var cdiResp map[string]string
- require.NoError(t, json.Unmarshal(cdiRec.Body.Bytes(), &cdiResp))
+ require.NoError(t, json.Unmarshal(cdiRec.Body.Bytes(), &cdiResp, jsonx.V1()))
cdiID := cdiResp["customDataIdentifierId"]
require.NotEmpty(t, cdiID)
@@ -154,7 +155,7 @@ func TestDisableMacie_DeletesResources(t *testing.T) {
require.Equal(t, http.StatusOK, allowListRec.Code)
var allowListResp map[string]string
- require.NoError(t, json.Unmarshal(allowListRec.Body.Bytes(), &allowListResp))
+ require.NoError(t, json.Unmarshal(allowListRec.Body.Bytes(), &allowListResp, jsonx.V1()))
allowListID := allowListResp["id"]
require.NotEmpty(t, allowListID)
@@ -297,7 +298,7 @@ func TestSessionTimestampsPresent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for _, field := range []string{"createdAt", "updatedAt"} {
raw, ok := resp[field]
@@ -320,7 +321,7 @@ func TestSessionUpdatedAtAdvances(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var before map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &before))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &before, jsonx.V1()))
createdAt := before["createdAt"].(string)
time.Sleep(1001 * time.Millisecond)
@@ -332,7 +333,7 @@ func TestSessionUpdatedAtAdvances(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var after map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &after))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &after, jsonx.V1()))
assert.Equal(t, createdAt, after["createdAt"].(string), "createdAt must not change after update")
assert.NotEqual(t, after["createdAt"], after["updatedAt"],
diff --git a/services/macie2/handler_findings.go b/services/macie2/handler_findings.go
index b63eec3a12..071159e651 100644
--- a/services/macie2/handler_findings.go
+++ b/services/macie2/handler_findings.go
@@ -1,11 +1,12 @@
package macie2
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseFindingsPath(method string, parts []string) (string, string) {
@@ -83,7 +84,7 @@ func (h *Handler) handleGetFindings(body []byte) (any, int, error) {
FindingIDs []string `json:"findingIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -111,7 +112,7 @@ func (h *Handler) handleListFindings(body []byte) (any, int, error) {
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
@@ -147,7 +148,7 @@ func (h *Handler) handleCreateSampleFindings(body []byte) (int, error) {
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
}
@@ -167,7 +168,7 @@ func (h *Handler) handleGetFindingStatistics(body []byte) (any, int, error) {
Size int `json:"size"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -223,7 +224,7 @@ func (h *Handler) handleGetFindingsPublicationConfiguration() (any, int, error)
func (h *Handler) handlePutFindingsPublicationConfiguration(body []byte) (int, error) {
var cfg FindingsPublicationConfig
- if err := json.Unmarshal(body, &cfg); err != nil {
+ if err := json.Unmarshal(body, &cfg, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
diff --git a/services/macie2/handler_findings_filters.go b/services/macie2/handler_findings_filters.go
index a0ba4f4d7e..86d043084e 100644
--- a/services/macie2/handler_findings_filters.go
+++ b/services/macie2/handler_findings_filters.go
@@ -1,7 +1,7 @@
package macie2
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -9,6 +9,7 @@ import (
"strconv"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseFindingsFilterPath(method string, parts []string) (string, string) {
@@ -80,7 +81,7 @@ func (h *Handler) handleCreateFindingsFilter(body []byte) (any, int, error) {
Name string `json:"name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -138,7 +139,7 @@ func (h *Handler) handleUpdateFindingsFilter(id string, body []byte) (any, int,
Name string `json:"name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
diff --git a/services/macie2/handler_findings_filters_test.go b/services/macie2/handler_findings_filters_test.go
index 1288d9a670..a002eeed77 100644
--- a/services/macie2/handler_findings_filters_test.go
+++ b/services/macie2/handler_findings_filters_test.go
@@ -1,13 +1,14 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -36,7 +37,7 @@ func TestMacie2_FindingsFilters(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]string
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["arn"])
assert.NotEmpty(t, resp["id"])
assert.Contains(t, resp["arn"], "findings-filter")
@@ -53,7 +54,7 @@ func TestMacie2_FindingsFilters(t *testing.T) {
"position": int32(3),
})
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["id"]
},
@@ -62,7 +63,7 @@ func TestMacie2_FindingsFilters(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "test-filter", resp["name"])
assert.Equal(t, "NOOP", resp["action"])
assert.Equal(t, "test desc", resp["description"])
@@ -77,7 +78,7 @@ func TestMacie2_FindingsFilters(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp, "findingsFilterListItems")
},
},
@@ -90,7 +91,7 @@ func TestMacie2_FindingsFilters(t *testing.T) {
"action": "ARCHIVE",
})
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["id"]
},
@@ -130,7 +131,7 @@ func TestListFindingsFiltersEmptyNotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
v, ok := resp["findingsFilterListItems"]
require.True(t, ok, "response must contain findingsFilterListItems key")
diff --git a/services/macie2/handler_findings_test.go b/services/macie2/handler_findings_test.go
index 46c17c0313..190d9ea785 100644
--- a/services/macie2/handler_findings_test.go
+++ b/services/macie2/handler_findings_test.go
@@ -1,13 +1,14 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -46,7 +47,7 @@ func TestMacie2_Findings(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp, "findingIds")
ids, ok := resp["findingIds"].([]any)
@@ -68,7 +69,7 @@ func TestMacie2_Findings(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp, "findings")
},
},
@@ -86,7 +87,7 @@ func TestMacie2_Findings(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp, "countsByGroup")
},
},
@@ -107,7 +108,7 @@ func TestMacie2_Findings(t *testing.T) {
if tt.name == "GetFindings returns findings array" {
listRec := doRequest(t, h, http.MethodPost, "/findings", map[string]any{})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
ids := listResp["findingIds"].([]any)
body = map[string]any{"findingIds": ids}
}
@@ -131,7 +132,7 @@ func TestListFindingsEmptyNotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
v, ok := resp["findingIds"]
require.True(t, ok, "response must contain findingIds key")
@@ -151,7 +152,7 @@ func TestGetFindingsEmptyInputNotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
v, ok := resp["findings"]
require.True(t, ok, "response must contain findings key")
@@ -175,7 +176,7 @@ func TestCreateSampleFindingsFieldShape(t *testing.T) {
listRec := doRequest(t, h, http.MethodPost, "/findings", map[string]any{})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
ids, ok := listResp["findingIds"].([]any)
require.True(t, ok)
require.Len(t, ids, 2)
@@ -184,7 +185,7 @@ func TestCreateSampleFindingsFieldShape(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
findings, ok := getResp["findings"].([]any)
require.True(t, ok)
require.Len(t, findings, 2)
@@ -266,7 +267,7 @@ func TestFindingsPublicationConfig(t *testing.T) {
// Verify
rec = doRequest(t, h, http.MethodGet, "/findings-publication-configuration", nil)
var updated map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
_, hasTopClassification := updated["publishClassificationFindings"]
_, hasTopPolicy := updated["publishPolicyFindings"]
diff --git a/services/macie2/handler_members.go b/services/macie2/handler_members.go
index ab1c595ddf..626d071fee 100644
--- a/services/macie2/handler_members.go
+++ b/services/macie2/handler_members.go
@@ -1,7 +1,7 @@
package macie2
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
@@ -9,6 +9,7 @@ import (
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseMembersPath(method string, parts []string) (string, string) {
@@ -156,7 +157,7 @@ func (h *Handler) handleCreateMember(body []byte) (int, error) {
Tags map[string]string `json:"tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -249,7 +250,7 @@ func (h *Handler) handleUpdateMemberSession(accountID string, body []byte) (int,
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
}
@@ -272,7 +273,7 @@ func (h *Handler) handleCreateInvitations(body []byte) (any, int, error) {
DisableEmailNotification bool `json:"disableEmailNotification"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -291,7 +292,7 @@ func (h *Handler) handleAcceptInvitation(body []byte) (int, error) {
MasterAccount string `json:"masterAccount"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -312,7 +313,7 @@ func (h *Handler) handleDeclineInvitations(body []byte) (any, int, error) {
AccountIDs []string `json:"accountIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
@@ -329,7 +330,7 @@ func (h *Handler) handleDeleteInvitations(body []byte) (any, int, error) {
AccountIDs []string `json:"accountIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
diff --git a/services/macie2/handler_members_test.go b/services/macie2/handler_members_test.go
index f2593080e0..6331f64e13 100644
--- a/services/macie2/handler_members_test.go
+++ b/services/macie2/handler_members_test.go
@@ -1,7 +1,7 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -38,7 +39,7 @@ func TestMembers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getMem map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getMem))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getMem, jsonx.V1()))
assert.Equal(t, "111111111111", getMem["accountId"])
assert.Equal(t, "member@example.com", getMem["email"])
assert.Equal(t, "Created", getMem["relationshipStatus"])
@@ -52,7 +53,7 @@ func TestMembers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
members, _ := listResp["members"].([]any)
assert.Len(t, members, 1)
@@ -63,7 +64,7 @@ func TestMembers(t *testing.T) {
// List after delete
rec = doRequest(t, h, http.MethodGet, "/members", nil)
var afterList map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &afterList))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &afterList, jsonx.V1()))
membersAfter, _ := afterList["members"].([]any)
assert.Empty(t, membersAfter)
},
@@ -87,7 +88,7 @@ func TestMembers(t *testing.T) {
// Verify status changed
rec = doRequest(t, h, http.MethodGet, "/members/222222222222", nil)
var mem map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mem))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mem, jsonx.V1()))
assert.Equal(t, "Removed", mem["relationshipStatus"])
},
},
@@ -111,7 +112,7 @@ func TestMembers(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/members/333333333333", nil)
var mem map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mem))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mem, jsonx.V1()))
assert.Equal(t, "PAUSED", mem["relationshipStatus"])
},
},
@@ -175,7 +176,7 @@ func TestListMembersOnlyAssociatedAndPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
members, ok := resp["members"].([]any)
require.True(t, ok)
require.Len(t, members, 1)
@@ -192,7 +193,7 @@ func TestListMembersOnlyAssociatedAndPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
members, ok := resp["members"].([]any)
require.True(t, ok)
assert.Len(t, members, 2)
@@ -205,7 +206,7 @@ func TestListMembersOnlyAssociatedAndPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
members, ok := resp["members"].([]any)
require.True(t, ok)
require.Len(t, members, 1)
@@ -221,7 +222,7 @@ func TestListMembersOnlyAssociatedAndPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
members2, ok := resp2["members"].([]any)
require.True(t, ok)
require.Len(t, members2, 1)
@@ -255,7 +256,7 @@ func TestInvitations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var countResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &countResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &countResp, jsonx.V1()))
count, _ := countResp["invitationsCount"].(float64)
assert.InDelta(t, 2, count, 0.0001)
@@ -264,7 +265,7 @@ func TestInvitations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
invitations, _ := listResp["invitations"].([]any)
assert.Len(t, invitations, 2)
},
@@ -285,7 +286,7 @@ func TestInvitations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var adminResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &adminResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &adminResp, jsonx.V1()))
admin, _ := adminResp["administrator"].(map[string]any)
assert.Equal(t, "123456789012", admin["accountId"])
},
@@ -306,7 +307,7 @@ func TestInvitations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var declineResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &declineResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &declineResp, jsonx.V1()))
assert.Empty(t, declineResp["unprocessedAccounts"])
// DeleteInvitations
diff --git a/services/macie2/handler_organization.go b/services/macie2/handler_organization.go
index 51a40772aa..3feec867ea 100644
--- a/services/macie2/handler_organization.go
+++ b/services/macie2/handler_organization.go
@@ -1,11 +1,12 @@
package macie2
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseAdminPath(method string, parts []string) (string, string) {
@@ -70,7 +71,7 @@ func (h *Handler) handleEnableOrganizationAdminAccount(body []byte) (int, error)
AdminAccountID string `json:"adminAccountId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
@@ -116,7 +117,7 @@ func (h *Handler) handleUpdateOrganizationConfiguration(body []byte) (int, error
AutoEnable bool `json:"autoEnable"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
diff --git a/services/macie2/handler_organization_test.go b/services/macie2/handler_organization_test.go
index 8db0e3f2c1..096cf8f486 100644
--- a/services/macie2/handler_organization_test.go
+++ b/services/macie2/handler_organization_test.go
@@ -1,13 +1,14 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -34,7 +35,7 @@ func TestOrgAdmin(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
accounts, _ := listResp["adminAccounts"].([]any)
assert.Len(t, accounts, 1)
@@ -45,7 +46,7 @@ func TestOrgAdmin(t *testing.T) {
// Verify removed
rec = doRequest(t, h, http.MethodGet, "/admin", nil)
var afterList map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &afterList))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &afterList, jsonx.V1()))
accountsAfter, _ := afterList["adminAccounts"].([]any)
assert.Empty(t, accountsAfter)
},
@@ -60,7 +61,7 @@ func TestOrgAdmin(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
autoEnable, _ := descResp["autoEnable"].(bool)
assert.False(t, autoEnable)
@@ -73,7 +74,7 @@ func TestOrgAdmin(t *testing.T) {
// Verify change
rec = doRequest(t, h, http.MethodGet, "/admin/configuration", nil)
var updated map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.True(t, updated["autoEnable"].(bool))
},
},
@@ -100,7 +101,7 @@ func TestDescribeOrganizationConfigurationWireShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "autoEnable")
assert.Contains(t, resp, "maxAccountLimitReached")
diff --git a/services/macie2/handler_resource_profiles.go b/services/macie2/handler_resource_profiles.go
index c82c4a95ce..95eca6269a 100644
--- a/services/macie2/handler_resource_profiles.go
+++ b/services/macie2/handler_resource_profiles.go
@@ -1,8 +1,10 @@
package macie2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseResourceProfilesPath(method string, parts []string) (string, string) {
@@ -81,7 +83,7 @@ func (h *Handler) handleUpdateResourceProfile(resourceARN string, body []byte) (
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
}
@@ -117,7 +119,7 @@ func (h *Handler) handleUpdateResourceProfileDetections(resourceARN string, body
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
}
diff --git a/services/macie2/handler_resource_profiles_test.go b/services/macie2/handler_resource_profiles_test.go
index 34f1e182df..fd66781717 100644
--- a/services/macie2/handler_resource_profiles_test.go
+++ b/services/macie2/handler_resource_profiles_test.go
@@ -1,13 +1,14 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -30,7 +31,7 @@ func TestResourceProfiles(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var profile map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &profile, jsonx.V1()))
assert.Equal(t, arn, profile["resourceArn"])
// UpdateResourceProfile
@@ -42,7 +43,7 @@ func TestResourceProfiles(t *testing.T) {
// Verify
rec = doRequest(t, h, http.MethodGet, "/resource-profiles?resourceArn="+arn, nil)
var updated map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.EqualValues(t, 75, updated["sensitivityScore"])
assert.True(t, updated["sensitivityScoreOverridden"].(bool))
},
@@ -59,7 +60,7 @@ func TestResourceProfiles(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var artResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &artResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &artResp, jsonx.V1()))
artifacts, _ := artResp["artifacts"].([]any)
assert.Empty(t, artifacts)
@@ -68,7 +69,7 @@ func TestResourceProfiles(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var detResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &detResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &detResp, jsonx.V1()))
detections, _ := detResp["detections"].([]any)
assert.Empty(t, detections)
diff --git a/services/macie2/handler_reveal_configuration.go b/services/macie2/handler_reveal_configuration.go
index f10c21e53c..fc379b834c 100644
--- a/services/macie2/handler_reveal_configuration.go
+++ b/services/macie2/handler_reveal_configuration.go
@@ -1,12 +1,13 @@
package macie2
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseRevealCfgPath(method string, _ []string) (string, string) {
@@ -57,7 +58,7 @@ func (h *Handler) handleUpdateRevealConfiguration(body []byte) (any, int, error)
} `json:"retrievalConfiguration"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
diff --git a/services/macie2/handler_reveal_configuration_test.go b/services/macie2/handler_reveal_configuration_test.go
index d10f2a64c5..65052be1b4 100644
--- a/services/macie2/handler_reveal_configuration_test.go
+++ b/services/macie2/handler_reveal_configuration_test.go
@@ -1,13 +1,14 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -28,7 +29,7 @@ func TestRevealConfiguration(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
cfg, _ := getResp["configuration"].(map[string]any)
assert.Equal(t, "DISABLED", cfg["status"])
@@ -44,7 +45,7 @@ func TestRevealConfiguration(t *testing.T) {
// Verify
rec = doRequest(t, h, http.MethodGet, "/reveal-configuration", nil)
var updated map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
updCfg, _ := updated["configuration"].(map[string]any)
assert.Equal(t, "ENABLED", updCfg["status"])
assert.Equal(t, "arn:aws:kms:us-east-1:000000000000:key/reveal-key", updCfg["kmsKeyId"])
@@ -93,7 +94,7 @@ func TestSensitiveDataOccurrences(t *testing.T) {
// Get finding IDs
rec := doRequest(t, h, http.MethodPost, "/findings", nil)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
findingIDs, _ := listResp["findingIds"].([]any)
require.NotEmpty(t, findingIDs)
findingID, _ := findingIDs[0].(string)
@@ -107,7 +108,7 @@ func TestSensitiveDataOccurrences(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var avail map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &avail))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &avail, jsonx.V1()))
assert.Equal(t, "AVAILABLE", avail["code"])
},
},
diff --git a/services/macie2/handler_sensitivity_inspection.go b/services/macie2/handler_sensitivity_inspection.go
index 644fc6353d..0c5eb245ce 100644
--- a/services/macie2/handler_sensitivity_inspection.go
+++ b/services/macie2/handler_sensitivity_inspection.go
@@ -1,12 +1,13 @@
package macie2
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseTemplatesPath(method string, parts []string) (string, string) {
@@ -85,7 +86,7 @@ func (h *Handler) handleUpdateSensitivityInspectionTemplate(templateID string, b
Name string `json:"name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
diff --git a/services/macie2/handler_sensitivity_inspection_test.go b/services/macie2/handler_sensitivity_inspection_test.go
index 26dd8177f3..daf66f8d48 100644
--- a/services/macie2/handler_sensitivity_inspection_test.go
+++ b/services/macie2/handler_sensitivity_inspection_test.go
@@ -1,13 +1,14 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -25,7 +26,7 @@ func TestUpdateSensitivityInspectionTemplate_FieldsSurviveIndependentUpdates(t *
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
templates, _ := listResp["sensitivityInspectionTemplates"].([]any)
require.Len(t, templates, 1)
tmpl0, _ := templates[0].(map[string]any)
@@ -53,7 +54,7 @@ func TestUpdateSensitivityInspectionTemplate_FieldsSurviveIndependentUpdates(t *
require.Equal(t, http.StatusOK, rec.Code)
var updated map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "original description", updated["description"],
"description must survive an Update that never mentioned it")
@@ -84,7 +85,7 @@ func TestSensitivityInspectionTemplates(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
templates, _ := listResp["sensitivityInspectionTemplates"].([]any)
require.Len(t, templates, 1)
@@ -97,7 +98,7 @@ func TestSensitivityInspectionTemplates(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, templateID, getResp["sensitivityInspectionTemplateId"])
// UpdateSensitivityInspectionTemplate
@@ -118,7 +119,7 @@ func TestSensitivityInspectionTemplates(t *testing.T) {
// Verify update
rec = doRequest(t, h, http.MethodGet, "/templates/sensitivity-inspections/"+templateID, nil)
var updated map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "Updated description", updated["description"])
},
},
diff --git a/services/macie2/handler_tags.go b/services/macie2/handler_tags.go
index 7c3525992d..0fd9fcf6be 100644
--- a/services/macie2/handler_tags.go
+++ b/services/macie2/handler_tags.go
@@ -1,10 +1,12 @@
package macie2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseTagPath(method string, parts []string) (string, string) {
@@ -63,7 +65,7 @@ func (h *Handler) handleTagResource(resourceARN string, body []byte) (int, error
Tags map[string]string `json:"tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return http.StatusBadRequest, ErrValidation
}
diff --git a/services/macie2/handler_tags_test.go b/services/macie2/handler_tags_test.go
index b5140e1dea..2fe0598b6a 100644
--- a/services/macie2/handler_tags_test.go
+++ b/services/macie2/handler_tags_test.go
@@ -1,7 +1,7 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -48,7 +49,7 @@ func TestMacie2_Tags(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
tags, ok := resp["tags"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "prod", tags["env"])
@@ -169,7 +170,7 @@ func TestMacie2_TagOperations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id := resp["customDataIdentifierId"]
doRequest(t, h, http.MethodDelete, "/custom-data-identifiers/"+id, nil)
@@ -220,14 +221,14 @@ func TestCreationTagsVisibleViaTagAPI(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
arn := created["arn"]
rec2 := doRequest(t, h, http.MethodGet, "/tags/"+arn, nil)
require.Equal(t, http.StatusOK, rec2.Code)
var tagResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagResp, jsonx.V1()))
tags, ok := tagResp["tags"].(map[string]any)
require.True(t, ok, "tags must be an object")
@@ -248,7 +249,7 @@ func TestUntagResourceRemovesSpecificKey(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
arn := created["arn"]
doRequest(t, h, http.MethodPost, "/tags/"+arn,
@@ -260,7 +261,7 @@ func TestUntagResourceRemovesSpecificKey(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var tagResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagResp, jsonx.V1()))
tags, ok := tagResp["tags"].(map[string]any)
require.True(t, ok)
@@ -285,7 +286,7 @@ func TestTagResourceMergesWithCreationTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
arn := created["arn"]
doRequest(t, h, http.MethodPost, "/tags/"+arn,
@@ -295,7 +296,7 @@ func TestTagResourceMergesWithCreationTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var tagResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagResp, jsonx.V1()))
tags, ok := tagResp["tags"].(map[string]any)
require.True(t, ok)
diff --git a/services/macie2/handler_test.go b/services/macie2/handler_test.go
index 8544fd75fb..98338c2256 100644
--- a/services/macie2/handler_test.go
+++ b/services/macie2/handler_test.go
@@ -2,7 +2,7 @@ package macie2_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -29,7 +30,7 @@ func doRequest(t *testing.T, h *macie2.Handler, method, path string, body any) *
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
diff --git a/services/macie2/handler_usage.go b/services/macie2/handler_usage.go
index 0ef8e37fa4..26e4807e1d 100644
--- a/services/macie2/handler_usage.go
+++ b/services/macie2/handler_usage.go
@@ -1,8 +1,10 @@
package macie2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseUsagePath(method string, parts []string) (string, string) {
@@ -46,7 +48,7 @@ func (h *Handler) handleGetUsageStatistics(body []byte) (any, int, error) {
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, http.StatusBadRequest, ErrValidation
}
}
diff --git a/services/macie2/handler_usage_test.go b/services/macie2/handler_usage_test.go
index 6a1bc63c44..f7dad328f7 100644
--- a/services/macie2/handler_usage_test.go
+++ b/services/macie2/handler_usage_test.go
@@ -1,13 +1,14 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -29,7 +30,7 @@ func TestUsageAndManagedIdentifiers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
records, _ := resp["records"].([]any)
assert.Empty(t, records)
},
@@ -43,7 +44,7 @@ func TestUsageAndManagedIdentifiers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
totals, _ := resp["usageTotals"].([]any)
assert.NotEmpty(t, totals)
},
@@ -57,7 +58,7 @@ func TestUsageAndManagedIdentifiers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["items"].([]any)
assert.NotEmpty(t, items)
diff --git a/services/macie2/list_filter_params_test.go b/services/macie2/list_filter_params_test.go
index 4c35ab824e..8ccdd887b2 100644
--- a/services/macie2/list_filter_params_test.go
+++ b/services/macie2/list_filter_params_test.go
@@ -1,13 +1,14 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -31,7 +32,7 @@ func TestBuckets_DescribeBuckets_MaxResultsHonoured(t *testing.T) {
NextToken string `json:"nextToken"`
Buckets []map[string]any `json:"buckets"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
require.Len(t, page1.Buckets, 1, "maxResults=1 must limit the page to 1 item")
require.NotEmpty(t, page1.NextToken, "a partial page must return a nextToken")
@@ -44,7 +45,7 @@ func TestBuckets_DescribeBuckets_MaxResultsHonoured(t *testing.T) {
NextToken string `json:"nextToken"`
Buckets []map[string]any `json:"buckets"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
assert.Len(t, page2.Buckets, 2, "second page must return the remainder")
}
@@ -72,7 +73,7 @@ func TestBuckets_DescribeBuckets_SortCriteriaHonoured(t *testing.T) {
var resp struct {
Buckets []map[string]any `json:"buckets"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Buckets, 3)
assert.Equal(t, "bbb-bucket", resp.Buckets[0]["bucketName"])
@@ -106,7 +107,7 @@ func TestGetFindingStatistics_FindingCriteriaHonoured(t *testing.T) {
var stats struct {
CountsByGroup []map[string]any `json:"countsByGroup"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &stats))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &stats, jsonx.V1()))
require.Len(t, stats.CountsByGroup, 1, "FindingCriteria must exclude TypeB's group")
assert.Equal(t, "Policy:IAMUser/TypeA", stats.CountsByGroup[0]["groupKey"])
}
@@ -142,7 +143,7 @@ func TestFindingsFilter_ArchiveActionHonoured(t *testing.T) {
var listResp struct {
FindingIDs []string `json:"findingIds"`
}
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.FindingIDs, 2)
rec4 := doRequest(t, h, http.MethodPost, "/findings/describe", map[string]any{
@@ -153,7 +154,7 @@ func TestFindingsFilter_ArchiveActionHonoured(t *testing.T) {
var describeResp struct {
Findings []map[string]any `json:"findings"`
}
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &describeResp, jsonx.V1()))
require.Len(t, describeResp.Findings, 2)
for _, f := range describeResp.Findings {
@@ -189,7 +190,7 @@ func TestListFindings_SortCriteriaHonoured(t *testing.T) {
var listResp struct {
FindingIDs []string `json:"findingIds"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.FindingIDs, 2)
rec3 := doRequest(t, h, http.MethodPost, "/findings/describe", map[string]any{
@@ -200,7 +201,7 @@ func TestListFindings_SortCriteriaHonoured(t *testing.T) {
var describeResp struct {
Findings []macie2.Finding `json:"findings"`
}
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &describeResp, jsonx.V1()))
byID := make(map[string]string, len(describeResp.Findings))
for _, f := range describeResp.Findings {
@@ -236,7 +237,7 @@ func TestListClassificationJobs_SortCriteriaHonoured(t *testing.T) {
var resp struct {
Items []map[string]any `json:"items"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Items, 2)
assert.Equal(t, "zzz-job", resp.Items[0]["name"])
diff --git a/services/macie2/multi_region_test.go b/services/macie2/multi_region_test.go
index b307d1465d..b37c623342 100644
--- a/services/macie2/multi_region_test.go
+++ b/services/macie2/multi_region_test.go
@@ -2,7 +2,8 @@ package macie2_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -180,8 +182,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/macie2/pagination_tie_test.go b/services/macie2/pagination_tie_test.go
index 11063a361a..58ec372854 100644
--- a/services/macie2/pagination_tie_test.go
+++ b/services/macie2/pagination_tie_test.go
@@ -1,7 +1,7 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -54,7 +55,7 @@ func TestDescribeBuckets_TiedObjectCount_NoDropOrDupAcrossPages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
buckets, _ := resp["buckets"].([]any)
for _, item := range buckets {
@@ -105,7 +106,7 @@ func TestListClassificationJobs_TiedName_NoDropOrDupAcrossPages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
created[resp["jobId"]] = true
}
@@ -120,7 +121,7 @@ func TestListClassificationJobs_TiedName_NoDropOrDupAcrossPages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["items"].([]any)
for _, item := range items {
@@ -168,7 +169,7 @@ func TestListCustomDataIdentifiers_TiedName_NoDropOrDupAcrossPages(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
created[resp["customDataIdentifierId"]] = true
}
@@ -180,7 +181,7 @@ func TestListCustomDataIdentifiers_TiedName_NoDropOrDupAcrossPages(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["items"].([]any)
for _, item := range items {
@@ -228,7 +229,7 @@ func TestListAllowLists_TiedName_NoDropOrDupAcrossPages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
created[resp["id"]] = true
}
@@ -240,7 +241,7 @@ func TestListAllowLists_TiedName_NoDropOrDupAcrossPages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["allowLists"].([]any)
for _, item := range items {
@@ -286,7 +287,7 @@ func TestListFindingsFilters_TiedPosition_NoDropOrDupAcrossPages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
created[resp["id"]] = true
}
@@ -298,7 +299,7 @@ func TestListFindingsFilters_TiedPosition_NoDropOrDupAcrossPages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["findingsFilterListItems"].([]any)
for _, item := range items {
@@ -348,7 +349,7 @@ func TestListFindings_TiedType_NoDropOrDupAcrossPages(t *testing.T) {
require.Equal(t, http.StatusOK, truthRec.Code)
var truthResp map[string]any
- require.NoError(t, json.Unmarshal(truthRec.Body.Bytes(), &truthResp))
+ require.NoError(t, json.Unmarshal(truthRec.Body.Bytes(), &truthResp, jsonx.V1()))
truthIDs, _ := truthResp["findingIds"].([]any)
require.Len(t, truthIDs, dupCount)
@@ -369,7 +370,7 @@ func TestListFindings_TiedType_NoDropOrDupAcrossPages(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
ids, _ := resp["findingIds"].([]any)
for _, id := range ids {
diff --git a/services/macie2/persistence.go b/services/macie2/persistence.go
index b3b7a57e14..75bb9ea699 100644
--- a/services/macie2/persistence.go
+++ b/services/macie2/persistence.go
@@ -2,7 +2,7 @@ package macie2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -52,7 +52,7 @@ const macie2SnapshotVersion = 3
// ClassExportConfig, FindingsPubConfig, and RevealConfig are single pointers,
// not collections, and are persisted directly alongside the tables.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Session *Session `json:"session,omitempty"`
Tags map[string]map[string]string `json:"tags"`
Administrator *AdministratorAccount `json:"administrator,omitempty"`
diff --git a/services/macie2/tag_leak_test.go b/services/macie2/tag_leak_test.go
index a7214cfe9c..02bc109c6f 100644
--- a/services/macie2/tag_leak_test.go
+++ b/services/macie2/tag_leak_test.go
@@ -1,13 +1,14 @@
package macie2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/macie2"
)
@@ -31,7 +32,7 @@ func TestMacie2_Delete_ClearsTags(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
arnStr, id := created["arn"], created["id"]
require.NotEmpty(t, arnStr)
@@ -45,7 +46,7 @@ func TestMacie2_Delete_ClearsTags(t *testing.T) {
})
require.Equal(t, http.StatusOK, otherRec.Code)
var otherCreated map[string]string
- require.NoError(t, json.Unmarshal(otherRec.Body.Bytes(), &otherCreated))
+ require.NoError(t, json.Unmarshal(otherRec.Body.Bytes(), &otherCreated, jsonx.V1()))
otherArn := otherCreated["arn"]
otherRec = doRequest(
t, h, http.MethodPost, "/tags/"+otherArn, map[string]any{"tags": map[string]string{"k": "v"}},
@@ -61,7 +62,7 @@ func TestMacie2_Delete_ClearsTags(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/tags/"+otherArn, nil)
require.Equal(t, http.StatusOK, rec.Code)
var otherTags map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &otherTags))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &otherTags, jsonx.V1()))
assert.NotEmpty(t, otherTags["tags"])
})
@@ -76,7 +77,7 @@ func TestMacie2_Delete_ClearsTags(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
arnStr, id := created["arn"], created["id"]
require.NotEmpty(t, arnStr)
@@ -96,6 +97,6 @@ func assertMacie2SnapshotTagsCleared(t *testing.T, h *macie2.Handler, arnStr str
var probe struct {
Tags map[string]map[string]string `json:"tags"`
}
- require.NoError(t, json.Unmarshal(h.Snapshot(t.Context()), &probe))
+ require.NoError(t, json.Unmarshal(h.Snapshot(t.Context()), &probe, jsonx.V1()))
assert.NotContains(t, probe.Tags, arnStr)
}
diff --git a/services/managedblockchain/accessors_test.go b/services/managedblockchain/accessors_test.go
index aa0245f9c8..a3e131bf45 100644
--- a/services/managedblockchain/accessors_test.go
+++ b/services/managedblockchain/accessors_test.go
@@ -2,7 +2,8 @@ package managedblockchain_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/managedblockchain"
)
@@ -70,7 +72,7 @@ func TestHandler_CreateAccessor(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
if tt.wantKey != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, tt.wantKey)
}
})
@@ -111,7 +113,7 @@ func TestHandler_GetAccessor(t *testing.T) {
var out struct {
AccessorID string `json:"AccessorId"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &out, jsonx.V1()))
id := tt.accessorID
if id == "" {
@@ -123,7 +125,7 @@ func TestHandler_GetAccessor(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "Accessor")
}
})
@@ -155,7 +157,7 @@ func TestHandler_DeleteAccessor(t *testing.T) {
var out struct {
AccessorID string `json:"AccessorId"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &out, jsonx.V1()))
rec := doRequest(t, h, http.MethodDelete, "/accessors/"+out.AccessorID, nil)
assert.Equal(t, tt.wantStatus, rec.Code)
@@ -171,7 +173,7 @@ func TestHandler_DeleteAccessor(t *testing.T) {
Status string `json:"Status"`
} `json:"Accessor"`
}
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&getOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &getOut, jsonx.V1()))
assert.Equal(t, "PENDING_DELETION", getOut.Accessor.Status)
})
}
@@ -208,7 +210,7 @@ func TestHandler_ListAccessors(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
accessors, ok := resp["Accessors"].([]any)
require.True(t, ok)
@@ -246,7 +248,7 @@ func TestHandler_AccessorRoundTrip(t *testing.T) {
BillingToken string `json:"BillingToken"`
NetworkType string `json:"NetworkType"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
assert.NotEmpty(t, createOut.AccessorID)
assert.NotEmpty(t, createOut.BillingToken)
assert.Equal(t, "ETHEREUM_MAINNET", createOut.NetworkType)
@@ -258,7 +260,7 @@ func TestHandler_AccessorRoundTrip(t *testing.T) {
var getOut struct {
Accessor map[string]any `json:"Accessor"`
}
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&getOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &getOut, jsonx.V1()))
assert.Equal(t, "AVAILABLE", getOut.Accessor["Status"])
// List.
@@ -268,7 +270,7 @@ func TestHandler_AccessorRoundTrip(t *testing.T) {
var listOut struct {
Accessors []map[string]any `json:"Accessors"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listOut, jsonx.V1()))
assert.Len(t, listOut.Accessors, 1)
// Delete.
@@ -284,7 +286,7 @@ func TestHandler_AccessorRoundTrip(t *testing.T) {
var listOut2 struct {
Accessors []map[string]any `json:"Accessors"`
}
- require.NoError(t, json.NewDecoder(listRec2.Body).Decode(&listOut2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec2.Body), &listOut2, jsonx.V1()))
require.Len(t, listOut2.Accessors, 1)
assert.Equal(t, "PENDING_DELETION", listOut2.Accessors[0]["Status"])
})
@@ -359,7 +361,7 @@ func TestHandler_ListAccessorsFilters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
accessors := resp["Accessors"].([]any)
assert.Len(t, accessors, tt.wantCount)
@@ -474,7 +476,7 @@ func TestHandler_AccessorLifecycleViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
accessorID, _ := createResp["AccessorId"].(string)
require.NotEmpty(t, accessorID)
@@ -485,7 +487,7 @@ func TestHandler_AccessorLifecycleViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp, jsonx.V1()))
accessor := getResp["Accessor"].(map[string]any)
assert.Equal(t, accessorID, accessor["Id"])
@@ -496,7 +498,7 @@ func TestHandler_AccessorLifecycleViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listResp, jsonx.V1()))
accessors := listResp["Accessors"].([]any)
assert.Len(t, accessors, 1)
@@ -511,7 +513,7 @@ func TestHandler_AccessorLifecycleViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec5.Code)
var getResp5 map[string]any
- require.NoError(t, json.Unmarshal(rec5.Body.Bytes(), &getResp5))
+ require.NoError(t, json.Unmarshal(rec5.Body.Bytes(), &getResp5, jsonx.V1()))
accessor5 := getResp5["Accessor"].(map[string]any)
assert.Equal(t, "PENDING_DELETION", accessor5["Status"])
})
diff --git a/services/managedblockchain/framework_attributes_test.go b/services/managedblockchain/framework_attributes_test.go
index 8b0f9edb1f..d4bac9d0a1 100644
--- a/services/managedblockchain/framework_attributes_test.go
+++ b/services/managedblockchain/framework_attributes_test.go
@@ -1,12 +1,14 @@
package managedblockchain_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_CreateNetwork_FrameworkConfiguration verifies CreateNetwork's
@@ -73,7 +75,7 @@ func TestHandler_CreateNetwork_FrameworkConfiguration(t *testing.T) {
var createResp struct {
NetworkID string `json:"NetworkId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
rec = doRequest(t, h, http.MethodGet, "/networks/"+createResp.NetworkID, nil)
require.Equal(t, http.StatusOK, rec.Code)
@@ -89,7 +91,7 @@ func TestHandler_CreateNetwork_FrameworkConfiguration(t *testing.T) {
VpcEndpointServiceName string `json:"VpcEndpointServiceName"`
} `json:"Network"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.NotEmpty(t, getResp.Network.VpcEndpointServiceName,
"VpcEndpointServiceName must be populated regardless of FrameworkConfiguration")
@@ -294,7 +296,7 @@ func TestHandler_CreateMember_FrameworkAttributesRoundTrip(t *testing.T) {
var createResp struct {
MemberID string `json:"MemberId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
rec = doRequest(t, h, http.MethodGet, "/networks/"+networkID+"/members/"+createResp.MemberID, nil)
require.Equal(t, http.StatusOK, rec.Code)
@@ -310,7 +312,7 @@ func TestHandler_CreateMember_FrameworkAttributesRoundTrip(t *testing.T) {
} `json:"FrameworkAttributes"`
} `json:"Member"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "admin", getResp.Member.FrameworkAttributes.Fabric.AdminUsername)
assert.NotEmpty(t, getResp.Member.FrameworkAttributes.Fabric.CaEndpoint)
@@ -364,7 +366,7 @@ func TestHandler_CreateNode_FrameworkAttributesRoundTrip(t *testing.T) {
var createResp struct {
NodeID string `json:"NodeId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
rec = doRequest(t, h, http.MethodGet,
"/networks/"+networkID+"/nodes/"+createResp.NodeID+"?memberId="+memberID, nil)
@@ -382,7 +384,7 @@ func TestHandler_CreateNode_FrameworkAttributesRoundTrip(t *testing.T) {
} `json:"FrameworkAttributes"`
} `json:"Node"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, tt.wantStateDB, getResp.Node.StateDB)
assert.Equal(t, "AWS Owned KMS Key", getResp.Node.KmsKeyArn,
diff --git a/services/managedblockchain/handler_accessors.go b/services/managedblockchain/handler_accessors.go
index 20dba28190..e3ca32fbcf 100644
--- a/services/managedblockchain/handler_accessors.go
+++ b/services/managedblockchain/handler_accessors.go
@@ -1,16 +1,18 @@
package managedblockchain
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateAccessor(c *echo.Context, body []byte) error {
var req createAccessorRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body")
}
diff --git a/services/managedblockchain/handler_members.go b/services/managedblockchain/handler_members.go
index 24ca90d926..680e8ae539 100644
--- a/services/managedblockchain/handler_members.go
+++ b/services/managedblockchain/handler_members.go
@@ -1,10 +1,12 @@
package managedblockchain
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateMember(c *echo.Context, networkID string, body []byte) error {
@@ -14,7 +16,7 @@ func (h *Handler) handleCreateMember(c *echo.Context, networkID string, body []b
var req createMemberRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body")
}
@@ -124,7 +126,7 @@ func (h *Handler) handleUpdateMember(c *echo.Context, resource string, body []by
var req updateMemberRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body")
}
diff --git a/services/managedblockchain/handler_networks.go b/services/managedblockchain/handler_networks.go
index 1416e6b303..31856782ea 100644
--- a/services/managedblockchain/handler_networks.go
+++ b/services/managedblockchain/handler_networks.go
@@ -1,16 +1,18 @@
package managedblockchain
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateNetwork(c *echo.Context, body []byte) error {
var req createNetworkRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body")
}
diff --git a/services/managedblockchain/handler_nodes.go b/services/managedblockchain/handler_nodes.go
index 4e049d2c03..4fd90fdb96 100644
--- a/services/managedblockchain/handler_nodes.go
+++ b/services/managedblockchain/handler_nodes.go
@@ -1,10 +1,12 @@
package managedblockchain
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleCreateNode handles POST /networks/{networkId}/nodes. Unlike member
@@ -17,7 +19,7 @@ func (h *Handler) handleCreateNode(c *echo.Context, networkID string, body []byt
}
var req createNodeRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body")
}
@@ -141,7 +143,7 @@ func (h *Handler) handleUpdateNode(c *echo.Context, resource string, body []byte
var req updateNodeRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body")
}
}
diff --git a/services/managedblockchain/handler_proposals.go b/services/managedblockchain/handler_proposals.go
index 356250c66f..10c846f519 100644
--- a/services/managedblockchain/handler_proposals.go
+++ b/services/managedblockchain/handler_proposals.go
@@ -1,10 +1,12 @@
package managedblockchain
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateProposal(c *echo.Context, networkID string, body []byte) error {
@@ -14,7 +16,7 @@ func (h *Handler) handleCreateProposal(c *echo.Context, networkID string, body [
var req createProposalRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body")
}
@@ -128,7 +130,7 @@ func (h *Handler) handleVoteOnProposal(c *echo.Context, resource string, body []
var req voteOnProposalRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body")
}
diff --git a/services/managedblockchain/handler_sdk_route_table_test.go b/services/managedblockchain/handler_sdk_route_table_test.go
index 3898d2a8e3..b4b62e31b7 100644
--- a/services/managedblockchain/handler_sdk_route_table_test.go
+++ b/services/managedblockchain/handler_sdk_route_table_test.go
@@ -1,7 +1,7 @@
package managedblockchain_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http/httptest"
"strings"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/managedblockchain"
)
@@ -116,7 +117,7 @@ func TestExtractOperation_SDKRouteTable(t *testing.T) {
var resp struct {
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEqual(t, "resource not found", resp.Message,
"method=%s path=%s op=%s: dispatched to the unmatched-route default", tc.method, tc.path, tc.op)
})
diff --git a/services/managedblockchain/handler_tags.go b/services/managedblockchain/handler_tags.go
index 4175bd5551..a8b283de87 100644
--- a/services/managedblockchain/handler_tags.go
+++ b/services/managedblockchain/handler_tags.go
@@ -1,11 +1,13 @@
package managedblockchain
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleListTagsForResource(c *echo.Context, resourceARN string) error {
@@ -30,7 +32,7 @@ func (h *Handler) handleTagResource(c *echo.Context, resourceARN string, body []
var req tagResourceRequest
- if parseErr := json.Unmarshal(body, &req); parseErr != nil {
+ if parseErr := json.Unmarshal(body, &req, jsonx.V1()); parseErr != nil {
return writeError(c, http.StatusBadRequest, "InvalidRequestException", "invalid request body")
}
diff --git a/services/managedblockchain/handler_test.go b/services/managedblockchain/handler_test.go
index 0afbe440a6..3598b04f8a 100644
--- a/services/managedblockchain/handler_test.go
+++ b/services/managedblockchain/handler_test.go
@@ -1,7 +1,7 @@
package managedblockchain_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -9,6 +9,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_HandlerMetadata(t *testing.T) {
@@ -341,7 +343,7 @@ func TestHandler_ErrorResponseHasCodeField(t *testing.T) {
assert.Equal(t, tt.wantHTTP, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantCode, resp["Code"],
"error response must include Code field; body: %s", rec.Body.String())
assert.NotEmpty(t, resp["message"],
@@ -388,7 +390,7 @@ func TestHandler_ErrorResponseMessageNotEmpty(t *testing.T) {
assert.NotEqual(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["message"], "error response must include non-empty message")
assert.NotEmpty(t, resp["Code"], "error response must include non-empty Code")
})
diff --git a/services/managedblockchain/input_validation_test.go b/services/managedblockchain/input_validation_test.go
index 7be832329f..77ebc98b15 100644
--- a/services/managedblockchain/input_validation_test.go
+++ b/services/managedblockchain/input_validation_test.go
@@ -1,13 +1,15 @@
package managedblockchain_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func networkBody(mutate func(body, member, fabric map[string]any)) map[string]any {
@@ -108,7 +110,7 @@ func TestCreateNetwork_InputValidation(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code, rec.Body.String())
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", resp["Code"])
assert.NotContains(t, resp["message"], "InvalidRequestException")
})
@@ -122,7 +124,7 @@ func TestCreateNetwork_RealisticIDs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Regexp(t, `^n-[A-Z2-7]{26}$`, resp["NetworkId"])
assert.Regexp(t, `^m-[A-Z2-7]{26}$`, resp["MemberId"])
}
@@ -134,7 +136,7 @@ func TestErrorMessages_NoCodePrefix(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["Code"])
assert.NotContains(t, resp["message"], "ResourceNotFoundException")
}
diff --git a/services/managedblockchain/invitations_test.go b/services/managedblockchain/invitations_test.go
index 8e4c01d8a7..0bf1d22962 100644
--- a/services/managedblockchain/invitations_test.go
+++ b/services/managedblockchain/invitations_test.go
@@ -1,13 +1,15 @@
package managedblockchain_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/managedblockchain"
)
@@ -42,7 +44,7 @@ func TestHandler_ListInvitations(t *testing.T) {
var resp struct {
Invitations []map[string]any `json:"Invitations"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Len(t, resp.Invitations, tt.wantLen)
})
}
@@ -119,7 +121,7 @@ func TestHandler_InvitationNetworkSummary(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
invitations := resp["Invitations"].([]any)
require.Len(t, invitations, 1)
@@ -153,7 +155,7 @@ func TestHandler_InvitationLifecycleViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
invitations := listResp["Invitations"].([]any)
assert.Len(t, invitations, 1)
@@ -199,7 +201,7 @@ func TestHandler_InvitationOmitsTopLevelNetworkFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
invitations := resp["Invitations"].([]any)
require.Len(t, invitations, 1)
diff --git a/services/managedblockchain/members_test.go b/services/managedblockchain/members_test.go
index ef58dec0ac..53be6e30b3 100644
--- a/services/managedblockchain/members_test.go
+++ b/services/managedblockchain/members_test.go
@@ -1,7 +1,7 @@
package managedblockchain_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/managedblockchain"
)
@@ -128,7 +129,7 @@ func TestHandler_MemberLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createNetResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createNetResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createNetResp, jsonx.V1()))
networkID := createNetResp["NetworkId"].(string)
// Create member
@@ -142,7 +143,7 @@ func TestHandler_MemberLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createMemResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createMemResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createMemResp, jsonx.V1()))
memberID := createMemResp["MemberId"].(string)
assert.NotEmpty(t, memberID)
@@ -155,7 +156,7 @@ func TestHandler_MemberLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
members, ok := listResp["Members"].([]any)
require.True(t, ok)
assert.Len(t, members, 2)
@@ -430,7 +431,7 @@ func TestHandler_ListMembersFilters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
members := resp["Members"].([]any)
assert.Len(t, members, tt.wantCount)
@@ -523,7 +524,7 @@ func TestHandler_MemberSummaryIsOwned(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
members := resp["Members"].([]any)
require.Len(t, members, 1)
@@ -552,7 +553,7 @@ func TestHandler_MemberObjectIsOwned(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
member := resp["Member"].(map[string]any)
isOwned, ok := member["IsOwned"]
@@ -610,7 +611,7 @@ func TestHandler_UpdateMemberLogPublishingConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp, jsonx.V1()))
member := getResp["Member"].(map[string]any)
logConfig, ok := member["LogPublishingConfiguration"]
@@ -708,7 +709,7 @@ func TestHandler_CreateMemberWithTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
memberID, _ := resp["MemberId"].(string)
require.NotEmpty(t, memberID)
@@ -718,7 +719,7 @@ func TestHandler_CreateMemberWithTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var memResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &memResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &memResp, jsonx.V1()))
member := memResp["Member"].(map[string]any)
tags := member["Tags"].(map[string]any)
diff --git a/services/managedblockchain/multi_region_test.go b/services/managedblockchain/multi_region_test.go
index d530c57313..5824f23b7e 100644
--- a/services/managedblockchain/multi_region_test.go
+++ b/services/managedblockchain/multi_region_test.go
@@ -2,7 +2,8 @@ package managedblockchain_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/managedblockchain"
)
@@ -198,8 +200,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/managedblockchain/networks_test.go b/services/managedblockchain/networks_test.go
index 851fb6bbf6..3265ceec9d 100644
--- a/services/managedblockchain/networks_test.go
+++ b/services/managedblockchain/networks_test.go
@@ -2,7 +2,7 @@ package managedblockchain_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/managedblockchain"
)
@@ -275,7 +276,7 @@ func TestHandler_CreateNetwork(t *testing.T) {
if tt.wantKey != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, tt.wantKey)
}
})
@@ -316,7 +317,7 @@ func TestHandler_GetNetwork(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
networkID := createResp["NetworkId"].(string)
id := tt.networkID
@@ -329,7 +330,7 @@ func TestHandler_GetNetwork(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "Network")
}
})
@@ -376,7 +377,7 @@ func TestHandler_ListNetworks(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
networks, ok := resp["Networks"].([]any)
require.True(t, ok)
@@ -466,7 +467,7 @@ func TestHandler_VotingPolicyStoredAndReturned(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
networkID := createResp["NetworkId"].(string)
@@ -474,7 +475,7 @@ func TestHandler_VotingPolicyStoredAndReturned(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp, jsonx.V1()))
network := getResp["Network"].(map[string]any)
@@ -549,7 +550,7 @@ func TestHandler_ListNetworksFilters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
networks := resp["Networks"].([]any)
assert.Len(t, networks, tt.wantCount)
@@ -674,7 +675,7 @@ func TestHandler_CreateNetworkWithTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
networkID, _ := resp["NetworkId"].(string)
require.NotEmpty(t, networkID)
@@ -684,7 +685,7 @@ func TestHandler_CreateNetworkWithTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var netResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &netResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &netResp, jsonx.V1()))
network := netResp["Network"].(map[string]any)
tags := network["Tags"].(map[string]any)
@@ -713,7 +714,7 @@ func TestHandler_CreateNetworkFoundingMemberTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
networkID, _ := resp["NetworkId"].(string)
memberID, _ := resp["MemberId"].(string)
@@ -724,7 +725,7 @@ func TestHandler_CreateNetworkFoundingMemberTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var memResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &memResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &memResp, jsonx.V1()))
member := memResp["Member"].(map[string]any)
tags := member["Tags"].(map[string]any)
diff --git a/services/managedblockchain/nodes_test.go b/services/managedblockchain/nodes_test.go
index d6eccfe9a5..bb9b415d2d 100644
--- a/services/managedblockchain/nodes_test.go
+++ b/services/managedblockchain/nodes_test.go
@@ -1,7 +1,8 @@
package managedblockchain_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"net/http"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/managedblockchain"
)
@@ -39,7 +41,7 @@ func TestHandler_NodeLifecycle_RealWireShape(t *testing.T) {
var createOut struct {
NodeID string `json:"NodeId"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
require.NotEmpty(t, createOut.NodeID)
nodePath := "/networks/" + netID + "/nodes/" + createOut.NodeID + "?memberId=" + memID
@@ -163,7 +165,7 @@ func TestHandler_GetNode(t *testing.T) {
var createOut struct {
NodeID string `json:"NodeId"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
getPath := fmt.Sprintf("%s/%s?memberId=%s", nodesPath, createOut.NodeID, memID)
rec := doRequest(t, h, http.MethodGet, getPath, nil)
@@ -210,7 +212,7 @@ func TestHandler_ListNodes(t *testing.T) {
var out struct {
Nodes []map[string]any `json:"Nodes"`
}
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &out, jsonx.V1()))
assert.Len(t, out.Nodes, tt.wantLen)
})
}
@@ -244,7 +246,7 @@ func TestHandler_DeleteNode(t *testing.T) {
var createOut struct {
NodeID string `json:"NodeId"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
delPath := fmt.Sprintf("%s/%s?memberId=%s", nodesPath, createOut.NodeID, memID)
rec := doRequest(t, h, http.MethodDelete, delPath, nil)
@@ -377,7 +379,7 @@ func TestHandler_ListNodesFilters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
nodes := resp["Nodes"].([]any)
assert.Len(t, nodes, tt.wantCount)
@@ -477,7 +479,7 @@ func TestHandler_NodeSummaryAvailabilityZone(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listResp, jsonx.V1()))
nodes := listResp["Nodes"].([]any)
require.Len(t, nodes, 1)
@@ -550,7 +552,7 @@ func TestHandler_UpdateNodeLogPublishingConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp, jsonx.V1()))
nodeResp := getResp["Node"].(map[string]any)
logConfig, ok := nodeResp["LogPublishingConfiguration"]
@@ -598,7 +600,7 @@ func TestHandler_CreateNodeWithTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
nodeID, _ := resp["NodeId"].(string)
require.NotEmpty(t, nodeID)
@@ -609,7 +611,7 @@ func TestHandler_CreateNodeWithTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var nodeResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &nodeResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &nodeResp, jsonx.V1()))
node := nodeResp["Node"].(map[string]any)
tags := node["Tags"].(map[string]any)
@@ -674,7 +676,7 @@ func TestHandler_NodeLifecycleBasicViaHTTP(t *testing.T) {
}
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
nodeID, _ := createResp["NodeId"].(string)
require.NotEmpty(t, nodeID)
@@ -685,7 +687,7 @@ func TestHandler_NodeLifecycleBasicViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp, jsonx.V1()))
node := getResp["Node"].(map[string]any)
assert.Equal(t, nodeID, node["Id"])
@@ -697,7 +699,7 @@ func TestHandler_NodeLifecycleBasicViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listResp, jsonx.V1()))
nodes := listResp["Nodes"].([]any)
assert.Len(t, nodes, 1)
diff --git a/services/managedblockchain/pagination_test.go b/services/managedblockchain/pagination_test.go
index 011d631e39..7a3a13e8f9 100644
--- a/services/managedblockchain/pagination_test.go
+++ b/services/managedblockchain/pagination_test.go
@@ -1,13 +1,15 @@
package managedblockchain_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_ListNetworks_Pagination verifies ListNetworks honors
@@ -33,7 +35,7 @@ func TestHandler_ListNetworks_Pagination(t *testing.T) {
rec := doRequestWithQuery(t, h, "/networks", map[string]string{"maxResults": "2"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
assert.Len(t, page1.Networks, 2)
require.NotEmpty(t, page1.NextToken, "a 5-item list capped at maxResults=2 must return a NextToken")
@@ -41,7 +43,7 @@ func TestHandler_ListNetworks_Pagination(t *testing.T) {
rec = doRequestWithQuery(t, h, "/networks", map[string]string{"maxResults": "2", "nextToken": page1.NextToken})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
assert.Len(t, page2.Networks, 2)
require.NotEmpty(t, page2.NextToken)
@@ -49,7 +51,7 @@ func TestHandler_ListNetworks_Pagination(t *testing.T) {
rec = doRequestWithQuery(t, h, "/networks", map[string]string{"maxResults": "2", "nextToken": page2.NextToken})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page3))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page3, jsonx.V1()))
assert.Len(t, page3.Networks, 1)
assert.Empty(t, page3.NextToken, "the final page must omit NextToken")
@@ -69,7 +71,7 @@ func TestHandler_ListNetworks_Pagination(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/networks", nil)
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &unpaged))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &unpaged, jsonx.V1()))
assert.Len(t, unpaged.Networks, 5)
assert.Empty(t, unpaged.NextToken)
}
@@ -109,7 +111,7 @@ func TestHandler_ListMembers_Pagination(t *testing.T) {
rec := doRequestWithQuery(t, h, "/networks/"+networkID+"/members", map[string]string{"maxResults": "3"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
assert.Len(t, page1.Members, 3)
require.NotEmpty(t, page1.NextToken)
@@ -121,7 +123,7 @@ func TestHandler_ListMembers_Pagination(t *testing.T) {
rec = doRequestWithQuery(t, h, "/networks/"+networkID+"/members",
map[string]string{"maxResults": "3", "nextToken": page1.NextToken})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
assert.Len(t, page2.Members, 1)
assert.Empty(t, page2.NextToken)
}
diff --git a/services/managedblockchain/persistence.go b/services/managedblockchain/persistence.go
index 68fc780427..d41dd9dc04 100644
--- a/services/managedblockchain/persistence.go
+++ b/services/managedblockchain/persistence.go
@@ -2,7 +2,7 @@ package managedblockchain
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -63,8 +63,8 @@ func buildProposalVoteDTORegistry() (*store.Registry, *store.Table[proposalVoteD
// is a derived reverse-lookup cache rebuilt from the tables above by
// rebuildARNIndexLocked on Restore, exactly as it was before this refactor.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
@@ -141,7 +141,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreTablesLocked rebuilds every store.Table on b from tables' per-table
// JSON, factored out of Restore to keep Restore's own cognitive complexity
// low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreTablesLocked(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreTablesLocked(tables map[string]jsontext.Value) error {
if err := b.registry.RestoreAll(tables); err != nil {
return fmt.Errorf("managedblockchain: restore tables: %w", err)
}
diff --git a/services/managedblockchain/persistence_test.go b/services/managedblockchain/persistence_test.go
index c693ad79dd..aee491df59 100644
--- a/services/managedblockchain/persistence_test.go
+++ b/services/managedblockchain/persistence_test.go
@@ -1,12 +1,13 @@
package managedblockchain_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/managedblockchain"
)
@@ -366,11 +367,11 @@ func TestManagedBlockchain_PersistenceVersionMismatch(t *testing.T) {
require.NotNil(t, snap)
var raw map[string]any
- require.NoError(t, json.Unmarshal(snap, &raw))
+ require.NoError(t, json.Unmarshal(snap, &raw, jsonx.V1()))
tt.mutate(t, raw)
- mutated, err := json.Marshal(raw)
+ mutated, err := json.Marshal(raw, jsonx.V1())
require.NoError(t, err)
// Seed b2 with unrelated pre-existing state so a discarded
diff --git a/services/managedblockchain/proposals_test.go b/services/managedblockchain/proposals_test.go
index 4d666de547..12be123952 100644
--- a/services/managedblockchain/proposals_test.go
+++ b/services/managedblockchain/proposals_test.go
@@ -2,7 +2,8 @@ package managedblockchain_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/managedblockchain"
)
@@ -75,7 +77,7 @@ func TestHandler_CreateProposal(t *testing.T) {
if tt.wantKey != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, tt.wantKey)
}
})
@@ -104,7 +106,7 @@ func TestCreateProposal_TagsReachTagStore(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
proposalID, _ := resp["ProposalId"].(string)
require.NotEmpty(t, proposalID)
@@ -112,7 +114,7 @@ func TestCreateProposal_TagsReachTagStore(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
proposal := getResp["Proposal"].(map[string]any)
proposalArn, _ := proposal["Arn"].(string)
require.NotEmpty(t, proposalArn)
@@ -158,7 +160,7 @@ func TestHandler_GetProposal(t *testing.T) {
var createOut struct {
ProposalID string `json:"ProposalId"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
pid := tt.proposalID
if pid == "" {
@@ -170,7 +172,7 @@ func TestHandler_GetProposal(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "Proposal")
}
})
@@ -210,7 +212,7 @@ func TestHandler_ListProposals(t *testing.T) {
var resp struct {
Proposals []map[string]any `json:"Proposals"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Len(t, resp.Proposals, tt.wantLen)
})
}
@@ -243,7 +245,7 @@ func TestHandler_ListProposalVotes(t *testing.T) {
var createOut struct {
ProposalID string `json:"ProposalId"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
path := fmt.Sprintf("/networks/%s/proposals/%s/votes", netID, createOut.ProposalID)
rec := doRequest(t, h, http.MethodGet, path, nil)
@@ -252,7 +254,7 @@ func TestHandler_ListProposalVotes(t *testing.T) {
var resp struct {
ProposalVotes []map[string]any `json:"ProposalVotes"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Len(t, resp.ProposalVotes, tt.wantLen)
})
}
@@ -314,7 +316,7 @@ func TestHandler_ProposalRoundTrip(t *testing.T) {
var createOut struct {
ProposalID string `json:"ProposalId"`
}
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createOut, jsonx.V1()))
assert.NotEmpty(t, createOut.ProposalID)
// Get.
@@ -325,7 +327,7 @@ func TestHandler_ProposalRoundTrip(t *testing.T) {
var getOut struct {
Proposal map[string]any `json:"Proposal"`
}
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&getOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &getOut, jsonx.V1()))
assert.Equal(t, "IN_PROGRESS", getOut.Proposal["Status"])
// List votes.
@@ -336,7 +338,7 @@ func TestHandler_ProposalRoundTrip(t *testing.T) {
var votesOut struct {
ProposalVotes []map[string]any `json:"ProposalVotes"`
}
- require.NoError(t, json.NewDecoder(votesRec.Body).Decode(&votesOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(votesRec.Body), &votesOut, jsonx.V1()))
assert.Empty(t, votesOut.ProposalVotes)
})
}
@@ -442,7 +444,7 @@ func TestHandler_ProposalActionsStoredAndReturned(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
proposalID := createResp["ProposalId"].(string)
@@ -450,7 +452,7 @@ func TestHandler_ProposalActionsStoredAndReturned(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp, jsonx.V1()))
proposal := getResp["Proposal"].(map[string]any)
@@ -498,7 +500,7 @@ func TestHandler_ProposalSummaryOmitsNetworkID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
proposals := resp["Proposals"].([]any)
require.Len(t, proposals, 1)
@@ -535,7 +537,7 @@ func TestHandler_ListProposalsStatusFilter(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
proposals, _ := listResp["Proposals"].([]any)
assert.Len(t, proposals, 2,
@@ -548,7 +550,7 @@ func TestHandler_ListProposalsStatusFilter(t *testing.T) {
require.Equal(t, http.StatusOK, listApprovedRec.Code)
var listApprovedResp map[string]any
- require.NoError(t, json.Unmarshal(listApprovedRec.Body.Bytes(), &listApprovedResp))
+ require.NoError(t, json.Unmarshal(listApprovedRec.Body.Bytes(), &listApprovedResp, jsonx.V1()))
approvedProposals, _ := listApprovedResp["Proposals"].([]any)
assert.Empty(t, approvedProposals,
@@ -579,7 +581,7 @@ func TestHandler_ListProposalsNoStatusFilterReturnsAll(t *testing.T) {
require.Equal(t, http.StatusOK, netRec.Code)
var netResp map[string]any
- require.NoError(t, json.Unmarshal(netRec.Body.Bytes(), &netResp))
+ require.NoError(t, json.Unmarshal(netRec.Body.Bytes(), &netResp, jsonx.V1()))
netID := netResp["NetworkId"].(string)
ownerID := netResp["MemberId"].(string)
@@ -590,7 +592,7 @@ func TestHandler_ListProposalsNoStatusFilterReturnsAll(t *testing.T) {
require.Equal(t, http.StatusOK, propRec1.Code)
var prop1 map[string]any
- require.NoError(t, json.Unmarshal(propRec1.Body.Bytes(), &prop1))
+ require.NoError(t, json.Unmarshal(propRec1.Body.Bytes(), &prop1, jsonx.V1()))
propID1 := prop1["ProposalId"].(string)
@@ -609,7 +611,7 @@ func TestHandler_ListProposalsNoStatusFilterReturnsAll(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
proposals, _ := listResp["Proposals"].([]any)
assert.Len(t, proposals, 2, "listing without status filter must return all proposals")
@@ -629,7 +631,7 @@ func TestHandler_ListProposalsNoStatusFilterReturnsAll(t *testing.T) {
require.Equal(t, http.StatusOK, listApprovedRec.Code)
var listApprovedResp map[string]any
- require.NoError(t, json.Unmarshal(listApprovedRec.Body.Bytes(), &listApprovedResp))
+ require.NoError(t, json.Unmarshal(listApprovedRec.Body.Bytes(), &listApprovedResp, jsonx.V1()))
approvedProposals, _ := listApprovedResp["Proposals"].([]any)
assert.Len(t, approvedProposals, 1, "filtering by APPROVED must return only approved proposals")
@@ -671,7 +673,7 @@ func TestHandler_ProposalExpirationDateViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
proposalID, _ := createResp["ProposalId"].(string)
require.NotEmpty(t, proposalID)
@@ -680,7 +682,7 @@ func TestHandler_ProposalExpirationDateViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp, jsonx.V1()))
proposal := getResp["Proposal"].(map[string]any)
assert.NotEmpty(t, proposal["ExpirationDate"])
@@ -706,7 +708,7 @@ func TestHandler_ProposalLifecycleViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
proposalID, _ := createResp["ProposalId"].(string)
require.NotEmpty(t, proposalID)
@@ -716,7 +718,7 @@ func TestHandler_ProposalLifecycleViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp, jsonx.V1()))
proposal := getResp["Proposal"].(map[string]any)
assert.Equal(t, proposalID, proposal["ProposalId"])
@@ -728,7 +730,7 @@ func TestHandler_ProposalLifecycleViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listResp, jsonx.V1()))
proposals := listResp["Proposals"].([]any)
assert.Len(t, proposals, 1)
@@ -737,7 +739,7 @@ func TestHandler_ProposalLifecycleViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec4.Code)
var votesResp map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &votesResp))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &votesResp, jsonx.V1()))
votes := votesResp["ProposalVotes"].([]any)
assert.Empty(t, votes)
}
diff --git a/services/managedblockchain/proposals_voting_test.go b/services/managedblockchain/proposals_voting_test.go
index 4688be42ee..2f5dc2999d 100644
--- a/services/managedblockchain/proposals_voting_test.go
+++ b/services/managedblockchain/proposals_voting_test.go
@@ -1,7 +1,7 @@
package managedblockchain_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/managedblockchain"
)
@@ -175,7 +176,7 @@ func TestHandler_ProposalOutstandingVoteCount(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
p := getResp["Proposal"].(map[string]any)
outstanding := int(p["OutstandingVoteCount"].(float64))
@@ -257,7 +258,7 @@ func TestHandler_ProposalStatusTransitions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createNetResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createNetResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createNetResp, jsonx.V1()))
networkID := createNetResp["NetworkId"].(string)
firstMemberID := createNetResp["MemberId"].(string)
@@ -275,7 +276,7 @@ func TestHandler_ProposalStatusTransitions(t *testing.T) {
require.Equal(t, http.StatusOK, memRec.Code)
var createMemResp map[string]any
- require.NoError(t, json.Unmarshal(memRec.Body.Bytes(), &createMemResp))
+ require.NoError(t, json.Unmarshal(memRec.Body.Bytes(), &createMemResp, jsonx.V1()))
extraMemberIDs = append(extraMemberIDs, createMemResp["MemberId"].(string))
}
@@ -288,7 +289,7 @@ func TestHandler_ProposalStatusTransitions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createPropResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createPropResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createPropResp, jsonx.V1()))
proposalID := createPropResp["ProposalId"].(string)
@@ -313,7 +314,7 @@ func TestHandler_ProposalStatusTransitions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
p := getResp["Proposal"].(map[string]any)
assert.Equal(t, tt.wantStatus, p["Status"])
@@ -353,7 +354,7 @@ func TestHandler_VoteOnProposalAlreadyCompleted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var netResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &netResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &netResp, jsonx.V1()))
networkID := netResp["NetworkId"].(string)
memberID := netResp["MemberId"].(string)
@@ -365,7 +366,7 @@ func TestHandler_VoteOnProposalAlreadyCompleted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var propResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &propResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &propResp, jsonx.V1()))
proposalID := propResp["ProposalId"].(string)
votePath := fmt.Sprintf("/networks/%s/proposals/%s/votes", networkID, proposalID)
@@ -382,7 +383,7 @@ func TestHandler_VoteOnProposalAlreadyCompleted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
p := getResp["Proposal"].(map[string]any)
assert.Equal(t, "APPROVED", p["Status"])
@@ -415,7 +416,7 @@ func TestHandler_VoteThresholdFloatPrecision(t *testing.T) {
require.Equal(t, http.StatusOK, netRec.Code)
var netResp map[string]any
- require.NoError(t, json.Unmarshal(netRec.Body.Bytes(), &netResp))
+ require.NoError(t, json.Unmarshal(netRec.Body.Bytes(), &netResp, jsonx.V1()))
netID := netResp["NetworkId"].(string)
ownerMemberID := netResp["MemberId"].(string)
@@ -450,7 +451,7 @@ func TestHandler_VoteThresholdFloatPrecision(t *testing.T) {
require.Equal(t, http.StatusOK, propRec.Code)
var propResp map[string]any
- require.NoError(t, json.Unmarshal(propRec.Body.Bytes(), &propResp))
+ require.NoError(t, json.Unmarshal(propRec.Body.Bytes(), &propResp, jsonx.V1()))
propID := propResp["ProposalId"].(string)
votePath := fmt.Sprintf("/networks/%s/proposals/%s/votes", netID, propID)
@@ -465,7 +466,7 @@ func TestHandler_VoteThresholdFloatPrecision(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
p := getResp["Proposal"].(map[string]any)
assert.Equal(t, "APPROVED", p["Status"],
@@ -497,7 +498,7 @@ func TestHandler_ApprovedProposalExecutesInvitationActions(t *testing.T) {
require.Equal(t, http.StatusOK, netRec.Code)
var netResp map[string]any
- require.NoError(t, json.Unmarshal(netRec.Body.Bytes(), &netResp))
+ require.NoError(t, json.Unmarshal(netRec.Body.Bytes(), &netResp, jsonx.V1()))
netID := netResp["NetworkId"].(string)
ownerMemberID := netResp["MemberId"].(string)
@@ -507,7 +508,7 @@ func TestHandler_ApprovedProposalExecutesInvitationActions(t *testing.T) {
require.Equal(t, http.StatusOK, listBefore.Code)
var listBeforeResp map[string]any
- require.NoError(t, json.Unmarshal(listBefore.Body.Bytes(), &listBeforeResp))
+ require.NoError(t, json.Unmarshal(listBefore.Body.Bytes(), &listBeforeResp, jsonx.V1()))
assert.Empty(t, listBeforeResp["Invitations"],
"no invitations should exist before proposal approval")
@@ -525,7 +526,7 @@ func TestHandler_ApprovedProposalExecutesInvitationActions(t *testing.T) {
require.Equal(t, http.StatusOK, propRec.Code)
var propResp map[string]any
- require.NoError(t, json.Unmarshal(propRec.Body.Bytes(), &propResp))
+ require.NoError(t, json.Unmarshal(propRec.Body.Bytes(), &propResp, jsonx.V1()))
propID := propResp["ProposalId"].(string)
@@ -540,7 +541,7 @@ func TestHandler_ApprovedProposalExecutesInvitationActions(t *testing.T) {
require.Equal(t, http.StatusOK, listAfter.Code)
var listAfterResp map[string]any
- require.NoError(t, json.Unmarshal(listAfter.Body.Bytes(), &listAfterResp))
+ require.NoError(t, json.Unmarshal(listAfter.Body.Bytes(), &listAfterResp, jsonx.V1()))
invitations, _ := listAfterResp["Invitations"].([]any)
assert.Len(t, invitations, 1,
@@ -573,7 +574,7 @@ func TestHandler_RejectionThresholdImpossibleApproval(t *testing.T) {
require.Equal(t, http.StatusOK, netRec.Code)
var netResp map[string]any
- require.NoError(t, json.Unmarshal(netRec.Body.Bytes(), &netResp))
+ require.NoError(t, json.Unmarshal(netRec.Body.Bytes(), &netResp, jsonx.V1()))
netID := netResp["NetworkId"].(string)
m0ID := netResp["MemberId"].(string)
@@ -594,7 +595,7 @@ func TestHandler_RejectionThresholdImpossibleApproval(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var r map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r, jsonx.V1()))
return r["MemberId"].(string)
}
@@ -610,7 +611,7 @@ func TestHandler_RejectionThresholdImpossibleApproval(t *testing.T) {
require.Equal(t, http.StatusOK, propRec.Code)
var propResp map[string]any
- require.NoError(t, json.Unmarshal(propRec.Body.Bytes(), &propResp))
+ require.NoError(t, json.Unmarshal(propRec.Body.Bytes(), &propResp, jsonx.V1()))
propID := propResp["ProposalId"].(string)
votePath := fmt.Sprintf("/networks/%s/proposals/%s/votes", netID, propID)
@@ -631,7 +632,7 @@ func TestHandler_RejectionThresholdImpossibleApproval(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
p := getResp["Proposal"].(map[string]any)
assert.Equal(t, "REJECTED", p["Status"],
@@ -662,7 +663,7 @@ func TestHandler_ProposalExpiresAfterExpirationDate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
p, ok := resp["Proposal"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "EXPIRED", p["Status"])
@@ -677,7 +678,7 @@ func TestHandler_ProposalExpiresAfterExpirationDate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
proposals, ok := resp["Proposals"].([]any)
require.True(t, ok)
require.Len(t, proposals, 1)
@@ -741,7 +742,7 @@ func TestHandler_ApprovedRemovalProposalCascadeDeletesEmptyNetwork(t *testing.T)
require.Equal(t, http.StatusOK, netRec.Code)
var netResp map[string]any
- require.NoError(t, json.Unmarshal(netRec.Body.Bytes(), &netResp))
+ require.NoError(t, json.Unmarshal(netRec.Body.Bytes(), &netResp, jsonx.V1()))
netID := netResp["NetworkId"].(string)
ownerMemberID := netResp["MemberId"].(string)
@@ -759,7 +760,7 @@ func TestHandler_ApprovedRemovalProposalCascadeDeletesEmptyNetwork(t *testing.T)
require.Equal(t, http.StatusOK, propRec.Code)
var propResp map[string]any
- require.NoError(t, json.Unmarshal(propRec.Body.Bytes(), &propResp))
+ require.NoError(t, json.Unmarshal(propRec.Body.Bytes(), &propResp, jsonx.V1()))
propID := propResp["ProposalId"].(string)
@@ -802,7 +803,7 @@ func TestHandler_ApprovedProposalActionFailedWhenTargetMemberAlreadyGone(t *test
require.Equal(t, http.StatusOK, netRec.Code)
var netResp map[string]any
- require.NoError(t, json.Unmarshal(netRec.Body.Bytes(), &netResp))
+ require.NoError(t, json.Unmarshal(netRec.Body.Bytes(), &netResp, jsonx.V1()))
netID := netResp["NetworkId"].(string)
ownerID := netResp["MemberId"].(string)
@@ -823,7 +824,7 @@ func TestHandler_ApprovedProposalActionFailedWhenTargetMemberAlreadyGone(t *test
require.Equal(t, http.StatusOK, propRec.Code)
var propResp map[string]any
- require.NoError(t, json.Unmarshal(propRec.Body.Bytes(), &propResp))
+ require.NoError(t, json.Unmarshal(propRec.Body.Bytes(), &propResp, jsonx.V1()))
propID := propResp["ProposalId"].(string)
@@ -842,7 +843,7 @@ func TestHandler_ApprovedProposalActionFailedWhenTargetMemberAlreadyGone(t *test
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
proposal, ok := getResp["Proposal"].(map[string]any)
require.True(t, ok)
diff --git a/services/managedblockchain/store_test.go b/services/managedblockchain/store_test.go
index df45f9acfb..b08fc0cfa5 100644
--- a/services/managedblockchain/store_test.go
+++ b/services/managedblockchain/store_test.go
@@ -2,7 +2,8 @@ package managedblockchain_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/managedblockchain"
)
@@ -61,7 +63,7 @@ func doRequest(t *testing.T, h *managedblockchain.Handler, method, path string,
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -133,7 +135,7 @@ func doRoutedRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -206,7 +208,7 @@ func createTestNetwork(t *testing.T, h *managedblockchain.Handler) (string, stri
NetworkID string `json:"NetworkId"`
MemberID string `json:"MemberId"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
return out.NetworkID, out.MemberID
}
diff --git a/services/managedblockchain/tags_test.go b/services/managedblockchain/tags_test.go
index 088bc4d2f7..858a99ffaa 100644
--- a/services/managedblockchain/tags_test.go
+++ b/services/managedblockchain/tags_test.go
@@ -1,7 +1,7 @@
package managedblockchain_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/managedblockchain"
)
@@ -138,7 +139,7 @@ func TestHandler_TagOperations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
// Get the network to find its ARN
networkID := createResp["NetworkId"].(string)
@@ -146,7 +147,7 @@ func TestHandler_TagOperations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var netResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &netResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &netResp, jsonx.V1()))
network := netResp["Network"].(map[string]any)
arn := network["Arn"].(string)
assert.NotEmpty(t, arn)
@@ -161,7 +162,7 @@ func TestHandler_TagOperations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags := tagsResp["Tags"].(map[string]any)
assert.Equal(t, "test", tags["env"])
@@ -233,7 +234,7 @@ func TestHandler_UntagResourceQuery(t *testing.T) {
require.Equal(t, http.StatusOK, netRec.Code)
var netResp map[string]any
- require.NoError(t, json.Unmarshal(netRec.Body.Bytes(), &netResp))
+ require.NoError(t, json.Unmarshal(netRec.Body.Bytes(), &netResp, jsonx.V1()))
network := netResp["Network"].(map[string]any)
arn := network["Arn"].(string)
@@ -298,7 +299,7 @@ func TestHandler_TagNodeViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
tags := resp["Tags"].(map[string]any)
assert.Equal(t, "gold", tags["tier"])
diff --git a/services/marketplaceentitlementservice/cbor.go b/services/marketplaceentitlementservice/cbor.go
index 84aff4ef14..651350c58a 100644
--- a/services/marketplaceentitlementservice/cbor.go
+++ b/services/marketplaceentitlementservice/cbor.go
@@ -3,12 +3,15 @@ package marketplaceentitlementservice
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"math"
"strconv"
"strings"
"github.com/aws/smithy-go/encoding/cbor"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// The pinned SDK speaks Smithy RPCv2 CBOR (POST /service/AWSMPEntitlementService/operation/);
@@ -33,7 +36,7 @@ func cborToJSONBytes(body []byte) ([]byte, error) {
return nil, fail(ErrInvalidParameter, "expected a CBOR map")
}
- return json.Marshal(cborToAny(m))
+ return json.Marshal(cborToAny(m), jsonx.V1())
}
func cborToAny(v cbor.Value) any {
@@ -55,13 +58,13 @@ func cborToAny(v cbor.Value) any {
case cbor.String:
return string(t)
case cbor.Uint:
- return json.Number(strconv.FormatUint(uint64(t), 10))
+ return jsonx.Number(strconv.FormatUint(uint64(t), 10))
case cbor.NegInt:
if uint64(t) > math.MaxInt64 {
return float64(-1) - float64(t)
}
- return json.Number(strconv.FormatInt(-1-int64(t), 10)) //nolint:gosec // bounded by the MaxInt64 check above
+ return jsonx.Number(strconv.FormatInt(-1-int64(t), 10)) //nolint:gosec // bounded by the MaxInt64 check above
case cbor.Float64:
return float64(t)
case cbor.Float32:
@@ -78,11 +81,10 @@ func cborToAny(v cbor.Value) any {
}
func jsonToCBORBytes(body []byte) ([]byte, error) {
- dec := json.NewDecoder(bytes.NewReader(body))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(body))
var v any
- if err := dec.Decode(&v); err != nil {
+ if err := json.UnmarshalDecode(dec, &v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, fail(ErrInternalServiceError, "encode response: %v", err)
}
@@ -109,14 +111,14 @@ func anyToCBOR(v any, key string) cbor.Value {
return cbor.String(t)
case bool:
return cbor.Bool(t)
- case json.Number:
+ case jsonx.Number:
return numberToCBOR(t, cborTimeKeys[key])
default:
return &cbor.Nil{}
}
}
-func numberToCBOR(n json.Number, timestamp bool) cbor.Value {
+func numberToCBOR(n jsonx.Number, timestamp bool) cbor.Value {
if !timestamp && !strings.ContainsAny(string(n), ".eE") {
if i, err := n.Int64(); err == nil {
if i >= 0 {
diff --git a/services/marketplaceentitlementservice/handler.go b/services/marketplaceentitlementservice/handler.go
index 82f1326fdc..85dca8cac0 100644
--- a/services/marketplaceentitlementservice/handler.go
+++ b/services/marketplaceentitlementservice/handler.go
@@ -1,7 +1,7 @@
package marketplaceentitlementservice
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"slices"
@@ -11,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -46,7 +47,7 @@ func decode(body []byte, v any) error {
return nil
}
- if err := json.Unmarshal(body, v); err != nil {
+ if err := json.Unmarshal(body, v, jsonx.V1()); err != nil {
return fail(ErrInvalidParameter, "malformed request body: %v", err)
}
@@ -148,7 +149,7 @@ func (h *Handler) Handler() echo.HandlerFunc {
}
func (h *Handler) writeCBOR(c *echo.Context, out any) error {
- raw, err := json.Marshal(out)
+ raw, err := json.Marshal(out, jsonx.V1())
if err == nil {
var enc []byte
diff --git a/services/marketplaceentitlementservice/persistence.go b/services/marketplaceentitlementservice/persistence.go
index e008500154..c664f97fce 100644
--- a/services/marketplaceentitlementservice/persistence.go
+++ b/services/marketplaceentitlementservice/persistence.go
@@ -2,7 +2,7 @@ package marketplaceentitlementservice
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,10 +12,10 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/marketplacemetering/customers.go b/services/marketplacemetering/customers.go
index 77cd241b3d..24318f4f85 100644
--- a/services/marketplacemetering/customers.go
+++ b/services/marketplacemetering/customers.go
@@ -5,11 +5,12 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"strings"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ent "github.com/blackbirdworks/gopherstack/services/marketplaceentitlementservice"
)
@@ -236,7 +237,7 @@ func (b *InMemoryBackend) IssueRegistrationToken(productCode, accountID string)
issued := b.now().Unix()
b.mu.RUnlock()
- raw, _ := json.Marshal(tokenClaims{P: productCode, A: accountID, I: issued})
+ raw, _ := json.Marshal(tokenClaims{P: productCode, A: accountID, I: issued}, jsonx.V1())
payload := base64.RawURLEncoding.EncodeToString(raw)
return payload + "." + mac(payload), nil
@@ -257,7 +258,8 @@ func (b *InMemoryBackend) ResolveCustomer(token string) (*ResolvedCustomer, erro
var claims tokenClaims
- if err != nil || json.Unmarshal(raw, &claims) != nil || !validProductCode(claims.P) || !validAccountID(claims.A) {
+ if err != nil || json.Unmarshal(raw, &claims, jsonx.V1()) != nil || !validProductCode(claims.P) ||
+ !validAccountID(claims.A) {
return nil, fail(ErrInvalidToken, "the registration token is not valid")
}
diff --git a/services/marketplacemetering/customers_test.go b/services/marketplacemetering/customers_test.go
index b250d0d20a..e898132092 100644
--- a/services/marketplacemetering/customers_test.go
+++ b/services/marketplacemetering/customers_test.go
@@ -2,7 +2,7 @@ package marketplacemetering_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
"time"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
mm "github.com/blackbirdworks/gopherstack/services/marketplacemetering"
)
@@ -87,7 +88,7 @@ func TestRegisterUsage(t *testing.T) {
require.NoError(t, decErr)
var claims map[string]any
- require.NoError(t, json.Unmarshal(raw, &claims))
+ require.NoError(t, json.Unmarshal(raw, &claims, jsonx.V1()))
assert.Equal(t, tt.product, claims["productCode"])
assert.Equal(t, "n-1", claims["nonce"])
assert.Equal(t, testAccount, claims["customerAWSAccountId"])
diff --git a/services/marketplacemetering/handler.go b/services/marketplacemetering/handler.go
index f9d559dc86..cc2733c612 100644
--- a/services/marketplacemetering/handler.go
+++ b/services/marketplacemetering/handler.go
@@ -2,7 +2,7 @@ package marketplacemetering
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"slices"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -82,7 +83,7 @@ func decode(body []byte, v any) error {
return nil
}
- if err := json.Unmarshal(body, v); err != nil {
+ if err := json.Unmarshal(body, v, jsonx.V1()); err != nil {
return fail(ErrValidation, "malformed request body: %v", err)
}
diff --git a/services/marketplacemetering/persistence.go b/services/marketplacemetering/persistence.go
index 61161f167f..39f22270ce 100644
--- a/services/marketplacemetering/persistence.go
+++ b/services/marketplacemetering/persistence.go
@@ -2,7 +2,7 @@ package marketplacemetering
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,10 +12,10 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/marketplacemetering/register.go b/services/marketplacemetering/register.go
index 3db3223d05..42c26faef7 100644
--- a/services/marketplacemetering/register.go
+++ b/services/marketplacemetering/register.go
@@ -3,11 +3,12 @@ package marketplacemetering
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"strconv"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// RegisterUsageInput is the RegisterUsage request.
@@ -67,11 +68,11 @@ func signRegistration(p *Product, in *RegisterUsageInput, account string, issued
enc := base64.RawURLEncoding
header, _ := json.Marshal(map[string]string{
"alg": "HS256", "typ": "JWT", "kid": "v" + strconv.Itoa(int(p.PublicKeyVersion)),
- })
+ }, jsonx.V1())
claims, _ := json.Marshal(map[string]any{
"productCode": in.ProductCode, "nonce": in.Nonce, "customerAWSAccountId": account,
"publicKeyVersion": p.PublicKeyVersion, "iat": issued,
- })
+ }, jsonx.V1())
signing := enc.EncodeToString(header) + "." + enc.EncodeToString(claims)
return signing + "." + enc.EncodeToString([]byte(mac(signing)))
diff --git a/services/mediaconvert/endpoints_test.go b/services/mediaconvert/endpoints_test.go
index 49036e6033..897666ac8c 100644
--- a/services/mediaconvert/endpoints_test.go
+++ b/services/mediaconvert/endpoints_test.go
@@ -2,7 +2,8 @@ package mediaconvert_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediaconvert"
)
@@ -41,7 +43,7 @@ func TestDescribeEndpoints_POST(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
endpoints, ok := out["endpoints"].([]any)
require.True(t, ok)
require.Len(t, endpoints, 1)
@@ -73,7 +75,7 @@ func TestDescribeEndpoints_WithModeAndMaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
endpoints, ok := out["endpoints"].([]any)
require.True(t, ok)
require.Len(t, endpoints, 1)
@@ -93,7 +95,7 @@ func TestDescribeEndpoints_MaxResultsCaps(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
endpoints, ok := out["endpoints"].([]any)
require.True(t, ok)
require.Len(t, endpoints, 1)
diff --git a/services/mediaconvert/handler_certificates.go b/services/mediaconvert/handler_certificates.go
index c1fda617d2..e9318f7c55 100644
--- a/services/mediaconvert/handler_certificates.go
+++ b/services/mediaconvert/handler_certificates.go
@@ -1,11 +1,13 @@
package mediaconvert
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseCertificateRoute(method, suffix string) mcRoute {
@@ -34,7 +36,7 @@ type associateCertificateInput struct {
func (h *Handler) handleAssociateCertificate(c *echo.Context, body []byte) error {
var in associateCertificateInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/mediaconvert/handler_endpoints.go b/services/mediaconvert/handler_endpoints.go
index e8f314d7ab..905460443f 100644
--- a/services/mediaconvert/handler_endpoints.go
+++ b/services/mediaconvert/handler_endpoints.go
@@ -1,10 +1,12 @@
package mediaconvert
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- Endpoints handler ---
@@ -27,7 +29,7 @@ type endpointEntry struct {
func (h *Handler) handleDescribeEndpoints(c *echo.Context, body []byte) error {
var in describeEndpointsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
}
diff --git a/services/mediaconvert/handler_job_templates.go b/services/mediaconvert/handler_job_templates.go
index faac04a49d..08ee60a3d7 100644
--- a/services/mediaconvert/handler_job_templates.go
+++ b/services/mediaconvert/handler_job_templates.go
@@ -1,12 +1,13 @@
package mediaconvert
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -60,7 +61,7 @@ type jobTemplatesListOutput struct {
func (h *Handler) handleCreateJobTemplate(c *echo.Context, body []byte) error {
var in createJobTemplateInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -138,7 +139,7 @@ type updateJobTemplateInput struct {
func (h *Handler) handleUpdateJobTemplate(c *echo.Context, name string, body []byte) error {
var in updateJobTemplateInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/mediaconvert/handler_jobs.go b/services/mediaconvert/handler_jobs.go
index 0efa62f2be..985fb45d2c 100644
--- a/services/mediaconvert/handler_jobs.go
+++ b/services/mediaconvert/handler_jobs.go
@@ -1,12 +1,13 @@
package mediaconvert
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -67,7 +68,7 @@ type jobsListOutput struct {
func (h *Handler) handleCreateJob(c *echo.Context, body []byte) error {
var in createJobInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -113,7 +114,7 @@ type updateJobInput struct {
func (h *Handler) handleUpdateJob(c *echo.Context, id string, body []byte) error {
var in updateJobInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/mediaconvert/handler_jobs_query.go b/services/mediaconvert/handler_jobs_query.go
index a7b2c2e80c..73d4386dc7 100644
--- a/services/mediaconvert/handler_jobs_query.go
+++ b/services/mediaconvert/handler_jobs_query.go
@@ -1,11 +1,13 @@
package mediaconvert
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parseJobsQueriesRoute(method, suffix string) mcRoute {
@@ -61,7 +63,7 @@ type startJobsQueryOutput struct {
func (h *Handler) handleStartJobsQuery(c *echo.Context, body []byte) error {
var in startJobsQueryInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/mediaconvert/handler_policy.go b/services/mediaconvert/handler_policy.go
index 584cb4ff63..9c2c02817b 100644
--- a/services/mediaconvert/handler_policy.go
+++ b/services/mediaconvert/handler_policy.go
@@ -1,10 +1,12 @@
package mediaconvert
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func parsePolicyRoute(method string) mcRoute {
@@ -55,7 +57,7 @@ type policyInputEntry struct {
func (h *Handler) handlePutPolicy(c *echo.Context, body []byte) error {
var in putPolicyInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/mediaconvert/handler_presets.go b/services/mediaconvert/handler_presets.go
index 46f3cb2068..2ca68a8446 100644
--- a/services/mediaconvert/handler_presets.go
+++ b/services/mediaconvert/handler_presets.go
@@ -1,12 +1,13 @@
package mediaconvert
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -55,7 +56,7 @@ type presetsListOutput struct {
func (h *Handler) handleCreatePreset(c *echo.Context, body []byte) error {
var in createPresetInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -120,7 +121,7 @@ type updatePresetInput struct {
func (h *Handler) handleUpdatePreset(c *echo.Context, name string, body []byte) error {
var in updatePresetInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/mediaconvert/handler_probe.go b/services/mediaconvert/handler_probe.go
index 2bd1ec93d9..f904e47523 100644
--- a/services/mediaconvert/handler_probe.go
+++ b/services/mediaconvert/handler_probe.go
@@ -1,10 +1,12 @@
package mediaconvert
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- Probe handler ---
@@ -19,7 +21,7 @@ type probeOutput struct {
func (h *Handler) handleProbe(c *echo.Context, body []byte) error {
var in probeInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/mediaconvert/handler_queues.go b/services/mediaconvert/handler_queues.go
index f8e0e4b64d..8864d2e773 100644
--- a/services/mediaconvert/handler_queues.go
+++ b/services/mediaconvert/handler_queues.go
@@ -1,12 +1,13 @@
package mediaconvert
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -62,7 +63,7 @@ type queuesListOutput struct {
func (h *Handler) handleCreateQueue(c *echo.Context, body []byte) error {
var in createQueueInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -124,7 +125,7 @@ type updateQueueInput struct {
func (h *Handler) handleUpdateQueue(c *echo.Context, name string, body []byte) error {
var in updateQueueInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/mediaconvert/handler_resource_shares.go b/services/mediaconvert/handler_resource_shares.go
index 5e2092ddb9..89df8d11f2 100644
--- a/services/mediaconvert/handler_resource_shares.go
+++ b/services/mediaconvert/handler_resource_shares.go
@@ -1,10 +1,12 @@
package mediaconvert
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- Resource share handlers ---
@@ -16,7 +18,7 @@ type createResourceShareInput struct {
func (h *Handler) handleCreateResourceShare(c *echo.Context, body []byte) error {
var in createResourceShareInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/mediaconvert/handler_tags.go b/services/mediaconvert/handler_tags.go
index 5740771c51..d97f9b1cd4 100644
--- a/services/mediaconvert/handler_tags.go
+++ b/services/mediaconvert/handler_tags.go
@@ -1,11 +1,13 @@
package mediaconvert
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// parseTagRoute maps tag-path requests to operations. Per the real
@@ -70,7 +72,7 @@ func (h *Handler) handleListTagsForResource(c *echo.Context, resourceARN string)
func (h *Handler) handleTagResource(c *echo.Context, body []byte) error {
var in tagResourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -85,7 +87,7 @@ func (h *Handler) handleTagResource(c *echo.Context, body []byte) error {
func (h *Handler) handleUntagResource(c *echo.Context, resourceARN string, body []byte) error {
var in untagResourceInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/mediaconvert/handler_test.go b/services/mediaconvert/handler_test.go
index 3feef9e596..c3272fad80 100644
--- a/services/mediaconvert/handler_test.go
+++ b/services/mediaconvert/handler_test.go
@@ -2,7 +2,8 @@ package mediaconvert_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediaconvert"
)
@@ -30,7 +32,7 @@ func doRequest(t *testing.T, h *mediaconvert.Handler, method, path string, body
var reqBody *bytes.Reader
if body != nil {
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
reqBody = bytes.NewReader(bodyBytes)
} else {
@@ -71,7 +73,7 @@ func parseJSONResponse(t *testing.T, h *mediaconvert.Handler, method, path strin
var resp map[string]any
if rec.Body.Len() > 0 {
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
}
return resp, rec.Code
@@ -357,7 +359,7 @@ func TestErrValidationMappedTo400(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
job := resp["job"].(map[string]any)
id := job["id"].(string)
@@ -406,7 +408,7 @@ func TestListHandlers_ReturnEmptySlices(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
list, ok := resp[tt.key]
require.True(t, ok, "key %q missing from response", tt.key)
diff --git a/services/mediaconvert/janitor_test.go b/services/mediaconvert/janitor_test.go
index 93240829e3..9b5b88f3ed 100644
--- a/services/mediaconvert/janitor_test.go
+++ b/services/mediaconvert/janitor_test.go
@@ -1,7 +1,8 @@
package mediaconvert_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediaconvert"
)
@@ -404,7 +406,7 @@ func TestFullJobLifecycle_ViaHTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
jobData := out["job"].(map[string]any)
jobID := jobData["id"].(string)
assert.Equal(t, "SUBMITTED", jobData["status"])
@@ -418,7 +420,7 @@ func TestFullJobLifecycle_ViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out2 map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out2, jsonx.V1()))
j2 := out2["job"].(map[string]any)
assert.Equal(t, "COMPLETE", j2["status"])
timing := j2["timing"].(map[string]any)
diff --git a/services/mediaconvert/job_templates_test.go b/services/mediaconvert/job_templates_test.go
index f22ef32d39..a7a6e3da5f 100644
--- a/services/mediaconvert/job_templates_test.go
+++ b/services/mediaconvert/job_templates_test.go
@@ -1,13 +1,15 @@
package mediaconvert_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediaconvert"
)
@@ -27,7 +29,7 @@ func TestMediaConvert_JobTemplate_FullLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
jtData, ok := createResp["jobTemplate"].(map[string]any)
require.True(t, ok)
assert.Equal(t, templateName, jtData["name"])
@@ -53,7 +55,7 @@ func TestMediaConvert_JobTemplate_FullLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&updateResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &updateResp, jsonx.V1()))
updatedJT, ok := updateResp["jobTemplate"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "updated description", updatedJT["description"])
@@ -118,7 +120,7 @@ func TestCreateJobTemplate_WithTags(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
jt := resp["jobTemplate"].(map[string]any)
tags := jt["tags"].(map[string]any)
assert.Equal(t, "12345", tags["cost-center"])
@@ -165,7 +167,7 @@ func TestCreateJobTemplate_AccelerationHopDestinationsStatusUpdateInterval(t *te
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
jt := resp["jobTemplate"].(map[string]any)
accel := jt["accelerationSettings"].(map[string]any)
@@ -238,7 +240,7 @@ func TestUpdateJobTemplate_ViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
jt := resp["jobTemplate"].(map[string]any)
accel := jt["accelerationSettings"].(map[string]any)
diff --git a/services/mediaconvert/jobs_test.go b/services/mediaconvert/jobs_test.go
index 7543ff2220..cc5a04bbf4 100644
--- a/services/mediaconvert/jobs_test.go
+++ b/services/mediaconvert/jobs_test.go
@@ -1,7 +1,8 @@
package mediaconvert_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediaconvert"
)
@@ -23,7 +25,7 @@ func createTestJob(t *testing.T, h *mediaconvert.Handler) string {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
job, ok := resp["job"].(map[string]any)
require.True(t, ok)
id, _ := job["id"].(string)
@@ -68,7 +70,7 @@ func TestMediaConvert_Job_FullLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
jobData, ok := createResp["job"].(map[string]any)
require.True(t, ok)
assert.Equal(t, role, jobData["role"])
@@ -130,7 +132,7 @@ func TestCreateJob_WithTags(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
j := resp["job"].(map[string]any)
tags := j["tags"].(map[string]any)
assert.Equal(t, "nightly", tags["run"])
@@ -279,7 +281,7 @@ func TestListJobs_OmitsTotalCount(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotContains(t, out, "totalCount")
assert.Len(t, out["jobs"], 2)
}
@@ -336,7 +338,7 @@ func TestCreateJob_TimingViaHTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
job := out["job"].(map[string]any)
timing := job["timing"].(map[string]any)
assert.Greater(t, timing["submitTime"], float64(0))
@@ -463,7 +465,7 @@ func TestUpdateJob_ViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
jobData, ok := out["job"].(map[string]any)
require.True(t, ok)
assert.InDelta(t, float64(5), jobData["priority"], 0)
@@ -511,7 +513,7 @@ func TestCreateJob_HopDestinationsViaHTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
jobData := out["job"].(map[string]any)
hops, ok := jobData["hopDestinations"].([]any)
require.True(t, ok)
@@ -588,7 +590,7 @@ func TestCreateJob_StatusUpdateIntervalViaHTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
jobData := out["job"].(map[string]any)
assert.Equal(t, "SECONDS_20", jobData["statusUpdateInterval"])
assert.Equal(t, "ENABLED", jobData["simulateReservedQueue"])
@@ -608,7 +610,7 @@ func TestCreateJob_AccelerationViaHTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
jobData := out["job"].(map[string]any)
assert.Equal(t, "PREFERRED", jobData["accelerationStatus"])
as := jobData["accelerationSettings"].(map[string]any)
@@ -688,7 +690,7 @@ func TestCreateJob_MessagesFieldViaHTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
jobData := out["job"].(map[string]any)
_, hasMessages := jobData["messages"]
assert.True(t, hasMessages, "messages field should be present")
@@ -786,14 +788,14 @@ func TestCreateJob_ClientRequestTokenDedupViaHTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.NewDecoder(rec1.Body).Decode(&out1))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec1.Body), &out1, jsonx.V1()))
id1 := out1["job"].(map[string]any)["id"].(string)
rec2 := doRequest(t, h, http.MethodPost, "/2017-08-29/jobs", body)
require.Equal(t, http.StatusCreated, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&out2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &out2, jsonx.V1()))
id2 := out2["job"].(map[string]any)["id"].(string)
assert.Equal(t, id1, id2, "duplicate token should return same job ID")
@@ -872,7 +874,7 @@ func TestCreateJob_JobEngineVersionViaHTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
jobData := out["job"].(map[string]any)
assert.Equal(t, "2017-08-29", jobData["jobEngineVersionUsed"])
assert.Equal(t, "2017-08-29", jobData["jobEngineVersionRequested"])
@@ -956,7 +958,7 @@ func TestCreateJob_PriorityViaHTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
jobData := out["job"].(map[string]any)
assert.InDelta(t, float64(15), jobData["priority"], 0)
}
@@ -1188,7 +1190,7 @@ func TestListJobs_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
jobs, ok := out["jobs"].([]any)
require.True(t, ok)
diff --git a/services/mediaconvert/multi_region_test.go b/services/mediaconvert/multi_region_test.go
index e1921199bc..7a174f4823 100644
--- a/services/mediaconvert/multi_region_test.go
+++ b/services/mediaconvert/multi_region_test.go
@@ -2,7 +2,8 @@ package mediaconvert_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/mediaconvert"
)
@@ -172,8 +174,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/mediaconvert/persistence.go b/services/mediaconvert/persistence.go
index abce59e334..3eb22ed0ba 100644
--- a/services/mediaconvert/persistence.go
+++ b/services/mediaconvert/persistence.go
@@ -2,10 +2,12 @@ package mediaconvert
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -75,7 +77,7 @@ func newDirtyDTORegistry() (*store.Registry, *store.Table[counterSnapshot], *sto
// absent: it was never persisted pre-Phase-3.3 either (see the old Restore,
// which always reset it to empty).
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
Certificates map[string]struct{} `json:"certificates"`
Policy *Policy `json:"policy,omitempty"`
@@ -142,7 +144,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.region,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "mediaconvert: failed to marshal snapshot", "error", err)
@@ -222,7 +224,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreDirtyTablesLocked restores the "dirty" tables (queueCounters,
// tokenIndex) from tables via the ephemeral DTO registry, converting each DTO
// back to its live type. The caller MUST hold b.mu for writing.
-func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]jsontext.Value) error {
dtoReg, cDTOs, tDTOs := newDirtyDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
diff --git a/services/mediaconvert/policy_test.go b/services/mediaconvert/policy_test.go
index 27698a9271..ac0cbebac8 100644
--- a/services/mediaconvert/policy_test.go
+++ b/services/mediaconvert/policy_test.go
@@ -1,13 +1,15 @@
package mediaconvert_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediaconvert"
)
@@ -86,7 +88,7 @@ func TestMediaConvert_Policy_DeleteAfterGet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
policy, ok := out["policy"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "ALLOWED", policy["httpInputs"])
@@ -114,7 +116,7 @@ func TestPutPolicy_Success(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
pol, ok := out["policy"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "ALLOWED", pol["httpInputs"])
@@ -142,7 +144,7 @@ func TestGetPolicy_AfterPut(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
pol := out["policy"].(map[string]any)
assert.Equal(t, "DISABLED", pol["s3Inputs"])
}
diff --git a/services/mediaconvert/presets_test.go b/services/mediaconvert/presets_test.go
index 5547f44942..f4a083ea63 100644
--- a/services/mediaconvert/presets_test.go
+++ b/services/mediaconvert/presets_test.go
@@ -1,7 +1,8 @@
package mediaconvert_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediaconvert"
)
@@ -28,7 +30,7 @@ func TestMediaConvert_Preset_FullLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
pData, ok := createResp["preset"].(map[string]any)
require.True(t, ok)
assert.Equal(t, presetName, pData["name"])
@@ -135,7 +137,7 @@ func TestCreatePreset_WithTags(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
p := resp["preset"].(map[string]any)
tags := p["tags"].(map[string]any)
assert.Equal(t, "alpha", tags["project"])
@@ -216,7 +218,7 @@ func TestUpdatePreset_Success(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
preset, ok := out["preset"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "updated desc", preset["description"])
diff --git a/services/mediaconvert/queues_test.go b/services/mediaconvert/queues_test.go
index d6d4b13685..c1093cd51b 100644
--- a/services/mediaconvert/queues_test.go
+++ b/services/mediaconvert/queues_test.go
@@ -1,13 +1,15 @@
package mediaconvert_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediaconvert"
)
@@ -59,7 +61,7 @@ func TestMediaConvert_Queue_FullLifecycle(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
queueData, ok := createResp["queue"].(map[string]any)
require.True(t, ok)
assert.Equal(t, queueName, queueData["name"])
@@ -86,7 +88,7 @@ func TestMediaConvert_Queue_FullLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&updateResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &updateResp, jsonx.V1()))
updatedQueue, ok := updateResp["queue"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "PAUSED", updatedQueue["status"])
@@ -140,7 +142,7 @@ func TestMediaConvert_ListQueues_Empty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
queues, ok := out["queues"].([]any)
require.True(t, ok)
require.Len(t, queues, 1)
@@ -191,7 +193,7 @@ func TestMediaConvert_UpdateQueue_ConcurrentJobsAndReservationPlanSettings(t *te
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
queueData := out["queue"].(map[string]any)
assert.InDelta(t, float64(7), queueData["concurrentJobs"], 0)
@@ -248,7 +250,7 @@ func TestMediaConvert_CreateQueue_MaximumConcurrentFeeds(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
createQueue, ok := createResp["queue"].(map[string]any)
require.True(t, ok)
tt.check(t, createQueue)
@@ -258,7 +260,7 @@ func TestMediaConvert_CreateQueue_MaximumConcurrentFeeds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
getQueue, ok := getResp["queue"].(map[string]any)
require.True(t, ok)
tt.check(t, getQueue)
@@ -284,7 +286,7 @@ func TestMediaConvert_UpdateQueue_MaximumConcurrentFeeds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
queueData, ok := out["queue"].(map[string]any)
require.True(t, ok)
assert.InDelta(t, float64(9), queueData["maximumConcurrentFeeds"], 0)
@@ -319,7 +321,7 @@ func TestCreateQueue_WithTags(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
queue := resp["queue"].(map[string]any)
tags := queue["tags"].(map[string]any)
assert.Equal(t, "prod", tags["env"])
@@ -459,7 +461,7 @@ func TestListQueues_JobCountsViaHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
queues, ok := out["queues"].([]any)
require.True(t, ok)
require.Len(t, queues, 2)
@@ -505,7 +507,7 @@ func TestCreateQueue_ReservationPlanViaHTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
queueData := out["queue"].(map[string]any)
rp, ok := queueData["reservationPlan"].(map[string]any)
require.True(t, ok, "reservationPlan should be in response")
@@ -525,7 +527,7 @@ func TestQueue_NoLegacyReservationPlanNameField(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
queueData := out["queue"].(map[string]any)
_, hasOldField := queueData["reservationPlanName"]
assert.False(t, hasOldField, "legacy reservationPlanName field should not be present")
@@ -555,7 +557,7 @@ func TestCreateQueue_ConcurrentJobsViaHTTP(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
queueData := out["queue"].(map[string]any)
assert.InDelta(t, float64(4), queueData["concurrentJobs"], 0)
}
@@ -576,7 +578,7 @@ func TestCreateQueue_ConcurrentJobsZeroDistinctFromUnset(t *testing.T) {
require.Equal(t, http.StatusCreated, unset.Code)
var unsetOut map[string]any
- require.NoError(t, json.NewDecoder(unset.Body).Decode(&unsetOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(unset.Body), &unsetOut, jsonx.V1()))
unsetQueue := unsetOut["queue"].(map[string]any)
_, hasField := unsetQueue["concurrentJobs"]
assert.False(t, hasField, "concurrentJobs must be omitted entirely when the client never sent it")
@@ -588,7 +590,7 @@ func TestCreateQueue_ConcurrentJobsZeroDistinctFromUnset(t *testing.T) {
require.Equal(t, http.StatusCreated, explicitZero.Code)
var zeroOut map[string]any
- require.NoError(t, json.NewDecoder(explicitZero.Body).Decode(&zeroOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(explicitZero.Body), &zeroOut, jsonx.V1()))
zeroQueue := zeroOut["queue"].(map[string]any)
zeroVal, hasZeroField := zeroQueue["concurrentJobs"]
require.True(t, hasZeroField, "an explicit concurrentJobs:0 must round-trip as a present field, not be dropped")
diff --git a/services/mediaconvert/resource_shares.go b/services/mediaconvert/resource_shares.go
index 35bd9a957b..cbc130061f 100644
--- a/services/mediaconvert/resource_shares.go
+++ b/services/mediaconvert/resource_shares.go
@@ -1,11 +1,13 @@
package mediaconvert
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// CreateResourceShare records a resource-share request for the given job ID.
@@ -38,7 +40,7 @@ func (b *InMemoryBackend) CreateResourceShare(jobID, supportCaseID string) (stri
details, err := json.Marshal(struct {
ShareToken string `json:"shareToken"`
SharedAt float64 `json:"sharedAt"`
- }{ShareToken: token, SharedAt: epochSeconds(time.Now())})
+ }{ShareToken: token, SharedAt: epochSeconds(time.Now())}, jsonx.V1())
if err != nil {
return "", fmt.Errorf("encode share details: %w", err)
}
diff --git a/services/mediaconvert/resource_shares_test.go b/services/mediaconvert/resource_shares_test.go
index 6a1b0c0750..18e4350b5b 100644
--- a/services/mediaconvert/resource_shares_test.go
+++ b/services/mediaconvert/resource_shares_test.go
@@ -1,13 +1,15 @@
package mediaconvert_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediaconvert"
)
@@ -20,7 +22,7 @@ func TestMediaConvert_CreateResourceShare_TableTests(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
job, _ := resp["job"].(map[string]any)
id, _ := job["id"].(string)
@@ -121,7 +123,7 @@ func TestCreateResourceShare_SetsShareStatus(t *testing.T) {
ShareToken string `json:"shareToken"`
SharedAt float64 `json:"sharedAt"`
}
- require.NoError(t, json.Unmarshal([]byte(*got.LastShareDetails), &details))
+ require.NoError(t, json.Unmarshal([]byte(*got.LastShareDetails), &details, jsonx.V1()))
assert.NotEmpty(t, details.ShareToken)
assert.Greater(t, details.SharedAt, float64(0))
}
diff --git a/services/mediaconvert/search_test.go b/services/mediaconvert/search_test.go
index 6c831c3520..cafbc5a779 100644
--- a/services/mediaconvert/search_test.go
+++ b/services/mediaconvert/search_test.go
@@ -1,13 +1,15 @@
package mediaconvert_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediaconvert"
)
@@ -74,7 +76,7 @@ func TestSearchJobs_QueueFilter(t *testing.T) {
require.Equal(t, http.StatusCreated, recQ.Code)
var qResp map[string]any
- require.NoError(t, json.Unmarshal(recQ.Body.Bytes(), &qResp))
+ require.NoError(t, json.Unmarshal(recQ.Body.Bytes(), &qResp, jsonx.V1()))
qMap, _ := qResp["queue"].(map[string]any)
qARN, _ := qMap["arn"].(string)
@@ -239,7 +241,7 @@ func TestSearchJobs_FilterByStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
jobs, _ := out["jobs"].([]any)
assert.Len(t, jobs, tt.wantCount)
})
diff --git a/services/mediaconvert/tags_test.go b/services/mediaconvert/tags_test.go
index 6178a0f26f..34af452930 100644
--- a/services/mediaconvert/tags_test.go
+++ b/services/mediaconvert/tags_test.go
@@ -1,13 +1,15 @@
package mediaconvert_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediaconvert"
)
@@ -40,7 +42,7 @@ func TestMediaConvert_Tags(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var tagsOut map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&tagsOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &tagsOut, jsonx.V1()))
resourceTags, _ := tagsOut["resourceTags"].(map[string]any)
tags, _ := resourceTags["tags"].(map[string]any)
assert.Equal(t, "test", tags["env"])
@@ -55,7 +57,7 @@ func TestMediaConvert_Tags(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/2017-08-29/tags/"+arn, nil)
require.Equal(t, http.StatusOK, rec.Code)
tagsOut = map[string]any{}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&tagsOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &tagsOut, jsonx.V1()))
resourceTags, _ = tagsOut["resourceTags"].(map[string]any)
assert.Empty(t, resourceTags["tags"])
}
diff --git a/services/mediaconvert/versions_test.go b/services/mediaconvert/versions_test.go
index d20677857e..8100cc54db 100644
--- a/services/mediaconvert/versions_test.go
+++ b/services/mediaconvert/versions_test.go
@@ -1,12 +1,14 @@
package mediaconvert_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestListVersions_ReturnsVersions(t *testing.T) {
@@ -39,7 +41,7 @@ func TestListVersions_ContainsKnownVersion(t *testing.T) {
} `json:"versions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.Versions)
found := false
diff --git a/services/medialive/handler.go b/services/medialive/handler.go
index fa8998ce13..2b518528f0 100644
--- a/services/medialive/handler.go
+++ b/services/medialive/handler.go
@@ -1,7 +1,8 @@
package medialive
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -12,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -448,7 +450,7 @@ func (h *Handler) handleREST(c *echo.Context) error {
var body map[string]any
if c.Request().ContentLength != 0 {
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
err.Error() != "EOF" {
c.Response().Header().Set(amznErrorTypeHeader, "BadRequestException")
diff --git a/services/medialive/handler_batch_test.go b/services/medialive/handler_batch_test.go
index c269fda308..0ba2abc079 100644
--- a/services/medialive/handler_batch_test.go
+++ b/services/medialive/handler_batch_test.go
@@ -1,12 +1,14 @@
package medialive_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestBatch_StartStopDelete(t *testing.T) {
@@ -80,7 +82,7 @@ func TestBatch_StartStopKnownChannels(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
successful := startResp["successful"].([]any)
assert.Len(t, successful, 1)
assert.Equal(t, chID, successful[0].(map[string]any)["id"])
@@ -97,7 +99,7 @@ func TestBatch_StartStopKnownChannels(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var delResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &delResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &delResp, jsonx.V1()))
delSuccessful := delResp["successful"].([]any)
assert.Len(t, delSuccessful, 1)
}
@@ -111,7 +113,7 @@ func TestBatch_StartNotFound(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
failed := resp["failed"].([]any)
assert.Len(t, failed, 1)
}
@@ -127,7 +129,7 @@ func TestBatch_DeleteInputSecurityGroups(t *testing.T) {
rec := doRequest(t, h, http.MethodPost, "/prod/inputSecurityGroups", map[string]any{})
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
groupID := created["securityGroup"].(map[string]any)["id"].(string)
rec = doRequest(t, h, http.MethodPost, "/prod/batch/delete", map[string]any{
@@ -135,7 +137,7 @@ func TestBatch_DeleteInputSecurityGroups(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
successful := resp["successful"].([]any)
require.Len(t, successful, 1)
assert.Equal(t, groupID, successful[0].(map[string]any)["id"])
diff --git a/services/medialive/handler_channels_test.go b/services/medialive/handler_channels_test.go
index 596fb2283f..78776c2d5d 100644
--- a/services/medialive/handler_channels_test.go
+++ b/services/medialive/handler_channels_test.go
@@ -1,7 +1,7 @@
package medialive_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/medialive"
)
@@ -36,7 +37,7 @@ func TestChannel_Create(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
ch := resp["channel"].(map[string]any)
assert.Contains(t, ch["arn"], "arn:aws:medialive:us-east-1:000000000000:channel:")
assert.Equal(t, "CREATING", ch["state"])
@@ -76,7 +77,7 @@ func TestChannel_CRUD(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/prod/channels/"+channelID, nil)
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "test-channel", descResp["name"])
assert.Equal(t, "IDLE", descResp["state"])
@@ -86,7 +87,7 @@ func TestChannel_CRUD(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
ch := updateResp["channel"].(map[string]any)
assert.Equal(t, "updated-channel", ch["name"])
@@ -94,7 +95,7 @@ func TestChannel_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/prod/channels", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["channels"], 1)
// Delete
@@ -117,7 +118,7 @@ func TestChannel_StartStop(t *testing.T) {
rec := doRequest(t, h, http.MethodPost, "/prod/channels/"+channelID+"/start", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
assert.Equal(t, "STARTING", startResp["state"])
// Start again returns conflict
@@ -128,7 +129,7 @@ func TestChannel_StartStop(t *testing.T) {
rec = doRequest(t, h, http.MethodPost, "/prod/channels/"+channelID+"/stop", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var stopResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stopResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stopResp, jsonx.V1()))
assert.Equal(t, "STOPPING", stopResp["state"])
}
@@ -144,19 +145,19 @@ func TestChannel_PipelinesRunningCount(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/prod/channels/"+channelID, nil)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.InDelta(t, 0, descResp["pipelinesRunningCount"], 0, "IDLE channel reports 0 running pipelines")
rec = doRequest(t, h, http.MethodPost, "/prod/channels/"+channelID+"/start", nil)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
assert.InDelta(t, 0, startResp["pipelinesRunningCount"], 0, "STARTING channel reports 0 running pipelines")
// The backend advances internal state to RUNNING immediately (see
// StartChannel's doc comment), so an immediate Describe should already
// report the class's full pipeline count.
rec = doRequest(t, h, http.MethodGet, "/prod/channels/"+channelID, nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "RUNNING", descResp["state"])
assert.InDelta(t, 2, descResp["pipelinesRunningCount"], 0, "RUNNING STANDARD channel reports 2 running pipelines")
}
@@ -227,7 +228,7 @@ func TestListChannels_Empty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["channels"])
}
diff --git a/services/medialive/handler_cloudwatch_alarm_templates_test.go b/services/medialive/handler_cloudwatch_alarm_templates_test.go
index 49aafbc968..bbbc5fed2a 100644
--- a/services/medialive/handler_cloudwatch_alarm_templates_test.go
+++ b/services/medialive/handler_cloudwatch_alarm_templates_test.go
@@ -1,12 +1,14 @@
package medialive_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCWAlarmTemplateGroup_CRUD(t *testing.T) {
@@ -25,7 +27,7 @@ func TestCWAlarmTemplateGroup_CRUD(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["id"])
assert.Equal(t, "test-cw-group", resp["name"])
assert.NotEmpty(t, resp["createdAt"])
@@ -73,7 +75,7 @@ func TestCWAlarmTemplateGroup_GetUpdateListDelete(t *testing.T) {
)
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
id := created["id"].(string)
// Get
@@ -92,14 +94,14 @@ func TestCWAlarmTemplateGroup_GetUpdateListDelete(t *testing.T) {
)
require.Equal(t, http.StatusOK, rec.Code)
var updated map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "cw-group-updated", updated["name"])
// List
rec = doRequest(t, h, http.MethodGet, "/prod/cloudwatch-alarm-template-groups", nil)
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
items := listResp["cloudWatchAlarmTemplateGroups"].([]any)
assert.Len(t, items, 1)
@@ -169,7 +171,7 @@ func TestCWAlarmTemplate_CRUD(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["id"])
assert.Equal(t, "InputLossSeconds", resp["metricName"])
assert.NotEmpty(t, resp["createdAt"])
@@ -202,7 +204,7 @@ func TestCWAlarmTemplate_GetUpdateListDelete(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
id := created["id"].(string)
// Get by ID
@@ -219,14 +221,14 @@ func TestCWAlarmTemplate_GetUpdateListDelete(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var updated map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "ActiveAlerts", updated["metricName"])
// List
rec = doRequest(t, h, http.MethodGet, "/prod/cloudwatch-alarm-templates", nil)
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
items := listResp["cloudWatchAlarmTemplates"].([]any)
assert.Len(t, items, 1)
diff --git a/services/medialive/handler_cluster_test.go b/services/medialive/handler_cluster_test.go
index 04cb47a09b..8d903a2d84 100644
--- a/services/medialive/handler_cluster_test.go
+++ b/services/medialive/handler_cluster_test.go
@@ -1,7 +1,7 @@
package medialive_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/medialive"
)
@@ -23,7 +24,7 @@ func createTestCluster(t *testing.T, h *medialive.Handler) string {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["id"].(string)
}
@@ -48,7 +49,7 @@ func TestCluster_Create(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp["arn"], "arn:aws:medialive:us-east-1:000000000000:cluster:")
assert.Equal(t, "ACTIVE", resp["state"])
assert.NotEmpty(t, resp["id"])
@@ -91,7 +92,7 @@ func TestCluster_CRUD(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/prod/clusters/"+clusterID, nil)
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, clusterID, descResp["id"])
assert.Equal(t, "ACTIVE", descResp["state"])
@@ -101,14 +102,14 @@ func TestCluster_CRUD(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Equal(t, "updated-cluster", updateResp["name"])
// List
rec = doRequest(t, h, http.MethodGet, "/prod/clusters", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
clusters := listResp["clusters"].([]any)
assert.Len(t, clusters, 1)
@@ -116,7 +117,7 @@ func TestCluster_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodDelete, "/prod/clusters/"+clusterID, nil)
assert.Equal(t, http.StatusOK, rec.Code)
var delResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &delResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &delResp, jsonx.V1()))
assert.Equal(t, "DELETED", delResp["state"])
assert.Equal(t, 0, medialive.ClusterCount(h.Backend.(*medialive.InMemoryBackend)))
@@ -158,7 +159,7 @@ func TestClusterAlerts_Empty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
alerts := resp["alerts"].([]any)
assert.Empty(t, alerts)
}
@@ -179,7 +180,7 @@ func TestNodeRegistrationScript(t *testing.T) {
assert.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
script, ok := resp["nodeRegistrationScript"].(string)
assert.True(t, ok)
assert.NotEmpty(t, script)
@@ -198,7 +199,7 @@ func TestNode_CRUD(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
nodeID := createResp["id"].(string)
assert.NotEmpty(t, nodeID)
assert.Equal(t, "ACTIVE", createResp["state"])
@@ -215,7 +216,7 @@ func TestNode_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/prod/clusters/"+clusterID+"/nodes/"+nodeID, nil)
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, nodeID, descResp["id"])
// Update node
@@ -231,7 +232,7 @@ func TestNode_CRUD(t *testing.T) {
)
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Equal(t, "updated-node", updateResp["name"])
assert.Equal(t, "BACKUP", updateResp["role"])
@@ -247,14 +248,14 @@ func TestNode_CRUD(t *testing.T) {
)
assert.Equal(t, http.StatusOK, rec.Code)
var stateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stateResp, jsonx.V1()))
assert.Equal(t, "DRAINING", stateResp["state"])
// List nodes
rec = doRequest(t, h, http.MethodGet, "/prod/clusters/"+clusterID+"/nodes", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
nodes := listResp["nodes"].([]any)
assert.Len(t, nodes, 1)
@@ -356,7 +357,7 @@ func TestListClusterAlerts(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
alerts := resp["alerts"].([]any)
if tt.wantEmpty {
diff --git a/services/medialive/handler_error_type_test.go b/services/medialive/handler_error_type_test.go
index df89404e59..9c742f9a75 100644
--- a/services/medialive/handler_error_type_test.go
+++ b/services/medialive/handler_error_type_test.go
@@ -2,7 +2,7 @@ package medialive_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/medialive"
@@ -90,7 +91,7 @@ func TestDescribeChannel_UnknownChannelRawEnvelope(t *testing.T) {
require.NoError(t, err)
var envelope map[string]any
- require.NoError(t, json.Unmarshal(raw, &envelope))
+ require.NoError(t, json.Unmarshal(raw, &envelope, jsonx.V1()))
_, hasMessage := envelope["Message"]
assert.True(t, hasMessage, "raw body must carry a Message key restjson.GetErrorInfo reads: %s", raw)
diff --git a/services/medialive/handler_event_bridge_rule_templates_test.go b/services/medialive/handler_event_bridge_rule_templates_test.go
index aa74931c2d..49bc5375cd 100644
--- a/services/medialive/handler_event_bridge_rule_templates_test.go
+++ b/services/medialive/handler_event_bridge_rule_templates_test.go
@@ -1,12 +1,14 @@
package medialive_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestEBRuleTemplateGroup_CRUD(t *testing.T) {
@@ -24,7 +26,7 @@ func TestEBRuleTemplateGroup_CRUD(t *testing.T) {
)
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
id := created["id"].(string)
assert.NotEmpty(t, id)
assert.NotEmpty(t, created["createdAt"])
@@ -50,7 +52,7 @@ func TestEBRuleTemplateGroup_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/prod/eventbridge-rule-template-groups", nil)
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["eventBridgeRuleTemplateGroups"].([]any), 1)
// Delete
@@ -135,7 +137,7 @@ func TestEBRuleTemplate_CRUD(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
id := created["id"].(string)
assert.NotEmpty(t, created["createdAt"])
assert.NotEmpty(t, created["modifiedAt"])
@@ -152,14 +154,14 @@ func TestEBRuleTemplate_CRUD(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var updated map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, "MEDIALIVE_MULTIPLEX_ALERT", updated["eventType"])
// List
rec = doRequest(t, h, http.MethodGet, "/prod/eventbridge-rule-templates", nil)
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["eventBridgeRuleTemplates"].([]any), 1)
// Delete
diff --git a/services/medialive/handler_input_security_groups_test.go b/services/medialive/handler_input_security_groups_test.go
index 9a6dd7e99a..86b3619913 100644
--- a/services/medialive/handler_input_security_groups_test.go
+++ b/services/medialive/handler_input_security_groups_test.go
@@ -1,13 +1,14 @@
package medialive_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/medialive"
)
@@ -26,7 +27,7 @@ func TestInputSecurityGroup_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
sg := createResp["securityGroup"].(map[string]any)
groupID := sg["id"].(string)
@@ -53,7 +54,7 @@ func TestInputSecurityGroup_CRUD(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
updatedSG := updateResp["securityGroup"].(map[string]any)
updatedRules := updatedSG["whitelistRules"].([]any)
assert.Len(t, updatedRules, 2)
@@ -62,7 +63,7 @@ func TestInputSecurityGroup_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/prod/inputSecurityGroups", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["inputSecurityGroups"], 1)
// Delete: real AWS's DeleteInputSecurityGroup marks the group DELETED
@@ -77,7 +78,7 @@ func TestInputSecurityGroup_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/prod/inputSecurityGroups/"+groupID, nil)
assert.Equal(t, http.StatusOK, rec.Code)
var describeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &describeResp, jsonx.V1()))
assert.Equal(t, "DELETED", describeResp["state"])
}
@@ -89,6 +90,6 @@ func TestListInputSecurityGroups_Empty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["inputSecurityGroups"])
}
diff --git a/services/medialive/handler_inputdevice_test.go b/services/medialive/handler_inputdevice_test.go
index 87305f1f3d..d83214b698 100644
--- a/services/medialive/handler_inputdevice_test.go
+++ b/services/medialive/handler_inputdevice_test.go
@@ -1,7 +1,7 @@
package medialive_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/medialive"
)
@@ -100,7 +101,7 @@ func TestHandlerListInputDevices(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
devices := resp["inputDevices"].([]any)
assert.Len(t, devices, tt.wantCount)
})
@@ -146,7 +147,7 @@ func TestHandlerDescribeInputDevice(t *testing.T) {
if tt.wantName != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantName, resp["name"])
}
})
@@ -192,7 +193,7 @@ func TestHandlerUpdateInputDevice(t *testing.T) {
if tt.wantName != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantName, resp["name"])
}
})
@@ -337,7 +338,7 @@ func TestHandlerTransferInputDevice_WireCasing(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
transfers := resp["inputDeviceTransfers"].([]any)
require.Len(t, transfers, 1)
@@ -432,7 +433,7 @@ func TestHandlerListInputDeviceTransfers(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
transfers := resp["inputDeviceTransfers"].([]any)
assert.Len(t, transfers, tt.wantCount)
}
@@ -494,7 +495,7 @@ func TestStartInputDeviceMaintenanceWindow(t *testing.T) {
// keys and would pass this test even with the phantom field
// still present.
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
assert.NotContains(t, resp, "maintenanceWindowActive")
}
})
diff --git a/services/medialive/handler_inputs_test.go b/services/medialive/handler_inputs_test.go
index 2efbe79479..d5c9e81d95 100644
--- a/services/medialive/handler_inputs_test.go
+++ b/services/medialive/handler_inputs_test.go
@@ -1,7 +1,7 @@
package medialive_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/medialive"
)
@@ -26,7 +27,7 @@ func TestInput_CRUD(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/prod/inputs/"+inputID, nil)
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "test-input", descResp["name"])
assert.Equal(t, "DETACHED", descResp["state"])
assert.Contains(t, descResp["arn"], "arn:aws:medialive:us-east-1:000000000000:input:")
@@ -37,7 +38,7 @@ func TestInput_CRUD(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
inp := updateResp["input"].(map[string]any)
assert.Equal(t, "updated-input", inp["name"])
@@ -45,7 +46,7 @@ func TestInput_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/prod/inputs", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["inputs"], 1)
// Delete: real terraform-provider-aws's waitInputDeleted polls
@@ -60,7 +61,7 @@ func TestInput_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/prod/inputs/"+inputID, nil)
assert.Equal(t, http.StatusOK, rec.Code)
var deletedResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &deletedResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &deletedResp, jsonx.V1()))
assert.Equal(t, "DELETED", deletedResp["state"])
}
@@ -196,7 +197,7 @@ func TestListInputs_Empty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["inputs"])
}
diff --git a/services/medialive/handler_multiplex_test.go b/services/medialive/handler_multiplex_test.go
index 926b0b73c3..2a45767c82 100644
--- a/services/medialive/handler_multiplex_test.go
+++ b/services/medialive/handler_multiplex_test.go
@@ -1,13 +1,14 @@
package medialive_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/medialive"
)
@@ -25,7 +26,7 @@ func createTestMultiplex(t *testing.T, h *medialive.Handler) string {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
m := resp["multiplex"].(map[string]any)
return m["id"].(string)
@@ -55,7 +56,7 @@ func TestMultiplex_Create(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
m := resp["multiplex"].(map[string]any)
assert.Contains(t, m["arn"], "arn:aws:medialive:us-east-1:000000000000:multiplex:")
assert.Equal(t, "CREATING", m["state"])
@@ -100,7 +101,7 @@ func TestMultiplex_CRUD(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/prod/multiplexes/"+multiplexID, nil)
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "test-multiplex", descResp["name"])
assert.Equal(t, "IDLE", descResp["state"])
@@ -114,7 +115,7 @@ func TestMultiplex_CRUD(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
m := updateResp["multiplex"].(map[string]any)
assert.Equal(t, "updated-multiplex", m["name"])
settings := m["multiplexSettings"].(map[string]any)
@@ -124,7 +125,7 @@ func TestMultiplex_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/prod/multiplexes", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["multiplexes"], 1)
// Delete: real AWS's DeleteMultiplex marks the multiplex DELETED rather
@@ -139,7 +140,7 @@ func TestMultiplex_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/prod/multiplexes/"+multiplexID, nil)
assert.Equal(t, http.StatusOK, rec.Code)
var describeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &describeResp, jsonx.V1()))
assert.Equal(t, "DELETED", describeResp["state"])
}
@@ -153,7 +154,7 @@ func TestMultiplex_StartStop(t *testing.T) {
rec := doRequest(t, h, http.MethodPost, "/prod/multiplexes/"+multiplexID+"/start", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
assert.Equal(t, "STARTING", startResp["state"])
// Start again returns conflict
@@ -164,7 +165,7 @@ func TestMultiplex_StartStop(t *testing.T) {
rec = doRequest(t, h, http.MethodPost, "/prod/multiplexes/"+multiplexID+"/stop", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var stopResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stopResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stopResp, jsonx.V1()))
assert.Equal(t, "STOPPING", stopResp["state"])
// Stop again returns conflict
@@ -239,7 +240,7 @@ func TestMultiplex_ListEmpty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["multiplexes"])
}
@@ -270,7 +271,7 @@ func TestMultiplexProgram_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
prog := createResp["multiplexProgram"].(map[string]any)
assert.Equal(t, "prog-1", prog["programName"])
settings := prog["multiplexProgramSettings"].(map[string]any)
@@ -290,7 +291,7 @@ func TestMultiplexProgram_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/prod/multiplexes/"+multiplexID+"/programs/prog-1", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "prog-1", descResp["programName"])
// Update
@@ -312,7 +313,7 @@ func TestMultiplexProgram_CRUD(t *testing.T) {
)
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
updatedProg := updateResp["multiplexProgram"].(map[string]any)
updatedSettings := updatedProg["multiplexProgramSettings"].(map[string]any)
assert.Equal(t, 2, int(updatedSettings["programNumber"].(float64)))
@@ -322,7 +323,7 @@ func TestMultiplexProgram_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/prod/multiplexes/"+multiplexID+"/programs", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["multiplexPrograms"], 1)
// Delete
@@ -431,7 +432,7 @@ func TestMultiplexProgram_ListEmpty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["multiplexPrograms"])
}
@@ -450,14 +451,14 @@ func TestMultiplexAlerts(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
mux := created["multiplex"].(map[string]any)
muxID := mux["id"].(string)
rec = doRequest(t, h, http.MethodGet, "/prod/multiplexes/"+muxID+"/alerts", nil)
require.Equal(t, http.StatusOK, rec.Code)
var alertsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &alertsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &alertsResp, jsonx.V1()))
assert.NotNil(t, alertsResp["alerts"])
rec = doRequest(t, h, http.MethodGet, "/prod/multiplexes/missing/alerts", nil)
diff --git a/services/medialive/handler_reservations_test.go b/services/medialive/handler_reservations_test.go
index 0ec5f47197..549547d2d9 100644
--- a/services/medialive/handler_reservations_test.go
+++ b/services/medialive/handler_reservations_test.go
@@ -1,7 +1,7 @@
package medialive_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/medialive"
)
@@ -27,7 +28,7 @@ func TestOfferings_ListDescribe(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
offerings := resp["offerings"].([]any)
assert.GreaterOrEqual(t, len(offerings), 3)
},
@@ -54,7 +55,7 @@ func TestOfferings_Describe(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/prod/offerings/87654321", nil)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "87654321", resp["offeringId"])
assert.NotEmpty(t, resp["region"])
_, hasName := resp["name"]
@@ -77,7 +78,7 @@ func TestReservations_PurchaseListDescribeDeleteUpdate(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rec.Code)
var purchaseResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &purchaseResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &purchaseResp, jsonx.V1()))
resv := purchaseResp["reservation"].(map[string]any)
reservationID := resv["reservationId"].(string)
assert.NotEmpty(t, reservationID)
@@ -92,7 +93,7 @@ func TestReservations_PurchaseListDescribeDeleteUpdate(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/prod/reservations", nil)
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["reservations"].([]any), 1)
// Update name
@@ -101,7 +102,7 @@ func TestReservations_PurchaseListDescribeDeleteUpdate(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var updatedResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updatedResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updatedResp, jsonx.V1()))
updatedResv := updatedResp["reservation"].(map[string]any)
assert.Equal(t, "renamed-reservation", updatedResv["name"])
diff --git a/services/medialive/handler_schedules_test.go b/services/medialive/handler_schedules_test.go
index 72f8eacbdd..f28c7e7b4f 100644
--- a/services/medialive/handler_schedules_test.go
+++ b/services/medialive/handler_schedules_test.go
@@ -1,12 +1,14 @@
package medialive_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestSchedule_DescribeDelete(t *testing.T) {
@@ -53,7 +55,7 @@ func TestBatchUpdateSchedule(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
creates := resp["creates"].(map[string]any)["scheduleActions"].([]any)
assert.Len(t, creates, 1)
diff --git a/services/medialive/handler_signal_maps_test.go b/services/medialive/handler_signal_maps_test.go
index 27833083de..2427d5c7c9 100644
--- a/services/medialive/handler_signal_maps_test.go
+++ b/services/medialive/handler_signal_maps_test.go
@@ -1,12 +1,14 @@
package medialive_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestSignalMap_CRUD(t *testing.T) {
@@ -23,7 +25,7 @@ func TestSignalMap_CRUD(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["id"])
assert.Equal(t, "CREATE_COMPLETE", resp["status"])
monitorDeployment, _ := resp["monitorDeployment"].(map[string]any)
@@ -60,7 +62,7 @@ func TestSignalMap_GetListDelete(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
id := created["id"].(string)
// Get by ID
@@ -75,7 +77,7 @@ func TestSignalMap_GetListDelete(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/prod/signal-maps", nil)
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
items := listResp["signalMaps"].([]any)
assert.Len(t, items, 1)
@@ -89,7 +91,7 @@ func TestSignalMap_GetListDelete(t *testing.T) {
rec = doRequest(t, h, http.MethodPost, "/prod/signal-maps/"+id+"/monitor-deployment", nil)
require.Equal(t, http.StatusAccepted, rec.Code)
var deployResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &deployResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &deployResp, jsonx.V1()))
deployMonitorDeployment, _ := deployResp["monitorDeployment"].(map[string]any)
assert.Equal(t, "DEPLOYMENT_COMPLETE", deployMonitorDeployment["status"])
@@ -127,7 +129,7 @@ func TestListSignalMaps_OmitsGetOnlyFields(t *testing.T) {
var out struct {
SignalMaps []map[string]any `json:"signalMaps"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.SignalMaps, 1)
item := out.SignalMaps[0]
diff --git a/services/medialive/handler_tags_test.go b/services/medialive/handler_tags_test.go
index f2a1e040e5..0697aa7e49 100644
--- a/services/medialive/handler_tags_test.go
+++ b/services/medialive/handler_tags_test.go
@@ -1,7 +1,7 @@
package medialive_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -9,6 +9,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestTags(t *testing.T) {
@@ -20,7 +22,7 @@ func TestTags(t *testing.T) {
// Get ARN
rec := doRequest(t, h, http.MethodGet, "/prod/channels/"+channelID, nil)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
resourceARN := descResp["arn"].(string)
// CreateTags
@@ -33,7 +35,7 @@ func TestTags(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/prod/tags/"+resourceARN, nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags := listResp["tags"].(map[string]any)
assert.Equal(t, "prod", tags["env"])
assert.Equal(t, "platform", tags["team"])
@@ -48,7 +50,7 @@ func TestTags(t *testing.T) {
// Verify tag removed
rec = doRequest(t, h, http.MethodGet, "/prod/tags/"+resourceARN, nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags = listResp["tags"].(map[string]any)
assert.NotContains(t, tags, "env")
assert.Equal(t, "platform", tags["team"])
@@ -74,14 +76,14 @@ func TestTags_StaySyncedWithResource(t *testing.T) {
})
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
ch := created["channel"].(map[string]any)
channelARN := ch["arn"].(string)
rec = doRequest(t, h, http.MethodGet, "/prod/tags/"+channelARN, nil)
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags := listResp["tags"].(map[string]any)
assert.Equal(t, "video-team", tags["owner"])
@@ -96,7 +98,7 @@ func TestTags_StaySyncedWithResource(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/prod/channels/"+channelID, nil)
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
descTags := descResp["tags"].(map[string]any)
assert.Equal(t, "video-team", descTags["owner"])
assert.Equal(t, "1234", descTags["cost-center"])
diff --git a/services/medialive/handler_test.go b/services/medialive/handler_test.go
index 6dae23bd1c..fd04dd3ad4 100644
--- a/services/medialive/handler_test.go
+++ b/services/medialive/handler_test.go
@@ -2,7 +2,7 @@ package medialive_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/medialive"
)
@@ -39,7 +40,7 @@ func doRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -64,7 +65,7 @@ func decodeBody(t *testing.T, body []byte) map[string]any {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
return resp
}
@@ -79,7 +80,7 @@ func createTestChannel(t *testing.T, h *medialive.Handler) string {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ch := resp["channel"].(map[string]any)
return ch["id"].(string)
@@ -95,7 +96,7 @@ func createTestInput(t *testing.T, h *medialive.Handler) string {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
inp := resp["input"].(map[string]any)
return inp["id"].(string)
diff --git a/services/medialive/list_summary_shapes_test.go b/services/medialive/list_summary_shapes_test.go
index 409a4d6912..b6a6abfe17 100644
--- a/services/medialive/list_summary_shapes_test.go
+++ b/services/medialive/list_summary_shapes_test.go
@@ -1,7 +1,7 @@
package medialive_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/medialive/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestListChannels_SummaryTagsRoundTrip proves ChannelSummary.Tags (over-wide
@@ -57,7 +59,7 @@ func TestListNetworks_RawBodyOmitsTags(t *testing.T) {
var listResp struct {
Networks []map[string]any `json:"networks"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Networks, 1)
assert.NotContains(t, listResp.Networks[0], "tags", "Network has no tags field on any CRUD/List shape")
assert.Equal(t, "net-leak", listResp.Networks[0]["name"])
diff --git a/services/medialive/multi_region_test.go b/services/medialive/multi_region_test.go
index c41173597f..e3f57b54ac 100644
--- a/services/medialive/multi_region_test.go
+++ b/services/medialive/multi_region_test.go
@@ -2,7 +2,8 @@ package medialive_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/medialive"
)
@@ -174,8 +176,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/medialive/persistence.go b/services/medialive/persistence.go
index 49edf6e6ce..ab5164d596 100644
--- a/services/medialive/persistence.go
+++ b/services/medialive/persistence.go
@@ -2,7 +2,7 @@ package medialive
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -44,7 +44,7 @@ const medialiveSnapshotVersion = 1
// intentionally absent: it is an ephemeral reverse index rebuilt from
// InputDevices on Restore, and was never part of the persisted shape.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
ScheduleActions map[string][]*storedScheduleAction `json:"scheduleActions"`
AccountKmsKeyID string `json:"accountKmsKeyId"`
diff --git a/services/medialive/whitebox_test.go b/services/medialive/whitebox_test.go
index a74c3d1d1d..3ef48fae64 100644
--- a/services/medialive/whitebox_test.go
+++ b/services/medialive/whitebox_test.go
@@ -2,7 +2,7 @@ package medialive
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func tagStoreCount(b *InMemoryBackend) int {
@@ -27,7 +29,7 @@ func doWhiteboxRequest(t *testing.T, h *Handler, method, path string, body any)
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -50,7 +52,7 @@ func decodeWhiteboxBody(t *testing.T, body []byte) map[string]any {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
return resp
}
diff --git a/services/mediapackage/handler.go b/services/mediapackage/handler.go
index 7f01e80a2b..05603e71a2 100644
--- a/services/mediapackage/handler.go
+++ b/services/mediapackage/handler.go
@@ -1,7 +1,8 @@
package mediapackage
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"strconv"
@@ -11,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -163,7 +165,12 @@ func (h *Handler) handleREST(c *echo.Context) error {
var body map[string]any
if c.Request().ContentLength != 0 {
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil && err.Error() != "EOF" {
+ if err := json.UnmarshalDecode(
+ jsontext.NewDecoder(c.Request().Body),
+ &body,
+ jsonx.V1(),
+ ); err != nil &&
+ err.Error() != "EOF" {
return c.JSON(http.StatusBadRequest, map[string]any{
keyMessage: "invalid JSON body",
keyType: "UnprocessableEntityException",
diff --git a/services/mediapackage/handler_channels_test.go b/services/mediapackage/handler_channels_test.go
index c9382bb512..9eefe29458 100644
--- a/services/mediapackage/handler_channels_test.go
+++ b/services/mediapackage/handler_channels_test.go
@@ -1,7 +1,7 @@
package mediapackage_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediapackage"
)
@@ -29,7 +30,7 @@ func TestChannel_Create(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp["arn"], "arn:aws:mediapackage:us-east-1:000000000000:channels/my-channel")
assert.Equal(t, "my-channel", resp["id"])
assert.Equal(t, "live stream", resp["description"])
@@ -76,7 +77,7 @@ func TestChannel_CRUD(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/channels/"+channelID, nil)
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, channelID, descResp["id"])
// Update
@@ -85,14 +86,14 @@ func TestChannel_CRUD(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Equal(t, "updated description", updateResp["description"])
// List
rec = doRequest(t, h, http.MethodGet, "/channels", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["channels"], 1)
// Delete
@@ -150,7 +151,7 @@ func TestChannel_RotateCredentials(t *testing.T) {
// Get original credentials
rec := doRequest(t, h, http.MethodGet, "/channels/"+channelID, nil)
var before map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &before))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &before, jsonx.V1()))
hls := before["hlsIngest"].(map[string]any)
eps := hls["ingestEndpoints"].([]any)
originalPassword := eps[0].(map[string]any)["password"].(string)
@@ -160,7 +161,7 @@ func TestChannel_RotateCredentials(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var after map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &after))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &after, jsonx.V1()))
hls = after["hlsIngest"].(map[string]any)
eps = hls["ingestEndpoints"].([]any)
newPassword := eps[0].(map[string]any)["password"].(string)
@@ -181,7 +182,7 @@ func TestChannel_ConfigureLogs(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, channelID, resp["id"])
}
@@ -193,7 +194,7 @@ func TestChannel_ListEmpty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["channels"])
}
@@ -281,7 +282,7 @@ func TestHandler_RotateChannelCredentials_RealPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, channelID, resp["id"])
// The old (fictional) path must no longer be recognized as this operation.
@@ -309,7 +310,7 @@ func TestHandler_ConfigureLogs_ReflectsInDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
egress, ok := resp["egressAccessLogs"].(map[string]any)
require.True(t, ok, "egressAccessLogs should be present after ConfigureLogs")
@@ -333,7 +334,7 @@ func TestHandler_Channel_CreatedAtPresent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["createdAt"])
}
@@ -354,7 +355,7 @@ func TestRotateIngestEndpointCredentials(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["id"])
hls := resp["hlsIngest"].(map[string]any)
@@ -401,7 +402,7 @@ func TestRotateIngestEndpointCredentials(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var chResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &chResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &chResp, jsonx.V1()))
hls := chResp["hlsIngest"].(map[string]any)
eps := hls["ingestEndpoints"].([]any)
require.NotEmpty(t, eps)
diff --git a/services/mediapackage/handler_harvest_jobs_test.go b/services/mediapackage/handler_harvest_jobs_test.go
index 4e4900543b..b0bd02c259 100644
--- a/services/mediapackage/handler_harvest_jobs_test.go
+++ b/services/mediapackage/handler_harvest_jobs_test.go
@@ -1,7 +1,7 @@
package mediapackage_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediapackage"
)
@@ -22,7 +23,7 @@ func createTestOriginEndpointForHarvest(t *testing.T, h *mediapackage.Handler, c
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["id"].(string)
}
@@ -61,7 +62,7 @@ func TestHarvestJob_Create(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "job-1", resp["id"])
assert.Equal(t, "IN_PROGRESS", resp["status"])
assert.NotEmpty(t, resp["arn"])
@@ -175,7 +176,7 @@ func TestHarvestJob_Describe(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "job-desc", resp["id"])
assert.Equal(t, "IN_PROGRESS", resp["status"])
assert.NotEmpty(t, resp["arn"])
@@ -238,7 +239,7 @@ func TestHarvestJob_List(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
jobs := resp["harvestJobs"].([]any)
assert.Len(t, jobs, 4)
},
@@ -251,7 +252,7 @@ func TestHarvestJob_List(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
jobs := resp["harvestJobs"].([]any)
assert.Len(t, jobs, 3)
for _, j := range jobs {
@@ -269,7 +270,7 @@ func TestHarvestJob_List(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
jobs := resp["harvestJobs"].([]any)
assert.Len(t, jobs, 4)
for _, j := range jobs {
@@ -360,7 +361,7 @@ func TestHarvestJob_CycleCreateDescribeList(t *testing.T) {
assert.Equal(t, 1, mediapackage.HarvestJobCount(backend))
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
assert.Equal(t, "cycle-job", created["id"])
assert.Contains(t, created["arn"].(string), "harvest_jobs/cycle-job")
@@ -369,7 +370,7 @@ func TestHarvestJob_CycleCreateDescribeList(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var described map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &described))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &described, jsonx.V1()))
assert.Equal(t, "cycle-job", described["id"])
assert.Equal(t, created["arn"], described["arn"])
assert.Equal(t, "2024-06-01T00:00:00Z", described["startTime"])
@@ -380,7 +381,7 @@ func TestHarvestJob_CycleCreateDescribeList(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var listed map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listed))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listed, jsonx.V1()))
jobs := listed["harvestJobs"].([]any)
assert.Len(t, jobs, 1)
@@ -418,7 +419,7 @@ func TestHarvestJob_Create_NeverClaimsSuccessBeforeWorkHappens(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "IN_PROGRESS", resp["status"])
}
diff --git a/services/mediapackage/handler_origin_endpoints_test.go b/services/mediapackage/handler_origin_endpoints_test.go
index 685fe24af6..1226f7d125 100644
--- a/services/mediapackage/handler_origin_endpoints_test.go
+++ b/services/mediapackage/handler_origin_endpoints_test.go
@@ -1,13 +1,14 @@
package mediapackage_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediapackage"
)
@@ -34,7 +35,7 @@ func TestOriginEndpoint_Create(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp["arn"], "arn:aws:mediapackage:us-east-1:000000000000:origin_endpoints/ep1")
assert.Equal(t, "ep1", resp["id"])
assert.Equal(t, "ch1", resp["channelId"])
@@ -89,7 +90,7 @@ func TestOriginEndpoint_CRUD(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/origin_endpoints/"+epID, nil)
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, epID, descResp["id"])
assert.Equal(t, channelID, descResp["channelId"])
@@ -100,7 +101,7 @@ func TestOriginEndpoint_CRUD(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Equal(t, "updated endpoint", updateResp["description"])
assert.Equal(t, "DENY", updateResp["origination"])
@@ -108,7 +109,7 @@ func TestOriginEndpoint_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/origin_endpoints", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["originEndpoints"], 1)
// Delete
@@ -158,7 +159,7 @@ func TestOriginEndpoint_DefaultOrigination(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ALLOW", resp["origination"])
assert.Equal(t, "index", resp["manifestName"])
}
@@ -171,7 +172,7 @@ func TestOriginEndpoint_ListEmpty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["originEndpoints"])
}
@@ -251,7 +252,7 @@ func TestHandler_OriginEndpoint_CreatedAtPresent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["createdAt"])
}
@@ -277,7 +278,7 @@ func TestHandler_OriginEndpoint_PackagingConfigRoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
require.Contains(t, created, "authorization")
require.Contains(t, created, "hlsPackage")
assert.NotContains(t, created, "dashPackage", "unset packaging blocks should be omitted, not null-filled")
@@ -286,7 +287,7 @@ func TestHandler_OriginEndpoint_PackagingConfigRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var described map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &described))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &described, jsonx.V1()))
auth, ok := described["authorization"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "arn:aws:iam::000000000000:role/r", auth["secretsRoleArn"])
@@ -300,7 +301,7 @@ func TestHandler_OriginEndpoint_PackagingConfigRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
endpoints, ok := listResp["originEndpoints"].([]any)
require.True(t, ok)
require.Len(t, endpoints, 1)
@@ -364,7 +365,7 @@ func TestHandler_OriginEndpoint_MssPackage_FullDepthRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var described map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &described))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &described, jsonx.V1()))
mss, ok := described["mssPackage"].(map[string]any)
require.True(t, ok)
diff --git a/services/mediapackage/handler_tags_test.go b/services/mediapackage/handler_tags_test.go
index 3d521666eb..257e33df1a 100644
--- a/services/mediapackage/handler_tags_test.go
+++ b/services/mediapackage/handler_tags_test.go
@@ -1,7 +1,7 @@
package mediapackage_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -9,6 +9,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestTags(t *testing.T) {
@@ -20,7 +22,7 @@ func TestTags(t *testing.T) {
// Get ARN
rec := doRequest(t, h, http.MethodGet, "/channels/"+channelID, nil)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
resourceARN := descResp["arn"].(string)
// TagResource
@@ -33,7 +35,7 @@ func TestTags(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/tags/"+resourceARN, nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags := listResp["tags"].(map[string]any)
assert.Equal(t, "prod", tags["env"])
assert.Equal(t, "platform", tags["team"])
@@ -48,7 +50,7 @@ func TestTags(t *testing.T) {
// Verify tag removed
rec = doRequest(t, h, http.MethodGet, "/tags/"+resourceARN, nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags = listResp["tags"].(map[string]any)
assert.NotContains(t, tags, "env")
assert.Equal(t, "platform", tags["team"])
diff --git a/services/mediapackage/handler_test.go b/services/mediapackage/handler_test.go
index b8ac228e93..b61970078c 100644
--- a/services/mediapackage/handler_test.go
+++ b/services/mediapackage/handler_test.go
@@ -2,7 +2,7 @@ package mediapackage_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediapackage"
)
@@ -30,7 +31,7 @@ func doRequest(t *testing.T, h *mediapackage.Handler, method, path string, body
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -62,7 +63,7 @@ func doRequestJSON(t *testing.T, h *mediapackage.Handler, method, path string, b
}
var out map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &out)
+ _ = json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1())
return rec.Code, out
}
@@ -77,7 +78,7 @@ func createTestChannel(t *testing.T, h *mediapackage.Handler) string {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["id"].(string)
}
@@ -92,7 +93,7 @@ func createTestOriginEndpoint(t *testing.T, h *mediapackage.Handler, channelID s
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["id"].(string)
}
diff --git a/services/mediapackage/multi_region_test.go b/services/mediapackage/multi_region_test.go
index 4b9ac9eef4..bd66b6e01c 100644
--- a/services/mediapackage/multi_region_test.go
+++ b/services/mediapackage/multi_region_test.go
@@ -2,7 +2,8 @@ package mediapackage_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/mediapackage"
)
@@ -172,8 +174,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/mediapackage/persistence.go b/services/mediapackage/persistence.go
index 5dc4b45d58..b2f98b2943 100644
--- a/services/mediapackage/persistence.go
+++ b/services/mediapackage/persistence.go
@@ -2,7 +2,7 @@ package mediapackage
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -31,7 +31,7 @@ const mediapackageSnapshotVersion = 1
// decoding a snapshot from an incompatible (older or newer) build of this
// backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
AccountID string `json:"accountId"`
Region string `json:"region"`
diff --git a/services/mediastore/containers.go b/services/mediastore/containers.go
index 706c0ed7f2..00c5f2cca0 100644
--- a/services/mediastore/containers.go
+++ b/services/mediastore/containers.go
@@ -2,7 +2,7 @@ package mediastore
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"regexp"
@@ -10,6 +10,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -199,7 +200,7 @@ func (b *InMemoryBackend) ListContainers(
// PutContainerPolicy stores a container access policy.
func (b *InMemoryBackend) PutContainerPolicy(ctx context.Context, name, policy string) error {
- if !json.Valid([]byte(policy)) {
+ if !jsontext.Value([]byte(policy)).IsValid(jsonx.V1()) {
return ErrInvalidPolicy
}
diff --git a/services/mediastore/handler.go b/services/mediastore/handler.go
index 41ae9a09d2..f62dd59c65 100644
--- a/services/mediastore/handler.go
+++ b/services/mediastore/handler.go
@@ -2,7 +2,7 @@ package mediastore
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"sort"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -131,7 +132,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var data map[string]any
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
@@ -221,7 +222,7 @@ func (h *Handler) dispatch(c *echo.Context, op string, body []byte) error {
func (h *Handler) handleCreateContainer(c *echo.Context, body []byte) error {
var req createContainerRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -244,7 +245,7 @@ func (h *Handler) handleCreateContainer(c *echo.Context, body []byte) error {
func (h *Handler) handleDeleteContainer(c *echo.Context, body []byte) error {
var req deleteContainerRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -262,7 +263,7 @@ func (h *Handler) handleDeleteContainer(c *echo.Context, body []byte) error {
func (h *Handler) handleDescribeContainer(c *echo.Context, body []byte) error {
var req describeContainerRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -283,7 +284,7 @@ func (h *Handler) handleDescribeContainer(c *echo.Context, body []byte) error {
func (h *Handler) handleListContainers(c *echo.Context, body []byte) error {
var req listContainersRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -309,7 +310,7 @@ func (h *Handler) handleListContainers(c *echo.Context, body []byte) error {
func (h *Handler) handlePutContainerPolicy(c *echo.Context, body []byte) error {
var req putContainerPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -327,7 +328,7 @@ func (h *Handler) handlePutContainerPolicy(c *echo.Context, body []byte) error {
func (h *Handler) handleGetContainerPolicy(c *echo.Context, body []byte) error {
var req getContainerPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -346,7 +347,7 @@ func (h *Handler) handleGetContainerPolicy(c *echo.Context, body []byte) error {
func (h *Handler) handleDeleteContainerPolicy(c *echo.Context, body []byte) error {
var req deleteContainerPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -364,7 +365,7 @@ func (h *Handler) handleDeleteContainerPolicy(c *echo.Context, body []byte) erro
func (h *Handler) handlePutCorsPolicy(c *echo.Context, body []byte) error {
var req putCorsPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -382,7 +383,7 @@ func (h *Handler) handlePutCorsPolicy(c *echo.Context, body []byte) error {
func (h *Handler) handleGetCorsPolicy(c *echo.Context, body []byte) error {
var req getCorsPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -401,7 +402,7 @@ func (h *Handler) handleGetCorsPolicy(c *echo.Context, body []byte) error {
func (h *Handler) handleDeleteCorsPolicy(c *echo.Context, body []byte) error {
var req deleteCorsPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -419,7 +420,7 @@ func (h *Handler) handleDeleteCorsPolicy(c *echo.Context, body []byte) error {
func (h *Handler) handlePutLifecyclePolicy(c *echo.Context, body []byte) error {
var req putLifecyclePolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -437,7 +438,7 @@ func (h *Handler) handlePutLifecyclePolicy(c *echo.Context, body []byte) error {
func (h *Handler) handleGetLifecyclePolicy(c *echo.Context, body []byte) error {
var req getLifecyclePolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -456,7 +457,7 @@ func (h *Handler) handleGetLifecyclePolicy(c *echo.Context, body []byte) error {
func (h *Handler) handleDeleteLifecyclePolicy(c *echo.Context, body []byte) error {
var req deleteLifecyclePolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -474,7 +475,7 @@ func (h *Handler) handleDeleteLifecyclePolicy(c *echo.Context, body []byte) erro
func (h *Handler) handlePutMetricPolicy(c *echo.Context, body []byte) error {
var req putMetricPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -492,7 +493,7 @@ func (h *Handler) handlePutMetricPolicy(c *echo.Context, body []byte) error {
func (h *Handler) handleGetMetricPolicy(c *echo.Context, body []byte) error {
var req getMetricPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -511,7 +512,7 @@ func (h *Handler) handleGetMetricPolicy(c *echo.Context, body []byte) error {
func (h *Handler) handleDeleteMetricPolicy(c *echo.Context, body []byte) error {
var req deleteMetricPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -529,7 +530,7 @@ func (h *Handler) handleDeleteMetricPolicy(c *echo.Context, body []byte) error {
func (h *Handler) handleStartAccessLogging(c *echo.Context, body []byte) error {
var req startAccessLoggingRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -547,7 +548,7 @@ func (h *Handler) handleStartAccessLogging(c *echo.Context, body []byte) error {
func (h *Handler) handleStopAccessLogging(c *echo.Context, body []byte) error {
var req stopAccessLoggingRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -565,7 +566,7 @@ func (h *Handler) handleStopAccessLogging(c *echo.Context, body []byte) error {
func (h *Handler) handleTagResource(c *echo.Context, body []byte) error {
var req tagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -585,7 +586,7 @@ func (h *Handler) handleTagResource(c *echo.Context, body []byte) error {
func (h *Handler) handleUntagResource(c *echo.Context, body []byte) error {
var req untagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -603,7 +604,7 @@ func (h *Handler) handleUntagResource(c *echo.Context, body []byte) error {
func (h *Handler) handleListTagsForResource(c *echo.Context, body []byte) error {
var req listTagsForResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/mediastore/handler_tags_test.go b/services/mediastore/handler_tags_test.go
index 37ccfff6dc..bafde795f1 100644
--- a/services/mediastore/handler_tags_test.go
+++ b/services/mediastore/handler_tags_test.go
@@ -1,12 +1,14 @@
package mediastore_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_Tags(t *testing.T) {
@@ -48,7 +50,7 @@ func TestHandler_Tags(t *testing.T) {
require.Equal(t, http.StatusOK, setupRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(setupRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(setupRec.Body.Bytes(), &createResp, jsonx.V1()))
containerMap, _ := createResp["Container"].(map[string]any)
containerARN, _ := containerMap["ARN"].(string)
diff --git a/services/mediastore/handler_test.go b/services/mediastore/handler_test.go
index 753201ff9f..1addee30a1 100644
--- a/services/mediastore/handler_test.go
+++ b/services/mediastore/handler_test.go
@@ -2,7 +2,7 @@ package mediastore_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediastore"
)
@@ -32,7 +33,7 @@ func doRequest(t *testing.T, h *mediastore.Handler, op string, body any) *httpte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -65,7 +66,7 @@ func unmarshalBody(t *testing.T, rec *httptest.ResponseRecorder) map[string]any
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
@@ -235,7 +236,7 @@ func TestHandler_ExtractResource(t *testing.T) {
h := newTestHandler(t)
e := echo.New()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
diff --git a/services/mediastore/lifecycle_policy.go b/services/mediastore/lifecycle_policy.go
index 1070178e24..63ae3f799b 100644
--- a/services/mediastore/lifecycle_policy.go
+++ b/services/mediastore/lifecycle_policy.go
@@ -2,13 +2,15 @@ package mediastore
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// PutLifecyclePolicy stores a lifecycle policy for a container.
func (b *InMemoryBackend) PutLifecyclePolicy(ctx context.Context, name, policy string) error {
- if !json.Valid([]byte(policy)) {
+ if !jsontext.Value([]byte(policy)).IsValid(jsonx.V1()) {
return ErrInvalidPolicy
}
diff --git a/services/mediastoredata/handler_test.go b/services/mediastoredata/handler_test.go
index 2d984b9470..83d28ff3d6 100644
--- a/services/mediastoredata/handler_test.go
+++ b/services/mediastoredata/handler_test.go
@@ -6,7 +6,7 @@ import (
"bytes"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediastoredata"
)
@@ -196,7 +197,7 @@ func TestMediaStoreData_PutObject(t *testing.T) {
if tt.wantETag {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["ETag"])
assert.NotEmpty(t, resp["StorageClass"])
}
@@ -354,7 +355,7 @@ func TestMediaStoreData_ListItems(t *testing.T) {
} `json:"Items"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Items, len(tt.wantNames))
for i, item := range resp.Items {
@@ -468,7 +469,7 @@ func TestMediaStoreData_Lifecycle(t *testing.T) {
} `json:"Items"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Items, 1)
assert.Equal(t, tt.wantFolder, listResp.Items[0].Name)
assert.Equal(t, "FOLDER", listResp.Items[0].Type)
@@ -496,7 +497,7 @@ func TestMediaStoreData_PutObject_SHA256InResponse(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, wantSHA, resp["ContentSHA256"])
assert.NotEmpty(t, rec.Header().Get("ETag"))
}
@@ -559,7 +560,7 @@ func TestMediaStoreData_PathValidation(t *testing.T) {
// AWS-wide/gopherstack-wide convention for parameter
// validation failures (see errors.go).
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
}
})
@@ -605,7 +606,7 @@ func TestMediaStoreData_StorageClassValidation(t *testing.T) {
if tt.wantType != "" {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
}
})
@@ -645,7 +646,7 @@ func TestMediaStoreData_UploadAvailabilityValidation(t *testing.T) {
if tt.wantType != "" {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
}
})
@@ -692,7 +693,7 @@ func TestMediaStoreData_ObjectSizeLimit(t *testing.T) {
if tt.wantType != "" {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
}
})
@@ -819,7 +820,7 @@ func TestMediaStoreData_RangeRequests(t *testing.T) {
// only matches this exact string, not a fabricated
// "InvalidRangeException".
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "RequestedRangeNotSatisfiableException", resp["__type"])
assert.NotEmpty(t, rec.Header().Get("Content-Range"))
}
@@ -848,7 +849,7 @@ func TestMediaStoreData_ListItems_Pagination(t *testing.T) {
} `json:"Items"`
}
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1, jsonx.V1()))
assert.Len(t, page1.Items, 2)
require.NotNil(t, page1.NextToken)
@@ -863,7 +864,7 @@ func TestMediaStoreData_ListItems_Pagination(t *testing.T) {
} `json:"Items"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
assert.Len(t, page2.Items, 2)
// Names must not overlap pages.
@@ -932,7 +933,7 @@ func TestMediaStoreData_ContentSHA256Verification(t *testing.T) {
// payload hash that doesn't match the actual body), not a fabricated
// mediastoredata-specific "InvalidContentSHA256Exception".
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "XAmzContentSHA256Mismatch", resp["__type"])
})
diff --git a/services/mediatailor/handler.go b/services/mediatailor/handler.go
index 192492dfcb..194f4fb4f5 100644
--- a/services/mediatailor/handler.go
+++ b/services/mediatailor/handler.go
@@ -1,7 +1,8 @@
package mediatailor
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -10,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -277,7 +279,12 @@ func (h *Handler) handleREST(c *echo.Context) error {
var body map[string]any
if c.Request().ContentLength != 0 {
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil && err.Error() != "EOF" {
+ if err := json.UnmarshalDecode(
+ jsontext.NewDecoder(c.Request().Body),
+ &body,
+ jsonx.V1(),
+ ); err != nil &&
+ err.Error() != "EOF" {
c.Response().Header().Set(amznErrorTypeHeader, "BadRequestException")
return c.JSON(http.StatusBadRequest, map[string]any{keyMessage: "invalid JSON body"})
diff --git a/services/mediatailor/handler_alerts_test.go b/services/mediatailor/handler_alerts_test.go
index 8f1255ce3d..ce9dbc8dce 100644
--- a/services/mediatailor/handler_alerts_test.go
+++ b/services/mediatailor/handler_alerts_test.go
@@ -1,12 +1,14 @@
package mediatailor_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestListAlerts(t *testing.T) {
@@ -37,7 +39,7 @@ func TestListAlerts(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if !tt.wantItems {
assert.Equal(t, "BadRequestException", rec.Header().Get("X-Amzn-Errortype"))
diff --git a/services/mediatailor/handler_channel_policy_test.go b/services/mediatailor/handler_channel_policy_test.go
index 7e75eba6fc..649146ba0c 100644
--- a/services/mediatailor/handler_channel_policy_test.go
+++ b/services/mediatailor/handler_channel_policy_test.go
@@ -1,13 +1,15 @@
package mediatailor_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestChannelPolicy_FullCycle's subtests share one Handler and depend on
@@ -43,7 +45,7 @@ func TestChannelPolicy_FullCycle(t *testing.T) { //nolint:tparallel // subtests
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["Policy"], "2012-10-17")
},
},
diff --git a/services/mediatailor/handler_channels_test.go b/services/mediatailor/handler_channels_test.go
index b456ef12ec..691529e14e 100644
--- a/services/mediatailor/handler_channels_test.go
+++ b/services/mediatailor/handler_channels_test.go
@@ -1,13 +1,14 @@
package mediatailor_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediatailor"
)
@@ -35,7 +36,7 @@ func TestChannel_CreateAndDescribe(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "my-channel", resp["ChannelName"])
assert.Contains(t, resp["Arn"], "arn:aws:mediatailor:us-east-1:000000000000:channel/my-channel")
assert.Equal(t, "STOPPED", resp["ChannelState"])
@@ -80,7 +81,7 @@ func TestChannel_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/channel/test-channel", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "test-channel", descResp["ChannelName"])
assert.Equal(t, "STOPPED", descResp["ChannelState"])
@@ -92,14 +93,14 @@ func TestChannel_CRUD(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Len(t, updateResp["Outputs"], 1)
// List
rec = doRequest(t, h, http.MethodGet, "/channels", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["Items"], 1)
// Delete
@@ -126,7 +127,7 @@ func TestChannel_StartStop(t *testing.T) {
// Verify running
rec = doRequest(t, h, http.MethodGet, "/channel/ch1", nil)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "RUNNING", descResp["ChannelState"])
// Start again is idempotent
@@ -139,7 +140,7 @@ func TestChannel_StartStop(t *testing.T) {
// Verify stopped
rec = doRequest(t, h, http.MethodGet, "/channel/ch1", nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "STOPPED", descResp["ChannelState"])
// Stop again is idempotent
@@ -201,7 +202,7 @@ func TestChannel_ListEmpty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["Items"])
}
@@ -227,7 +228,7 @@ func TestListChannels_WithItems(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Items"].([]any)
assert.Len(t, items, 2)
}
@@ -252,7 +253,7 @@ func TestChannelOutput_WithHLS(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
outputs, _ := resp["Outputs"].([]any)
require.Len(t, outputs, 1)
out, _ := outputs[0].(map[string]any)
@@ -308,7 +309,7 @@ func TestCreateChannel_ReturnsTier(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "BASIC", resp["Tier"], "Tier must default to BASIC")
}
@@ -362,7 +363,7 @@ func TestCreateChannel_FillerSlate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
fs, ok := resp["FillerSlate"].(map[string]any)
require.True(t, ok, "FillerSlate must be present in response")
@@ -387,7 +388,7 @@ func TestCreateChannel_TagsSurviveDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
createdTags, _ := created["tags"].(map[string]any)
assert.Equal(t, "prod", createdTags["env"], "tags must be present in the create response")
@@ -395,7 +396,7 @@ func TestCreateChannel_TagsSurviveDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var described map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &described))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &described, jsonx.V1()))
describedTags, _ := described["tags"].(map[string]any)
assert.Equal(t, "prod", describedTags["env"], "tags set at creation must survive to DescribeChannel")
}
@@ -417,7 +418,7 @@ func TestChannel_AudiencesAndTimeShift(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
audiences, _ := resp["Audiences"].([]any)
assert.ElementsMatch(t, []any{"aud1", "aud2"}, audiences)
ts, ok := resp["TimeShiftConfiguration"].(map[string]any)
@@ -430,7 +431,7 @@ func TestChannel_AudiencesAndTimeShift(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
updateResp := map[string]any{}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
audiences, _ = updateResp["Audiences"].([]any)
assert.ElementsMatch(t, []any{"aud3"}, audiences)
assert.Nil(t, updateResp["TimeShiftConfiguration"], "UpdateChannel omitting TimeShiftConfiguration must clear it")
@@ -466,7 +467,7 @@ func TestCreateChannel_Tier(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantTier, resp["Tier"])
}
})
diff --git a/services/mediatailor/handler_functions_test.go b/services/mediatailor/handler_functions_test.go
index ce1ab0b21c..b025edbb52 100644
--- a/services/mediatailor/handler_functions_test.go
+++ b/services/mediatailor/handler_functions_test.go
@@ -1,13 +1,15 @@
package mediatailor_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestFunction_CRUD's subtests share one Handler and depend on running in
@@ -39,7 +41,7 @@ func TestFunction_CRUD(t *testing.T) { //nolint:tparallel // subtests are order-
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "fn1", resp["FunctionId"])
assert.Equal(t, "CUSTOM_OUTPUT", resp["FunctionType"])
assert.Contains(t, resp["Arn"], ":mediatailor:")
@@ -55,7 +57,7 @@ func TestFunction_CRUD(t *testing.T) { //nolint:tparallel // subtests are order-
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "fn1", resp["FunctionId"])
assert.Equal(t, "my function", resp["Description"])
},
@@ -69,7 +71,7 @@ func TestFunction_CRUD(t *testing.T) { //nolint:tparallel // subtests are order-
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Items"].([]any)
assert.Len(t, items, 1)
},
@@ -161,7 +163,7 @@ func TestListFunctions_WithItems(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Items"].([]any)
assert.Len(t, items, 2)
}
diff --git a/services/mediatailor/handler_live_sources_test.go b/services/mediatailor/handler_live_sources_test.go
index b3a6897e9f..d067ff9d53 100644
--- a/services/mediatailor/handler_live_sources_test.go
+++ b/services/mediatailor/handler_live_sources_test.go
@@ -1,7 +1,7 @@
package mediatailor_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediatailor"
)
@@ -40,7 +41,7 @@ func TestLiveSource_Create(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ls1", resp["LiveSourceName"])
assert.Equal(t, "sl1", resp["SourceLocationName"])
assert.Contains(t, resp["Arn"], ":mediatailor:")
@@ -105,7 +106,7 @@ func TestLiveSource_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ls1", resp["LiveSourceName"])
// update
@@ -117,7 +118,7 @@ func TestLiveSource_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var updated map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
cfgs, _ := updated["HttpPackageConfigurations"].([]any)
require.Len(t, cfgs, 1)
cfg0, _ := cfgs[0].(map[string]any)
@@ -128,7 +129,7 @@ func TestLiveSource_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
items, _ := listResp["Items"].([]any)
assert.Len(t, items, 1)
@@ -202,7 +203,7 @@ func TestListLiveSources_WithItems(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Items"].([]any)
assert.Len(t, items, 2)
}
diff --git a/services/mediatailor/handler_logs_test.go b/services/mediatailor/handler_logs_test.go
index f479e61136..ca94cd4063 100644
--- a/services/mediatailor/handler_logs_test.go
+++ b/services/mediatailor/handler_logs_test.go
@@ -1,13 +1,15 @@
package mediatailor_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestConfigureLogsForChannel(t *testing.T) {
@@ -30,7 +32,7 @@ func TestConfigureLogsForChannel(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ch1", resp["ChannelName"])
logTypes, _ := resp["LogTypes"].([]any)
assert.Len(t, logTypes, 1)
@@ -83,7 +85,7 @@ func TestConfigureLogsForPlaybackConfiguration(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "pc1", resp["PlaybackConfigurationName"])
assert.InDelta(t, float64(60), resp["PercentEnabled"], 0.0001)
},
@@ -196,7 +198,7 @@ func TestConfigureLogsForChannel_PersistsToDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
logCfg, ok := resp["LogConfiguration"].(map[string]any)
require.True(t, ok, "DescribeChannel must include LogConfiguration")
@@ -225,7 +227,7 @@ func TestConfigureLogsForPlaybackConfiguration_PersistsToGet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
logCfg, ok := resp["LogConfiguration"].(map[string]any)
require.True(t, ok, "GetPlaybackConfiguration must include LogConfiguration")
@@ -328,7 +330,7 @@ func TestConfigureLogsForPlaybackConfiguration_SurvivesRePut(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
logCfg, ok := resp["LogConfiguration"].(map[string]any)
require.True(t, ok)
diff --git a/services/mediatailor/handler_playback_configurations_test.go b/services/mediatailor/handler_playback_configurations_test.go
index 816ce67550..c909bf0406 100644
--- a/services/mediatailor/handler_playback_configurations_test.go
+++ b/services/mediatailor/handler_playback_configurations_test.go
@@ -1,13 +1,14 @@
package mediatailor_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediatailor"
)
@@ -32,7 +33,7 @@ func TestPlaybackConfiguration_PutAndGet(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "my-config", resp["Name"])
assert.Contains(
t,
@@ -83,7 +84,7 @@ func TestPlaybackConfiguration_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/playbackConfiguration/test-config", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "test-config", getResp["Name"])
assert.Equal(t, "https://ads.example.com", getResp["AdDecisionServerUrl"])
@@ -91,7 +92,7 @@ func TestPlaybackConfiguration_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/playbackConfigurations", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["Items"], 1)
// Delete
@@ -124,7 +125,7 @@ func TestPlaybackConfiguration_PutIdempotent(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/playbackConfiguration/cfg1", nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "https://new-ads.example.com", resp["AdDecisionServerUrl"])
}
@@ -165,7 +166,7 @@ func TestPlaybackConfiguration_ListEmpty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["Items"])
}
@@ -187,7 +188,7 @@ func TestListPlaybackConfigurations_WithNextToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Items"].([]any)
assert.Len(t, items, 3)
}
@@ -280,7 +281,7 @@ func TestPutPlaybackConfiguration_TagsReplacedOnUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags, _ := resp["tags"].(map[string]any)
assert.Contains(t, tags, "new", "new tag must be present after update")
@@ -301,7 +302,7 @@ func TestPutPlaybackConfiguration_HlsConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
hlsCfg, ok := resp["HlsConfiguration"].(map[string]any)
require.True(t, ok, "HlsConfiguration must be present in response")
@@ -366,14 +367,14 @@ func TestPutPlaybackConfiguration_ExtraConfigRoundTrips(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var putResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
assertPlaybackConfigExtras(t, putResp)
rec = doRequest(t, h, http.MethodGet, "/playbackConfiguration/cfg1", nil)
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assertPlaybackConfigExtras(t, getResp)
}
@@ -447,7 +448,7 @@ func TestPutPlaybackConfiguration_AdsPersonalization(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var putResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
tt.check(t, putResp)
name, _ := tt.body["Name"].(string)
@@ -455,7 +456,7 @@ func TestPutPlaybackConfiguration_AdsPersonalization(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
tt.check(t, getResp)
})
}
@@ -476,13 +477,13 @@ func TestPutPlaybackConfiguration_DualStackFieldsAbsent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var putResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
rec = doRequest(t, h, http.MethodGet, "/playbackConfiguration/cfg-dualstack", nil)
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
for _, resp := range []map[string]any{putResp, getResp} {
_, ok := resp["DualStackPlaybackEndpointPrefix"]
diff --git a/services/mediatailor/handler_prefetch_schedules_test.go b/services/mediatailor/handler_prefetch_schedules_test.go
index 45cb29f107..c15ecf4792 100644
--- a/services/mediatailor/handler_prefetch_schedules_test.go
+++ b/services/mediatailor/handler_prefetch_schedules_test.go
@@ -1,13 +1,15 @@
package mediatailor_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestPrefetchSchedule_CRUD(t *testing.T) {
@@ -28,7 +30,7 @@ func TestPrefetchSchedule_CRUD(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "sched1", resp["Name"])
assert.Equal(t, "pc1", resp["PlaybackConfigurationName"])
assert.Contains(t, resp["Arn"], ":mediatailor:")
@@ -58,7 +60,7 @@ func TestPrefetchSchedule_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "sched1", resp["Name"])
// list
@@ -66,7 +68,7 @@ func TestPrefetchSchedule_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
items, _ := listResp["Items"].([]any)
assert.Len(t, items, 1)
@@ -136,7 +138,7 @@ func TestListPrefetchSchedules_WithItems(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Items"].([]any)
assert.Len(t, items, 2)
}
@@ -167,7 +169,7 @@ func TestListPrefetchSchedules_PostWithBodyPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Items"].([]any)
assert.Len(t, items, 1, "body MaxResults must limit the page size")
assert.NotEmpty(t, resp["NextToken"], "a NextToken must be returned when more pages remain")
@@ -204,7 +206,7 @@ func TestCreatePrefetchSchedule_RetrievalAndConsumption(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
retrieval, ok := resp["Retrieval"].(map[string]any)
require.True(t, ok, "Retrieval must be present in response")
@@ -246,7 +248,7 @@ func TestListPrefetchSchedules_FiltersByScheduleTypeAndStreamID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Items"].([]any)
assert.Len(t, items, 2, "ScheduleType=SINGLE must exclude the RECURRING schedule")
@@ -255,7 +257,7 @@ func TestListPrefetchSchedules_FiltersByScheduleTypeAndStreamID(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ = resp["Items"].([]any)
assert.Len(t, items, 2, "StreamId=stream-1 must match single-a and recurring-a only")
}
@@ -278,7 +280,7 @@ func TestPrefetchSchedule_TagsAndScheduleTypeRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "RECURRING", resp["ScheduleType"])
assert.Equal(t, "stream-9", resp["StreamId"])
tags, _ := resp["tags"].(map[string]any)
diff --git a/services/mediatailor/handler_programs_test.go b/services/mediatailor/handler_programs_test.go
index 013bac219c..c200aa7f66 100644
--- a/services/mediatailor/handler_programs_test.go
+++ b/services/mediatailor/handler_programs_test.go
@@ -1,12 +1,14 @@
package mediatailor_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestProgram_CRUD(t *testing.T) {
@@ -24,7 +26,7 @@ func TestProgram_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
assert.Equal(t, "prog1", created["ProgramName"])
assert.Equal(t, "ch1", created["ChannelName"])
assert.Contains(t, created["Arn"], ":mediatailor:")
@@ -34,7 +36,7 @@ func TestProgram_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var described map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &described))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &described, jsonx.V1()))
assert.Equal(t, "prog1", described["ProgramName"])
assert.Equal(t, "sl1", described["SourceLocationName"])
@@ -49,7 +51,7 @@ func TestProgram_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var schedResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &schedResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &schedResp, jsonx.V1()))
items, _ := schedResp["Items"].([]any)
assert.Len(t, items, 1)
item0, _ := items[0].(map[string]any)
@@ -151,7 +153,7 @@ func TestHandleGetChannelSchedule_WithItems(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var schedResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &schedResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &schedResp, jsonx.V1()))
items, _ := schedResp["Items"].([]any)
require.Len(t, items, 1)
}
diff --git a/services/mediatailor/handler_source_locations_test.go b/services/mediatailor/handler_source_locations_test.go
index 1d2a862431..4783fbb050 100644
--- a/services/mediatailor/handler_source_locations_test.go
+++ b/services/mediatailor/handler_source_locations_test.go
@@ -1,13 +1,14 @@
package mediatailor_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediatailor"
)
@@ -32,7 +33,7 @@ func TestSourceLocation_CreateAndDescribe(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "my-location", resp["SourceLocationName"])
assert.Contains(t, resp["Arn"], "arn:aws:mediatailor:us-east-1:000000000000:sourceLocation/my-location")
httpCfg, _ := resp["HttpConfiguration"].(map[string]any)
@@ -76,7 +77,7 @@ func TestSourceLocation_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/sourceLocation/test-sl", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "test-sl", descResp["SourceLocationName"])
// Update
@@ -85,7 +86,7 @@ func TestSourceLocation_CRUD(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
httpCfg, _ := updateResp["HttpConfiguration"].(map[string]any)
assert.Equal(t, "https://updated.example.com", httpCfg["BaseUrl"])
@@ -93,7 +94,7 @@ func TestSourceLocation_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/sourceLocations", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["Items"], 1)
// Delete
@@ -152,7 +153,7 @@ func TestSourceLocation_ListEmpty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["Items"])
}
@@ -172,7 +173,7 @@ func TestListSourceLocations_WithItems(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Items"].([]any)
assert.Len(t, items, 2)
}
@@ -267,7 +268,7 @@ func TestCreateSourceLocation_TagsSurviveDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags, _ := resp["tags"].(map[string]any)
assert.Equal(t, "video", tags["team"], "tags set at creation must survive to DescribeSourceLocation")
}
@@ -285,7 +286,7 @@ func TestSourceLocation_Timestamps(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
require.NotNil(t, created["CreationTime"], "CreationTime must be populated")
require.NotNil(t, created["LastModifiedTime"], "LastModifiedTime must be populated")
@@ -295,7 +296,7 @@ func TestSourceLocation_Timestamps(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var updated map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updated, jsonx.V1()))
assert.Equal(t, created["CreationTime"], updated["CreationTime"], "CreationTime must not change on update")
require.NotNil(t, updated["LastModifiedTime"])
}
diff --git a/services/mediatailor/handler_tags_test.go b/services/mediatailor/handler_tags_test.go
index 2817fabed1..2892d5c111 100644
--- a/services/mediatailor/handler_tags_test.go
+++ b/services/mediatailor/handler_tags_test.go
@@ -1,7 +1,7 @@
package mediatailor_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -9,6 +9,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestTags_FullCycle(t *testing.T) {
@@ -25,7 +27,7 @@ func TestTags_FullCycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
resourceARN, _ := putResp["PlaybackConfigurationArn"].(string)
// TagResource
@@ -38,7 +40,7 @@ func TestTags_FullCycle(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/tags/"+resourceARN, nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags, _ := listResp["tags"].(map[string]any)
assert.Equal(t, "prod", tags["env"])
assert.Equal(t, "media", tags["team"])
@@ -53,7 +55,7 @@ func TestTags_FullCycle(t *testing.T) {
// Verify tag removed
rec = doRequest(t, h, http.MethodGet, "/tags/"+resourceARN, nil)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags, _ = listResp["tags"].(map[string]any)
assert.NotContains(t, tags, "env")
assert.Equal(t, "media", tags["team"])
@@ -94,7 +96,7 @@ func TestTagResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ = resp["PlaybackConfigurationArn"].(string)
} else {
arn = "arn:aws:mediatailor:us-east-1:000000000000:playbackConfiguration/nonexistent"
@@ -132,7 +134,7 @@ func TestListTagsForResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ = resp["PlaybackConfigurationArn"].(string)
} else {
arn = "arn:aws:mediatailor:us-east-1:000000000000:playbackConfiguration/nonexistent"
@@ -170,7 +172,7 @@ func TestUntagResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ = resp["PlaybackConfigurationArn"].(string)
// Actually tag it first
diff --git a/services/mediatailor/handler_test.go b/services/mediatailor/handler_test.go
index 010eb8ad3d..df6f724bc5 100644
--- a/services/mediatailor/handler_test.go
+++ b/services/mediatailor/handler_test.go
@@ -2,7 +2,7 @@ package mediatailor_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediatailor"
)
@@ -32,7 +33,7 @@ func doRequest(t *testing.T, h *mediatailor.Handler, method, path string, body a
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -528,7 +529,7 @@ func TestHandler_TagsWireKey(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// A PascalCase "Tags" key must NOT be how tags are surfaced.
_, wrongKeyPresent := resp["Tags"]
@@ -550,7 +551,7 @@ func TestHandler_TagsWireKey(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags, ok := resp["tags"].(map[string]any)
require.True(t, ok, "response must carry tags under lowercase 'tags' key")
@@ -568,7 +569,7 @@ func TestHandler_TagsWireKey(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags, ok := resp["tags"].(map[string]any)
require.True(t, ok, "response must carry tags under lowercase 'tags' key")
@@ -591,7 +592,7 @@ func TestHandler_ChannelTimestamps_EpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
creationTime, ok := resp["CreationTime"].(float64)
require.True(t, ok, "CreationTime must decode as a JSON number, got %T", resp["CreationTime"])
@@ -625,7 +626,7 @@ func TestHandler_PaginationQueryParamCasing(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Items"].([]any)
assert.Len(t, items, 1, "lowercase maxResults query param must limit the page size")
assert.NotEmpty(t, resp["NextToken"], "a NextToken must be returned when more pages remain")
@@ -643,7 +644,7 @@ func TestHandler_PaginationQueryParamCasing(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Items"].([]any)
assert.Len(t, items, 1, "PascalCase MaxResults query param must limit the page size")
assert.NotEmpty(t, resp["NextToken"], "a NextToken must be returned when more pages remain")
@@ -661,7 +662,7 @@ func TestHandler_PaginationQueryParamCasing(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Items"].([]any)
assert.Len(t, items, 1, "ListFunctions must actually paginate, not return every function")
assert.NotEmpty(t, resp["NextToken"], "a NextToken must be returned when more pages remain")
@@ -695,7 +696,7 @@ func TestHandler_PaginationQueryParamCasing(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Items"].([]any)
assert.Len(t, items, 1, "GetChannelSchedule must actually paginate, not return every program")
assert.NotEmpty(t, resp["NextToken"], "a NextToken must be returned when more pages remain")
diff --git a/services/mediatailor/handler_vod_sources_test.go b/services/mediatailor/handler_vod_sources_test.go
index 6b99e2ab46..fa2488bf60 100644
--- a/services/mediatailor/handler_vod_sources_test.go
+++ b/services/mediatailor/handler_vod_sources_test.go
@@ -1,13 +1,14 @@
package mediatailor_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediatailor"
)
@@ -37,7 +38,7 @@ func TestVodSource_CreateAndDescribe(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "my-vod", resp["VodSourceName"])
assert.Equal(t, "sl1", resp["SourceLocationName"])
assert.Contains(
@@ -87,7 +88,7 @@ func TestVodSource_CRUD(t *testing.T) {
rec = doRequest(t, h, http.MethodGet, "/sourceLocation/sl1/vodSource/vod1", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "vod1", descResp["VodSourceName"])
// Update
@@ -99,14 +100,14 @@ func TestVodSource_CRUD(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Len(t, updateResp["HttpPackageConfigurations"], 2)
// List
rec = doRequest(t, h, http.MethodGet, "/sourceLocation/sl1/vodSources", nil)
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["Items"], 1)
// Delete
@@ -154,7 +155,7 @@ func TestVodSource_ListEmpty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["Items"])
}
@@ -177,7 +178,7 @@ func TestListVodSources_WithItems(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Items"].([]any)
assert.Len(t, items, 2)
}
@@ -238,7 +239,7 @@ func TestCreateVodSource_TagsSurviveDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags, _ := resp["tags"].(map[string]any)
assert.Equal(t, "video", tags["team"], "tags set at creation must survive to DescribeVodSource")
}
@@ -260,7 +261,7 @@ func TestVodSource_Timestamps(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
require.NotNil(t, created["CreationTime"])
require.NotNil(t, created["LastModifiedTime"])
}
diff --git a/services/mediatailor/multi_region_test.go b/services/mediatailor/multi_region_test.go
index 48215c9fd1..b5a6bab9d8 100644
--- a/services/mediatailor/multi_region_test.go
+++ b/services/mediatailor/multi_region_test.go
@@ -2,7 +2,8 @@ package mediatailor_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/mediatailor"
)
@@ -180,8 +182,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/mediatailor/persistence.go b/services/mediatailor/persistence.go
index 2e5840fc82..e0771067ec 100644
--- a/services/mediatailor/persistence.go
+++ b/services/mediatailor/persistence.go
@@ -2,7 +2,7 @@ package mediatailor
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -32,7 +32,7 @@ const mediatailorSnapshotVersion = 1
// decoding a snapshot from an incompatible (older or newer) build of this
// backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
AccountID string `json:"accountID"`
Region string `json:"region"`
diff --git a/services/mediatailor/wire_field_fixes_test.go b/services/mediatailor/wire_field_fixes_test.go
index 733124b622..ffc4bba2d2 100644
--- a/services/mediatailor/wire_field_fixes_test.go
+++ b/services/mediatailor/wire_field_fixes_test.go
@@ -1,7 +1,7 @@
package mediatailor_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mediatailor"
)
@@ -153,7 +154,7 @@ func TestGetPrefetchSchedule_NoCreationTimeOnWire(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code, getRec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotContains(
t, resp, "CreationTime",
"GetPrefetchScheduleOutput has no CreationTime member on the real API; emitting one is a fabricated field",
@@ -224,7 +225,7 @@ func TestCreateChannel_NoLogConfigurationOnWire(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code, createRec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
assert.NotContains(
t, createResp, "LogConfiguration",
"CreateChannelOutput has no LogConfiguration member on the real API",
@@ -236,7 +237,7 @@ func TestCreateChannel_NoLogConfigurationOnWire(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code, updateRec.Body.String())
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.NotContains(
t, updateResp, "LogConfiguration",
"UpdateChannelOutput has no LogConfiguration member on the real API",
@@ -246,7 +247,7 @@ func TestCreateChannel_NoLogConfigurationOnWire(t *testing.T) {
require.Equal(t, http.StatusOK, describeRec.Code, describeRec.Body.String())
var describeResp map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp, jsonx.V1()))
assert.Contains(
t, describeResp, "LogConfiguration",
"DescribeChannelOutput.LogConfiguration IS required on the real API and must stay present",
diff --git a/services/memorydb/acls_test.go b/services/memorydb/acls_test.go
index ec99afe7ea..cfdcef82a3 100644
--- a/services/memorydb/acls_test.go
+++ b/services/memorydb/acls_test.go
@@ -2,13 +2,14 @@ package memorydb_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -119,7 +120,7 @@ func TestUpdateACLSliceNoAlias(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
acls := resp["ACLs"].([]any)
acl := acls[0].(map[string]any)
users := acl["UserNames"].([]any)
diff --git a/services/memorydb/dataplane_test.go b/services/memorydb/dataplane_test.go
index 18144e2916..e5087b49dd 100644
--- a/services/memorydb/dataplane_test.go
+++ b/services/memorydb/dataplane_test.go
@@ -3,7 +3,7 @@ package memorydb_test
import (
"bufio"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net"
"strconv"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -114,7 +115,7 @@ func describeClusterObj(t *testing.T, h *memorydb.Handler, name string) map[stri
var resp struct {
Clusters []map[string]any `json:"Clusters"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Clusters, 1)
return resp.Clusters[0]
diff --git a/services/memorydb/errcode_test.go b/services/memorydb/errcode_test.go
index 4306b8c722..ff94b90ac2 100644
--- a/services/memorydb/errcode_test.go
+++ b/services/memorydb/errcode_test.go
@@ -1,12 +1,14 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// -- Error __type wire-shape regression tests -------------------------------
@@ -22,7 +24,7 @@ func responseType(t *testing.T, body []byte) string {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
typ, _ := resp["__type"].(string)
@@ -105,7 +107,7 @@ func TestDeleteUser_CascadesFromACL(t *testing.T) {
UserNames []string `json:"UserNames"`
} `json:"ACLs"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.ACLs, 1)
assert.NotContains(t, resp.ACLs[0].UserNames, "in-use-user")
}
diff --git a/services/memorydb/events.go b/services/memorydb/events.go
index f533898f7c..ae5c0faaf0 100644
--- a/services/memorydb/events.go
+++ b/services/memorydb/events.go
@@ -2,10 +2,11 @@ package memorydb
import (
"context"
- "encoding/json"
"fmt"
"sort"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// AddEvent appends an event to the backend event log (used internally for seeding).
@@ -75,7 +76,7 @@ func (b *InMemoryBackend) DescribeEvents(ctx context.Context, req *describeEvent
// field. Real aws-sdk-go-v2 clients serialize these TStamp shapes as a JSON
// number of epoch seconds (awsjson1.1), so n arrives as json.Number, not an
// RFC3339 string. Returns nil, nil when n is the empty (unset) value.
-func parseEpochRequestTime(n json.Number) (*time.Time, error) {
+func parseEpochRequestTime(n jsonx.Number) (*time.Time, error) {
if n == "" {
return nil, nil //nolint:nilnil // absent filter is a valid, distinct state from "parse failed"
}
diff --git a/services/memorydb/events_test.go b/services/memorydb/events_test.go
index e11be642f3..89dbec1982 100644
--- a/services/memorydb/events_test.go
+++ b/services/memorydb/events_test.go
@@ -1,7 +1,7 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -83,7 +84,7 @@ func TestDescribeEvents_Pagination(t *testing.T) {
NextToken string `json:"NextToken"`
Events []map[string]any `json:"Events"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &first))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &first, jsonx.V1()))
require.Len(t, first.Events, 1, "first page must truncate to MaxResults")
require.NotEmpty(t, first.NextToken, "NextToken must be set when more events remain")
@@ -94,7 +95,7 @@ func TestDescribeEvents_Pagination(t *testing.T) {
NextToken string `json:"NextToken"`
Events []map[string]any `json:"Events"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &second))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &second, jsonx.V1()))
require.Len(t, second.Events, 1, "second page must return the next event")
assert.NotEqual(t, first.Events[0]["Message"], second.Events[0]["Message"], "no event must repeat across pages")
}
diff --git a/services/memorydb/handler.go b/services/memorydb/handler.go
index c4e703f486..5802b14970 100644
--- a/services/memorydb/handler.go
+++ b/services/memorydb/handler.go
@@ -2,7 +2,7 @@ package memorydb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -150,7 +151,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var data map[string]any
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
diff --git a/services/memorydb/handler_acls.go b/services/memorydb/handler_acls.go
index 1e76accc6c..1093fb6b9f 100644
--- a/services/memorydb/handler_acls.go
+++ b/services/memorydb/handler_acls.go
@@ -2,16 +2,18 @@ package memorydb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateACL(ctx context.Context, c *echo.Context, body []byte) error {
var req createACLRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -34,7 +36,7 @@ func (h *Handler) handleCreateACL(ctx context.Context, c *echo.Context, body []b
func (h *Handler) handleDescribeACLs(ctx context.Context, c *echo.Context, body []byte) error {
var req describeACLRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -61,7 +63,7 @@ func (h *Handler) handleDescribeACLs(ctx context.Context, c *echo.Context, body
func (h *Handler) handleDeleteACL(ctx context.Context, c *echo.Context, body []byte) error {
var req deleteACLRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -80,7 +82,7 @@ func (h *Handler) handleDeleteACL(ctx context.Context, c *echo.Context, body []b
func (h *Handler) handleUpdateACL(ctx context.Context, c *echo.Context, body []byte) error {
var req updateACLRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/memorydb/handler_acls_test.go b/services/memorydb/handler_acls_test.go
index fe9ff8d87b..9de290f055 100644
--- a/services/memorydb/handler_acls_test.go
+++ b/services/memorydb/handler_acls_test.go
@@ -1,13 +1,14 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -105,7 +106,7 @@ func TestHandler_ACL_Tags(t *testing.T) {
// Get ACL ARN
var aclResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &aclResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &aclResp, jsonx.V1()))
aclMap := aclResp["ACL"].(map[string]any)
aclARN := aclMap["ARN"].(string)
@@ -290,7 +291,7 @@ func TestHandler_Pagination_NextToken(t *testing.T) {
rec1 := doRequest(t, h, "DescribeACLs", map[string]any{"MaxResults": 2})
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
acls1, _ := resp1["ACLs"].([]any)
assert.Len(t, acls1, 2)
nextToken, _ := resp1["NextToken"].(string)
@@ -303,7 +304,7 @@ func TestHandler_Pagination_NextToken(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
acls2, _ := resp2["ACLs"].([]any)
assert.NotEmpty(t, acls2)
diff --git a/services/memorydb/handler_clusters.go b/services/memorydb/handler_clusters.go
index 102346888b..7aba7437d3 100644
--- a/services/memorydb/handler_clusters.go
+++ b/services/memorydb/handler_clusters.go
@@ -2,7 +2,7 @@ package memorydb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"time"
@@ -10,12 +10,13 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateCluster(ctx context.Context, c *echo.Context, body []byte) error {
var req createClusterRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -42,7 +43,7 @@ func (h *Handler) handleCreateCluster(ctx context.Context, c *echo.Context, body
func (h *Handler) handleDescribeClusters(ctx context.Context, c *echo.Context, body []byte) error {
var req describeClusterRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -71,7 +72,7 @@ func (h *Handler) handleDescribeClusters(ctx context.Context, c *echo.Context, b
func (h *Handler) handleDeleteCluster(ctx context.Context, c *echo.Context, body []byte) error {
var req deleteClusterRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -100,7 +101,7 @@ func (h *Handler) handleDeleteCluster(ctx context.Context, c *echo.Context, body
func (h *Handler) handleUpdateCluster(ctx context.Context, c *echo.Context, body []byte) error {
var req updateClusterRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -121,7 +122,7 @@ func (h *Handler) handleUpdateCluster(ctx context.Context, c *echo.Context, body
func (h *Handler) handleBatchUpdateCluster(ctx context.Context, c *echo.Context, body []byte) error {
var req batchUpdateClusterRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -165,7 +166,7 @@ func (h *Handler) handleBatchUpdateCluster(ctx context.Context, c *echo.Context,
func (h *Handler) handleFailoverShard(ctx context.Context, c *echo.Context, body []byte) error {
var req failoverShardRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -184,7 +185,7 @@ func (h *Handler) handleFailoverShard(ctx context.Context, c *echo.Context, body
func (h *Handler) handleListAllowedNodeTypeUpdates(ctx context.Context, c *echo.Context, body []byte) error {
var req listAllowedNodeTypeUpdatesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/memorydb/handler_clusters_lifecycle_test.go b/services/memorydb/handler_clusters_lifecycle_test.go
index cb96d1b0d9..a87af2b427 100644
--- a/services/memorydb/handler_clusters_lifecycle_test.go
+++ b/services/memorydb/handler_clusters_lifecycle_test.go
@@ -1,7 +1,7 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -46,7 +47,7 @@ func TestHandler_DescribeClusters_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusters := resp["Clusters"].([]any)
assert.Len(t, clusters, tt.wantCount)
@@ -88,7 +89,7 @@ func TestHandler_DescribeClusters_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp1, jsonx.V1()))
// NextToken may be empty or non-empty depending on implementation
nextToken, _ := resp1["NextToken"].(string)
@@ -98,7 +99,7 @@ func TestHandler_DescribeClusters_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
clusters := resp2["Clusters"].([]any)
// At minimum should return a valid response
@@ -137,7 +138,7 @@ func TestHandler_DescribeClusters_ShowShards(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusters := resp["Clusters"].([]any)
require.Len(t, clusters, 1)
@@ -574,7 +575,7 @@ func TestHandler_BatchUpdateCluster_Mixed(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
processed := resp["ProcessedClusters"].([]any)
unprocessed := resp["UnprocessedClusters"].([]any)
@@ -681,7 +682,7 @@ func TestHandler_Engine_Valkey(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cl := resp["Cluster"].(map[string]any)
assert.Equal(t, tt.wantEngine, cl["Engine"])
assert.Equal(t, tt.wantVersion, cl["EngineVersion"])
@@ -880,7 +881,7 @@ func TestHandler_UpdateCluster_AutoMinorVersionUpgradeNotSettable(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cl := resp["Cluster"].(map[string]any)
assert.Equal(t, true, cl["AutoMinorVersionUpgrade"], "no real client can change this via UpdateCluster")
}
@@ -1076,7 +1077,7 @@ func TestHandler_CreateCluster_RestoreFromSnapshot(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cl := resp["Cluster"].(map[string]any)
assert.Equal(t, tt.wantEngineVersion, cl["EngineVersion"])
assert.Equal(t, "restored-cluster", cl["Name"])
@@ -1215,7 +1216,7 @@ func TestHandler_BatchUpdateCluster_MissingNames(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
processed := resp["ProcessedClusters"].([]any)
unprocessed := resp["UnprocessedClusters"].([]any)
assert.Len(t, processed, tt.wantProcessed)
diff --git a/services/memorydb/handler_clusters_test.go b/services/memorydb/handler_clusters_test.go
index de76d5addd..f716d12c63 100644
--- a/services/memorydb/handler_clusters_test.go
+++ b/services/memorydb/handler_clusters_test.go
@@ -2,7 +2,7 @@ package memorydb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -58,7 +59,7 @@ func TestHandler_CreateCluster(t *testing.T) {
if tt.name == "missing target header" {
e := echo.New()
- bodyBytes, _ := json.Marshal(tt.body)
+ bodyBytes, _ := json.Marshal(tt.body, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
@@ -74,7 +75,7 @@ func TestHandler_CreateCluster(t *testing.T) {
if tt.wantCluster {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusterVal, ok := resp["Cluster"]
require.True(t, ok, "response should contain Cluster field")
@@ -137,7 +138,7 @@ func TestHandler_DescribeClusters(t *testing.T) {
if tt.wantCount > 0 {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusters, ok := resp["Clusters"].([]any)
require.True(t, ok)
assert.Len(t, clusters, tt.wantCount)
@@ -207,7 +208,7 @@ func TestHandler_ClusterResponse_ParameterGroupStatus(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
clusters, _ := resp["Clusters"].([]any)
require.Len(t, clusters, 1)
cl, _ := clusters[0].(map[string]any)
@@ -228,7 +229,7 @@ func TestHandler_ClusterResponse_MultiRegionClusterName(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
cl, _ := createResp["Cluster"].(map[string]any)
// Field must be present (empty string is fine for a cluster not in multi-region).
@@ -250,7 +251,7 @@ func TestHandler_ClusterParameterGroupStatus_InSync(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &resp, jsonx.V1()))
cl, _ := resp["Cluster"].(map[string]any)
assert.Equal(t, "in-sync", cl["ParameterGroupStatus"],
"default ParameterGroupStatus must be in-sync")
@@ -343,7 +344,7 @@ func TestHandler_BatchUpdateCluster(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
processed := resp["ProcessedClusters"].([]any)
assert.Len(t, processed, tt.wantProcessed)
@@ -403,7 +404,7 @@ func TestHandler_UpdateCluster(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cluster := resp["Cluster"].(map[string]any)
assert.Equal(t, "updated", cluster["Description"])
}
@@ -562,7 +563,7 @@ func TestHandler_SnsTopicStatusStoredOnUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
cluster, _ := updateResp["Cluster"].(map[string]any)
gotStatus, _ := cluster["SnsTopicStatus"].(string)
@@ -733,7 +734,7 @@ func TestHandler_DeleteCluster_WithSnapshot_EmitsEvent(t *testing.T) {
require.Equal(t, http.StatusOK, evRec.Code)
var evResp map[string]any
- require.NoError(t, json.Unmarshal(evRec.Body.Bytes(), &evResp))
+ require.NoError(t, json.Unmarshal(evRec.Body.Bytes(), &evResp, jsonx.V1()))
events, _ := evResp["Events"].([]any)
// At least create + delete events.
assert.GreaterOrEqual(t, len(events), 2, "expected create and delete events")
@@ -789,7 +790,7 @@ func TestHandler_BatchUpdateCluster_Extended(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
processed, _ := resp["ProcessedClusters"].([]any)
unprocessed, _ := resp["UnprocessedClusters"].([]any)
assert.Len(t, processed, tt.wantProcessed)
@@ -873,7 +874,7 @@ func TestHandler_EnginePatchVersion_AllFamilies(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "create: %s", rec.Body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cl, _ := resp["Cluster"].(map[string]any)
assert.Equal(t, tt.wantPatchVersion, cl["EnginePatchVersion"],
diff --git a/services/memorydb/handler_engine_versions.go b/services/memorydb/handler_engine_versions.go
index 911a9182e0..d82b2f2cb5 100644
--- a/services/memorydb/handler_engine_versions.go
+++ b/services/memorydb/handler_engine_versions.go
@@ -2,16 +2,18 @@ package memorydb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDescribeEngineVersions(ctx context.Context, c *echo.Context, body []byte) error {
var req describeEngineVersionsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/memorydb/handler_engine_versions_test.go b/services/memorydb/handler_engine_versions_test.go
index ad7258ac70..7a93978f6b 100644
--- a/services/memorydb/handler_engine_versions_test.go
+++ b/services/memorydb/handler_engine_versions_test.go
@@ -1,12 +1,14 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_DescribeEngineVersions(t *testing.T) {
@@ -51,7 +53,7 @@ func TestHandler_DescribeEngineVersions(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions := resp["EngineVersions"].([]any)
assert.GreaterOrEqual(t, len(versions), tt.wantMinCount)
})
@@ -86,7 +88,7 @@ func TestHandler_DescribeEngineVersions_Filtering(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions, _ := resp["EngineVersions"].([]any)
assert.GreaterOrEqual(t, len(versions), tt.wantMinCount)
@@ -148,7 +150,7 @@ func TestHandler_DescribeEngineVersions_Filtered(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions := resp["EngineVersions"].([]any)
assert.GreaterOrEqual(t, len(versions), tt.wantMin)
@@ -194,7 +196,7 @@ func TestHandler_DescribeEngineVersions_IncludesValkey(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions := resp["EngineVersions"].([]any)
assert.GreaterOrEqual(t, len(versions), tt.wantMinCount)
@@ -225,7 +227,7 @@ func TestHandler_DescribeEngineVersions_EachHasEngine(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for _, v := range resp["EngineVersions"].([]any) {
ev := v.(map[string]any)
diff --git a/services/memorydb/handler_events.go b/services/memorydb/handler_events.go
index b10d74a016..5fd0b4822f 100644
--- a/services/memorydb/handler_events.go
+++ b/services/memorydb/handler_events.go
@@ -2,12 +2,13 @@ package memorydb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -20,7 +21,7 @@ const defaultDescribeEventsLimit = 100
func (h *Handler) handleDescribeEvents(ctx context.Context, c *echo.Context, body []byte) error {
var req describeEventsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/memorydb/handler_events_test.go b/services/memorydb/handler_events_test.go
index c1a8093bb0..aee2c59225 100644
--- a/services/memorydb/handler_events_test.go
+++ b/services/memorydb/handler_events_test.go
@@ -2,7 +2,7 @@ package memorydb_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -98,7 +99,7 @@ func TestWireEpoch_DescribeEvents_DateIsNumber(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
events, _ := resp["Events"].([]any)
require.NotEmpty(t, events, "expected at least one event for the created cluster")
@@ -130,7 +131,7 @@ func TestWireEpoch_DescribeEvents_StartTimeAcceptsEpochNumber(t *testing.T) {
"DescribeEvents must accept an epoch-seconds StartTime the way a real SDK client sends it")
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
events, _ := resp["Events"].([]any)
assert.NotEmpty(t, events, "StartTime=0 (epoch start) should not filter out the cluster-creation event")
@@ -143,7 +144,7 @@ func TestWireEpoch_DescribeEvents_StartTimeAcceptsEpochNumber(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
resp = nil
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
events, _ = resp["Events"].([]any)
assert.Empty(t, events, "a future StartTime should filter out all past events")
}
@@ -183,7 +184,7 @@ func TestHandler_DescribeEvents(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
events := resp["Events"].([]any)
assert.Len(t, events, tt.wantCount)
})
@@ -242,7 +243,7 @@ func TestHandler_Events_Generated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
events, _ := resp["Events"].([]any)
assert.NotEmpty(t, events, "expected events for %s", tt.wantSrcName)
})
@@ -308,7 +309,7 @@ func TestHandler_Events_SourceTypeFiltering(t *testing.T) {
require.Equal(t, http.StatusOK, evRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(evRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(evRec.Body.Bytes(), &resp, jsonx.V1()))
events, _ := resp["Events"].([]any)
assert.GreaterOrEqual(t, len(events), tt.wantEventCount)
})
@@ -435,7 +436,7 @@ func TestHandler_Events_AutoPopulated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
events := resp["Events"].([]any)
require.NotEmpty(t, events, "expected at least one event for source %q type %q", tt.wantSrc, tt.wantType)
@@ -501,7 +502,7 @@ func TestHandler_DescribeEvents_Duration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
events := resp["Events"].([]any)
assert.GreaterOrEqual(t, len(events), tt.wantMinCount)
})
diff --git a/services/memorydb/handler_multi_region_cluster_membership_test.go b/services/memorydb/handler_multi_region_cluster_membership_test.go
index 46e7c2d026..b8e561cc01 100644
--- a/services/memorydb/handler_multi_region_cluster_membership_test.go
+++ b/services/memorydb/handler_multi_region_cluster_membership_test.go
@@ -1,12 +1,14 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_MultiRegionCluster_TLSEnabled verifies TLSEnabled defaults to
@@ -60,7 +62,7 @@ func TestHandler_MultiRegionCluster_TLSEnabled(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
mrc, _ := resp["MultiRegionCluster"].(map[string]any)
require.NotNil(t, mrc)
assert.Equal(t, tt.wantTLS, mrc["TLSEnabled"])
@@ -86,7 +88,7 @@ func TestHandler_DescribeMultiRegionClusters_ShowClusterDetails(t *testing.T) {
require.Equal(t, http.StatusOK, createMRC.Code)
var mrcResp map[string]any
- require.NoError(t, json.Unmarshal(createMRC.Body.Bytes(), &mrcResp))
+ require.NoError(t, json.Unmarshal(createMRC.Body.Bytes(), &mrcResp, jsonx.V1()))
mrc, _ := mrcResp["MultiRegionCluster"].(map[string]any)
mrcName, _ := mrc["MultiRegionClusterName"].(string)
require.NotEmpty(t, mrcName)
@@ -108,7 +110,7 @@ func TestHandler_DescribeMultiRegionClusters_ShowClusterDetails(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
mrcs, _ := resp["MultiRegionClusters"].([]any)
require.Len(t, mrcs, 1)
got, _ := mrcs[0].(map[string]any)
@@ -125,7 +127,7 @@ func TestHandler_DescribeMultiRegionClusters_ShowClusterDetails(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
mrcs, _ := resp["MultiRegionClusters"].([]any)
require.Len(t, mrcs, 1)
got, _ := mrcs[0].(map[string]any)
@@ -160,7 +162,7 @@ func TestHandler_CreateCluster_MultiRegionClusterName_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code, "body: %s", rec.Body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["__type"], "MultiRegionClusterNotFoundFault")
}
@@ -180,7 +182,7 @@ func TestHandler_CreateCluster_MultiRegionClusterName_Linked(t *testing.T) {
require.Equal(t, http.StatusOK, createMRC.Code)
var mrcResp map[string]any
- require.NoError(t, json.Unmarshal(createMRC.Body.Bytes(), &mrcResp))
+ require.NoError(t, json.Unmarshal(createMRC.Body.Bytes(), &mrcResp, jsonx.V1()))
mrc, _ := mrcResp["MultiRegionCluster"].(map[string]any)
mrcName, _ := mrc["MultiRegionClusterName"].(string)
require.NotEmpty(t, mrcName)
@@ -194,7 +196,7 @@ func TestHandler_CreateCluster_MultiRegionClusterName_Linked(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cl, _ := resp["Cluster"].(map[string]any)
assert.Equal(t, mrcName, cl["MultiRegionClusterName"])
}
diff --git a/services/memorydb/handler_multi_region_clusters.go b/services/memorydb/handler_multi_region_clusters.go
index 0f26dea002..bd796ec767 100644
--- a/services/memorydb/handler_multi_region_clusters.go
+++ b/services/memorydb/handler_multi_region_clusters.go
@@ -2,17 +2,19 @@ package memorydb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"sort"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateMultiRegionCluster(ctx context.Context, c *echo.Context, body []byte) error {
var req createMultiRegionClusterRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -42,7 +44,7 @@ func (h *Handler) handleCreateMultiRegionCluster(ctx context.Context, c *echo.Co
func (h *Handler) handleDeleteMultiRegionCluster(ctx context.Context, c *echo.Context, body []byte) error {
var req deleteMultiRegionClusterRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -71,7 +73,7 @@ func (h *Handler) handleDeleteMultiRegionCluster(ctx context.Context, c *echo.Co
func (h *Handler) handleDescribeMultiRegionClusters(ctx context.Context, c *echo.Context, body []byte) error {
var req describeMultiRegionClustersRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -108,7 +110,7 @@ func (h *Handler) handleDescribeMultiRegionClusters(ctx context.Context, c *echo
func (h *Handler) handleDescribeMultiRegionParameterGroups(ctx context.Context, c *echo.Context, body []byte) error {
var req describeMultiRegionParameterGroupsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -143,7 +145,7 @@ func (h *Handler) handleDescribeMultiRegionParameterGroups(ctx context.Context,
func (h *Handler) handleListAllowedMultiRegionClusterUpdates(ctx context.Context, c *echo.Context, body []byte) error {
var req listAllowedMultiRegionClusterUpdatesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -170,7 +172,7 @@ func (h *Handler) handleListAllowedMultiRegionClusterUpdates(ctx context.Context
func (h *Handler) handleUpdateMultiRegionCluster(ctx context.Context, c *echo.Context, body []byte) error {
var req updateMultiRegionClusterRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -196,7 +198,7 @@ func (h *Handler) handleUpdateMultiRegionCluster(ctx context.Context, c *echo.Co
func (h *Handler) handleDescribeMultiRegionParameters(ctx context.Context, c *echo.Context, body []byte) error {
var req describeMultiRegionParametersRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/memorydb/handler_multi_region_clusters_test.go b/services/memorydb/handler_multi_region_clusters_test.go
index b12e81e4c7..03736590fd 100644
--- a/services/memorydb/handler_multi_region_clusters_test.go
+++ b/services/memorydb/handler_multi_region_clusters_test.go
@@ -1,13 +1,14 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -59,7 +60,7 @@ func TestHandler_DescribeMultiRegionParameters_WithGroup(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["MultiRegionParameters"])
}
@@ -75,7 +76,7 @@ func TestHandler_ListAllowedMultiRegionClusterUpdates_OK(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var mrcOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mrcOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mrcOut, jsonx.V1()))
mrc := mrcOut["MultiRegionCluster"].(map[string]any)
mrcName := mrc["MultiRegionClusterName"].(string)
@@ -126,7 +127,7 @@ func TestHandler_UpdateMultiRegionCluster_OK(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var mrcOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mrcOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mrcOut, jsonx.V1()))
mrc := mrcOut["MultiRegionCluster"].(map[string]any)
mrcName := mrc["MultiRegionClusterName"].(string)
@@ -137,7 +138,7 @@ func TestHandler_UpdateMultiRegionCluster_OK(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
updated := out["MultiRegionCluster"].(map[string]any)
assert.Equal(t, "updated description", updated["Description"])
}
@@ -224,7 +225,7 @@ func TestHandler_CreateMultiRegionCluster(t *testing.T) {
if tt.wantMRC {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
mrc, ok := resp["MultiRegionCluster"]
require.True(t, ok)
mrcMap := mrc.(map[string]any)
@@ -272,7 +273,7 @@ func TestHandler_DeleteMultiRegionCluster(t *testing.T) {
var createResp map[string]any
createRec := doRequest(t, h, "DescribeMultiRegionClusters", map[string]any{})
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
clusters := createResp["MultiRegionClusters"].([]any)
require.Len(t, clusters, 1)
clusterName := clusters[0].(map[string]any)["MultiRegionClusterName"].(string)
@@ -342,7 +343,7 @@ func TestHandler_DescribeMultiRegionClusters(t *testing.T) {
if tt.wantCount > 0 {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusters := resp["MultiRegionClusters"].([]any)
assert.Len(t, clusters, tt.wantCount)
}
@@ -382,7 +383,7 @@ func TestHandler_DescribeMultiRegionParameterGroups(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups := resp["MultiRegionParameterGroups"].([]any)
assert.Len(t, groups, tt.wantCount)
}
@@ -414,7 +415,7 @@ func TestHandler_MultiRegionParameterGroups_DefaultSeeded(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups, _ := resp["MultiRegionParameterGroups"].([]any)
assert.NotEmpty(t, groups, "multi-region parameter group %q should be seeded", tt.pgName)
})
@@ -430,7 +431,7 @@ func TestHandler_MultiRegionParameterGroups_DefaultCount(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups, _ := resp["MultiRegionParameterGroups"].([]any)
assert.GreaterOrEqual(t, len(groups), 4, "at least 4 default multi-region parameter groups expected")
}
@@ -457,7 +458,7 @@ func TestHandler_MultiRegionParameters_DefaultNonEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
params, _ := resp["MultiRegionParameters"].([]any)
assert.NotEmpty(t, params, "multi-region parameters should be non-empty for %q", tt.pgName)
})
@@ -481,7 +482,7 @@ func TestHandler_MultiRegionParameters_HaveSourceField(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
params, _ := resp["MultiRegionParameters"].([]any)
require.NotEmpty(t, params)
@@ -593,7 +594,7 @@ func TestHandler_DescribeMultiRegionParameterGroups_FilteredAndNotFound(t *testi
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups := resp["MultiRegionParameterGroups"].([]any)
assert.Len(t, groups, tt.wantCount)
}
diff --git a/services/memorydb/handler_parameter_groups.go b/services/memorydb/handler_parameter_groups.go
index 00c088e52f..f5982753b2 100644
--- a/services/memorydb/handler_parameter_groups.go
+++ b/services/memorydb/handler_parameter_groups.go
@@ -2,17 +2,19 @@ package memorydb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"sort"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateParameterGroup(ctx context.Context, c *echo.Context, body []byte) error {
var req createParameterGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -32,10 +34,15 @@ func (h *Handler) handleCreateParameterGroup(ctx context.Context, c *echo.Contex
return c.JSON(http.StatusOK, createParameterGroupResponse{ParameterGroup: toParameterGroupObject(pg)})
}
-func (h *Handler) handleDescribeParameterGroups(ctx context.Context, c *echo.Context, body []byte) error {
+//nolint:dupl // structurally similar handlers
+func (h *Handler) handleDescribeParameterGroups(
+ ctx context.Context,
+ c *echo.Context,
+ body []byte,
+) error {
var req describeParameterGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -63,7 +70,7 @@ func (h *Handler) handleDescribeParameterGroups(ctx context.Context, c *echo.Con
func (h *Handler) handleDeleteParameterGroup(ctx context.Context, c *echo.Context, body []byte) error {
var req deleteParameterGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -82,7 +89,7 @@ func (h *Handler) handleDeleteParameterGroup(ctx context.Context, c *echo.Contex
func (h *Handler) handleUpdateParameterGroup(ctx context.Context, c *echo.Context, body []byte) error {
var req updateParameterGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -103,7 +110,7 @@ func (h *Handler) handleUpdateParameterGroup(ctx context.Context, c *echo.Contex
func (h *Handler) handleDescribeParameters(ctx context.Context, c *echo.Context, body []byte) error {
var req describeParametersRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -135,7 +142,7 @@ func (h *Handler) handleDescribeParameters(ctx context.Context, c *echo.Context,
func (h *Handler) handleResetParameterGroup(ctx context.Context, c *echo.Context, body []byte) error {
var req resetParameterGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/memorydb/handler_parameter_groups_test.go b/services/memorydb/handler_parameter_groups_test.go
index a661f38af9..a07cc72396 100644
--- a/services/memorydb/handler_parameter_groups_test.go
+++ b/services/memorydb/handler_parameter_groups_test.go
@@ -1,7 +1,7 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"sync"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -67,7 +68,7 @@ func TestHandler_DescribeParameters_OK(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotNil(t, out["Parameters"])
}
@@ -595,7 +596,7 @@ func TestHandler_Pagination_MaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
pgs, _ := resp["ParameterGroups"].([]any)
assert.LessOrEqual(t, len(pgs), tt.maxResults)
if tt.maxResults >= 5 {
@@ -866,7 +867,7 @@ func TestDescribeParameters_Pagination(t *testing.T) {
var allParams struct {
Parameters []map[string]any `json:"Parameters"`
}
- require.NoError(t, json.Unmarshal(all.Body.Bytes(), &allParams))
+ require.NoError(t, json.Unmarshal(all.Body.Bytes(), &allParams, jsonx.V1()))
require.NotEmpty(t, allParams.Parameters)
pageSize := len(allParams.Parameters) - 1
@@ -882,7 +883,7 @@ func TestDescribeParameters_Pagination(t *testing.T) {
NextToken string `json:"NextToken"`
Parameters []map[string]any `json:"Parameters"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &first))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &first, jsonx.V1()))
require.Len(t, first.Parameters, pageSize, "first page must truncate to MaxResults")
require.NotEmpty(t, first.NextToken, "NextToken must be set when more parameters remain")
@@ -897,7 +898,7 @@ func TestDescribeParameters_Pagination(t *testing.T) {
NextToken string `json:"NextToken"`
Parameters []map[string]any `json:"Parameters"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &second))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &second, jsonx.V1()))
require.Len(t, second.Parameters, len(allParams.Parameters)-pageSize, "second page must return the remainder")
assert.NotEqual(
t, first.Parameters[0]["Name"], second.Parameters[0]["Name"], "no parameter must repeat across pages",
diff --git a/services/memorydb/handler_reserved_nodes.go b/services/memorydb/handler_reserved_nodes.go
index 0e3e84006e..0c224a4fd0 100644
--- a/services/memorydb/handler_reserved_nodes.go
+++ b/services/memorydb/handler_reserved_nodes.go
@@ -2,16 +2,18 @@ package memorydb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDescribeReservedNodes(ctx context.Context, c *echo.Context, body []byte) error {
var req describeReservedNodesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -33,7 +35,7 @@ func (h *Handler) handleDescribeReservedNodes(ctx context.Context, c *echo.Conte
func (h *Handler) handleDescribeReservedNodesOfferings(ctx context.Context, c *echo.Context, body []byte) error {
var req describeReservedNodesOfferingsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -59,7 +61,7 @@ func (h *Handler) handleDescribeReservedNodesOfferings(ctx context.Context, c *e
func (h *Handler) handlePurchaseReservedNodesOffering(ctx context.Context, c *echo.Context, body []byte) error {
var req purchaseReservedNodesOfferingRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/memorydb/handler_reserved_nodes_test.go b/services/memorydb/handler_reserved_nodes_test.go
index 13bd235941..faafa8aca4 100644
--- a/services/memorydb/handler_reserved_nodes_test.go
+++ b/services/memorydb/handler_reserved_nodes_test.go
@@ -1,13 +1,14 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -45,7 +46,7 @@ func TestHandler_DescribeReservedNodes(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
nodes := resp["ReservedNodes"].([]any)
assert.Len(t, nodes, tt.wantCount)
@@ -93,7 +94,7 @@ func TestHandler_DescribeReservedNodesOfferings(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
offerings := resp["ReservedNodesOfferings"].([]any)
assert.GreaterOrEqual(t, len(offerings), tt.wantMin)
@@ -150,7 +151,7 @@ func TestHandler_PurchaseReservedNodesOffering(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rn, ok := resp["ReservedNode"].(map[string]any)
require.True(t, ok)
assert.NotEmpty(t, rn["NodeType"])
@@ -179,7 +180,7 @@ func TestHandler_PurchaseReservedNodesOffering_ThenDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, recDescribe.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(recDescribe.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(recDescribe.Body.Bytes(), &resp, jsonx.V1()))
nodes := resp["ReservedNodes"].([]any)
assert.Len(t, nodes, 1)
@@ -196,7 +197,7 @@ func TestWireEpoch_ReservedNode_StartTimeIsNumber(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rn, _ := resp["ReservedNode"].(map[string]any)
require.NotNil(t, rn)
@@ -238,7 +239,7 @@ func TestHandler_ReservedNodes_PurchaseAndDescribe(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rn, _ := resp["ReservedNode"].(map[string]any)
assert.NotNil(t, rn)
assert.NotEmpty(t, rn["ReservationId"])
@@ -257,7 +258,7 @@ func TestHandler_ReservedNodesOfferings_Seeded(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
offerings, _ := resp["ReservedNodesOfferings"].([]any)
assert.GreaterOrEqual(t, len(offerings), 3, "at least 3 reserved node offerings should be seeded")
}
@@ -281,7 +282,7 @@ func TestHandler_ReservedNodes_DescribeAfterPurchase(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
nodes, _ := resp["ReservedNodes"].([]any)
assert.Len(t, nodes, 1)
@@ -345,7 +346,7 @@ func TestHandler_ReservedNodes_WithPurchase(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
nodes := resp["ReservedNodes"].([]any)
assert.Len(t, nodes, tt.wantCount)
@@ -384,7 +385,7 @@ func TestHandler_ReservedNodesOfferings_Filtered(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
offerings := resp["ReservedNodesOfferings"].([]any)
assert.Len(t, offerings, tt.wantCount)
@@ -441,7 +442,7 @@ func TestHandler_DescribeReservedNodesOfferings_DurationFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
offerings := resp["ReservedNodesOfferings"].([]any)
assert.GreaterOrEqual(t, len(offerings), tt.wantMinCount)
diff --git a/services/memorydb/handler_service_updates.go b/services/memorydb/handler_service_updates.go
index 5daadc40fb..b758a93d36 100644
--- a/services/memorydb/handler_service_updates.go
+++ b/services/memorydb/handler_service_updates.go
@@ -2,17 +2,18 @@ package memorydb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDescribeServiceUpdates(ctx context.Context, c *echo.Context, body []byte) error {
var req describeServiceUpdatesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
updates, err := h.Backend.DescribeServiceUpdates(ctx, &req)
diff --git a/services/memorydb/handler_service_updates_test.go b/services/memorydb/handler_service_updates_test.go
index 4131cfcbfe..ae16d54868 100644
--- a/services/memorydb/handler_service_updates_test.go
+++ b/services/memorydb/handler_service_updates_test.go
@@ -1,13 +1,14 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -34,7 +35,7 @@ func TestWireEpoch_DescribeServiceUpdates_DatesAreNumbers(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
updates, _ := resp["ServiceUpdates"].([]any)
require.NotEmpty(t, updates)
@@ -62,7 +63,7 @@ func TestHandler_DescribeServiceUpdates_Basic(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotNil(t, out["ServiceUpdates"])
}
@@ -125,7 +126,7 @@ func TestHandler_DescribeServiceUpdates_Filtering(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
updates, _ := resp["ServiceUpdates"].([]any)
assert.GreaterOrEqual(t, len(updates), tt.wantMinCount)
assert.LessOrEqual(t, len(updates), tt.wantMaxCount)
@@ -172,7 +173,7 @@ func TestHandler_DescribeServiceUpdates_Filtered(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
updates := resp["ServiceUpdates"].([]any)
assert.Len(t, updates, tt.wantCount)
@@ -227,7 +228,7 @@ func TestHandler_DescribeServiceUpdates_SeededFixtures(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
updates := resp["ServiceUpdates"].([]any)
assert.GreaterOrEqual(t, len(updates), tt.wantMinCount)
})
@@ -244,7 +245,7 @@ func TestHandler_DescribeServiceUpdates_FieldsPopulated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
updates := resp["ServiceUpdates"].([]any)
require.NotEmpty(t, updates)
@@ -316,7 +317,7 @@ func TestHandler_DescribeServiceUpdates_ClusterNamesFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
updates := resp["ServiceUpdates"].([]any)
require.Len(t, updates, tt.wantCount)
@@ -352,7 +353,7 @@ func TestHandler_BatchUpdateCluster_ThenDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
updates := resp["ServiceUpdates"].([]any)
require.Len(t, updates, 1)
assert.Equal(t, "complete", updates[0].(map[string]any)["Status"])
diff --git a/services/memorydb/handler_shards_test.go b/services/memorydb/handler_shards_test.go
index 91f44d8580..e6d1f3b424 100644
--- a/services/memorydb/handler_shards_test.go
+++ b/services/memorydb/handler_shards_test.go
@@ -1,7 +1,7 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -69,7 +70,7 @@ func TestHandler_ListAllowedNodeTypeUpdates_OK(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotNil(t, out["ScaleUpNodeTypes"])
assert.NotNil(t, out["ScaleDownNodeTypes"])
}
@@ -114,7 +115,7 @@ func TestHandler_FailoverShard_EmitsEvent_ViaMinimalBody(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "failover: %s", rec.Body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cl, _ := resp["Cluster"].(map[string]any)
assert.Equal(t, "failover-cluster", cl["Name"])
@@ -124,7 +125,7 @@ func TestHandler_FailoverShard_EmitsEvent_ViaMinimalBody(t *testing.T) {
})
require.Equal(t, http.StatusOK, evRec.Code)
var evResp map[string]any
- require.NoError(t, json.Unmarshal(evRec.Body.Bytes(), &evResp))
+ require.NoError(t, json.Unmarshal(evRec.Body.Bytes(), &evResp, jsonx.V1()))
events, _ := evResp["Events"].([]any)
assert.GreaterOrEqual(t, len(events), 2, "create + failover events expected")
}
@@ -277,7 +278,7 @@ func TestHandler_UpdateCluster_ShardConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cl := resp["Cluster"].(map[string]any)
assert.InDelta(t, tt.wantShards, cl["NumberOfShards"], 0)
assert.InDelta(t, tt.wantReplicas, firstShardReplicaCount(t, cl), 0)
@@ -335,7 +336,7 @@ func TestHandler_DescribeClusters_ShowShardDetails(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusters := resp["Clusters"].([]any)
require.Len(t, clusters, 1)
cl := clusters[0].(map[string]any)
@@ -398,7 +399,7 @@ func TestHandler_ShardNodes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
clusters := resp["Clusters"].([]any)
cl := clusters[0].(map[string]any)
@@ -489,7 +490,7 @@ func TestHandler_FailoverShard(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Cluster"])
}
})
@@ -520,7 +521,7 @@ func TestHandler_FailoverShard_EmitsEvent(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
events := resp["Events"].([]any)
found := false
@@ -584,7 +585,7 @@ func TestHandler_ListAllowedNodeTypeUpdates(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["ScaleUpNodeTypes"])
assert.NotNil(t, resp["ScaleDownNodeTypes"])
}
diff --git a/services/memorydb/handler_snapshots.go b/services/memorydb/handler_snapshots.go
index 577ddc606c..08df00abf6 100644
--- a/services/memorydb/handler_snapshots.go
+++ b/services/memorydb/handler_snapshots.go
@@ -2,16 +2,18 @@ package memorydb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateSnapshot(ctx context.Context, c *echo.Context, body []byte) error {
var req createSnapshotRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -38,7 +40,7 @@ func (h *Handler) handleCreateSnapshot(ctx context.Context, c *echo.Context, bod
func (h *Handler) handleCopySnapshot(ctx context.Context, c *echo.Context, body []byte) error {
var req copySnapshotRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -70,7 +72,7 @@ func (h *Handler) handleCopySnapshot(ctx context.Context, c *echo.Context, body
func (h *Handler) handleDeleteSnapshot(ctx context.Context, c *echo.Context, body []byte) error {
var req deleteSnapshotRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -89,7 +91,7 @@ func (h *Handler) handleDeleteSnapshot(ctx context.Context, c *echo.Context, bod
func (h *Handler) handleDescribeSnapshots(ctx context.Context, c *echo.Context, body []byte) error {
var req describeSnapshotRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -124,7 +126,7 @@ func (h *Handler) handleDescribeSnapshots(ctx context.Context, c *echo.Context,
func (h *Handler) handleExportSnapshot(ctx context.Context, c *echo.Context, body []byte) error {
var req exportSnapshotRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/memorydb/handler_snapshots_test.go b/services/memorydb/handler_snapshots_test.go
index 4e4b8b8273..d0a7f1a662 100644
--- a/services/memorydb/handler_snapshots_test.go
+++ b/services/memorydb/handler_snapshots_test.go
@@ -1,7 +1,7 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"sync"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -41,7 +42,7 @@ func TestWireShape_Snapshot_NoTopLevelCreationTime_HasDataTiering(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
snap, _ := resp["Snapshot"].(map[string]any)
require.NotNil(t, snap)
@@ -146,7 +147,7 @@ func TestHandler_ExportSnapshot(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "export snapshot: %s", rec.Body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
snap, _ := resp["Snapshot"].(map[string]any)
assert.Equal(t, "export-snap", snap["Name"], "Snapshot.Name must match")
assert.NotEmpty(t, snap["ARN"], "Snapshot.ARN must be present")
@@ -236,7 +237,7 @@ func TestHandler_CreateSnapshot(t *testing.T) {
if tt.wantSnapshot {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
snap, ok := resp["Snapshot"]
require.True(t, ok)
snapMap := snap.(map[string]any)
@@ -309,7 +310,7 @@ func TestHandler_CopySnapshot(t *testing.T) {
if tt.wantName != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
snap := resp["Snapshot"].(map[string]any)
assert.Equal(t, tt.wantName, snap["Name"])
}
@@ -523,7 +524,7 @@ func TestHandler_Snapshot_FilterBySource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
snaps, _ := resp["Snapshots"].([]any)
assert.GreaterOrEqual(t, len(snaps), tt.wantMinResults,
"filter=%q: expected >= %d snapshots", tt.filterSource, tt.wantMinResults)
@@ -627,7 +628,7 @@ func TestHandler_DescribeSnapshots_Filtered(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
snapshots := resp["Snapshots"].([]any)
assert.Len(t, snapshots, tt.wantCount)
@@ -711,7 +712,7 @@ func TestHandler_AutomatedSnapshot_OnCreate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
snaps := resp["Snapshots"].([]any)
if tt.wantAutoSnap {
@@ -785,7 +786,7 @@ func TestHandler_DescribeSnapshots_SourceFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
snaps := resp["Snapshots"].([]any)
assert.GreaterOrEqual(t, len(snaps), tt.wantMinCount)
@@ -999,7 +1000,7 @@ func TestHandler_DescribeSnapshots_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
snaps := resp["Snapshots"].([]any)
assert.Len(t, snaps, 2)
assert.NotEmpty(t, resp["NextToken"])
diff --git a/services/memorydb/handler_subnet_groups.go b/services/memorydb/handler_subnet_groups.go
index 424fc4e25a..2bd198102e 100644
--- a/services/memorydb/handler_subnet_groups.go
+++ b/services/memorydb/handler_subnet_groups.go
@@ -2,19 +2,20 @@ package memorydb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateSubnetGroup(ctx context.Context, c *echo.Context, body []byte) error {
var req createSubnetGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -34,10 +35,15 @@ func (h *Handler) handleCreateSubnetGroup(ctx context.Context, c *echo.Context,
return c.JSON(http.StatusOK, createSubnetGroupResponse{SubnetGroup: toSubnetGroupObject(sg)})
}
-func (h *Handler) handleDescribeSubnetGroups(ctx context.Context, c *echo.Context, body []byte) error {
+//nolint:dupl // structurally similar handlers
+func (h *Handler) handleDescribeSubnetGroups(
+ ctx context.Context,
+ c *echo.Context,
+ body []byte,
+) error {
var req describeSubnetGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -60,7 +66,7 @@ func (h *Handler) handleDescribeSubnetGroups(ctx context.Context, c *echo.Contex
func (h *Handler) handleDeleteSubnetGroup(ctx context.Context, c *echo.Context, body []byte) error {
var req deleteSubnetGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -79,7 +85,7 @@ func (h *Handler) handleDeleteSubnetGroup(ctx context.Context, c *echo.Context,
func (h *Handler) handleUpdateSubnetGroup(ctx context.Context, c *echo.Context, body []byte) error {
var req updateSubnetGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/memorydb/handler_subnet_groups_test.go b/services/memorydb/handler_subnet_groups_test.go
index e61d074d47..490e3ccc97 100644
--- a/services/memorydb/handler_subnet_groups_test.go
+++ b/services/memorydb/handler_subnet_groups_test.go
@@ -1,13 +1,14 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -283,7 +284,7 @@ func TestHandler_SubnetGroup_SupportedNetworkTypesAndAvailabilityZone(t *testing
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sg, _ := resp["SubnetGroup"].(map[string]any)
require.NotNil(t, sg)
diff --git a/services/memorydb/handler_tags.go b/services/memorydb/handler_tags.go
index fe75c677d4..70160154c8 100644
--- a/services/memorydb/handler_tags.go
+++ b/services/memorydb/handler_tags.go
@@ -2,16 +2,18 @@ package memorydb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleListTags(ctx context.Context, c *echo.Context, body []byte) error {
var req listTagsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -30,7 +32,7 @@ func (h *Handler) handleListTags(ctx context.Context, c *echo.Context, body []by
func (h *Handler) handleTagResource(ctx context.Context, c *echo.Context, body []byte) error {
var req tagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -60,7 +62,7 @@ func (h *Handler) handleTagResource(ctx context.Context, c *echo.Context, body [
func (h *Handler) handleUntagResource(ctx context.Context, c *echo.Context, body []byte) error {
var req untagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/memorydb/handler_tags_test.go b/services/memorydb/handler_tags_test.go
index 9c185347d6..018ded1354 100644
--- a/services/memorydb/handler_tags_test.go
+++ b/services/memorydb/handler_tags_test.go
@@ -1,7 +1,7 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -42,7 +43,7 @@ func TestHandler_Tags(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
clusterMap := createResp["Cluster"].(map[string]any)
clusterARN := clusterMap["ARN"].(string)
@@ -52,7 +53,7 @@ func TestHandler_Tags(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsResp, jsonx.V1()))
tagList := tagsResp["TagList"].([]any)
require.NotEmpty(t, tagList)
}
@@ -470,7 +471,7 @@ func TestHandler_CreateCluster_TagsStored(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tagList, _ := listResp["TagList"].([]any)
require.Len(t, tagList, 2)
@@ -504,7 +505,7 @@ func TestHandler_Tags_AllResources(t *testing.T) {
"Tags": []map[string]any{{"Key": "Env", "Value": "prod"}},
})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["SubnetGroup"].(map[string]any)["ARN"].(string)
},
@@ -523,7 +524,7 @@ func TestHandler_Tags_AllResources(t *testing.T) {
"Tags": []map[string]any{{"Key": "Env", "Value": "prod"}},
})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["User"].(map[string]any)["ARN"].(string)
},
@@ -538,7 +539,7 @@ func TestHandler_Tags_AllResources(t *testing.T) {
"Tags": []map[string]any{{"Key": "Env", "Value": "prod"}},
})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["ParameterGroup"].(map[string]any)["ARN"].(string)
},
@@ -559,7 +560,7 @@ func TestHandler_Tags_AllResources(t *testing.T) {
"Tags": []map[string]any{{"Key": "Env", "Value": "prod"}},
})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["Snapshot"].(map[string]any)["ARN"].(string)
},
@@ -578,7 +579,7 @@ func TestHandler_Tags_AllResources(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tagList := resp["TagList"].([]any)
assert.NotEmpty(t, tagList)
@@ -664,7 +665,7 @@ func TestHandler_TagOperations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "create: %s", rec.Body)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
resourceARN := tt.getARN(createResp)
require.NotEmpty(t, resourceARN)
@@ -683,7 +684,7 @@ func TestHandler_TagOperations(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tagList, _ := listResp["TagList"].([]any)
assert.Len(t, tagList, 2)
@@ -697,7 +698,7 @@ func TestHandler_TagOperations(t *testing.T) {
// Verify only 1 tag remains.
listRec2 := doRequest(t, h, "ListTags", map[string]any{"ResourceArn": resourceARN})
require.Equal(t, http.StatusOK, listRec2.Code)
- require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp, jsonx.V1()))
tagList2, _ := listResp["TagList"].([]any)
assert.Len(t, tagList2, 1)
})
@@ -794,7 +795,7 @@ func TestHandler_TagResource_Limit(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
clusterMap := createResp["Cluster"].(map[string]any)
arn := clusterMap["ARN"].(string)
@@ -890,7 +891,7 @@ func TestHandler_Tags_Extended(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tagList := resp["TagList"].([]any)
tags := make(map[string]string)
for _, t := range tagList {
@@ -931,7 +932,7 @@ func TestHandler_UntagResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
tagList := resp["TagList"].([]any)
assert.Len(t, tagList, 1)
tag := tagList[0].(map[string]any)
diff --git a/services/memorydb/handler_test.go b/services/memorydb/handler_test.go
index 6d99adc725..d367ccffd6 100644
--- a/services/memorydb/handler_test.go
+++ b/services/memorydb/handler_test.go
@@ -2,7 +2,7 @@ package memorydb_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -23,7 +24,7 @@ func doCreateCluster(t *testing.T, h *memorydb.Handler, body map[string]any) (ma
t.Helper()
rec := doRequest(t, h, "CreateCluster", body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp, rec.Code
}
@@ -37,7 +38,7 @@ func doDescribeUsers(t *testing.T, h *memorydb.Handler, userName string) []any {
rec := doRequest(t, h, "DescribeUsers", body)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
users, _ := resp["Users"].([]any)
return users
@@ -52,7 +53,7 @@ func doDescribeACLs(t *testing.T, h *memorydb.Handler, aclName string) []any {
rec := doRequest(t, h, "DescribeACLs", body)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
acls, _ := resp["ACLs"].([]any)
return acls
@@ -67,7 +68,7 @@ func doDescribeSnapshots(t *testing.T, h *memorydb.Handler, snapshotName string)
rec := doRequest(t, h, "DescribeSnapshots", body)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
snaps, _ := resp["Snapshots"].([]any)
return snaps
@@ -82,7 +83,7 @@ func doDescribeParameterGroups(t *testing.T, h *memorydb.Handler, pgName string)
rec := doRequest(t, h, "DescribeParameterGroups", body)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pgs, _ := resp["ParameterGroups"].([]any)
return pgs
@@ -93,7 +94,7 @@ func doDescribeParameters(t *testing.T, h *memorydb.Handler, pgName string) []an
rec := doRequest(t, h, "DescribeParameters", map[string]any{"ParameterGroupName": pgName})
require.Equal(t, http.StatusOK, rec.Code, "DescribeParameters failed: %s", rec.Body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
params, _ := resp["Parameters"].([]any)
return params
@@ -130,7 +131,7 @@ func doRequest(t *testing.T, h *memorydb.Handler, op string, body any) *httptest
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -238,7 +239,7 @@ func TestHandler_Infrastructure(t *testing.T) {
// callers use this instead and assert on the result back in the main
// goroutine.
func doRequestAsync(h *memorydb.Handler, op string, body any) (*httptest.ResponseRecorder, error) {
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
if err != nil {
return nil, err
}
@@ -280,7 +281,7 @@ func collectSnapshotARNs(t *testing.T, h *memorydb.Handler) []string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
snaps, _ := resp["Snapshots"].([]any)
@@ -383,7 +384,7 @@ func TestExtractResourceSnapshotName(t *testing.T) {
func doRequestNoTarget(t *testing.T, h *memorydb.Handler, body any) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -409,7 +410,7 @@ func doRequestWithServiceHeader( //nolint:unused // existing issue.
) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -554,7 +555,7 @@ func TestHandler_ExtractResource(t *testing.T) {
t.Parallel()
h := newTestHandler(t)
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -741,7 +742,7 @@ func createCluster(t *testing.T, h *memorydb.Handler, body map[string]any) map[s
require.Equal(t, http.StatusOK, rec.Code, "create cluster failed: %s", rec.Body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
@@ -818,7 +819,7 @@ func TestPaginationAcrossResourceTypes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// Find the list field.
var items []any
diff --git a/services/memorydb/handler_users.go b/services/memorydb/handler_users.go
index 3217145680..d469649a1d 100644
--- a/services/memorydb/handler_users.go
+++ b/services/memorydb/handler_users.go
@@ -2,18 +2,20 @@ package memorydb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"math"
"net/http"
"slices"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateUser(ctx context.Context, c *echo.Context, body []byte) error {
var req createUserRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -36,7 +38,7 @@ func (h *Handler) handleCreateUser(ctx context.Context, c *echo.Context, body []
func (h *Handler) handleDescribeUsers(ctx context.Context, c *echo.Context, body []byte) error {
var req describeUserRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -92,7 +94,7 @@ func filterUsersByName(users []*User, filters []userFilterReq) []*User {
func (h *Handler) handleDeleteUser(ctx context.Context, c *echo.Context, body []byte) error {
var req deleteUserRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -111,7 +113,7 @@ func (h *Handler) handleDeleteUser(ctx context.Context, c *echo.Context, body []
func (h *Handler) handleUpdateUser(ctx context.Context, c *echo.Context, body []byte) error {
var req updateUserRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/memorydb/handler_users_test.go b/services/memorydb/handler_users_test.go
index 7448593fe0..91b3d0c270 100644
--- a/services/memorydb/handler_users_test.go
+++ b/services/memorydb/handler_users_test.go
@@ -1,13 +1,14 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -42,7 +43,7 @@ func TestWireAuthType_DefaultUserIsNoPassword(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
user, _ := resp["User"].(map[string]any)
auth, _ := user["Authentication"].(map[string]any)
@@ -86,7 +87,7 @@ func TestWireAuthType_UpdateUserValidatesType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
user, _ := resp["User"].(map[string]any)
auth, _ := user["Authentication"].(map[string]any)
assert.Equal(t, "no-password", auth["Type"])
@@ -172,7 +173,7 @@ func TestHandler_UserResponse_NoEngineField(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
u, _ := resp["User"].(map[string]any)
_, hasEngine := u["Engine"]
assert.False(t, hasEngine, "Engine must not appear in the User response (not part of the real SDK type)")
@@ -192,7 +193,7 @@ func TestHandler_UserResponse_ACLNames_CreateReturnsEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
u, _ := resp["User"].(map[string]any)
aclNames, hasField := u["ACLNames"]
diff --git a/services/memorydb/lifecycle_test.go b/services/memorydb/lifecycle_test.go
index 5cd2491ff2..161e50989a 100644
--- a/services/memorydb/lifecycle_test.go
+++ b/services/memorydb/lifecycle_test.go
@@ -1,7 +1,7 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -29,7 +30,7 @@ func TestCluster_LifecycleDelay_Default(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cl, _ := resp["Cluster"].(map[string]any)
assert.Equal(t, "available", cl["Status"])
}
@@ -63,7 +64,7 @@ func TestCluster_LifecycleDelay_CreatingThenAvailable(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code, "body: %s", createRec.Body)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
cl, _ := createResp["Cluster"].(map[string]any)
assert.Equal(t, "creating", cl["Status"], "CreateCluster response must show creating while pending")
@@ -72,7 +73,7 @@ func TestCluster_LifecycleDelay_CreatingThenAvailable(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
clusters, _ := descResp["Clusters"].([]any)
require.Len(t, clusters, 1)
got, _ := clusters[0].(map[string]any)
@@ -85,7 +86,7 @@ func TestCluster_LifecycleDelay_CreatingThenAvailable(t *testing.T) {
require.Equal(t, http.StatusOK, afterRec.Code)
var afterResp map[string]any
- require.NoError(t, json.Unmarshal(afterRec.Body.Bytes(), &afterResp))
+ require.NoError(t, json.Unmarshal(afterRec.Body.Bytes(), &afterResp, jsonx.V1()))
afterClusters, _ := afterResp["Clusters"].([]any)
require.Len(t, afterClusters, 1)
afterGot, _ := afterClusters[0].(map[string]any)
@@ -123,7 +124,7 @@ func TestCluster_LifecycleDelay_UpdateAndUnrelatedOpsObserveOverlay(t *testing.T
require.Equal(t, http.StatusOK, updateRec.Code, "body: %s", updateRec.Body)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
cl, _ := updateResp["Cluster"].(map[string]any)
assert.Equal(t, "creating", cl["Status"], "UpdateCluster response must still observe the pending status")
assert.Equal(t, "updated while creating", cl["Description"])
diff --git a/services/memorydb/models_events.go b/services/memorydb/models_events.go
index c6a1a00d64..4c73580c72 100644
--- a/services/memorydb/models_events.go
+++ b/services/memorydb/models_events.go
@@ -1,8 +1,9 @@
package memorydb
import (
- "encoding/json"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Event represents a MemoryDB event.
@@ -19,13 +20,13 @@ type Event struct {
// requires a quoted RFC3339 string -- decoding a real client's request would
// fail with a SerializationException. See parseEpochRequestTime (backend.go).
type describeEventsRequest struct {
- MaxResults *int32 `json:"MaxResults,omitempty"`
- Duration *int32 `json:"Duration,omitempty"`
- StartTime json.Number `json:"StartTime,omitempty"`
- EndTime json.Number `json:"EndTime,omitempty"`
- SourceName string `json:"SourceName,omitempty"`
- SourceType string `json:"SourceType,omitempty"`
- NextToken string `json:"NextToken,omitempty"`
+ MaxResults *int32 `json:"MaxResults,omitempty"`
+ Duration *int32 `json:"Duration,omitempty"`
+ StartTime jsonx.Number `json:"StartTime,omitempty"`
+ EndTime jsonx.Number `json:"EndTime,omitempty"`
+ SourceName string `json:"SourceName,omitempty"`
+ SourceType string `json:"SourceType,omitempty"`
+ NextToken string `json:"NextToken,omitempty"`
}
// eventObject.Date is epoch seconds (float64), matching the real
diff --git a/services/memorydb/parameter_groups_test.go b/services/memorydb/parameter_groups_test.go
index de0b417a5f..37a4b30e06 100644
--- a/services/memorydb/parameter_groups_test.go
+++ b/services/memorydb/parameter_groups_test.go
@@ -2,13 +2,14 @@ package memorydb_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -118,7 +119,7 @@ func TestDescribeParameters_Sorted(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
params, _ := out["Parameters"].([]any)
for i := 1; i < len(params); i++ {
diff --git a/services/memorydb/persistence.go b/services/memorydb/persistence.go
index 3820a8d821..2f70cfe712 100644
--- a/services/memorydb/persistence.go
+++ b/services/memorydb/persistence.go
@@ -2,8 +2,10 @@ package memorydb
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -32,7 +34,7 @@ const memorydbSnapshotVersion = 1
// [store.Table.Snapshot] slice.
type backendSnapshot struct {
ParameterGroups map[string][]*ParameterGroup `json:"parameterGroups"`
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Clusters map[string][]*Cluster `json:"clusters"`
ACLs map[string][]*ACL `json:"acls"`
SubnetGroups map[string][]*SubnetGroup `json:"subnetGroups"`
@@ -89,7 +91,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
DefaultRegion: b.defaultRegion,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "memorydb: failed to marshal snapshot", "error", err)
diff --git a/services/memorydb/persistence_test.go b/services/memorydb/persistence_test.go
index 22f8e93a07..99d3ce0a01 100644
--- a/services/memorydb/persistence_test.go
+++ b/services/memorydb/persistence_test.go
@@ -1,13 +1,14 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -259,10 +260,10 @@ func TestInMemoryBackend_Restore_IncompatibleVersion(t *testing.T) {
// current memorydbSnapshotVersion constant.
var raw map[string]any
- require.NoError(t, json.Unmarshal(original.Snapshot(ctx), &raw))
+ require.NoError(t, json.Unmarshal(original.Snapshot(ctx), &raw, jsonx.V1()))
raw["version"] = -1
- badSnap, err := json.Marshal(raw)
+ badSnap, err := json.Marshal(raw, jsonx.V1())
require.NoError(t, err)
fresh := memorydb.NewInMemoryBackend("123456789012", "us-east-1")
diff --git a/services/memorydb/shard_detail_test.go b/services/memorydb/shard_detail_test.go
index ef4d7fe011..18dcee75b9 100644
--- a/services/memorydb/shard_detail_test.go
+++ b/services/memorydb/shard_detail_test.go
@@ -1,7 +1,7 @@
package memorydb_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -16,7 +17,7 @@ func decodeBody(t *testing.T, body []byte) map[string]any {
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
return out
}
diff --git a/services/memorydb/snapshots_test.go b/services/memorydb/snapshots_test.go
index fadb512861..aa93a4e590 100644
--- a/services/memorydb/snapshots_test.go
+++ b/services/memorydb/snapshots_test.go
@@ -2,13 +2,14 @@ package memorydb_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -56,7 +57,7 @@ func TestDescribeSnapshots(t *testing.T) {
if tt.wantCount > 0 {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
snaps := resp["Snapshots"].([]any)
assert.Len(t, snaps, tt.wantCount)
}
@@ -99,7 +100,7 @@ func TestDescribeSnapshotNoTopLevelCreationTimeField(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
snaps := resp["Snapshots"].([]any)
require.Len(t, snaps, 1)
diff --git a/services/memorydb/tags_test.go b/services/memorydb/tags_test.go
index 334cd811da..068783db24 100644
--- a/services/memorydb/tags_test.go
+++ b/services/memorydb/tags_test.go
@@ -2,13 +2,14 @@ package memorydb_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/memorydb"
)
@@ -108,7 +109,7 @@ func TestTagResourceReturnsTagList(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tagList, ok := resp["TagList"].([]any)
require.True(t, ok)
@@ -138,7 +139,7 @@ func TestUntagResourceReturnsTagList(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tagList := resp["TagList"].([]any)
assert.Len(t, tagList, 1)
diff --git a/services/mgn/handler.go b/services/mgn/handler.go
index 65fd2ddf8d..a653293bd2 100644
--- a/services/mgn/handler.go
+++ b/services/mgn/handler.go
@@ -2,7 +2,7 @@ package mgn
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
@@ -239,7 +240,7 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
addErrorFields(body, apiErr)
}
- payload, _ := json.Marshal(body)
+ payload, _ := json.Marshal(body, jsonx.V1())
c.Response().Header().Set("X-Amzn-Errortype", errType)
@@ -385,14 +386,14 @@ func decodeJSONBody(body []byte, v any) error {
return nil
}
- if err := json.Unmarshal(body, v); err != nil {
+ if err := json.Unmarshal(body, v, jsonx.V1()); err != nil {
return validationError("malformed request body: " + err.Error())
}
var pg struct {
NextToken string `json:"nextToken"`
}
- if json.Unmarshal(body, &pg) == nil && page.ValidateToken(pg.NextToken) != nil {
+ if json.Unmarshal(body, &pg, jsonx.V1()) == nil && page.ValidateToken(pg.NextToken) != nil {
return validationError("Invalid nextToken")
}
@@ -402,7 +403,7 @@ func decodeJSONBody(body []byte, v any) error {
// marshalResponse marshals v, wrapping any error as an internal failure
// (marshaling our own well-typed wire structs should never fail).
func marshalResponse(v any) ([]byte, error) {
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return nil, internalServerError("mgn: marshal response: " + err.Error())
}
diff --git a/services/mgn/multi_region_test.go b/services/mgn/multi_region_test.go
index 467dace111..bb52c77f2f 100644
--- a/services/mgn/multi_region_test.go
+++ b/services/mgn/multi_region_test.go
@@ -2,7 +2,8 @@ package mgn_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/mgn"
)
@@ -183,8 +185,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/mgn/persistence.go b/services/mgn/persistence.go
index 1df02b9f57..c3115392e1 100644
--- a/services/mgn/persistence.go
+++ b/services/mgn/persistence.go
@@ -2,7 +2,7 @@ package mgn
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -19,11 +19,11 @@ const mgnSnapshotVersion = 1
// backendSnapshot is the top-level on-disk shape for the MGN backend.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountId"`
- Region string `json:"region"`
- Version int `json:"version"`
- ServiceInitialized bool `json:"serviceInitialized"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountId"`
+ Region string `json:"region"`
+ Version int `json:"version"`
+ ServiceInitialized bool `json:"serviceInitialized"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/mgn/s3import_launch.go b/services/mgn/s3import_launch.go
index f731522c9c..e081a81957 100644
--- a/services/mgn/s3import_launch.go
+++ b/services/mgn/s3import_launch.go
@@ -2,13 +2,16 @@ package mgn
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
"slices"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -388,10 +391,9 @@ func parseActionParameters(raw string) (map[string]string, error) {
ExternalParameters map[string]string `json:"externalParameters"`
}
- dec := json.NewDecoder(bytes.NewReader([]byte(raw)))
- dec.DisallowUnknownFields()
+ dec := jsontext.NewDecoder(bytes.NewReader([]byte(raw)))
- if err := dec.Decode(&v); err != nil {
+ if err := json.UnmarshalDecode(dec, &v, jsonx.V1(), json.RejectUnknownMembers(true)); err != nil {
return nil, fmt.Errorf("%w: %w", errImportBadParameters, err)
}
diff --git a/services/mgn/wire_field_fixes_test.go b/services/mgn/wire_field_fixes_test.go
index 924cf8026c..29405e5ab1 100644
--- a/services/mgn/wire_field_fixes_test.go
+++ b/services/mgn/wire_field_fixes_test.go
@@ -2,7 +2,8 @@ package mgn_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/mgn"
@@ -37,7 +39,7 @@ func newRawServer(t *testing.T, h *mgn.Handler) string {
func rawPost(t *testing.T, url string, body map[string]any) map[string]any {
t.Helper()
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, url, bytes.NewReader(raw))
@@ -50,7 +52,7 @@ func rawPost(t *testing.T, url string, body map[string]any) map[string]any {
t.Cleanup(func() { _ = resp.Body.Close() })
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
require.Equal(t, http.StatusOK, resp.StatusCode, "response: %v", out)
return out
diff --git a/services/mq/broker_pagination_test.go b/services/mq/broker_pagination_test.go
index 99fd89e905..8af5a254dc 100644
--- a/services/mq/broker_pagination_test.go
+++ b/services/mq/broker_pagination_test.go
@@ -4,7 +4,8 @@ package mq_test
// continuation across pages, maxResults limits, and default listing.
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mq"
)
@@ -54,11 +56,11 @@ func TestListBrokers_PaginationOpaqueToken(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, path, nil)
require.Equal(t, http.StatusOK, rec.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
var brokers []any
- require.NoError(t, json.Unmarshal(out["brokerSummaries"], &brokers))
+ require.NoError(t, json.Unmarshal(out["brokerSummaries"], &brokers, jsonx.V1()))
assert.Len(t, brokers, tt.wantCount)
rawToken, hasToken := out["nextToken"]
@@ -66,7 +68,7 @@ func TestListBrokers_PaginationOpaqueToken(t *testing.T) {
require.True(t, hasToken, "nextToken must be present when more pages exist")
var tok string
- require.NoError(t, json.Unmarshal(rawToken, &tok))
+ require.NoError(t, json.Unmarshal(rawToken, &tok, jsonx.V1()))
assert.NotEmpty(t, tok)
// Token must be opaque base64 integer, not a broker name.
@@ -74,7 +76,7 @@ func TestListBrokers_PaginationOpaqueToken(t *testing.T) {
assert.Equal(t, tt.wantCount, offset, "token encodes offset = items returned so far")
} else if hasToken {
var tok string
- require.NoError(t, json.Unmarshal(rawToken, &tok))
+ require.NoError(t, json.Unmarshal(rawToken, &tok, jsonx.V1()))
assert.Empty(t, tok, "nextToken must be empty string when no more pages")
}
})
@@ -92,27 +94,27 @@ func TestListBrokers_PaginationContinuation(t *testing.T) {
rec1 := doRequest(t, h, http.MethodGet, "/v1/brokers?maxResults=2", nil)
require.Equal(t, http.StatusOK, rec1.Code)
- var page1 map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1))
+ var page1 map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1, jsonx.V1()))
var tok string
- require.NoError(t, json.Unmarshal(page1["nextToken"], &tok))
+ require.NoError(t, json.Unmarshal(page1["nextToken"], &tok, jsonx.V1()))
require.NotEmpty(t, tok)
rec2 := doRequest(t, h, http.MethodGet, "/v1/brokers?maxResults=2&nextToken="+tok, nil)
require.Equal(t, http.StatusOK, rec2.Code)
- var page2 map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ var page2 map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
var brokers2 []any
- require.NoError(t, json.Unmarshal(page2["brokerSummaries"], &brokers2))
+ require.NoError(t, json.Unmarshal(page2["brokerSummaries"], &brokers2, jsonx.V1()))
assert.Len(t, brokers2, 1, "page 2 should contain the remaining broker")
// No further pages.
if rawTok2, ok := page2["nextToken"]; ok {
var tok2 string
- require.NoError(t, json.Unmarshal(rawTok2, &tok2))
+ require.NoError(t, json.Unmarshal(rawTok2, &tok2, jsonx.V1()))
assert.Empty(t, tok2, "no nextToken on final page")
}
}
diff --git a/services/mq/configurations_test.go b/services/mq/configurations_test.go
index fe6441e74c..88d2eff153 100644
--- a/services/mq/configurations_test.go
+++ b/services/mq/configurations_test.go
@@ -2,7 +2,8 @@ package mq_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strconv"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mq"
)
@@ -79,7 +81,7 @@ func TestUpdateConfiguration(t *testing.T) {
updateBody, err := json.Marshal(map[string]any{
"description": "updated description",
"data": "...",
- })
+ }, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -331,11 +333,11 @@ func TestListConfigurations_PaginationOpaqueToken(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, path, nil)
require.Equal(t, http.StatusOK, rec.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
var cfgs []any
- require.NoError(t, json.Unmarshal(out["configurations"], &cfgs))
+ require.NoError(t, json.Unmarshal(out["configurations"], &cfgs, jsonx.V1()))
assert.Len(t, cfgs, tt.wantCount)
rawToken, hasToken := out["nextToken"]
@@ -343,13 +345,13 @@ func TestListConfigurations_PaginationOpaqueToken(t *testing.T) {
require.True(t, hasToken)
var tok string
- require.NoError(t, json.Unmarshal(rawToken, &tok))
+ require.NoError(t, json.Unmarshal(rawToken, &tok, jsonx.V1()))
offset := decodeOpaqueToken(t, tok)
assert.Equal(t, tt.wantCount, offset)
} else if hasToken {
var tok string
- require.NoError(t, json.Unmarshal(rawToken, &tok))
+ require.NoError(t, json.Unmarshal(rawToken, &tok, jsonx.V1()))
assert.Empty(t, tok)
}
})
diff --git a/services/mq/handler_brokers.go b/services/mq/handler_brokers.go
index 22394e9104..83f38e9eaf 100644
--- a/services/mq/handler_brokers.go
+++ b/services/mq/handler_brokers.go
@@ -1,7 +1,7 @@
package mq
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"sort"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -39,7 +40,7 @@ type createBrokerInput struct {
func (h *Handler) handleCreateBroker(c *echo.Context, body []byte) error {
var in createBrokerInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -233,7 +234,7 @@ type updateBrokerResponse struct {
func (h *Handler) handleUpdateBroker(c *echo.Context, brokerID string, body []byte) error {
var in updateBrokerInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -550,7 +551,7 @@ type promoteInput struct {
func (h *Handler) handlePromote(c *echo.Context, brokerID string, body []byte) error {
var in promoteInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/mq/handler_configurations.go b/services/mq/handler_configurations.go
index 32a8c2702e..4c082a8c13 100644
--- a/services/mq/handler_configurations.go
+++ b/services/mq/handler_configurations.go
@@ -1,7 +1,7 @@
package mq
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -25,7 +26,7 @@ type createConfigurationInput struct {
func (h *Handler) handleCreateConfiguration(c *echo.Context, body []byte) error {
var in createConfigurationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("BadRequestException", "invalid request body"),
@@ -122,7 +123,7 @@ type updateConfigurationInput struct {
func (h *Handler) handleUpdateConfiguration(c *echo.Context, configID string, body []byte) error {
var in updateConfigurationInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(
http.StatusBadRequest,
errorResponse("BadRequestException", "invalid request body"),
diff --git a/services/mq/handler_tags.go b/services/mq/handler_tags.go
index e2fd087cce..f9a87f9b49 100644
--- a/services/mq/handler_tags.go
+++ b/services/mq/handler_tags.go
@@ -1,10 +1,12 @@
package mq
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleListTags(c *echo.Context, resourceARN string) error {
@@ -22,7 +24,7 @@ type createTagsInput struct {
func (h *Handler) handleCreateTags(c *echo.Context, resourceARN string, body []byte) error {
var in createTagsInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/mq/handler_test.go b/services/mq/handler_test.go
index 8d5443f2eb..b84bfcb03a 100644
--- a/services/mq/handler_test.go
+++ b/services/mq/handler_test.go
@@ -3,7 +3,7 @@ package mq_test
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strconv"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mq"
)
@@ -47,7 +48,7 @@ func doRequest(t *testing.T, h *mq.Handler, method, path string, body any) *http
var reqBody *bytes.Reader
if body != nil {
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
reqBody = bytes.NewReader(bodyBytes)
} else {
@@ -92,7 +93,7 @@ func parseResponse(t *testing.T, rec *httptest.ResponseRecorder) map[string]any
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/mq/handler_users.go b/services/mq/handler_users.go
index 4802132281..fbe69684dc 100644
--- a/services/mq/handler_users.go
+++ b/services/mq/handler_users.go
@@ -1,13 +1,14 @@
package mq
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -27,7 +28,7 @@ type createUserBody struct {
func (h *Handler) handleCreateUser(c *echo.Context, brokerID, username string, body []byte) error {
var in createUserBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
@@ -83,7 +84,7 @@ type updateUserBody struct {
func (h *Handler) handleUpdateUser(c *echo.Context, brokerID, username string, body []byte) error {
var in updateUserBody
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return c.JSON(http.StatusBadRequest, errorResponse("BadRequestException", "invalid request body"))
}
diff --git a/services/mq/models.go b/services/mq/models.go
index a3e02c9d10..eeab71ccef 100644
--- a/services/mq/models.go
+++ b/services/mq/models.go
@@ -1,8 +1,10 @@
package mq
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -245,7 +247,7 @@ func (l LdapServerMetadata) MarshalJSON() ([]byte, error) {
a := alias(l)
a.ServiceAccountPassword = ""
- b, err := json.Marshal(a)
+ b, err := json.Marshal(a, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("marshal ldap server metadata: %w", err)
}
diff --git a/services/mq/multi_region_test.go b/services/mq/multi_region_test.go
index 296bdd746d..65b4fca182 100644
--- a/services/mq/multi_region_test.go
+++ b/services/mq/multi_region_test.go
@@ -1,7 +1,8 @@
package mq_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mq"
)
@@ -38,7 +40,7 @@ func regionCall(t *testing.T, h *mq.Handler, region, method, path, body string)
out := map[string]any{}
if rec.Body.Len() > 0 {
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
}
return out
@@ -129,9 +131,9 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(t.Context())
- var doc map[string]json.RawMessage
+ var doc map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(snap, &doc))
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
_, hasRegions := doc["regions"]
assert.Equal(t, tc.wantRegions, hasRegions)
diff --git a/services/mq/persistence.go b/services/mq/persistence.go
index 0adc89f782..1d07f21127 100644
--- a/services/mq/persistence.go
+++ b/services/mq/persistence.go
@@ -2,9 +2,11 @@ package mq
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -29,7 +31,7 @@ const mqSnapshotVersion = 1
// store.Table-backed, see store_setup.go) but was persisted before this
// refactor and remains so.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
AccountID string `json:"accountID"`
Region string `json:"region"`
@@ -56,7 +58,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.region,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "mq: failed to marshal snapshot", "error", err)
diff --git a/services/mq/user_summary_wire_test.go b/services/mq/user_summary_wire_test.go
index 8bdc3389ce..5243f9e988 100644
--- a/services/mq/user_summary_wire_test.go
+++ b/services/mq/user_summary_wire_test.go
@@ -1,13 +1,14 @@
package mq_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mq"
)
@@ -40,7 +41,7 @@ func TestUserSummary_NoFabricatedConsoleAccess(t *testing.T) {
"ListUsers' UserSummary items must not carry a fabricated consoleAccess key")
broker := describeTestBroker(t, h, brokerID)
- usersRaw, err := json.Marshal(broker["users"])
+ usersRaw, err := json.Marshal(broker["users"], jsonx.V1())
require.NoError(t, err)
assert.NotContains(t, string(usersRaw), "consoleAccess",
"DescribeBrokerOutput.Users (UserSummary) must not carry a fabricated consoleAccess key")
diff --git a/services/mwaa/airflow_access.go b/services/mwaa/airflow_access.go
index b1466338f0..4a49869c44 100644
--- a/services/mwaa/airflow_access.go
+++ b/services/mwaa/airflow_access.go
@@ -5,7 +5,7 @@ import (
"context"
"crypto/rand"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -13,6 +13,8 @@ import (
"strings"
"time"
"unicode"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -71,7 +73,7 @@ func (e *airflowEngine) doWith(
}
if out != nil && len(res.body) > 0 {
- _ = json.Unmarshal(res.body, out)
+ _ = json.Unmarshal(res.body, out, jsonx.V1())
}
return res.status, res.body, nil
@@ -127,7 +129,7 @@ func (e *airflowEngine) newToken(key, envName, kind string) string {
_, _ = rand.Read(sig)
enc := base64.RawURLEncoding
- payload, _ := json.Marshal(map[string]any{"env": envName, "type": kind, "exp": exp.Unix()})
+ payload, _ := json.Marshal(map[string]any{"env": envName, "type": kind, "exp": exp.Unix()}, jsonx.V1())
tok := enc.EncodeToString([]byte(`{"alg":"HS256","typ":"JWT"}`)) + "." + enc.EncodeToString(payload) + "." +
enc.EncodeToString(sig)
@@ -276,7 +278,7 @@ func (e *airflowEngine) proxyRest(
if req.Body != nil {
var err error
- if body, err = json.Marshal(req.Body); err != nil {
+ if body, err = json.Marshal(req.Body, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: Body is not JSON: %w", ErrInvalidParameter, err)
}
}
@@ -297,7 +299,7 @@ func (e *airflowEngine) proxyRest(
doc := any(map[string]any{})
if len(raw) > 0 {
- if jerr := json.Unmarshal(raw, &doc); jerr != nil {
+ if jerr := json.Unmarshal(raw, &doc, jsonx.V1()); jerr != nil {
doc = string(raw)
}
}
diff --git a/services/mwaa/airflow_auth.go b/services/mwaa/airflow_auth.go
index 72457c870b..5e68c3fd42 100644
--- a/services/mwaa/airflow_auth.go
+++ b/services/mwaa/airflow_auth.go
@@ -4,13 +4,15 @@ import (
"context"
"crypto/rand"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
"net/url"
"regexp"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -115,7 +117,7 @@ func (e *airflowEngine) loginV3(ctx context.Context, t airflowTarget) ([]*http.C
}
func (e *airflowEngine) v3Token(ctx context.Context, t airflowTarget) (string, error) {
- body, _ := json.Marshal(map[string]string{"username": airflowAdminUser, "password": t.pass})
+ body, _ := json.Marshal(map[string]string{"username": airflowAdminUser, "password": t.pass}, jsonx.V1())
res, err := e.request(ctx, http.MethodPost, t.base+"/auth/token", body, http.Header{
headerContentType: {"application/json"},
@@ -128,7 +130,8 @@ func (e *airflowEngine) v3Token(ctx context.Context, t airflowTarget) (string, e
Token string `json:"access_token"`
}
- if res.status >= http.StatusMultipleChoices || json.Unmarshal(res.body, &out) != nil || out.Token == "" {
+ if res.status >= http.StatusMultipleChoices || json.Unmarshal(res.body, &out, jsonx.V1()) != nil ||
+ out.Token == "" {
return "", fmt.Errorf("%w: status %d", errAirflowLogin, res.status)
}
diff --git a/services/mwaa/airflow_auth_test.go b/services/mwaa/airflow_auth_test.go
index 5507348082..1b8700dc6d 100644
--- a/services/mwaa/airflow_auth_test.go
+++ b/services/mwaa/airflow_auth_test.go
@@ -1,7 +1,7 @@
package mwaa_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"os"
"path/filepath"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const startupBody = "export MY_VAR=from-startup\necho hello-startup\n"
@@ -196,7 +197,7 @@ func TestAirflowAuth_SSOLoginAndRest(t *testing.T) {
desc, err := rig.b.GetEnvironment(t.Context(), "auth")
require.NoError(t, err)
- raw, _ := json.Marshal(desc)
+ raw, _ := json.Marshal(desc, jsonx.V1())
assert.NotContains(t, string(raw), pass)
rec := doMWAARequest(t, rig.h, http.MethodPost, "/webtoken/auth", nil)
@@ -204,7 +205,7 @@ func TestAirflowAuth_SSOLoginAndRest(t *testing.T) {
var tok map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tok))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tok, jsonx.V1()))
sso := rawRequest(t, rig.h, http.MethodGet, "/aws_mwaa/aws-console-sso?login=true&token="+tok["WebToken"], "", nil)
require.Equal(t, http.StatusFound, sso.Code)
@@ -235,7 +236,7 @@ func TestAirflowAuth_BrowserTarget(t *testing.T) {
var tok map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tok))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tok, jsonx.V1()))
env, err := rig.b.GetEnvironment(t.Context(), "host")
require.NoError(t, err)
diff --git a/services/mwaa/airflow_engine_test.go b/services/mwaa/airflow_engine_test.go
index 4be1b516ed..2571834f15 100644
--- a/services/mwaa/airflow_engine_test.go
+++ b/services/mwaa/airflow_engine_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"maps"
"net/http"
@@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mwaa"
)
@@ -414,7 +415,7 @@ func TestAirflowEngine_CLIAndTokens(t *testing.T) {
var tok map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tok))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tok, jsonx.V1()))
assert.NotEmpty(t, tok["CliToken"])
tests := []struct {
@@ -442,7 +443,7 @@ func TestAirflowEngine_CLIAndTokens(t *testing.T) {
var resp map[string]string
- require.NoError(t, json.Unmarshal(out.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(out.Body.Bytes(), &resp, jsonx.V1()))
stdout, err := base64.StdEncoding.DecodeString(resp["stdout"])
require.NoError(t, err)
stderr, err := base64.StdEncoding.DecodeString(resp["stderr"])
@@ -466,7 +467,7 @@ func TestAirflowEngine_WebLoginAndRest(t *testing.T) {
var tok map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tok))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tok, jsonx.V1()))
env, err := rig.b.GetEnvironment(t.Context(), "web")
require.NoError(t, err)
diff --git a/services/mwaa/airflow_sync.go b/services/mwaa/airflow_sync.go
index a763753543..6828e1f177 100644
--- a/services/mwaa/airflow_sync.go
+++ b/services/mwaa/airflow_sync.go
@@ -4,7 +4,7 @@ import (
"archive/zip"
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -18,6 +18,7 @@ import (
awss3 "github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
s3backend "github.com/blackbirdworks/gopherstack/services/s3"
)
@@ -200,7 +201,7 @@ func (e *airflowEngine) stageStatic(ctx context.Context, dir string, in airflowL
_ = os.Chmod(filepath.Join(dir, "state"), airflowDirMode)
- passwords, _ := json.Marshal(map[string]string{airflowAdminUser: in.adminPass})
+ passwords, _ := json.Marshal(map[string]string{airflowAdminUser: in.adminPass}, jsonx.V1())
for name, data := range map[string][]byte{
"requirements.txt": reqs,
diff --git a/services/mwaa/environments_validation_test.go b/services/mwaa/environments_validation_test.go
index 31af2efa1c..e12fdde6ba 100644
--- a/services/mwaa/environments_validation_test.go
+++ b/services/mwaa/environments_validation_test.go
@@ -2,7 +2,7 @@ package mwaa_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mwaa"
)
@@ -441,7 +442,7 @@ func TestWorkerReplacementStrategy_AbsentFromCreate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "an unknown/invented field on Create must not fail validation")
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasField := resp["WorkerReplacementStrategy"]
assert.False(t, hasField, "CreateEnvironment response must never echo WorkerReplacementStrategy")
}
diff --git a/services/mwaa/export_test.go b/services/mwaa/export_test.go
index b4ab003473..551925057d 100644
--- a/services/mwaa/export_test.go
+++ b/services/mwaa/export_test.go
@@ -1,9 +1,11 @@
package mwaa
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// EnvironmentCount returns the total number of environments in the backend across
@@ -68,7 +70,7 @@ func NewInvokeRequest(method, path string) *invokeRestAPIRequest {
func ResponseJSON(t *testing.T, r *InvokeRestAPIResponse) string {
t.Helper()
- raw, err := json.Marshal(r.RestAPIResponse)
+ raw, err := json.Marshal(r.RestAPIResponse, jsonx.V1())
if err != nil {
t.Fatal(err)
}
diff --git a/services/mwaa/handler.go b/services/mwaa/handler.go
index 76d590e740..1cf8259daa 100644
--- a/services/mwaa/handler.go
+++ b/services/mwaa/handler.go
@@ -2,7 +2,7 @@ package mwaa
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -328,7 +329,7 @@ func decodeJSONBody(c *echo.Context, target any) bool {
return false
}
- if jsonErr := json.Unmarshal(body, target); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, target, jsonx.V1()); jsonErr != nil {
_ = writeErrorResponse(c, http.StatusBadRequest, "ValidationException", "invalid request body")
return false
diff --git a/services/mwaa/handler_cli_token_test.go b/services/mwaa/handler_cli_token_test.go
index 6812d4491f..0cf2aef90f 100644
--- a/services/mwaa/handler_cli_token_test.go
+++ b/services/mwaa/handler_cli_token_test.go
@@ -1,13 +1,16 @@
package mwaa_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCreateCliToken_HTTP_NonAvailable_Returns404(t *testing.T) {
@@ -58,7 +61,7 @@ func TestHandler_CliToken_HappyPath(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["CliToken"])
}
@@ -95,7 +98,7 @@ func TestHandler_CreateCliToken(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["CliToken"])
}
})
@@ -118,7 +121,7 @@ func TestCliToken_HTTP_ResponseStructure(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["CliToken"])
assert.NotEmpty(t, resp["WebServerHostname"])
@@ -156,7 +159,7 @@ func TestHTTP_TokensIncludeWebServerHostname(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp[tt.wantKey])
assert.NotEmpty(t, resp["WebServerHostname"])
assert.Contains(t, resp["WebServerHostname"], "amazonaws.com")
diff --git a/services/mwaa/handler_environments_test.go b/services/mwaa/handler_environments_test.go
index 9ffc310db6..c6a79201a3 100644
--- a/services/mwaa/handler_environments_test.go
+++ b/services/mwaa/handler_environments_test.go
@@ -2,7 +2,7 @@ package mwaa_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/url"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mwaa"
)
@@ -71,7 +72,7 @@ func TestHandler_CreateEnvironment(t *testing.T) {
if tt.wantArn {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Arn"])
}
})
@@ -122,7 +123,7 @@ func TestHandler_GetEnvironment(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Environment"])
}
})
@@ -168,7 +169,7 @@ func TestHandler_ListEnvironments(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
envs, ok := resp["Environments"].([]any)
require.True(t, ok)
@@ -317,7 +318,7 @@ func TestGetEnvironment_DerivedFields(t *testing.T) {
Environment mwaa.Environment `json:"Environment"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
env := resp.Environment
assert.Equal(t, int32(2), env.Schedulers)
@@ -393,7 +394,7 @@ func TestListEnvironments_Pagination(t *testing.T) {
Environments []string `json:"Environments"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantNames, resp.Environments)
assert.Equal(t, tt.wantNext, resp.NextToken)
})
@@ -481,7 +482,7 @@ func TestListEnvironments_HTTP_NoMaxResults_UsesDefault(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp["Environments"]
assert.True(t, ok, "response must have Environments key")
}
@@ -510,7 +511,7 @@ func TestListEnvironments_HTTP_NextToken_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
envs1 := resp1["Environments"].([]any)
assert.Len(t, envs1, 3)
@@ -525,7 +526,7 @@ func TestListEnvironments_HTTP_NextToken_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
envs2 := resp2["Environments"].([]any)
assert.Len(t, envs2, 2, "second page should have the remaining 2 environments")
@@ -555,7 +556,7 @@ func TestHTTP_GetEnvironment_DefaultsPresent(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
env := resp["Environment"].(map[string]any)
assert.InEpsilon(t, float64(10), env["MaxWorkers"], 0.001, "default MaxWorkers=10 should be visible via HTTP")
@@ -1014,7 +1015,7 @@ func TestLifecycle_HTTP_CreateResponseDoesNotExposeStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Arn"])
assert.Nil(t, resp["Status"], "create response must not expose Status")
}
@@ -1040,7 +1041,7 @@ func TestLifecycle_HTTP_GetEnvShowsCreatingThenAvailable(t *testing.T) {
Status string `json:"Status"`
} `json:"Environment"`
}
- require.NoError(t, json.Unmarshal(get1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(get1.Body.Bytes(), &resp1, jsonx.V1()))
assert.Equal(t, "CREATING", resp1.Environment.Status)
// Second GET: AVAILABLE
@@ -1052,7 +1053,7 @@ func TestLifecycle_HTTP_GetEnvShowsCreatingThenAvailable(t *testing.T) {
Status string `json:"Status"`
} `json:"Environment"`
}
- require.NoError(t, json.Unmarshal(get2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(get2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Equal(t, "AVAILABLE", resp2.Environment.Status)
}
@@ -1210,7 +1211,7 @@ func TestHTTP_FullCRUDLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
envARN := createResp["Arn"]
assert.NotEmpty(t, envARN)
@@ -1224,7 +1225,7 @@ func TestHTTP_FullCRUDLifecycle(t *testing.T) {
EnvironmentClass string `json:"EnvironmentClass"`
} `json:"Environment"`
}
- require.NoError(t, json.Unmarshal(get1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(get1.Body.Bytes(), &resp1, jsonx.V1()))
assert.Equal(t, "CREATING", resp1.Environment.Status)
assert.Equal(t, "2.9.2", resp1.Environment.AirflowVersion)
assert.Equal(t, "mw1.medium", resp1.Environment.EnvironmentClass)
@@ -1237,7 +1238,7 @@ func TestHTTP_FullCRUDLifecycle(t *testing.T) {
Status string `json:"Status"`
} `json:"Environment"`
}
- require.NoError(t, json.Unmarshal(get2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(get2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Equal(t, "AVAILABLE", resp2.Environment.Status)
// Update.
@@ -1255,7 +1256,7 @@ func TestHTTP_FullCRUDLifecycle(t *testing.T) {
EnvironmentClass string `json:"EnvironmentClass"`
} `json:"Environment"`
}
- require.NoError(t, json.Unmarshal(get3.Body.Bytes(), &resp3))
+ require.NoError(t, json.Unmarshal(get3.Body.Bytes(), &resp3, jsonx.V1()))
assert.Equal(t, "UPDATING", resp3.Environment.Status)
assert.Equal(t, "mw1.large", resp3.Environment.EnvironmentClass)
@@ -1304,7 +1305,7 @@ func TestHTTP_LoggingConfig_AllModules(t *testing.T) {
LoggingConfiguration *mwaa.LoggingConfiguration `json:"LoggingConfiguration"`
} `json:"Environment"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
require.NotNil(t, resp.Environment.LoggingConfiguration)
require.NotNil(t, resp.Environment.LoggingConfiguration.DagProcessingLogs)
assert.Equal(t, "INFO", resp.Environment.LoggingConfiguration.DagProcessingLogs.LogLevel)
@@ -1345,7 +1346,7 @@ func TestHTTP_LoggingConfig_CloudWatchLogGroupArn_NotEchoedFromRequest(t *testin
LoggingConfiguration *mwaa.LoggingConfiguration `json:"LoggingConfiguration"`
} `json:"Environment"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
require.NotNil(t, resp.Environment.LoggingConfiguration)
require.NotNil(t, resp.Environment.LoggingConfiguration.DagProcessingLogs)
assert.Equal(t, "INFO", resp.Environment.LoggingConfiguration.DagProcessingLogs.LogLevel)
diff --git a/services/mwaa/handler_metrics.go b/services/mwaa/handler_metrics.go
index cac93f0911..a369a1ce3e 100644
--- a/services/mwaa/handler_metrics.go
+++ b/services/mwaa/handler_metrics.go
@@ -1,7 +1,7 @@
package mwaa
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
@@ -9,6 +9,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handlePublishMetrics(c *echo.Context, name string) error {
@@ -19,7 +20,7 @@ func (h *Handler) handlePublishMetrics(c *echo.Context, name string) error {
var req publishMetricsRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
diff --git a/services/mwaa/handler_metrics_test.go b/services/mwaa/handler_metrics_test.go
index 8bada07a86..e1abccd3e0 100644
--- a/services/mwaa/handler_metrics_test.go
+++ b/services/mwaa/handler_metrics_test.go
@@ -2,7 +2,7 @@ package mwaa_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -11,6 +11,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_PublishMetrics(t *testing.T) {
@@ -120,7 +122,7 @@ func TestHandler_PublishMetrics_NotFound_ErrorType(t *testing.T) {
Message string `json:"message"`
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body.Type)
}
diff --git a/services/mwaa/handler_rest_api.go b/services/mwaa/handler_rest_api.go
index 8b0f149334..063e810c8d 100644
--- a/services/mwaa/handler_rest_api.go
+++ b/services/mwaa/handler_rest_api.go
@@ -1,7 +1,7 @@
package mwaa
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
@@ -9,6 +9,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleInvokeRestAPI(c *echo.Context, name string) error {
@@ -19,7 +20,7 @@ func (h *Handler) handleInvokeRestAPI(c *echo.Context, name string) error {
var req invokeRestAPIRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
diff --git a/services/mwaa/handler_rest_api_test.go b/services/mwaa/handler_rest_api_test.go
index d52b7aed8d..6eb983ff8c 100644
--- a/services/mwaa/handler_rest_api_test.go
+++ b/services/mwaa/handler_rest_api_test.go
@@ -1,7 +1,7 @@
package mwaa_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_InvokeRestApi(t *testing.T) {
@@ -126,7 +128,7 @@ func TestHandler_InvokeRestApi(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.InDelta(t, float64(200), resp["RestApiStatusCode"], 0)
}
})
diff --git a/services/mwaa/handler_tags.go b/services/mwaa/handler_tags.go
index 55208bd07f..b1c1d54d95 100644
--- a/services/mwaa/handler_tags.go
+++ b/services/mwaa/handler_tags.go
@@ -1,7 +1,7 @@
package mwaa
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
@@ -9,6 +9,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleListTagsForResource(c *echo.Context, resourceARN string) error {
@@ -42,7 +43,7 @@ func (h *Handler) handleTagResource(c *echo.Context, resourceARN string) error {
Tags map[string]string `json:"Tags"`
}
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
diff --git a/services/mwaa/handler_tags_test.go b/services/mwaa/handler_tags_test.go
index 6eae6720be..79c03fbeae 100644
--- a/services/mwaa/handler_tags_test.go
+++ b/services/mwaa/handler_tags_test.go
@@ -1,13 +1,15 @@
package mwaa_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_TagsFlow(t *testing.T) {
@@ -39,7 +41,7 @@ func TestHandler_TagsFlow(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
envARN := createResp["Arn"]
require.NotEmpty(t, envARN)
@@ -54,7 +56,7 @@ func TestHandler_TagsFlow(t *testing.T) {
assert.Equal(t, http.StatusOK, listTagRec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(listTagRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(listTagRec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags, ok := tagsResp["Tags"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "test", tags["env"])
@@ -104,7 +106,7 @@ func TestHandler_UntagResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
envARN := createResp["Arn"]
require.NotEmpty(t, envARN)
@@ -129,7 +131,7 @@ func TestHandler_UntagResource(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags := tagsResp["Tags"].(map[string]any)
assert.Len(t, tags, tt.wantTagsLen)
})
@@ -158,14 +160,14 @@ func TestListTagsForResource_HTTP_EmptyTagsShape(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
envARN := createResp["Arn"]
rec := doMWAARequest(t, h, http.MethodGet, "/tags/"+envARN, nil)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp["Tags"]
assert.True(t, ok, "response must have Tags key even when empty")
}
@@ -187,7 +189,7 @@ func TestTagLimit_HTTP_TagResource_Exceeds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
envARN := createResp["Arn"]
// Build 51 tags.
@@ -257,7 +259,7 @@ func TestTags_HTTP_CreateWithTagsRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
envARN := createResp["Arn"]
// ListTagsForResource returns the creation tags.
@@ -267,7 +269,7 @@ func TestTags_HTTP_CreateWithTagsRoundTrip(t *testing.T) {
var tagsResp struct {
Tags map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(tagRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(tagRec.Body.Bytes(), &tagsResp, jsonx.V1()))
assert.Equal(t, "airflow", tagsResp.Tags["service"])
assert.Equal(t, "production", tagsResp.Tags["tier"])
}
diff --git a/services/mwaa/handler_test.go b/services/mwaa/handler_test.go
index 931a1a0360..daf2fbff23 100644
--- a/services/mwaa/handler_test.go
+++ b/services/mwaa/handler_test.go
@@ -2,7 +2,7 @@ package mwaa_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mwaa"
)
@@ -37,7 +38,7 @@ func doMWAARequest(t *testing.T, h *mwaa.Handler, method, path string, body any)
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
diff --git a/services/mwaa/handler_web_login_token_test.go b/services/mwaa/handler_web_login_token_test.go
index df79cda172..48a6370ea6 100644
--- a/services/mwaa/handler_web_login_token_test.go
+++ b/services/mwaa/handler_web_login_token_test.go
@@ -1,13 +1,16 @@
package mwaa_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCreateWebLoginToken_HTTP_NonAvailable_Returns404(t *testing.T) {
@@ -57,7 +60,7 @@ func TestHandler_WebToken_HappyPath(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["WebToken"])
}
@@ -94,7 +97,7 @@ func TestHandler_CreateWebLoginToken(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["WebToken"])
}
})
@@ -117,7 +120,7 @@ func TestWebLoginToken_HTTP_ResponseStructure(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["WebToken"])
assert.NotEmpty(t, resp["WebServerHostname"])
diff --git a/services/mwaa/persistence.go b/services/mwaa/persistence.go
index 87e7877f6f..0487a5eaae 100644
--- a/services/mwaa/persistence.go
+++ b/services/mwaa/persistence.go
@@ -2,7 +2,7 @@ package mwaa
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -73,7 +73,7 @@ func regionalDTOKeyFn[V any](d *regionalDTO[V]) string { return regionKey(d.Regi
// decoding a snapshot from an incompatible (older or newer) build of this
// backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Metrics map[string]map[string][]MetricDatum `json:"metrics"`
AccountID string `json:"accountID"`
Region string `json:"region"`
@@ -173,7 +173,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreResourceTables rebuilds the environments store.Table from snap's
// per-table JSON, factored out of Restore to keep Restore's own cognitive
// complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
dtoReg, envDTOs := buildPersistenceDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
diff --git a/services/mwaa/tags_test.go b/services/mwaa/tags_test.go
index 6a92630053..880fd4b324 100644
--- a/services/mwaa/tags_test.go
+++ b/services/mwaa/tags_test.go
@@ -2,7 +2,7 @@ package mwaa_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mwaa"
)
@@ -333,7 +334,7 @@ func TestBackend_ARNIndex(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
envARN = resp["Arn"]
}
diff --git a/services/mwaa/web_login_identity_test.go b/services/mwaa/web_login_identity_test.go
index bf89dc60dd..447ef94342 100644
--- a/services/mwaa/web_login_identity_test.go
+++ b/services/mwaa/web_login_identity_test.go
@@ -2,7 +2,7 @@ package mwaa_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/mwaa"
)
@@ -52,7 +53,7 @@ func TestCreateWebLoginTokenIamIdentity(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.want, out["IamIdentity"])
})
}
diff --git a/services/neptune/engine_probe.go b/services/neptune/engine_probe.go
index ebc2449e50..bf006a76d8 100644
--- a/services/neptune/engine_probe.go
+++ b/services/neptune/engine_probe.go
@@ -7,7 +7,7 @@ import (
"crypto/sha1" //nolint:gosec // RFC 6455 mandates SHA-1 for the handshake accept key
"encoding/base64"
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -17,6 +17,8 @@ import (
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -98,7 +100,7 @@ func ProbeGremlin(ctx context.Context, addr string) error {
}
var resp gremlinResponse
- if err = json.Unmarshal(reply, &resp); err != nil {
+ if err = json.Unmarshal(reply, &resp, jsonx.V1()); err != nil {
return fmt.Errorf("decode gremlin response: %w", err)
}
@@ -114,7 +116,7 @@ func gremlinPayload() ([]byte, error) {
RequestID: uuid.NewString(),
Op: "eval",
Args: map[string]any{"gremlin": gremlinProbeQ, "language": "gremlin-groovy"},
- })
+ }, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("encode gremlin request: %w", err)
}
diff --git a/services/neptune/gremlin/frame.go b/services/neptune/gremlin/frame.go
index 5f789a44e5..4ede1d092a 100644
--- a/services/neptune/gremlin/frame.go
+++ b/services/neptune/gremlin/frame.go
@@ -2,11 +2,14 @@ package gremlin
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strings"
"github.com/google/uuid"
"github.com/gorilla/websocket"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const uuidBytes = 16
@@ -79,7 +82,8 @@ func recoverFrame(messageType int, data []byte) FrameRequest {
RequestID string `json:"requestId"`
}
- if json.NewDecoder(bytes.NewReader(body)).Decode(&raw) == nil && raw.RequestID != "" {
+ if json.UnmarshalDecode(jsontext.NewDecoder(bytes.NewReader(body)), &raw, jsonx.V1()) == nil &&
+ raw.RequestID != "" {
req.ID = raw.RequestID
}
diff --git a/services/neptune/gremlin/graphson.go b/services/neptune/gremlin/graphson.go
index f859ea5711..a2a981569c 100644
--- a/services/neptune/gremlin/graphson.go
+++ b/services/neptune/gremlin/graphson.go
@@ -2,9 +2,12 @@ package gremlin
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"math"
"strconv"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -190,10 +193,9 @@ func (w graphsonWriter) emptyMap() any {
func marshalNoEscape(v any) ([]byte, error) {
var buf bytes.Buffer
- enc := json.NewEncoder(&buf)
- enc.SetEscapeHTML(false)
+ enc := jsontext.NewEncoder(&buf, jsonx.V1(), jsontext.EscapeForHTML(false))
- if err := enc.Encode(v); err != nil {
+ if err := json.MarshalEncode(enc, v); err != nil {
return nil, err //nolint:wrapcheck // plain encode error
}
@@ -203,7 +205,7 @@ func marshalNoEscape(v any) ([]byte, error) {
// decodeGraphSON turns a decoded JSON request value (numbers as json.Number) into engine values.
func decodeGraphSON(v any) (any, error) {
switch t := v.(type) {
- case json.Number:
+ case jsonx.Number:
return numberFromJSON(t), nil
case []any:
return decodeGraphSONList(t)
@@ -219,7 +221,7 @@ func decodeGraphSON(v any) (any, error) {
return v, nil
}
-func numberFromJSON(n json.Number) any {
+func numberFromJSON(n jsonx.Number) any {
if i, err := n.Int64(); err == nil {
return i
}
@@ -295,7 +297,7 @@ func decodeTyped(typ string, val any) (any, error) {
func decodeNumber(val any) (any, error) {
switch t := val.(type) {
- case json.Number:
+ case jsonx.Number:
return numberFromJSON(t), nil
case string:
f, err := strconv.ParseFloat(t, 64)
diff --git a/services/neptune/gremlin/server.go b/services/neptune/gremlin/server.go
index fe995d2fd9..c872ef47a8 100644
--- a/services/neptune/gremlin/server.go
+++ b/services/neptune/gremlin/server.go
@@ -3,7 +3,8 @@ package gremlin
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -13,6 +14,7 @@ import (
"github.com/google/uuid"
"github.com/gorilla/websocket"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/neptune/opencypher"
)
@@ -151,10 +153,9 @@ func parseJSONRequest(body []byte, c codec) (*message, error) {
Processor string `json:"processor"`
}
- dec := json.NewDecoder(bytes.NewReader(body))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(body))
- if err := dec.Decode(&raw); err != nil {
+ if err := json.UnmarshalDecode(dec, &raw, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, invalidArg("Malformed message: %v", err)
}
@@ -422,7 +423,7 @@ func (s *Server) serveHTTP(w http.ResponseWriter, r *http.Request) {
func toJSONNumber(v any) any {
if f, ok := v.(float64); ok {
- return json.Number(formatFloat(f))
+ return jsonx.Number(formatFloat(f))
}
return v
@@ -437,7 +438,12 @@ func readHTTPRequest(w http.ResponseWriter, r *http.Request) (*httpRequest, erro
var req httpRequest
- if err := json.NewDecoder(r.Body).Decode(&req); err != nil && !errors.Is(err, io.EOF) {
+ if err := json.UnmarshalDecode(
+ jsontext.NewDecoder(r.Body),
+ &req,
+ jsonx.V1(),
+ ); err != nil &&
+ !errors.Is(err, io.EOF) {
return nil, invalidArg("Malformed request body: %v", err)
}
@@ -459,5 +465,8 @@ func writeHTTPError(w http.ResponseWriter, e *Error, id string) {
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
w.WriteHeader(status)
- _ = json.NewEncoder(w).Encode(map[string]string{jsonReqID: id, jsonCode: e.Code, "detailedMessage": e.Detail})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]string{jsonReqID: id, jsonCode: e.Code, "detailedMessage": e.Detail},
+ )
}
diff --git a/services/neptune/gremlin/server_test.go b/services/neptune/gremlin/server_test.go
index 70ac09fe0c..c7b5f289bf 100644
--- a/services/neptune/gremlin/server_test.go
+++ b/services/neptune/gremlin/server_test.go
@@ -2,7 +2,8 @@ package gremlin_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/neptune/gremlin"
"github.com/blackbirdworks/gopherstack/services/neptune/opencypher"
@@ -55,7 +57,7 @@ type wsResponse struct {
Code int `json:"code"`
} `json:"status"`
Result struct {
- Data json.RawMessage `json:"data"`
+ Data jsontext.Value `json:"data"`
} `json:"result"`
}
@@ -75,7 +77,7 @@ func call(t *testing.T, c *websocket.Conn, mt int, payload []byte) []wsResponse
require.NoError(t, err)
var r wsResponse
- require.NoError(t, json.Unmarshal(data, &r), string(data))
+ require.NoError(t, json.Unmarshal(data, &r, jsonx.V1()), string(data))
out = append(out, r)
@@ -89,7 +91,7 @@ const evalFrame = `{"requestId":"11111111-1111-1111-1111-111111111111","op":"eva
`"args":{"gremlin":%q,"language":"gremlin-groovy","aliases":{"g":"g"}%s}}`
func evalBody(script, extra string) string {
- b, _ := json.Marshal(script)
+ b, _ := json.Marshal(script, jsonx.V1())
return strings.Replace(strings.Replace(evalFrame, "%q", string(b), 1), "%s", extra, 1)
}
diff --git a/services/neptune/gremlin_ws_proxy_test.go b/services/neptune/gremlin_ws_proxy_test.go
index 7e43c9c833..0916fad851 100644
--- a/services/neptune/gremlin_ws_proxy_test.go
+++ b/services/neptune/gremlin_ws_proxy_test.go
@@ -5,7 +5,7 @@ import (
"crypto/sha256"
"encoding/binary"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net"
"net/http"
@@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iam"
)
@@ -136,7 +137,7 @@ func wsFreePort(t *testing.T) int {
func wsEval(script string) []byte {
b, _ := json.Marshal(map[string]any{
"requestId": wsTestReqID, "op": "eval", "processor": "", "args": map[string]any{"gremlin": script},
- })
+ }, jsonx.V1())
return b
}
@@ -290,7 +291,7 @@ func TestGremlinProxyFrameAuthorization(t *testing.T) {
return
}
- require.NoError(t, json.Unmarshal(raw, &out), string(raw))
+ require.NoError(t, json.Unmarshal(raw, &out, jsonx.V1()), string(raw))
assert.InDelta(t, tt.wantCode, out.Status.Code, 0)
assert.Contains(t, out.Status.Message, tt.wantMsg)
diff --git a/services/neptune/handler_test.go b/services/neptune/handler_test.go
index 6ca880a42b..b8aa07df99 100644
--- a/services/neptune/handler_test.go
+++ b/services/neptune/handler_test.go
@@ -1,7 +1,7 @@
package neptune_test
import (
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"io"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/neptune"
)
@@ -552,7 +553,7 @@ func TestPersistenceRoundTrip(t *testing.T) {
// Verify it's valid JSON
var raw map[string]any
- require.NoError(t, json.Unmarshal(data, &raw))
+ require.NoError(t, json.Unmarshal(data, &raw, jsonx.V1()))
restored := neptune.NewInMemoryBackend("000000000000", "us-east-1")
require.NoError(t, restored.Restore(t.Context(), data))
diff --git a/services/neptune/iam_auth.go b/services/neptune/iam_auth.go
index 519d2bddfa..bb562a9181 100644
--- a/services/neptune/iam_auth.go
+++ b/services/neptune/iam_auth.go
@@ -4,7 +4,7 @@ import (
"bytes"
"cmp"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"net"
@@ -15,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/bolt"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iam"
"github.com/blackbirdworks/gopherstack/services/neptune/gremlin"
"github.com/blackbirdworks/gopherstack/services/neptune/opencypher"
@@ -289,7 +290,7 @@ func (ca clusterAuth) authenticateBolt(auth map[string]any, now time.Time) (*iam
}
var tok map[string]string
- if err := json.Unmarshal([]byte(raw), &tok); err != nil {
+ if err := json.Unmarshal([]byte(raw), &tok, jsonx.V1()); err != nil {
return nil, errIAMBadBoltToken
}
@@ -435,7 +436,7 @@ func authorizeProxied(w http.ResponseWriter, r *http.Request) bool {
Gremlin string `json:"gremlin"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
script = cmp.Or(req.Gremlin, script)
}
diff --git a/services/neptune/iam_dataplane_test.go b/services/neptune/iam_dataplane_test.go
index 76c2aee13b..ab8212a7c9 100644
--- a/services/neptune/iam_dataplane_test.go
+++ b/services/neptune/iam_dataplane_test.go
@@ -3,7 +3,7 @@ package neptune_test
import (
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"net"
@@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/bolt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/iam"
"github.com/blackbirdworks/gopherstack/services/neptune"
@@ -233,7 +234,7 @@ func dpGremlinWS(t *testing.T, c neptune.DBCluster, accessKey, script string) (m
frame, err := json.Marshal(map[string]any{
"requestId": "7f1a8f4c-0b0e-4a8a-9d3a-1c2d3e4f5a6b", "op": "eval", "processor": "",
"args": map[string]any{"gremlin": script},
- })
+ }, jsonx.V1())
require.NoError(t, err)
require.NoError(t, conn.WriteMessage(websocket.TextMessage, frame))
@@ -241,7 +242,7 @@ func dpGremlinWS(t *testing.T, c neptune.DBCluster, accessKey, script string) (m
require.NoError(t, err)
var out map[string]any
- require.NoError(t, json.Unmarshal(raw, &out))
+ require.NoError(t, json.Unmarshal(raw, &out, jsonx.V1()))
return out, nil
}
@@ -294,7 +295,7 @@ func dpBoltToken(t *testing.T, c neptune.DBCluster, accessKey, secret, service s
raw, err := json.Marshal(map[string]string{
"Authorization": req.Header.Get("Authorization"), "HttpMethod": http.MethodGet,
"X-Amz-Date": req.Header.Get("X-Amz-Date"), "Host": req.Host,
- })
+ }, jsonx.V1())
require.NoError(t, err)
return string(raw)
diff --git a/services/neptune/opencypher/graph_test.go b/services/neptune/opencypher/graph_test.go
index 758876f8ba..594970b179 100644
--- a/services/neptune/opencypher/graph_test.go
+++ b/services/neptune/opencypher/graph_test.go
@@ -1,12 +1,13 @@
package opencypher_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/neptune/opencypher"
)
@@ -22,7 +23,7 @@ func run(t *testing.T, g *opencypher.Graph, q string, params map[string]any) (st
return "", err
}
- b, err := json.Marshal(res)
+ b, err := json.Marshal(res, jsonx.V1())
require.NoError(t, err)
return string(b), nil
@@ -149,7 +150,7 @@ func TestNodeAndRelationshipShapes(t *testing.T) {
var rows []map[string]map[string]any
- require.NoError(t, json.Unmarshal([]byte(got), &rows))
+ require.NoError(t, json.Unmarshal([]byte(got), &rows, jsonx.V1()))
require.Len(t, rows, 1)
a, r := rows[0]["a"], rows[0]["r"]
diff --git a/services/neptune/opencypher/persist.go b/services/neptune/opencypher/persist.go
index c9ddacf56a..fc0fea1a5e 100644
--- a/services/neptune/opencypher/persist.go
+++ b/services/neptune/opencypher/persist.go
@@ -3,11 +3,14 @@ package opencypher
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"math"
"sort"
"strconv"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -63,7 +66,7 @@ func (g *Graph) Export(ctx context.Context) ([]byte, error) {
})
}
- b, err := json.Marshal(d)
+ b, err := json.Marshal(d, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("encode graph: %w", err)
}
@@ -105,10 +108,9 @@ func (g *Graph) Import(ctx context.Context, data []byte) error {
}
func decodeUseNumber(data []byte, v any) error {
- dec := json.NewDecoder(bytes.NewReader(data))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(data))
- return dec.Decode(v)
+ return json.UnmarshalDecode(dec, v, jsonx.V1(), jsonx.UseNumber())
}
func encodeProps(p map[string]any) map[string]any {
@@ -149,7 +151,7 @@ func decodeProps(p map[string]any) map[string]any {
func decodeValue(v any) any {
switch t := v.(type) {
- case json.Number:
+ case jsonx.Number:
if i, err := t.Int64(); err == nil {
return i
}
diff --git a/services/neptune/opencypher/value.go b/services/neptune/opencypher/value.go
index 39e5ddc023..b5f1a3a1b3 100644
--- a/services/neptune/opencypher/value.go
+++ b/services/neptune/opencypher/value.go
@@ -3,12 +3,15 @@ package opencypher
import (
"bytes"
"cmp"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"math"
"sort"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Result is the table produced by RETURN; a query with no RETURN has no columns.
@@ -36,8 +39,8 @@ func (r *Result) MarshalJSON() ([]byte, error) {
buf.WriteByte(',')
}
- k, _ := json.Marshal(col)
- v, err := json.Marshal(jsonValue(row[j]))
+ k, _ := json.Marshal(col, jsonx.V1())
+ v, err := json.Marshal(jsonValue(row[j]), jsonx.V1())
if err != nil {
return nil, fmt.Errorf("encode %s: %w", col, err)
}
@@ -112,11 +115,10 @@ func ParseParameters(raw string) (map[string]any, error) {
return map[string]any{}, nil
}
- dec := json.NewDecoder(strings.NewReader(raw))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(strings.NewReader(raw))
var m map[string]any
- if err := dec.Decode(&m); err != nil {
+ if err := json.UnmarshalDecode(dec, &m, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, &Error{Code: CodeInvalidParameter, Detail: "parameters must be a JSON object: " + err.Error()}
}
@@ -129,7 +131,7 @@ func ParseParameters(raw string) (map[string]any, error) {
func normalize(v any) any {
switch t := v.(type) {
- case json.Number:
+ case jsonx.Number:
if i, err := t.Int64(); err == nil {
return i
}
diff --git a/services/neptune/opencypher_front.go b/services/neptune/opencypher_front.go
index 1a864f5095..ce60f4072f 100644
--- a/services/neptune/opencypher_front.go
+++ b/services/neptune/opencypher_front.go
@@ -2,7 +2,8 @@ package neptune
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -20,6 +21,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/bolt"
"github.com/blackbirdworks/gopherstack/pkgs/container"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/sgfilter"
"github.com/blackbirdworks/gopherstack/services/neptune/gremlin"
"github.com/blackbirdworks/gopherstack/services/neptune/opencypher"
@@ -169,7 +171,7 @@ type cypherCall struct {
func writeCypherJSON(w http.ResponseWriter, status int, body any) {
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
w.WriteHeader(status)
- _ = json.NewEncoder(w).Encode(body)
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), body)
}
func writeCypherError(w http.ResponseWriter, status int, code, detail string) {
@@ -207,7 +209,7 @@ var errMethodNotAllowed = errors.New("method not allowed")
func readCypherJSON(body io.Reader) (cypherCall, error) {
var req cypherRequest
- if err := json.NewDecoder(body).Decode(&req); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(body), &req, jsonx.V1()); err != nil {
return cypherCall{}, fmt.Errorf("decode request: %w", err)
}
@@ -225,7 +227,7 @@ func readCypherJSON(body io.Reader) (cypherCall, error) {
case string:
call.params = p
default:
- raw, _ := json.Marshal(p)
+ raw, _ := json.Marshal(p, jsonx.V1())
call.params = string(raw)
}
@@ -291,7 +293,7 @@ func (f *cypherFront) runQuery(ctx context.Context, w http.ResponseWriter, query
return
}
- writeCypherJSON(w, http.StatusOK, map[string]json.RawMessage{"results": rowsOrEmpty(rows)})
+ writeCypherJSON(w, http.StatusOK, map[string]jsontext.Value{"results": rowsOrEmpty(rows)})
}
func (f *cypherFront) serveExplain(ctx context.Context, w http.ResponseWriter, call cypherCall, params map[string]any) {
@@ -318,9 +320,9 @@ func (f *cypherFront) serveExplain(ctx context.Context, w http.ResponseWriter, c
_, _ = io.WriteString(w, plan)
}
-func rowsOrEmpty(rows []byte) json.RawMessage {
+func rowsOrEmpty(rows []byte) jsontext.Value {
if len(rows) == 0 {
- return json.RawMessage("[]")
+ return jsontext.Value("[]")
}
return rows
diff --git a/services/neptune/opencypher_front_test.go b/services/neptune/opencypher_front_test.go
index 2574392dcc..3792a13275 100644
--- a/services/neptune/opencypher_front_test.go
+++ b/services/neptune/opencypher_front_test.go
@@ -2,7 +2,7 @@ package neptune_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net"
"net/http"
@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/neptune"
)
@@ -72,7 +73,7 @@ func cypherDo(t *testing.T, method, target, contentType, body string) (int, map[
require.NoError(t, err)
var out map[string]any
- require.NoError(t, json.Unmarshal(raw, &out), string(raw))
+ require.NoError(t, json.Unmarshal(raw, &out, jsonx.V1()), string(raw))
return resp.StatusCode, out
}
diff --git a/services/neptune/opencypher_queries_internal_test.go b/services/neptune/opencypher_queries_internal_test.go
index b9bc663d6b..1e3d6f9cd4 100644
--- a/services/neptune/opencypher_queries_internal_test.go
+++ b/services/neptune/opencypher_queries_internal_test.go
@@ -2,7 +2,7 @@ package neptune
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/neptune/opencypher"
)
@@ -22,7 +23,7 @@ func statusDo(t *testing.T, f *cypherFront, method, target string) (int, map[str
f.serveQueryStatus(rec, req)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return rec.Code, out
}
diff --git a/services/neptune/persistence.go b/services/neptune/persistence.go
index 7b5c12c5c4..fc4c2f4a2f 100644
--- a/services/neptune/persistence.go
+++ b/services/neptune/persistence.go
@@ -2,7 +2,7 @@ package neptune
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -54,14 +54,14 @@ func regionalDTOKeyFn[V any](d *regionalDTO[V]) string { return regionKey(d.Regi
// from an incompatible (older or newer) build of this backend as though it
// were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ClusterRoles map[string]map[string][]string `json:"clusterRoles"`
Tags map[string]map[string][]Tag `json:"tags"`
ParameterOverrides map[string]map[string]ParameterValue `json:"parameterOverrides"`
ClusterParameterOverrides map[string]map[string]ParameterValue `json:"clusterParameterOverrides"`
PendingMaintenanceActions map[string]map[string]PendingMaintenanceAction `json:"pendingMaintenanceActions"`
EventsLog map[string][]Event `json:"eventsLog"`
- OpenCypherGraphs map[string]json.RawMessage `json:"openCypherGraphs,omitempty"`
+ OpenCypherGraphs map[string]jsontext.Value `json:"openCypherGraphs,omitempty"`
AccountID string `json:"accountID"`
Region string `json:"region"`
Version int `json:"version"`
@@ -289,7 +289,7 @@ func unwrapRegionalDTOs[V any](dtos *store.Table[regionalDTO[V]], setRegion func
// restoreResourceTables rebuilds every store.Table on b from snap's
// per-table JSON, factored out of Restore to keep Restore's own cognitive
// complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
dtoReg, clusterDTOs, instanceDTOs, subnetGroupDTOs, clusterParameterGroupDTOs,
clusterSnapshotDTOs, parameterGroupDTOs, clusterEndpointDTOs, eventSubscriptionDTOs,
globalClusterDTOs := buildPersistenceDTORegistry()
@@ -342,12 +342,12 @@ func (b *InMemoryBackend) graphLocked(key string) *opencypher.Graph {
return g
}
-func (b *InMemoryBackend) exportGraphsLocked(ctx context.Context) map[string]json.RawMessage {
+func (b *InMemoryBackend) exportGraphsLocked(ctx context.Context) map[string]jsontext.Value {
if len(b.graphs) == 0 {
return nil
}
- out := make(map[string]json.RawMessage, len(b.graphs))
+ out := make(map[string]jsontext.Value, len(b.graphs))
for key, g := range b.graphs {
raw, err := g.Export(ctx)
@@ -363,7 +363,7 @@ func (b *InMemoryBackend) exportGraphsLocked(ctx context.Context) map[string]jso
return out
}
-func (b *InMemoryBackend) importGraphsLocked(ctx context.Context, dumps map[string]json.RawMessage) {
+func (b *InMemoryBackend) importGraphsLocked(ctx context.Context, dumps map[string]jsontext.Value) {
b.graphs = make(map[string]*opencypher.Graph, len(dumps))
for key, raw := range dumps {
diff --git a/services/networkfirewall/handler.go b/services/networkfirewall/handler.go
index 721cad7a10..418abf4e0d 100644
--- a/services/networkfirewall/handler.go
+++ b/services/networkfirewall/handler.go
@@ -2,8 +2,7 @@ package networkfirewall
import (
"context"
- "encoding/json"
- "errors"
+ "encoding/json/v2"
"fmt"
"net/http"
"slices"
@@ -12,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -85,7 +85,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var data map[string]any
- if json.Unmarshal(body, &data) != nil {
+ if json.Unmarshal(body, &data, jsonx.V1()) != nil {
return ""
}
@@ -127,12 +127,12 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- _, isSyntax := errors.AsType[*json.SyntaxError](err)
- _, isType := errors.AsType[*json.UnmarshalTypeError](err)
+ isSyntax := jsonx.IsSyntaxError(err)
+ isType := jsonx.IsTypeError(err)
if isSyntax || isType {
err = invalid("The request body is not valid: %v", err)
diff --git a/services/networkfirewall/ordering_test.go b/services/networkfirewall/ordering_test.go
index eaeab0fdc3..90dfa2acd7 100644
--- a/services/networkfirewall/ordering_test.go
+++ b/services/networkfirewall/ordering_test.go
@@ -1,7 +1,8 @@
package networkfirewall_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"slices"
"testing"
@@ -10,23 +11,25 @@ import (
"github.com/aws/aws-sdk-go-v2/service/networkfirewall/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// retime rewrites timeField on every record that has idField, giving the i-th id (sorted) times[i].
func retime(t *testing.T, snap []byte, timeField string, times []string) []byte {
t.Helper()
- var root map[string]json.RawMessage
+ var root map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(snap, &root))
+ require.NoError(t, json.Unmarshal(snap, &root, jsonx.V1()))
- var tables map[string]json.RawMessage
+ var tables map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(root["tables"], &tables))
+ require.NoError(t, json.Unmarshal(root["tables"], &tables, jsonx.V1()))
for name, raw := range tables {
var rows []map[string]any
- if json.Unmarshal(raw, &rows) != nil {
+ if json.Unmarshal(raw, &rows, jsonx.V1()) != nil {
continue
}
@@ -50,18 +53,18 @@ func retime(t *testing.T, snap []byte, timeField string, times []string) []byte
}
}
- b, err := json.Marshal(rows)
+ b, err := json.Marshal(rows, jsonx.V1())
require.NoError(t, err)
tables[name] = b
}
- tb, err := json.Marshal(tables)
+ tb, err := json.Marshal(tables, jsonx.V1())
require.NoError(t, err)
root["tables"] = tb
- out, err := json.Marshal(root)
+ out, err := json.Marshal(root, jsonx.V1())
require.NoError(t, err)
return out
diff --git a/services/networkfirewall/persistence.go b/services/networkfirewall/persistence.go
index 727b6973df..2cf39f7ea0 100644
--- a/services/networkfirewall/persistence.go
+++ b/services/networkfirewall/persistence.go
@@ -2,7 +2,7 @@ package networkfirewall
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -13,8 +13,8 @@ const networkFirewallSnapshotVersion = 1
// backendSnapshot is the on-disk shape: one JSON array per registered table.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serializes backend state to JSON.
diff --git a/services/networkfirewall/resource_policy.go b/services/networkfirewall/resource_policy.go
index 73ae4c26a5..551b14ec16 100644
--- a/services/networkfirewall/resource_policy.go
+++ b/services/networkfirewall/resource_policy.go
@@ -2,7 +2,11 @@ package networkfirewall
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "encoding/json/jsontext"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// PutResourcePolicyInput is the PutResourcePolicy request.
@@ -29,10 +33,10 @@ type DeleteResourcePolicyInput = DescribeResourcePolicyInput
func validatePolicyDocument(doc string) error {
var parsed struct {
- Statement json.RawMessage `json:"Statement"`
+ Statement jsontext.Value `json:"Statement"`
}
- if err := json.Unmarshal([]byte(doc), &parsed); err != nil || len(parsed.Statement) == 0 {
+ if err := json.Unmarshal([]byte(doc), &parsed, jsonx.V1()); err != nil || len(parsed.Statement) == 0 {
return invalidResourcePolicy("The policy must be a JSON document with a Statement")
}
diff --git a/services/networkmanager/corenetworkpolicydiff.go b/services/networkmanager/corenetworkpolicydiff.go
index e2ddc6a36f..65e364880b 100644
--- a/services/networkmanager/corenetworkpolicydiff.go
+++ b/services/networkmanager/corenetworkpolicydiff.go
@@ -1,8 +1,11 @@
package networkmanager
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"reflect"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// This file implements the real ADD/MODIFY/REMOVE structural diff
@@ -28,17 +31,17 @@ import (
type namedSection struct {
key string
- raw json.RawMessage
+ raw jsontext.Value
}
// policyDocSections is the subset of the real Cloud WAN policy-document
// schema's top-level keys this diff engine reads.
type policyDocSections struct {
- CoreNetworkConfiguration json.RawMessage `json:"core-network-configuration"`
- Segments []json.RawMessage `json:"segments"`
- NetworkFunctionGroups []json.RawMessage `json:"network-function-groups"`
- SegmentActions []json.RawMessage `json:"segment-actions"`
- AttachmentPolicies []json.RawMessage `json:"attachment-policies"`
+ CoreNetworkConfiguration jsontext.Value `json:"core-network-configuration"`
+ Segments []jsontext.Value `json:"segments"`
+ NetworkFunctionGroups []jsontext.Value `json:"network-function-groups"`
+ SegmentActions []jsontext.Value `json:"segment-actions"`
+ AttachmentPolicies []jsontext.Value `json:"attachment-policies"`
}
func parsePolicyDocSections(doc string) policyDocSections {
@@ -51,22 +54,22 @@ func parsePolicyDocSections(doc string) policyDocSections {
// CreateCoreNetwork) -- an error here means an empty/absent document,
// which decodes to the zero value, itself a valid "nothing to diff"
// input to diffNamedSections.
- _ = json.Unmarshal([]byte(doc), &s)
+ _ = json.Unmarshal([]byte(doc), &s, jsonx.V1())
return s
}
-func namedByKey(items []json.RawMessage, keyField string) map[string]namedSection {
+func namedByKey(items []jsontext.Value, keyField string) map[string]namedSection {
out := make(map[string]namedSection, len(items))
for _, item := range items {
- var fields map[string]json.RawMessage
- if err := json.Unmarshal(item, &fields); err != nil {
+ var fields map[string]jsontext.Value
+ if err := json.Unmarshal(item, &fields, jsonx.V1()); err != nil {
continue
}
var key string
- if err := json.Unmarshal(fields[keyField], &key); err != nil {
+ if err := json.Unmarshal(fields[keyField], &key, jsonx.V1()); err != nil {
continue
}
@@ -82,7 +85,7 @@ func namedByKey(items []json.RawMessage, keyField string) map[string]namedSectio
// key present in either oldItems or newItems, keyed by keyField (e.g.
// "name", "rule-number").
func diffSectionEntries(
- oldItems, newItems []json.RawMessage,
+ oldItems, newItems []jsontext.Value,
keyField, changeType, pathPrefix string,
values func(key string) *CoreNetworkChangeValues,
) []CoreNetworkChange {
@@ -139,9 +142,9 @@ func withRawJSON(v *CoreNetworkChangeValues, raw string) *CoreNetworkChangeValue
return v
}
-func jsonEqual(a, b json.RawMessage) bool {
+func jsonEqual(a, b jsontext.Value) bool {
var av, bv any
- if json.Unmarshal(a, &av) != nil || json.Unmarshal(b, &bv) != nil {
+ if json.Unmarshal(a, &av, jsonx.V1()) != nil || json.Unmarshal(b, &bv, jsonx.V1()) != nil {
return string(a) == string(b)
}
@@ -159,7 +162,7 @@ const (
// diffCoreNetworkConfiguration compares the single "core-network-configuration"
// object, which has no natural key -- a single MODIFY/ADD/REMOVE covers it.
-func diffCoreNetworkConfiguration(oldRaw, newRaw json.RawMessage) []CoreNetworkChange {
+func diffCoreNetworkConfiguration(oldRaw, newRaw jsontext.Value) []CoreNetworkChange {
base := CoreNetworkChange{
Identifier: coreNetworkConfigurationIdentifier, IdentifierPath: coreNetworkConfigurationChangeType,
Type: coreNetworkConfigurationChangeType,
diff --git a/services/networkmanager/corenetworks.go b/services/networkmanager/corenetworks.go
index f9355f8fda..3bd265fad4 100644
--- a/services/networkmanager/corenetworks.go
+++ b/services/networkmanager/corenetworks.go
@@ -1,10 +1,11 @@
package networkmanager
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"slices"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -42,7 +43,7 @@ func (b *InMemoryBackend) CreateCoreNetwork(
return nil, validationError(fmt.Sprintf("%s %s not found", resourceGlobalNetwork, globalNetworkID))
}
- if policyDocument != "" && !json.Valid([]byte(policyDocument)) {
+ if policyDocument != "" && !jsontext.Value([]byte(policyDocument)).IsValid(jsonx.V1()) {
return nil, coreNetworkPolicyError("PolicyDocument is not valid JSON", []CoreNetworkPolicyError{
{ErrorCode: "InvalidPolicyDocument", Message: "PolicyDocument must be valid JSON", Path: "/"},
})
@@ -170,7 +171,7 @@ func (b *InMemoryBackend) PutCoreNetworkPolicy(
return nil, nil, notFoundError(resourceCoreNetwork, coreNetworkID)
}
- if !json.Valid([]byte(policyDocument)) {
+ if !jsontext.Value([]byte(policyDocument)).IsValid(jsonx.V1()) {
return nil, nil, coreNetworkPolicyError("PolicyDocument is not valid JSON", []CoreNetworkPolicyError{
{ErrorCode: "InvalidPolicyDocument", Message: "PolicyDocument must be valid JSON", Path: "/"},
})
diff --git a/services/networkmanager/handler.go b/services/networkmanager/handler.go
index 40d7a603c5..6ab7053341 100644
--- a/services/networkmanager/handler.go
+++ b/services/networkmanager/handler.go
@@ -2,7 +2,7 @@ package networkmanager
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -245,7 +246,7 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
addErrorFields(body, apiErr)
}
- payload, _ := json.Marshal(body)
+ payload, _ := json.Marshal(body, jsonx.V1())
c.Response().Header().Set("X-Amzn-Errortype", errType)
@@ -378,7 +379,7 @@ func decodeJSONBody(body []byte, v any) error {
return nil
}
- if err := json.Unmarshal(body, v); err != nil {
+ if err := json.Unmarshal(body, v, jsonx.V1()); err != nil {
return validationError("malformed request body: " + err.Error())
}
@@ -388,7 +389,7 @@ func decodeJSONBody(body []byte, v any) error {
// marshalResponse marshals v, wrapping any error as an internal failure
// (marshaling our own well-typed wire structs should never fail).
func marshalResponse(v any) ([]byte, error) {
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return nil, internalServerError("networkmanager: marshal response: " + err.Error())
}
diff --git a/services/networkmanager/introspection.go b/services/networkmanager/introspection.go
index ffceea9890..f0d2c763c7 100644
--- a/services/networkmanager/introspection.go
+++ b/services/networkmanager/introspection.go
@@ -1,9 +1,10 @@
package networkmanager
import (
- "encoding/json"
+ "encoding/json/v2"
"sort"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -201,7 +202,7 @@ func (b *InMemoryBackend) coreNetworkScopedResourceItems(
}
func mustJSON(v any) string {
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return "{}"
}
diff --git a/services/networkmanager/persistence.go b/services/networkmanager/persistence.go
index cdb47f4c19..a996c5daa4 100644
--- a/services/networkmanager/persistence.go
+++ b/services/networkmanager/persistence.go
@@ -2,7 +2,7 @@ package networkmanager
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -20,11 +20,11 @@ const networkManagerSnapshotVersion = 1
// backendSnapshot is the top-level on-disk shape for the Network Manager
// backend.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- OrgStatus *organizationStatus `json:"orgStatus"`
- AccountID string `json:"accountId"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ OrgStatus *organizationStatus `json:"orgStatus"`
+ AccountID string `json:"accountId"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/networkmanager/resourcepolicy.go b/services/networkmanager/resourcepolicy.go
index b784652e07..d97230c78e 100644
--- a/services/networkmanager/resourcepolicy.go
+++ b/services/networkmanager/resourcepolicy.go
@@ -1,6 +1,10 @@
package networkmanager
-import "encoding/json"
+import (
+ "encoding/json/jsontext"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
+)
// This file implements PARITY.md family W: a resource-based IAM-style JSON
// policy document attached to a NetworkManager resource ARN (used for
@@ -16,7 +20,7 @@ func (b *InMemoryBackend) PutResourcePolicy(resourceArn, policyDocument string)
b.mu.Lock("PutResourcePolicy")
defer b.mu.Unlock()
- if !json.Valid([]byte(policyDocument)) {
+ if !jsontext.Value([]byte(policyDocument)).IsValid(jsonx.V1()) {
return validationError("PolicyDocument is not valid JSON")
}
diff --git a/services/networkmanager/validate.go b/services/networkmanager/validate.go
index 0f640f3cb0..d52a4ebd95 100644
--- a/services/networkmanager/validate.go
+++ b/services/networkmanager/validate.go
@@ -1,13 +1,14 @@
package networkmanager
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/url"
"strconv"
"unicode/utf8"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -93,7 +94,7 @@ func decodeObject(body []byte) (map[string]any, bool) {
}
var m map[string]any
- if err := json.Unmarshal(body, &m); err != nil {
+ if err := json.Unmarshal(body, &m, jsonx.V1()); err != nil {
return nil, false
}
diff --git a/services/networkmonitor/handler.go b/services/networkmonitor/handler.go
index b1fa05d524..5d9b874387 100644
--- a/services/networkmonitor/handler.go
+++ b/services/networkmonitor/handler.go
@@ -2,7 +2,7 @@ package networkmonitor
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -293,9 +294,6 @@ func (h *Handler) dispatch(
}
func (h *Handler) handleError(c *echo.Context, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
var code string
var status int
@@ -313,8 +311,8 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
status = http.StatusBadRequest
code = "ValidationException"
case errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr),
- errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err),
+ jsonx.IsTypeError(err):
status = http.StatusBadRequest
code = "ValidationException"
default:
@@ -402,7 +400,7 @@ func extractTagResourceARN(path string) string {
func (h *Handler) handleCreateMonitor(ctx context.Context, body []byte) ([]byte, error) {
var req createMonitorRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -430,7 +428,7 @@ func (h *Handler) handleCreateMonitor(ctx context.Context, body []byte) ([]byte,
Tags: m.Tags,
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
func (h *Handler) handleDeleteMonitor(ctx context.Context, path string) ([]byte, error) {
@@ -473,7 +471,7 @@ func (h *Handler) handleGetMonitor(ctx context.Context, path string) ([]byte, er
ModifiedAt: epochSeconds(m.ModifiedAt),
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
func (h *Handler) handleUpdateMonitor(
@@ -487,7 +485,7 @@ func (h *Handler) handleUpdateMonitor(
}
var req updateMonitorRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -505,7 +503,7 @@ func (h *Handler) handleUpdateMonitor(
Tags: m.Tags,
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
func (h *Handler) handleListMonitors(ctx context.Context, query url.Values) ([]byte, error) {
@@ -529,7 +527,7 @@ func (h *Handler) handleListMonitors(ctx context.Context, query url.Values) ([]b
NextToken: outToken,
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
func (h *Handler) handleCreateProbe(ctx context.Context, path string, body []byte) ([]byte, error) {
@@ -539,7 +537,7 @@ func (h *Handler) handleCreateProbe(ctx context.Context, path string, body []byt
}
var req createProbeRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -552,7 +550,7 @@ func (h *Handler) handleCreateProbe(ctx context.Context, path string, body []byt
return nil, err
}
- return json.Marshal(toProbeWire(probe))
+ return json.Marshal(toProbeWire(probe), jsonx.V1())
}
func (h *Handler) handleDeleteProbe(ctx context.Context, path string) ([]byte, error) {
@@ -591,7 +589,7 @@ func (h *Handler) handleGetProbe(ctx context.Context, path string) ([]byte, erro
return nil, err
}
- return json.Marshal(toProbeWire(probe))
+ return json.Marshal(toProbeWire(probe), jsonx.V1())
}
func (h *Handler) handleUpdateProbe(ctx context.Context, path string, body []byte) ([]byte, error) {
@@ -607,7 +605,7 @@ func (h *Handler) handleUpdateProbe(ctx context.Context, path string, body []byt
}
var req updateProbeRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -616,7 +614,7 @@ func (h *Handler) handleUpdateProbe(ctx context.Context, path string, body []byt
return nil, err
}
- return json.Marshal(toProbeWire(probe))
+ return json.Marshal(toProbeWire(probe), jsonx.V1())
}
func (h *Handler) handleListTagsForResource(ctx context.Context, path string) ([]byte, error) {
@@ -634,7 +632,7 @@ func (h *Handler) handleListTagsForResource(ctx context.Context, path string) ([
tags = map[string]string{}
}
- return json.Marshal(listTagsForResourceResponse{Tags: tags})
+ return json.Marshal(listTagsForResourceResponse{Tags: tags}, jsonx.V1())
}
func (h *Handler) handleTagResource(ctx context.Context, path string, body []byte) ([]byte, error) {
@@ -644,7 +642,7 @@ func (h *Handler) handleTagResource(ctx context.Context, path string, body []byt
}
var req tagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/networkmonitor/handler_monitors_test.go b/services/networkmonitor/handler_monitors_test.go
index 1414a279a4..61ba10624f 100644
--- a/services/networkmonitor/handler_monitors_test.go
+++ b/services/networkmonitor/handler_monitors_test.go
@@ -1,13 +1,16 @@
package networkmonitor_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandlerCreateMonitor(t *testing.T) {
@@ -185,7 +188,7 @@ func TestHandlerListMonitors(t *testing.T) {
}
var resp map[string]any
- if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
+ if err := json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -271,9 +274,9 @@ func TestHandlerGetMonitorProbeTimestampsEpochFloat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out struct {
- Probes []map[string]json.RawMessage `json:"probes"`
+ Probes []map[string]jsontext.Value `json:"probes"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Probes, 1, "expected one probe in GetMonitor response")
createdAtRaw, hasCreatedAt := out.Probes[0]["createdAt"]
@@ -281,7 +284,7 @@ func TestHandlerGetMonitorProbeTimestampsEpochFloat(t *testing.T) {
if hasCreatedAt {
var asFloat float64
- require.NoError(t, json.Unmarshal(createdAtRaw, &asFloat),
+ require.NoError(t, json.Unmarshal(createdAtRaw, &asFloat, jsonx.V1()),
"probe createdAt in GetMonitor must be JSON number, got: %s", string(createdAtRaw))
assert.Greater(t, asFloat, float64(0))
}
@@ -310,15 +313,15 @@ func TestHandlerGetMonitorTimestampsEpochFloat(t *testing.T) {
rec := doNMRequest(t, h, http.MethodGet, "/monitors/epoch-mon", nil)
require.Equal(t, http.StatusOK, rec.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
fieldRaw, ok := raw[tt.field]
assert.True(t, ok, "%s must be present", tt.field)
if ok {
var asFloat float64
- require.NoError(t, json.Unmarshal(fieldRaw, &asFloat),
+ require.NoError(t, json.Unmarshal(fieldRaw, &asFloat, jsonx.V1()),
"%s must be JSON number, got: %s", tt.field, string(fieldRaw))
assert.Greater(t, asFloat, float64(0))
}
@@ -382,7 +385,7 @@ func TestHandlerListMonitorsPagination(t *testing.T) {
NextToken string `json:"nextToken"`
Monitors []any `json:"monitors"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
pageCounts = append(pageCounts, len(out.Monitors))
token = out.NextToken
@@ -412,7 +415,7 @@ func TestHandlerListMonitorsTokenPastEnd(t *testing.T) {
NextToken string `json:"nextToken"`
Monitors []any `json:"monitors"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.Monitors, "token past last item must return empty list, not first page")
assert.Empty(t, out.NextToken)
diff --git a/services/networkmonitor/handler_probes_test.go b/services/networkmonitor/handler_probes_test.go
index 31d3610f5b..5d857395b4 100644
--- a/services/networkmonitor/handler_probes_test.go
+++ b/services/networkmonitor/handler_probes_test.go
@@ -1,12 +1,15 @@
package networkmonitor_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandlerProbeLifecycle(t *testing.T) {
@@ -38,7 +41,7 @@ func TestHandlerProbeLifecycle(t *testing.T) {
}
var probeResp map[string]any
- if err := json.Unmarshal(rr.Body.Bytes(), &probeResp); err != nil {
+ if err := json.Unmarshal(rr.Body.Bytes(), &probeResp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal probe: %v", err)
}
@@ -96,15 +99,15 @@ func TestHandlerProbeTimestampsEpochFloat(t *testing.T) {
rec := doNMRequest(t, h, http.MethodGet, "/monitors/ts-mon/probes/"+probeID, nil)
require.Equal(t, http.StatusOK, rec.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
createdAtRaw, hasCreatedAt := raw["createdAt"]
assert.True(t, hasCreatedAt, "createdAt must be present in GetProbe response")
if hasCreatedAt {
var asFloat float64
- require.NoError(t, json.Unmarshal(createdAtRaw, &asFloat),
+ require.NoError(t, json.Unmarshal(createdAtRaw, &asFloat, jsonx.V1()),
"createdAt must be a JSON number (epoch seconds), got: %s", string(createdAtRaw))
assert.Greater(t, asFloat, float64(0), "createdAt epoch value must be positive")
}
@@ -145,7 +148,7 @@ func TestHandlerProbeAddressFamilyAutoDetect(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantFamily, out["addressFamily"], "addressFamily for %s", tt.destination)
})
}
@@ -169,6 +172,6 @@ func TestHandlerUpdateProbeStatePending(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "PENDING", out["state"], "probe state after UpdateProbe with state=PENDING")
}
diff --git a/services/networkmonitor/handler_tags_test.go b/services/networkmonitor/handler_tags_test.go
index b5a07e40e9..3d37da5bc6 100644
--- a/services/networkmonitor/handler_tags_test.go
+++ b/services/networkmonitor/handler_tags_test.go
@@ -1,9 +1,11 @@
package networkmonitor_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandlerTags(t *testing.T) {
@@ -21,7 +23,7 @@ func TestHandlerTags(t *testing.T) {
}
var mon map[string]any
- if err := json.Unmarshal(rr.Body.Bytes(), &mon); err != nil {
+ if err := json.Unmarshal(rr.Body.Bytes(), &mon, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
@@ -36,7 +38,7 @@ func TestHandlerTags(t *testing.T) {
}
var tagResp map[string]any
- if err := json.Unmarshal(rr.Body.Bytes(), &tagResp); err != nil {
+ if err := json.Unmarshal(rr.Body.Bytes(), &tagResp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal tags: %v", err)
}
@@ -58,7 +60,7 @@ func TestHandlerTags(t *testing.T) {
}
rr = doNMRequest(t, h, http.MethodGet, "/tags/"+monARN, nil)
- if err := json.Unmarshal(rr.Body.Bytes(), &tagResp); err != nil {
+ if err := json.Unmarshal(rr.Body.Bytes(), &tagResp, jsonx.V1()); err != nil {
t.Fatalf("unmarshal: %v", err)
}
diff --git a/services/networkmonitor/handler_test.go b/services/networkmonitor/handler_test.go
index d0771c314c..671ad94a6a 100644
--- a/services/networkmonitor/handler_test.go
+++ b/services/networkmonitor/handler_test.go
@@ -2,7 +2,7 @@ package networkmonitor_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/networkmonitor"
)
@@ -36,7 +37,7 @@ func doNMRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
if err != nil {
t.Fatalf("marshal body: %v", err)
}
@@ -69,7 +70,7 @@ func createMonitorP(t *testing.T, h *networkmonitor.Handler, name string) string
require.Equal(t, http.StatusOK, rec.Code, "create monitor: %s", rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
arn, _ := out["monitorArn"].(string)
require.NotEmpty(t, arn)
@@ -90,7 +91,7 @@ func createProbeP(t *testing.T, h *networkmonitor.Handler, monitorName, destinat
require.Equal(t, http.StatusOK, rec.Code, "create probe: %s", rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
id, _ := out["probeId"].(string)
require.NotEmpty(t, id)
diff --git a/services/networkmonitor/persistence.go b/services/networkmonitor/persistence.go
index 4128694355..cdef74dee7 100644
--- a/services/networkmonitor/persistence.go
+++ b/services/networkmonitor/persistence.go
@@ -2,7 +2,7 @@ package networkmonitor
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
@@ -25,9 +25,9 @@ const networkmonitorSnapshotVersion = 1
// backend. Tables holds one JSON-encoded array per registered store.Table
// (just "monitors"), produced by [store.Registry.SnapshotAll].
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- NextProbeSeq int64 `json:"next_probe_seq"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ NextProbeSeq int64 `json:"next_probe_seq"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/oam/persistence.go b/services/oam/persistence.go
index ee310d30a2..e7b2cbe8e1 100644
--- a/services/oam/persistence.go
+++ b/services/oam/persistence.go
@@ -2,7 +2,7 @@ package oam
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,11 +12,11 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Seq int64 `json:"seq"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Seq int64 `json:"seq"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/oam/policy.go b/services/oam/policy.go
index c38d3f1445..27c497cfac 100644
--- a/services/oam/policy.go
+++ b/services/oam/policy.go
@@ -1,11 +1,13 @@
package oam
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"slices"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iam"
)
@@ -15,24 +17,24 @@ const (
)
type rawPolicy struct {
- Version string `json:"Version,omitempty"`
- Statement json.RawMessage `json:"Statement"`
+ Version string `json:"Version,omitempty"`
+ Statement jsontext.Value `json:"Statement"`
}
-func decodeStatements(raw json.RawMessage) ([]map[string]any, bool) {
+func decodeStatements(raw jsontext.Value) ([]map[string]any, bool) {
trimmed := strings.TrimSpace(string(raw))
switch {
case strings.HasPrefix(trimmed, "{"):
var one map[string]any
- if json.Unmarshal(raw, &one) != nil {
+ if json.Unmarshal(raw, &one, jsonx.V1()) != nil {
return nil, false
}
return []map[string]any{one}, true
case strings.HasPrefix(trimmed, "["):
var many []map[string]any
- if json.Unmarshal(raw, &many) != nil {
+ if json.Unmarshal(raw, &many, jsonx.V1()) != nil {
return nil, false
}
@@ -63,7 +65,7 @@ func stringList(v any) []string {
func validateSinkPolicy(policy string) error {
var p rawPolicy
- if err := json.Unmarshal([]byte(policy), &p); err != nil {
+ if err := json.Unmarshal([]byte(policy), &p, jsonx.V1()); err != nil {
return fail(ErrInvalidParameter, "Policy is not valid JSON")
}
@@ -120,7 +122,7 @@ func (b *InMemoryBackend) sinkPolicyAllowsLocked(
}
var p rawPolicy
- if json.Unmarshal([]byte(sink.Policy), &p) != nil {
+ if json.Unmarshal([]byte(sink.Policy), &p, jsonx.V1()) != nil {
return false
}
@@ -141,7 +143,7 @@ func (b *InMemoryBackend) sinkPolicyAllowsLocked(
return false
}
- doc, err := json.Marshal(map[string]any{"Version": p.Version, "Statement": applicable})
+ doc, err := json.Marshal(map[string]any{"Version": p.Version, "Statement": applicable}, jsonx.V1())
if err != nil {
return false
}
diff --git a/services/oam/realclient_links_test.go b/services/oam/realclient_links_test.go
index 96f2ef845c..b6b01c7600 100644
--- a/services/oam/realclient_links_test.go
+++ b/services/oam/realclient_links_test.go
@@ -1,7 +1,7 @@
package oam_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/oam"
)
@@ -23,11 +24,11 @@ func allowPolicy(account, condition string) string {
if condition != "" {
var c any
- _ = json.Unmarshal([]byte(condition), &c)
+ _ = json.Unmarshal([]byte(condition), &c, jsonx.V1())
stmt["Condition"] = c
}
- b, _ := json.Marshal(map[string]any{"Version": "2012-10-17", "Statement": []any{stmt}})
+ b, _ := json.Marshal(map[string]any{"Version": "2012-10-17", "Statement": []any{stmt}}, jsonx.V1())
return string(b)
}
diff --git a/services/ociapigateway/auth.go b/services/ociapigateway/auth.go
index 7228d56e18..41f9a74700 100644
--- a/services/ociapigateway/auth.go
+++ b/services/ociapigateway/auth.go
@@ -7,7 +7,8 @@ import (
"crypto/rsa"
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"encoding/pem"
"errors"
"fmt"
@@ -20,6 +21,7 @@ import (
"github.com/golang-jwt/jwt/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
)
@@ -170,7 +172,11 @@ func (b *InMemoryBackend) remoteKeys(ctx context.Context, uri string, hours *int
} `json:"keys"`
}
- if err = json.NewDecoder(io.LimitReader(resp.Body, jwksMaxBytes)).Decode(&doc); err != nil {
+ if err = json.UnmarshalDecode(
+ jsontext.NewDecoder(io.LimitReader(resp.Body, jwksMaxBytes)),
+ &doc,
+ jsonx.V1(),
+ ); err != nil {
return nil, err
}
@@ -460,7 +466,7 @@ func (b *InMemoryBackend) customAuth(ctx context.Context, a *authDoc, token stri
payload = map[string]any{"type": "USER_DEFINED", "data": data}
}
- body, _ := json.Marshal(payload) //nolint:errchkjson // static shape
+ body, _ := json.Marshal(payload, jsonx.V1()) //nolint:errchkjson // static shape
out, err := inv.InvokeFunction(ctx, a.FunctionID, FunctionRequest{
Method: http.MethodPost, Header: http.Header{"Content-Type": {"application/json"}}, Body: body,
@@ -475,7 +481,7 @@ func (b *InMemoryBackend) customAuth(ctx context.Context, a *authDoc, token stri
Active bool `json:"active"`
}
- if json.Unmarshal(out.Body, &verdict) != nil || !verdict.Active {
+ if json.Unmarshal(out.Body, &verdict, jsonx.V1()) != nil || !verdict.Active {
return authResult{wwwAuth: verdict.WWWAuth}
}
diff --git a/services/ociapigateway/auth_test.go b/services/ociapigateway/auth_test.go
index 390b485b5f..866d961d0a 100644
--- a/services/ociapigateway/auth_test.go
+++ b/services/ociapigateway/auth_test.go
@@ -5,7 +5,8 @@ import (
"crypto/rsa"
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"encoding/pem"
"math/big"
"net/http"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ociapigateway "github.com/blackbirdworks/gopherstack/services/ociapigateway"
)
@@ -51,7 +53,7 @@ func pemOf(t *testing.T, k *rsa.PrivateKey, kid string) string {
b, err := json.Marshal(map[string]string{
"format": "PEM", "kid": kid, "key": string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: der})),
- })
+ }, jsonx.V1())
require.NoError(t, err)
return string(b)
@@ -262,9 +264,9 @@ func TestJWTRemoteJWKS(t *testing.T) {
fetches.Add(1)
var key map[string]any
- _ = json.Unmarshal([]byte(jwkOf(k, "r1")), &key)
+ _ = json.Unmarshal([]byte(jwkOf(k, "r1")), &key, jsonx.V1())
- _ = json.NewEncoder(w).Encode(map[string]any{"keys": []any{key}})
+ _ = json.MarshalEncode(jsontext.NewEncoder(w, jsonx.V1()), map[string]any{"keys": []any{key}})
}))
t.Cleanup(jwks.Close)
@@ -356,7 +358,7 @@ func TestCustomAuthenticator(t *testing.T) {
Token string `json:"token"`
}
- _ = json.Unmarshal(in.Body, &p)
+ _ = json.Unmarshal(in.Body, &p, jsonx.V1())
verdict := map[string]any{"active": false, "wwwAuthenticate": `Bearer realm="orders"`}
@@ -367,7 +369,7 @@ func TestCustomAuthenticator(t *testing.T) {
verdict = map[string]any{"active": true, "scope": []string{"orders.write"}}
}
- out, _ := json.Marshal(verdict)
+ out, _ := json.Marshal(verdict, jsonx.V1())
return ociapigateway.FunctionResponse{Status: 200, Body: out}, nil
}}
diff --git a/services/ociapigateway/certificates.go b/services/ociapigateway/certificates.go
index 4e776742ba..e15a4e1795 100644
--- a/services/ociapigateway/certificates.go
+++ b/services/ociapigateway/certificates.go
@@ -3,7 +3,7 @@ package ociapigateway
import (
"crypto/tls"
"crypto/x509"
- "encoding/json"
+ "encoding/json/jsontext"
"encoding/pem"
"time"
@@ -13,12 +13,12 @@ import (
type createCertificateIn struct {
Tags
- CompartmentID string `json:"compartmentId"`
- DisplayName string `json:"displayName"`
- PrivateKey string `json:"privateKey"`
- Certificate string `json:"certificate"`
- Intermediate string `json:"intermediateCertificates"`
- Locks json.RawMessage `json:"locks"`
+ CompartmentID string `json:"compartmentId"`
+ DisplayName string `json:"displayName"`
+ PrivateKey string `json:"privateKey"`
+ Certificate string `json:"certificate"`
+ Intermediate string `json:"intermediateCertificates"`
+ Locks jsontext.Value `json:"locks"`
}
type updateCertificateIn struct {
@@ -33,7 +33,7 @@ type CertificateWire struct {
TimeNotValidAfter time.Time `json:"timeNotValidAfter"`
DefinedTags map[string]map[string]any `json:"definedTags,omitempty"`
FreeformTags map[string]string `json:"freeformTags,omitempty"`
- Locks json.RawMessage `json:"locks,omitempty"`
+ Locks jsontext.Value `json:"locks,omitempty"`
ID string `json:"id"`
CompartmentID string `json:"compartmentId"`
DisplayName string `json:"displayName"`
diff --git a/services/ociapigateway/dataplane.go b/services/ociapigateway/dataplane.go
index 24794a4f73..77916d1246 100644
--- a/services/ociapigateway/dataplane.go
+++ b/services/ociapigateway/dataplane.go
@@ -1,7 +1,7 @@
package ociapigateway
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"maps"
"net/http"
@@ -9,6 +9,7 @@ import (
"strconv"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -26,7 +27,10 @@ func writeGWError(w http.ResponseWriter, status int, msg string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
- out, _ := json.Marshal(map[string]any{"code": status, "message": msg}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ map[string]any{"code": status, "message": msg},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = w.Write(out)
}
diff --git a/services/ociapigateway/dataplane_test.go b/services/ociapigateway/dataplane_test.go
index fef97d428b..b22b60c78f 100644
--- a/services/ociapigateway/dataplane_test.go
+++ b/services/ociapigateway/dataplane_test.go
@@ -2,7 +2,7 @@ package ociapigateway_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ociapigateway "github.com/blackbirdworks/gopherstack/services/ociapigateway"
)
@@ -452,7 +453,7 @@ func TestSpecRoundTrip(t *testing.T) {
} `json:"specification"`
}
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
assert.Equal(t, "STOCK_RESPONSE_BACKEND", out.Specification.Routes[0].Backend.Type)
assert.NotContains(t, body, "StubResponse")
}
diff --git a/services/ociapigateway/deployments.go b/services/ociapigateway/deployments.go
index e0b3aa781f..f67d68bd15 100644
--- a/services/ociapigateway/deployments.go
+++ b/services/ociapigateway/deployments.go
@@ -1,7 +1,7 @@
package ociapigateway
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/page"
@@ -10,18 +10,18 @@ import (
type createDeploymentIn struct {
Tags
- GatewayID string `json:"gatewayId"`
- CompartmentID string `json:"compartmentId"`
- PathPrefix string `json:"pathPrefix"`
- DisplayName string `json:"displayName"`
- Locks json.RawMessage `json:"locks"`
- Specification json.RawMessage `json:"specification"`
+ GatewayID string `json:"gatewayId"`
+ CompartmentID string `json:"compartmentId"`
+ PathPrefix string `json:"pathPrefix"`
+ DisplayName string `json:"displayName"`
+ Locks jsontext.Value `json:"locks"`
+ Specification jsontext.Value `json:"specification"`
}
type updateDeploymentIn struct {
Tags
- DisplayName *string `json:"displayName"`
- Specification json.RawMessage `json:"specification"`
+ DisplayName *string `json:"displayName"`
+ Specification jsontext.Value `json:"specification"`
}
// DeploymentWire is the wire form of a deployment.
@@ -37,8 +37,8 @@ type DeploymentWire struct {
Endpoint string `json:"endpoint"`
DisplayName string `json:"displayName"`
LifecycleState string `json:"lifecycleState"`
- Locks json.RawMessage `json:"locks,omitempty"`
- Specification json.RawMessage `json:"specification,omitempty"`
+ Locks jsontext.Value `json:"locks,omitempty"`
+ Specification jsontext.Value `json:"specification,omitempty"`
}
func (b *InMemoryBackend) deploymentView(d *Deployment, full bool) DeploymentWire {
diff --git a/services/ociapigateway/gateways.go b/services/ociapigateway/gateways.go
index 91ae8de001..7ce31e0923 100644
--- a/services/ociapigateway/gateways.go
+++ b/services/ociapigateway/gateways.go
@@ -1,36 +1,38 @@
package ociapigateway
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"slices"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
type createGatewayIn struct {
Tags
- Locks json.RawMessage `json:"locks"`
- CaBundles json.RawMessage `json:"caBundles"`
- ResponseCache json.RawMessage `json:"responseCacheDetails"`
- DisplayName string `json:"displayName"`
- CompartmentID string `json:"compartmentId"`
- EndpointType string `json:"endpointType"`
- SubnetID string `json:"subnetId"`
- CertificateID string `json:"certificateId"`
- IPMode string `json:"ipMode"`
- NSGs []string `json:"networkSecurityGroupIds"`
+ Locks jsontext.Value `json:"locks"`
+ CaBundles jsontext.Value `json:"caBundles"`
+ ResponseCache jsontext.Value `json:"responseCacheDetails"`
+ DisplayName string `json:"displayName"`
+ CompartmentID string `json:"compartmentId"`
+ EndpointType string `json:"endpointType"`
+ SubnetID string `json:"subnetId"`
+ CertificateID string `json:"certificateId"`
+ IPMode string `json:"ipMode"`
+ NSGs []string `json:"networkSecurityGroupIds"`
}
type updateGatewayIn struct {
Tags
- CaBundles json.RawMessage `json:"caBundles"`
- ResponseCache json.RawMessage `json:"responseCacheDetails"`
- DisplayName *string `json:"displayName"`
- CertificateID *string `json:"certificateId"`
- NSGs []string `json:"networkSecurityGroupIds"`
+ CaBundles jsontext.Value `json:"caBundles"`
+ ResponseCache jsontext.Value `json:"responseCacheDetails"`
+ DisplayName *string `json:"displayName"`
+ CertificateID *string `json:"certificateId"`
+ NSGs []string `json:"networkSecurityGroupIds"`
}
type changeCompartmentIn struct {
@@ -43,9 +45,9 @@ type GatewayWire struct {
TimeUpdated time.Time `json:"timeUpdated"`
DefinedTags map[string]map[string]any `json:"definedTags,omitempty"`
FreeformTags map[string]string `json:"freeformTags,omitempty"`
- Locks json.RawMessage `json:"locks,omitempty"`
- CaBundles json.RawMessage `json:"caBundles,omitempty"`
- ResponseCache json.RawMessage `json:"responseCacheDetails,omitempty"`
+ Locks jsontext.Value `json:"locks,omitempty"`
+ CaBundles jsontext.Value `json:"caBundles,omitempty"`
+ ResponseCache jsontext.Value `json:"responseCacheDetails,omitempty"`
ID string `json:"id"`
CompartmentID string `json:"compartmentId"`
EndpointType string `json:"endpointType"`
@@ -117,12 +119,12 @@ func (b *InMemoryBackend) validateGateway(in *createGatewayIn) *APIError {
return b.checkCertificateRef(in.CertificateID)
}
-func jsonIsNoCache(raw json.RawMessage) bool {
+func jsonIsNoCache(raw jsontext.Value) bool {
var d struct {
Type string `json:"type"`
}
- return json.Unmarshal(raw, &d) == nil && d.Type == "NONE"
+ return json.Unmarshal(raw, &d, jsonx.V1()) == nil && d.Type == "NONE"
}
func (b *InMemoryBackend) gatewayNameTaken(compartment, name, except string) bool {
@@ -169,7 +171,7 @@ func (b *InMemoryBackend) CreateGateway(in createGatewayIn) (GatewayWire, string
return gatewayView(g), wr, g.Etag, nil
}
-func nullRaw(r json.RawMessage) json.RawMessage {
+func nullRaw(r jsontext.Value) jsontext.Value {
if string(r) == jsonNull {
return nil
}
diff --git a/services/ociapigateway/handler.go b/services/ociapigateway/handler.go
index 714b9cd74f..e98844c165 100644
--- a/services/ociapigateway/handler.go
+++ b/services/ociapigateway/handler.go
@@ -3,7 +3,8 @@ package ociapigateway
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +17,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -116,7 +119,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ociapigateway: encode response: %w", err)
}
@@ -125,9 +128,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -162,10 +163,9 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- dec := json.NewDecoder(bytes.NewReader(q.body))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(q.body))
- if err := dec.Decode(v); err != nil {
+ if err := json.UnmarshalDecode(dec, v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -187,7 +187,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -262,7 +265,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ociapigateway/helpers_test.go b/services/ociapigateway/helpers_test.go
index e791ca8a95..810f0ec7f2 100644
--- a/services/ociapigateway/helpers_test.go
+++ b/services/ociapigateway/helpers_test.go
@@ -5,7 +5,7 @@ import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"fmt"
"io"
@@ -19,6 +19,7 @@ import (
"github.com/oracle/oci-go-sdk/v65/common"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
ociapigateway "github.com/blackbirdworks/gopherstack/services/ociapigateway"
@@ -190,7 +191,7 @@ func specFromJSON(t *testing.T, s string) *apigateway.ApiSpecification {
t.Helper()
var spec apigateway.ApiSpecification
- require.NoError(t, json.Unmarshal([]byte(s), &spec))
+ require.NoError(t, json.Unmarshal([]byte(s), &spec, jsonx.V1()))
return &spec
}
diff --git a/services/ociapigateway/models.go b/services/ociapigateway/models.go
index 6158ff5b8a..d03c5e2ef9 100644
--- a/services/ociapigateway/models.go
+++ b/services/ociapigateway/models.go
@@ -1,7 +1,7 @@
package ociapigateway
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -38,59 +38,59 @@ func (t *Tags) merge(in Tags) {
// Gateway is an API gateway.
type Gateway struct {
- TimeCreated time.Time `json:"timeCreated"`
- TimeUpdated time.Time `json:"timeUpdated"`
- Tags Tags `json:"tags"`
- TransitionAt *time.Time `json:"transitionAt,omitempty"`
- Locks json.RawMessage `json:"locks,omitempty"`
- CaBundles json.RawMessage `json:"caBundles,omitempty"`
- ResponseCache json.RawMessage `json:"responseCache,omitempty"`
- ID string `json:"id"`
- CompartmentID string `json:"compartmentId"`
- EndpointType string `json:"endpointType"`
- DisplayName string `json:"displayName"`
- SubnetID string `json:"subnetId"`
- Hostname string `json:"hostname"`
- CertificateID string `json:"certificateId,omitempty"`
- IPMode string `json:"ipMode,omitempty"`
- State string `json:"state"`
- Etag string `json:"etag"`
- NSGs []string `json:"networkSecurityGroupIds,omitempty"`
+ TimeCreated time.Time `json:"timeCreated"`
+ TimeUpdated time.Time `json:"timeUpdated"`
+ Tags Tags `json:"tags"`
+ TransitionAt *time.Time `json:"transitionAt,omitempty"`
+ Locks jsontext.Value `json:"locks,omitempty"`
+ CaBundles jsontext.Value `json:"caBundles,omitempty"`
+ ResponseCache jsontext.Value `json:"responseCache,omitempty"`
+ ID string `json:"id"`
+ CompartmentID string `json:"compartmentId"`
+ EndpointType string `json:"endpointType"`
+ DisplayName string `json:"displayName"`
+ SubnetID string `json:"subnetId"`
+ Hostname string `json:"hostname"`
+ CertificateID string `json:"certificateId,omitempty"`
+ IPMode string `json:"ipMode,omitempty"`
+ State string `json:"state"`
+ Etag string `json:"etag"`
+ NSGs []string `json:"networkSecurityGroupIds,omitempty"`
}
// Deployment is an API deployment on a gateway.
type Deployment struct {
- TimeCreated time.Time `json:"timeCreated"`
- TimeUpdated time.Time `json:"timeUpdated"`
- Tags Tags `json:"tags"`
- TransitionAt *time.Time `json:"transitionAt,omitempty"`
- ID string `json:"id"`
- GatewayID string `json:"gatewayId"`
- CompartmentID string `json:"compartmentId"`
- PathPrefix string `json:"pathPrefix"`
- DisplayName string `json:"displayName"`
- State string `json:"state"`
- Etag string `json:"etag"`
- Locks json.RawMessage `json:"locks,omitempty"`
- Spec json.RawMessage `json:"specification"`
+ TimeCreated time.Time `json:"timeCreated"`
+ TimeUpdated time.Time `json:"timeUpdated"`
+ Tags Tags `json:"tags"`
+ TransitionAt *time.Time `json:"transitionAt,omitempty"`
+ ID string `json:"id"`
+ GatewayID string `json:"gatewayId"`
+ CompartmentID string `json:"compartmentId"`
+ PathPrefix string `json:"pathPrefix"`
+ DisplayName string `json:"displayName"`
+ State string `json:"state"`
+ Etag string `json:"etag"`
+ Locks jsontext.Value `json:"locks,omitempty"`
+ Spec jsontext.Value `json:"specification"`
}
// Certificate is a TLS certificate usable by a gateway.
type Certificate struct {
- TimeCreated time.Time `json:"timeCreated"`
- TimeUpdated time.Time `json:"timeUpdated"`
- TimeNotValidAfter time.Time `json:"timeNotValidAfter"`
- Tags Tags `json:"tags"`
- TransitionAt *time.Time `json:"transitionAt,omitempty"`
- Locks json.RawMessage `json:"locks,omitempty"`
- ID string `json:"id"`
- CompartmentID string `json:"compartmentId"`
- DisplayName string `json:"displayName"`
- CertPEM string `json:"certificate"`
- Intermediate string `json:"intermediateCertificates,omitempty"`
- State string `json:"state"`
- Etag string `json:"etag"`
- SubjectNames []string `json:"subjectNames"`
+ TimeCreated time.Time `json:"timeCreated"`
+ TimeUpdated time.Time `json:"timeUpdated"`
+ TimeNotValidAfter time.Time `json:"timeNotValidAfter"`
+ Tags Tags `json:"tags"`
+ TransitionAt *time.Time `json:"transitionAt,omitempty"`
+ Locks jsontext.Value `json:"locks,omitempty"`
+ ID string `json:"id"`
+ CompartmentID string `json:"compartmentId"`
+ DisplayName string `json:"displayName"`
+ CertPEM string `json:"certificate"`
+ Intermediate string `json:"intermediateCertificates,omitempty"`
+ State string `json:"state"`
+ Etag string `json:"etag"`
+ SubjectNames []string `json:"subjectNames"`
}
// WorkRequest records an asynchronous operation.
diff --git a/services/ociapigateway/spec.go b/services/ociapigateway/spec.go
index 331f768915..63fdb134e0 100644
--- a/services/ociapigateway/spec.go
+++ b/services/ociapigateway/spec.go
@@ -2,12 +2,15 @@ package ociapigateway
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/url"
"regexp"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Backend, authentication and policy discriminators.
@@ -144,12 +147,12 @@ type authDoc struct {
}
type requestPolicies struct {
- Authentication *authDoc `json:"authentication"`
- RateLimiting *rateDoc `json:"rateLimiting"`
- Cors *corsDoc `json:"cors"`
- MutualTLS json.RawMessage `json:"mutualTls"`
- UsagePlans json.RawMessage `json:"usagePlans"`
- DynamicAuthentication json.RawMessage `json:"dynamicAuthentication"`
+ Authentication *authDoc `json:"authentication"`
+ RateLimiting *rateDoc `json:"rateLimiting"`
+ Cors *corsDoc `json:"cors"`
+ MutualTLS jsontext.Value `json:"mutualTls"`
+ UsagePlans jsontext.Value `json:"usagePlans"`
+ DynamicAuthentication jsontext.Value `json:"dynamicAuthentication"`
}
type setItem struct {
@@ -196,19 +199,19 @@ type authzDoc struct {
}
type routeRequestPolicies struct {
- Authorization *authzDoc `json:"authorization"`
- Cors *corsDoc `json:"cors"`
- HeaderTransform *transformDoc `json:"headerTransformations"`
- QueryTransform *transformDoc `json:"queryParameterTransformations"`
- QueryValidations json.RawMessage `json:"queryParameterValidations"`
- HeaderValidations json.RawMessage `json:"headerValidations"`
- BodyValidation json.RawMessage `json:"bodyValidation"`
- ResponseCacheLookup json.RawMessage `json:"responseCacheLookup"`
+ Authorization *authzDoc `json:"authorization"`
+ Cors *corsDoc `json:"cors"`
+ HeaderTransform *transformDoc `json:"headerTransformations"`
+ QueryTransform *transformDoc `json:"queryParameterTransformations"`
+ QueryValidations jsontext.Value `json:"queryParameterValidations"`
+ HeaderValidations jsontext.Value `json:"headerValidations"`
+ BodyValidation jsontext.Value `json:"bodyValidation"`
+ ResponseCacheLookup jsontext.Value `json:"responseCacheLookup"`
}
type routeResponsePolicies struct {
- HeaderTransform *transformDoc `json:"headerTransformations"`
- ResponseCacheStore json.RawMessage `json:"responseCacheStore"`
+ HeaderTransform *transformDoc `json:"headerTransformations"`
+ ResponseCacheStore jsontext.Value `json:"responseCacheStore"`
}
type routeDoc struct {
@@ -221,7 +224,7 @@ type routeDoc struct {
type specDoc struct {
RequestPolicies *requestPolicies `json:"requestPolicies"`
- LoggingPolicies json.RawMessage `json:"loggingPolicies"`
+ LoggingPolicies jsontext.Value `json:"loggingPolicies"`
Routes []routeDoc `json:"routes"`
}
@@ -231,7 +234,7 @@ var (
prefixCharRe = regexp.MustCompile(`^[A-Za-z0-9$\-_.+!*'(),%;:@&=]+$`)
)
-func isPresent(raw json.RawMessage) bool {
+func isPresent(raw jsontext.Value) bool {
return len(raw) > 0 && !bytes.Equal(raw, []byte(jsonNull))
}
@@ -265,13 +268,13 @@ func validatePathPrefix(p string) *APIError {
}
// compileSpec decodes and validates a deployment specification.
-func compileSpec(raw json.RawMessage) (*compiledSpec, *APIError) {
+func compileSpec(raw jsontext.Value) (*compiledSpec, *APIError) {
if !isPresent(raw) {
return nil, errMissing("specification is required")
}
var doc specDoc
- if err := json.Unmarshal(raw, &doc); err != nil {
+ if err := json.Unmarshal(raw, &doc, jsonx.V1()); err != nil {
return nil, errParam("specification is not valid: " + err.Error())
}
@@ -312,7 +315,7 @@ func validateRequestPolicies(p *requestPolicies) *APIError {
return nil
}
- for name, raw := range map[string]json.RawMessage{
+ for name, raw := range map[string]jsontext.Value{
"mutualTls": p.MutualTLS, "usagePlans": p.UsagePlans, "dynamicAuthentication": p.DynamicAuthentication,
} {
if isPresent(raw) {
@@ -525,7 +528,7 @@ func validateRouteRequestPolicies(p *routeRequestPolicies) *APIError {
return nil
}
- for name, raw := range map[string]json.RawMessage{
+ for name, raw := range map[string]jsontext.Value{
"queryParameterValidations": p.QueryValidations, "headerValidations": p.HeaderValidations,
"bodyValidation": p.BodyValidation, "responseCacheLookup": p.ResponseCacheLookup,
} {
diff --git a/services/ociartifacts/dataplane.go b/services/ociartifacts/dataplane.go
index 3772030349..042339cd55 100644
--- a/services/ociartifacts/dataplane.go
+++ b/services/ociartifacts/dataplane.go
@@ -4,12 +4,13 @@ import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociregistry"
)
@@ -111,7 +112,7 @@ func (b *InMemoryBackend) IssueToken(user string, anon bool, scopes []Scope) (st
b.mu.RUnlock()
body, _ := json.Marshal(
- tokenClaims{Sub: user, Anon: anon, Scopes: raw, Exp: exp},
+ tokenClaims{Sub: user, Anon: anon, Scopes: raw, Exp: exp}, jsonx.V1(),
) //nolint:errchkjson // static shape
payload := base64.RawURLEncoding.EncodeToString(body)
@@ -131,7 +132,7 @@ func (b *InMemoryBackend) ParseToken(token string) (tokenClaims, bool) {
}
var c tokenClaims
- if json.Unmarshal(raw, &c) != nil {
+ if json.Unmarshal(raw, &c, jsonx.V1()) != nil {
return tokenClaims{}, false
}
@@ -274,7 +275,7 @@ func (b *InMemoryBackend) RecordPush(display, ref, digest, user string, body []b
var doc manifestDoc
- _ = json.Unmarshal(body, &doc)
+ _ = json.Unmarshal(body, &doc, jsonx.V1())
b.mu.Lock("RecordPush")
defer b.mu.Unlock()
diff --git a/services/ociartifacts/handler.go b/services/ociartifacts/handler.go
index 69e6ba2dd5..efcd5df436 100644
--- a/services/ociartifacts/handler.go
+++ b/services/ociartifacts/handler.go
@@ -7,7 +7,8 @@ package ociartifacts
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -20,6 +21,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -118,7 +121,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ociartifacts: encode response: %w", err)
}
@@ -127,9 +130,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -172,10 +173,9 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- dec := json.NewDecoder(bytes.NewReader(q.body))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(q.body))
- if err := dec.Decode(v); err != nil {
+ if err := json.UnmarshalDecode(dec, v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return newErr(http.StatusBadRequest, "CannotParseRequest",
"The request body is not valid JSON for this operation")
}
@@ -194,7 +194,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -266,7 +269,7 @@ func (h *Handler) admin(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ociartifacts/helpers_test.go b/services/ociartifacts/helpers_test.go
index 21b98b682d..40bee2da4e 100644
--- a/services/ociartifacts/helpers_test.go
+++ b/services/ociartifacts/helpers_test.go
@@ -7,7 +7,7 @@ import (
"crypto/rsa"
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"fmt"
"io"
@@ -22,6 +22,7 @@ import (
"github.com/oracle/oci-go-sdk/v65/genericartifactscontent"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
"github.com/blackbirdworks/gopherstack/pkgs/ociregistry/ocitest"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
@@ -254,7 +255,7 @@ func (e *env) upload(
func jsonBody(t *testing.T, v any) string {
t.Helper()
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
return string(out)
diff --git a/services/ociartifacts/registry.go b/services/ociartifacts/registry.go
index 4dc292baf2..9140ad9173 100644
--- a/services/ociartifacts/registry.go
+++ b/services/ociartifacts/registry.go
@@ -5,11 +5,12 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociregistry"
)
@@ -28,7 +29,7 @@ type regErrorEntry struct {
}
func writeJSON(w http.ResponseWriter, status int, v any) {
- raw, err := json.Marshal(v)
+ raw, err := json.Marshal(v, jsonx.V1())
if err != nil {
status, raw = http.StatusInternalServerError, []byte(`{"errors":[{"code":"UNKNOWN","message":"encode"}]}`)
}
diff --git a/services/ociartifacts/registry_test.go b/services/ociartifacts/registry_test.go
index 5dbbf1fb00..28c43b61ee 100644
--- a/services/ociartifacts/registry_test.go
+++ b/services/ociartifacts/registry_test.go
@@ -1,7 +1,7 @@
package ociartifacts_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociregistry/ocitest"
"github.com/blackbirdworks/gopherstack/services/ociartifacts"
)
@@ -36,7 +37,7 @@ func (e *env) token(t *testing.T, authz, scope string) (int, tokenReply) {
var out tokenReply
if resp.Status == http.StatusOK {
- require.NoError(t, json.Unmarshal(resp.Body, &out))
+ require.NoError(t, json.Unmarshal(resp.Body, &out, jsonx.V1()))
}
return resp.Status, out
@@ -845,7 +846,7 @@ func manifestFor(t *testing.T, cfgDigest, layerDigest string, layerSize, cfgSize
"layers": []any{map[string]any{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "size": layerSize, "digest": layerDigest,
}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
return body
diff --git a/services/ocicontainerengine/handler.go b/services/ocicontainerengine/handler.go
index 82ccbad374..0dbcf22207 100644
--- a/services/ocicontainerengine/handler.go
+++ b/services/ocicontainerengine/handler.go
@@ -2,7 +2,7 @@ package ocicontainerengine
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -15,6 +15,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -115,7 +117,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ocicontainerengine: encode response: %w", err)
}
@@ -124,9 +126,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -163,7 +163,7 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- if err := json.Unmarshal(q.body, v); err != nil {
+ if err := json.Unmarshal(q.body, v, jsonx.V1()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -185,7 +185,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -254,7 +257,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ocicontainerengine/helpers_test.go b/services/ocicontainerengine/helpers_test.go
index 5d56c80776..b242888e8a 100644
--- a/services/ocicontainerengine/helpers_test.go
+++ b/services/ocicontainerengine/helpers_test.go
@@ -5,7 +5,7 @@ import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"fmt"
"io"
@@ -19,6 +19,7 @@ import (
ke "github.com/oracle/oci-go-sdk/v65/containerengine"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
@@ -345,7 +346,7 @@ func (e *env) mkPool(
}
func quote(s string) string {
- b, _ := json.Marshal(s)
+ b, _ := json.Marshal(s, jsonx.V1())
return string(b)
}
diff --git a/services/ocicontainerengine/models.go b/services/ocicontainerengine/models.go
index 2790480923..65e599f08e 100644
--- a/services/ocicontainerengine/models.go
+++ b/services/ocicontainerengine/models.go
@@ -1,7 +1,7 @@
package ocicontainerengine
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -80,15 +80,15 @@ type TagCfg struct {
// ClusterOptions is the cluster create and update options block.
type ClusterOptions struct {
- K8sNetwork *NetworkConfig `json:"kubernetesNetworkConfig,omitempty"`
- AddOns *AddOns `json:"addOns,omitempty"`
- Admission *Admission `json:"admissionControllerOptions,omitempty"`
- PersistentVolume *TagCfg `json:"persistentVolumeConfig,omitempty"`
- ServiceLb *TagCfg `json:"serviceLbConfig,omitempty"`
- OIDCAuth json.RawMessage `json:"openIdConnectTokenAuthenticationConfig,omitempty"`
- OIDCDiscovery json.RawMessage `json:"openIdConnectDiscovery,omitempty"`
- ServiceLbSubnetID []string `json:"serviceLbSubnetIds,omitempty"`
- IPFamilies []string `json:"ipFamilies,omitempty"`
+ K8sNetwork *NetworkConfig `json:"kubernetesNetworkConfig,omitempty"`
+ AddOns *AddOns `json:"addOns,omitempty"`
+ Admission *Admission `json:"admissionControllerOptions,omitempty"`
+ PersistentVolume *TagCfg `json:"persistentVolumeConfig,omitempty"`
+ ServiceLb *TagCfg `json:"serviceLbConfig,omitempty"`
+ OIDCAuth jsontext.Value `json:"openIdConnectTokenAuthenticationConfig,omitempty"`
+ OIDCDiscovery jsontext.Value `json:"openIdConnectDiscovery,omitempty"`
+ ServiceLbSubnetID []string `json:"serviceLbSubnetIds,omitempty"`
+ IPFamilies []string `json:"ipFamilies,omitempty"`
}
// KeyDetail references an image signing key.
@@ -144,11 +144,11 @@ type Cluster struct {
// Placement is one nodeConfigDetails placement configuration.
type Placement struct {
- PreemptibleConfig json.RawMessage `json:"preemptibleNodeConfig,omitempty"`
- AvailabilityDom string `json:"availabilityDomain"`
- SubnetID string `json:"subnetId"`
- CapacityReserveID string `json:"capacityReservationId,omitempty"`
- FaultDomains []string `json:"faultDomains,omitempty"`
+ PreemptibleConfig jsontext.Value `json:"preemptibleNodeConfig,omitempty"`
+ AvailabilityDom string `json:"availabilityDomain"`
+ SubnetID string `json:"subnetId"`
+ CapacityReserveID string `json:"capacityReservationId,omitempty"`
+ FaultDomains []string `json:"faultDomains,omitempty"`
}
// NodeConfig is nodeConfigDetails.
@@ -205,8 +205,8 @@ type NodePool struct {
QuantityPerSub *int `json:"quantityPerSubnet,omitempty"`
Tags Tags `json:"tags"`
NodeMetadata map[string]string `json:"nodeMetadata,omitempty"`
- Eviction json.RawMessage `json:"eviction,omitempty"`
- Cycling json.RawMessage `json:"cycling,omitempty"`
+ Eviction jsontext.Value `json:"eviction,omitempty"`
+ Cycling jsontext.Value `json:"cycling,omitempty"`
ID string `json:"id"`
ClusterID string `json:"clusterId"`
CompartmentID string `json:"compartmentId"`
diff --git a/services/ocicontainerengine/nodepools.go b/services/ocicontainerengine/nodepools.go
index 6915f697c7..7a95f7dd1a 100644
--- a/services/ocicontainerengine/nodepools.go
+++ b/services/ocicontainerengine/nodepools.go
@@ -2,7 +2,7 @@ package ocicontainerengine
import (
"encoding/binary"
- "encoding/json"
+ "encoding/json/jsontext"
"math"
"net/netip"
"regexp"
@@ -46,10 +46,10 @@ type CreateNodePoolIn struct {
Quantity *int `json:"quantityPerSubnet"`
NodeConfig *NodeConfigIn `json:"nodeConfigDetails"`
NodeMetadata map[string]string `json:"nodeMetadata"`
- Eviction json.RawMessage `json:"nodeEvictionNodePoolSettings"`
- Cycling json.RawMessage `json:"nodePoolCyclingDetails"`
- SecondaryVnics json.RawMessage `json:"secondaryVnics"`
- PrimaryVnic json.RawMessage `json:"primaryVnic"`
+ Eviction jsontext.Value `json:"nodeEvictionNodePoolSettings"`
+ Cycling jsontext.Value `json:"nodePoolCyclingDetails"`
+ SecondaryVnics jsontext.Value `json:"secondaryVnics"`
+ PrimaryVnic jsontext.Value `json:"primaryVnic"`
Compartment string `json:"compartmentId"`
ClusterID string `json:"clusterId"`
Name string `json:"name"`
@@ -71,10 +71,10 @@ type UpdateNodePoolIn struct {
SSHKey *string `json:"sshPublicKey"`
Shape *string `json:"nodeShape"`
NodeMetadata map[string]string `json:"nodeMetadata"`
- Eviction json.RawMessage `json:"nodeEvictionNodePoolSettings"`
- Cycling json.RawMessage `json:"nodePoolCyclingDetails"`
- SecondaryVnics json.RawMessage `json:"secondaryVnics"`
- PrimaryVnic json.RawMessage `json:"primaryVnic"`
+ Eviction jsontext.Value `json:"nodeEvictionNodePoolSettings"`
+ Cycling jsontext.Value `json:"nodePoolCyclingDetails"`
+ SecondaryVnics jsontext.Value `json:"secondaryVnics"`
+ PrimaryVnic jsontext.Value `json:"primaryVnic"`
LaunchType string `json:"networkLaunchType"`
Labels []KeyValue `json:"initialNodeLabels"`
SubnetIDs []string `json:"subnetIds"`
@@ -105,8 +105,8 @@ type NodePoolWire struct {
NodeSourceDetails *NodeSource `json:"nodeSourceDetails,omitempty"`
QuantityPerSubnet *int `json:"quantityPerSubnet,omitempty"`
NodeConfigDetails *NodeConfig `json:"nodeConfigDetails,omitempty"`
- Eviction json.RawMessage `json:"nodeEvictionNodePoolSettings,omitempty"`
- Cycling json.RawMessage `json:"nodePoolCyclingDetails,omitempty"`
+ Eviction jsontext.Value `json:"nodeEvictionNodePoolSettings,omitempty"`
+ Cycling jsontext.Value `json:"nodePoolCyclingDetails,omitempty"`
Nodes []NodeWire `json:"nodes,omitempty"`
ID string `json:"id"`
ClusterID string `json:"clusterId"`
@@ -171,7 +171,7 @@ func nodePoolView(np *NodePool, summary bool) NodePoolWire {
return w
}
-func rejectUnsupported(secondary, primary json.RawMessage, launch string) *APIError {
+func rejectUnsupported(secondary, primary jsontext.Value, launch string) *APIError {
if len(secondary) > 0 && string(secondary) != "null" && string(secondary) != "[]" {
return errParam("secondaryVnics are not supported by this emulator")
}
diff --git a/services/ocicontainerinstances/handler.go b/services/ocicontainerinstances/handler.go
index e20e9b677a..2954bf5269 100644
--- a/services/ocicontainerinstances/handler.go
+++ b/services/ocicontainerinstances/handler.go
@@ -3,7 +3,8 @@ package ocicontainerinstances
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +17,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -116,7 +119,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ocicontainerinstances: encode response: %w", err)
}
@@ -125,9 +128,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -168,10 +169,9 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- dec := json.NewDecoder(bytes.NewReader(q.body))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(q.body))
- if err := dec.Decode(v); err != nil {
+ if err := json.UnmarshalDecode(dec, v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -193,7 +193,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -262,7 +265,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ocicontainerinstances/instances.go b/services/ocicontainerinstances/instances.go
index 00f1c5a148..46210b2fb8 100644
--- a/services/ocicontainerinstances/instances.go
+++ b/services/ocicontainerinstances/instances.go
@@ -1,11 +1,12 @@
package ocicontainerinstances
import (
- "encoding/json"
+ "encoding/json/v2"
"slices"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -23,13 +24,13 @@ const (
)
func (in *Instance) clone() *Instance {
- raw, err := json.Marshal(in)
+ raw, err := json.Marshal(in, jsonx.V1())
if err != nil {
return in
}
out := &Instance{}
- if json.Unmarshal(raw, out) != nil {
+ if json.Unmarshal(raw, out, jsonx.V1()) != nil {
return in
}
@@ -172,7 +173,7 @@ func (b *InMemoryBackend) resolveCreds(secrets []PullSecret) ([]Credential, erro
Password string `json:"password"`
}
- if json.Unmarshal([]byte(raw), &auth) != nil || auth.Username == "" || auth.Password == "" {
+ if json.Unmarshal([]byte(raw), &auth, jsonx.V1()) != nil || auth.Username == "" || auth.Password == "" {
return nil, errVaultSecretShape
}
diff --git a/services/ocicontainerinstances/models.go b/services/ocicontainerinstances/models.go
index 979f7b29e4..927876d227 100644
--- a/services/ocicontainerinstances/models.go
+++ b/services/ocicontainerinstances/models.go
@@ -1,7 +1,7 @@
package ocicontainerinstances
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -110,7 +110,7 @@ type Container struct {
State string `json:"lifecycleState"`
Details string `json:"lifecycleDetails,omitempty"`
WorkingDir string `json:"workingDirectory,omitempty"`
- Security json.RawMessage `json:"securityContext,omitempty"`
+ Security jsontext.Value `json:"securityContext,omitempty"`
Command []string `json:"command,omitempty"`
Args []string `json:"arguments,omitempty"`
Mounts []VolumeMount `json:"volumeMounts,omitempty"`
@@ -170,7 +170,7 @@ type Instance struct {
Restart string `json:"containerRestartPolicy"`
Etag string `json:"etag"`
Target string `json:"target,omitempty"`
- Security json.RawMessage `json:"securityContext,omitempty"`
+ Security jsontext.Value `json:"securityContext,omitempty"`
Containers []*Container `json:"containers"`
Volumes []Volume `json:"volumes,omitempty"`
Vnics []Vnic `json:"vnics"`
diff --git a/services/ocicontainerinstances/validate.go b/services/ocicontainerinstances/validate.go
index 4189af447c..cd086133d0 100644
--- a/services/ocicontainerinstances/validate.go
+++ b/services/ocicontainerinstances/validate.go
@@ -3,13 +3,15 @@ package ocicontainerinstances
import (
"cmp"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/netip"
"path"
"slices"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -152,7 +154,7 @@ func (b *InMemoryBackend) buildVnics(inst *Instance, vnics []createVnicIn) *APIE
return nil
}
-func checkSecurity(raw json.RawMessage) *APIError {
+func checkSecurity(raw jsontext.Value) *APIError {
if len(raw) == 0 || string(raw) == "null" {
return nil
}
@@ -161,7 +163,7 @@ func checkSecurity(raw json.RawMessage) *APIError {
Type string `json:"securityContextType"`
}
- if err := json.Unmarshal(raw, &sc); err != nil || sc.Type != "LINUX" {
+ if err := json.Unmarshal(raw, &sc, jsonx.V1()); err != nil || sc.Type != "LINUX" {
return errParam("securityContext.securityContextType must be LINUX")
}
diff --git a/services/ocicontainerinstances/volumes.go b/services/ocicontainerinstances/volumes.go
index cd44c26fc7..de23dfb81f 100644
--- a/services/ocicontainerinstances/volumes.go
+++ b/services/ocicontainerinstances/volumes.go
@@ -2,13 +2,16 @@ package ocicontainerinstances
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"os"
"path"
"path/filepath"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -166,13 +169,13 @@ func (s *scratch) binds(mounts []VolumeMount) []string {
}
// securityUser maps a LINUX security context to docker's uid[:gid] user and the fsGroup supplementary group.
-func securityUser(raw json.RawMessage) string {
+func securityUser(raw jsontext.Value) string {
var sc struct {
User *int `json:"runAsUser"`
Group *int `json:"runAsGroup"`
}
- if len(raw) == 0 || json.Unmarshal(raw, &sc) != nil || sc.User == nil {
+ if len(raw) == 0 || json.Unmarshal(raw, &sc, jsonx.V1()) != nil || sc.User == nil {
return ""
}
diff --git a/services/ocicontainerinstances/wire.go b/services/ocicontainerinstances/wire.go
index ef59865aee..2a3ba70a9c 100644
--- a/services/ocicontainerinstances/wire.go
+++ b/services/ocicontainerinstances/wire.go
@@ -1,7 +1,7 @@
package ocicontainerinstances
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -49,7 +49,7 @@ type (
ImageURL string `json:"imageUrl"`
DisplayName string `json:"displayName"`
WorkingDir string `json:"workingDirectory"`
- SecurityCtx json.RawMessage `json:"securityContext"`
+ SecurityCtx jsontext.Value `json:"securityContext"`
Command []string `json:"command"`
Arguments []string `json:"arguments"`
VolumeMounts []VolumeMount `json:"volumeMounts"`
@@ -61,7 +61,7 @@ type (
Graceful *int64 `json:"gracefulShutdownTimeoutInSeconds"`
Freeform map[string]string `json:"freeformTags"`
Defined map[string]any `json:"definedTags"`
- SecurityCtx json.RawMessage `json:"securityContext"`
+ SecurityCtx jsontext.Value `json:"securityContext"`
Compartment string `json:"compartmentId"`
AD string `json:"availabilityDomain"`
Shape string `json:"shape"`
@@ -114,7 +114,7 @@ type instanceSummaryWire struct {
Details string `json:"lifecycleDetails,omitempty"`
Shape string `json:"shape"`
Restart string `json:"containerRestartPolicy"`
- Security json.RawMessage `json:"securityContext,omitempty"`
+ Security jsontext.Value `json:"securityContext,omitempty"`
ShapeConfig shapeConfigWire `json:"shapeConfig"`
Count int `json:"containerCount"`
VolumeCount int `json:"volumeCount"`
@@ -218,7 +218,7 @@ type containerSummaryWire struct {
Details string `json:"lifecycleDetails,omitempty"`
InstanceID string `json:"containerInstanceId"`
ImageURL string `json:"imageUrl"`
- Security json.RawMessage `json:"securityContext,omitempty"`
+ Security jsontext.Value `json:"securityContext,omitempty"`
NoResource bool `json:"isResourcePrincipalDisabled"`
}
diff --git a/services/ocicore/handler.go b/services/ocicore/handler.go
index 8810659825..9dbc45d914 100644
--- a/services/ocicore/handler.go
+++ b/services/ocicore/handler.go
@@ -2,7 +2,7 @@ package ocicore
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -14,6 +14,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -114,7 +116,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ocicore: encode response: %w", err)
}
@@ -123,9 +125,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -157,7 +157,7 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- if err := json.Unmarshal(q.body, v); err != nil {
+ if err := json.Unmarshal(q.body, v, jsonx.V1()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -179,7 +179,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -248,7 +251,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ocicore/instance.go b/services/ocicore/instance.go
index ca5e1d1c4d..e0bbb8e2b3 100644
--- a/services/ocicore/instance.go
+++ b/services/ocicore/instance.go
@@ -2,10 +2,13 @@ package ocicore
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"regexp"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -39,7 +42,7 @@ type LaunchInstanceIn struct {
ImageID string `json:"imageId"`
Shape string `json:"shape"`
SubnetID string `json:"subnetId"`
- LaunchVolumeAttachments []json.RawMessage `json:"launchVolumeAttachments"`
+ LaunchVolumeAttachments []jsontext.Value `json:"launchVolumeAttachments"`
}
func checkMetadata(md map[string]string, ext map[string]any) *APIError {
@@ -50,7 +53,7 @@ func checkMetadata(md map[string]string, ext map[string]any) *APIError {
}
for k, v := range ext {
- raw, _ := json.Marshal(v)
+ raw, _ := json.Marshal(v, jsonx.V1())
size += len(k) + len(raw)
}
diff --git a/services/ocievents/condition.go b/services/ocievents/condition.go
index 3082564441..f0dec8bc81 100644
--- a/services/ocievents/condition.go
+++ b/services/ocievents/condition.go
@@ -2,11 +2,14 @@ package ocievents
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"slices"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// parseCondition decodes a rule condition: a JSON object whose leaves are strings, numbers, booleans or
@@ -16,15 +19,15 @@ func parseCondition(s string) (map[string]any, *APIError) {
return nil, errMissing("condition is required")
}
- dec := json.NewDecoder(bytes.NewReader([]byte(s)))
- dec.UseNumber()
+ data := []byte(s)
+ dec := jsontext.NewDecoder(bytes.NewReader(data))
var v any
- if err := dec.Decode(&v); err != nil {
+ if err := json.UnmarshalDecode(dec, &v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, errParam("condition is not valid JSON: " + err.Error())
}
- if dec.More() {
+ if rest := bytes.TrimLeft(data[dec.InputOffset():], " \t\r\n"); len(rest) > 0 && rest[0] != ']' && rest[0] != '}' {
return nil, errParam("condition must be a single JSON object")
}
@@ -64,7 +67,7 @@ func validateNode(path string, v any) *APIError {
return errParam(path + ": array elements must be strings, numbers or booleans")
}
}
- case string, json.Number, bool:
+ case string, jsonx.Number, bool:
default:
return errParam(path + ": null is not a valid filter value")
}
@@ -74,7 +77,7 @@ func validateNode(path string, v any) *APIError {
func isScalar(v any) bool {
switch v.(type) {
- case string, json.Number, float64, bool:
+ case string, jsonx.Number, float64, bool:
return true
default:
return false
@@ -85,7 +88,7 @@ func scalarString(v any) string {
switch t := v.(type) {
case string:
return t
- case json.Number:
+ case jsonx.Number:
return t.String()
case float64:
return strconv.FormatFloat(t, 'f', -1, 64)
diff --git a/services/ocievents/delivery_test.go b/services/ocievents/delivery_test.go
index e6c6617746..0078c7be85 100644
--- a/services/ocievents/delivery_test.go
+++ b/services/ocievents/delivery_test.go
@@ -1,7 +1,7 @@
package ocievents_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ocievents "github.com/blackbirdworks/gopherstack/services/ocievents"
ocinotifications "github.com/blackbirdworks/gopherstack/services/ocinotifications"
ociobjectstorage "github.com/blackbirdworks/gopherstack/services/ociobjectstorage"
@@ -167,7 +168,7 @@ func TestFilterMatching(t *testing.T) {
require.Len(t, got, 1)
var env map[string]any
- require.NoError(t, json.Unmarshal([]byte(got[0]), &env))
+ require.NoError(t, json.Unmarshal([]byte(got[0]), &env, jsonx.V1()))
assert.Equal(t, tt.event.EventType, env["eventType"])
assert.Equal(t, "0.1", env["cloudEventsVersion"])
assert.Equal(t, "2.0", env["eventTypeVersion"])
@@ -379,7 +380,7 @@ func (p *pipeline) streamed(t *testing.T) []map[string]any {
for _, m := range msgs {
var env map[string]any
- require.NoError(t, json.Unmarshal(m.Value, &env))
+ require.NoError(t, json.Unmarshal(m.Value, &env, jsonx.V1()))
out = append(out, env)
}
@@ -438,7 +439,7 @@ func TestObjectEventsDelivered(t *testing.T) {
assert.Equal(t, ociobjectstorage.DefaultNamespace, details["namespace"])
var fromFn map[string]any
- require.NoError(t, json.Unmarshal([]byte(invoked[i]), &fromFn))
+ require.NoError(t, json.Unmarshal([]byte(invoked[i]), &fromFn, jsonx.V1()))
assert.Equal(t, streamed[i]["eventID"], fromFn["eventID"])
}
diff --git a/services/ocievents/emit.go b/services/ocievents/emit.go
index b126f77024..7f9d10526e 100644
--- a/services/ocievents/emit.go
+++ b/services/ocievents/emit.go
@@ -2,11 +2,13 @@ package ocievents
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Delivery statuses.
@@ -96,13 +98,13 @@ func buildEnvelope(ev Event, id string) ([]byte, map[string]any, error) {
CompartmentID: ev.CompartmentID, ResourceName: ev.ResourceName, ResourceID: ev.ResourceID,
AvailabilityDomain: ad, AdditionalDetails: details,
},
- })
+ }, jsonx.V1())
if err != nil {
return nil, nil, err //nolint:wrapcheck // plain marshal failure
}
var generic map[string]any
- if err = json.Unmarshal(raw, &generic); err != nil {
+ if err = json.Unmarshal(raw, &generic, jsonx.V1()); err != nil {
return nil, nil, err //nolint:wrapcheck // plain unmarshal failure
}
diff --git a/services/ocievents/handler.go b/services/ocievents/handler.go
index 80c8e93608..93bbce8cbb 100644
--- a/services/ocievents/handler.go
+++ b/services/ocievents/handler.go
@@ -3,7 +3,8 @@ package ocievents
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +17,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -115,7 +118,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ocievents: encode response: %w", err)
}
@@ -124,9 +127,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -154,10 +155,9 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- dec := json.NewDecoder(bytes.NewReader(q.body))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(q.body))
- if err := dec.Decode(v); err != nil {
+ if err := json.UnmarshalDecode(dec, v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -179,7 +179,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -257,7 +260,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ocievents/rules_test.go b/services/ocievents/rules_test.go
index d9e05e0808..77911aed05 100644
--- a/services/ocievents/rules_test.go
+++ b/services/ocievents/rules_test.go
@@ -1,7 +1,7 @@
package ocievents_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,8 @@ import (
"github.com/oracle/oci-go-sdk/v65/events"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestRuleLifecycle(t *testing.T) {
@@ -247,7 +249,7 @@ func TestCreateRuleValidation(t *testing.T) {
m := validRuleBody()
tt.mutate(m)
- raw, err := json.Marshal(m)
+ raw, err := json.Marshal(m, jsonx.V1())
require.NoError(t, err)
status, body := rawDo(t, http.MethodPost, e.base+"/20181201/rules", string(raw))
diff --git a/services/ocifunctions/docker_test.go b/services/ocifunctions/docker_test.go
index 28f4b9b572..1bcbdcde93 100644
--- a/services/ocifunctions/docker_test.go
+++ b/services/ocifunctions/docker_test.go
@@ -1,7 +1,7 @@
package ocifunctions_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"os"
"os/exec"
@@ -16,6 +16,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
"github.com/blackbirdworks/gopherstack/pkgs/gcpserve"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
oci "github.com/blackbirdworks/gopherstack/services/ocifunctions"
)
@@ -148,7 +149,7 @@ func TestDocker_FDKImage(t *testing.T) {
got := map[string]string{}
if ierr == nil && out != "" && resp.RawResponse.StatusCode == http.StatusOK {
- require.NoError(t, json.Unmarshal([]byte(out), &got), out)
+ require.NoError(t, json.Unmarshal([]byte(out), &got, jsonx.V1()), out)
}
return resp.RawResponse, got, ierr
diff --git a/services/ocifunctions/fake_runtime_test.go b/services/ocifunctions/fake_runtime_test.go
index d6259d71e2..8a7630c45d 100644
--- a/services/ocifunctions/fake_runtime_test.go
+++ b/services/ocifunctions/fake_runtime_test.go
@@ -2,7 +2,8 @@ package ocifunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -14,6 +15,7 @@ import (
"sync/atomic"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errNoSocketDir = errors.New("no socket directory mounted at /tmp/iofs")
@@ -99,8 +101,10 @@ func (f *fakeRuntime) handler(id string, spec container.Spec) http.Handler {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Fn-Http-Status", "200")
w.Header().Set("Fn-Http-H-X-Echo", "1")
- _ = json.NewEncoder(w).
- Encode(map[string]any{"body": string(body), "env": specEnv(spec), "path": r.URL.Path})
+ _ = json.MarshalEncode(
+ jsontext.NewEncoder(w, jsonx.V1()),
+ map[string]any{"body": string(body), "env": specEnv(spec), "path": r.URL.Path},
+ )
}
})
}
diff --git a/services/ocifunctions/funcs.go b/services/ocifunctions/funcs.go
index ba0f50f5c0..f48ea409f0 100644
--- a/services/ocifunctions/funcs.go
+++ b/services/ocifunctions/funcs.go
@@ -1,7 +1,7 @@
package ocifunctions
import (
- "encoding/json"
+ "encoding/json/jsontext"
"maps"
"strings"
"time"
@@ -39,8 +39,8 @@ type fnIn struct {
Source *sourceIn `json:"sourceDetails"`
Name string `json:"displayName"`
AppID string `json:"applicationId"`
- Failure json.RawMessage `json:"failureDestination"`
- Success json.RawMessage `json:"successDestination"`
+ Failure jsontext.Value `json:"failureDestination"`
+ Success jsontext.Value `json:"successDestination"`
}
type fnWire struct {
@@ -60,8 +60,8 @@ type fnWire struct {
LifecycleState string `json:"lifecycleState"`
Shape string `json:"shape,omitempty"`
InvokeEndpoint string `json:"invokeEndpoint"`
- Success json.RawMessage `json:"successDestination,omitempty"`
- Failure json.RawMessage `json:"failureDestination,omitempty"`
+ Success jsontext.Value `json:"successDestination,omitempty"`
+ Failure jsontext.Value `json:"failureDestination,omitempty"`
Memory int64 `json:"memoryInMBs"`
Timeout int `json:"timeoutInSeconds"`
}
diff --git a/services/ocifunctions/handler.go b/services/ocifunctions/handler.go
index dad9738a7d..989087c535 100644
--- a/services/ocifunctions/handler.go
+++ b/services/ocifunctions/handler.go
@@ -2,7 +2,7 @@ package ocifunctions
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +16,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -116,7 +118,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ocifunctions: encode response: %w", err)
}
@@ -125,9 +127,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -174,7 +174,7 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- if err := json.Unmarshal(q.body, v); err != nil {
+ if err := json.Unmarshal(q.body, v, jsonx.V1()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -196,7 +196,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -258,7 +261,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ocifunctions/invoke_test.go b/services/ocifunctions/invoke_test.go
index 18435e46e8..ff2803a089 100644
--- a/services/ocifunctions/invoke_test.go
+++ b/services/ocifunctions/invoke_test.go
@@ -2,7 +2,7 @@ package ocifunctions_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
oci "github.com/blackbirdworks/gopherstack/services/ocifunctions"
)
@@ -116,7 +117,7 @@ func TestInvoke_FDKContract(t *testing.T) {
require.NoError(t, err)
var got echo
- require.NoError(t, json.Unmarshal([]byte(out), &got))
+ require.NoError(t, json.Unmarshal([]byte(out), &got, jsonx.V1()))
assert.Equal(t, "hello", got.Body)
assert.Equal(t, "/call", got.Path)
assert.Equal(t, "1", resp.RawResponse.Header.Get("Fn-Http-H-X-Echo"))
diff --git a/services/ocifunctions/models.go b/services/ocifunctions/models.go
index 0babf8141f..25495d094f 100644
--- a/services/ocifunctions/models.go
+++ b/services/ocifunctions/models.go
@@ -1,7 +1,7 @@
package ocifunctions
import (
- "encoding/json"
+ "encoding/json/jsontext"
"maps"
"time"
)
@@ -104,8 +104,8 @@ type Function struct {
Image string `json:"image"`
ImageDigest string `json:"imageDigest,omitempty"`
Etag string `json:"etag"`
- Success json.RawMessage `json:"successDestination,omitempty"`
- Failure json.RawMessage `json:"failureDestination,omitempty"`
+ Success jsontext.Value `json:"successDestination,omitempty"`
+ Failure jsontext.Value `json:"failureDestination,omitempty"`
Memory int64 `json:"memoryInMBs"`
Seq int64 `json:"seq"`
Timeout int `json:"timeoutInSeconds"`
diff --git a/services/ocifunctions/persistence_test.go b/services/ocifunctions/persistence_test.go
index 01acc87b0c..69682631da 100644
--- a/services/ocifunctions/persistence_test.go
+++ b/services/ocifunctions/persistence_test.go
@@ -1,7 +1,7 @@
package ocifunctions_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
oci "github.com/blackbirdworks/gopherstack/services/ocifunctions"
)
@@ -84,10 +85,10 @@ func TestRestoreRejects(t *testing.T) {
app := src.mkApp(t, "app", nil)
body := map[string]any{}
- require.NoError(t, json.Unmarshal(src.handler.Snapshot(t.Context()), &body))
+ require.NoError(t, json.Unmarshal(src.handler.Snapshot(t.Context()), &body, jsonx.V1()))
tt.mutate(body)
- out, err := json.Marshal(body)
+ out, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
dst := newEnv(t, envOpts{})
diff --git a/services/ociidentity/handler.go b/services/ociidentity/handler.go
index fd731fe458..3c00c18ed6 100644
--- a/services/ociidentity/handler.go
+++ b/services/ociidentity/handler.go
@@ -2,7 +2,7 @@ package ociidentity
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -14,6 +14,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -113,7 +115,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ociidentity: encode response: %w", err)
}
@@ -122,9 +124,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -156,7 +156,7 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- if err := json.Unmarshal(q.body, v); err != nil {
+ if err := json.Unmarshal(q.body, v, jsonx.V1()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -178,7 +178,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -240,7 +243,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ociidentity/handler_test.go b/services/ociidentity/handler_test.go
index 154320a3f2..c4f01984e1 100644
--- a/services/ociidentity/handler_test.go
+++ b/services/ociidentity/handler_test.go
@@ -3,7 +3,7 @@ package ociidentity_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
oci "github.com/blackbirdworks/gopherstack/services/ociidentity"
)
@@ -71,7 +72,7 @@ func TestRawErrorEnvelopes(t *testing.T) {
Code string `json:"code"`
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(body, &eb))
+ require.NoError(t, json.Unmarshal(body, &eb, jsonx.V1()))
assert.Equal(t, tt.wantCode, eb.Code)
assert.NotEmpty(t, eb.Message)
})
@@ -87,7 +88,12 @@ func TestEnforcedAuthAndAdminEndpoint(t *testing.T) {
assert.Equal(t, http.StatusUnauthorized, svcErr(t, err).GetHTTPStatusCode())
body, err := json.Marshal(
- map[string]string{"tenancy": testTenancy, "user": "ocid1.user.oc1..bootstrap", "publicKey": e.kp.pubPEM},
+ map[string]string{
+ "tenancy": testTenancy,
+ "user": "ocid1.user.oc1..bootstrap",
+ "publicKey": e.kp.pubPEM,
+ },
+ jsonx.V1(),
)
require.NoError(t, err)
diff --git a/services/ociloadbalancer/handler.go b/services/ociloadbalancer/handler.go
index 92581c3156..942fbce6c3 100644
--- a/services/ociloadbalancer/handler.go
+++ b/services/ociloadbalancer/handler.go
@@ -3,7 +3,8 @@ package ociloadbalancer
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +17,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -116,7 +119,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ociloadbalancer: encode response: %w", err)
}
@@ -125,9 +128,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -162,10 +163,9 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- dec := json.NewDecoder(bytes.NewReader(q.body))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(q.body))
- if err := dec.Decode(v); err != nil {
+ if err := json.UnmarshalDecode(dec, v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -187,7 +187,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -262,7 +265,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ociloadbalancer/store.go b/services/ociloadbalancer/store.go
index baa3fdb44d..9a8889bf26 100644
--- a/services/ociloadbalancer/store.go
+++ b/services/ociloadbalancer/store.go
@@ -4,7 +4,7 @@ import (
"cmp"
"crypto/tls"
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"net/netip"
"slices"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
"github.com/blackbirdworks/gopherstack/pkgs/page"
@@ -164,13 +165,13 @@ func (b *InMemoryBackend) transitionAt() *time.Time {
// clone deep-copies a load balancer; stored load balancers are immutable once published.
func (lb *LoadBalancer) clone() *LoadBalancer {
- raw, err := json.Marshal(lb)
+ raw, err := json.Marshal(lb, jsonx.V1())
if err != nil {
return lb
}
out := &LoadBalancer{}
- if json.Unmarshal(raw, out) != nil {
+ if json.Unmarshal(raw, out, jsonx.V1()) != nil {
return lb
}
diff --git a/services/ocilogging/agents.go b/services/ocilogging/agents.go
index 20744d8a4e..4d44664b5a 100644
--- a/services/ocilogging/agents.go
+++ b/services/ocilogging/agents.go
@@ -2,10 +2,12 @@ package ocilogging
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -28,7 +30,7 @@ type CreateAgentConfigIn struct {
GroupAssoc *groupAssociationWire `json:"groupAssociation"`
Name string `json:"displayName"`
Compartment string `json:"compartmentId"`
- ServiceConfig json.RawMessage `json:"serviceConfiguration"`
+ ServiceConfig jsontext.Value `json:"serviceConfiguration"`
}
// UpdateAgentConfigIn is the UpdateUnifiedAgentConfiguration request body.
@@ -38,7 +40,7 @@ type UpdateAgentConfigIn struct {
Description *string `json:"description"`
Name *string `json:"displayName"`
GroupAssoc *groupAssociationWire `json:"groupAssociation"`
- ServiceConfig json.RawMessage `json:"serviceConfiguration"`
+ ServiceConfig jsontext.Value `json:"serviceConfiguration"`
}
// AgentConfigWire is the GetUnifiedAgentConfiguration response.
@@ -54,7 +56,7 @@ type AgentConfigWire struct {
LifecycleState string `json:"lifecycleState"`
ConfigurationState string `json:"configurationState"`
GroupAssociation groupAssociationWire `json:"groupAssociation"`
- ServiceConfig json.RawMessage `json:"serviceConfiguration"`
+ ServiceConfig jsontext.Value `json:"serviceConfiguration"`
Enabled bool `json:"isEnabled"`
}
@@ -88,7 +90,7 @@ func agentView(a *AgentConfig) AgentConfigWire {
}
// validateServiceConfig checks the polymorphic serviceConfiguration and returns its configurationType.
-func (b *InMemoryBackend) validateServiceConfig(raw json.RawMessage) (string, *APIError) {
+func (b *InMemoryBackend) validateServiceConfig(raw jsontext.Value) (string, *APIError) {
if len(raw) == 0 || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
return "", errMissing("serviceConfiguration is required")
}
@@ -104,7 +106,7 @@ func (b *InMemoryBackend) validateServiceConfig(raw json.RawMessage) (string, *A
} `json:"sources"`
}
- if err := json.Unmarshal(raw, &c); err != nil {
+ if err := json.Unmarshal(raw, &c, jsonx.V1()); err != nil {
return "", errParam("serviceConfiguration is not a valid configuration object")
}
diff --git a/services/ocilogging/handler.go b/services/ocilogging/handler.go
index 897686c2de..4777977912 100644
--- a/services/ocilogging/handler.go
+++ b/services/ocilogging/handler.go
@@ -2,7 +2,7 @@ package ocilogging
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -15,6 +15,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -115,7 +117,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ocilogging: encode response: %w", err)
}
@@ -124,9 +126,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -161,7 +161,7 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- if err := json.Unmarshal(q.body, v); err != nil {
+ if err := json.Unmarshal(q.body, v, jsonx.V1()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -183,7 +183,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -252,7 +255,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ocilogging/ingest.go b/services/ocilogging/ingest.go
index d7a9b7b04a..9a0012d10b 100644
--- a/services/ocilogging/ingest.go
+++ b/services/ocilogging/ingest.go
@@ -1,12 +1,14 @@
package ocilogging
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
"unicode/utf8"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -140,7 +142,7 @@ type ServiceEvent struct {
// EmitServiceLog stores the event in every ACTIVE SERVICE log whose source matches; it returns how many logs took it.
func (b *InMemoryBackend) EmitServiceLog(service, resource, category string, ev ServiceEvent) int {
- data, err := json.Marshal(ev.Data)
+ data, err := json.Marshal(ev.Data, jsonx.V1())
if err != nil {
return 0
}
diff --git a/services/ocilogging/models.go b/services/ocilogging/models.go
index 4021b7813e..c30863401c 100644
--- a/services/ocilogging/models.go
+++ b/services/ocilogging/models.go
@@ -1,7 +1,7 @@
package ocilogging
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -87,21 +87,21 @@ type Log struct {
// AgentConfig is a stored unified agent configuration.
type AgentConfig struct {
- TimeCreated time.Time `json:"timeCreated"`
- TimeModified time.Time `json:"timeModified"`
- TransitionAt *time.Time `json:"transitionAt,omitempty"`
- ServiceConfig json.RawMessage `json:"serviceConfiguration"`
- Tags Tags `json:"tags"`
- ID string `json:"id"`
- CompartmentID string `json:"compartmentId"`
- Name string `json:"displayName"`
- Description string `json:"description,omitempty"`
- State string `json:"state"`
- ConfigType string `json:"configurationType"`
- Etag string `json:"etag"`
- Groups []string `json:"groupList,omitempty"`
- Seq int64 `json:"seq"`
- Enabled bool `json:"isEnabled"`
+ TimeCreated time.Time `json:"timeCreated"`
+ TimeModified time.Time `json:"timeModified"`
+ TransitionAt *time.Time `json:"transitionAt,omitempty"`
+ ServiceConfig jsontext.Value `json:"serviceConfiguration"`
+ Tags Tags `json:"tags"`
+ ID string `json:"id"`
+ CompartmentID string `json:"compartmentId"`
+ Name string `json:"displayName"`
+ Description string `json:"description,omitempty"`
+ State string `json:"state"`
+ ConfigType string `json:"configurationType"`
+ Etag string `json:"etag"`
+ Groups []string `json:"groupList,omitempty"`
+ Seq int64 `json:"seq"`
+ Enabled bool `json:"isEnabled"`
}
// Entry is one ingested log record.
diff --git a/services/ocilogging/persistence_test.go b/services/ocilogging/persistence_test.go
index 7162513235..00de9b90b7 100644
--- a/services/ocilogging/persistence_test.go
+++ b/services/ocilogging/persistence_test.go
@@ -1,7 +1,7 @@
package ocilogging_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
oci "github.com/blackbirdworks/gopherstack/services/ocilogging"
)
@@ -220,7 +221,7 @@ func TestAdminKeyEndpoint(t *testing.T) {
t.Parallel()
e := newEnv(t, envOpts{})
- pub, err := json.Marshal(e.kp.pubPEM)
+ pub, err := json.Marshal(e.kp.pubPEM, jsonx.V1())
require.NoError(t, err)
status, _ := rawDo(t, http.MethodPost, e.base+"/_gopherstack/oci/keys",
diff --git a/services/ocilogging/query.go b/services/ocilogging/query.go
index 2f086be207..c46621e28e 100644
--- a/services/ocilogging/query.go
+++ b/services/ocilogging/query.go
@@ -2,7 +2,7 @@ package ocilogging
import (
"cmp"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"slices"
@@ -10,6 +10,7 @@ import (
"strings"
"unicode"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -624,7 +625,7 @@ func scalarString(v any) string {
return ""
}
- b, _ := json.Marshal(v)
+ b, _ := json.Marshal(v, jsonx.V1())
return string(b)
}
diff --git a/services/ocilogging/search.go b/services/ocilogging/search.go
index 3c5c8e6b6a..9c8c500d3d 100644
--- a/services/ocilogging/search.go
+++ b/services/ocilogging/search.go
@@ -1,13 +1,14 @@
package ocilogging
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
"strconv"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -106,7 +107,7 @@ func (b *InMemoryBackend) buildRow(l *Log, e *Entry) *row {
var data any = e.Data
var parsed any
- if json.Unmarshal([]byte(e.Data), &parsed) == nil {
+ if json.Unmarshal([]byte(e.Data), &parsed, jsonx.V1()) == nil {
if _, isMap := parsed.(map[string]any); isMap {
data = parsed
}
diff --git a/services/ocimonitoring/alarms.go b/services/ocimonitoring/alarms.go
index b3ba39da3a..bb4105bf01 100644
--- a/services/ocimonitoring/alarms.go
+++ b/services/ocimonitoring/alarms.go
@@ -1,7 +1,7 @@
package ocimonitoring
import (
- "encoding/json"
+ "encoding/json/jsontext"
"net/http"
"regexp"
"slices"
@@ -63,57 +63,57 @@ type SuppressionWire struct {
// CreateAlarmIn is the CreateAlarm request body.
type CreateAlarmIn struct {
Tags
- InSubtree *bool `json:"metricCompartmentIdInSubtree"`
- PerDimension *bool `json:"isNotificationsPerMetricDimensionEnabled"`
- Enabled *bool `json:"isEnabled"`
- Suppression *SuppressionWire `json:"suppression"`
- ResourceGroup *string `json:"resourceGroup"`
- Resolution *string `json:"resolution"`
- Pending *string `json:"pendingDuration"`
- Body *string `json:"body"`
- MessageFormat *string `json:"messageFormat"`
- Repeat *string `json:"repeatNotificationDuration"`
- RuleName *string `json:"ruleName"`
- NotifVersion *string `json:"notificationVersion"`
- NotifTitle *string `json:"notificationTitle"`
- EvalSlack *string `json:"evaluationSlackDuration"`
- AlarmSummary *string `json:"alarmSummary"`
- Name string `json:"displayName"`
- Compartment string `json:"compartmentId"`
- MetricComp string `json:"metricCompartmentId"`
- Namespace string `json:"namespace"`
- Query string `json:"query"`
- Severity string `json:"severity"`
- Destinations []string `json:"destinations"`
- Overrides []json.RawMessage `json:"overrides"`
+ InSubtree *bool `json:"metricCompartmentIdInSubtree"`
+ PerDimension *bool `json:"isNotificationsPerMetricDimensionEnabled"`
+ Enabled *bool `json:"isEnabled"`
+ Suppression *SuppressionWire `json:"suppression"`
+ ResourceGroup *string `json:"resourceGroup"`
+ Resolution *string `json:"resolution"`
+ Pending *string `json:"pendingDuration"`
+ Body *string `json:"body"`
+ MessageFormat *string `json:"messageFormat"`
+ Repeat *string `json:"repeatNotificationDuration"`
+ RuleName *string `json:"ruleName"`
+ NotifVersion *string `json:"notificationVersion"`
+ NotifTitle *string `json:"notificationTitle"`
+ EvalSlack *string `json:"evaluationSlackDuration"`
+ AlarmSummary *string `json:"alarmSummary"`
+ Name string `json:"displayName"`
+ Compartment string `json:"compartmentId"`
+ MetricComp string `json:"metricCompartmentId"`
+ Namespace string `json:"namespace"`
+ Query string `json:"query"`
+ Severity string `json:"severity"`
+ Destinations []string `json:"destinations"`
+ Overrides []jsontext.Value `json:"overrides"`
}
// UpdateAlarmIn is the UpdateAlarm request body.
type UpdateAlarmIn struct {
Tags
- Name *string `json:"displayName"`
- Compartment *string `json:"compartmentId"`
- MetricComp *string `json:"metricCompartmentId"`
- InSubtree *bool `json:"metricCompartmentIdInSubtree"`
- Namespace *string `json:"namespace"`
- ResourceGroup *string `json:"resourceGroup"`
- Query *string `json:"query"`
- Resolution *string `json:"resolution"`
- Pending *string `json:"pendingDuration"`
- Severity *string `json:"severity"`
- Body *string `json:"body"`
- PerDimension *bool `json:"isNotificationsPerMetricDimensionEnabled"`
- MessageFormat *string `json:"messageFormat"`
- Destinations []string `json:"destinations"`
- Repeat *string `json:"repeatNotificationDuration"`
- Suppression *SuppressionWire `json:"suppression"`
- Enabled *bool `json:"isEnabled"`
- RuleName *string `json:"ruleName"`
- NotifVersion *string `json:"notificationVersion"`
- NotifTitle *string `json:"notificationTitle"`
- EvalSlack *string `json:"evaluationSlackDuration"`
- AlarmSummary *string `json:"alarmSummary"`
- Overrides []json.RawMessage `json:"overrides"`
+ Name *string `json:"displayName"`
+ Compartment *string `json:"compartmentId"`
+ MetricComp *string `json:"metricCompartmentId"`
+ InSubtree *bool `json:"metricCompartmentIdInSubtree"`
+ Namespace *string `json:"namespace"`
+ ResourceGroup *string `json:"resourceGroup"`
+ Query *string `json:"query"`
+ Resolution *string `json:"resolution"`
+ Pending *string `json:"pendingDuration"`
+ Severity *string `json:"severity"`
+ Body *string `json:"body"`
+ PerDimension *bool `json:"isNotificationsPerMetricDimensionEnabled"`
+ MessageFormat *string `json:"messageFormat"`
+ Destinations []string `json:"destinations"`
+ Repeat *string `json:"repeatNotificationDuration"`
+ Suppression *SuppressionWire `json:"suppression"`
+ Enabled *bool `json:"isEnabled"`
+ RuleName *string `json:"ruleName"`
+ NotifVersion *string `json:"notificationVersion"`
+ NotifTitle *string `json:"notificationTitle"`
+ EvalSlack *string `json:"evaluationSlackDuration"`
+ AlarmSummary *string `json:"alarmSummary"`
+ Overrides []jsontext.Value `json:"overrides"`
}
// AlarmWire is the Alarm response shape.
@@ -145,7 +145,7 @@ type AlarmWire struct {
Repeat string `json:"repeatNotificationDuration,omitempty"`
LifecycleState string `json:"lifecycleState"`
Destinations []string `json:"destinations"`
- Overrides []json.RawMessage `json:"overrides,omitempty"`
+ Overrides []jsontext.Value `json:"overrides,omitempty"`
Enabled bool `json:"isEnabled"`
}
diff --git a/services/ocimonitoring/alarms_test.go b/services/ocimonitoring/alarms_test.go
index 0d7d64e76c..6525e9b923 100644
--- a/services/ocimonitoring/alarms_test.go
+++ b/services/ocimonitoring/alarms_test.go
@@ -1,7 +1,7 @@
package ocimonitoring_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -10,6 +10,8 @@ import (
"github.com/oracle/oci-go-sdk/v65/monitoring"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (e *env) alarmDetails(topic string, mod func(*monitoring.CreateAlarmDetails)) monitoring.CreateAlarmDetails {
@@ -305,7 +307,7 @@ func TestAlarmFiringDeliversToNotifications(t *testing.T) {
} `json:"alarmMetaData"`
}
- require.NoError(t, json.Unmarshal([]byte(msg.body), &n))
+ require.NoError(t, json.Unmarshal([]byte(msg.body), &n, jsonx.V1()))
assert.Equal(t, "OK_TO_FIRING", n.Type)
assert.Equal(t, "CRITICAL", n.Severity)
assert.Equal(t, "cpu-high", n.Title)
@@ -327,7 +329,7 @@ func TestAlarmFiringDeliversToNotifications(t *testing.T) {
Type string `json:"type"`
}
- require.NoError(t, json.Unmarshal([]byte(o.sink.next(t).body), &ok))
+ require.NoError(t, json.Unmarshal([]byte(o.sink.next(t).body), &ok, jsonx.V1()))
assert.Equal(t, "FIRING_TO_OK", ok.Type)
hist, err := e.mon.GetAlarmHistory(t.Context(), monitoring.GetAlarmHistoryRequest{AlarmId: a.Id})
@@ -509,7 +511,7 @@ func TestAlarmGroupedThreshold(t *testing.T) {
} `json:"alarmMetaData"`
}
- require.NoError(t, json.Unmarshal([]byte(o.sink.next(t).body), &n))
+ require.NoError(t, json.Unmarshal([]byte(o.sink.next(t).body), &n, jsonx.V1()))
assert.Equal(t, 1, n.AlarmMetaData[0].TotalMetricsFiring, "only web2 breaches; db1 is filtered out")
assert.Equal(t, []map[string]string{{"host": "web2"}}, n.AlarmMetaData[0].Dimensions)
}
diff --git a/services/ocimonitoring/eval.go b/services/ocimonitoring/eval.go
index dc2b5915c1..19b8134724 100644
--- a/services/ocimonitoring/eval.go
+++ b/services/ocimonitoring/eval.go
@@ -2,12 +2,14 @@ package ocimonitoring
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"slices"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -231,9 +233,9 @@ func renderNotice(a *Alarm, kind string, now time.Time, hits []firing) (string,
)
if a.MessageFormat == formatPretty {
- out, err = json.MarshalIndent(w, "", " ")
+ out, err = json.Marshal(w, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
} else {
- out, err = json.Marshal(w)
+ out, err = json.Marshal(w, jsonx.V1())
}
if err != nil {
diff --git a/services/ocimonitoring/handler.go b/services/ocimonitoring/handler.go
index 0ee1af6e2f..a760cd4499 100644
--- a/services/ocimonitoring/handler.go
+++ b/services/ocimonitoring/handler.go
@@ -2,7 +2,7 @@ package ocimonitoring
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -15,6 +15,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -115,7 +117,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ocimonitoring: encode response: %w", err)
}
@@ -124,9 +126,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -154,7 +154,7 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- if err := json.Unmarshal(q.body, v); err != nil {
+ if err := json.Unmarshal(q.body, v, jsonx.V1()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -176,7 +176,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -245,7 +248,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ocimonitoring/models.go b/services/ocimonitoring/models.go
index fed338bd5f..f3b51796e0 100644
--- a/services/ocimonitoring/models.go
+++ b/services/ocimonitoring/models.go
@@ -1,7 +1,7 @@
package ocimonitoring
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -75,7 +75,7 @@ type Alarm struct {
PerDimension *bool `json:"isNotificationsPerMetricDimensionEnabled,omitempty"`
Tags Tags `json:"tags"`
Extra map[string]string `json:"extra,omitempty"`
- Overrides []json.RawMessage `json:"overrides,omitempty"`
+ Overrides []jsontext.Value `json:"overrides,omitempty"`
ID string `json:"id"`
Name string `json:"displayName"`
CompartmentID string `json:"compartmentId"`
diff --git a/services/ocimonitoring/persistence_test.go b/services/ocimonitoring/persistence_test.go
index 264b0d9624..1f2ec0b94c 100644
--- a/services/ocimonitoring/persistence_test.go
+++ b/services/ocimonitoring/persistence_test.go
@@ -1,7 +1,7 @@
package ocimonitoring_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
oci "github.com/blackbirdworks/gopherstack/services/ocimonitoring"
)
@@ -150,7 +151,7 @@ func TestAdminKeyEndpoint(t *testing.T) {
t.Parallel()
e := newEnv(t, envOpts{})
- pub, err := json.Marshal(e.kp.pubPEM)
+ pub, err := json.Marshal(e.kp.pubPEM, jsonx.V1())
require.NoError(t, err)
status, _ := rawDo(t, http.MethodPost, e.base+"/_gopherstack/oci/keys",
diff --git a/services/ocimysql/channels.go b/services/ocimysql/channels.go
index 5652f02f85..2f79c13220 100644
--- a/services/ocimysql/channels.go
+++ b/services/ocimysql/channels.go
@@ -1,7 +1,7 @@
package ocimysql
import (
- "encoding/json"
+ "encoding/json/jsontext"
"net/http"
"strings"
@@ -28,13 +28,13 @@ type ChannelSourceIn struct {
// ChannelTargetIn is the channel target details.
type ChannelTargetIn struct {
- DelayInSeconds *int `json:"delayInSeconds"`
- TargetType string `json:"targetType"`
- DBSystemID string `json:"dbSystemId"`
- ChannelName string `json:"channelName"`
- ApplierUsername string `json:"applierUsername"`
- TablesWithoutPrimaryKeyHandling string `json:"tablesWithoutPrimaryKeyHandling"`
- Filters json.RawMessage `json:"filters"`
+ DelayInSeconds *int `json:"delayInSeconds"`
+ TargetType string `json:"targetType"`
+ DBSystemID string `json:"dbSystemId"`
+ ChannelName string `json:"channelName"`
+ ApplierUsername string `json:"applierUsername"`
+ TablesWithoutPrimaryKeyHandling string `json:"tablesWithoutPrimaryKeyHandling"`
+ Filters jsontext.Value `json:"filters"`
}
// CreateChannelIn is the CreateChannel body.
diff --git a/services/ocimysql/dbsystems.go b/services/ocimysql/dbsystems.go
index 2ff6784d18..8afb68f2a3 100644
--- a/services/ocimysql/dbsystems.go
+++ b/services/ocimysql/dbsystems.go
@@ -1,7 +1,8 @@
package ocimysql
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"net/http"
"regexp"
@@ -9,6 +10,7 @@ import (
"time"
"unicode"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -64,7 +66,7 @@ type BackupPolicyIn struct {
WindowStartTime *string `json:"windowStartTime"`
RetentionInDays *int `json:"retentionInDays"`
SoftDelete string `json:"softDelete"`
- CopyPolicies json.RawMessage `json:"copyPolicies"`
+ CopyPolicies jsontext.Value `json:"copyPolicies"`
}
// DeletionPolicyIn is the create/update deletion policy.
@@ -89,33 +91,33 @@ type SourceIn struct {
// CreateDBSystemIn is the CreateDBSystem body.
type CreateDBSystemIn struct {
Tags
- Extra map[string]json.RawMessage `json:"-"`
- IsHighlyAvailable *bool `json:"isHighlyAvailable"`
- DataStorageGBs *int `json:"dataStorageSizeInGBs"`
- Port *int `json:"port"`
- PortX *int `json:"portX"`
- DataStorage *DataStorageIn `json:"dataStorage"`
- BackupPolicy *BackupPolicyIn `json:"backupPolicy"`
- Source *SourceIn `json:"source"`
- DeletionPolicy *DeletionPolicyIn `json:"deletionPolicy"`
- Maintenance map[string]any `json:"maintenance"`
- Description string `json:"description"`
- AdminPassword string `json:"adminPassword"`
- DisplayName string `json:"displayName"`
- ShapeName string `json:"shapeName"`
- AvailabilityDom string `json:"availabilityDomain"`
- FaultDomain string `json:"faultDomain"`
- ConfigurationID string `json:"configurationId"`
- MysqlVersion string `json:"mysqlVersion"`
- AdminUsername string `json:"adminUsername"`
- SubnetID string `json:"subnetId"`
- HostnameLabel string `json:"hostnameLabel"`
- IPAddress string `json:"ipAddress"`
- CrashRecovery string `json:"crashRecovery"`
- DBManagement string `json:"databaseManagement"`
- DatabaseMode string `json:"databaseMode"`
- AccessMode string `json:"accessMode"`
- CompartmentID string `json:"compartmentId"`
+ Extra map[string]jsontext.Value `json:"-"`
+ IsHighlyAvailable *bool `json:"isHighlyAvailable"`
+ DataStorageGBs *int `json:"dataStorageSizeInGBs"`
+ Port *int `json:"port"`
+ PortX *int `json:"portX"`
+ DataStorage *DataStorageIn `json:"dataStorage"`
+ BackupPolicy *BackupPolicyIn `json:"backupPolicy"`
+ Source *SourceIn `json:"source"`
+ DeletionPolicy *DeletionPolicyIn `json:"deletionPolicy"`
+ Maintenance map[string]any `json:"maintenance"`
+ Description string `json:"description"`
+ AdminPassword string `json:"adminPassword"`
+ DisplayName string `json:"displayName"`
+ ShapeName string `json:"shapeName"`
+ AvailabilityDom string `json:"availabilityDomain"`
+ FaultDomain string `json:"faultDomain"`
+ ConfigurationID string `json:"configurationId"`
+ MysqlVersion string `json:"mysqlVersion"`
+ AdminUsername string `json:"adminUsername"`
+ SubnetID string `json:"subnetId"`
+ HostnameLabel string `json:"hostnameLabel"`
+ IPAddress string `json:"ipAddress"`
+ CrashRecovery string `json:"crashRecovery"`
+ DBManagement string `json:"databaseManagement"`
+ DatabaseMode string `json:"databaseMode"`
+ AccessMode string `json:"accessMode"`
+ CompartmentID string `json:"compartmentId"`
}
func validPassword(p string) bool {
@@ -906,13 +908,13 @@ func (h *Handler) createDBSystem(q *req) error {
return q.json(http.StatusOK, d, etag)
}
-func extras(body []byte) map[string]json.RawMessage {
- var all map[string]json.RawMessage
- if json.Unmarshal(body, &all) != nil {
+func extras(body []byte) map[string]jsontext.Value {
+ var all map[string]jsontext.Value
+ if json.Unmarshal(body, &all, jsonx.V1()) != nil {
return nil
}
- out := map[string]json.RawMessage{}
+ out := map[string]jsontext.Value{}
for _, k := range extraKeys {
if v, ok := all[k]; ok && string(v) != "null" {
diff --git a/services/ocimysql/handler.go b/services/ocimysql/handler.go
index f81ca587b9..8e1d93291c 100644
--- a/services/ocimysql/handler.go
+++ b/services/ocimysql/handler.go
@@ -2,7 +2,7 @@ package ocimysql
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -15,6 +15,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -115,7 +117,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ocimysql: encode response: %w", err)
}
@@ -124,9 +126,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -163,7 +163,7 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- if err := json.Unmarshal(q.body, v); err != nil {
+ if err := json.Unmarshal(q.body, v, jsonx.V1()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -185,7 +185,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -254,7 +257,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ocimysql/lifecycle.go b/services/ocimysql/lifecycle.go
index b851c375d2..c5db7d2af0 100644
--- a/services/ocimysql/lifecycle.go
+++ b/services/ocimysql/lifecycle.go
@@ -2,7 +2,7 @@ package ocimysql
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"net/http"
"net/netip"
"slices"
@@ -159,29 +159,29 @@ func (b *InMemoryBackend) mutable(id, ifMatch string, states ...string) (*DBSyst
// UpdateDBSystemIn is the UpdateDBSystem body.
type UpdateDBSystemIn struct {
Tags
- DataStorageGBs *int `json:"dataStorageSizeInGBs"`
- HostnameLabel *string `json:"hostnameLabel"`
- SubnetID *string `json:"subnetId"`
- ShapeName *string `json:"shapeName"`
- MysqlVersion *string `json:"mysqlVersion"`
- ConfigID *string `json:"configurationId"`
- AdminUsername *string `json:"adminUsername"`
- Port *int `json:"port"`
- Extra map[string]json.RawMessage `json:"-"`
- IPAddress *string `json:"ipAddress"`
- Description *string `json:"description"`
- HA *bool `json:"isHighlyAvailable"`
- AdminPassword *string `json:"adminPassword"`
- PortX *int `json:"portX"`
- DataStorage *DataStorageIn `json:"dataStorage"`
- BackupPolicy *BackupPolicyIn `json:"backupPolicy"`
- Deletion *DeletionPolicyIn `json:"deletionPolicy"`
- Maintenance map[string]any `json:"maintenance"`
- DisplayName *string `json:"displayName"`
- DBManagement string `json:"databaseManagement"`
- DatabaseMode string `json:"databaseMode"`
- AccessMode string `json:"accessMode"`
- CrashRecovery string `json:"crashRecovery"`
+ DataStorageGBs *int `json:"dataStorageSizeInGBs"`
+ HostnameLabel *string `json:"hostnameLabel"`
+ SubnetID *string `json:"subnetId"`
+ ShapeName *string `json:"shapeName"`
+ MysqlVersion *string `json:"mysqlVersion"`
+ ConfigID *string `json:"configurationId"`
+ AdminUsername *string `json:"adminUsername"`
+ Port *int `json:"port"`
+ Extra map[string]jsontext.Value `json:"-"`
+ IPAddress *string `json:"ipAddress"`
+ Description *string `json:"description"`
+ HA *bool `json:"isHighlyAvailable"`
+ AdminPassword *string `json:"adminPassword"`
+ PortX *int `json:"portX"`
+ DataStorage *DataStorageIn `json:"dataStorage"`
+ BackupPolicy *BackupPolicyIn `json:"backupPolicy"`
+ Deletion *DeletionPolicyIn `json:"deletionPolicy"`
+ Maintenance map[string]any `json:"maintenance"`
+ DisplayName *string `json:"displayName"`
+ DBManagement string `json:"databaseManagement"`
+ DatabaseMode string `json:"databaseMode"`
+ AccessMode string `json:"accessMode"`
+ CrashRecovery string `json:"crashRecovery"`
}
type updateEffect struct {
@@ -228,7 +228,7 @@ func (b *InMemoryBackend) applyUpdate(n *DBSystem, in UpdateDBSystemIn) (updateE
for k, v := range in.Extra {
if n.Extra == nil {
- n.Extra = map[string]json.RawMessage{}
+ n.Extra = map[string]jsontext.Value{}
}
n.Extra[k] = v
diff --git a/services/ocimysql/models.go b/services/ocimysql/models.go
index 10049fd971..400ad67502 100644
--- a/services/ocimysql/models.go
+++ b/services/ocimysql/models.go
@@ -1,7 +1,7 @@
package ocimysql
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -40,13 +40,13 @@ func (t *Tags) merge(in Tags) {
// BackupPolicy is the automatic backup policy of a DB system.
type BackupPolicy struct {
- Tags Tags `json:"tags"`
- WindowStartTime string `json:"windowStartTime"`
- SoftDelete string `json:"softDelete"`
- CopyPolicies json.RawMessage `json:"copyPolicies,omitempty"`
- RetentionInDays int `json:"retentionInDays"`
- IsEnabled bool `json:"isEnabled"`
- PitrEnabled bool `json:"pitrEnabled"`
+ Tags Tags `json:"tags"`
+ WindowStartTime string `json:"windowStartTime"`
+ SoftDelete string `json:"softDelete"`
+ CopyPolicies jsontext.Value `json:"copyPolicies,omitempty"`
+ RetentionInDays int `json:"retentionInDays"`
+ IsEnabled bool `json:"isEnabled"`
+ PitrEnabled bool `json:"pitrEnabled"`
}
// DeletionPolicy controls what DeleteDBSystem does.
@@ -64,48 +64,48 @@ type Source struct {
// DBSystem is a MySQL DB system. AdminPassword and RootSecret are never returned by the API.
type DBSystem struct {
- TimeCreated time.Time `json:"timeCreated"`
- TimeUpdated time.Time `json:"timeUpdated"`
- LastAutoBackup time.Time `json:"lastAutoBackup"`
- Tags Tags `json:"tags"`
- TransitionAt *time.Time `json:"transitionAt,omitempty"`
- Source *Source `json:"source,omitempty"`
- Extra map[string]json.RawMessage `json:"extra,omitempty"`
- Maintenance map[string]any `json:"maintenance,omitempty"`
- FD string `json:"faultDomain"`
- AdminUser string `json:"adminUsername"`
- ID string `json:"id"`
- CompartmentID string `json:"compartmentId"`
- Name string `json:"displayName"`
- Description string `json:"description,omitempty"`
- SubnetID string `json:"subnetId"`
- Shape string `json:"shapeName"`
- Version string `json:"mysqlVersion"`
- ConfigID string `json:"configurationId"`
- Hostname string `json:"hostnameLabel,omitempty"`
- AD string `json:"availabilityDomain"`
- EngineHost string `json:"engineHost,omitempty"`
- State string `json:"state"`
- After string `json:"after,omitempty"`
- Details string `json:"details,omitempty"`
- Etag string `json:"etag"`
- IP string `json:"ipAddress"`
- AdminPass string `json:"adminPassword,omitempty"`
- RootSecret string `json:"rootSecret,omitempty"`
- DatabaseMode string `json:"databaseMode"`
- AccessMode string `json:"accessMode"`
- CrashRecovery string `json:"crashRecovery"`
- DBManagement string `json:"databaseManagement"`
- Deletion DeletionPolicy `json:"deletionPolicy"`
- BackupPolicy BackupPolicy `json:"backupPolicy"`
- DataGB int `json:"dataStorageSizeInGBs"`
- MaxGB int `json:"maxStorageSizeInGBs,omitempty"`
- Port int `json:"port"`
- PortX int `json:"portX"`
- EnginePort int `json:"enginePort,omitempty"`
- Seq int64 `json:"seq"`
- AutoExpand bool `json:"isAutoExpandStorageEnabled"`
- HA bool `json:"isHighlyAvailable"`
+ TimeCreated time.Time `json:"timeCreated"`
+ TimeUpdated time.Time `json:"timeUpdated"`
+ LastAutoBackup time.Time `json:"lastAutoBackup"`
+ Tags Tags `json:"tags"`
+ TransitionAt *time.Time `json:"transitionAt,omitempty"`
+ Source *Source `json:"source,omitempty"`
+ Extra map[string]jsontext.Value `json:"extra,omitempty"`
+ Maintenance map[string]any `json:"maintenance,omitempty"`
+ FD string `json:"faultDomain"`
+ AdminUser string `json:"adminUsername"`
+ ID string `json:"id"`
+ CompartmentID string `json:"compartmentId"`
+ Name string `json:"displayName"`
+ Description string `json:"description,omitempty"`
+ SubnetID string `json:"subnetId"`
+ Shape string `json:"shapeName"`
+ Version string `json:"mysqlVersion"`
+ ConfigID string `json:"configurationId"`
+ Hostname string `json:"hostnameLabel,omitempty"`
+ AD string `json:"availabilityDomain"`
+ EngineHost string `json:"engineHost,omitempty"`
+ State string `json:"state"`
+ After string `json:"after,omitempty"`
+ Details string `json:"details,omitempty"`
+ Etag string `json:"etag"`
+ IP string `json:"ipAddress"`
+ AdminPass string `json:"adminPassword,omitempty"`
+ RootSecret string `json:"rootSecret,omitempty"`
+ DatabaseMode string `json:"databaseMode"`
+ AccessMode string `json:"accessMode"`
+ CrashRecovery string `json:"crashRecovery"`
+ DBManagement string `json:"databaseManagement"`
+ Deletion DeletionPolicy `json:"deletionPolicy"`
+ BackupPolicy BackupPolicy `json:"backupPolicy"`
+ DataGB int `json:"dataStorageSizeInGBs"`
+ MaxGB int `json:"maxStorageSizeInGBs,omitempty"`
+ Port int `json:"port"`
+ PortX int `json:"portX"`
+ EnginePort int `json:"enginePort,omitempty"`
+ Seq int64 `json:"seq"`
+ AutoExpand bool `json:"isAutoExpandStorageEnabled"`
+ HA bool `json:"isHighlyAvailable"`
}
// Backup is a DB system backup. Dump holds the logical dump when the docker engine took it.
@@ -134,24 +134,24 @@ type Backup struct {
// ChannelSource is the replication source of a channel; the password is never stored.
type ChannelSource struct {
- Extra map[string]json.RawMessage `json:"extra,omitempty"`
- SourceType string `json:"sourceType"`
- Hostname string `json:"hostname"`
- Username string `json:"username"`
- SslMode string `json:"sslMode"`
- Port int `json:"port"`
- HasPassword bool `json:"hasPassword"`
+ Extra map[string]jsontext.Value `json:"extra,omitempty"`
+ SourceType string `json:"sourceType"`
+ Hostname string `json:"hostname"`
+ Username string `json:"username"`
+ SslMode string `json:"sslMode"`
+ Port int `json:"port"`
+ HasPassword bool `json:"hasPassword"`
}
// ChannelTarget is the DB system a channel replicates into.
type ChannelTarget struct {
- TargetType string `json:"targetType"`
- DBSystemID string `json:"dbSystemId"`
- ChannelName string `json:"channelName"`
- ApplierUsername string `json:"applierUsername"`
- TablesWithoutPrimaryKeyHandling string `json:"tablesWithoutPrimaryKeyHandling"`
- Filters json.RawMessage `json:"filters,omitempty"`
- DelayInSeconds int `json:"delayInSeconds"`
+ TargetType string `json:"targetType"`
+ DBSystemID string `json:"dbSystemId"`
+ ChannelName string `json:"channelName"`
+ ApplierUsername string `json:"applierUsername"`
+ TablesWithoutPrimaryKeyHandling string `json:"tablesWithoutPrimaryKeyHandling"`
+ Filters jsontext.Value `json:"filters,omitempty"`
+ DelayInSeconds int `json:"delayInSeconds"`
}
// Channel is a replication channel. It records configuration and lifecycle only; no data is replicated.
diff --git a/services/ocinosql/alter.go b/services/ocinosql/alter.go
index f1be37a408..2fc0c31509 100644
--- a/services/ocinosql/alter.go
+++ b/services/ocinosql/alter.go
@@ -1,10 +1,12 @@
package ocinosql
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strconv"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -121,7 +123,7 @@ func checkAddColumn(cols []Column, c Column) *APIError {
return nil
}
-func coerceDefault(ty *dtype, raw json.RawMessage) (any, string) {
+func coerceDefault(ty *dtype, raw jsontext.Value) (any, string) {
v, err := decodeJSON(raw)
if err != nil {
return nil, "is not a valid literal"
@@ -154,7 +156,7 @@ func reshapeRows(t *Table, removed []string, added []Column) {
}
}
- r.Value, _ = json.Marshal(m)
+ r.Value, _ = json.Marshal(m, jsonx.V1())
}
}
@@ -216,9 +218,9 @@ func renderIdentity(id *Identity) string {
return " GENERATED BY DEFAULT AS IDENTITY"
}
-func renderDefault(raw json.RawMessage) string {
+func renderDefault(raw jsontext.Value) string {
var s string
- if json.Unmarshal(raw, &s) == nil {
+ if json.Unmarshal(raw, &s, jsonx.V1()) == nil {
return "'" + strings.ReplaceAll(s, "'", "''") + "'"
}
diff --git a/services/ocinosql/ddl.go b/services/ocinosql/ddl.go
index b48777d1f8..c8efdde147 100644
--- a/services/ocinosql/ddl.go
+++ b/services/ocinosql/ddl.go
@@ -1,11 +1,13 @@
package ocinosql
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"regexp"
"strconv"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -20,12 +22,12 @@ var identRe = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_]*$`)
// Column is one table column.
type Column struct {
- Name string `json:"name"`
- Type string `json:"type"`
- Default json.RawMessage `json:"default,omitempty"`
- NotNull bool `json:"notNull,omitempty"`
- AsUUID bool `json:"asUuid,omitempty"`
- Gen bool `json:"generated,omitempty"`
+ Name string `json:"name"`
+ Type string `json:"type"`
+ Default jsontext.Value `json:"default,omitempty"`
+ NotNull bool `json:"notNull,omitempty"`
+ AsUUID bool `json:"asUuid,omitempty"`
+ Gen bool `json:"generated,omitempty"`
}
// Identity is the table's identity column spec and its counter.
@@ -318,21 +320,21 @@ func (p *parser) parseColumnOption(col *Column, t *dtype, id *Identity) (bool, *
return true, id, nil
}
-func (p *parser) defaultLiteral() (json.RawMessage, *APIError) {
+func (p *parser) defaultLiteral() (jsontext.Value, *APIError) {
t := p.next()
switch {
case t.kind == tkNumber:
- return json.RawMessage(t.text), nil
+ return jsontext.Value(t.text), nil
case t.kind == tkString:
- b, _ := json.Marshal(t.text)
+ b, _ := json.Marshal(t.text, jsonx.V1())
return b, nil
case t.up == kwTrue || t.up == kwFalse:
- return json.RawMessage(strings.ToLower(t.text)), nil
+ return jsontext.Value(strings.ToLower(t.text)), nil
case t.up == kwNull:
- return json.RawMessage("null"), nil
+ return jsontext.Value("null"), nil
case t.text == "-" && p.peek().kind == tkNumber:
- return json.RawMessage("-" + p.next().text), nil
+ return jsontext.Value("-" + p.next().text), nil
}
return nil, errParam("syntax error: DEFAULT must be a number, string, boolean or NULL literal")
diff --git a/services/ocinosql/handler.go b/services/ocinosql/handler.go
index 4f9a1016e1..ee846e70e9 100644
--- a/services/ocinosql/handler.go
+++ b/services/ocinosql/handler.go
@@ -3,7 +3,8 @@ package ocinosql
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +17,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -116,7 +119,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ocinosql: encode response: %w", err)
}
@@ -125,9 +128,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -164,10 +165,9 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- dec := json.NewDecoder(bytes.NewReader(q.body))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(q.body))
- if err := dec.Decode(v); err != nil {
+ if err := json.UnmarshalDecode(dec, v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -189,7 +189,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -258,7 +261,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ocinosql/limits.go b/services/ocinosql/limits.go
index 5ba7ab51f7..8869fe6d6c 100644
--- a/services/ocinosql/limits.go
+++ b/services/ocinosql/limits.go
@@ -1,8 +1,9 @@
package ocinosql
import (
- "encoding/json"
"strconv"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Service limits from Oracle's "Oracle NoSQL Database Cloud Service Limits"
@@ -18,7 +19,7 @@ func valueBytes(v any) int {
switch x := v.(type) {
case string:
return len(x)
- case json.Number:
+ case jsonx.Number:
return len(x.String())
case map[string]any:
n := 0
diff --git a/services/ocinosql/models.go b/services/ocinosql/models.go
index 0c45a77e8d..7fb5196c81 100644
--- a/services/ocinosql/models.go
+++ b/services/ocinosql/models.go
@@ -1,7 +1,7 @@
package ocinosql
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -46,9 +46,9 @@ type Limits struct {
// Row is one stored table row.
type Row struct {
- Expire *time.Time `json:"expire,omitempty"`
- Version string `json:"version"`
- Value json.RawMessage `json:"value"`
+ Expire *time.Time `json:"expire,omitempty"`
+ Version string `json:"version"`
+ Value jsontext.Value `json:"value"`
}
// IndexKey is one key of a secondary index.
diff --git a/services/ocinosql/rows.go b/services/ocinosql/rows.go
index ef5c06e37f..6be2183f69 100644
--- a/services/ocinosql/rows.go
+++ b/services/ocinosql/rows.go
@@ -3,11 +3,13 @@ package ocinosql
import (
"encoding/base64"
"encoding/binary"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strconv"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -35,26 +37,26 @@ type usageWire struct {
// UpdateRowResult is the UpdateRow response body.
type UpdateRowResult struct {
- Version *string `json:"version"`
- ExistingVersion *string `json:"existingVersion,omitempty"`
- GeneratedValue *string `json:"generatedValue,omitempty"`
- ExistingValue json.RawMessage `json:"existingValue,omitempty"`
- Usage usageWire `json:"usage"`
+ Version *string `json:"version"`
+ ExistingVersion *string `json:"existingVersion,omitempty"`
+ GeneratedValue *string `json:"generatedValue,omitempty"`
+ ExistingValue jsontext.Value `json:"existingValue,omitempty"`
+ Usage usageWire `json:"usage"`
}
// RowResult is the GetRow response body.
type RowResult struct {
- TimeOfExpiration *time.Time `json:"timeOfExpiration,omitempty"`
- Value json.RawMessage `json:"value"`
- Usage usageWire `json:"usage"`
+ TimeOfExpiration *time.Time `json:"timeOfExpiration,omitempty"`
+ Value jsontext.Value `json:"value"`
+ Usage usageWire `json:"usage"`
}
// DeleteRowResult is the DeleteRow response body.
type DeleteRowResult struct {
- ExistingVersion *string `json:"existingVersion,omitempty"`
- ExistingValue json.RawMessage `json:"existingValue,omitempty"`
- IsSuccess bool `json:"isSuccess"`
- Usage usageWire `json:"usage"`
+ ExistingVersion *string `json:"existingVersion,omitempty"`
+ ExistingValue jsontext.Value `json:"existingValue,omitempty"`
+ IsSuccess bool `json:"isSuccess"`
+ Usage usageWire `json:"usage"`
}
func unitsFor(size int) int { return max(1, (size+bytesPerUnit-1)/bytesPerUnit) }
@@ -189,7 +191,7 @@ func (t *Table) generateIdentity(col string, out map[string]any, given map[strin
v := strconv.FormatInt(id.Next, 10)
id.Next += id.Increment
- out[col], given[col] = json.Number(v), true
+ out[col], given[col] = jsonx.Number(v), true
return &v, nil
}
@@ -293,7 +295,7 @@ func (b *InMemoryBackend) UpdateRow(ref, ifMatch string, in updateRowIn) (Update
return b.refusedPut(t, existing, in, now), nil
}
- raw, _ := json.Marshal(row)
+ raw, _ := json.Marshal(row, jsonx.V1())
t.Rows[key] = &Row{Value: raw, Version: t.nextVersion(), Expire: expire}
w := unitsFor(len(raw))
t.record(now, 0, w)
@@ -346,7 +348,7 @@ func parseKey(t *Table, parts []string) (map[string]any, *APIError) {
var raw any = val
if sv.types[i].isNumeric() {
- raw = json.Number(val)
+ raw = jsonx.Number(val)
}
n, msg := coerce(sv.types[i], raw)
diff --git a/services/ocinosql/sqlexec.go b/services/ocinosql/sqlexec.go
index a2479f3f5f..849b559911 100644
--- a/services/ocinosql/sqlexec.go
+++ b/services/ocinosql/sqlexec.go
@@ -1,12 +1,12 @@
package ocinosql
import (
- "encoding/json"
"slices"
"strconv"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -373,7 +373,7 @@ func (pl *plan) sortEntries(es []entry) {
func (pl *plan) project(es []entry) []map[string]any {
st := pl.stmt
if st.count {
- return []map[string]any{{"Column_1": json.Number(strconv.Itoa(len(es)))}}
+ return []map[string]any{{"Column_1": jsonx.Number(strconv.Itoa(len(es)))}}
}
out := make([]map[string]any, 0, len(es))
diff --git a/services/ocinosql/sqlparse.go b/services/ocinosql/sqlparse.go
index 0ce8cc9d24..bff8b6e844 100644
--- a/services/ocinosql/sqlparse.go
+++ b/services/ocinosql/sqlparse.go
@@ -1,9 +1,10 @@
package ocinosql
import (
- "encoding/json"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type step struct {
@@ -493,7 +494,7 @@ func (p *parser) parseOperand() (expr, *APIError) {
return nil, p.fail("a number")
}
- return &litExpr{v: json.Number("-" + n.text)}, nil
+ return &litExpr{v: jsonx.Number("-" + n.text)}, nil
case t.kind == tkIdent && !isLiteralKw(t.up):
steps, err := p.parsePath()
if err != nil {
@@ -518,7 +519,7 @@ func isLiteralKw(up string) bool { return up == kwTrue || up == kwFalse || up ==
func literalOf(t tok) (expr, bool) {
switch {
case t.kind == tkNumber:
- return &litExpr{v: json.Number(t.text)}, true
+ return &litExpr{v: jsonx.Number(t.text)}, true
case t.kind == tkString:
return &litExpr{v: t.text}, true
case t.kind == tkVar:
diff --git a/services/ocinosql/tables.go b/services/ocinosql/tables.go
index 35b6d094a0..83c4d024c3 100644
--- a/services/ocinosql/tables.go
+++ b/services/ocinosql/tables.go
@@ -1,13 +1,14 @@
package ocinosql
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"slices"
"strconv"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -369,7 +370,7 @@ func schemaView2(t *Table) *schemaWire {
if len(c.Default) > 0 {
var s string
- if json.Unmarshal(c.Default, &s) != nil {
+ if json.Unmarshal(c.Default, &s, jsonx.V1()) != nil {
s = string(c.Default)
}
diff --git a/services/ocinosql/values.go b/services/ocinosql/values.go
index 646010f0f0..4d780f2aa5 100644
--- a/services/ocinosql/values.go
+++ b/services/ocinosql/values.go
@@ -3,7 +3,8 @@ package ocinosql
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"math"
"math/big"
@@ -13,6 +14,7 @@ import (
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/strs"
)
@@ -46,18 +48,17 @@ const (
)
func decodeJSON(raw []byte) (any, error) {
- dec := json.NewDecoder(bytes.NewReader(raw))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(raw))
var v any
- if err := dec.Decode(&v); err != nil {
+ if err := json.UnmarshalDecode(dec, &v, jsonx.V1(), jsonx.UseNumber()); err != nil {
return nil, fmt.Errorf("decode json: %w", err)
}
return v, nil
}
-func decodeRow(raw json.RawMessage) map[string]any {
+func decodeRow(raw jsontext.Value) map[string]any {
v, err := decodeJSON(raw)
if err != nil {
return map[string]any{}
@@ -69,7 +70,7 @@ func decodeRow(raw json.RawMessage) map[string]any {
}
func coerceInt(v any, lo, hi int64, kind string) (any, string) {
- n, ok := v.(json.Number)
+ n, ok := v.(jsonx.Number)
if !ok {
return nil, "is not a valid " + kind
}
@@ -93,7 +94,7 @@ func coerceInt(v any, lo, hi int64, kind string) (any, string) {
return nil, "is out of range for " + kind
}
- return json.Number(strconv.FormatInt(i, 10)), ""
+ return jsonx.Number(strconv.FormatInt(i, 10)), ""
}
func parseTimestamp(v any) (time.Time, bool) {
@@ -106,7 +107,7 @@ func parseTimestamp(v any) (time.Time, bool) {
return t.UTC(), true
}
}
- case json.Number:
+ case jsonx.Number:
if ms, err := strconv.ParseInt(x.String(), 10, 64); err == nil {
return time.UnixMilli(ms).UTC(), true
}
@@ -149,7 +150,7 @@ func coerce(t *dtype, v any) (any, string) {
func coerceScalar(t *dtype, v any) (any, string) {
switch t.kind {
case kNumber:
- n, ok := v.(json.Number)
+ n, ok := v.(jsonx.Number)
if _, rok := new(big.Rat).SetString(n.String()); !ok || !rok {
return nil, "is not a valid NUMBER"
}
@@ -263,7 +264,7 @@ func coerceRecord(t *dtype, v any) (any, string) {
return out, ""
}
-func cmpNum(a, b json.Number) (int, bool) {
+func cmpNum(a, b jsonx.Number) (int, bool) {
ra, ok1 := new(big.Rat).SetString(a.String())
rb, ok2 := new(big.Rat).SetString(b.String())
@@ -277,8 +278,8 @@ func cmpNum(a, b json.Number) (int, bool) {
// compareVals orders two scalar values of the same family; ok is false when they are not comparable.
func compareVals(a, b any) (int, bool) {
switch x := a.(type) {
- case json.Number:
- if y, ok := b.(json.Number); ok {
+ case jsonx.Number:
+ if y, ok := b.(jsonx.Number); ok {
return cmpNum(x, y)
}
case string:
@@ -302,7 +303,7 @@ func compareVals(a, b any) (int, bool) {
}
func keyPart(v any) string {
- if n, ok := v.(json.Number); ok {
+ if n, ok := v.(jsonx.Number); ok {
if r, rok := new(big.Rat).SetString(n.String()); rok {
return r.RatString()
}
@@ -326,7 +327,7 @@ func rowKey(pk []string, row map[string]any) (string, *APIError) {
parts[i] = keyPart(v)
}
- b, _ := json.Marshal(parts) //nolint:errchkjson // []string cannot fail
+ b, _ := json.Marshal(parts, jsonx.V1()) //nolint:errchkjson // []string cannot fail
return string(b), nil
}
@@ -340,7 +341,7 @@ func validUUID(s string) bool {
}
func coerceFloat(kind string, v any) (any, string) {
- n, ok := v.(json.Number)
+ n, ok := v.(jsonx.Number)
if !ok {
return nil, "is not a valid " + kind
}
@@ -350,5 +351,5 @@ func coerceFloat(kind string, v any) (any, string) {
return nil, "is not a valid " + kind
}
- return json.Number(strconv.FormatFloat(f, 'g', -1, 64)), ""
+ return jsonx.Number(strconv.FormatFloat(f, 'g', -1, 64)), ""
}
diff --git a/services/ocinotifications/handler.go b/services/ocinotifications/handler.go
index 9e9d2dc97c..713514fdd5 100644
--- a/services/ocinotifications/handler.go
+++ b/services/ocinotifications/handler.go
@@ -2,7 +2,7 @@ package ocinotifications
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +16,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -117,7 +119,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ocinotifications: encode response: %w", err)
}
@@ -126,9 +128,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -168,7 +168,7 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- if err := json.Unmarshal(q.body, v); err != nil {
+ if err := json.Unmarshal(q.body, v, jsonx.V1()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -190,7 +190,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -278,7 +281,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ocinotifications/publish.go b/services/ocinotifications/publish.go
index 9418c1535e..87b4cec7ef 100644
--- a/services/ocinotifications/publish.go
+++ b/services/ocinotifications/publish.go
@@ -3,7 +3,8 @@ package ocinotifications
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -14,6 +15,8 @@ import (
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -116,7 +119,7 @@ func (b *InMemoryBackend) queueConfirmation(s *Subscription, t *Topic) {
"SubscriptionId": s.ID, "ConfirmationURL": d.ConfirmationURL,
"Message": "You have chosen to subscribe to the topic " + t.Name +
". To confirm the subscription, visit the ConfirmationURL included in this message.",
- })
+ }, jsonx.V1())
d.Body = string(body)
d.Title = "Confirm your subscription to " + t.Name
b.route(d)
@@ -154,7 +157,7 @@ func (b *InMemoryBackend) PublishMessage(topicID, messageType string, in Publish
return PublishWire{}, errParam("a message is limited to 64 KB")
}
- if messageType == msgJSON && !json.Valid([]byte(*in.Body)) {
+ if messageType == msgJSON && !jsontext.Value([]byte(*in.Body)).IsValid(jsonx.V1()) {
return PublishWire{}, errParam("body must be valid JSON when messageType is JSON")
}
diff --git a/services/ocinotifications/subs.go b/services/ocinotifications/subs.go
index de6576d716..e12d9fbf5d 100644
--- a/services/ocinotifications/subs.go
+++ b/services/ocinotifications/subs.go
@@ -1,7 +1,7 @@
package ocinotifications
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/mail"
"net/url"
@@ -13,6 +13,7 @@ import (
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -104,7 +105,7 @@ func policyOf(s *Subscription) deliveryPolicyWire {
}
func subView(s *Subscription) SubWire {
- pol, _ := json.Marshal(policyOf(s)) //nolint:errchkjson // static shape
+ pol, _ := json.Marshal(policyOf(s), jsonx.V1()) //nolint:errchkjson // static shape
return SubWire{
ID: s.ID,
diff --git a/services/ociobjectstorage/auth_test.go b/services/ociobjectstorage/auth_test.go
index 94450b9ab6..94c243c587 100644
--- a/services/ociobjectstorage/auth_test.go
+++ b/services/ociobjectstorage/auth_test.go
@@ -1,7 +1,7 @@
package ociobjectstorage_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -114,14 +115,17 @@ func TestAdminKeyRegistration(t *testing.T) {
_, err := e.client.GetNamespace(t.Context(), objectstorage.GetNamespaceRequest{})
assert.Equal(t, 401, svcErr(t, err).GetHTTPStatusCode())
- payload, _ := json.Marshal(map[string]string{"tenancy": testTenancy, "user": testUser, "publicKey": e.keys.pubPEM})
+ payload, _ := json.Marshal(
+ map[string]string{"tenancy": testTenancy, "user": testUser, "publicKey": e.keys.pubPEM},
+ jsonx.V1(),
+ )
code, body := rawDo(t, http.MethodPost, e.baseURL+"/_gopherstack/oci/keys", string(payload))
require.Equal(t, http.StatusCreated, code, body)
var out struct {
KeyID string `json:"keyId"`
}
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
assert.Equal(t, testTenancy+"/"+testUser+"/"+e.keys.fprint, out.KeyID)
resp, err := e.client.GetNamespace(t.Context(), objectstorage.GetNamespaceRequest{})
diff --git a/services/ociobjectstorage/handler.go b/services/ociobjectstorage/handler.go
index a7a60b6e8f..0861f616f8 100644
--- a/services/ociobjectstorage/handler.go
+++ b/services/ociobjectstorage/handler.go
@@ -2,7 +2,7 @@ package ociobjectstorage
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -13,6 +13,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -112,18 +114,17 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ociobjectstorage: encode response: %w", err)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
-
- if q.r.Method != http.MethodHead {
- _, _ = q.w.Write(out)
+ if q.r.Method == http.MethodHead {
+ out = nil
}
+ httputils.WriteJSONBytes(q.w, status, out)
+
return nil
}
@@ -151,7 +152,10 @@ func (h *Handler) fail(q *req, err error) {
q.w.WriteHeader(ae.Status)
if q.r.Method != http.MethodHead {
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
}
@@ -268,7 +272,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ociobjectstorage/ops.go b/services/ociobjectstorage/ops.go
index 7243b1315d..7659a5f2b6 100644
--- a/services/ociobjectstorage/ops.go
+++ b/services/ociobjectstorage/ops.go
@@ -1,11 +1,13 @@
package ociobjectstorage
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func buildOps() map[string]opFunc {
@@ -54,7 +56,7 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- if err := json.Unmarshal(q.body, v); err != nil {
+ if err := json.Unmarshal(q.body, v, jsonx.V1()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
diff --git a/services/ociqueue/handler.go b/services/ociqueue/handler.go
index 62a3e9dcfc..a97c4c777f 100644
--- a/services/ociqueue/handler.go
+++ b/services/ociqueue/handler.go
@@ -2,7 +2,7 @@ package ociqueue
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +16,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -116,7 +118,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ociqueue: encode response: %w", err)
}
@@ -125,9 +127,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -174,7 +174,7 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- if err := json.Unmarshal(q.body, v); err != nil {
+ if err := json.Unmarshal(q.body, v, jsonx.V1()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -196,7 +196,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -265,7 +268,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ociqueue/helpers_test.go b/services/ociqueue/helpers_test.go
index bde7f5c137..60b9f673b3 100644
--- a/services/ociqueue/helpers_test.go
+++ b/services/ociqueue/helpers_test.go
@@ -5,7 +5,7 @@ import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"fmt"
"io"
@@ -19,6 +19,7 @@ import (
"github.com/oracle/oci-go-sdk/v65/queue"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
@@ -223,7 +224,7 @@ func get(t *testing.T, c queue.QueueClient, q queue.Queue, mod func(*queue.GetMe
}
func quote(s string) string {
- b, _ := json.Marshal(s)
+ b, _ := json.Marshal(s, jsonx.V1())
return string(b)
}
diff --git a/services/ociqueue/queues.go b/services/ociqueue/queues.go
index 99019f4c11..95b81da2e2 100644
--- a/services/ociqueue/queues.go
+++ b/services/ociqueue/queues.go
@@ -1,7 +1,7 @@
package ociqueue
import (
- "encoding/json"
+ "encoding/json/jsontext"
"net/http"
"slices"
"strconv"
@@ -27,27 +27,27 @@ const (
// CreateQueueIn is the CreateQueue request body.
type CreateQueueIn struct {
Tags
- Retention *int `json:"retentionInSeconds"`
- Visibility *int `json:"visibilityInSeconds"`
- Timeout *int `json:"timeoutInSeconds"`
- ChannelLimit *int `json:"channelConsumptionLimit"`
- DLQCount *int `json:"deadLetterQueueDeliveryCount"`
- KMSKeyID *string `json:"customEncryptionKeyId"`
- Name string `json:"displayName"`
- Compartment string `json:"compartmentId"`
- Capabilities []json.RawMessage `json:"capabilities"`
+ Retention *int `json:"retentionInSeconds"`
+ Visibility *int `json:"visibilityInSeconds"`
+ Timeout *int `json:"timeoutInSeconds"`
+ ChannelLimit *int `json:"channelConsumptionLimit"`
+ DLQCount *int `json:"deadLetterQueueDeliveryCount"`
+ KMSKeyID *string `json:"customEncryptionKeyId"`
+ Name string `json:"displayName"`
+ Compartment string `json:"compartmentId"`
+ Capabilities []jsontext.Value `json:"capabilities"`
}
// UpdateQueueIn is the UpdateQueue request body.
type UpdateQueueIn struct {
Tags
- Name *string `json:"displayName"`
- Visibility *int `json:"visibilityInSeconds"`
- Timeout *int `json:"timeoutInSeconds"`
- ChannelLimit *int `json:"channelConsumptionLimit"`
- DLQCount *int `json:"deadLetterQueueDeliveryCount"`
- KMSKeyID *string `json:"customEncryptionKeyId"`
- Capabilities []json.RawMessage `json:"capabilities"`
+ Name *string `json:"displayName"`
+ Visibility *int `json:"visibilityInSeconds"`
+ Timeout *int `json:"timeoutInSeconds"`
+ ChannelLimit *int `json:"channelConsumptionLimit"`
+ DLQCount *int `json:"deadLetterQueueDeliveryCount"`
+ KMSKeyID *string `json:"customEncryptionKeyId"`
+ Capabilities []jsontext.Value `json:"capabilities"`
}
// PurgeIn is the PurgeQueue request body.
@@ -131,7 +131,7 @@ func firstErr(errs ...*APIError) *APIError {
return nil
}
-func checkCapabilities(c []json.RawMessage) *APIError {
+func checkCapabilities(c []jsontext.Value) *APIError {
if len(c) > 0 {
return errParam("capabilities are not supported by this emulator")
}
diff --git a/services/ocistreaming/cursors.go b/services/ocistreaming/cursors.go
index 8398e57860..4e39d60a55 100644
--- a/services/ocistreaming/cursors.go
+++ b/services/ocistreaming/cursors.go
@@ -2,7 +2,7 @@ package ocistreaming
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"strconv"
@@ -10,6 +10,8 @@ import (
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -39,7 +41,7 @@ type cursorTok struct {
}
func (c cursorTok) encode() string {
- raw, _ := json.Marshal(c) //nolint:errchkjson // static shape
+ raw, _ := json.Marshal(c, jsonx.V1()) //nolint:errchkjson // static shape
return base64.RawURLEncoding.EncodeToString(raw)
}
@@ -48,7 +50,7 @@ func decodeCursor(streamID, v string) (cursorTok, error) {
var c cursorTok
raw, err := base64.RawURLEncoding.DecodeString(v)
- if err != nil || json.Unmarshal(raw, &c) != nil || c.Stream != streamID {
+ if err != nil || json.Unmarshal(raw, &c, jsonx.V1()) != nil || c.Stream != streamID {
return c, errParam("The cursor is invalid or does not belong to this stream")
}
diff --git a/services/ocistreaming/handler.go b/services/ocistreaming/handler.go
index ad3f717cba..16e9a24e15 100644
--- a/services/ocistreaming/handler.go
+++ b/services/ocistreaming/handler.go
@@ -2,7 +2,7 @@ package ocistreaming
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +16,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -116,7 +118,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ocistreaming: encode response: %w", err)
}
@@ -125,9 +127,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -174,7 +174,7 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- if err := json.Unmarshal(q.body, v); err != nil {
+ if err := json.Unmarshal(q.body, v, jsonx.V1()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -196,7 +196,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -258,7 +261,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ocistreaming/service_test.go b/services/ocistreaming/service_test.go
index 935e5ecdef..052cc03a6d 100644
--- a/services/ocistreaming/service_test.go
+++ b/services/ocistreaming/service_test.go
@@ -2,7 +2,7 @@ package ocistreaming_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -95,7 +96,10 @@ func TestAdminKeyRegistration(t *testing.T) {
_, err := e.admin.ListStreamPools(t.Context(), req)
assert.Equal(t, http.StatusUnauthorized, svcErr(t, err).GetHTTPStatusCode())
- payload, _ := json.Marshal(map[string]string{"tenancy": testTenancy, "user": testUser, "publicKey": e.kp.pubPEM})
+ payload, _ := json.Marshal(
+ map[string]string{"tenancy": testTenancy, "user": testUser, "publicKey": e.kp.pubPEM},
+ jsonx.V1(),
+ )
code, body := rawDo(t, http.MethodPost, e.base+"/_gopherstack/oci/keys", string(payload))
require.Equal(t, http.StatusCreated, code, body)
diff --git a/services/ocivault/handler.go b/services/ocivault/handler.go
index 8523e4ebdd..0689cff9a4 100644
--- a/services/ocivault/handler.go
+++ b/services/ocivault/handler.go
@@ -2,7 +2,7 @@ package ocivault
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +16,8 @@ import (
"github.com/google/uuid"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/ociauth"
@@ -115,7 +117,7 @@ type errorBody struct {
func (q *req) header(k, v string) { q.w.Header().Set(k, v) }
func (q *req) json(status int, v any, etag string) error {
- out, err := json.Marshal(v)
+ out, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("ocivault: encode response: %w", err)
}
@@ -124,9 +126,7 @@ func (q *req) json(status int, v any, etag string) error {
q.header("etag", etag)
}
- q.header("Content-Type", "application/json")
- q.w.WriteHeader(status)
- _, _ = q.w.Write(out)
+ httputils.WriteJSONBytes(q.w, status, out)
return nil
}
@@ -173,7 +173,7 @@ func (q *req) decode(v any) error {
return errMissing("A request body is required")
}
- if err := json.Unmarshal(q.body, v); err != nil {
+ if err := json.Unmarshal(q.body, v, jsonx.V1()); err != nil {
return newErr(
http.StatusBadRequest,
"CannotParseRequest",
@@ -195,7 +195,10 @@ func (h *Handler) fail(q *req, err error) {
q.header("Content-Type", "application/json")
q.w.WriteHeader(ae.Status)
- out, _ := json.Marshal(errorBody{Code: ae.Code, Message: ae.Message}) //nolint:errchkjson // static shape
+ out, _ := json.Marshal(
+ errorBody{Code: ae.Code, Message: ae.Message},
+ jsonx.V1(),
+ ) //nolint:errchkjson // static shape
_, _ = q.w.Write(out)
}
@@ -257,7 +260,7 @@ func (h *Handler) adminKeys(q *req) {
var in adminKeyReq
body, _ := io.ReadAll(io.LimitReader(q.r.Body, adminBodyLimit))
- if err := json.Unmarshal(body, &in); err != nil || in.Tenancy == "" || in.User == "" {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil || in.Tenancy == "" || in.User == "" {
h.fail(q, errParam("tenancy, user and publicKey are required"))
return
diff --git a/services/ocivault/handler_test.go b/services/ocivault/handler_test.go
index 4e5db40ae3..ad23de5ad4 100644
--- a/services/ocivault/handler_test.go
+++ b/services/ocivault/handler_test.go
@@ -2,7 +2,7 @@ package ocivault_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"strings"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -112,7 +113,7 @@ func TestPathVaultRouting(t *testing.T) {
VaultID string `json:"vaultId"`
}
- require.NoError(t, json.Unmarshal([]byte(out), &k))
+ require.NoError(t, json.Unmarshal([]byte(out), &k, jsonx.V1()))
assert.Equal(t, *v.Id, k.VaultID)
code, out = rawDo(t, http.MethodGet, e.base+"/_vault/"+*v.Id+"/20180608/keys?compartmentId="+testCompartment, "")
@@ -161,7 +162,7 @@ func TestErrorBodyShapeAndUnknownRoutes(t *testing.T) {
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal([]byte(out), &eb))
+ require.NoError(t, json.Unmarshal([]byte(out), &eb, jsonx.V1()))
assert.Equal(t, tt.wantCode, eb.Code)
assert.NotEmpty(t, eb.Message)
assert.NotEqual(t, eb.Code, eb.Message)
@@ -234,7 +235,10 @@ func TestAdminKeyRegistration(t *testing.T) {
_, err := e.vaults.ListVaults(t.Context(), listReq)
assert.Equal(t, http.StatusUnauthorized, svcErr(t, err).GetHTTPStatusCode())
- payload, _ := json.Marshal(map[string]string{"tenancy": testTenancy, "user": testUser, "publicKey": e.kp.pubPEM})
+ payload, _ := json.Marshal(
+ map[string]string{"tenancy": testTenancy, "user": testUser, "publicKey": e.kp.pubPEM},
+ jsonx.V1(),
+ )
code, body := rawDo(t, http.MethodPost, e.base+"/_gopherstack/oci/keys", string(payload))
require.Equal(t, http.StatusCreated, code, body)
diff --git a/services/ocivault/models.go b/services/ocivault/models.go
index 6fcbc68a31..60af20ab9d 100644
--- a/services/ocivault/models.go
+++ b/services/ocivault/models.go
@@ -1,7 +1,7 @@
package ocivault
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -106,7 +106,7 @@ type Secret struct {
Description string `json:"description,omitempty"`
State string `json:"state"`
Etag string `json:"etag"`
- Rules []json.RawMessage `json:"rules,omitempty"`
+ Rules []jsontext.Value `json:"rules,omitempty"`
Versions []*SecretVersion `json:"versions"`
Seq int64 `json:"seq"`
Current int64 `json:"current"`
diff --git a/services/ocivault/seal.go b/services/ocivault/seal.go
index c8851b7ec5..fb6cf32bee 100644
--- a/services/ocivault/seal.go
+++ b/services/ocivault/seal.go
@@ -12,8 +12,10 @@ import (
_ "crypto/sha512" // registers SHA-384/512 for crypto.Hash.New
"encoding/base64"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"hash"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -51,7 +53,7 @@ func aadBytes(aad map[string]string) []byte {
return nil
}
- b, _ := json.Marshal(aad) //nolint:errchkjson // map[string]string always marshals; keys are sorted
+ b, _ := json.Marshal(aad, jsonx.V1()) //nolint:errchkjson // map[string]string always marshals; keys are sorted
return b
}
diff --git a/services/ocivault/secrets.go b/services/ocivault/secrets.go
index fce0df635b..3c55f64207 100644
--- a/services/ocivault/secrets.go
+++ b/services/ocivault/secrets.go
@@ -3,13 +3,15 @@ package ocivault
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"regexp"
"slices"
"strconv"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/iso8601"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -37,16 +39,16 @@ type CreateSecretIn struct {
DefinedTags map[string]map[string]any `json:"definedTags"`
Metadata map[string]any `json:"metadata"`
SecretContent *SecretContent `json:"secretContent"`
- ReplicationConfig json.RawMessage `json:"replicationConfig"`
- RotationConfig json.RawMessage `json:"rotationConfig"`
- SecretGenerationContext json.RawMessage `json:"secretGenerationContext"`
+ ReplicationConfig jsontext.Value `json:"replicationConfig"`
+ RotationConfig jsontext.Value `json:"rotationConfig"`
+ SecretGenerationContext jsontext.Value `json:"secretGenerationContext"`
EnableAutoGeneration *bool `json:"enableAutoGeneration"`
CompartmentID string `json:"compartmentId"`
KeyID string `json:"keyId"`
SecretName string `json:"secretName"`
VaultID string `json:"vaultId"`
Description string `json:"description"`
- SecretRules []json.RawMessage `json:"secretRules"`
+ SecretRules []jsontext.Value `json:"secretRules"`
}
// UpdateSecretIn is the UpdateSecret request body.
@@ -57,11 +59,11 @@ type UpdateSecretIn struct {
DefinedTags map[string]map[string]any `json:"definedTags"`
Metadata map[string]any `json:"metadata"`
SecretContent *SecretContent `json:"secretContent"`
- ReplicationConfig json.RawMessage `json:"replicationConfig"`
- RotationConfig json.RawMessage `json:"rotationConfig"`
- SecretGenerationContext json.RawMessage `json:"secretGenerationContext"`
+ ReplicationConfig jsontext.Value `json:"replicationConfig"`
+ RotationConfig jsontext.Value `json:"rotationConfig"`
+ SecretGenerationContext jsontext.Value `json:"secretGenerationContext"`
EnableAutoGeneration *bool `json:"enableAutoGeneration"`
- SecretRules []json.RawMessage `json:"secretRules"`
+ SecretRules []jsontext.Value `json:"secretRules"`
}
type expiryRule struct {
@@ -70,7 +72,7 @@ type expiryRule struct {
Interval string `json:"secretVersionExpiryInterval"`
}
-func unsupported(raw json.RawMessage, auto *bool, what string) *APIError {
+func unsupported(raw jsontext.Value, auto *bool, what string) *APIError {
if len(bytes.TrimSpace(raw)) > 0 && string(bytes.TrimSpace(raw)) != "null" || auto != nil && *auto {
return errParam(what + " is not supported by this emulator")
}
@@ -78,10 +80,10 @@ func unsupported(raw json.RawMessage, auto *bool, what string) *APIError {
return nil
}
-func rejectUnsupported(repl, rot, gen json.RawMessage, auto *bool) *APIError {
+func rejectUnsupported(repl, rot, gen jsontext.Value, auto *bool) *APIError {
for _, c := range []struct {
what string
- raw json.RawMessage
+ raw jsontext.Value
}{
{what: "replicationConfig", raw: repl},
{what: "rotationConfig", raw: rot},
@@ -95,13 +97,18 @@ func rejectUnsupported(repl, rot, gen json.RawMessage, auto *bool) *APIError {
return unsupported(nil, auto, "enableAutoGeneration")
}
-func validateRules(rules []json.RawMessage) *APIError {
+func validateRules(rules []jsontext.Value) *APIError {
for _, r := range rules {
var head struct {
RuleType string `json:"ruleType"`
}
- if err := json.Unmarshal(r, &head); err != nil || (head.RuleType != ruleExpiry && head.RuleType != ruleReuse) {
+ if err := json.Unmarshal(
+ r,
+ &head,
+ jsonx.V1(),
+ ); err != nil ||
+ (head.RuleType != ruleExpiry && head.RuleType != ruleReuse) {
return errParam("secretRules[].ruleType must be SECRET_EXPIRY_RULE or SECRET_REUSE_RULE")
}
@@ -115,9 +122,9 @@ func validateRules(rules []json.RawMessage) *APIError {
return nil
}
-func parseExpiry(raw json.RawMessage) (expiryRule, *APIError) {
+func parseExpiry(raw jsontext.Value) (expiryRule, *APIError) {
var e expiryRule
- if err := json.Unmarshal(raw, &e); err != nil {
+ if err := json.Unmarshal(raw, &e, jsonx.V1()); err != nil {
return e, errParam("invalid SECRET_EXPIRY_RULE")
}
@@ -130,13 +137,13 @@ func parseExpiry(raw json.RawMessage) (expiryRule, *APIError) {
return e, nil
}
-func ruleOfType(rules []json.RawMessage, t string) json.RawMessage {
+func ruleOfType(rules []jsontext.Value, t string) jsontext.Value {
for _, r := range rules {
var head struct {
RuleType string `json:"ruleType"`
}
- if json.Unmarshal(r, &head) == nil && head.RuleType == t {
+ if json.Unmarshal(r, &head, jsonx.V1()) == nil && head.RuleType == t {
return r
}
}
@@ -144,7 +151,7 @@ func ruleOfType(rules []json.RawMessage, t string) json.RawMessage {
return nil
}
-func (b *InMemoryBackend) versionExpiry(rules []json.RawMessage) *time.Time {
+func (b *InMemoryBackend) versionExpiry(rules []jsontext.Value) *time.Time {
raw := ruleOfType(rules, ruleExpiry)
if raw == nil {
return nil
diff --git a/services/ocivault/secrets_test.go b/services/ocivault/secrets_test.go
index 4246998146..379b082494 100644
--- a/services/ocivault/secrets_test.go
+++ b/services/ocivault/secrets_test.go
@@ -1,7 +1,7 @@
package ocivault_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
oci "github.com/blackbirdworks/gopherstack/services/ocivault"
)
@@ -644,7 +645,7 @@ func TestSecretIfMatchAndCompartmentMove(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "ocid1.compartment.oc1..moved", *moved.CompartmentId)
- raw, err := json.Marshal(moved.Secret)
+ raw, err := json.Marshal(moved.Secret, jsonx.V1())
require.NoError(t, err)
assert.Contains(t, string(raw), "currentVersionNumber")
}
diff --git a/services/ocivault/secretwire.go b/services/ocivault/secretwire.go
index 61f6c10afa..3d0e484fd4 100644
--- a/services/ocivault/secretwire.go
+++ b/services/ocivault/secretwire.go
@@ -1,7 +1,7 @@
package ocivault
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -20,7 +20,7 @@ type secretWire struct {
VaultID string `json:"vaultId"`
KeyID string `json:"keyId"`
Description string `json:"description,omitempty"`
- SecretRules []json.RawMessage `json:"secretRules,omitempty"`
+ SecretRules []jsontext.Value `json:"secretRules,omitempty"`
}
type secretSummaryWire struct {
diff --git a/services/omics/delete_precondition_test.go b/services/omics/delete_precondition_test.go
index d913d06d06..ae40065d2c 100644
--- a/services/omics/delete_precondition_test.go
+++ b/services/omics/delete_precondition_test.go
@@ -1,12 +1,14 @@
package omics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDeleteRun_RequiresTerminalState verifies real AWS DeleteRun semantics
@@ -26,7 +28,7 @@ func TestDeleteRun_RequiresTerminalState(t *testing.T) {
require.Equal(t, http.StatusCreated, startRec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
runID := startResp["id"].(string)
delRec := doRequest(t, h, http.MethodDelete, "/run/"+runID, nil)
@@ -55,7 +57,7 @@ func TestDeleteReferenceStore_RequiresEmpty(t *testing.T) {
require.Equal(t, http.StatusCreated, storeRec.Code)
var store map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store, jsonx.V1()))
storeID := store["id"].(string)
jobRec := doRequest(t, h, http.MethodPost, "/referencestore/"+storeID+"/importjob", map[string]any{
@@ -68,7 +70,7 @@ func TestDeleteReferenceStore_RequiresEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
refs, ok := listResp["references"].([]any)
require.True(t, ok)
require.Len(t, refs, 1)
@@ -100,7 +102,7 @@ func TestDeleteSequenceStore_RequiresEmpty(t *testing.T) {
require.Equal(t, http.StatusCreated, storeRec.Code)
var store map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store, jsonx.V1()))
storeID := store["id"].(string)
jobRec := doRequest(t, h, http.MethodPost, "/sequencestore/"+storeID+"/importjob", map[string]any{
@@ -113,7 +115,7 @@ func TestDeleteSequenceStore_RequiresEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
readSets, ok := listResp["readSets"].([]any)
require.True(t, ok)
require.Len(t, readSets, 1)
@@ -147,7 +149,7 @@ func TestDeleteReference_RequiresNoAssociatedReadSet(t *testing.T) {
require.Equal(t, http.StatusCreated, refStoreRec.Code)
var refStore map[string]any
- require.NoError(t, json.Unmarshal(refStoreRec.Body.Bytes(), &refStore))
+ require.NoError(t, json.Unmarshal(refStoreRec.Body.Bytes(), &refStore, jsonx.V1()))
refStoreID := refStore["id"].(string)
refJobRec := doRequest(t, h, http.MethodPost, "/referencestore/"+refStoreID+"/importjob", map[string]any{
@@ -160,7 +162,7 @@ func TestDeleteReference_RequiresNoAssociatedReadSet(t *testing.T) {
require.Equal(t, http.StatusOK, refListRec.Code)
var refListResp map[string]any
- require.NoError(t, json.Unmarshal(refListRec.Body.Bytes(), &refListResp))
+ require.NoError(t, json.Unmarshal(refListRec.Body.Bytes(), &refListResp, jsonx.V1()))
refs, ok := refListResp["references"].([]any)
require.True(t, ok)
require.Len(t, refs, 1)
@@ -173,7 +175,7 @@ func TestDeleteReference_RequiresNoAssociatedReadSet(t *testing.T) {
require.Equal(t, http.StatusCreated, seqStoreRec.Code)
var seqStore map[string]any
- require.NoError(t, json.Unmarshal(seqStoreRec.Body.Bytes(), &seqStore))
+ require.NoError(t, json.Unmarshal(seqStoreRec.Body.Bytes(), &seqStore, jsonx.V1()))
seqStoreID := seqStore["id"].(string)
readSetJobRec := doRequest(t, h, http.MethodPost, "/sequencestore/"+seqStoreID+"/importjob", map[string]any{
@@ -190,7 +192,7 @@ func TestDeleteReference_RequiresNoAssociatedReadSet(t *testing.T) {
require.Equal(t, http.StatusOK, readSetListRec.Code)
var readSetListResp map[string]any
- require.NoError(t, json.Unmarshal(readSetListRec.Body.Bytes(), &readSetListResp))
+ require.NoError(t, json.Unmarshal(readSetListRec.Body.Bytes(), &readSetListResp, jsonx.V1()))
readSets, ok := readSetListResp["readSets"].([]any)
require.True(t, ok)
require.Len(t, readSets, 1)
diff --git a/services/omics/handler.go b/services/omics/handler.go
index cb633586ce..0cdc04ccf8 100644
--- a/services/omics/handler.go
+++ b/services/omics/handler.go
@@ -1,7 +1,7 @@
package omics
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -782,7 +783,7 @@ func readJSON(c *echo.Context, v any) error {
return nil
}
- if jsonErr := json.Unmarshal(body, v); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, v, jsonx.V1()); jsonErr != nil {
if writeErr := c.JSON(http.StatusBadRequest, errResp(errValidation, "invalid JSON")); writeErr != nil {
return writeErr
}
diff --git a/services/omics/handler_annotation_stores_test.go b/services/omics/handler_annotation_stores_test.go
index eece97ac5b..8128a5d787 100644
--- a/services/omics/handler_annotation_stores_test.go
+++ b/services/omics/handler_annotation_stores_test.go
@@ -1,13 +1,14 @@
package omics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/omics"
)
@@ -31,7 +32,7 @@ func TestOmics_AnnotationStore(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
// Real CreateAnnotationStoreOutput is narrower than
// GetAnnotationStoreOutput -- no storeArn/reference member
// (api_op_CreateAnnotationStore.go:68-95); storeArn is
@@ -98,14 +99,14 @@ func TestCreateAnnotationStoreStoresReference(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Equal(t, "CREATING", createResp["status"])
getRec := doRequest(t, h, http.MethodGet, "/annotationStore/ann-store-ref", nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.NotNil(t, getResp["reference"])
// GetAnnotationStore advances CREATING->ACTIVE on its first poll (the
// waiter-hang fix -- see PARITY.md), so the immediate Get after Create
@@ -130,7 +131,7 @@ func TestCreateAnnotationStore_ResponseShape(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "v1", resp["versionName"])
assert.NotContains(t, resp, "storeArn")
assert.NotContains(t, resp, "reference")
@@ -157,7 +158,7 @@ func TestListAnnotationImportJobs_FiltersByStatusStoreNameAndIds(t *testing.T) {
require.Equal(t, http.StatusCreated, jobRec.Code)
var job map[string]any
- require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &job))
+ require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &job, jsonx.V1()))
jobID := job["jobId"].(string)
// storeName filter: no job targets "other-store".
@@ -166,7 +167,7 @@ func TestListAnnotationImportJobs_FiltersByStatusStoreNameAndIds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
jobs, ok := resp["annotationImportJobs"].([]any)
require.True(t, ok)
assert.Empty(t, jobs)
@@ -177,7 +178,7 @@ func TestListAnnotationImportJobs_FiltersByStatusStoreNameAndIds(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
jobs2, ok := resp2["annotationImportJobs"].([]any)
require.True(t, ok)
require.Len(t, jobs2, 1)
@@ -188,7 +189,7 @@ func TestListAnnotationImportJobs_FiltersByStatusStoreNameAndIds(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var resp3 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp3, jsonx.V1()))
jobs3, ok := resp3["annotationImportJobs"].([]any)
require.True(t, ok)
assert.Empty(t, jobs3)
@@ -197,7 +198,7 @@ func TestListAnnotationImportJobs_FiltersByStatusStoreNameAndIds(t *testing.T) {
require.Equal(t, http.StatusOK, rec4.Code)
var resp4 map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &resp4))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &resp4, jsonx.V1()))
jobs4, ok := resp4["annotationImportJobs"].([]any)
require.True(t, ok)
require.Len(t, jobs4, 1)
@@ -218,7 +219,7 @@ func createAnnotationStorePair(t *testing.T, h *omics.Handler) map[string]string
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ids[name], _ = resp["id"].(string)
}
@@ -307,7 +308,7 @@ func TestListAnnotationStores_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.ElementsMatch(t, tc.wantNames, annotationStoreNames(t, resp["annotationStores"]))
})
}
@@ -348,7 +349,7 @@ func TestListAnnotationStoreVersions_FiltersByStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions, ok := resp["annotationStoreVersions"].([]any)
require.True(t, ok)
assert.Len(t, versions, tc.wantCount)
diff --git a/services/omics/handler_configurations_test.go b/services/omics/handler_configurations_test.go
index 25cca88b2e..31b52066cb 100644
--- a/services/omics/handler_configurations_test.go
+++ b/services/omics/handler_configurations_test.go
@@ -1,12 +1,14 @@
package omics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestOmics_Configuration exercises the Configuration CRUD family.
@@ -39,7 +41,7 @@ func TestOmics_Configuration(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "cfg1", resp["name"])
assert.Equal(t, "ACTIVE", resp["status"])
assert.NotEmpty(t, resp["arn"])
@@ -101,14 +103,14 @@ func TestS3AccessPolicy_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
assert.Equal(t, apArn, putResp["s3AccessPointArn"])
getRec := doRequest(t, h, http.MethodGet, "/s3accesspolicy/"+apArn, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
policy, _ := getResp["s3AccessPolicy"].(string)
assert.JSONEq(t, `{"Version":"2012-10-17"}`, policy, "real wire key is s3AccessPolicy")
assert.Nil(t, getResp["policy"], "policy is not a real wire key")
diff --git a/services/omics/handler_read_sets_test.go b/services/omics/handler_read_sets_test.go
index 7f3934320d..a5a80de901 100644
--- a/services/omics/handler_read_sets_test.go
+++ b/services/omics/handler_read_sets_test.go
@@ -3,7 +3,7 @@ package omics_test
import (
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/omics"
)
@@ -31,7 +32,7 @@ func TestOmics_UploadReadSetPart_And_GetReadSet(t *testing.T) {
rec := doRequest(t, h, http.MethodPost, "/sequencestore", map[string]any{"name": "s"})
require.Equal(t, http.StatusCreated, rec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp, jsonx.V1()))
storeID := storeResp["id"].(string)
rec2 := doRequestRaw(t, h, http.MethodPut,
@@ -49,7 +50,7 @@ func TestOmics_UploadReadSetPart_And_GetReadSet(t *testing.T) {
rec := doRequest(t, h, http.MethodPost, "/sequencestore", map[string]any{"name": "s"})
require.Equal(t, http.StatusCreated, rec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp, jsonx.V1()))
storeID := storeResp["id"].(string)
rec2 := doRequestRaw(t, h, http.MethodPut,
@@ -67,7 +68,7 @@ func TestOmics_UploadReadSetPart_And_GetReadSet(t *testing.T) {
rec := doRequest(t, h, http.MethodPost, "/sequencestore", map[string]any{"name": "s"})
require.Equal(t, http.StatusCreated, rec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp, jsonx.V1()))
storeID := storeResp["id"].(string)
rec2 := doRequest(t, h, http.MethodPost,
@@ -76,7 +77,7 @@ func TestOmics_UploadReadSetPart_And_GetReadSet(t *testing.T) {
)
require.Equal(t, http.StatusCreated, rec2.Code)
var uploadResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &uploadResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &uploadResp, jsonx.V1()))
uploadID := uploadResp["uploadId"].(string)
payload := []byte("ACGT sequence data")
@@ -90,7 +91,7 @@ func TestOmics_UploadReadSetPart_And_GetReadSet(t *testing.T) {
)
require.Equal(t, http.StatusOK, rec3.Code)
var partResp map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &partResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &partResp, jsonx.V1()))
assert.Equal(t, expectedChecksum, partResp["checksum"])
assert.Equal(t, "SHA256", partResp["checksumAlgorithm"])
},
@@ -102,7 +103,7 @@ func TestOmics_UploadReadSetPart_And_GetReadSet(t *testing.T) {
rec := doRequest(t, h, http.MethodPost, "/sequencestore", map[string]any{"name": "s"})
require.Equal(t, http.StatusCreated, rec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp, jsonx.V1()))
storeID := storeResp["id"].(string)
rec2 := doRequest(t, h, http.MethodPost,
@@ -111,7 +112,7 @@ func TestOmics_UploadReadSetPart_And_GetReadSet(t *testing.T) {
)
require.Equal(t, http.StatusCreated, rec2.Code)
var uploadResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &uploadResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &uploadResp, jsonx.V1()))
uploadID := uploadResp["uploadId"].(string)
payload := []byte("hello omics")
@@ -128,7 +129,7 @@ func TestOmics_UploadReadSetPart_And_GetReadSet(t *testing.T) {
)
require.Equal(t, http.StatusOK, rec4.Code)
var rsResp map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &rsResp))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &rsResp, jsonx.V1()))
rsID := rsResp["readSetId"].(string)
rec5 := doRequestRaw(t, h, http.MethodGet,
@@ -146,7 +147,7 @@ func TestOmics_UploadReadSetPart_And_GetReadSet(t *testing.T) {
rec := doRequest(t, h, http.MethodPost, "/sequencestore", map[string]any{"name": "s"})
require.Equal(t, http.StatusCreated, rec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp, jsonx.V1()))
storeID := storeResp["id"].(string)
rec2 := doRequestRaw(t, h, http.MethodGet,
@@ -179,7 +180,7 @@ func TestCreateMultipartReadSetUpload_WireShape(t *testing.T) {
require.Equal(t, http.StatusCreated, storeRec.Code)
var store map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store, jsonx.V1()))
storeID := store["id"].(string)
rec := doRequest(t, h, http.MethodPost, "/sequencestore/"+storeID+"/upload", map[string]any{
@@ -191,7 +192,7 @@ func TestCreateMultipartReadSetUpload_WireShape(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "FASTQ", resp["sourceFileType"])
assert.Equal(t, "sample-1", resp["sampleId"])
assert.Equal(t, "subject-1", resp["subjectId"])
@@ -211,7 +212,7 @@ func TestReadSetMetadata_FileTypeField(t *testing.T) {
require.Equal(t, http.StatusCreated, storeRec.Code)
var store map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store, jsonx.V1()))
storeID := store["id"].(string)
jobRec := doRequest(t, h, http.MethodPost, "/sequencestore/"+storeID+"/importjob", map[string]any{
@@ -224,7 +225,7 @@ func TestReadSetMetadata_FileTypeField(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
readSets, ok := listResp["readSets"].([]any)
require.True(t, ok)
require.Len(t, readSets, 1)
@@ -280,7 +281,7 @@ func TestReadSetMetadata_FilesField_ImportJob(t *testing.T) {
require.Equal(t, http.StatusCreated, storeRec.Code)
var store map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store, jsonx.V1()))
storeID := store["id"].(string)
jobRec := doRequest(t, h, http.MethodPost, "/sequencestore/"+storeID+"/importjob", map[string]any{
@@ -293,7 +294,7 @@ func TestReadSetMetadata_FilesField_ImportJob(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
readSets, ok := listResp["readSets"].([]any)
require.True(t, ok)
require.Len(t, readSets, 1)
@@ -309,7 +310,7 @@ func TestReadSetMetadata_FilesField_ImportJob(t *testing.T) {
require.Equal(t, http.StatusOK, metaRec.Code)
var meta map[string]any
- require.NoError(t, json.Unmarshal(metaRec.Body.Bytes(), &meta))
+ require.NoError(t, json.Unmarshal(metaRec.Body.Bytes(), &meta, jsonx.V1()))
files, ok := meta["files"].(map[string]any)
require.True(t, ok, "files sub-object must be present")
assert.Contains(t, files, "source1")
@@ -357,7 +358,7 @@ func TestReadSetMetadata_FilesField_MultipartUpload(t *testing.T) {
require.Equal(t, http.StatusCreated, storeRec.Code)
var store map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store, jsonx.V1()))
storeID := store["id"].(string)
uploadRec := doRequest(t, h, http.MethodPost,
@@ -367,7 +368,7 @@ func TestReadSetMetadata_FilesField_MultipartUpload(t *testing.T) {
require.Equal(t, http.StatusCreated, uploadRec.Code)
var uploadResp map[string]any
- require.NoError(t, json.Unmarshal(uploadRec.Body.Bytes(), &uploadResp))
+ require.NoError(t, json.Unmarshal(uploadRec.Body.Bytes(), &uploadResp, jsonx.V1()))
uploadID := uploadResp["uploadId"].(string)
part1Rec := doRequestRaw(t, h, http.MethodPut,
@@ -396,7 +397,7 @@ func TestReadSetMetadata_FilesField_MultipartUpload(t *testing.T) {
// "readSetId" -- the files sub-object only ever appears on
// GetReadSetMetadata, so fetch it there.
var completeResp map[string]any
- require.NoError(t, json.Unmarshal(completeRec.Body.Bytes(), &completeResp))
+ require.NoError(t, json.Unmarshal(completeRec.Body.Bytes(), &completeResp, jsonx.V1()))
readSetID, ok := completeResp["readSetId"].(string)
require.True(t, ok, "readSetId must be present")
require.NotEmpty(t, readSetID)
@@ -408,7 +409,7 @@ func TestReadSetMetadata_FilesField_MultipartUpload(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var rs map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &rs))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &rs, jsonx.V1()))
files, ok := rs["files"].(map[string]any)
require.True(t, ok, "files sub-object must be present")
@@ -476,7 +477,7 @@ func TestUploadReadSetPart_BodyReadError_InternalServerException(t *testing.T) {
require.Equal(t, http.StatusInternalServerError, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InternalServerException", resp["__type"])
})
}
diff --git a/services/omics/handler_readjson_test.go b/services/omics/handler_readjson_test.go
index 138e1727d2..d13e3fad6c 100644
--- a/services/omics/handler_readjson_test.go
+++ b/services/omics/handler_readjson_test.go
@@ -2,7 +2,8 @@ package omics_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -11,6 +12,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestReadJSONMalformedWritesExactlyOneDocument guards against the same
@@ -32,14 +35,14 @@ func TestReadJSONMalformedWritesExactlyOneDocument(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
- dec := json.NewDecoder(rec.Body)
+ dec := jsontext.NewDecoder(rec.Body)
var first map[string]any
- require.NoError(t, dec.Decode(&first))
+ require.NoError(t, json.UnmarshalDecode(dec, &first, jsonx.V1()))
require.Contains(t, first, "message")
var second map[string]any
- err := dec.Decode(&second)
+ err := json.UnmarshalDecode(dec, &second, jsonx.V1())
require.ErrorIs(
t,
err,
diff --git a/services/omics/handler_reference_stores_test.go b/services/omics/handler_reference_stores_test.go
index 1f94d9367e..30de0ab053 100644
--- a/services/omics/handler_reference_stores_test.go
+++ b/services/omics/handler_reference_stores_test.go
@@ -1,7 +1,7 @@
package omics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/omics"
)
@@ -32,7 +33,7 @@ func TestOmics_ReferenceStore(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp["arn"], "arn:aws:omics:")
assert.Equal(t, "test-store", resp["name"])
},
@@ -66,7 +67,7 @@ func TestOmics_ReferenceStore(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rec2 := doRequest(t, h, http.MethodGet, "/referencestore/"+resp["id"].(string), nil)
assert.Equal(t, http.StatusOK, rec2.Code)
@@ -80,7 +81,7 @@ func TestOmics_ReferenceStore(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rec2 := doRequest(t, h, http.MethodDelete, "/referencestore/"+resp["id"].(string), nil)
assert.Equal(t, http.StatusOK, rec2.Code)
@@ -98,7 +99,7 @@ func TestOmics_ReferenceStore(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stores := resp["referenceStores"].([]any)
assert.Len(t, stores, 2)
})
@@ -144,7 +145,7 @@ func TestOmics_GetReference_Binary(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return fmt.Sprintf("/referencestore/%s/reference/doesnotexist", resp["id"])
},
@@ -157,7 +158,7 @@ func TestOmics_GetReference_Binary(t *testing.T) {
rec := doRequest(t, h, http.MethodPost, "/referencestore", map[string]any{"name": "s"})
require.Equal(t, http.StatusCreated, rec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp, jsonx.V1()))
storeID := storeResp["id"].(string)
rec2 := doRequest(t, h, http.MethodPost,
@@ -176,7 +177,7 @@ func TestOmics_GetReference_Binary(t *testing.T) {
)
require.Equal(t, http.StatusOK, rec3.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &listResp, jsonx.V1()))
refs := listResp["references"].([]any)
require.Len(t, refs, 1)
refID := refs[0].(map[string]any)["id"].(string)
@@ -210,14 +211,14 @@ func TestDeleteReferenceStoreReturnsID(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
storeID := createResp["id"].(string)
delRec := doRequest(t, h, http.MethodDelete, "/referencestore/"+storeID, nil)
require.Equal(t, http.StatusOK, delRec.Code)
var delResp map[string]any
- require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &delResp))
+ require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &delResp, jsonx.V1()))
assert.Equal(t, storeID, delResp["id"])
}
@@ -233,7 +234,7 @@ func TestListReferenceImportJobs_FiltersByStatus(t *testing.T) {
require.Equal(t, http.StatusCreated, storeRec.Code)
var store map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store, jsonx.V1()))
storeID := store["id"].(string)
jobRec := doRequest(t, h, http.MethodPost, "/referencestore/"+storeID+"/importjob", map[string]any{
@@ -247,7 +248,7 @@ func TestListReferenceImportJobs_FiltersByStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
jobs, ok := resp["importJobs"].([]any)
require.True(t, ok)
assert.Empty(t, jobs, "no import job has failed")
@@ -257,7 +258,7 @@ func TestListReferenceImportJobs_FiltersByStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
jobs2, ok := resp2["importJobs"].([]any)
require.True(t, ok)
assert.Len(t, jobs2, 1)
@@ -274,7 +275,7 @@ func TestGetReferenceMetadata_FilesField(t *testing.T) {
require.Equal(t, http.StatusCreated, storeRec.Code)
var store map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store, jsonx.V1()))
storeID := store["id"].(string)
jobRec := doRequest(t, h, http.MethodPost, "/referencestore/"+storeID+"/importjob", map[string]any{
@@ -287,7 +288,7 @@ func TestGetReferenceMetadata_FilesField(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
refs, ok := listResp["references"].([]any)
require.True(t, ok)
require.Len(t, refs, 1)
@@ -300,7 +301,7 @@ func TestGetReferenceMetadata_FilesField(t *testing.T) {
require.Equal(t, http.StatusOK, metaRec.Code)
var meta map[string]any
- require.NoError(t, json.Unmarshal(metaRec.Body.Bytes(), &meta))
+ require.NoError(t, json.Unmarshal(metaRec.Body.Bytes(), &meta, jsonx.V1()))
files, ok := meta["files"].(map[string]any)
require.True(t, ok, "files sub-object must be present")
source, ok := files["source"].(map[string]any)
diff --git a/services/omics/handler_runs_test.go b/services/omics/handler_runs_test.go
index 733fcd424f..074f90c72b 100644
--- a/services/omics/handler_runs_test.go
+++ b/services/omics/handler_runs_test.go
@@ -1,13 +1,14 @@
package omics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/omics"
)
@@ -31,7 +32,7 @@ func TestOmics_RunGroup(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp["arn"], "arn:aws:omics:")
},
},
@@ -43,7 +44,7 @@ func TestOmics_RunGroup(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotNil(t, resp["items"])
},
},
@@ -100,7 +101,7 @@ func TestOmics_Run(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp["arn"], "arn:aws:omics:")
},
},
@@ -153,7 +154,7 @@ func TestRunStartsAsPending(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "PENDING", resp["status"])
}
@@ -171,7 +172,7 @@ func TestStartRunReturnsPartialResponse(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["arn"])
assert.NotEmpty(t, resp["id"])
assert.Equal(t, "PENDING", resp["status"])
@@ -192,7 +193,7 @@ func TestUpdateRunCachePersistsDescription(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
cacheID := createResp["id"].(string)
updateRec := doRequest(t, h, http.MethodPost, "/runCache/"+cacheID, map[string]any{
@@ -204,7 +205,7 @@ func TestUpdateRunCachePersistsDescription(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "updated description", getResp["description"])
}
@@ -222,7 +223,7 @@ func TestListRunsInBatchReturnsAssociatedRuns(t *testing.T) {
require.Equal(t, http.StatusCreated, batchRec.Code)
var batchResp map[string]any
- require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batchResp))
+ require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batchResp, jsonx.V1()))
batchID := batchResp["id"].(string)
// Start a second run explicitly associated with this batch via the
@@ -242,7 +243,7 @@ func TestListRunsInBatchReturnsAssociatedRuns(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
runs, ok := listResp["runs"].([]any)
require.True(t, ok)
assert.Len(t, runs, 2)
@@ -265,11 +266,11 @@ func TestListRunsInBatchEmptyForOtherBatch(t *testing.T) {
require.Equal(t, http.StatusCreated, batch2Rec.Code)
var b1 map[string]any
- require.NoError(t, json.Unmarshal(batch1Rec.Body.Bytes(), &b1))
+ require.NoError(t, json.Unmarshal(batch1Rec.Body.Bytes(), &b1, jsonx.V1()))
batchID1 := b1["id"].(string)
var b2 map[string]any
- require.NoError(t, json.Unmarshal(batch2Rec.Body.Bytes(), &b2))
+ require.NoError(t, json.Unmarshal(batch2Rec.Body.Bytes(), &b2, jsonx.V1()))
batchID2 := b2["id"].(string)
// Associate a run with batch 1.
@@ -286,7 +287,7 @@ func TestListRunsInBatchEmptyForOtherBatch(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
runs, ok := listResp["runs"].([]any)
require.True(t, ok)
assert.Len(t, runs, 1)
@@ -317,7 +318,7 @@ func TestCancelRunGuardsTerminalState(t *testing.T) {
require.Equal(t, http.StatusCreated, startRec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
runID := startResp["id"].(string)
// Cancel once.
@@ -349,7 +350,7 @@ func TestStartRun_ResponseIncludesOptionalFields(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["uuid"], "StartRunOutput.uuid must be populated")
assert.Equal(t, "VPC", resp["networkingMode"])
assert.Equal(t, "s3://bucket/output", resp["runOutputUri"])
@@ -367,7 +368,7 @@ func TestRun_BatchIDSerializedAsBatchIdKey(t *testing.T) {
require.Equal(t, http.StatusCreated, batchRec.Code)
var batch map[string]any
- require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batch))
+ require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batch, jsonx.V1()))
batchID := batch["id"].(string)
runRec := doRequest(t, h, http.MethodPost, "/run", map[string]any{
@@ -379,14 +380,14 @@ func TestRun_BatchIDSerializedAsBatchIdKey(t *testing.T) {
require.Equal(t, http.StatusCreated, runRec.Code)
var run map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &run))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &run, jsonx.V1()))
runID := run["id"].(string)
getRec := doRequest(t, h, http.MethodGet, "/run/"+runID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, batchID, getResp["batchId"], "real wire key is batchId")
assert.Nil(t, getResp["runBatchId"], "runBatchId is not a real wire key")
}
@@ -412,7 +413,7 @@ func TestListRuns_FiltersByNameRunGroupBatchStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
runs, ok := resp["items"].([]any)
require.True(t, ok)
require.Len(t, runs, 1)
@@ -422,7 +423,7 @@ func TestListRuns_FiltersByNameRunGroupBatchStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
runs2, ok := resp2["items"].([]any)
require.True(t, ok)
require.Len(t, runs2, 1)
@@ -432,7 +433,7 @@ func TestListRuns_FiltersByNameRunGroupBatchStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var resp3 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp3, jsonx.V1()))
runs3, ok := resp3["items"].([]any)
require.True(t, ok)
assert.Empty(t, runs3, "no run has been cancelled yet")
@@ -450,7 +451,7 @@ func TestListRunTasks_FiltersByStatus(t *testing.T) {
require.Equal(t, http.StatusCreated, runRec.Code)
var run map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &run))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &run, jsonx.V1()))
runID := run["id"].(string)
// The auto-created task starts PENDING; filtering for RUNNING must exclude it.
@@ -458,7 +459,7 @@ func TestListRunTasks_FiltersByStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tasks, ok := resp["items"].([]any)
require.True(t, ok)
assert.Empty(t, tasks)
@@ -467,7 +468,7 @@ func TestListRunTasks_FiltersByStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
tasks2, ok := resp2["items"].([]any)
require.True(t, ok)
assert.Len(t, tasks2, 1)
@@ -486,7 +487,7 @@ func TestListRunGroups_FiltersByName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups, ok := resp["items"].([]any)
require.True(t, ok)
require.Len(t, groups, 1)
@@ -508,7 +509,7 @@ func TestListRunBatches_FiltersByNameAndStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
batches, ok := resp["items"].([]any)
require.True(t, ok)
require.Len(t, batches, 1)
@@ -520,7 +521,7 @@ func TestListRunBatches_FiltersByNameAndStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
batches2, ok := resp2["items"].([]any)
require.True(t, ok)
assert.Len(t, batches2, 2)
@@ -537,7 +538,7 @@ func TestListRunsInBatch_FiltersByRunID(t *testing.T) {
require.Equal(t, http.StatusCreated, batchRec.Code)
var batch map[string]any
- require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batch))
+ require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batch, jsonx.V1()))
batchID := batch["id"].(string)
runRec := doRequest(t, h, http.MethodPost, "/run", map[string]any{
@@ -547,14 +548,14 @@ func TestListRunsInBatch_FiltersByRunID(t *testing.T) {
require.Equal(t, http.StatusCreated, runRec.Code)
var run map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &run))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &run, jsonx.V1()))
runID := run["id"].(string)
rec := doRequest(t, h, http.MethodGet, "/runBatch/"+batchID+"/run?runId=does-not-exist", nil)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
runs, ok := resp["runs"].([]any)
require.True(t, ok)
assert.Empty(t, runs, "runId filter must exclude non-matching runs")
@@ -563,7 +564,7 @@ func TestListRunsInBatch_FiltersByRunID(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
runs2, ok := resp2["runs"].([]any)
require.True(t, ok)
require.Len(t, runs2, 1)
@@ -587,7 +588,7 @@ func TestDeleteBatch_RequiresTerminalState(t *testing.T) {
require.Equal(t, http.StatusCreated, batchRec.Code)
var batch map[string]any
- require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batch))
+ require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batch, jsonx.V1()))
batchID := batch["id"].(string)
omics.SetRunBatchStatusForTest(backend, batchID, "SUBMITTING")
diff --git a/services/omics/handler_sequence_stores_test.go b/services/omics/handler_sequence_stores_test.go
index ba22dcc24c..cd1fcceee6 100644
--- a/services/omics/handler_sequence_stores_test.go
+++ b/services/omics/handler_sequence_stores_test.go
@@ -1,12 +1,14 @@
package omics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestOmics_SequenceStore(t *testing.T) {
@@ -29,7 +31,7 @@ func TestOmics_SequenceStore(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp["arn"], "arn:aws:omics:")
assert.Equal(t, "seq-store", resp["name"])
},
@@ -55,7 +57,7 @@ func TestOmics_SequenceStore(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
stores := resp["sequenceStores"].([]any)
assert.Empty(t, stores)
},
@@ -89,7 +91,7 @@ func TestSequenceStoreHasStatusAndUpdateTime(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
assert.Equal(t, "ACTIVE", created["status"])
id, ok := created["id"].(string)
@@ -99,7 +101,7 @@ func TestSequenceStoreHasStatusAndUpdateTime(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ACTIVE", resp["status"])
assert.NotEmpty(t, resp["updateTime"])
}
@@ -122,7 +124,7 @@ func TestCreateSequenceStoreETagAlgorithmAndS3AccessConfig(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "SHA256up", resp["eTagAlgorithmFamily"])
assert.NotContains(t, resp, "eTagAlgorithm")
@@ -143,13 +145,13 @@ func TestDeleteSequenceStoreReturnsID(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
storeID := createResp["id"].(string)
delRec := doRequest(t, h, http.MethodDelete, "/sequencestore/"+storeID, nil)
require.Equal(t, http.StatusOK, delRec.Code)
var delResp map[string]any
- require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &delResp))
+ require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &delResp, jsonx.V1()))
assert.Equal(t, storeID, delResp["id"])
}
diff --git a/services/omics/handler_shares_test.go b/services/omics/handler_shares_test.go
index 4c8b50ba37..3d03699125 100644
--- a/services/omics/handler_shares_test.go
+++ b/services/omics/handler_shares_test.go
@@ -1,7 +1,7 @@
package omics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/omics"
)
@@ -78,7 +79,7 @@ func createShare(t *testing.T, h *omics.Handler) string {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id, _ := resp["shareId"].(string)
@@ -158,7 +159,7 @@ func TestOmics_Share_ResponseShape(t *testing.T) {
require.Less(t, rec.Code, http.StatusBadRequest)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
keys := make([]string, 0, len(resp))
for k := range resp {
@@ -183,7 +184,7 @@ func TestOmics_GetShare_FullObjectFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
share, ok := resp["share"].(map[string]any)
require.True(t, ok)
@@ -265,7 +266,7 @@ func createSharePair(t *testing.T, h *omics.Handler) map[string]string {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ids[key], _ = resp["shareId"].(string)
}
@@ -355,7 +356,7 @@ func TestListShares_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
var want []string
if tc.wantKey != "" {
diff --git a/services/omics/handler_tags_test.go b/services/omics/handler_tags_test.go
index a17493f3ba..45bb68244f 100644
--- a/services/omics/handler_tags_test.go
+++ b/services/omics/handler_tags_test.go
@@ -1,7 +1,7 @@
package omics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -9,6 +9,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestOmics_Tags(t *testing.T) {
@@ -21,7 +23,7 @@ func TestOmics_Tags(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var wfResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &wfResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &wfResp, jsonx.V1()))
wfARN := wfResp["arn"].(string)
// TagResource
@@ -33,7 +35,7 @@ func TestOmics_Tags(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags := tagsResp["tags"].(map[string]any)
assert.Equal(t, "test", tags["env"])
}
diff --git a/services/omics/handler_test.go b/services/omics/handler_test.go
index f63508583f..dda411a38d 100644
--- a/services/omics/handler_test.go
+++ b/services/omics/handler_test.go
@@ -2,13 +2,14 @@ package omics_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http/httptest"
"testing"
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/omics"
)
@@ -48,7 +49,7 @@ func doRequest(t *testing.T, h *omics.Handler, method, path string, body any) *h
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
diff --git a/services/omics/handler_variant_stores_test.go b/services/omics/handler_variant_stores_test.go
index 9bfee1679d..27b8409fe9 100644
--- a/services/omics/handler_variant_stores_test.go
+++ b/services/omics/handler_variant_stores_test.go
@@ -1,13 +1,14 @@
package omics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/omics"
)
@@ -31,7 +32,7 @@ func TestOmics_VariantStore(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "var-store", resp["name"])
assert.NotEmpty(t, resp["id"])
assert.NotContains(t, resp, "storeArn")
@@ -82,7 +83,7 @@ func TestCreateVariantStoreStoresReference(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["reference"])
assert.Equal(t, "CREATING", resp["status"])
}
@@ -106,7 +107,7 @@ func TestListVariantImportJobs_FiltersByStatusStoreNameAndIds(t *testing.T) {
require.Equal(t, http.StatusCreated, jobRec.Code)
var job map[string]any
- require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &job))
+ require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &job, jsonx.V1()))
jobID := job["jobId"].(string)
rec := doRequest(t, h, http.MethodPost, "/import/variants",
@@ -114,7 +115,7 @@ func TestListVariantImportJobs_FiltersByStatusStoreNameAndIds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
jobs, ok := resp["variantImportJobs"].([]any)
require.True(t, ok)
assert.Empty(t, jobs)
@@ -123,7 +124,7 @@ func TestListVariantImportJobs_FiltersByStatusStoreNameAndIds(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
jobs2, ok := resp2["variantImportJobs"].([]any)
require.True(t, ok)
require.Len(t, jobs2, 1)
@@ -142,7 +143,7 @@ func createVariantStorePair(t *testing.T, h *omics.Handler) map[string]string {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ids[name], _ = resp["id"].(string)
}
@@ -231,7 +232,7 @@ func TestListVariantStores_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.ElementsMatch(t, tc.wantNames, variantStoreNames(t, resp["variantStores"]))
})
}
diff --git a/services/omics/handler_workflows_test.go b/services/omics/handler_workflows_test.go
index 315ed6ac10..838abd5f4b 100644
--- a/services/omics/handler_workflows_test.go
+++ b/services/omics/handler_workflows_test.go
@@ -1,12 +1,14 @@
package omics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestOmics_Workflow(t *testing.T) {
@@ -29,7 +31,7 @@ func TestOmics_Workflow(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp["arn"], "arn:aws:omics:")
assert.NotEmpty(t, resp["id"])
assert.Equal(t, "CREATING", resp["status"])
@@ -50,7 +52,7 @@ func TestOmics_Workflow(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotNil(t, resp["items"])
},
},
@@ -90,7 +92,7 @@ func TestWorkflowStartsAsCreating(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "CREATING", resp["status"])
}
@@ -107,7 +109,7 @@ func TestCreateWorkflowReturnsPartialResponse(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["arn"])
assert.NotEmpty(t, resp["id"])
assert.Nil(t, resp["name"], "full workflow object must not be returned")
@@ -127,14 +129,14 @@ func TestWorkflowHasTypeField(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
wfID := createResp["id"].(string)
getRec := doRequest(t, h, http.MethodGet, "/workflow/"+wfID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "PRIVATE", getResp["type"])
}
@@ -151,7 +153,7 @@ func TestUpdateWorkflowReturnsWorkflow(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
wfID := createResp["id"].(string)
updateRec := doRequest(t, h, http.MethodPost, "/workflow/"+wfID, map[string]any{
@@ -161,7 +163,7 @@ func TestUpdateWorkflowReturnsWorkflow(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Equal(t, "updated-name", updateResp["name"])
assert.Equal(t, "new description", updateResp["description"])
}
@@ -191,7 +193,7 @@ func TestCreateWorkflow_ResponseIncludesUUID(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["uuid"], "CreateWorkflowOutput.uuid must be populated")
}
@@ -208,7 +210,7 @@ func TestListWorkflows_FiltersByNameAndType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
workflows, ok := resp["items"].([]any)
require.True(t, ok)
require.Len(t, workflows, 1)
@@ -220,7 +222,7 @@ func TestListWorkflows_FiltersByNameAndType(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
workflows2, ok := resp2["items"].([]any)
require.True(t, ok)
assert.Empty(t, workflows2)
@@ -237,7 +239,7 @@ func TestListWorkflowVersions_FiltersByType(t *testing.T) {
require.Equal(t, http.StatusCreated, wfRec.Code)
var wf map[string]any
- require.NoError(t, json.Unmarshal(wfRec.Body.Bytes(), &wf))
+ require.NoError(t, json.Unmarshal(wfRec.Body.Bytes(), &wf, jsonx.V1()))
wfID := wf["id"].(string)
verRec := doRequest(t, h, http.MethodPost, "/workflow/"+wfID+"/version", map[string]any{"versionName": "v1"})
@@ -249,7 +251,7 @@ func TestListWorkflowVersions_FiltersByType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions, ok := resp["items"].([]any)
require.True(t, ok)
assert.Empty(t, versions)
@@ -258,7 +260,7 @@ func TestListWorkflowVersions_FiltersByType(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
versions2, ok := resp2["items"].([]any)
require.True(t, ok)
assert.Len(t, versions2, 1)
diff --git a/services/omics/lifecycle_poll_test.go b/services/omics/lifecycle_poll_test.go
index d8bf3b0b1f..3d38213b66 100644
--- a/services/omics/lifecycle_poll_test.go
+++ b/services/omics/lifecycle_poll_test.go
@@ -11,12 +11,14 @@ package omics_test
// way real AWS eventually does.
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func Test_GetWorkflow_AdvancesCreatingToActiveOnPoll(t *testing.T) {
@@ -30,7 +32,7 @@ func Test_GetWorkflow_AdvancesCreatingToActiveOnPoll(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
id := created["id"].(string)
require.Equal(t, "CREATING", created["status"])
@@ -38,7 +40,7 @@ func Test_GetWorkflow_AdvancesCreatingToActiveOnPoll(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "ACTIVE", got["status"], "waiter clients poll GetWorkflow until Status==ACTIVE")
}
@@ -53,14 +55,14 @@ func Test_GetAnnotationStore_AdvancesCreatingToActiveOnPoll(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
require.Equal(t, "CREATING", created["status"])
getRec := doRequest(t, h, http.MethodGet, "/annotationStore/my-store", nil)
require.Equal(t, http.StatusOK, getRec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "ACTIVE", got["status"])
}
@@ -74,14 +76,14 @@ func Test_GetVariantStore_AdvancesCreatingToActiveOnPoll(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
require.Equal(t, "CREATING", created["status"])
getRec := doRequest(t, h, http.MethodGet, "/variantStore/my-vstore", nil)
require.Equal(t, http.StatusOK, getRec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "ACTIVE", got["status"])
}
@@ -96,7 +98,7 @@ func Test_GetWorkflowVersion_AdvancesCreatingToActiveOnPoll(t *testing.T) {
require.Equal(t, http.StatusCreated, wfRec.Code)
var wf map[string]any
- require.NoError(t, json.Unmarshal(wfRec.Body.Bytes(), &wf))
+ require.NoError(t, json.Unmarshal(wfRec.Body.Bytes(), &wf, jsonx.V1()))
workflowID := wf["id"].(string)
verRec := doRequest(t, h, http.MethodPost, "/workflow/"+workflowID+"/version", map[string]any{
@@ -105,14 +107,14 @@ func Test_GetWorkflowVersion_AdvancesCreatingToActiveOnPoll(t *testing.T) {
require.Equal(t, http.StatusCreated, verRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &created, jsonx.V1()))
require.Equal(t, "CREATING", created["status"])
getRec := doRequest(t, h, http.MethodGet, "/workflow/"+workflowID+"/version/v1", nil)
require.Equal(t, http.StatusOK, getRec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "ACTIVE", got["status"])
}
@@ -131,7 +133,7 @@ func Test_GetRun_AdvancesPendingToRunningToCompletedAcrossPolls(t *testing.T) {
require.Equal(t, http.StatusCreated, startRec.Code)
var started map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &started))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &started, jsonx.V1()))
id := started["id"].(string)
require.Equal(t, "PENDING", started["status"])
@@ -139,7 +141,7 @@ func Test_GetRun_AdvancesPendingToRunningToCompletedAcrossPolls(t *testing.T) {
require.Equal(t, http.StatusOK, poll1.Code)
var p1 map[string]any
- require.NoError(t, json.Unmarshal(poll1.Body.Bytes(), &p1))
+ require.NoError(t, json.Unmarshal(poll1.Body.Bytes(), &p1, jsonx.V1()))
assert.Equal(t, "RUNNING", p1["status"], "RunRunningWaiter polls until Status==RUNNING")
assert.NotEmpty(t, p1["startTime"])
@@ -147,7 +149,7 @@ func Test_GetRun_AdvancesPendingToRunningToCompletedAcrossPolls(t *testing.T) {
require.Equal(t, http.StatusOK, poll2.Code)
var p2 map[string]any
- require.NoError(t, json.Unmarshal(poll2.Body.Bytes(), &p2))
+ require.NoError(t, json.Unmarshal(poll2.Body.Bytes(), &p2, jsonx.V1()))
assert.Equal(t, "COMPLETED", p2["status"], "RunCompletedWaiter polls until Status==COMPLETED")
assert.NotEmpty(t, p2["stopTime"])
}
@@ -167,14 +169,14 @@ func Test_GetRunTask_AdvancesPendingToRunningToCompletedAcrossPolls(t *testing.T
require.Equal(t, http.StatusCreated, startRec.Code)
var started map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &started))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &started, jsonx.V1()))
runID := started["id"].(string)
tasksRec := doRequest(t, h, http.MethodGet, "/run/"+runID+"/task", nil)
require.Equal(t, http.StatusOK, tasksRec.Code)
var tasksResp map[string]any
- require.NoError(t, json.Unmarshal(tasksRec.Body.Bytes(), &tasksResp))
+ require.NoError(t, json.Unmarshal(tasksRec.Body.Bytes(), &tasksResp, jsonx.V1()))
tasks := tasksResp["items"].([]any)
require.Len(t, tasks, 1)
taskID := tasks[0].(map[string]any)["taskId"].(string)
@@ -183,14 +185,14 @@ func Test_GetRunTask_AdvancesPendingToRunningToCompletedAcrossPolls(t *testing.T
require.Equal(t, http.StatusOK, poll1.Code)
var p1 map[string]any
- require.NoError(t, json.Unmarshal(poll1.Body.Bytes(), &p1))
+ require.NoError(t, json.Unmarshal(poll1.Body.Bytes(), &p1, jsonx.V1()))
assert.Equal(t, "RUNNING", p1["status"])
poll2 := doRequest(t, h, http.MethodGet, "/run/"+runID+"/task/"+taskID, nil)
require.Equal(t, http.StatusOK, poll2.Code)
var p2 map[string]any
- require.NoError(t, json.Unmarshal(poll2.Body.Bytes(), &p2))
+ require.NoError(t, json.Unmarshal(poll2.Body.Bytes(), &p2, jsonx.V1()))
assert.Equal(t, "COMPLETED", p2["status"])
}
@@ -209,7 +211,7 @@ func Test_CancelRun_TerminalStateIsNotAdvancedByFurtherPolls(t *testing.T) {
require.Equal(t, http.StatusCreated, startRec.Code)
var started map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &started))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &started, jsonx.V1()))
id := started["id"].(string)
cancelRec := doRequest(t, h, http.MethodPost, "/run/"+id+"/cancel", nil)
@@ -220,7 +222,7 @@ func Test_CancelRun_TerminalStateIsNotAdvancedByFurtherPolls(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "CANCELLED", got["status"])
}
}
diff --git a/services/omics/list_summary_shapes_test.go b/services/omics/list_summary_shapes_test.go
index 2bc2f2afeb..42f90e7ed8 100644
--- a/services/omics/list_summary_shapes_test.go
+++ b/services/omics/list_summary_shapes_test.go
@@ -2,7 +2,8 @@ package omics_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -12,6 +13,8 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestListSummaries_MemberRoundTrips drives each op's Create/Start then List
@@ -166,7 +169,7 @@ func TestListReadSetUploadParts_RawBodyUsesPartSourceKey(t *testing.T) {
require.Equal(t, http.StatusCreated, storeRec.Code)
var store map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store, jsonx.V1()))
storeID := store["id"].(string)
uploadRec := doRequest(t, h, http.MethodPost, "/sequencestore/"+storeID+"/upload", map[string]any{
@@ -178,7 +181,7 @@ func TestListReadSetUploadParts_RawBodyUsesPartSourceKey(t *testing.T) {
require.Equal(t, http.StatusCreated, uploadRec.Code)
var upload map[string]any
- require.NoError(t, json.Unmarshal(uploadRec.Body.Bytes(), &upload))
+ require.NoError(t, json.Unmarshal(uploadRec.Body.Bytes(), &upload, jsonx.V1()))
uploadID := upload["uploadId"].(string)
partRec := doRequest(
@@ -198,7 +201,7 @@ func TestListReadSetUploadParts_RawBodyUsesPartSourceKey(t *testing.T) {
var listResp struct {
Parts []map[string]any `json:"parts"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Parts, 1)
assert.Equal(t, "SOURCE1", listResp.Parts[0]["partSource"])
@@ -244,7 +247,7 @@ func TestListSummaries_OmitDescribeOnlyFields(t *testing.T) {
require.Equal(t, http.StatusCreated, storeRec.Code)
var store map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store, jsonx.V1()))
storeID := store["id"].(string)
jobRec := doRequest(t, h, http.MethodPost, "/sequencestore/"+storeID+"/activationjob", map[string]any{
@@ -269,7 +272,7 @@ func TestListSummaries_OmitDescribeOnlyFields(t *testing.T) {
require.Equal(t, http.StatusCreated, storeRec.Code)
var store map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store, jsonx.V1()))
storeID := store["id"].(string)
jobRec := doRequest(t, h, http.MethodPost, "/sequencestore/"+storeID+"/importjob", map[string]any{
@@ -295,7 +298,7 @@ func TestListSummaries_OmitDescribeOnlyFields(t *testing.T) {
require.Equal(t, http.StatusCreated, storeRec.Code)
var store map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store, jsonx.V1()))
storeID := store["id"].(string)
jobRec := doRequest(t, h, http.MethodPost, "/sequencestore/"+storeID+"/importjob", map[string]any{
@@ -321,7 +324,7 @@ func TestListSummaries_OmitDescribeOnlyFields(t *testing.T) {
require.Equal(t, http.StatusCreated, storeRec.Code)
var store map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store, jsonx.V1()))
storeID := store["id"].(string)
jobRec := doRequest(t, h, http.MethodPost, "/referencestore/"+storeID+"/importjob", map[string]any{
@@ -347,7 +350,7 @@ func TestListSummaries_OmitDescribeOnlyFields(t *testing.T) {
require.Equal(t, http.StatusCreated, storeRec.Code)
var store map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &store, jsonx.V1()))
storeID := store["id"].(string)
jobRec := doRequest(t, h, http.MethodPost, "/referencestore/"+storeID+"/importjob", map[string]any{
@@ -413,7 +416,7 @@ func TestListSummaries_OmitDescribeOnlyFields(t *testing.T) {
require.Equal(t, http.StatusCreated, wfRec.Code)
var wf map[string]any
- require.NoError(t, json.Unmarshal(wfRec.Body.Bytes(), &wf))
+ require.NoError(t, json.Unmarshal(wfRec.Body.Bytes(), &wf, jsonx.V1()))
wfID := wf["id"].(string)
runRec := doRequest(t, h, http.MethodPost, "/run", map[string]any{
@@ -425,7 +428,7 @@ func TestListSummaries_OmitDescribeOnlyFields(t *testing.T) {
require.Equal(t, http.StatusCreated, runRec.Code)
var run map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &run))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &run, jsonx.V1()))
runID := run["id"].(string)
listRec := doRequest(t, h, http.MethodGet, "/run/"+runID+"/task", nil)
@@ -445,7 +448,7 @@ func TestListSummaries_OmitDescribeOnlyFields(t *testing.T) {
require.Equal(t, http.StatusCreated, wfRec.Code)
var wf map[string]any
- require.NoError(t, json.Unmarshal(wfRec.Body.Bytes(), &wf))
+ require.NoError(t, json.Unmarshal(wfRec.Body.Bytes(), &wf, jsonx.V1()))
wfID := wf["id"].(string)
runRec := doRequest(t, h, http.MethodPost, "/run", map[string]any{
@@ -478,7 +481,7 @@ func TestListSummaries_OmitDescribeOnlyFields(t *testing.T) {
require.Equal(t, http.StatusCreated, wfRec.Code)
var wf map[string]any
- require.NoError(t, json.Unmarshal(wfRec.Body.Bytes(), &wf))
+ require.NoError(t, json.Unmarshal(wfRec.Body.Bytes(), &wf, jsonx.V1()))
wfID := wf["id"].(string)
batchRec := doRequest(t, h, http.MethodPost, "/runBatch", map[string]any{
@@ -495,7 +498,7 @@ func TestListSummaries_OmitDescribeOnlyFields(t *testing.T) {
require.Equal(t, http.StatusCreated, batchRec.Code)
var batch map[string]any
- require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batch))
+ require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batch, jsonx.V1()))
batchID := batch["id"].(string)
listRec := doRequest(t, h, http.MethodGet, "/runBatch/"+batchID+"/run", nil)
@@ -543,7 +546,7 @@ func TestListSummaries_OmitDescribeOnlyFields(t *testing.T) {
require.Equal(t, http.StatusCreated, wfRec.Code)
var wf map[string]any
- require.NoError(t, json.Unmarshal(wfRec.Body.Bytes(), &wf))
+ require.NoError(t, json.Unmarshal(wfRec.Body.Bytes(), &wf, jsonx.V1()))
wfID := wf["id"].(string)
verRec := doRequest(t, h, http.MethodPost, "/workflow/"+wfID+"/version", map[string]any{
@@ -566,14 +569,14 @@ func TestListSummaries_OmitDescribeOnlyFields(t *testing.T) {
func listItems(t *testing.T, body []byte, key string) []map[string]any {
t.Helper()
- var resp map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(body, &resp))
+ var resp map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
raw, ok := resp[key]
require.True(t, ok, "response missing key %q", key)
var items []map[string]any
- require.NoError(t, json.Unmarshal(raw, &items))
+ require.NoError(t, json.Unmarshal(raw, &items, jsonx.V1()))
return items
}
diff --git a/services/omics/multi_region_test.go b/services/omics/multi_region_test.go
index 160187145f..dad3847770 100644
--- a/services/omics/multi_region_test.go
+++ b/services/omics/multi_region_test.go
@@ -2,7 +2,8 @@ package omics_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/omics"
)
@@ -172,8 +174,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/omics/pagination_query_params_test.go b/services/omics/pagination_query_params_test.go
index 3e37de9490..c36d89d4ac 100644
--- a/services/omics/pagination_query_params_test.go
+++ b/services/omics/pagination_query_params_test.go
@@ -18,12 +18,14 @@ package omics_test
// "maxResults" as the query key; that is covered separately below.
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func Test_ListReferenceStores_PaginatesViaQueryString(t *testing.T) {
@@ -46,7 +48,7 @@ func Test_ListReferenceStores_PaginatesViaQueryString(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stores := resp["referenceStores"].([]any)
assert.Len(t, stores, 2, "maxResults=2 from the query string must be honored")
require.NotEmpty(t, resp["nextToken"], "a nextToken must be returned when more pages remain")
@@ -59,7 +61,7 @@ func Test_ListReferenceStores_PaginatesViaQueryString(t *testing.T) {
require.Equal(t, http.StatusOK, nextRec.Code)
var nextResp map[string]any
- require.NoError(t, json.Unmarshal(nextRec.Body.Bytes(), &nextResp))
+ require.NoError(t, json.Unmarshal(nextRec.Body.Bytes(), &nextResp, jsonx.V1()))
remaining := nextResp["referenceStores"].([]any)
assert.Len(t, remaining, 1, "nextToken from the query string must resume from page two")
}
@@ -81,7 +83,7 @@ func Test_ListAnnotationStores_PaginatesViaQueryString(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stores := resp["annotationStores"].([]any)
assert.Len(t, stores, 2)
require.NotEmpty(t, resp["nextToken"])
@@ -104,13 +106,13 @@ func Test_ListRunBatches_UsesMaxItemsNotMaxResultsQueryParam(t *testing.T) {
require.Equal(t, http.StatusOK, ignoredRec.Code)
var ignoredResp map[string]any
- require.NoError(t, json.Unmarshal(ignoredRec.Body.Bytes(), &ignoredResp))
+ require.NoError(t, json.Unmarshal(ignoredRec.Body.Bytes(), &ignoredResp, jsonx.V1()))
assert.Len(t, ignoredResp["items"].([]any), 3, "maxResults must not cap ListBatch")
limitedRec := doRequest(t, h, http.MethodGet, "/runBatch?maxItems=1", nil)
require.Equal(t, http.StatusOK, limitedRec.Code)
var limitedResp map[string]any
- require.NoError(t, json.Unmarshal(limitedRec.Body.Bytes(), &limitedResp))
+ require.NoError(t, json.Unmarshal(limitedRec.Body.Bytes(), &limitedResp, jsonx.V1()))
assert.Len(t, limitedResp["items"].([]any), 1, "maxItems must cap ListBatch")
}
diff --git a/services/omics/persistence.go b/services/omics/persistence.go
index 4ea867662e..3ce9b72b5e 100644
--- a/services/omics/persistence.go
+++ b/services/omics/persistence.go
@@ -2,7 +2,7 @@ package omics
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -40,7 +40,7 @@ const omicsSnapshotVersion = 2
// restored directly. The five left-raw maps (see the InMemoryBackend doc
// comment in store.go) are persisted as plain fields alongside Tables.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
UploadParts map[string]map[string][]*ReadSetUploadPart `json:"uploadParts"`
UploadPartData map[string]map[string]map[string]map[int][]byte `json:"uploadPartData"`
ReadSetBytes map[string]map[string][]byte `json:"readSetBytes"`
diff --git a/services/omics/run_options_test.go b/services/omics/run_options_test.go
index 3d4181be6f..8b49ec326d 100644
--- a/services/omics/run_options_test.go
+++ b/services/omics/run_options_test.go
@@ -1,20 +1,22 @@
package omics_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func decodeBody(t *testing.T, body []byte) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(body, &m))
+ require.NoError(t, json.Unmarshal(body, &m, jsonx.V1()))
return m
}
diff --git a/services/omics/runbatch_route_test.go b/services/omics/runbatch_route_test.go
index 53c9700d6c..8bcd8f6683 100644
--- a/services/omics/runbatch_route_test.go
+++ b/services/omics/runbatch_route_test.go
@@ -15,7 +15,7 @@ package omics_test
// batch *records* — the reverse of what those two real operations do.
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -23,6 +23,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Test_ListRunsInBatch_ReachableOnlyViaGET proves the real AWS wire shape
@@ -37,14 +39,14 @@ func Test_ListRunsInBatch_ReachableOnlyViaGET(t *testing.T) {
require.Equal(t, http.StatusCreated, batchRec.Code)
var batch map[string]any
- require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batch))
+ require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batch, jsonx.V1()))
batchID := batch["id"].(string)
getRec := doRequest(t, h, http.MethodGet, "/runBatch/"+batchID+"/run", nil)
assert.Equal(t, http.StatusOK, getRec.Code, "real ListRunsInBatch is GET")
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.NotNil(t, getResp["runs"], "GET must be classified as ListRunsInBatch")
e := echo.New()
@@ -68,7 +70,7 @@ func Test_DeleteBatch_DeletesBatchResourceViaURIID(t *testing.T) {
require.Equal(t, http.StatusCreated, batchRec.Code)
var batch map[string]any
- require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batch))
+ require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batch, jsonx.V1()))
batchID := batch["id"].(string)
delRec := doRequest(t, h, http.MethodDelete, "/runBatch/"+batchID, nil)
@@ -92,7 +94,7 @@ func Test_DeleteRunBatch_DeletesOnlyRunsLeavesBatchIntact(t *testing.T) {
require.Equal(t, http.StatusCreated, batchRec.Code)
var batch map[string]any
- require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batch))
+ require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batch, jsonx.V1()))
batchID := batch["id"].(string)
runRec := doRequest(t, h, http.MethodPost, "/run", map[string]any{
@@ -104,7 +106,7 @@ func Test_DeleteRunBatch_DeletesOnlyRunsLeavesBatchIntact(t *testing.T) {
require.Equal(t, http.StatusCreated, runRec.Code)
var run map[string]any
- require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &run))
+ require.NoError(t, json.Unmarshal(runRec.Body.Bytes(), &run, jsonx.V1()))
runID := run["id"].(string)
delRec := doRequest(t, h, http.MethodPost, "/runBatch/delete", map[string]any{
@@ -113,7 +115,7 @@ func Test_DeleteRunBatch_DeletesOnlyRunsLeavesBatchIntact(t *testing.T) {
require.Equal(t, http.StatusOK, delRec.Code)
var delResp map[string]any
- require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &delResp))
+ require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &delResp, jsonx.V1()))
assert.Empty(t, delResp, "real DeleteRunBatchOutput is empty — no errors array")
getBatchRec := doRequest(t, h, http.MethodGet, "/runBatch/"+batchID, nil)
diff --git a/services/omics/s3_source.go b/services/omics/s3_source.go
index c6f4fdc5fe..3b5a3139a0 100644
--- a/services/omics/s3_source.go
+++ b/services/omics/s3_source.go
@@ -2,10 +2,12 @@ package omics
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/url"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -78,7 +80,7 @@ func (h *Handler) registryMapFromURI(ctx context.Context, m map[string]any, uri
}
var out map[string]any
- if err = json.Unmarshal(data, &out); err != nil || out == nil {
+ if err = json.Unmarshal(data, &out, jsonx.V1()); err != nil || out == nil {
return nil, fmt.Errorf("%w: containerRegistryMapUri must contain a JSON object", ErrValidation)
}
@@ -92,7 +94,7 @@ func (h *Handler) inlineSettingsFromURI(ctx context.Context, uri string) ([]inli
}
var out []inlineRunSettingWire
- if err = json.Unmarshal(data, &out); err != nil {
+ if err = json.Unmarshal(data, &out, jsonx.V1()); err != nil {
return nil, fmt.Errorf(
"%w: batchRunSettings.s3UriSettings must contain a JSON array of run settings",
ErrValidation,
diff --git a/services/omics/wire_field_additions_test.go b/services/omics/wire_field_additions_test.go
index e541cb2311..9799aa07fd 100644
--- a/services/omics/wire_field_additions_test.go
+++ b/services/omics/wire_field_additions_test.go
@@ -2,7 +2,7 @@ package omics_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -20,6 +20,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/omics"
)
@@ -578,7 +579,7 @@ func TestListAnnotationImportJobs_OmitsGetOnlyFields(t *testing.T) {
var resp struct {
ImportJobs []map[string]any `json:"annotationImportJobs"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.ImportJobs, 1)
job := resp.ImportJobs[0]
@@ -626,7 +627,7 @@ func TestListVariantImportJobs_OmitsGetOnlyFields(t *testing.T) {
var resp struct {
ImportJobs []map[string]any `json:"variantImportJobs"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.ImportJobs, 1)
job := resp.ImportJobs[0]
@@ -682,7 +683,7 @@ func TestOmicsStoreLists_OmitGetOnlyFields(t *testing.T) {
var resp struct {
Stores []map[string]any `json:"annotationStores"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Stores, 1)
return resp.Stores[0]
@@ -708,7 +709,7 @@ func TestOmicsStoreLists_OmitGetOnlyFields(t *testing.T) {
var resp struct {
Stores []map[string]any `json:"variantStores"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Stores, 1)
return resp.Stores[0]
@@ -737,7 +738,7 @@ func TestOmicsStoreLists_OmitGetOnlyFields(t *testing.T) {
var resp struct {
Versions []map[string]any `json:"annotationStoreVersions"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Versions, 1)
return resp.Versions[0]
diff --git a/services/opensearch/access_policy.go b/services/opensearch/access_policy.go
index f714881411..da063e14eb 100644
--- a/services/opensearch/access_policy.go
+++ b/services/opensearch/access_policy.go
@@ -1,9 +1,10 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iam"
)
@@ -28,7 +29,7 @@ func accessPolicyDenies(accessPolicies, action, resourceARN string) bool {
}
var pd iam.PolicyDocument
- if err := json.Unmarshal([]byte(accessPolicies), &pd); err != nil {
+ if err := json.Unmarshal([]byte(accessPolicies), &pd, jsonx.V1()); err != nil {
return false
}
diff --git a/services/opensearch/bulk_test.go b/services/opensearch/bulk_test.go
index 4ec6401b32..99503156e5 100644
--- a/services/opensearch/bulk_test.go
+++ b/services/opensearch/bulk_test.go
@@ -2,7 +2,7 @@ package opensearch_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -33,7 +34,7 @@ func doBulkRequest(t *testing.T, h *opensearch.Handler, path, contentType, ndjso
var out map[string]any
if len(raw) > 0 {
- require.NoError(t, json.Unmarshal(raw, &out))
+ require.NoError(t, json.Unmarshal(raw, &out, jsonx.V1()))
}
return resp.StatusCode, out
diff --git a/services/opensearch/data_sources.go b/services/opensearch/data_sources.go
index 18ea2c9aee..8b0db4d17d 100644
--- a/services/opensearch/data_sources.go
+++ b/services/opensearch/data_sources.go
@@ -1,7 +1,7 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
@@ -14,7 +14,7 @@ const dataSourceStatusActive = "ACTIVE"
// AddDataSource adds a data source to a domain.
func (b *InMemoryBackend) AddDataSource(
domainName, name, description string,
- dataSourceType json.RawMessage,
+ dataSourceType jsontext.Value,
) (string, error) {
if domainName == "" {
return "", fmt.Errorf("%w: DomainName is required", ErrInvalidParameter)
@@ -54,7 +54,7 @@ func (b *InMemoryBackend) AddDataSource(
// AddDirectQueryDataSource adds a direct-query data source.
func (b *InMemoryBackend) AddDirectQueryDataSource(
name, description string,
- dataSourceType json.RawMessage,
+ dataSourceType jsontext.Value,
openSearchArns []string,
dataSourceAccessPolicy ...string,
) (string, error) {
@@ -132,7 +132,7 @@ func (b *InMemoryBackend) ListDataSources(domainName string) ([]*DataSource, err
// Status are optional and left unchanged when omitted.
func (b *InMemoryBackend) UpdateDataSource(
domainName, name, description string,
- dataSourceType json.RawMessage,
+ dataSourceType jsontext.Value,
status string,
) error {
b.mu.Lock("UpdateDataSource")
@@ -209,7 +209,7 @@ func (b *InMemoryBackend) GetDirectQueryDataSource(name string) (*DirectQueryDat
// and OpenSearchArns on every update call.
func (b *InMemoryBackend) UpdateDirectQueryDataSource(
name, description string,
- dataSourceType json.RawMessage,
+ dataSourceType jsontext.Value,
openSearchArns []string,
dataSourceAccessPolicy string,
) (*DirectQueryDataSource, error) {
diff --git a/services/opensearch/documents_handler_test.go b/services/opensearch/documents_handler_test.go
index c490890057..699f1579d2 100644
--- a/services/opensearch/documents_handler_test.go
+++ b/services/opensearch/documents_handler_test.go
@@ -1,7 +1,7 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -23,7 +24,7 @@ func decodeBody(t *testing.T, resp *http.Response) map[string]any {
var out map[string]any
if len(raw) > 0 {
- require.NoError(t, json.Unmarshal(raw, &out))
+ require.NoError(t, json.Unmarshal(raw, &out, jsonx.V1()))
}
return out
diff --git a/services/opensearch/domains_test.go b/services/opensearch/domains_test.go
index eb6762cecf..bf56474b0e 100644
--- a/services/opensearch/domains_test.go
+++ b/services/opensearch/domains_test.go
@@ -1,7 +1,7 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"testing"
"github.com/stretchr/testify/assert"
@@ -40,7 +40,7 @@ func TestDeleteDomain_Cascade(t *testing.T) {
b := opensearch.NewInMemoryBackend(testAccountID, testRegion)
b.AddDomainInternal("my-domain", "")
- _, err := b.AddDataSource("my-domain", "ds1", "desc", json.RawMessage(`{"S3GlueDataCatalog":{}}`))
+ _, err := b.AddDataSource("my-domain", "ds1", "desc", jsontext.Value(`{"S3GlueDataCatalog":{}}`))
require.NoError(t, err)
b.AddPackageInternal("pkg-001", "test-pkg", "TXT-DICTIONARY")
diff --git a/services/opensearch/handler.go b/services/opensearch/handler.go
index ee1c6898f3..1bcaa4922e 100644
--- a/services/opensearch/handler.go
+++ b/services/opensearch/handler.go
@@ -1,7 +1,7 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/endpointstrategy"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -504,7 +505,7 @@ func (h *Handler) handleConfigPostRoute(w http.ResponseWriter, r *http.Request,
body, _ := httputils.ReadBody(r)
var req domainJSON
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
if vErr := validateDomainOptions(&req); vErr != nil {
@@ -833,7 +834,7 @@ func (h *Handler) dispatchDomainPostRoutesExtended(
NodeID string `json:"NodeId"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
m, err := h.Backend.StartDomainMaintenance(domainName, req.Action, req.NodeID)
if err != nil {
@@ -850,7 +851,7 @@ func (h *Handler) dispatchDomainPostRoutesExtended(
Account string `json:"Account"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
_ = h.Backend.RevokeVpcEndpointAccess(domainName, req.Account)
w.WriteHeader(http.StatusOK)
diff --git a/services/opensearch/handler_advanced.go b/services/opensearch/handler_advanced.go
index 104b974477..efccba51a7 100644
--- a/services/opensearch/handler_advanced.go
+++ b/services/opensearch/handler_advanced.go
@@ -1,7 +1,7 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -9,6 +9,7 @@ import (
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -159,7 +160,7 @@ func (h *Handler) handleUpgradeDomainRoutes(w http.ResponseWriter, r *http.Reque
PerformCheckOnly bool `json:"PerformCheckOnly"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
upgradeErr := h.Backend.CheckUpgradeDomain(req.DomainName, req.TargetVersion)
diff --git a/services/opensearch/handler_advanced_test.go b/services/opensearch/handler_advanced_test.go
index 0546e25ee3..380979aec5 100644
--- a/services/opensearch/handler_advanced_test.go
+++ b/services/opensearch/handler_advanced_test.go
@@ -1,13 +1,15 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -75,7 +77,7 @@ func TestOpenSearchHandler_GetCompatibleVersions(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
versions, ok := out["CompatibleVersions"].([]any)
require.True(t, ok)
@@ -102,7 +104,7 @@ func TestOpenSearchHandler_ListInstanceTypeDetails(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
details, ok := out["InstanceTypeDetails"].([]any)
require.True(t, ok)
@@ -168,7 +170,7 @@ func TestGetCompatibleVersions(t *testing.T) {
}
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
versions, ok := out["CompatibleVersions"].([]any)
require.True(t, ok)
@@ -198,7 +200,7 @@ func TestListInstanceTypeDetails_InstanceRoleAndSecurity(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
details, ok := out["InstanceTypeDetails"].([]any)
require.True(t, ok)
@@ -268,7 +270,7 @@ func TestListVersions_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
versions, ok := out["Versions"].([]any)
require.True(t, ok)
@@ -303,7 +305,7 @@ func TestListVersions_NextTokenContinuation(t *testing.T) {
require.Equal(t, http.StatusOK, resp1.StatusCode)
var page1 map[string]any
- require.NoError(t, json.NewDecoder(resp1.Body).Decode(&page1))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp1.Body), &page1, jsonx.V1()))
versions1 := page1["Versions"].([]any)
assert.Len(t, versions1, 3)
@@ -316,7 +318,7 @@ func TestListVersions_NextTokenContinuation(t *testing.T) {
require.Equal(t, http.StatusOK, resp2.StatusCode)
var page2 map[string]any
- require.NoError(t, json.NewDecoder(resp2.Body).Decode(&page2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp2.Body), &page2, jsonx.V1()))
versions2 := page2["Versions"].([]any)
assert.NotEmpty(t, versions2)
@@ -447,7 +449,7 @@ func TestDescribeInstanceTypeLimits_ResponseFormat(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
limitsByRole, ok := out["LimitsByRole"].(map[string]any)
require.True(t, ok, "LimitsByRole must be an object")
@@ -517,7 +519,7 @@ func TestUpgradeDomain_HTTPHandler(t *testing.T) {
if len(tt.wantFields) > 0 {
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
for _, field := range tt.wantFields {
assert.Contains(t, out, field)
}
@@ -590,7 +592,7 @@ func TestListInstanceTypeDetails_HTTPHandler(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
details, ok := out["InstanceTypeDetails"].([]any)
require.True(t, ok)
diff --git a/services/opensearch/handler_applications.go b/services/opensearch/handler_applications.go
index 9ea4e60b5f..f37f08500b 100644
--- a/services/opensearch/handler_applications.go
+++ b/services/opensearch/handler_applications.go
@@ -1,7 +1,7 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -9,6 +9,7 @@ import (
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -150,7 +151,7 @@ func (h *Handler) handleDefaultApplicationSettingRoutes(w http.ResponseWriter, r
}
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
@@ -215,7 +216,7 @@ func (h *Handler) handleApplicationIDRoutes(w http.ResponseWriter, r *http.Reque
DataSources []appDSJSON `json:"DataSources"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
appConfigs := make([]AppConfig, len(req.AppConfigs))
for i, ac := range req.AppConfigs {
@@ -289,7 +290,7 @@ func (h *Handler) handleCreateApplication(w http.ResponseWriter, r *http.Request
}
var req createApplicationRequest
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/opensearch/handler_applications_test.go b/services/opensearch/handler_applications_test.go
index 2d1484def3..7f023ebe13 100644
--- a/services/opensearch/handler_applications_test.go
+++ b/services/opensearch/handler_applications_test.go
@@ -1,7 +1,8 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -133,7 +135,7 @@ func TestApplications_HTTPHandler(t *testing.T) {
require.Equal(t, http.StatusOK, lr.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(lr.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(lr.Body), &out, jsonx.V1()))
apps, ok := out["ApplicationSummaries"].([]any)
require.True(t, ok)
@@ -156,7 +158,7 @@ func TestApplications_GetAndUpdateWireShape(t *testing.T) {
cr := doRequest(t, h, http.MethodPost, "/2021-01-01/opensearch/application",
map[string]any{"Name": "wire-app"})
var cOut map[string]any
- require.NoError(t, json.NewDecoder(cr.Body).Decode(&cOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(cr.Body), &cOut, jsonx.V1()))
cr.Body.Close()
require.Equal(t, http.StatusOK, cr.StatusCode)
@@ -172,7 +174,7 @@ func TestApplications_GetAndUpdateWireShape(t *testing.T) {
require.Equal(t, http.StatusOK, gr.StatusCode)
var gOut map[string]any
- require.NoError(t, json.NewDecoder(gr.Body).Decode(&gOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(gr.Body), &gOut, jsonx.V1()))
assert.Equal(t, "ACTIVE", gOut["status"])
assert.NotEmpty(t, gOut["endpoint"])
assert.NotEmpty(t, gOut["createdAt"])
@@ -185,7 +187,7 @@ func TestApplications_GetAndUpdateWireShape(t *testing.T) {
require.Equal(t, http.StatusOK, ur.StatusCode)
var uOut map[string]any
- require.NoError(t, json.NewDecoder(ur.Body).Decode(&uOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(ur.Body), &uOut, jsonx.V1()))
assert.NotContains(t, uOut, "status")
assert.NotEmpty(t, uOut["createdAt"])
assert.NotEmpty(t, uOut["lastUpdatedAt"])
@@ -281,7 +283,7 @@ func createTestApplication(t *testing.T, h *opensearch.Handler, name string) str
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
id, ok := out["id"].(string)
require.True(t, ok)
@@ -301,7 +303,7 @@ func domainARN(t *testing.T, h *opensearch.Handler, domainName string) string {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status, ok := out["DomainStatus"].(map[string]any)
require.True(t, ok)
@@ -340,7 +342,7 @@ func TestDataSourceAttachments_HTTPHandler(t *testing.T) {
require.Equal(t, http.StatusOK, ar.StatusCode)
var attOut map[string]any
- require.NoError(t, json.NewDecoder(ar.Body).Decode(&attOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(ar.Body), &attOut, jsonx.V1()))
assert.Equal(t, appID, attOut["id"])
assert.Equal(t, arn, attOut["dataSourceArn"])
assert.Equal(t, arn, attOut["arn"])
@@ -356,7 +358,7 @@ func TestDataSourceAttachments_HTTPHandler(t *testing.T) {
map[string]any{"dataSourceArn": arn})
defer ar2.Body.Close()
var attOut2 map[string]any
- require.NoError(t, json.NewDecoder(ar2.Body).Decode(&attOut2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(ar2.Body), &attOut2, jsonx.V1()))
assert.Equal(t, attOut["attachmentId"], attOut2["attachmentId"])
lr := doRequest(t, h, http.MethodPost,
@@ -365,7 +367,7 @@ func TestDataSourceAttachments_HTTPHandler(t *testing.T) {
require.Equal(t, http.StatusOK, lr.StatusCode)
var listOut map[string]any
- require.NoError(t, json.NewDecoder(lr.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(lr.Body), &listOut, jsonx.V1()))
attachments, ok := listOut["attachments"].([]any)
require.True(t, ok)
require.Len(t, attachments, 1)
@@ -384,7 +386,7 @@ func TestDataSourceAttachments_HTTPHandler(t *testing.T) {
require.Equal(t, http.StatusOK, dr.StatusCode)
var descOut map[string]any
- require.NoError(t, json.NewDecoder(dr.Body).Decode(&descOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(dr.Body), &descOut, jsonx.V1()))
assert.Equal(t, "ATTACHED", descOut["status"])
detR := doRequest(t, h, http.MethodPost,
@@ -619,7 +621,7 @@ func TestCapabilities_HTTPHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rr.StatusCode)
var regOut map[string]any
- require.NoError(t, json.NewDecoder(rr.Body).Decode(®Out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rr.Body), ®Out, jsonx.V1()))
assert.Equal(t, appID, regOut["applicationId"])
assert.Equal(t, "ai-capability", regOut["capabilityName"])
assert.Equal(t, "active", regOut["status"])
@@ -633,7 +635,7 @@ func TestCapabilities_HTTPHandler(t *testing.T) {
require.Equal(t, http.StatusOK, gr.StatusCode)
var getOut map[string]any
- require.NoError(t, json.NewDecoder(gr.Body).Decode(&getOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(gr.Body), &getOut, jsonx.V1()))
assert.Equal(t, "active", getOut["status"])
assert.Equal(t, []any{}, getOut["failures"])
@@ -643,7 +645,7 @@ func TestCapabilities_HTTPHandler(t *testing.T) {
require.Equal(t, http.StatusOK, dr.StatusCode)
var deregOut map[string]any
- require.NoError(t, json.NewDecoder(dr.Body).Decode(&deregOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(dr.Body), &deregOut, jsonx.V1()))
// DeregisterCapabilityOutput always reports "deleting" per its doc,
// regardless of this emulator's actual (synchronous) removal.
assert.Equal(t, "deleting", deregOut["status"])
@@ -744,7 +746,7 @@ func TestMigrations_HTTPHandler(t *testing.T) {
require.Equal(t, http.StatusOK, sr.StatusCode)
var startOut map[string]any
- require.NoError(t, json.NewDecoder(sr.Body).Decode(&startOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(sr.Body), &startOut, jsonx.V1()))
migrationID, ok := startOut["migrationId"].(string)
require.True(t, ok)
require.NotEmpty(t, migrationID)
@@ -757,7 +759,7 @@ func TestMigrations_HTTPHandler(t *testing.T) {
require.Equal(t, http.StatusOK, gr.StatusCode)
var getOut map[string]any
- require.NoError(t, json.NewDecoder(gr.Body).Decode(&getOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(gr.Body), &getOut, jsonx.V1()))
assert.Equal(t, appID, getOut["applicationId"])
assert.Equal(t, "SUCCEEDED", getOut["status"])
// No saved-object store exists to migrate real objects, so counts
@@ -774,7 +776,7 @@ func TestMigrations_HTTPHandler(t *testing.T) {
require.Equal(t, http.StatusOK, lr.StatusCode)
var listOut map[string]any
- require.NoError(t, json.NewDecoder(lr.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(lr.Body), &listOut, jsonx.V1()))
migrations, ok := listOut["migrations"].([]any)
require.True(t, ok)
require.Len(t, migrations, 1)
@@ -783,7 +785,7 @@ func TestMigrations_HTTPHandler(t *testing.T) {
"/2021-01-01/opensearch/app-migrations?applicationId="+appID+"&status=PENDING", nil)
defer filteredR.Body.Close()
var filteredOut map[string]any
- require.NoError(t, json.NewDecoder(filteredR.Body).Decode(&filteredOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(filteredR.Body), &filteredOut, jsonx.V1()))
assert.Empty(t, filteredOut["migrations"])
},
},
diff --git a/services/opensearch/handler_bulk.go b/services/opensearch/handler_bulk.go
index 7c508df9a4..b3be4e2030 100644
--- a/services/opensearch/handler_bulk.go
+++ b/services/opensearch/handler_bulk.go
@@ -2,13 +2,15 @@ package opensearch
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// bulkActionMeta is the per-action metadata object documented for every
@@ -38,9 +40,9 @@ type bulkActionEnvelope struct {
// scripting engine, so a non-empty Script is rejected (see
// handleBulkUpdate).
type bulkUpdateBody struct {
- Doc map[string]any `json:"doc,omitempty"`
- Script json.RawMessage `json:"script,omitempty"`
- DocAsUpsert bool `json:"doc_as_upsert,omitempty"`
+ Doc map[string]any `json:"doc,omitempty"`
+ Script jsontext.Value `json:"script,omitempty"`
+ DocAsUpsert bool `json:"doc_as_upsert,omitempty"`
}
// bulkAction is one parsed action line plus its source (nil for "delete").
@@ -48,7 +50,7 @@ type bulkAction struct {
Op string
Index string
ID string
- Source json.RawMessage
+ Source jsontext.Value
}
const (
@@ -186,7 +188,7 @@ func parseBulkActions(body []byte) ([]bulkAction, error) {
for i := 0; i < len(lines); i++ {
var env bulkActionEnvelope
- if err := json.Unmarshal(lines[i], &env); err != nil {
+ if err := json.Unmarshal(lines[i], &env, jsonx.V1()); err != nil {
return nil, fmt.Errorf("malformed action line %d: %w", i+1, err)
}
@@ -238,7 +240,7 @@ func (h *Handler) processBulkAction(domainName, defaultIndex string, act bulkAct
}
}
-func (h *Handler) processBulkIndex(domainName, index, id string, source json.RawMessage) (map[string]any, bool) {
+func (h *Handler) processBulkIndex(domainName, index, id string, source jsontext.Value) (map[string]any, bool) {
doc, err := unmarshalBulkSource(source)
if err != nil {
return bulkErrorItem(bulkOpIndex, index, id, http.StatusBadRequest,
@@ -258,7 +260,7 @@ func (h *Handler) processBulkIndex(domainName, index, id string, source json.Raw
return bulkSuccessItem(bulkOpIndex, index, newID, result, meta), false
}
-func (h *Handler) processBulkCreate(domainName, index, id string, source json.RawMessage) (map[string]any, bool) {
+func (h *Handler) processBulkCreate(domainName, index, id string, source jsontext.Value) (map[string]any, bool) {
doc, err := unmarshalBulkSource(source)
if err != nil {
return bulkErrorItem(bulkOpCreate, index, id, http.StatusBadRequest,
@@ -273,7 +275,7 @@ func (h *Handler) processBulkCreate(domainName, index, id string, source json.Ra
return bulkSuccessItem(bulkOpCreate, index, newID, docResultCreated, meta), false
}
-func (h *Handler) processBulkUpdate(domainName, index, id string, source json.RawMessage) (map[string]any, bool) {
+func (h *Handler) processBulkUpdate(domainName, index, id string, source jsontext.Value) (map[string]any, bool) {
if id == "" {
return bulkErrorItem(bulkOpUpdate, index, id, http.StatusBadRequest,
"illegal_argument_exception", "_id is required for the update action"), true
@@ -281,7 +283,7 @@ func (h *Handler) processBulkUpdate(domainName, index, id string, source json.Ra
var body bulkUpdateBody
if len(source) > 0 {
- if err := json.Unmarshal(source, &body); err != nil {
+ if err := json.Unmarshal(source, &body, jsonx.V1()); err != nil {
return bulkErrorItem(bulkOpUpdate, index, id, http.StatusBadRequest,
"illegal_argument_exception", "invalid update body"), true
}
@@ -339,13 +341,13 @@ func (h *Handler) processBulkDelete(domainName, index, id string) (map[string]an
// unmarshalBulkSource decodes a bulk action's source line, tolerating an
// absent one (an empty document, matching IndexDocument's own handling of a
// missing body).
-func unmarshalBulkSource(source json.RawMessage) (map[string]any, error) {
+func unmarshalBulkSource(source jsontext.Value) (map[string]any, error) {
if len(source) == 0 {
return map[string]any{}, nil
}
doc := map[string]any{}
- if err := json.Unmarshal(source, &doc); err != nil {
+ if err := json.Unmarshal(source, &doc, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/opensearch/handler_capabilities.go b/services/opensearch/handler_capabilities.go
index 5189667b78..5b6d601d7c 100644
--- a/services/opensearch/handler_capabilities.go
+++ b/services/opensearch/handler_capabilities.go
@@ -1,12 +1,13 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// capabilityConfigJSON is the wire shape of CapabilityBaseRequestConfig /
@@ -71,7 +72,7 @@ func (h *Handler) handleRegisterCapability(w http.ResponseWriter, r *http.Reques
var req registerCapabilityRequest
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/opensearch/handler_data_source_attachments.go b/services/opensearch/handler_data_source_attachments.go
index ba671b72fd..6e4b1f1461 100644
--- a/services/opensearch/handler_data_source_attachments.go
+++ b/services/opensearch/handler_data_source_attachments.go
@@ -1,11 +1,12 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Sub-path segments for the four data source attachment routes under
@@ -104,7 +105,7 @@ func (h *Handler) readDataSourceAttachmentRequest(
var req dataSourceAttachmentRequest
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return dataSourceAttachmentRequest{}, false
@@ -191,7 +192,7 @@ func (h *Handler) handleListDataSourceAttachments(w http.ResponseWriter, r *http
var req listDataSourceAttachmentsRequest
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/opensearch/handler_data_sources.go b/services/opensearch/handler_data_sources.go
index ae193a16fb..32e97115b8 100644
--- a/services/opensearch/handler_data_sources.go
+++ b/services/opensearch/handler_data_sources.go
@@ -1,12 +1,14 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// directQueryDataSourceJSON renders a DirectQueryDataSource as the wire-shape
@@ -15,11 +17,11 @@ import (
// "DataSourceName" -- they are genuinely different field names on the real
// API, not a typo.
type directQueryDataSourceJSON struct {
- DataSourceArn string `json:"DataSourceArn"`
- DataSourceName string `json:"DataSourceName"`
- DataSourceType json.RawMessage `json:"DataSourceType,omitempty"`
- Description string `json:"Description,omitempty"`
- OpenSearchArns []string `json:"OpenSearchArns"`
+ DataSourceArn string `json:"DataSourceArn"`
+ DataSourceName string `json:"DataSourceName"`
+ DataSourceType jsontext.Value `json:"DataSourceType,omitempty"`
+ Description string `json:"Description,omitempty"`
+ OpenSearchArns []string `json:"OpenSearchArns"`
}
func toDirectQueryDataSourceJSON(ds *DirectQueryDataSource) directQueryDataSourceJSON {
@@ -44,10 +46,10 @@ type getDirectQueryDataSourceOutputJSON struct {
// dataSourceJSON renders a DataSource as the wire-shape types.DataSourceDetails
// / GetDataSourceOutput object (top-level fields, no envelope).
type dataSourceJSON struct {
- Description string `json:"Description,omitempty"`
- Name string `json:"Name"`
- Status string `json:"Status,omitempty"`
- DataSourceType json.RawMessage `json:"DataSourceType,omitempty"`
+ Description string `json:"Description,omitempty"`
+ Name string `json:"Name"`
+ Status string `json:"Status,omitempty"`
+ DataSourceType jsontext.Value `json:"DataSourceType,omitempty"`
}
func toDataSourceJSON(ds *DataSource) dataSourceJSON {
@@ -129,13 +131,13 @@ func (h *Handler) handleUpdateDirectQueryDataSource(
return
}
var req struct {
- DataSourceType json.RawMessage `json:"DataSourceType"`
- Description string `json:"Description"`
- DataSourceAccessPolicy string `json:"DataSourceAccessPolicy"`
- OpenSearchArns []string `json:"OpenSearchArns"`
+ DataSourceType jsontext.Value `json:"DataSourceType"`
+ Description string `json:"Description"`
+ DataSourceAccessPolicy string `json:"DataSourceAccessPolicy"`
+ OpenSearchArns []string `json:"OpenSearchArns"`
}
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
@@ -160,9 +162,9 @@ func (h *Handler) handleUpdateDirectQueryDataSource(
// (types.UpdateDataSourceInput -- DomainName and Name come from the URL
// path, not the body).
type updateDataSourceRequest struct {
- Description string `json:"Description"`
- Status string `json:"Status"`
- DataSourceType json.RawMessage `json:"DataSourceType"`
+ Description string `json:"Description"`
+ Status string `json:"Status"`
+ DataSourceType jsontext.Value `json:"DataSourceType"`
}
// handleUpdateDataSource handles PUT /2021-01-01/opensearch/domain/{DomainName}/dataSource/{Name}.
@@ -176,7 +178,7 @@ func (h *Handler) handleUpdateDataSource(w http.ResponseWriter, r *http.Request,
var req updateDataSourceRequest
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
@@ -195,9 +197,9 @@ func (h *Handler) handleUpdateDataSource(w http.ResponseWriter, r *http.Request,
// addDataSourceRequest is the JSON request body for AddDataSource.
type addDataSourceRequest struct {
- Name string `json:"Name"`
- Description string `json:"Description"`
- DataSourceType json.RawMessage `json:"DataSourceType"`
+ Name string `json:"Name"`
+ Description string `json:"Description"`
+ DataSourceType jsontext.Value `json:"DataSourceType"`
}
// addDataSourceOutput is the JSON response for AddDataSource.
@@ -214,7 +216,7 @@ func (h *Handler) handleAddDataSource(w http.ResponseWriter, r *http.Request, do
}
var req addDataSourceRequest
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
@@ -246,11 +248,11 @@ func (h *Handler) handleAddDataSource(w http.ResponseWriter, r *http.Request, do
// addDirectQueryDataSourceRequest is the JSON request body for AddDirectQueryDataSource.
type addDirectQueryDataSourceRequest struct {
- DataSourceName string `json:"DataSourceName"`
- Description string `json:"Description"`
- DataSourceType json.RawMessage `json:"DataSourceType"`
- DataSourceAccessPolicy string `json:"DataSourceAccessPolicy"`
- OpenSearchArns []string `json:"OpenSearchArns"`
+ DataSourceName string `json:"DataSourceName"`
+ Description string `json:"Description"`
+ DataSourceType jsontext.Value `json:"DataSourceType"`
+ DataSourceAccessPolicy string `json:"DataSourceAccessPolicy"`
+ OpenSearchArns []string `json:"OpenSearchArns"`
}
// addDirectQueryDataSourceOutput is the JSON response for AddDirectQueryDataSource.
@@ -267,7 +269,7 @@ func (h *Handler) handleAddDirectQueryDataSource(w http.ResponseWriter, r *http.
}
var req addDirectQueryDataSourceRequest
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/opensearch/handler_data_sources_test.go b/services/opensearch/handler_data_sources_test.go
index 0647e8e372..efa4f7f28b 100644
--- a/services/opensearch/handler_data_sources_test.go
+++ b/services/opensearch/handler_data_sources_test.go
@@ -1,7 +1,8 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -47,7 +49,7 @@ func TestAddDataSource_DataSourceTypeJSON(t *testing.T) {
require.Equal(t, http.StatusOK, getResp.StatusCode)
var ds map[string]any
- require.NoError(t, json.NewDecoder(getResp.Body).Decode(&ds))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getResp.Body), &ds, jsonx.V1()))
// No "DataSource" envelope: fields are top-level.
assert.Equal(t, "my-ds", ds["Name"])
@@ -85,7 +87,7 @@ func TestAddDirectQueryDataSource_DataSourceTypeJSON(t *testing.T) {
require.Equal(t, http.StatusOK, getResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(getResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getResp.Body), &out, jsonx.V1()))
assert.Equal(t, "dq-ds", out["DataSourceName"])
dsType, ok := out["DataSourceType"].(map[string]any)
@@ -97,7 +99,7 @@ func TestAddDataSource_DomainNotFound(t *testing.T) {
t.Parallel()
b := opensearch.NewInMemoryBackend(testAccountID, testRegion)
- _, err := b.AddDataSource("nonexistent", "ds1", "desc", json.RawMessage(`{"S3GlueDataCatalog":{}}`))
+ _, err := b.AddDataSource("nonexistent", "ds1", "desc", jsontext.Value(`{"S3GlueDataCatalog":{}}`))
require.Error(t, err)
assert.ErrorIs(t, err, opensearch.ErrDomainNotFound)
}
@@ -106,10 +108,10 @@ func TestAddDirectQueryDataSource_Duplicate(t *testing.T) {
t.Parallel()
b := opensearch.NewInMemoryBackend(testAccountID, testRegion)
- _, err := b.AddDirectQueryDataSource("ds-1", "desc", json.RawMessage(`{"CloudWatchLog":{}}`), nil)
+ _, err := b.AddDirectQueryDataSource("ds-1", "desc", jsontext.Value(`{"CloudWatchLog":{}}`), nil)
require.NoError(t, err)
- _, err = b.AddDirectQueryDataSource("ds-1", "desc2", json.RawMessage(`{"SecurityLake":{}}`), nil)
+ _, err = b.AddDirectQueryDataSource("ds-1", "desc2", jsontext.Value(`{"SecurityLake":{}}`), nil)
require.Error(t, err)
assert.ErrorIs(t, err, opensearch.ErrDataSourceAlreadyExists)
}
@@ -118,7 +120,7 @@ func TestDirectQueryDataSource_ARN(t *testing.T) {
t.Parallel()
b := opensearch.NewInMemoryBackend(testAccountID, testRegion)
- dsARN, err := b.AddDirectQueryDataSource("my-dq", "desc", json.RawMessage(`{"CloudWatchLog":{}}`), nil)
+ dsARN, err := b.AddDirectQueryDataSource("my-dq", "desc", jsontext.Value(`{"CloudWatchLog":{}}`), nil)
require.NoError(t, err)
assert.Contains(t, dsARN, "directQueryDataSource/my-dq")
assert.Contains(t, dsARN, testAccountID)
@@ -185,7 +187,7 @@ func TestDataSources_Lifecycle(t *testing.T) {
b.AddDomainInternal("ds-domain", "")
for _, name := range tt.addSources {
- _, err := b.AddDataSource("ds-domain", name, "", json.RawMessage(`{"S3GlueDataCatalog":{}}`))
+ _, err := b.AddDataSource("ds-domain", name, "", jsontext.Value(`{"S3GlueDataCatalog":{}}`))
require.NoError(t, err)
}
@@ -234,7 +236,7 @@ func TestDirectQuery_DeleteAndList(t *testing.T) {
require.Equal(t, http.StatusOK, lr.StatusCode)
var lOut map[string]any
- require.NoError(t, json.NewDecoder(lr.Body).Decode(&lOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(lr.Body), &lOut, jsonx.V1()))
sources, ok := lOut["DirectQueryDataSources"].([]any)
require.True(t, ok)
require.Len(t, sources, 1)
@@ -250,7 +252,7 @@ func TestDirectQuery_DeleteAndList(t *testing.T) {
lr2 := doRequest(t, h, http.MethodGet, "/2021-01-01/opensearch/directQueryDataSource", nil)
defer lr2.Body.Close()
var lOut2 map[string]any
- require.NoError(t, json.NewDecoder(lr2.Body).Decode(&lOut2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(lr2.Body), &lOut2, jsonx.V1()))
sources2 := lOut2["DirectQueryDataSources"].([]any)
assert.Empty(t, sources2)
}
@@ -281,7 +283,7 @@ func TestDirectQuery_UpdateReturnsARN(t *testing.T) {
require.Equal(t, http.StatusOK, ur.StatusCode)
var uOut map[string]any
- require.NoError(t, json.NewDecoder(ur.Body).Decode(&uOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(ur.Body), &uOut, jsonx.V1()))
assert.Contains(t, uOut, "DataSourceArn")
}
@@ -310,7 +312,7 @@ func TestDataSources_ListAndGet(t *testing.T) {
require.Equal(t, http.StatusOK, lr.StatusCode)
var lOut map[string]any
- require.NoError(t, json.NewDecoder(lr.Body).Decode(&lOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(lr.Body), &lOut, jsonx.V1()))
sources, ok := lOut["DataSources"].([]any)
require.True(t, ok)
assert.Len(t, sources, 2)
@@ -322,7 +324,7 @@ func TestDataSources_ListAndGet(t *testing.T) {
require.Equal(t, http.StatusOK, gr.StatusCode)
var ds map[string]any
- require.NoError(t, json.NewDecoder(gr.Body).Decode(&ds))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(gr.Body), &ds, jsonx.V1()))
assert.Equal(t, "ds-alpha", ds["Name"])
assert.Equal(t, "ACTIVE", ds["Status"])
}
@@ -355,7 +357,7 @@ func TestDataSources_UpdateAndDelete(t *testing.T) {
require.Equal(t, http.StatusOK, ur.StatusCode)
var uOut map[string]any
- require.NoError(t, json.NewDecoder(ur.Body).Decode(&uOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(ur.Body), &uOut, jsonx.V1()))
assert.Contains(t, uOut["Message"], "updated")
// Get reflects the updated description.
@@ -363,7 +365,7 @@ func TestDataSources_UpdateAndDelete(t *testing.T) {
"/2021-01-01/opensearch/domain/ds-ud-domain/dataSource/ds-target", nil)
defer gr.Body.Close()
var gOut map[string]any
- require.NoError(t, json.NewDecoder(gr.Body).Decode(&gOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(gr.Body), &gOut, jsonx.V1()))
assert.Equal(t, "after", gOut["Description"])
// Delete.
@@ -373,7 +375,7 @@ func TestDataSources_UpdateAndDelete(t *testing.T) {
require.Equal(t, http.StatusOK, del.StatusCode)
var delOut map[string]any
- require.NoError(t, json.NewDecoder(del.Body).Decode(&delOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(del.Body), &delOut, jsonx.V1()))
assert.Equal(t, "DataSource deleted", delOut["Message"])
// List should be empty after delete.
@@ -381,7 +383,7 @@ func TestDataSources_UpdateAndDelete(t *testing.T) {
"/2021-01-01/opensearch/domain/ds-ud-domain/dataSource", nil)
defer lr.Body.Close()
var lOut map[string]any
- require.NoError(t, json.NewDecoder(lr.Body).Decode(&lOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(lr.Body), &lOut, jsonx.V1()))
remaining := lOut["DataSources"].([]any)
assert.Empty(t, remaining)
}
@@ -584,6 +586,6 @@ func TestOpenSearchBackend_AddDataSource_MissingName(t *testing.T) {
_, err := b.CreateDomain(opensearch.CreateDomainInput{Name: "my-domain"})
require.NoError(t, err)
- _, err = b.AddDataSource("my-domain", "", "desc", json.RawMessage(`{"S3GlueDataCatalog":{}}`))
+ _, err = b.AddDataSource("my-domain", "", "desc", jsontext.Value(`{"S3GlueDataCatalog":{}}`))
require.Error(t, err)
}
diff --git a/services/opensearch/handler_domain_config.go b/services/opensearch/handler_domain_config.go
index 8d914a6b3f..91cc9a7530 100644
--- a/services/opensearch/handler_domain_config.go
+++ b/services/opensearch/handler_domain_config.go
@@ -1,11 +1,12 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// dryRunResultsJSON mirrors aws-sdk-go-v2 types.DryRunResults.
@@ -209,7 +210,7 @@ func (h *Handler) handleCancelDomainConfigChange(
var req cancelDomainConfigChangeRequest
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/opensearch/handler_domain_config_test.go b/services/opensearch/handler_domain_config_test.go
index d2961b37ab..a86c778701 100644
--- a/services/opensearch/handler_domain_config_test.go
+++ b/services/opensearch/handler_domain_config_test.go
@@ -1,7 +1,8 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -31,7 +33,7 @@ func TestOpenSearchHandler_UpdateDomainConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, upResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(upResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(upResp.Body), &out, jsonx.V1()))
domainConfig, ok := out["DomainConfig"].(map[string]any)
require.True(t, ok)
@@ -58,7 +60,7 @@ func Test_UpdateDomainConfig_DryRun(t *testing.T) {
assert.Equal(t, http.StatusOK, dryResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(dryResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(dryResp.Body), &out, jsonx.V1()))
domainConfig, ok := out["DomainConfig"].(map[string]any)
require.True(t, ok)
@@ -75,7 +77,7 @@ func Test_UpdateDomainConfig_DryRun(t *testing.T) {
defer descResp.Body.Close()
var descOut map[string]any
- require.NoError(t, json.NewDecoder(descResp.Body).Decode(&descOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(descResp.Body), &descOut, jsonx.V1()))
status, ok := descOut["DomainStatus"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "OpenSearch_2.7", status["EngineVersion"], "dry run must not persist the change")
@@ -101,7 +103,7 @@ func TestOpenSearchHandler_DefaultApplicationSetting(t *testing.T) {
// GET before any setting → empty ARN.
resp := doRequest(t, h, http.MethodGet, "/2021-01-01/opensearch/defaultApplicationSetting", nil)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Empty(t, out["applicationArn"])
@@ -115,7 +117,7 @@ func TestOpenSearchHandler_DefaultApplicationSetting(t *testing.T) {
assert.Equal(t, http.StatusOK, putResp.StatusCode)
var putOut map[string]any
- require.NoError(t, json.NewDecoder(putResp.Body).Decode(&putOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(putResp.Body), &putOut, jsonx.V1()))
assert.Equal(t, appArn, putOut["applicationArn"])
// GET after PUT → should return the stored ARN.
@@ -125,7 +127,7 @@ func TestOpenSearchHandler_DefaultApplicationSetting(t *testing.T) {
assert.Equal(t, http.StatusOK, getResp.StatusCode)
var out2 map[string]any
- require.NoError(t, json.NewDecoder(getResp.Body).Decode(&out2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getResp.Body), &out2, jsonx.V1()))
assert.Equal(t, appArn, out2["applicationArn"])
// PUT with SetAsDefault=false clears the default.
@@ -135,7 +137,7 @@ func TestOpenSearchHandler_DefaultApplicationSetting(t *testing.T) {
assert.Equal(t, http.StatusOK, clearResp.StatusCode)
var clearOut map[string]any
- require.NoError(t, json.NewDecoder(clearResp.Body).Decode(&clearOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(clearResp.Body), &clearOut, jsonx.V1()))
assert.Empty(t, clearOut["applicationArn"])
}
@@ -167,7 +169,7 @@ func TestDescribeDomainConfig_RealData(t *testing.T) {
require.Equal(t, http.StatusOK, cfgResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(cfgResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(cfgResp.Body), &out, jsonx.V1()))
domainConfig, ok := out["DomainConfig"].(map[string]any)
require.True(t, ok)
@@ -447,7 +449,7 @@ func TestUpdateDomainConfig_AllOptions(t *testing.T) {
require.Equal(t, http.StatusOK, upResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(upResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(upResp.Body), &out, jsonx.V1()))
tt.verify(t, out)
})
}
@@ -515,7 +517,7 @@ func TestDescribeDomainConfig_FullConfig(t *testing.T) {
require.Equal(t, http.StatusOK, cfgResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(cfgResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(cfgResp.Body), &out, jsonx.V1()))
dc, ok := out["DomainConfig"].(map[string]any)
require.True(t, ok)
@@ -602,7 +604,7 @@ func TestUpdateDomainConfig_MutatesState(t *testing.T) {
require.Equal(t, http.StatusOK, descResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(descResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(descResp.Body), &out, jsonx.V1()))
tt.verifyFn(t, out)
})
}
@@ -670,7 +672,7 @@ func TestCancelDomainConfigChange_ReturnsLastChangeID(t *testing.T) {
var got struct {
CancelledChangeIDs []string `json:"CancelledChangeIds"`
}
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&got))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &got, jsonx.V1()))
if tt.wantEmptyIDs {
assert.Empty(t, got.CancelledChangeIDs)
diff --git a/services/opensearch/handler_domain_maintenance_test.go b/services/opensearch/handler_domain_maintenance_test.go
index 610ce9c36d..e24576ca11 100644
--- a/services/opensearch/handler_domain_maintenance_test.go
+++ b/services/opensearch/handler_domain_maintenance_test.go
@@ -1,12 +1,15 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDomainMaintenance_StartListGet(t *testing.T) {
@@ -23,7 +26,7 @@ func TestDomainMaintenance_StartListGet(t *testing.T) {
require.Equal(t, http.StatusOK, sr.StatusCode)
var sOut map[string]any
- require.NoError(t, json.NewDecoder(sr.Body).Decode(&sOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(sr.Body), &sOut, jsonx.V1()))
maintID := sOut["MaintenanceId"].(string)
assert.NotEmpty(t, maintID)
@@ -34,7 +37,7 @@ func TestDomainMaintenance_StartListGet(t *testing.T) {
require.Equal(t, http.StatusOK, lr.StatusCode)
var lOut map[string]any
- require.NoError(t, json.NewDecoder(lr.Body).Decode(&lOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(lr.Body), &lOut, jsonx.V1()))
maintenances, ok := lOut["DomainMaintenances"].([]any)
require.True(t, ok)
assert.Len(t, maintenances, 1)
@@ -46,7 +49,7 @@ func TestDomainMaintenance_StartListGet(t *testing.T) {
require.Equal(t, http.StatusOK, gr.StatusCode)
var gOut map[string]any
- require.NoError(t, json.NewDecoder(gr.Body).Decode(&gOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(gr.Body), &gOut, jsonx.V1()))
assert.Equal(t, maintID, gOut["MaintenanceId"])
}
@@ -111,7 +114,7 @@ func TestDeleteDomain_CleansUpMaintenances(t *testing.T) {
var got struct {
DomainMaintenances []any `json:"DomainMaintenances"`
}
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&got))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &got, jsonx.V1()))
assert.Empty(t, got.DomainMaintenances, "expected no orphaned maintenances after delete+recreate")
})
}
diff --git a/services/opensearch/handler_domain_options.go b/services/opensearch/handler_domain_options.go
index e51573f026..ea0db43575 100644
--- a/services/opensearch/handler_domain_options.go
+++ b/services/opensearch/handler_domain_options.go
@@ -1,11 +1,13 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"regexp"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// domainClusterConfig holds the cluster configuration request parameters for a domain.
@@ -31,12 +33,12 @@ type domainClusterConfigAlias domainClusterConfig
// UnmarshalJSON records which members the request set, so UpdateDomainConfig can
// leave omitted ones unchanged.
func (cc *domainClusterConfig) UnmarshalJSON(data []byte) error {
- if err := json.Unmarshal(data, (*domainClusterConfigAlias)(cc)); err != nil {
+ if err := json.Unmarshal(data, (*domainClusterConfigAlias)(cc), jsonx.V1()); err != nil {
return err
}
- var raw map[string]json.RawMessage
- if err := json.Unmarshal(data, &raw); err != nil {
+ var raw map[string]jsontext.Value
+ if err := json.Unmarshal(data, &raw, jsonx.V1()); err != nil {
return err
}
diff --git a/services/opensearch/handler_domain_options_test.go b/services/opensearch/handler_domain_options_test.go
index 6b0a397227..642f70b1af 100644
--- a/services/opensearch/handler_domain_options_test.go
+++ b/services/opensearch/handler_domain_options_test.go
@@ -1,13 +1,16 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestAudit1_CreateDomain_FullClusterConfig verifies all ClusterConfig fields round-trip through
@@ -101,7 +104,7 @@ func TestCreateDomain_FullClusterConfig(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status, ok := out["DomainStatus"].(map[string]any)
require.True(t, ok)
cc, ok := status["ClusterConfig"].(map[string]any)
@@ -130,7 +133,7 @@ func TestCreateDomain_SoftwareUpdateOptions(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status, ok := out["DomainStatus"].(map[string]any)
require.True(t, ok)
swu, ok := status["SoftwareUpdateOptions"].(map[string]any)
@@ -203,7 +206,7 @@ func TestCreateDomain_EBSOptions(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status, ok := out["DomainStatus"].(map[string]any)
require.True(t, ok)
ebs, ok := status["EBSOptions"].(map[string]any)
@@ -274,7 +277,7 @@ func TestCreateDomain_EncryptionOptions(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status, ok := out["DomainStatus"].(map[string]any)
require.True(t, ok)
@@ -344,7 +347,7 @@ func TestCreateDomain_DomainEndpointOptions(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status, ok := out["DomainStatus"].(map[string]any)
require.True(t, ok)
deo, ok := status["DomainEndpointOptions"].(map[string]any)
@@ -426,7 +429,7 @@ func TestCreateDomain_AdvancedSecurityOptions(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status, ok := out["DomainStatus"].(map[string]any)
require.True(t, ok)
aso, ok := status["AdvancedSecurityOptions"].(map[string]any)
@@ -485,7 +488,7 @@ func TestCreateDomain_VPCOptions(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status, ok := out["DomainStatus"].(map[string]any)
require.True(t, ok)
vpc, ok := status["VPCOptions"].(map[string]any)
@@ -546,7 +549,7 @@ func TestCreateDomain_CognitoOptions(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status, ok := out["DomainStatus"].(map[string]any)
require.True(t, ok)
cognito, ok := status["CognitoOptions"].(map[string]any)
@@ -624,7 +627,7 @@ func TestCreateDomain_LogPublishingOptions(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status, ok := out["DomainStatus"].(map[string]any)
require.True(t, ok)
logs, ok := status["LogPublishingOptions"].(map[string]any)
@@ -665,7 +668,7 @@ func TestCreateDomain_SnapshotOptions(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status, ok := out["DomainStatus"].(map[string]any)
require.True(t, ok)
snapOpts, ok := status["SnapshotOptions"].(map[string]any)
@@ -722,7 +725,7 @@ func TestCreateDomain_AccessPolicies(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status, ok := out["DomainStatus"].(map[string]any)
require.True(t, ok)
gotPolicies, _ := status["AccessPolicies"].(string)
diff --git a/services/opensearch/handler_domain_status_test.go b/services/opensearch/handler_domain_status_test.go
index e6c75d119e..da99345c86 100644
--- a/services/opensearch/handler_domain_status_test.go
+++ b/services/opensearch/handler_domain_status_test.go
@@ -1,7 +1,8 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"strconv"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -33,7 +35,7 @@ func TestOpenSearchHandler_DescribeDomainHealth(t *testing.T) {
assert.Equal(t, http.StatusOK, healthResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(healthResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(healthResp.Body), &out, jsonx.V1()))
assert.Equal(t, "Active", out["DomainState"])
// 3 instances * 5 shards = 15. TotalShards is NumberOfShards, a string
@@ -61,7 +63,7 @@ func TestOpenSearchHandler_DescribeDomainNodes(t *testing.T) {
assert.Equal(t, http.StatusOK, nodesResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(nodesResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(nodesResp.Body), &out, jsonx.V1()))
nodes, ok := out["DomainNodesStatusList"].([]any)
require.True(t, ok)
@@ -125,7 +127,7 @@ func TestOpenSearchHandler_DescribeDomainChangeProgress(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status, ok := out["ChangeProgressStatus"].(map[string]any)
require.True(t, ok)
@@ -151,7 +153,7 @@ func TestOpenSearchHandler_DescribeDryRunProgress(t *testing.T) {
require.NoError(t, err)
var out map[string]any
- require.NoError(t, json.Unmarshal(bodyBytes, &out))
+ require.NoError(t, json.Unmarshal(bodyBytes, &out, jsonx.V1()))
status, ok := out["DryRunProgressStatus"].(map[string]any)
require.True(t, ok)
@@ -204,7 +206,7 @@ func TestDescribeDomainHealth_AdditionalFields(t *testing.T) {
require.Equal(t, http.StatusOK, hResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(hResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(hResp.Body), &out, jsonx.V1()))
// Existing fields.
assert.Equal(t, "Active", out["DomainState"])
@@ -247,7 +249,7 @@ func TestDescribeDomainNodes_StorageAndAZ(t *testing.T) {
require.Equal(t, http.StatusOK, nResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(nResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(nResp.Body), &out, jsonx.V1()))
nodes, ok := out["DomainNodesStatusList"].([]any)
require.True(t, ok)
@@ -279,7 +281,7 @@ func TestDescribeDomainChangeProgress_Timestamps(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status, ok := out["ChangeProgressStatus"].(map[string]any)
require.True(t, ok)
@@ -301,7 +303,7 @@ func TestDescribeDryRunProgress_ValidationFailures(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
status, ok := out["DryRunProgressStatus"].(map[string]any)
require.True(t, ok)
@@ -339,7 +341,7 @@ func TestGetDomainHealth_WarmNodeCount(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
warmCount, ok := out["WarmNodeCount"].(string)
require.True(t, ok, "WarmNodeCount must be present")
diff --git a/services/opensearch/handler_domains.go b/services/opensearch/handler_domains.go
index 6fbb7a7278..7469368f48 100644
--- a/services/opensearch/handler_domains.go
+++ b/services/opensearch/handler_domains.go
@@ -1,7 +1,7 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -10,6 +10,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -124,7 +125,7 @@ func (h *Handler) handleCreateDomain(w http.ResponseWriter, r *http.Request) {
}
var req domainJSON
- if err = json.Unmarshal(body, &req); err != nil {
+ if err = json.Unmarshal(body, &req, jsonx.V1()); err != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
@@ -249,7 +250,7 @@ func (h *Handler) handleDescribeDomains(w http.ResponseWriter, r *http.Request)
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
domains, err := h.Backend.DescribeDomains(req.DomainNames)
@@ -430,7 +431,7 @@ func (h *Handler) handleStartServiceSoftwareUpdate(w http.ResponseWriter, r *htt
}
var req startServiceSoftwareUpdateRequest
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
@@ -482,7 +483,7 @@ func (h *Handler) handleRollbackServiceSoftwareUpdate(w http.ResponseWriter, r *
}
var req rollbackServiceSoftwareUpdateRequest
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
@@ -552,7 +553,7 @@ func (h *Handler) handleCancelServiceSoftwareUpdate(w http.ResponseWriter, r *ht
}
var req cancelServiceSoftwareUpdateRequest
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/opensearch/handler_domains_test.go b/services/opensearch/handler_domains_test.go
index 81974116e0..98dc682469 100644
--- a/services/opensearch/handler_domains_test.go
+++ b/services/opensearch/handler_domains_test.go
@@ -1,7 +1,8 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -62,7 +64,7 @@ func TestStartServiceSoftwareUpdate_ScheduleAt(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
swo, ok := out["ServiceSoftwareOptions"].(map[string]any)
require.True(t, ok, "expected ServiceSoftwareOptions in response")
@@ -109,7 +111,7 @@ func TestOpenSearchHandler_DescribeDomains(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
list, ok := out["DomainStatusList"].([]any)
require.True(t, ok)
@@ -141,7 +143,7 @@ func TestOpenSearchHandler_DescribeDomains_All(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
list, ok := out["DomainStatusList"].([]any)
require.True(t, ok)
@@ -184,7 +186,7 @@ func TestDescribeDomains_FullStatusShape(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
list, ok := out["DomainStatusList"].([]any)
require.True(t, ok)
@@ -424,7 +426,7 @@ func TestListDomainNames_EngineTypeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
domainNames, ok := out["DomainNames"].([]any)
require.True(t, ok)
@@ -516,7 +518,7 @@ func TestDescribeDomains_FullDomainStatus(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
list, ok := out["DomainStatusList"].([]any)
require.True(t, ok)
@@ -582,7 +584,7 @@ func TestListDomainNames_ReturnsBothDomains(t *testing.T) {
EngineType string `json:"EngineType"`
} `json:"DomainNames"`
}
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&got))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &got, jsonx.V1()))
assert.Len(t, got.DomainNames, tt.wantCount)
returnedNames := make(map[string]bool, len(got.DomainNames))
@@ -682,7 +684,7 @@ func Test_DomainStatus_DomainID(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.StatusCode)
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createResp.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createResp.Body), &createOut, jsonx.V1()))
createStatus, ok := createOut["DomainStatus"].(map[string]any)
require.True(t, ok, "DomainStatus missing")
assert.Equal(t, "123456789012/domainid-test", createStatus["DomainId"])
@@ -693,7 +695,7 @@ func Test_DomainStatus_DomainID(t *testing.T) {
require.Equal(t, http.StatusOK, describeResp.StatusCode)
var describeOut map[string]any
- require.NoError(t, json.NewDecoder(describeResp.Body).Decode(&describeOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(describeResp.Body), &describeOut, jsonx.V1()))
describeStatus, ok := describeOut["DomainStatus"].(map[string]any)
require.True(t, ok, "DomainStatus missing")
assert.Equal(t, "123456789012/domainid-test", describeStatus["DomainId"])
@@ -771,7 +773,7 @@ func TestOpenSearchHandler_ListDomainNames(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
names, ok := out["DomainNames"].([]any)
require.True(t, ok)
@@ -1122,7 +1124,7 @@ func TestInsights_HTTPHandler(t *testing.T) {
if tt.wantCode == http.StatusOK && tt.path == "/2021-01-01/opensearch/insights" {
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
insights, ok := out["Insights"].([]any)
require.True(t, ok)
assert.Empty(t, insights)
diff --git a/services/opensearch/handler_inbound_connections.go b/services/opensearch/handler_inbound_connections.go
index 9907b5a2fa..1e76155517 100644
--- a/services/opensearch/handler_inbound_connections.go
+++ b/services/opensearch/handler_inbound_connections.go
@@ -1,12 +1,13 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// describeConnectionsRequest is the shared request body shape for
@@ -154,7 +155,7 @@ func (h *Handler) readDescribeConnectionsRequest(
var req describeConnectionsRequest
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "failed to parse body")
return describeConnectionsRequest{}, false
diff --git a/services/opensearch/handler_inbound_connections_test.go b/services/opensearch/handler_inbound_connections_test.go
index 53e5708eb5..687732156d 100644
--- a/services/opensearch/handler_inbound_connections_test.go
+++ b/services/opensearch/handler_inbound_connections_test.go
@@ -1,7 +1,8 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -41,7 +43,7 @@ func TestInboundConnections_DescribeRejectDelete(t *testing.T) {
require.Equal(t, http.StatusOK, dr.StatusCode)
var dOut map[string]any
- require.NoError(t, json.NewDecoder(dr.Body).Decode(&dOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(dr.Body), &dOut, jsonx.V1()))
conns, ok := dOut["Connections"].([]any)
require.True(t, ok)
require.NotEmpty(t, conns)
@@ -57,7 +59,7 @@ func TestInboundConnections_DescribeRejectDelete(t *testing.T) {
require.Equal(t, http.StatusOK, rr.StatusCode)
var rOut map[string]any
- require.NoError(t, json.NewDecoder(rr.Body).Decode(&rOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rr.Body), &rOut, jsonx.V1()))
rConn := rOut["Connection"].(map[string]any)
assert.Equal(t, "REJECTED", rConn["ConnectionStatus"].(map[string]any)["StatusCode"])
@@ -68,7 +70,7 @@ func TestInboundConnections_DescribeRejectDelete(t *testing.T) {
require.Equal(t, http.StatusOK, del.StatusCode)
var delOut map[string]any
- require.NoError(t, json.NewDecoder(del.Body).Decode(&delOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(del.Body), &delOut, jsonx.V1()))
delConn := delOut["Connection"].(map[string]any)
assert.Equal(t, "conn-inb-1", delConn["ConnectionId"])
}
@@ -164,7 +166,7 @@ func TestOutboundConnection_CreateThenAcceptMirrorsBothSides(t *testing.T) {
require.Equal(t, http.StatusOK, cr.StatusCode)
var cOut map[string]any
- require.NoError(t, json.NewDecoder(cr.Body).Decode(&cOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(cr.Body), &cOut, jsonx.V1()))
connID := cOut["ConnectionId"].(string)
// Accepting the mirrored inbound connection also activates the outbound side.
@@ -174,7 +176,7 @@ func TestOutboundConnection_CreateThenAcceptMirrorsBothSides(t *testing.T) {
require.Equal(t, http.StatusOK, ar.StatusCode)
var aOut map[string]any
- require.NoError(t, json.NewDecoder(ar.Body).Decode(&aOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(ar.Body), &aOut, jsonx.V1()))
aConn := aOut["Connection"].(map[string]any)
assert.Equal(t, "ACTIVE", aConn["ConnectionStatus"].(map[string]any)["StatusCode"])
@@ -182,7 +184,7 @@ func TestOutboundConnection_CreateThenAcceptMirrorsBothSides(t *testing.T) {
defer dr.Body.Close()
var dOut map[string]any
- require.NoError(t, json.NewDecoder(dr.Body).Decode(&dOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(dr.Body), &dOut, jsonx.V1()))
conns := dOut["Connections"].([]any)
require.Len(t, conns, 1)
outConn := conns[0].(map[string]any)
diff --git a/services/opensearch/handler_indices.go b/services/opensearch/handler_indices.go
index 01e5a43931..9d3488486b 100644
--- a/services/opensearch/handler_indices.go
+++ b/services/opensearch/handler_indices.go
@@ -1,12 +1,13 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// createIndexRealRequest and updateIndexRealRequest mirror
@@ -46,7 +47,7 @@ func (h *Handler) handleCreateIndexRealRoute(w http.ResponseWriter, r *http.Requ
var req createIndexRealRequest
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
if req.IndexName == "" {
@@ -79,7 +80,7 @@ func (h *Handler) handleUpdateIndexRoute(w http.ResponseWriter, r *http.Request,
body, _ := httputils.ReadBody(r)
var req updateIndexRealRequest
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
_, err := h.Backend.UpdateIndex(parts[0], parts[1], nil, nil, req.IndexSchema)
@@ -301,7 +302,7 @@ func (h *Handler) handleCreateIndex(w http.ResponseWriter, r *http.Request, sp i
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
if _, err := h.Backend.CreateIndex(sp.domain, sp.index, req.Mappings, req.Settings, req.Aliases, nil); err != nil {
@@ -322,7 +323,7 @@ func (h *Handler) handleIndexDocument(w http.ResponseWriter, r *http.Request, sp
body, _ := httputils.ReadBody(r)
var doc map[string]any
if len(body) > 0 {
- if err := json.Unmarshal(body, &doc); err != nil {
+ if err := json.Unmarshal(body, &doc, jsonx.V1()); err != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid document body")
return
@@ -360,7 +361,7 @@ func (h *Handler) handleSearchIndex(w http.ResponseWriter, r *http.Request, sp i
Size *int `json:"size"`
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid search body")
return
diff --git a/services/opensearch/handler_insights.go b/services/opensearch/handler_insights.go
index 6a81df76c5..2b76de02de 100644
--- a/services/opensearch/handler_insights.go
+++ b/services/opensearch/handler_insights.go
@@ -1,11 +1,12 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// insightEntityJSON matches types.InsightEntity (used by ListInsights and
@@ -52,7 +53,7 @@ func (h *Handler) handleListInsights(w http.ResponseWriter, r *http.Request) {
var req listInsightsRequest
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
@@ -81,7 +82,7 @@ func (h *Handler) handleDescribeInsightDetails(w http.ResponseWriter, r *http.Re
var req describeInsightDetailsRequest
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
@@ -111,7 +112,7 @@ func (h *Handler) handleInsightFeedback(w http.ResponseWriter, r *http.Request)
var req insightFeedbackRequest
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/opensearch/handler_migrations.go b/services/opensearch/handler_migrations.go
index e19243b5f4..b77be92966 100644
--- a/services/opensearch/handler_migrations.go
+++ b/services/opensearch/handler_migrations.go
@@ -1,13 +1,14 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strconv"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// migrationSourceJSON matches types.MigrationSource.
@@ -108,7 +109,7 @@ func (h *Handler) handleStartMigration(w http.ResponseWriter, r *http.Request) {
var req startMigrationRequest
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/opensearch/handler_outbound_connections.go b/services/opensearch/handler_outbound_connections.go
index 2f1e13f5e4..0452b9f938 100644
--- a/services/opensearch/handler_outbound_connections.go
+++ b/services/opensearch/handler_outbound_connections.go
@@ -1,11 +1,12 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleCCOutboundRoutes handles outbound cross-cluster connection sub-routes.
@@ -129,7 +130,7 @@ func (h *Handler) handleCreateOutboundConnection(w http.ResponseWriter, r *http.
var req createOutboundConnectionRequest
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "failed to parse body")
return
diff --git a/services/opensearch/handler_outbound_connections_test.go b/services/opensearch/handler_outbound_connections_test.go
index 297c7cf987..cdd352887e 100644
--- a/services/opensearch/handler_outbound_connections_test.go
+++ b/services/opensearch/handler_outbound_connections_test.go
@@ -1,12 +1,15 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestOutboundConnections_CreateDescribeDelete(t *testing.T) {
@@ -29,7 +32,7 @@ func TestOutboundConnections_CreateDescribeDelete(t *testing.T) {
require.Equal(t, http.StatusOK, cr.StatusCode)
var cOut map[string]any
- require.NoError(t, json.NewDecoder(cr.Body).Decode(&cOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(cr.Body), &cOut, jsonx.V1()))
connID := cOut["ConnectionId"].(string)
assert.NotEmpty(t, connID)
assert.Equal(t, "my-alias", cOut["ConnectionAlias"])
@@ -44,7 +47,7 @@ func TestOutboundConnections_CreateDescribeDelete(t *testing.T) {
require.Equal(t, http.StatusOK, dr.StatusCode)
var dOut map[string]any
- require.NoError(t, json.NewDecoder(dr.Body).Decode(&dOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(dr.Body), &dOut, jsonx.V1()))
conns, ok := dOut["Connections"].([]any)
require.True(t, ok)
assert.Len(t, conns, 1)
@@ -55,7 +58,7 @@ func TestOutboundConnections_CreateDescribeDelete(t *testing.T) {
require.Equal(t, http.StatusOK, ir.StatusCode)
var iOut map[string]any
- require.NoError(t, json.NewDecoder(ir.Body).Decode(&iOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(ir.Body), &iOut, jsonx.V1()))
inConns, ok := iOut["Connections"].([]any)
require.True(t, ok)
require.Len(t, inConns, 1)
@@ -71,7 +74,7 @@ func TestOutboundConnections_CreateDescribeDelete(t *testing.T) {
require.Equal(t, http.StatusOK, del.StatusCode)
var delOut map[string]any
- require.NoError(t, json.NewDecoder(del.Body).Decode(&delOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(del.Body), &delOut, jsonx.V1()))
conn := delOut["Connection"].(map[string]any)
assert.Equal(t, connID, conn["ConnectionId"])
}
diff --git a/services/opensearch/handler_packages.go b/services/opensearch/handler_packages.go
index 79d0e1a3ef..29bf1e6d99 100644
--- a/services/opensearch/handler_packages.go
+++ b/services/opensearch/handler_packages.go
@@ -1,12 +1,13 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDissociatePackage(
@@ -42,7 +43,7 @@ func (h *Handler) handleDissociatePackages(w http.ResponseWriter, r *http.Reques
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
packageIDs := make([]string, 0, len(req.PackageList))
@@ -143,7 +144,7 @@ func (h *Handler) handleDescribePackages(w http.ResponseWriter, r *http.Request)
MaxResults int32 `json:"MaxResults"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
filters := make(map[string][]string, len(req.Filters))
@@ -185,7 +186,7 @@ func (h *Handler) handleUpdatePackageRoute(w http.ResponseWriter, r *http.Reques
PackageDescription string `json:"PackageDescription"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
pkg, updateErr := h.Backend.UpdatePackage(req.PackageID, req.PackageDescription)
@@ -212,7 +213,7 @@ func (h *Handler) handleUpdatePackageScopeRoute(w http.ResponseWriter, r *http.R
PackageUserList []string `json:"PackageUserList"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
pkg, err := h.Backend.UpdatePackageScope(req.PackageID, req.Operation, req.PackageUserList)
@@ -363,7 +364,7 @@ func (h *Handler) handlePackageRootRoutes(w http.ResponseWriter, r *http.Request
PackageDescription string `json:"PackageDescription"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
var pkgSource *PackageSource
if req.PackageSource != nil {
@@ -501,7 +502,7 @@ func (h *Handler) handleAssociatePackages(w http.ResponseWriter, r *http.Request
}
var req associatePackagesRequest
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/opensearch/handler_packages_test.go b/services/opensearch/handler_packages_test.go
index fe64f5fea3..80d437409d 100644
--- a/services/opensearch/handler_packages_test.go
+++ b/services/opensearch/handler_packages_test.go
@@ -1,7 +1,8 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -15,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -98,7 +100,7 @@ func TestOpenSearchHandler_DeletePackage_RejectedWhileAssociated(t *testing.T) {
map[string]any{"PackageName": "delpkg-pkg", "PackageType": "TXT-DICTIONARY"})
var pkgOut map[string]any
- require.NoError(t, json.NewDecoder(pkgResp.Body).Decode(&pkgOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(pkgResp.Body), &pkgOut, jsonx.V1()))
pkgResp.Body.Close()
pkgDetails, ok := pkgOut["PackageDetails"].(map[string]any)
@@ -138,7 +140,7 @@ func TestOpenSearchHandler_DissociatePackage(t *testing.T) {
map[string]any{"PackageName": "my-pkg", "PackageType": "TXT-DICTIONARY"})
var pkgOut map[string]any
- require.NoError(t, json.NewDecoder(pkgResp.Body).Decode(&pkgOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(pkgResp.Body), &pkgOut, jsonx.V1()))
pkgResp.Body.Close()
pkgDetails, ok := pkgOut["PackageDetails"].(map[string]any)
@@ -160,7 +162,7 @@ func TestOpenSearchHandler_DissociatePackage(t *testing.T) {
assert.Equal(t, http.StatusOK, dissocResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(dissocResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(dissocResp.Body), &out, jsonx.V1()))
details2, ok := out["DomainPackageDetails"].(map[string]any)
require.True(t, ok)
@@ -184,7 +186,7 @@ func TestOpenSearchHandler_DissociatePackages(t *testing.T) {
pkgResp := doRequest(t, h, http.MethodPost, "/2021-01-01/packages",
map[string]any{"PackageName": name, "PackageType": "TXT-DICTIONARY"})
var pkgOut map[string]any
- require.NoError(t, json.NewDecoder(pkgResp.Body).Decode(&pkgOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(pkgResp.Body), &pkgOut, jsonx.V1()))
pkgResp.Body.Close()
id := pkgOut["PackageDetails"].(map[string]any)["PackageID"].(string)
@@ -211,7 +213,7 @@ func TestOpenSearchHandler_DissociatePackages(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
list, ok := out["DomainPackageDetailsList"].([]any)
require.True(t, ok)
@@ -233,7 +235,7 @@ func TestListPackagesForDomain_ReturnsDomainPackageDetailsShape(t *testing.T) {
pkgResp := doRequest(t, h, http.MethodPost, "/2021-01-01/packages",
map[string]any{"PackageName": "list-pkg", "PackageType": "TXT-DICTIONARY"})
var pkgOut map[string]any
- require.NoError(t, json.NewDecoder(pkgResp.Body).Decode(&pkgOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(pkgResp.Body), &pkgOut, jsonx.V1()))
pkgResp.Body.Close()
pkgID := pkgOut["PackageDetails"].(map[string]any)["PackageID"].(string)
@@ -250,7 +252,7 @@ func TestListPackagesForDomain_ReturnsDomainPackageDetailsShape(t *testing.T) {
require.Equal(t, http.StatusOK, lr.StatusCode)
var lOut map[string]any
- require.NoError(t, json.NewDecoder(lr.Body).Decode(&lOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(lr.Body), &lOut, jsonx.V1()))
list, ok := lOut["DomainPackageDetailsList"].([]any)
require.True(t, ok)
require.Len(t, list, 1)
@@ -272,7 +274,7 @@ func TestListDomainsForPackage_ReturnsDomainPackageDetailsShape(t *testing.T) {
pkgResp := doRequest(t, h, http.MethodPost, "/2021-01-01/packages",
map[string]any{"PackageName": "shared-pkg", "PackageType": "TXT-DICTIONARY"})
var pkgOut map[string]any
- require.NoError(t, json.NewDecoder(pkgResp.Body).Decode(&pkgOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(pkgResp.Body), &pkgOut, jsonx.V1()))
pkgResp.Body.Close()
pkgID := pkgOut["PackageDetails"].(map[string]any)["PackageID"].(string)
@@ -287,7 +289,7 @@ func TestListDomainsForPackage_ReturnsDomainPackageDetailsShape(t *testing.T) {
require.Equal(t, http.StatusOK, lr.StatusCode)
var lOut map[string]any
- require.NoError(t, json.NewDecoder(lr.Body).Decode(&lOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(lr.Body), &lOut, jsonx.V1()))
list, ok := lOut["DomainPackageDetailsList"].([]any)
require.True(t, ok)
require.Len(t, list, 1)
diff --git a/services/opensearch/handler_reserved_instances.go b/services/opensearch/handler_reserved_instances.go
index 29c5550712..45276a1a41 100644
--- a/services/opensearch/handler_reserved_instances.go
+++ b/services/opensearch/handler_reserved_instances.go
@@ -1,11 +1,12 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleReservedInstancesRoutes handles GET /reservedInstances → DescribeReservedInstances.
@@ -68,7 +69,7 @@ func (h *Handler) handlePurchaseReservedInstanceOffering(w http.ResponseWriter,
InstanceCount int `json:"InstanceCount"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
if req.InstanceCount == 0 {
diff --git a/services/opensearch/handler_reserved_instances_test.go b/services/opensearch/handler_reserved_instances_test.go
index 7d1b67ab08..2aed7d8114 100644
--- a/services/opensearch/handler_reserved_instances_test.go
+++ b/services/opensearch/handler_reserved_instances_test.go
@@ -1,12 +1,15 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestReservedInstances_ListOfferingsAndPurchase(t *testing.T) {
@@ -21,7 +24,7 @@ func TestReservedInstances_ListOfferingsAndPurchase(t *testing.T) {
require.Equal(t, http.StatusOK, lor.StatusCode)
var loOut map[string]any
- require.NoError(t, json.NewDecoder(lor.Body).Decode(&loOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(lor.Body), &loOut, jsonx.V1()))
offerings, ok := loOut["ReservedInstanceOfferings"].([]any)
require.True(t, ok)
require.NotEmpty(t, offerings, "must have at least one offering")
@@ -40,7 +43,7 @@ func TestReservedInstances_ListOfferingsAndPurchase(t *testing.T) {
require.Equal(t, http.StatusOK, pr.StatusCode)
var pOut map[string]any
- require.NoError(t, json.NewDecoder(pr.Body).Decode(&pOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(pr.Body), &pOut, jsonx.V1()))
assert.NotEmpty(t, pOut["ReservedInstanceId"])
assert.Equal(t, "my-reservation", pOut["ReservationName"])
@@ -51,7 +54,7 @@ func TestReservedInstances_ListOfferingsAndPurchase(t *testing.T) {
require.Equal(t, http.StatusOK, dr.StatusCode)
var dOut map[string]any
- require.NoError(t, json.NewDecoder(dr.Body).Decode(&dOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(dr.Body), &dOut, jsonx.V1()))
instances, ok := dOut["ReservedInstances"].([]any)
require.True(t, ok)
require.Len(t, instances, 1)
@@ -67,7 +70,7 @@ func TestReservedInstances_ListOfferingsAndPurchase(t *testing.T) {
require.Equal(t, http.StatusOK, fr.StatusCode)
var fOut map[string]any
- require.NoError(t, json.NewDecoder(fr.Body).Decode(&fOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(fr.Body), &fOut, jsonx.V1()))
filtered := fOut["ReservedInstances"].([]any)
require.Len(t, filtered, 1)
assert.Equal(t, reservationID, filtered[0].(map[string]any)["ReservedInstanceId"])
@@ -79,7 +82,7 @@ func TestReservedInstances_ListOfferingsAndPurchase(t *testing.T) {
require.Equal(t, http.StatusOK, ofr.StatusCode)
var ofOut map[string]any
- require.NoError(t, json.NewDecoder(ofr.Body).Decode(&ofOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(ofr.Body), &ofOut, jsonx.V1()))
filteredOfferings := ofOut["ReservedInstanceOfferings"].([]any)
require.Len(t, filteredOfferings, 1)
assert.Equal(t, offeringID, filteredOfferings[0].(map[string]any)["ReservedInstanceOfferingId"])
diff --git a/services/opensearch/handler_scheduled_actions.go b/services/opensearch/handler_scheduled_actions.go
index a8aad1267c..39c36a60bc 100644
--- a/services/opensearch/handler_scheduled_actions.go
+++ b/services/opensearch/handler_scheduled_actions.go
@@ -1,11 +1,12 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// updateScheduledActionRequest is the JSON request body for
@@ -30,7 +31,7 @@ func (h *Handler) handleUpdateScheduledAction(w http.ResponseWriter, r *http.Req
var req updateScheduledActionRequest
if len(body) > 0 {
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/opensearch/handler_scheduled_actions_test.go b/services/opensearch/handler_scheduled_actions_test.go
index b72706b05b..e01eceb547 100644
--- a/services/opensearch/handler_scheduled_actions_test.go
+++ b/services/opensearch/handler_scheduled_actions_test.go
@@ -1,13 +1,15 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -32,7 +34,7 @@ func TestScheduledActions_ListForDomain(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
actions, ok := out["ScheduledActions"].([]any)
require.True(t, ok)
require.Len(t, actions, 1)
@@ -45,7 +47,7 @@ func TestScheduledActions_ListForDomain(t *testing.T) {
require.Equal(t, http.StatusOK, otherResp.StatusCode)
var otherOut map[string]any
- require.NoError(t, json.NewDecoder(otherResp.Body).Decode(&otherOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(otherResp.Body), &otherOut, jsonx.V1()))
assert.Empty(t, otherOut["ScheduledActions"])
}
@@ -75,7 +77,7 @@ func TestScheduledActions_UpdateReschedulesExistingAction(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
action, ok := out["ScheduledAction"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "action-upd", action["Id"])
diff --git a/services/opensearch/handler_serverless.go b/services/opensearch/handler_serverless.go
index 3bed19a7c2..951544f995 100644
--- a/services/opensearch/handler_serverless.go
+++ b/services/opensearch/handler_serverless.go
@@ -1,11 +1,12 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -101,7 +102,7 @@ func (h *Handler) handleServerlessCollectionCreate(w http.ResponseWriter, r *htt
KmsKeyArn string `json:"kmsKeyArn"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
coll, err := h.Backend.CreateServerlessCollection(
@@ -166,7 +167,7 @@ func (h *Handler) handleServerlessCollectionBatchRoutes(w http.ResponseWriter, r
Names []string `json:"names"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
colls := h.Backend.BatchGetServerlessCollections(req.IDs, req.Names)
if colls == nil {
@@ -217,7 +218,7 @@ func (h *Handler) handleServerlessPolicyCRUDRoot(
Description string `json:"description"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
result, err := ops.create(req.Type, req.Name, req.Description, req.Policy)
if err != nil {
@@ -265,7 +266,7 @@ func (h *Handler) handleServerlessPolicyCRUDByName(
PolicyVersion string `json:"policyVersion"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
result, err := ops.update(policyType, name, req.Description, req.Policy, req.PolicyVersion)
if err != nil {
@@ -379,7 +380,7 @@ func (h *Handler) handleServerlessSecurityConfigCollection(w http.ResponseWriter
Description string `json:"description"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
sc, err := h.Backend.CreateServerlessSecurityConfig(
req.Type,
@@ -428,7 +429,7 @@ func (h *Handler) handleServerlessSecurityConfigItem(w http.ResponseWriter, r *h
ConfigVersion string `json:"configVersion"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
sc, err := h.Backend.UpdateServerlessSecurityConfig(
id,
@@ -512,7 +513,7 @@ func (h *Handler) handleServerlessNetworkPolicyRoutes(
Description string `json:"description"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
np, err := h.Backend.CreateServerlessNetworkPolicy(
req.Type,
diff --git a/services/opensearch/handler_serverless_jsonrpc.go b/services/opensearch/handler_serverless_jsonrpc.go
index e9c47a43e2..58fd5ea3d3 100644
--- a/services/opensearch/handler_serverless_jsonrpc.go
+++ b/services/opensearch/handler_serverless_jsonrpc.go
@@ -1,7 +1,8 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"sort"
@@ -10,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// openSearchServerlessTargetPrefix is the real opensearchserverless@v1.34.4
@@ -72,7 +74,7 @@ func (h *Handler) handleServerlessJSONRPC(c *echo.Context, op string) error {
var input map[string]any
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &input); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &input, jsonx.V1()); jsonErr != nil {
return awserr.Write(c, awserr.ProtocolJSON10, awserr.APIError{
Code: errValidation,
Message: "invalid JSON",
@@ -100,7 +102,7 @@ func (h *Handler) handleServerlessJSONRPC(c *echo.Context, op string) error {
return awserr.Write(c, awserr.ProtocolJSON10, apiErr)
}
- payload, marshalErr := json.Marshal(out)
+ payload, marshalErr := json.Marshal(out, jsonx.V1())
if marshalErr != nil {
return awserr.Write(c, awserr.ProtocolJSON10, serverlessInternalError())
}
@@ -697,7 +699,7 @@ func policyDetailJR(v any) map[string]any {
m["description"] = pl.Description
}
if pl.Policy != "" {
- m["policy"] = json.RawMessage(pl.Policy)
+ m["policy"] = jsontext.Value(pl.Policy)
}
return m
@@ -732,13 +734,13 @@ func decodeSAMLOptionsJR(raw any) *ServerlessSAMLOptions {
return nil
}
- b, err := json.Marshal(m)
+ b, err := json.Marshal(m, jsonx.V1())
if err != nil {
return nil
}
var opts ServerlessSAMLOptions
- if unmarshalErr := json.Unmarshal(b, &opts); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(b, &opts, jsonx.V1()); unmarshalErr != nil {
return nil
}
diff --git a/services/opensearch/handler_serverless_real_client_test.go b/services/opensearch/handler_serverless_real_client_test.go
index 17f0134206..968fa95f0c 100644
--- a/services/opensearch/handler_serverless_real_client_test.go
+++ b/services/opensearch/handler_serverless_real_client_test.go
@@ -1,7 +1,7 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http/httptest"
"testing"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -70,7 +71,7 @@ func decodeDocument(t *testing.T, doc interface{ UnmarshalSmithyDocument(v any)
var v any
require.NoError(t, doc.UnmarshalSmithyDocument(&v))
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
return string(b)
diff --git a/services/opensearch/handler_serverless_test.go b/services/opensearch/handler_serverless_test.go
index cb6d55947a..9614485870 100644
--- a/services/opensearch/handler_serverless_test.go
+++ b/services/opensearch/handler_serverless_test.go
@@ -1,13 +1,15 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -43,7 +45,7 @@ func TestServerless_CreateCollection(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
detail, ok := out["createCollectionDetail"].(map[string]any)
require.True(t, ok, "createCollectionDetail missing")
@@ -67,7 +69,7 @@ func TestServerless_CreateCollection_DefaultType(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
detail := out["createCollectionDetail"].(map[string]any)
assert.Equal(t, "SEARCH", detail["type"])
}
@@ -92,7 +94,7 @@ func TestServerless_BatchGetCollections(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
details, ok := out["collectionDetails"].([]any)
require.True(t, ok)
@@ -116,7 +118,7 @@ func TestServerless_ListCollections(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
summaries, ok := out["collectionSummaries"].([]any)
require.True(t, ok)
@@ -132,7 +134,7 @@ func TestServerless_DeleteCollection(t *testing.T) {
createResp := doRequest(t, h, http.MethodPost, "/2021-11-01/opensearch/serverless/collection",
map[string]any{"name": "to-delete"})
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createResp.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createResp.Body), &createOut, jsonx.V1()))
createResp.Body.Close()
id := createOut["createCollectionDetail"].(map[string]any)["id"].(string)
@@ -144,7 +146,7 @@ func TestServerless_DeleteCollection(t *testing.T) {
assert.Equal(t, http.StatusOK, delResp.StatusCode)
var delOut map[string]any
- require.NoError(t, json.NewDecoder(delResp.Body).Decode(&delOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(delResp.Body), &delOut, jsonx.V1()))
detail := delOut["deleteCollectionDetail"].(map[string]any)
assert.Equal(t, "DELETED", detail["status"])
@@ -152,7 +154,7 @@ func TestServerless_DeleteCollection(t *testing.T) {
listResp := doRequest(t, h, http.MethodGet, "/2021-11-01/opensearch/serverless/collections", nil)
defer listResp.Body.Close()
var listOut map[string]any
- require.NoError(t, json.NewDecoder(listResp.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listResp.Body), &listOut, jsonx.V1()))
summaries := listOut["collectionSummaries"].([]any)
assert.Empty(t, summaries)
}
@@ -190,7 +192,7 @@ func TestServerless_CreateAccessPolicy(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
detail, ok := out["accessPolicyDetail"].(map[string]any)
require.True(t, ok)
@@ -221,7 +223,7 @@ func TestServerless_GetAccessPolicy(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
detail := out["accessPolicyDetail"].(map[string]any)
assert.Equal(t, "get-test", detail["name"])
}
@@ -244,7 +246,7 @@ func TestServerless_ListAccessPolicies(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
summaries := out["accessPolicySummaries"].([]any)
assert.Len(t, summaries, 2)
}
@@ -259,7 +261,7 @@ func TestServerless_UpdateAccessPolicy(t *testing.T) {
map[string]any{"name": "update-ap", "type": "data", "policy": `[{"old":true}]`})
var createOut map[string]any
- require.NoError(t, json.NewDecoder(r.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(r.Body), &createOut, jsonx.V1()))
r.Body.Close()
version := createOut["accessPolicyDetail"].(map[string]any)["policyVersion"].(string)
@@ -276,7 +278,7 @@ func TestServerless_UpdateAccessPolicy(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
detail := out["accessPolicyDetail"].(map[string]any)
assert.Equal(t, "updated", detail["description"])
}
@@ -300,7 +302,7 @@ func TestServerless_DeleteAccessPolicy(t *testing.T) {
"/2021-11-01/opensearch/serverless/accesspolicies?type=data", nil)
defer listResp.Body.Close()
var listOut map[string]any
- require.NoError(t, json.NewDecoder(listResp.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listResp.Body), &listOut, jsonx.V1()))
assert.Empty(t, listOut["accessPolicySummaries"].([]any))
}
@@ -339,7 +341,7 @@ func TestServerless_CreateSecurityConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
detail, ok := out["securityConfigDetail"].(map[string]any)
require.True(t, ok)
@@ -357,7 +359,7 @@ func TestServerless_GetSecurityConfig(t *testing.T) {
map[string]any{"type": "saml"})
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createResp.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createResp.Body), &createOut, jsonx.V1()))
createResp.Body.Close()
id := createOut["securityConfigDetail"].(map[string]any)["id"].(string)
@@ -368,7 +370,7 @@ func TestServerless_GetSecurityConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
assert.Equal(t, id, out["securityConfigDetail"].(map[string]any)["id"])
}
@@ -390,7 +392,7 @@ func TestServerless_ListSecurityConfigs(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
summaries := out["securityConfigSummaries"].([]any)
assert.Len(t, summaries, 3)
}
@@ -404,7 +406,7 @@ func TestServerless_UpdateSecurityConfig(t *testing.T) {
map[string]any{"type": "saml", "description": "old"})
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createResp.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createResp.Body), &createOut, jsonx.V1()))
createResp.Body.Close()
id := createOut["securityConfigDetail"].(map[string]any)["id"].(string)
@@ -421,7 +423,7 @@ func TestServerless_UpdateSecurityConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
detail := out["securityConfigDetail"].(map[string]any)
assert.Equal(t, "updated desc", detail["description"])
}
@@ -435,7 +437,7 @@ func TestServerless_DeleteSecurityConfig(t *testing.T) {
map[string]any{"type": "saml"})
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createResp.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createResp.Body), &createOut, jsonx.V1()))
createResp.Body.Close()
id := createOut["securityConfigDetail"].(map[string]any)["id"].(string)
@@ -472,7 +474,7 @@ func TestServerless_CreateEncryptionPolicy(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
detail := out["encryptionPolicyDetail"].(map[string]any)
assert.Equal(t, "enc-policy", detail["name"])
assert.NotEmpty(t, detail["policyVersion"])
@@ -495,7 +497,7 @@ func TestServerless_ListEncryptionPolicies(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
summaries := out["encryptionPolicySummaries"].([]any)
assert.Len(t, summaries, 2)
}
@@ -515,7 +517,7 @@ func TestServerless_GetEncryptionPolicy(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
assert.Equal(t, "get-ep", out["encryptionPolicyDetail"].(map[string]any)["name"])
}
@@ -528,7 +530,7 @@ func TestServerless_UpdateEncryptionPolicy(t *testing.T) {
map[string]any{"name": "upd-ep", "type": "encryption", "policy": `{"old":true}`})
var createOut map[string]any
- require.NoError(t, json.NewDecoder(r.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(r.Body), &createOut, jsonx.V1()))
r.Body.Close()
version := createOut["encryptionPolicyDetail"].(map[string]any)["policyVersion"].(string)
@@ -543,7 +545,7 @@ func TestServerless_UpdateEncryptionPolicy(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
assert.Equal(t, "updated", out["encryptionPolicyDetail"].(map[string]any)["description"])
}
@@ -587,7 +589,7 @@ func TestServerless_CreateNetworkPolicy(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
detail := out["networkPolicyDetail"].(map[string]any)
assert.Equal(t, "net-policy", detail["name"])
}
@@ -609,7 +611,7 @@ func TestServerless_ListNetworkPolicies(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
summaries := out["networkPolicySummaries"].([]any)
assert.Len(t, summaries, 3)
}
@@ -655,7 +657,7 @@ func TestDomain_OffPeakWindowOptions_Create(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
st := out["DomainStatus"].(map[string]any)
opw, ok := st["OffPeakWindowOptions"].(map[string]any)
@@ -691,7 +693,7 @@ func TestDomain_OffPeakWindowOptions_UpdateConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, upResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(upResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(upResp.Body), &out, jsonx.V1()))
cfg := out["DomainConfig"].(map[string]any)
opwCfg := cfg["OffPeakWindowOptions"].(map[string]any)
opwOpts := opwCfg["Options"].(map[string]any)
@@ -721,7 +723,7 @@ func TestDomain_IdentityCenterOptions_Create(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
st := out["DomainStatus"].(map[string]any)
idcOpts, ok := st["IdentityCenterOptions"].(map[string]any)
@@ -752,7 +754,7 @@ func TestDomain_IdentityCenterOptions_UpdateConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, upResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(upResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(upResp.Body), &out, jsonx.V1()))
cfg := out["DomainConfig"].(map[string]any)
idcCfg := cfg["IdentityCenterOptions"].(map[string]any)
idcOpts := idcCfg["Options"].(map[string]any)
@@ -779,7 +781,7 @@ func TestDomain_EnableSoftwareUpdateOptions_Create(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
st := out["DomainStatus"].(map[string]any)
swOpts, ok := st["SoftwareUpdateOptions"].(map[string]any)
@@ -809,7 +811,7 @@ func TestDomain_ClusterConfigOmitsBlueGreenDeploymentOptions(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
cc := out["DomainStatus"].(map[string]any)["ClusterConfig"].(map[string]any)
assert.NotContains(t, cc, "BlueGreenDeploymentOptions")
assert.NotContains(t, cc, "ColdStorageEnabled")
@@ -838,7 +840,7 @@ func TestPackage_CreateWithSource(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
pkg := out["PackageDetails"].(map[string]any)
assert.Equal(t, "my-dictionary", pkg["PackageName"])
@@ -869,7 +871,7 @@ func TestPackage_CreateWithEncryptionOptions(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
pkg := out["PackageDetails"].(map[string]any)
encOpts, ok := pkg["PackageEncryptionOptions"].(map[string]any)
@@ -889,7 +891,7 @@ func TestPackage_AvailablePackageVersion(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
pkg := out["PackageDetails"].(map[string]any)
assert.Equal(t, "1", pkg["AvailablePackageVersion"])
}
@@ -908,7 +910,7 @@ func TestServerless_Persistence_CollectionsRoundTrip(t *testing.T) {
map[string]any{"name": "persist-coll", "type": "TIMESERIES"})
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createResp.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createResp.Body), &createOut, jsonx.V1()))
createResp.Body.Close()
origID := createOut["createCollectionDetail"].(map[string]any)["id"].(string)
@@ -924,7 +926,7 @@ func TestServerless_Persistence_CollectionsRoundTrip(t *testing.T) {
defer listResp.Body.Close()
var listOut map[string]any
- require.NoError(t, json.NewDecoder(listResp.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listResp.Body), &listOut, jsonx.V1()))
summaries := listOut["collectionSummaries"].([]any)
require.Len(t, summaries, 1)
assert.Equal(t, origID, summaries[0].(map[string]any)["id"])
@@ -947,7 +949,7 @@ func TestServerless_Persistence_AccessPoliciesRoundTrip(t *testing.T) {
defer listResp.Body.Close()
var listOut map[string]any
- require.NoError(t, json.NewDecoder(listResp.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listResp.Body), &listOut, jsonx.V1()))
summaries := listOut["accessPolicySummaries"].([]any)
require.Len(t, summaries, 1)
assert.Equal(t, "persist-ap", summaries[0].(map[string]any)["name"])
diff --git a/services/opensearch/handler_tags.go b/services/opensearch/handler_tags.go
index 8d35500958..5b495f50ef 100644
--- a/services/opensearch/handler_tags.go
+++ b/services/opensearch/handler_tags.go
@@ -1,10 +1,11 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -67,7 +68,7 @@ func (h *Handler) handleAddTags(w http.ResponseWriter, r *http.Request) {
}
var req addTagsInput
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
@@ -104,7 +105,7 @@ func (h *Handler) handleRemoveTags(w http.ResponseWriter, r *http.Request) {
}
var req removeTagsInput
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
diff --git a/services/opensearch/handler_tags_test.go b/services/opensearch/handler_tags_test.go
index 84d393efe8..a75a99ccff 100644
--- a/services/opensearch/handler_tags_test.go
+++ b/services/opensearch/handler_tags_test.go
@@ -1,7 +1,8 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -21,7 +23,7 @@ func createDomainAndGetARN(t *testing.T, h *opensearch.Handler, domainName strin
})
var createOut map[string]any
- require.NoError(t, json.NewDecoder(createResp.Body).Decode(&createOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createResp.Body), &createOut, jsonx.V1()))
createResp.Body.Close()
status := createOut["DomainStatus"].(map[string]any)
@@ -50,7 +52,7 @@ func TestOpenSearchHandler_AddTags(t *testing.T) {
defer listResp.Body.Close()
var listOut map[string]any
- require.NoError(t, json.NewDecoder(listResp.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listResp.Body), &listOut, jsonx.V1()))
tagList, ok := listOut["TagList"].([]any)
require.True(t, ok)
@@ -84,7 +86,7 @@ func TestOpenSearchHandler_RemoveTags(t *testing.T) {
defer listResp.Body.Close()
var listOut map[string]any
- require.NoError(t, json.NewDecoder(listResp.Body).Decode(&listOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listResp.Body), &listOut, jsonx.V1()))
tagList, ok := listOut["TagList"].([]any)
require.True(t, ok)
@@ -105,7 +107,7 @@ func TestOpenSearchHandler_ListTags_EmptyDomain(t *testing.T) {
defer resp.Body.Close()
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
tagList, ok := out["TagList"].([]any)
require.True(t, ok)
diff --git a/services/opensearch/handler_test.go b/services/opensearch/handler_test.go
index 117add802e..4658bc2a09 100644
--- a/services/opensearch/handler_test.go
+++ b/services/opensearch/handler_test.go
@@ -2,7 +2,7 @@ package opensearch_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -46,7 +47,7 @@ func doRequest(t *testing.T, h *opensearch.Handler, method, path string, body an
var reqBody io.Reader
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
reqBody = bytes.NewReader(b)
diff --git a/services/opensearch/handler_vpc_endpoints.go b/services/opensearch/handler_vpc_endpoints.go
index 1fb854c1bc..340232b15a 100644
--- a/services/opensearch/handler_vpc_endpoints.go
+++ b/services/opensearch/handler_vpc_endpoints.go
@@ -1,12 +1,13 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// vpcEndpointSummaryJSON mirrors types.VpcEndpointSummary: DomainArn, Status,
@@ -58,7 +59,7 @@ func (h *Handler) handleAuthorizeVpcEndpointAccess(
}
var req authorizeVpcEndpointAccessRequest
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
h.writeError(r, w, http.StatusBadRequest, "ValidationException", "invalid JSON body")
return
@@ -95,7 +96,7 @@ func (h *Handler) handleVpcEndpointsRoutes(w http.ResponseWriter, r *http.Reques
VpcEndpointIDs []string `json:"VpcEndpointIds"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
endpoints, errs := h.Backend.DescribeVpcEndpoints(req.VpcEndpointIDs)
h.writeJSON(r, w, map[string]any{"VpcEndpoints": endpoints, "VpcEndpointErrors": errs})
@@ -130,7 +131,7 @@ func (h *Handler) handleUpdateVpcEndpoint(w http.ResponseWriter, r *http.Request
VpcEndpointID string `json:"VpcEndpointId"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
ep, updateErr := h.Backend.UpdateVpcEndpoint(req.VpcEndpointID, req.VpcOptions)
@@ -158,7 +159,7 @@ func (h *Handler) handleVpcEndpointRootRoutes(w http.ResponseWriter, r *http.Req
DomainArn string `json:"DomainArn"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
ep, createErr := h.Backend.CreateVpcEndpoint(req.DomainArn, req.VpcOptions)
if createErr != nil {
diff --git a/services/opensearch/handler_vpc_endpoints_test.go b/services/opensearch/handler_vpc_endpoints_test.go
index 3ff1e04797..b22b99ab4d 100644
--- a/services/opensearch/handler_vpc_endpoints_test.go
+++ b/services/opensearch/handler_vpc_endpoints_test.go
@@ -1,7 +1,8 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -102,7 +104,7 @@ func TestVpcEndpoints_CreateAndList(t *testing.T) {
require.Equal(t, http.StatusOK, cr.StatusCode)
var cOut map[string]any
- require.NoError(t, json.NewDecoder(cr.Body).Decode(&cOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(cr.Body), &cOut, jsonx.V1()))
ep := cOut["VpcEndpoint"].(map[string]any)
assert.NotEmpty(t, ep["VpcEndpointId"])
assert.Equal(t, "ACTIVE", ep["Status"])
@@ -120,7 +122,7 @@ func TestVpcEndpoints_CreateAndList(t *testing.T) {
require.Equal(t, http.StatusOK, lr.StatusCode)
var lOut map[string]any
- require.NoError(t, json.NewDecoder(lr.Body).Decode(&lOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(lr.Body), &lOut, jsonx.V1()))
eps, ok := lOut["VpcEndpointSummaryList"].([]any)
require.True(t, ok)
assert.Len(t, eps, 1)
@@ -175,7 +177,7 @@ func TestVpcEndpoints_ListOmitsGetOnlyFields(t *testing.T) {
require.Equal(t, http.StatusOK, lr.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(lr.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(lr.Body), &out, jsonx.V1()))
items, ok := out[tt.key].([]any)
require.True(t, ok)
require.Len(t, items, 1)
@@ -205,7 +207,7 @@ func TestVpcEndpoints_DescribeByIDs(t *testing.T) {
cr := doRequest(t, h, http.MethodPost, "/2021-01-01/opensearch/vpcEndpoints",
map[string]any{"DomainArn": domARN, "VpcOptions": map[string]any{}})
var cOut map[string]any
- require.NoError(t, json.NewDecoder(cr.Body).Decode(&cOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(cr.Body), &cOut, jsonx.V1()))
cr.Body.Close()
epID := cOut["VpcEndpoint"].(map[string]any)["VpcEndpointId"].(string)
@@ -216,7 +218,7 @@ func TestVpcEndpoints_DescribeByIDs(t *testing.T) {
require.Equal(t, http.StatusOK, dr.StatusCode)
var dOut map[string]any
- require.NoError(t, json.NewDecoder(dr.Body).Decode(&dOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(dr.Body), &dOut, jsonx.V1()))
described, ok := dOut["VpcEndpoints"].([]any)
require.True(t, ok)
assert.Len(t, described, 1)
@@ -232,7 +234,7 @@ func TestVpcEndpoints_UpdateAndDelete(t *testing.T) {
cr := doRequest(t, h, http.MethodPost, "/2021-01-01/opensearch/vpcEndpoints",
map[string]any{"DomainArn": domARN, "VpcOptions": map[string]any{}})
var cOut map[string]any
- require.NoError(t, json.NewDecoder(cr.Body).Decode(&cOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(cr.Body), &cOut, jsonx.V1()))
cr.Body.Close()
epID := cOut["VpcEndpoint"].(map[string]any)["VpcEndpointId"].(string)
@@ -248,7 +250,7 @@ func TestVpcEndpoints_UpdateAndDelete(t *testing.T) {
require.Equal(t, http.StatusOK, del.StatusCode)
var dOut map[string]any
- require.NoError(t, json.NewDecoder(del.Body).Decode(&dOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(del.Body), &dOut, jsonx.V1()))
summary := dOut["VpcEndpointSummary"].(map[string]any)
assert.Equal(t, epID, summary["VpcEndpointId"])
assert.Equal(t, domARN, summary["DomainArn"])
@@ -258,7 +260,7 @@ func TestVpcEndpoints_UpdateAndDelete(t *testing.T) {
lr := doRequest(t, h, http.MethodGet, "/2021-01-01/opensearch/vpcEndpoints", nil)
defer lr.Body.Close()
var lOut map[string]any
- require.NoError(t, json.NewDecoder(lr.Body).Decode(&lOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(lr.Body), &lOut, jsonx.V1()))
eps := lOut["VpcEndpointSummaryList"].([]any)
assert.Empty(t, eps)
}
@@ -304,7 +306,7 @@ func TestVpcEndpoints_DescribeUnknownIDReturnsEndpointNotFoundError(t *testing.T
require.Equal(t, http.StatusOK, dr.StatusCode)
var dOut map[string]any
- require.NoError(t, json.NewDecoder(dr.Body).Decode(&dOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(dr.Body), &dOut, jsonx.V1()))
errs, ok := dOut["VpcEndpointErrors"].([]any)
require.True(t, ok)
require.Len(t, errs, 1)
diff --git a/services/opensearch/interfaces.go b/services/opensearch/interfaces.go
index 34262241f8..1f74cdc80e 100644
--- a/services/opensearch/interfaces.go
+++ b/services/opensearch/interfaces.go
@@ -2,7 +2,7 @@ package opensearch
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -48,16 +48,16 @@ type StorageBackend interface {
DeleteOutboundConnection(connectionID string) (*OutboundConnection, error)
// Data source operations
- AddDataSource(domainName, name, description string, dataSourceType json.RawMessage) (string, error)
+ AddDataSource(domainName, name, description string, dataSourceType jsontext.Value) (string, error)
GetDataSource(domainName, name string) (*DataSource, error)
ListDataSources(domainName string) ([]*DataSource, error)
- UpdateDataSource(domainName, name, description string, dataSourceType json.RawMessage, status string) error
+ UpdateDataSource(domainName, name, description string, dataSourceType jsontext.Value, status string) error
DeleteDataSource(domainName, name string) error
// Direct-query data source operations
AddDirectQueryDataSource(
name, description string,
- dataSourceType json.RawMessage,
+ dataSourceType jsontext.Value,
openSearchArns []string,
dataSourceAccessPolicy ...string,
) (string, error)
@@ -65,7 +65,7 @@ type StorageBackend interface {
GetDirectQueryDataSource(name string) (*DirectQueryDataSource, error)
UpdateDirectQueryDataSource(
name, description string,
- dataSourceType json.RawMessage,
+ dataSourceType jsontext.Value,
openSearchArns []string,
dataSourceAccessPolicy string,
) (*DirectQueryDataSource, error)
diff --git a/services/opensearch/lifecycle_policies.go b/services/opensearch/lifecycle_policies.go
index 7c685a0e92..85760fa302 100644
--- a/services/opensearch/lifecycle_policies.go
+++ b/services/opensearch/lifecycle_policies.go
@@ -1,11 +1,13 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// slLifecyclePolicyResourceTypeIndex is the only real ResourceType value
@@ -53,7 +55,7 @@ type lifecyclePolicyDocument struct {
// (matching every other serverless policy family in this package).
func parseLifecycleRules(policy string) []lifecycleRule {
var doc lifecyclePolicyDocument
- if err := json.Unmarshal([]byte(policy), &doc); err != nil {
+ if err := json.Unmarshal([]byte(policy), &doc, jsonx.V1()); err != nil {
return nil
}
diff --git a/services/opensearch/models.go b/services/opensearch/models.go
index afd93ec76f..6f002682be 100644
--- a/services/opensearch/models.go
+++ b/services/opensearch/models.go
@@ -1,7 +1,7 @@
package opensearch
import (
- "encoding/json"
+ "encoding/json/jsontext"
"regexp"
"time"
@@ -196,11 +196,11 @@ type InboundConnection struct {
// DataSource represents a data source attached to an OpenSearch domain.
type DataSource struct {
- Name string `json:"name"`
- Description string `json:"description"`
- Status string `json:"status,omitempty"`
- DomainName string `json:"-"`
- DataSourceType json.RawMessage `json:"dataSourceType,omitempty"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Status string `json:"status,omitempty"`
+ DomainName string `json:"-"`
+ DataSourceType jsontext.Value `json:"dataSourceType,omitempty"`
}
// DirectQueryDataSource represents a direct-query data source.
@@ -208,12 +208,12 @@ type DirectQueryDataSource struct {
// DataSourceType is stored as raw JSON for the same reason as
// DataSource.DataSourceType above -- types.DirectQueryDataSourceType is
// also a tagged union (CloudWatchLog / SecurityLake members).
- DataSourceType json.RawMessage `json:"dataSourceType,omitempty"`
- Name string `json:"name"`
- Description string `json:"description"`
- DataSourceArn string `json:"dataSourceArn"`
- DataSourceAccessPolicy string `json:"dataSourceAccessPolicy,omitempty"`
- OpenSearchArns []string `json:"openSearchArns"`
+ DataSourceType jsontext.Value `json:"dataSourceType,omitempty"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ DataSourceArn string `json:"dataSourceArn"`
+ DataSourceAccessPolicy string `json:"dataSourceAccessPolicy,omitempty"`
+ OpenSearchArns []string `json:"openSearchArns"`
}
// DomainPackageDetails holds details about a package associated with a domain.
diff --git a/services/opensearch/multi_region_test.go b/services/opensearch/multi_region_test.go
index f64f2ac3e4..a3e70f5598 100644
--- a/services/opensearch/multi_region_test.go
+++ b/services/opensearch/multi_region_test.go
@@ -1,7 +1,8 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opensearch"
)
@@ -38,7 +40,7 @@ func regionDo(t *testing.T, h *opensearch.Handler, region, method, path, body st
out := map[string]any{}
if rec.Body.Len() > 0 {
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
}
return out
@@ -127,9 +129,9 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(t.Context())
- var doc map[string]json.RawMessage
+ var doc map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(snap, &doc))
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
_, hasRegions := doc["regions"]
assert.Equal(t, tc.wantRegions, hasRegions)
diff --git a/services/opensearch/persistence.go b/services/opensearch/persistence.go
index 1120d80970..06ff58d6d4 100644
--- a/services/opensearch/persistence.go
+++ b/services/opensearch/persistence.go
@@ -2,7 +2,7 @@ package opensearch
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"time"
@@ -118,11 +118,11 @@ func fromDryRunSnapshot(v *dryRunSnapshot) *DryRunStatus {
}
type dataSourceSnapshot struct {
- Name string `json:"name"`
- Description string `json:"description"`
- Status string `json:"status,omitempty"`
- DomainName string `json:"domainName"`
- DataSourceType json.RawMessage `json:"dataSourceType,omitempty"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Status string `json:"status,omitempty"`
+ DomainName string `json:"domainName"`
+ DataSourceType jsontext.Value `json:"dataSourceType,omitempty"`
}
func dataSourceSnapshotKey(v *dataSourceSnapshot) string { return dataSourceKey(v.DomainName, v.Name) }
@@ -381,7 +381,7 @@ var dirtyTableNames = struct {
// conversion notes.
type backendSnapshot struct {
AccountCapacityLimits ServerlessCapacityLimits `json:"accountCapacityLimits"`
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
VpcAuthorizations map[string][]AuthorizedPrincipal `json:"vpcAuthorizations"`
ScheduledActions map[string][]*ScheduledAction `json:"scheduledActions"`
PackageAssociations map[string]map[string]bool `json:"packageAssociations"`
diff --git a/services/opensearch/persistence_test.go b/services/opensearch/persistence_test.go
index ab2a20ce0a..52b7dad38c 100644
--- a/services/opensearch/persistence_test.go
+++ b/services/opensearch/persistence_test.go
@@ -1,7 +1,7 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"net/http"
"net/http/httptest"
"testing"
@@ -633,10 +633,10 @@ func TestOpenSearchHandler_Persistence_AdditionalResources(t *testing.T) {
opensearch.SeedInboundConnection(b, "conn-abc")
- _, err = b.AddDataSource("snap-domain", "my-ds", "desc", json.RawMessage(`{"S3GlueDataCatalog":{}}`))
+ _, err = b.AddDataSource("snap-domain", "my-ds", "desc", jsontext.Value(`{"S3GlueDataCatalog":{}}`))
require.NoError(t, err)
- _, err = b.AddDirectQueryDataSource("my-dq", "desc", json.RawMessage(`{"CloudWatchLog":{}}`), []string{})
+ _, err = b.AddDirectQueryDataSource("my-dq", "desc", jsontext.Value(`{"CloudWatchLog":{}}`), []string{})
require.NoError(t, err)
b.AddPackageInternal("pkg-001", "test-pkg", "TXT-DICTIONARY")
@@ -786,7 +786,7 @@ func TestInMemoryBackend_SnapshotRestore_FullState(t *testing.T) {
require.NoError(t, err)
// "Dirty" DTO tables: domainDataSources, domainIndexes, dryRuns.
- _, err = original.AddDataSource(domain.Name, "ds-1", "a data source", json.RawMessage(`{"S3GlueDataCatalog":{}}`))
+ _, err = original.AddDataSource(domain.Name, "ds-1", "a data source", jsontext.Value(`{"S3GlueDataCatalog":{}}`))
require.NoError(t, err)
_, err = original.CreateIndex(domain.Name, "idx-1", nil, nil, nil, nil)
@@ -806,7 +806,7 @@ func TestInMemoryBackend_SnapshotRestore_FullState(t *testing.T) {
_, err = original.AddDirectQueryDataSource(
"dq-1",
"a direct query source",
- json.RawMessage(`{"CloudWatchLog":{}}`),
+ jsontext.Value(`{"CloudWatchLog":{}}`),
nil,
)
require.NoError(t, err)
diff --git a/services/opensearch/store_test.go b/services/opensearch/store_test.go
index 8035a6941c..ed03f21384 100644
--- a/services/opensearch/store_test.go
+++ b/services/opensearch/store_test.go
@@ -1,7 +1,7 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"testing"
"github.com/stretchr/testify/assert"
@@ -67,10 +67,10 @@ func TestExportCounts(t *testing.T) {
opensearch.SeedInboundConnection(b, "conn-1")
- _, err := b.AddDataSource("d1", "ds-1", "desc", json.RawMessage(`{"S3GlueDataCatalog":{}}`))
+ _, err := b.AddDataSource("d1", "ds-1", "desc", jsontext.Value(`{"S3GlueDataCatalog":{}}`))
require.NoError(t, err)
- _, err = b.AddDirectQueryDataSource("dq-1", "desc", json.RawMessage(`{"CloudWatchLog":{}}`), nil)
+ _, err = b.AddDirectQueryDataSource("dq-1", "desc", jsontext.Value(`{"CloudWatchLog":{}}`), nil)
require.NoError(t, err)
_, err = b.CreateApplication("app-1", nil, nil, nil)
diff --git a/services/opensearch/wire_field_fixes_test.go b/services/opensearch/wire_field_fixes_test.go
index 9623549ddf..2ace603785 100644
--- a/services/opensearch/wire_field_fixes_test.go
+++ b/services/opensearch/wire_field_fixes_test.go
@@ -1,7 +1,8 @@
package opensearch_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -11,6 +12,8 @@ import (
opensearchsdktypes "github.com/aws/aws-sdk-go-v2/service/opensearch/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestUpgradeDomain_RealSDKClient proves UpgradeDomainOutput
@@ -64,7 +67,7 @@ func TestUpgradeDomain_RawBody_NoInventedStepStatus(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
_, hasStepStatus := out["StepStatus"]
assert.False(t, hasStepStatus, "UpgradeDomainOutput has no StepStatus member")
@@ -87,7 +90,7 @@ func TestVpcEndpoint_RawBody_NoLeakedStatusUntil(t *testing.T) {
cr := doRequest(t, h, http.MethodPost, "/2021-01-01/opensearch/vpcEndpoints",
map[string]any{"DomainArn": domARN, "VpcOptions": map[string]any{"SubnetIds": []string{"subnet-1"}}})
var cOut map[string]any
- require.NoError(t, json.NewDecoder(cr.Body).Decode(&cOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(cr.Body), &cOut, jsonx.V1()))
cr.Body.Close()
epID := cOut["VpcEndpoint"].(map[string]any)["VpcEndpointId"].(string)
@@ -100,7 +103,7 @@ func TestVpcEndpoint_RawBody_NoLeakedStatusUntil(t *testing.T) {
require.Equal(t, http.StatusOK, descResp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(descResp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(descResp.Body), &out, jsonx.V1()))
endpoints, ok := out["VpcEndpoints"].([]any)
require.True(t, ok)
require.Len(t, endpoints, 1, "endpoint must still be present during its DELETING window")
diff --git a/services/opsworks/handler.go b/services/opsworks/handler.go
index 29b3c85410..41204d20b2 100644
--- a/services/opsworks/handler.go
+++ b/services/opsworks/handler.go
@@ -2,7 +2,7 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -12,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -270,13 +271,10 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, awserr.ErrNotFound):
return writeError(c, http.StatusBadRequest, errResp("ResourceNotFoundException", err.Error()))
@@ -287,8 +285,8 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
// ValidationException, not 501.
return writeError(c, http.StatusBadRequest, errResp("ValidationException", err.Error()))
case errors.Is(err, errInvalidRequest),
- errors.As(err, &syntaxErr),
- errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err),
+ jsonx.IsTypeError(err):
return writeError(c, http.StatusBadRequest, errResp("ValidationException", err.Error()))
default:
logger.Load(c.Request().Context()).ErrorContext(c.Request().Context(), "opsworks error", "error", err)
diff --git a/services/opsworks/handler_agent_versions.go b/services/opsworks/handler_agent_versions.go
index aa310db815..c42b2b202b 100644
--- a/services/opsworks/handler_agent_versions.go
+++ b/services/opsworks/handler_agent_versions.go
@@ -2,8 +2,10 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleDescribeAgentVersions handles DescribeAgentVersions requests.
@@ -17,7 +19,7 @@ func (h *Handler) handleDescribeAgentVersions(_ context.Context, body []byte) (a
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
diff --git a/services/opsworks/handler_apps.go b/services/opsworks/handler_apps.go
index 067c5359f6..5a5184dd70 100644
--- a/services/opsworks/handler_apps.go
+++ b/services/opsworks/handler_apps.go
@@ -2,8 +2,10 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleCreateApp handles CreateApp requests.
@@ -15,7 +17,7 @@ func (h *Handler) handleCreateApp(_ context.Context, body []byte) (any, error) {
AppOptions
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -35,7 +37,7 @@ func (h *Handler) handleDescribeApps(_ context.Context, body []byte) (any, error
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -57,7 +59,7 @@ func (h *Handler) handleUpdateApp(_ context.Context, body []byte) (any, error) {
AppOptions
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -74,7 +76,7 @@ func (h *Handler) handleDeleteApp(_ context.Context, body []byte) (any, error) {
AppID string `json:"AppId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/opsworks/handler_commands.go b/services/opsworks/handler_commands.go
index 404792631b..b26858f30e 100644
--- a/services/opsworks/handler_commands.go
+++ b/services/opsworks/handler_commands.go
@@ -2,8 +2,10 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleDescribeCommands handles DescribeCommands requests.
@@ -16,7 +18,7 @@ func (h *Handler) handleDescribeCommands(_ context.Context, body []byte) (any, e
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
diff --git a/services/opsworks/handler_deployments.go b/services/opsworks/handler_deployments.go
index e1322b004c..64c329ac6a 100644
--- a/services/opsworks/handler_deployments.go
+++ b/services/opsworks/handler_deployments.go
@@ -2,8 +2,10 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleCreateDeployment handles CreateDeployment requests.
@@ -20,7 +22,7 @@ func (h *Handler) handleCreateDeployment(_ context.Context, body []byte) (any, e
LayerIDs []string `json:"LayerIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -44,7 +46,7 @@ func (h *Handler) handleDescribeDeployments(_ context.Context, body []byte) (any
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
diff --git a/services/opsworks/handler_ecs_clusters.go b/services/opsworks/handler_ecs_clusters.go
index 12fdb7634f..69f6c65340 100644
--- a/services/opsworks/handler_ecs_clusters.go
+++ b/services/opsworks/handler_ecs_clusters.go
@@ -2,9 +2,10 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -21,7 +22,7 @@ func (h *Handler) handleRegisterEcsCluster(_ context.Context, body []byte) (any,
StackID string `json:"StackId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -39,7 +40,7 @@ func (h *Handler) handleDeregisterEcsCluster(_ context.Context, body []byte) (an
EcsClusterArn string `json:"EcsClusterArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -60,7 +61,7 @@ func (h *Handler) handleDescribeEcsClusters(_ context.Context, body []byte) (any
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
diff --git a/services/opsworks/handler_elastic_ips.go b/services/opsworks/handler_elastic_ips.go
index 0862569cda..b5640218d2 100644
--- a/services/opsworks/handler_elastic_ips.go
+++ b/services/opsworks/handler_elastic_ips.go
@@ -2,8 +2,10 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleRegisterElasticIP handles RegisterElasticIp requests. The real
@@ -16,7 +18,7 @@ func (h *Handler) handleRegisterElasticIP(_ context.Context, body []byte) (any,
StackID string `json:"StackId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -34,7 +36,7 @@ func (h *Handler) handleDeregisterElasticIP(_ context.Context, body []byte) (any
ElasticIP string `json:"ElasticIp"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -52,7 +54,7 @@ func (h *Handler) handleAssociateElasticIP(_ context.Context, body []byte) (any,
InstanceID string `json:"InstanceId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -69,7 +71,7 @@ func (h *Handler) handleDisassociateElasticIP(_ context.Context, body []byte) (a
ElasticIP string `json:"ElasticIp"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -89,7 +91,7 @@ func (h *Handler) handleDescribeElasticIps(_ context.Context, body []byte) (any,
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -109,7 +111,7 @@ func (h *Handler) handleUpdateElasticIP(_ context.Context, body []byte) (any, er
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/opsworks/handler_elastic_load_balancers.go b/services/opsworks/handler_elastic_load_balancers.go
index 8601ec67bb..d90a8c361b 100644
--- a/services/opsworks/handler_elastic_load_balancers.go
+++ b/services/opsworks/handler_elastic_load_balancers.go
@@ -2,8 +2,10 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleAttachElasticLoadBalancer handles AttachElasticLoadBalancer requests.
@@ -13,7 +15,7 @@ func (h *Handler) handleAttachElasticLoadBalancer(_ context.Context, body []byte
LayerID string `json:"LayerId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -31,7 +33,7 @@ func (h *Handler) handleDetachElasticLoadBalancer(_ context.Context, body []byte
LayerID string `json:"LayerId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -50,7 +52,7 @@ func (h *Handler) handleDescribeElasticLoadBalancers(_ context.Context, body []b
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
diff --git a/services/opsworks/handler_instances.go b/services/opsworks/handler_instances.go
index 598ef5ceba..1d775260c4 100644
--- a/services/opsworks/handler_instances.go
+++ b/services/opsworks/handler_instances.go
@@ -2,10 +2,11 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
)
@@ -25,7 +26,7 @@ func (h *Handler) handleCreateInstance(_ context.Context, body []byte) (any, err
LayerIDs []string `json:"LayerIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -56,7 +57,7 @@ func (h *Handler) handleRegisterInstance(_ context.Context, body []byte) (any, e
InstanceExtras
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -74,7 +75,7 @@ func (h *Handler) handleDeregisterInstance(_ context.Context, body []byte) (any,
InstanceID string `json:"InstanceId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -92,7 +93,7 @@ func (h *Handler) handleAssignInstance(_ context.Context, body []byte) (any, err
LayerIDs []string `json:"LayerIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -109,7 +110,7 @@ func (h *Handler) handleUnassignInstance(_ context.Context, body []byte) (any, e
InstanceID string `json:"InstanceId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -129,7 +130,7 @@ func (h *Handler) handleDescribeInstances(_ context.Context, body []byte) (any,
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -156,7 +157,7 @@ func (h *Handler) handleUpdateInstance(_ context.Context, body []byte) (any, err
LayerIDs []string `json:"LayerIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -185,7 +186,7 @@ func (h *Handler) handleDeleteInstance(_ context.Context, body []byte) (any, err
InstanceID string `json:"InstanceId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -204,7 +205,7 @@ func (h *Handler) handleStartInstance(_ context.Context, body []byte) (any, erro
InstanceID string `json:"InstanceId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -221,7 +222,7 @@ func (h *Handler) handleStopInstance(_ context.Context, body []byte) (any, error
InstanceID string `json:"InstanceId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -238,7 +239,7 @@ func (h *Handler) handleRebootInstance(_ context.Context, body []byte) (any, err
InstanceID string `json:"InstanceId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -276,9 +277,9 @@ func instancesToJSON(instances []*Instance) []map[string]any {
"ReportedOs": map[string]any{},
}
- if raw, err := json.Marshal(i.Extras); err == nil {
+ if raw, err := json.Marshal(i.Extras, jsonx.V1()); err == nil {
var extra map[string]any
- if json.Unmarshal(raw, &extra) == nil {
+ if json.Unmarshal(raw, &extra, jsonx.V1()) == nil {
maps.Copy(m, extra)
}
}
diff --git a/services/opsworks/handler_layers.go b/services/opsworks/handler_layers.go
index b776cfd869..0ffb64302d 100644
--- a/services/opsworks/handler_layers.go
+++ b/services/opsworks/handler_layers.go
@@ -2,9 +2,11 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleCreateLayer handles CreateLayer requests.
@@ -17,7 +19,7 @@ func (h *Handler) handleCreateLayer(_ context.Context, body []byte) (any, error)
LayerSettings
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -37,7 +39,7 @@ func (h *Handler) handleDescribeLayers(_ context.Context, body []byte) (any, err
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -59,7 +61,7 @@ func (h *Handler) handleUpdateLayer(_ context.Context, body []byte) (any, error)
LayerSettings
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -76,7 +78,7 @@ func (h *Handler) handleDeleteLayer(_ context.Context, body []byte) (any, error)
LayerID string `json:"LayerId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -121,9 +123,9 @@ func layersToJSON(layers []*Layer) []map[string]any {
"Attributes": attrs,
}
- if raw, err := json.Marshal(l.Settings); err == nil {
+ if raw, err := json.Marshal(l.Settings, jsonx.V1()); err == nil {
var extra map[string]any
- if json.Unmarshal(raw, &extra) == nil {
+ if json.Unmarshal(raw, &extra, jsonx.V1()) == nil {
maps.Copy(m, extra)
}
}
diff --git a/services/opsworks/handler_load_based_scaling.go b/services/opsworks/handler_load_based_scaling.go
index 057ad6731e..942075ac0c 100644
--- a/services/opsworks/handler_load_based_scaling.go
+++ b/services/opsworks/handler_load_based_scaling.go
@@ -2,8 +2,10 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleSetLoadBasedAutoScaling handles SetLoadBasedAutoScaling requests.
@@ -15,7 +17,7 @@ func (h *Handler) handleSetLoadBasedAutoScaling(_ context.Context, body []byte)
Enable bool `json:"Enable"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -33,7 +35,7 @@ func (h *Handler) handleDescribeLoadBasedAutoScaling(_ context.Context, body []b
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
diff --git a/services/opsworks/handler_rds_db_instances.go b/services/opsworks/handler_rds_db_instances.go
index c8dfdaf3a6..fc783768ff 100644
--- a/services/opsworks/handler_rds_db_instances.go
+++ b/services/opsworks/handler_rds_db_instances.go
@@ -2,8 +2,10 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleRegisterRdsDBInstance handles RegisterRdsDbInstance requests.
@@ -15,7 +17,7 @@ func (h *Handler) handleRegisterRdsDBInstance(_ context.Context, body []byte) (a
DBPassword string `json:"DbPassword"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -34,7 +36,7 @@ func (h *Handler) handleDeregisterRdsDBInstance(_ context.Context, body []byte)
RdsDBInstanceArn string `json:"RdsDbInstanceArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -53,7 +55,7 @@ func (h *Handler) handleDescribeRdsDBInstances(_ context.Context, body []byte) (
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -74,7 +76,7 @@ func (h *Handler) handleUpdateRdsDBInstance(_ context.Context, body []byte) (any
DBPassword string `json:"DbPassword"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/opsworks/handler_service_errors.go b/services/opsworks/handler_service_errors.go
index e6c43b18ef..cfcbe37102 100644
--- a/services/opsworks/handler_service_errors.go
+++ b/services/opsworks/handler_service_errors.go
@@ -2,8 +2,10 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleDescribeServiceErrors handles DescribeServiceErrors requests.
@@ -15,7 +17,7 @@ func (h *Handler) handleDescribeServiceErrors(_ context.Context, body []byte) (a
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -37,7 +39,7 @@ func (h *Handler) handleDescribeRaidArrays(_ context.Context, body []byte) (any,
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
diff --git a/services/opsworks/handler_stacks.go b/services/opsworks/handler_stacks.go
index 6c4f58813f..800c37c4ce 100644
--- a/services/opsworks/handler_stacks.go
+++ b/services/opsworks/handler_stacks.go
@@ -2,8 +2,10 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleCreateStack handles CreateStack requests.
@@ -36,7 +38,7 @@ func (h *Handler) handleCreateStack(_ context.Context, body []byte) (any, error)
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -114,7 +116,7 @@ func (h *Handler) handleCloneStack(_ context.Context, body []byte) (any, error)
CloneAppIDs []string `json:"CloneAppIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -164,7 +166,7 @@ func (h *Handler) handleDescribeStacks(_ context.Context, body []byte) (any, err
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -206,7 +208,7 @@ func (h *Handler) handleUpdateStack(_ context.Context, body []byte) (any, error)
StackID string `json:"StackId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -252,7 +254,7 @@ func (h *Handler) handleDeleteStack(_ context.Context, body []byte) (any, error)
StackID string `json:"StackId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -269,7 +271,7 @@ func (h *Handler) handleStartStack(_ context.Context, body []byte) (any, error)
StackID string `json:"StackId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -286,7 +288,7 @@ func (h *Handler) handleStopStack(_ context.Context, body []byte) (any, error) {
StackID string `json:"StackId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -303,7 +305,7 @@ func (h *Handler) handleGetHostnameSuggestion(_ context.Context, body []byte) (a
LayerID string `json:"LayerId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -321,7 +323,7 @@ func (h *Handler) handleDescribeStackSummary(_ context.Context, body []byte) (an
StackID string `json:"StackId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -339,7 +341,7 @@ func (h *Handler) handleDescribeStackProvisioningParameters(_ context.Context, b
StackID string `json:"StackId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/opsworks/handler_tags.go b/services/opsworks/handler_tags.go
index 3c10d43636..f8225e36e4 100644
--- a/services/opsworks/handler_tags.go
+++ b/services/opsworks/handler_tags.go
@@ -2,8 +2,10 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleTagResource handles TagResource requests.
@@ -13,7 +15,7 @@ func (h *Handler) handleTagResource(_ context.Context, body []byte) (any, error)
ResourceArn string `json:"ResourceArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -31,7 +33,7 @@ func (h *Handler) handleUntagResource(_ context.Context, body []byte) (any, erro
TagKeys []string `json:"TagKeys"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -50,7 +52,7 @@ func (h *Handler) handleListTags(_ context.Context, body []byte) (any, error) {
MaxResults int32 `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/opsworks/handler_test.go b/services/opsworks/handler_test.go
index 1964c9ab5a..836123741e 100644
--- a/services/opsworks/handler_test.go
+++ b/services/opsworks/handler_test.go
@@ -2,7 +2,7 @@ package opsworks_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/opsworks"
)
@@ -29,7 +30,7 @@ func doTarget(t *testing.T, h *opsworks.Handler, operation string, body any) *ht
if body != nil {
var marshalErr error
- bodyBytes, marshalErr = json.Marshal(body)
+ bodyBytes, marshalErr = json.Marshal(body, jsonx.V1())
require.NoError(t, marshalErr)
}
@@ -50,7 +51,7 @@ func parseJSON(t *testing.T, body []byte) map[string]any {
t.Helper()
var result map[string]any
- require.NoError(t, json.Unmarshal(body, &result))
+ require.NoError(t, json.Unmarshal(body, &result, jsonx.V1()))
return result
}
diff --git a/services/opsworks/handler_time_based_scaling.go b/services/opsworks/handler_time_based_scaling.go
index 0fe13b6653..9e7499d081 100644
--- a/services/opsworks/handler_time_based_scaling.go
+++ b/services/opsworks/handler_time_based_scaling.go
@@ -2,8 +2,10 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleSetTimeBasedAutoScaling handles SetTimeBasedAutoScaling requests.
@@ -13,7 +15,7 @@ func (h *Handler) handleSetTimeBasedAutoScaling(_ context.Context, body []byte)
InstanceID string `json:"InstanceId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -31,7 +33,7 @@ func (h *Handler) handleDescribeTimeBasedAutoScaling(_ context.Context, body []b
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
diff --git a/services/opsworks/handler_users_permissions.go b/services/opsworks/handler_users_permissions.go
index ee29435fb3..c562c40da9 100644
--- a/services/opsworks/handler_users_permissions.go
+++ b/services/opsworks/handler_users_permissions.go
@@ -2,8 +2,10 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleCreateUserProfile handles CreateUserProfile requests.
@@ -15,7 +17,7 @@ func (h *Handler) handleCreateUserProfile(_ context.Context, body []byte) (any,
AllowSelfManagement bool `json:"AllowSelfManagement"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -38,7 +40,7 @@ func (h *Handler) handleDeleteUserProfile(_ context.Context, body []byte) (any,
IamUserArn string `json:"IamUserArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -56,7 +58,7 @@ func (h *Handler) handleDescribeUserProfiles(_ context.Context, body []byte) (an
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -77,7 +79,7 @@ func (h *Handler) handleUpdateUserProfile(_ context.Context, body []byte) (any,
SSHPublicKey string `json:"SshPublicKey"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -112,7 +114,7 @@ func (h *Handler) handleUpdateMyUserProfile(_ context.Context, body []byte) (any
SSHPublicKey string `json:"SshPublicKey"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -152,7 +154,7 @@ func (h *Handler) handleSetPermission(_ context.Context, body []byte) (any, erro
AllowSudo bool `json:"AllowSudo"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -171,7 +173,7 @@ func (h *Handler) handleDescribePermissions(_ context.Context, body []byte) (any
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -206,7 +208,7 @@ func (h *Handler) handleGrantAccess(_ context.Context, body []byte) (any, error)
ValidForInMinutes int32 `json:"ValidForInMinutes"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/opsworks/handler_volumes.go b/services/opsworks/handler_volumes.go
index 63c5c36b0d..7f1b471055 100644
--- a/services/opsworks/handler_volumes.go
+++ b/services/opsworks/handler_volumes.go
@@ -2,8 +2,10 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleRegisterVolume handles RegisterVolume requests.
@@ -13,7 +15,7 @@ func (h *Handler) handleRegisterVolume(_ context.Context, body []byte) (any, err
StackID string `json:"StackId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -31,7 +33,7 @@ func (h *Handler) handleDeregisterVolume(_ context.Context, body []byte) (any, e
VolumeID string `json:"VolumeId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -49,7 +51,7 @@ func (h *Handler) handleAssignVolume(_ context.Context, body []byte) (any, error
InstanceID string `json:"InstanceId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -66,7 +68,7 @@ func (h *Handler) handleUnassignVolume(_ context.Context, body []byte) (any, err
VolumeID string `json:"VolumeId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -87,7 +89,7 @@ func (h *Handler) handleDescribeVolumes(_ context.Context, body []byte) (any, er
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -108,7 +110,7 @@ func (h *Handler) handleUpdateVolume(_ context.Context, body []byte) (any, error
MountPoint string `json:"MountPoint"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/opsworks/interfaces.go b/services/opsworks/interfaces.go
index 2108c0e51f..d38a371d84 100644
--- a/services/opsworks/interfaces.go
+++ b/services/opsworks/interfaces.go
@@ -2,7 +2,7 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -314,10 +314,10 @@ type LayerSettings struct {
Attributes map[string]string `json:"Attributes,omitempty"`
CustomInstanceProfileArn string `json:"CustomInstanceProfileArn,omitempty"`
CustomJSON string `json:"CustomJson,omitempty"`
- CustomRecipes json.RawMessage `json:"CustomRecipes,omitempty"`
- CloudWatchLogsConfiguration json.RawMessage `json:"CloudWatchLogsConfiguration,omitempty"`
- LifecycleEventConfiguration json.RawMessage `json:"LifecycleEventConfiguration,omitempty"`
- VolumeConfigurations json.RawMessage `json:"VolumeConfigurations,omitempty"`
+ CustomRecipes jsontext.Value `json:"CustomRecipes,omitempty"`
+ CloudWatchLogsConfiguration jsontext.Value `json:"CloudWatchLogsConfiguration,omitempty"`
+ LifecycleEventConfiguration jsontext.Value `json:"LifecycleEventConfiguration,omitempty"`
+ VolumeConfigurations jsontext.Value `json:"VolumeConfigurations,omitempty"`
Packages []string `json:"Packages,omitempty"`
CustomSecurityGroupIDs []string `json:"CustomSecurityGroupIds,omitempty"`
}
diff --git a/services/opsworks/layers.go b/services/opsworks/layers.go
index 8b86af2e27..029a81572d 100644
--- a/services/opsworks/layers.go
+++ b/services/opsworks/layers.go
@@ -1,7 +1,7 @@
package opsworks
import (
- "encoding/json"
+ "encoding/json/jsontext"
"maps"
"reflect"
"slices"
@@ -185,7 +185,7 @@ func overlayBool(dst **bool, src *bool) {
}
}
-func overlayRaw(cur, in json.RawMessage) json.RawMessage {
+func overlayRaw(cur, in jsontext.Value) jsontext.Value {
if len(in) > 0 {
return in
}
diff --git a/services/opsworks/models.go b/services/opsworks/models.go
index d26fe01151..757f8e54c1 100644
--- a/services/opsworks/models.go
+++ b/services/opsworks/models.go
@@ -1,10 +1,12 @@
package opsworks
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"slices"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -176,7 +178,7 @@ func (i *storedInstance) UnmarshalJSON(data []byte) error {
alias
}
- if err := json.Unmarshal(data, &v); err != nil {
+ if err := json.Unmarshal(data, &v, jsonx.V1()); err != nil {
return err
}
diff --git a/services/opsworks/multi_region_test.go b/services/opsworks/multi_region_test.go
index 4ff7f930b9..449c1d35dd 100644
--- a/services/opsworks/multi_region_test.go
+++ b/services/opsworks/multi_region_test.go
@@ -2,7 +2,8 @@ package opsworks_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/opsworks"
)
@@ -176,8 +178,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/opsworks/persistence.go b/services/opsworks/persistence.go
index 033bba6ecf..94df4a6de0 100644
--- a/services/opsworks/persistence.go
+++ b/services/opsworks/persistence.go
@@ -2,7 +2,7 @@ package opsworks
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -30,7 +30,7 @@ const opsworksSnapshotVersion = 1
// from an incompatible (older or newer) build of this backend as though it
// were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
AccountID string `json:"accountID"`
Region string `json:"region"`
diff --git a/services/opsworks/whitebox_test.go b/services/opsworks/whitebox_test.go
index be4e71e088..7a7a15be66 100644
--- a/services/opsworks/whitebox_test.go
+++ b/services/opsworks/whitebox_test.go
@@ -1,11 +1,13 @@
package opsworks
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestFormatOpsWorksTimeNonUTCZone builds every timestamp input in a non-UTC
@@ -81,10 +83,10 @@ func TestStoredInstanceUnmarshalJSON_LegacyLayerID(t *testing.T) {
t.Parallel()
var legacy storedInstance
- require.NoError(t, json.Unmarshal([]byte(`{"instanceId":"i-1","layerId":"l-1"}`), &legacy))
+ require.NoError(t, json.Unmarshal([]byte(`{"instanceId":"i-1","layerId":"l-1"}`), &legacy, jsonx.V1()))
require.Equal(t, []string{"l-1"}, legacy.LayerIDs)
var current storedInstance
- require.NoError(t, json.Unmarshal([]byte(`{"instanceId":"i-2","layerIds":["l-2","l-3"]}`), ¤t))
+ require.NoError(t, json.Unmarshal([]byte(`{"instanceId":"i-2","layerIds":["l-2","l-3"]}`), ¤t, jsonx.V1()))
require.Equal(t, []string{"l-2", "l-3"}, current.LayerIDs)
}
diff --git a/services/organizations/account_provisioning.go b/services/organizations/account_provisioning.go
index 56a3b02127..58f43014ec 100644
--- a/services/organizations/account_provisioning.go
+++ b/services/organizations/account_provisioning.go
@@ -1,8 +1,10 @@
package organizations
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const defaultAccessRoleName = "OrganizationAccountAccessRole"
@@ -28,7 +30,7 @@ func accessRoleTrustPolicy(managementAccountID string) string {
"Principal": map[string]string{"AWS": fmt.Sprintf("arn:aws:iam::%s:root", managementAccountID)},
"Action": "sts:AssumeRole",
}},
- })
+ }, jsonx.V1())
return string(doc)
}
diff --git a/services/organizations/default_policy_test.go b/services/organizations/default_policy_test.go
index 3c710b366a..3d5ead4f98 100644
--- a/services/organizations/default_policy_test.go
+++ b/services/organizations/default_policy_test.go
@@ -1,12 +1,15 @@
package organizations_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDefaultFullAWSAccessPolicy_ViaHandler verifies that every organization
@@ -21,7 +24,7 @@ func TestDefaultFullAWSAccessPolicy_ViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp describePolicyResp
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, "p-FullAWSAccess", resp.Policy.PolicySummary.ID)
assert.Equal(t, "FullAWSAccess", resp.Policy.PolicySummary.Name)
@@ -44,7 +47,7 @@ func TestDefaultFullAWSAccessPolicy_ARN_ViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp describePolicyResp
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t,
"arn:aws:organizations::aws:policy/SERVICE_CONTROL_POLICY/p-FullAWSAccess",
@@ -65,7 +68,7 @@ func TestDefaultFullAWSAccessPolicy_AttachedToRoot_ViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp listPoliciesForTargetResp
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
found := false
@@ -90,7 +93,7 @@ func TestDeleteFullAWSAccessPolicy_Rejected_ViaHandler(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "AccessDeniedException", errResp["__type"])
// The survivor check: DescribePolicy must still find it afterward.
@@ -115,14 +118,14 @@ func TestUpdateFullAWSAccessPolicy_Rejected_ViaHandler(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "AccessDeniedException", errResp["__type"])
describeRec := doRequest(t, h, "DescribePolicy", map[string]any{"PolicyId": "p-FullAWSAccess"})
require.Equal(t, http.StatusOK, describeRec.Code)
var resp describePolicyResp
- require.NoError(t, json.NewDecoder(describeRec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(describeRec.Body), &resp, jsonx.V1()))
assert.Equal(t, "FullAWSAccess", resp.Policy.PolicySummary.Name, "name must not be hijacked")
assert.JSONEq(t,
`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}`,
@@ -149,7 +152,7 @@ func TestUserPolicy_AwsManagedFalse_StillDeletable_ViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp createPolicyResp
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createResp, jsonx.V1()))
assert.False(t, createResp.Policy.PolicySummary.AwsManaged, "user-created policy must not be AwsManaged")
policyID := createResp.Policy.PolicySummary.ID
diff --git a/services/organizations/delegated_administrators_test.go b/services/organizations/delegated_administrators_test.go
index 0e508fe649..bebfba20c0 100644
--- a/services/organizations/delegated_administrators_test.go
+++ b/services/organizations/delegated_administrators_test.go
@@ -1,7 +1,8 @@
package organizations_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -90,7 +92,7 @@ func TestDelegatedServices(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &createResp, jsonx.V1()))
accountID := createResp["CreateAccountStatus"].(map[string]any)["AccountId"].(string)
rec := doRequest(t, h, "EnableAWSServiceAccess", map[string]any{
@@ -111,7 +113,7 @@ func TestDelegatedServices(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
services, ok := resp["DelegatedServices"].([]any)
require.True(t, ok)
require.Len(t, services, 1)
@@ -407,7 +409,7 @@ func TestRegisterDelegatedAdmin_ViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
statusObj := createResp["CreateAccountStatus"].(map[string]any)
accountID := statusObj["AccountId"].(string)
diff --git a/services/organizations/effective_policy.go b/services/organizations/effective_policy.go
index 1ae31675ec..92c937c0c8 100644
--- a/services/organizations/effective_policy.go
+++ b/services/organizations/effective_policy.go
@@ -1,9 +1,11 @@
package organizations
import (
- "encoding/json"
+ "encoding/json/v2"
"slices"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// validEffectivePolicyTypes is the EffectivePolicyType enum (aws-sdk-go-v2 organizations v1.53.5 enums.go).
@@ -130,12 +132,12 @@ func (b *InMemoryBackend) mergeTagStyleChain(chain []effectivePolicyEntry) (stri
for _, entry := range slices.Backward(chain) {
var obj map[string]any
- if err := json.Unmarshal([]byte(entry.content), &obj); err == nil {
+ if err := json.Unmarshal([]byte(entry.content), &obj, jsonx.V1()); err == nil {
mergeJSONObjects(merged, obj)
}
}
- if data, err := json.Marshal(merged); err == nil {
+ if data, err := json.Marshal(merged, jsonx.V1()); err == nil {
return string(data), chain[0].id
}
diff --git a/services/organizations/effective_policy_test.go b/services/organizations/effective_policy_test.go
index 229f10a176..0b63e2c2fa 100644
--- a/services/organizations/effective_policy_test.go
+++ b/services/organizations/effective_policy_test.go
@@ -1,13 +1,15 @@
package organizations_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -137,7 +139,7 @@ func TestDescribeEffectivePolicy_MergeTagPolicy(t *testing.T) {
// Merged content should have both "env" (from child) and "owner" (from parent).
var merged map[string]any
- require.NoError(t, json.Unmarshal([]byte(ep.PolicyContent), &merged))
+ require.NoError(t, json.Unmarshal([]byte(ep.PolicyContent), &merged, jsonx.V1()))
tags, hasTags := merged["tags"].(map[string]any)
require.True(t, hasTags, "merged policy should have 'tags' object")
@@ -234,7 +236,7 @@ func TestHandler_DescribeEffectivePolicy(t *testing.T) {
require.Equal(t, http.StatusOK, acctRec.Code)
var acctResp map[string]any
- require.NoError(t, json.NewDecoder(acctRec.Body).Decode(&acctResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(acctRec.Body), &acctResp, jsonx.V1()))
status := acctResp["CreateAccountStatus"].(map[string]any)
targetID = status["AccountId"].(string)
@@ -247,7 +249,7 @@ func TestHandler_DescribeEffectivePolicy(t *testing.T) {
require.Equal(t, http.StatusOK, policyRec.Code)
var policyResp map[string]any
- require.NoError(t, json.NewDecoder(policyRec.Body).Decode(&policyResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(policyRec.Body), &policyResp, jsonx.V1()))
policyObj := policyResp["Policy"].(map[string]any)
summary := policyObj["PolicySummary"].(map[string]any)
policyID := summary["Id"].(string)
@@ -274,7 +276,7 @@ func TestHandler_DescribeEffectivePolicy(t *testing.T) {
if tt.wantContent {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
ep, ok := resp["EffectivePolicy"].(map[string]any)
require.True(t, ok, "response must have EffectivePolicy")
assert.NotEmpty(t, ep["PolicyContent"])
diff --git a/services/organizations/handler.go b/services/organizations/handler.go
index b765d40a32..fac4ae4a82 100644
--- a/services/organizations/handler.go
+++ b/services/organizations/handler.go
@@ -1,7 +1,7 @@
package organizations
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -139,7 +140,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
var data map[string]any
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
diff --git a/services/organizations/handler_accounts.go b/services/organizations/handler_accounts.go
index 73e71913c1..f69d557859 100644
--- a/services/organizations/handler_accounts.go
+++ b/services/organizations/handler_accounts.go
@@ -1,12 +1,13 @@
package organizations
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -165,7 +166,7 @@ func (h *Handler) dispatchAccount(c *echo.Context, op string, body []byte) (bool
func (h *Handler) handleListAccounts(c *echo.Context, body []byte) error {
var req listAccountsRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
}
@@ -237,7 +238,7 @@ func (h *Handler) validateCreateAccountInput(
func (h *Handler) handleCreateAccount(c *echo.Context, body []byte) error {
var req createAccountRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -258,7 +259,7 @@ func (h *Handler) handleCreateAccount(c *echo.Context, body []byte) error {
func (h *Handler) handleDescribeCreateAccountStatus(c *echo.Context, body []byte) error {
var req describeCreateAccountStatusRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -272,7 +273,7 @@ func (h *Handler) handleDescribeCreateAccountStatus(c *echo.Context, body []byte
func (h *Handler) handleDescribeAccount(c *echo.Context, body []byte) error {
var req describeAccountRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -286,7 +287,7 @@ func (h *Handler) handleDescribeAccount(c *echo.Context, body []byte) error {
func (h *Handler) handleRemoveAccountFromOrganization(c *echo.Context, body []byte) error {
var req removeAccountFromOrganizationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -299,7 +300,7 @@ func (h *Handler) handleRemoveAccountFromOrganization(c *echo.Context, body []by
func (h *Handler) handleMoveAccount(c *echo.Context, body []byte) error {
var req moveAccountRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -312,7 +313,7 @@ func (h *Handler) handleMoveAccount(c *echo.Context, body []byte) error {
func (h *Handler) handleCloseAccount(c *echo.Context, body []byte) error {
var req closeAccountRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -329,7 +330,7 @@ func (h *Handler) handleCloseAccount(c *echo.Context, body []byte) error {
func (h *Handler) handleCreateGovCloudAccount(c *echo.Context, body []byte) error {
var req createGovCloudAccountRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -350,7 +351,7 @@ func (h *Handler) handleCreateGovCloudAccount(c *echo.Context, body []byte) erro
func (h *Handler) handleListCreateAccountStatus(c *echo.Context, body []byte) error {
var req listCreateAccountStatusRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -375,7 +376,7 @@ func (h *Handler) handleListCreateAccountStatus(c *echo.Context, body []byte) er
func (h *Handler) handleListAccountsWithInvalidEffectivePolicy(c *echo.Context, body []byte) error {
var req listAccountsWithInvalidEffectivePolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/organizations/handler_accounts_test.go b/services/organizations/handler_accounts_test.go
index d4a078af30..a660e0b34c 100644
--- a/services/organizations/handler_accounts_test.go
+++ b/services/organizations/handler_accounts_test.go
@@ -1,7 +1,8 @@
package organizations_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -96,7 +98,7 @@ func TestCreateAccountStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
status := createResp["CreateAccountStatus"].(map[string]any)
requestID, ok := status["Id"].(string)
require.True(t, ok, "CreateAccountStatus.Id must be present")
@@ -106,7 +108,7 @@ func TestCreateAccountStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &listResp, jsonx.V1()))
statuses, ok := listResp["CreateAccountStatuses"].([]any)
require.True(t, ok)
@@ -208,7 +210,7 @@ func TestCreateAccount_RoleName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
// Describe account.
status := resp["CreateAccountStatus"].(map[string]any)
@@ -218,7 +220,7 @@ func TestCreateAccount_RoleName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&descResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &descResp, jsonx.V1()))
acct := descResp["Account"].(map[string]any)
assert.NotContains(t, acct, "RoleName")
@@ -283,7 +285,7 @@ func TestCreateAccount_IamUserAccessToBilling(t *testing.T) {
if tt.wantStatus == http.StatusOK && tt.wantIamAccess != "" {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
status := resp["CreateAccountStatus"].(map[string]any)
accountID := status["AccountId"].(string)
@@ -292,7 +294,7 @@ func TestCreateAccount_IamUserAccessToBilling(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&descResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &descResp, jsonx.V1()))
acct := descResp["Account"].(map[string]any)
assert.NotContains(t, acct, "IamUserAccessToBilling")
@@ -325,7 +327,7 @@ func TestCloseAccount_ViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
statusObj := createResp["CreateAccountStatus"].(map[string]any)
accountID := statusObj["AccountId"].(string)
@@ -371,7 +373,7 @@ func TestEmailUniqueness_ViaHandler(t *testing.T) {
AccountID string `json:"AccountId"`
} `json:"CreateAccountStatus"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, "FAILED", resp.CreateAccountStatus.State)
assert.Equal(t, "EMAIL_ALREADY_EXISTS", resp.CreateAccountStatus.FailureReason)
assert.Empty(t, resp.CreateAccountStatus.AccountID)
@@ -392,7 +394,7 @@ func TestCreateAccount_NoGovCloudID_ViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
statusObj, ok := resp["CreateAccountStatus"].(map[string]any)
require.True(t, ok, "response must have CreateAccountStatus")
@@ -417,7 +419,7 @@ func TestGovCloudAccount_HasGovCloudID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
statusObj, ok := resp["CreateAccountStatus"].(map[string]any)
require.True(t, ok, "response must have CreateAccountStatus")
@@ -458,7 +460,7 @@ func TestHandler_ListAccounts(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
accounts, ok := resp["Accounts"].([]any)
require.True(t, ok)
assert.NotEmpty(t, accounts)
@@ -531,7 +533,7 @@ func TestHandler_DescribeCreateAccountStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
status := createResp["CreateAccountStatus"].(map[string]any)
statusID := status["Id"].(string)
@@ -599,7 +601,7 @@ func TestHandler_MoveAccount(t *testing.T) {
require.Equal(t, http.StatusOK, ouRec.Code)
var ouResp map[string]any
- require.NoError(t, json.NewDecoder(ouRec.Body).Decode(&ouResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(ouRec.Body), &ouResp, jsonx.V1()))
ou := ouResp["OrganizationalUnit"].(map[string]any)
ouID := ou["Id"].(string)
@@ -658,7 +660,7 @@ func TestHandler_CloseAccount(t *testing.T) {
require.Equal(t, http.StatusOK, acctRec.Code)
var acctResp map[string]any
- require.NoError(t, json.NewDecoder(acctRec.Body).Decode(&acctResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(acctRec.Body), &acctResp, jsonx.V1()))
status := acctResp["CreateAccountStatus"].(map[string]any)
body = map[string]any{"AccountId": status["AccountId"]}
@@ -736,7 +738,7 @@ func TestHandler_CreateGovCloudAccount(t *testing.T) {
if tt.wantGovID {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
status, ok := resp["CreateAccountStatus"].(map[string]any)
require.True(t, ok, "response must have CreateAccountStatus")
diff --git a/services/organizations/handler_delegated_administrators.go b/services/organizations/handler_delegated_administrators.go
index 78559fc5bf..d44847e721 100644
--- a/services/organizations/handler_delegated_administrators.go
+++ b/services/organizations/handler_delegated_administrators.go
@@ -1,11 +1,12 @@
package organizations
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -81,7 +82,7 @@ func (h *Handler) dispatchDelegatedAdmin(c *echo.Context, op string, body []byte
func (h *Handler) handleRegisterDelegatedAdministrator(c *echo.Context, body []byte) error {
var req registerDelegatedAdministratorRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -94,7 +95,7 @@ func (h *Handler) handleRegisterDelegatedAdministrator(c *echo.Context, body []b
func (h *Handler) handleDeregisterDelegatedAdministrator(c *echo.Context, body []byte) error {
var req deregisterDelegatedAdministratorRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -107,7 +108,7 @@ func (h *Handler) handleDeregisterDelegatedAdministrator(c *echo.Context, body [
func (h *Handler) handleListDelegatedAdministrators(c *echo.Context, body []byte) error {
var req listDelegatedAdministratorsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -144,7 +145,7 @@ func (h *Handler) handleListDelegatedAdministrators(c *echo.Context, body []byte
func (h *Handler) handleListDelegatedServicesForAccount(c *echo.Context, body []byte) error {
var req listDelegatedServicesForAccountRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/organizations/handler_effective_policy.go b/services/organizations/handler_effective_policy.go
index a70841fb8e..235a5db36d 100644
--- a/services/organizations/handler_effective_policy.go
+++ b/services/organizations/handler_effective_policy.go
@@ -1,11 +1,12 @@
package organizations
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -62,7 +63,7 @@ func (h *Handler) dispatchEffectivePolicy(c *echo.Context, op string, body []byt
func (h *Handler) handleDescribeEffectivePolicy(c *echo.Context, body []byte) error {
var req describeEffectivePolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -87,7 +88,7 @@ func (h *Handler) handleDescribeEffectivePolicy(c *echo.Context, body []byte) er
func (h *Handler) handleListEffectivePolicyValidationErrors(c *echo.Context, body []byte) error {
var req listEffectivePolicyValidationErrorsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/organizations/handler_handshakes.go b/services/organizations/handler_handshakes.go
index 16e5f71102..459feee8d7 100644
--- a/services/organizations/handler_handshakes.go
+++ b/services/organizations/handler_handshakes.go
@@ -1,13 +1,14 @@
package organizations
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -274,7 +275,7 @@ func (h *Handler) dispatchTransferOps(c *echo.Context, op string, body []byte) (
func (h *Handler) handleAcceptHandshake(c *echo.Context, body []byte) error {
var req acceptHandshakeRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -292,7 +293,7 @@ func (h *Handler) handleAcceptHandshake(c *echo.Context, body []byte) error {
func (h *Handler) handleCancelHandshake(c *echo.Context, body []byte) error {
var req cancelHandshakeRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -310,7 +311,7 @@ func (h *Handler) handleCancelHandshake(c *echo.Context, body []byte) error {
func (h *Handler) handleDeclineHandshake(c *echo.Context, body []byte) error {
var req declineHandshakeRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -328,7 +329,7 @@ func (h *Handler) handleDeclineHandshake(c *echo.Context, body []byte) error {
func (h *Handler) handleDescribeHandshake(c *echo.Context, body []byte) error {
var req describeHandshakeRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -346,7 +347,7 @@ func (h *Handler) handleDescribeHandshake(c *echo.Context, body []byte) error {
func (h *Handler) handleDescribeResponsibilityTransfer(c *echo.Context, body []byte) error {
var req describeResponsibilityTransferRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -376,7 +377,7 @@ func (h *Handler) handleEnableAllFeatures(c *echo.Context, _ []byte) error {
func (h *Handler) handleInviteAccountToOrganization(c *echo.Context, body []byte) error {
var req inviteAccountToOrganizationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -398,7 +399,7 @@ func (h *Handler) handleInviteAccountToOrganization(c *echo.Context, body []byte
func (h *Handler) handleInviteOrganizationToTransferResponsibility(c *echo.Context, body []byte) error {
var req inviteOrganizationToTransferResponsibilityRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -449,7 +450,7 @@ func (h *Handler) handleLeaveOrganization(c *echo.Context, _ []byte) error {
func (h *Handler) handleListHandshakesForAccount(c *echo.Context, body []byte) error {
var req listHandshakesFilterRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
}
@@ -486,7 +487,7 @@ func (h *Handler) handleListHandshakesForAccount(c *echo.Context, body []byte) e
func (h *Handler) handleListHandshakesForOrganization(c *echo.Context, body []byte) error {
var req listHandshakesFilterRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
}
@@ -522,7 +523,7 @@ func (h *Handler) handleListHandshakesForOrganization(c *echo.Context, body []by
func (h *Handler) handleListInboundResponsibilityTransfers(c *echo.Context, body []byte) error {
var req listInboundResponsibilityTransfersRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
}
@@ -556,7 +557,7 @@ func (h *Handler) handleListInboundResponsibilityTransfers(c *echo.Context, body
func (h *Handler) handleListOutboundResponsibilityTransfers(c *echo.Context, body []byte) error {
var req listOutboundResponsibilityTransfersRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
}
@@ -589,7 +590,7 @@ func (h *Handler) handleListOutboundResponsibilityTransfers(c *echo.Context, bod
func (h *Handler) handleTerminateResponsibilityTransfer(c *echo.Context, body []byte) error {
var req terminateResponsibilityTransferRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -616,7 +617,7 @@ func (h *Handler) handleTerminateResponsibilityTransfer(c *echo.Context, body []
func (h *Handler) handleUpdateResponsibilityTransfer(c *echo.Context, body []byte) error {
var req updateResponsibilityTransferRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/organizations/handler_handshakes_test.go b/services/organizations/handler_handshakes_test.go
index 40c7e46a7c..41ddf05b15 100644
--- a/services/organizations/handler_handshakes_test.go
+++ b/services/organizations/handler_handshakes_test.go
@@ -2,7 +2,8 @@ package organizations_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -33,7 +35,7 @@ func TestHandshakeOps(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var inviteResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&inviteResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &inviteResp, jsonx.V1()))
invited := inviteResp["Handshake"].(map[string]any)
handshakeID, ok := invited["Id"].(string)
require.True(t, ok, "InviteAccountToOrganization must return a Handshake.Id")
@@ -55,7 +57,7 @@ func handshakeListContains(t *testing.T, rec *httptest.ResponseRecorder, handsha
t.Helper()
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
handshakes, ok := resp["Handshakes"].([]any)
require.True(t, ok)
@@ -110,7 +112,7 @@ func TestEnableAllFeatures(t *testing.T) {
if tt.wantAction != "" {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
hs, ok := resp["Handshake"].(map[string]any)
require.True(t, ok, "response must contain Handshake")
assert.Equal(t, tt.wantAction, hs["Action"])
@@ -193,7 +195,7 @@ func TestHandshakeFilter_Handler(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
handshakes := resp["Handshakes"].([]any)
assert.Len(t, handshakes, tt.wantCount)
@@ -333,7 +335,7 @@ func TestHandshakeExpiration_ViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
handshake := resp["Handshake"].(map[string]any)
assert.Equal(t, "EXPIRED", handshake["State"].(string))
}
@@ -355,7 +357,7 @@ func TestInviteAccount_DuplicateOpen_ViaHandler(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, r2.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(r2.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(r2.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "DuplicateHandshakeException", errResp["__type"])
}
@@ -513,7 +515,7 @@ func TestHandler_HandshakeLifecycle(t *testing.T) {
if tt.wantState != "" {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
hs, ok := resp["Handshake"].(map[string]any)
require.True(t, ok, "response must have Handshake field")
@@ -582,7 +584,7 @@ func TestHandler_DescribeResponsibilityTransfer(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
details, ok := resp["ResponsibilityTransfer"].(map[string]any)
require.True(t, ok, "response must have ResponsibilityTransfer")
assert.Equal(t, tt.transferID, details["Id"])
diff --git a/services/organizations/handler_organization.go b/services/organizations/handler_organization.go
index e9a0b9d037..4df466400a 100644
--- a/services/organizations/handler_organization.go
+++ b/services/organizations/handler_organization.go
@@ -1,10 +1,12 @@
package organizations
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createOrganizationRequest struct {
@@ -49,7 +51,7 @@ func (h *Handler) dispatchOrg(c *echo.Context, op string, body []byte) (bool, er
func (h *Handler) handleCreateOrganization(c *echo.Context, body []byte) error {
var req createOrganizationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/organizations/handler_organizational_units.go b/services/organizations/handler_organizational_units.go
index 8782b8aeb2..d2f071e1eb 100644
--- a/services/organizations/handler_organizational_units.go
+++ b/services/organizations/handler_organizational_units.go
@@ -1,11 +1,12 @@
package organizations
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -122,7 +123,7 @@ func (h *Handler) dispatchOU(c *echo.Context, op string, body []byte) (bool, err
func (h *Handler) handleCreateOrganizationalUnit(c *echo.Context, body []byte) error {
var req createOrganizationalUnitRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -140,7 +141,7 @@ func (h *Handler) handleCreateOrganizationalUnit(c *echo.Context, body []byte) e
func (h *Handler) handleDescribeOrganizationalUnit(c *echo.Context, body []byte) error {
var req describeOrganizationalUnitRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -154,7 +155,7 @@ func (h *Handler) handleDescribeOrganizationalUnit(c *echo.Context, body []byte)
func (h *Handler) handleDeleteOrganizationalUnit(c *echo.Context, body []byte) error {
var req deleteOrganizationalUnitRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -167,7 +168,7 @@ func (h *Handler) handleDeleteOrganizationalUnit(c *echo.Context, body []byte) e
func (h *Handler) handleUpdateOrganizationalUnit(c *echo.Context, body []byte) error {
var req updateOrganizationalUnitRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -181,7 +182,7 @@ func (h *Handler) handleUpdateOrganizationalUnit(c *echo.Context, body []byte) e
func (h *Handler) handleListOrganizationalUnitsForParent(c *echo.Context, body []byte) error {
var req listOrganizationalUnitsForParentRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -202,7 +203,7 @@ func (h *Handler) handleListOrganizationalUnitsForParent(c *echo.Context, body [
func (h *Handler) handleListAccountsForParent(c *echo.Context, body []byte) error {
var req listAccountsForParentRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -223,7 +224,7 @@ func (h *Handler) handleListAccountsForParent(c *echo.Context, body []byte) erro
func (h *Handler) handleListParents(c *echo.Context, body []byte) error {
var req listParentsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -243,7 +244,7 @@ func (h *Handler) handleListParents(c *echo.Context, body []byte) error {
func (h *Handler) handleListChildren(c *echo.Context, body []byte) error {
var req listChildrenRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/organizations/handler_organizational_units_test.go b/services/organizations/handler_organizational_units_test.go
index f83119a1d4..1e36a88037 100644
--- a/services/organizations/handler_organizational_units_test.go
+++ b/services/organizations/handler_organizational_units_test.go
@@ -1,13 +1,16 @@
package organizations_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_CreateOrganizationalUnit tests the HTTP handler for CreateOrganizationalUnit.
@@ -38,7 +41,7 @@ func TestHandler_CreateOrganizationalUnit(t *testing.T) {
require.Equal(t, http.StatusOK, rootsRec.Code)
var rootsResp map[string]any
- require.NoError(t, json.NewDecoder(rootsRec.Body).Decode(&rootsResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rootsRec.Body), &rootsResp, jsonx.V1()))
roots := rootsResp["Roots"].([]any)
rootID := roots[0].(map[string]any)["Id"].(string)
@@ -50,7 +53,7 @@ func TestHandler_CreateOrganizationalUnit(t *testing.T) {
if tt.wantOU {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
ou, ok := resp["OrganizationalUnit"].(map[string]any)
require.True(t, ok, "response should contain OrganizationalUnit")
assert.NotEmpty(t, ou["Id"])
@@ -90,7 +93,7 @@ func TestHandler_ListParents(t *testing.T) {
require.Equal(t, http.StatusOK, acctRec.Code)
var acctResp map[string]any
- require.NoError(t, json.NewDecoder(acctRec.Body).Decode(&acctResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(acctRec.Body), &acctResp, jsonx.V1()))
status := acctResp["CreateAccountStatus"].(map[string]any)
accountID := status["AccountId"].(string)
@@ -99,7 +102,7 @@ func TestHandler_ListParents(t *testing.T) {
if tt.wantParents {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
parents, ok := resp["Parents"].([]any)
require.True(t, ok, "response should contain Parents")
assert.NotEmpty(t, parents)
@@ -164,7 +167,7 @@ func TestOUDepth_ViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var rootsResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&rootsResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &rootsResp, jsonx.V1()))
roots := rootsResp["Roots"].([]any)
rootID := roots[0].(map[string]any)["Id"].(string)
@@ -179,7 +182,7 @@ func TestOUDepth_ViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "depth %d should succeed", d)
var ouResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&ouResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &ouResp, jsonx.V1()))
parentID = ouResp["OrganizationalUnit"].(map[string]any)["Id"].(string)
}
@@ -205,7 +208,7 @@ func TestOUNameUniqueness_ViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var rootsResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&rootsResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &rootsResp, jsonx.V1()))
roots := rootsResp["Roots"].([]any)
rootID := roots[0].(map[string]any)["Id"].(string)
@@ -258,7 +261,7 @@ func TestHandler_DescribeOrganizationalUnit(t *testing.T) {
require.Equal(t, http.StatusOK, ouRec.Code)
var ouResp map[string]any
- require.NoError(t, json.NewDecoder(ouRec.Body).Decode(&ouResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(ouRec.Body), &ouResp, jsonx.V1()))
ou := ouResp["OrganizationalUnit"].(map[string]any)
ouID := ou["Id"].(string)
@@ -295,7 +298,7 @@ func TestHandler_DeleteOrganizationalUnit(t *testing.T) {
require.Equal(t, http.StatusOK, ouRec.Code)
var ouResp map[string]any
- require.NoError(t, json.NewDecoder(ouRec.Body).Decode(&ouResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(ouRec.Body), &ouResp, jsonx.V1()))
ou := ouResp["OrganizationalUnit"].(map[string]any)
ouID := ou["Id"].(string)
@@ -332,7 +335,7 @@ func TestHandler_UpdateOrganizationalUnit(t *testing.T) {
require.Equal(t, http.StatusOK, ouRec.Code)
var ouResp map[string]any
- require.NoError(t, json.NewDecoder(ouRec.Body).Decode(&ouResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(ouRec.Body), &ouResp, jsonx.V1()))
ou := ouResp["OrganizationalUnit"].(map[string]any)
ouID := ou["Id"].(string)
diff --git a/services/organizations/handler_pagination_test.go b/services/organizations/handler_pagination_test.go
index b6d51956ae..8a29247858 100644
--- a/services/organizations/handler_pagination_test.go
+++ b/services/organizations/handler_pagination_test.go
@@ -2,7 +2,8 @@ package organizations_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -11,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -73,7 +75,7 @@ func TestHandler_Pagination(t *testing.T) {
if tt.nextToken != "" {
reqBody["NextToken"] = tt.nextToken
}
- bodyBytes, _ := json.Marshal(reqBody)
+ bodyBytes, _ := json.Marshal(reqBody, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
req.Header.Set("X-Amz-Target", "AWSOrganizationsV20161128.ListAccounts")
@@ -94,7 +96,7 @@ func TestHandler_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var res map[string]any
- err = json.Unmarshal(rec.Body.Bytes(), &res)
+ err = json.Unmarshal(rec.Body.Bytes(), &res, jsonx.V1())
require.NoError(t, err)
accounts, ok := res["Accounts"].([]any)
@@ -139,7 +141,7 @@ func TestListOps_HonorMaxResultsAndNextToken(t *testing.T) {
NextToken string `json:"NextToken"`
Accounts []map[string]any `json:"Accounts"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
require.Len(t, resp.Accounts, 2, "MaxResults=2 must truncate to 2 items")
require.NotEmpty(t, resp.NextToken, "truncated page must carry a NextToken")
@@ -155,7 +157,7 @@ func TestListOps_HonorMaxResultsAndNextToken(t *testing.T) {
NextToken string `json:"NextToken"`
Accounts []map[string]any `json:"Accounts"`
}
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&resp2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &resp2, jsonx.V1()))
require.Len(t, resp2.Accounts, 2, "second page must continue from the token")
})
@@ -180,7 +182,7 @@ func TestListOps_HonorMaxResultsAndNextToken(t *testing.T) {
} `json:"PolicySummary"`
} `json:"Policy"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&pResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &pResp, jsonx.V1()))
rec = doRequest(t, h, "AttachPolicy", map[string]any{
"PolicyId": pResp.Policy.PolicySummary.ID,
@@ -200,7 +202,7 @@ func TestListOps_HonorMaxResultsAndNextToken(t *testing.T) {
NextToken string `json:"NextToken"`
Policies []map[string]any `json:"Policies"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
require.Len(t, resp.Policies, 1)
require.NotEmpty(t, resp.NextToken)
})
@@ -221,7 +223,7 @@ func TestListOps_HonorMaxResultsAndNextToken(t *testing.T) {
ID string `json:"Id"`
} `json:"OrganizationalUnit"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&ouResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &ouResp, jsonx.V1()))
rec = doRequest(t, h, "CreatePolicy", map[string]any{
"Name": "p",
@@ -238,7 +240,7 @@ func TestListOps_HonorMaxResultsAndNextToken(t *testing.T) {
} `json:"PolicySummary"`
} `json:"Policy"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&pResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &pResp, jsonx.V1()))
policyID := pResp.Policy.PolicySummary.ID
for _, targetID := range []string{rootID, ouResp.OrganizationalUnit.ID} {
@@ -259,7 +261,7 @@ func TestListOps_HonorMaxResultsAndNextToken(t *testing.T) {
NextToken string `json:"NextToken"`
Targets []map[string]any `json:"Targets"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
require.Len(t, resp.Targets, 1)
require.NotEmpty(t, resp.NextToken)
})
@@ -284,7 +286,7 @@ func TestListOps_HonorMaxResultsAndNextToken(t *testing.T) {
AccountID string `json:"AccountId"`
} `json:"CreateAccountStatus"`
}
- require.NoError(t, json.NewDecoder(caRec.Body).Decode(&caResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(caRec.Body), &caResp, jsonx.V1()))
rec = doRequest(t, h, "RegisterDelegatedAdministrator", map[string]any{
"AccountId": caResp.CreateAccountStatus.AccountID,
@@ -302,7 +304,7 @@ func TestListOps_HonorMaxResultsAndNextToken(t *testing.T) {
NextToken string `json:"NextToken"`
DelegatedAdministrators []map[string]any `json:"DelegatedAdministrators"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
require.Len(t, resp.DelegatedAdministrators, 1)
require.NotEmpty(t, resp.NextToken)
})
@@ -329,7 +331,7 @@ func TestListOps_HonorMaxResultsAndNextToken(t *testing.T) {
NextToken string `json:"NextToken"`
CreateAccountStatuses []map[string]any `json:"CreateAccountStatuses"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
require.Len(t, resp.CreateAccountStatuses, 2)
require.NotEmpty(t, resp.NextToken)
})
@@ -359,7 +361,7 @@ func TestListOps_HonorMaxResultsAndNextToken(t *testing.T) {
NextToken string `json:"NextToken"`
Tags []map[string]any `json:"Tags"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
require.Len(t, resp.Tags, 3, "all 3 tags fit under the default page size")
require.Empty(t, resp.NextToken)
})
@@ -383,7 +385,7 @@ func TestListOps_HonorMaxResultsAndNextToken(t *testing.T) {
NextToken string `json:"NextToken"`
EnabledServicePrincipals []map[string]any `json:"EnabledServicePrincipals"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
require.Len(t, resp.EnabledServicePrincipals, 1)
require.NotEmpty(t, resp.NextToken)
@@ -409,7 +411,7 @@ func TestListOps_HonorMaxResultsAndNextToken(t *testing.T) {
AccountID string `json:"AccountId"`
} `json:"CreateAccountStatus"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&caResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &caResp, jsonx.V1()))
accountID := caResp.CreateAccountStatus.AccountID
for _, sp := range []string{"config.amazonaws.com", "cloudtrail.amazonaws.com"} {
@@ -433,7 +435,7 @@ func TestListOps_HonorMaxResultsAndNextToken(t *testing.T) {
NextToken string `json:"NextToken"`
DelegatedServices []map[string]any `json:"DelegatedServices"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
require.Len(t, resp.DelegatedServices, 1)
require.NotEmpty(t, resp.NextToken)
})
diff --git a/services/organizations/handler_policies.go b/services/organizations/handler_policies.go
index dd472d9a15..1ed3c8670d 100644
--- a/services/organizations/handler_policies.go
+++ b/services/organizations/handler_policies.go
@@ -1,11 +1,12 @@
package organizations
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -115,7 +116,7 @@ func (h *Handler) dispatchPolicy(c *echo.Context, op string, body []byte) (bool,
func (h *Handler) handleCreatePolicy(c *echo.Context, body []byte) error {
var req createPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -133,7 +134,7 @@ func (h *Handler) handleCreatePolicy(c *echo.Context, body []byte) error {
func (h *Handler) handleDescribePolicy(c *echo.Context, body []byte) error {
var req describePolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -147,7 +148,7 @@ func (h *Handler) handleDescribePolicy(c *echo.Context, body []byte) error {
func (h *Handler) handleUpdatePolicy(c *echo.Context, body []byte) error {
var req updatePolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -161,7 +162,7 @@ func (h *Handler) handleUpdatePolicy(c *echo.Context, body []byte) error {
func (h *Handler) handleDeletePolicy(c *echo.Context, body []byte) error {
var req deletePolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -174,7 +175,7 @@ func (h *Handler) handleDeletePolicy(c *echo.Context, body []byte) error {
func (h *Handler) handleListPolicies(c *echo.Context, body []byte) error {
var req listPoliciesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -195,7 +196,7 @@ func (h *Handler) handleListPolicies(c *echo.Context, body []byte) error {
func (h *Handler) handleEnablePolicyType(c *echo.Context, body []byte) error {
var req enablePolicyTypeRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -209,7 +210,7 @@ func (h *Handler) handleEnablePolicyType(c *echo.Context, body []byte) error {
func (h *Handler) handleDisablePolicyType(c *echo.Context, body []byte) error {
var req disablePolicyTypeRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/organizations/handler_policy_attachments.go b/services/organizations/handler_policy_attachments.go
index 53cab343c2..5801f0be49 100644
--- a/services/organizations/handler_policy_attachments.go
+++ b/services/organizations/handler_policy_attachments.go
@@ -1,11 +1,12 @@
package organizations
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -67,7 +68,7 @@ func (h *Handler) dispatchPolicyAttachments(c *echo.Context, op string, body []b
func (h *Handler) handleAttachPolicy(c *echo.Context, body []byte) error {
var req attachPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -80,7 +81,7 @@ func (h *Handler) handleAttachPolicy(c *echo.Context, body []byte) error {
func (h *Handler) handleDetachPolicy(c *echo.Context, body []byte) error {
var req detachPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -93,7 +94,7 @@ func (h *Handler) handleDetachPolicy(c *echo.Context, body []byte) error {
func (h *Handler) handleListPoliciesForTarget(c *echo.Context, body []byte) error {
var req listPoliciesForTargetRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -114,7 +115,7 @@ func (h *Handler) handleListPoliciesForTarget(c *echo.Context, body []byte) erro
func (h *Handler) handleListTargetsForPolicy(c *echo.Context, body []byte) error {
var req listTargetsForPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/organizations/handler_resource_policy.go b/services/organizations/handler_resource_policy.go
index 964e5a3ba3..8b213511b8 100644
--- a/services/organizations/handler_resource_policy.go
+++ b/services/organizations/handler_resource_policy.go
@@ -1,10 +1,12 @@
package organizations
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type resourcePolicySummaryObject struct {
@@ -70,7 +72,7 @@ func (h *Handler) handlePutResourcePolicy(c *echo.Context, body []byte) error {
Tags []Tag `json:"Tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/organizations/handler_roots.go b/services/organizations/handler_roots.go
index 2d42c4f4ff..abf114b21a 100644
--- a/services/organizations/handler_roots.go
+++ b/services/organizations/handler_roots.go
@@ -1,11 +1,12 @@
package organizations
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -43,7 +44,7 @@ type listRootsRequest struct {
func (h *Handler) handleListRoots(c *echo.Context, body []byte) error {
var req listRootsRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
}
diff --git a/services/organizations/handler_service_access.go b/services/organizations/handler_service_access.go
index 7f090ac437..f755a41ac2 100644
--- a/services/organizations/handler_service_access.go
+++ b/services/organizations/handler_service_access.go
@@ -1,11 +1,12 @@
package organizations
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -52,7 +53,7 @@ func (h *Handler) dispatchServiceAccess(c *echo.Context, op string, body []byte)
func (h *Handler) handleEnableAWSServiceAccess(c *echo.Context, body []byte) error {
var req enableAWSServiceAccessRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -65,7 +66,7 @@ func (h *Handler) handleEnableAWSServiceAccess(c *echo.Context, body []byte) err
func (h *Handler) handleDisableAWSServiceAccess(c *echo.Context, body []byte) error {
var req disableAWSServiceAccessRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -79,7 +80,7 @@ func (h *Handler) handleDisableAWSServiceAccess(c *echo.Context, body []byte) er
func (h *Handler) handleListAWSServiceAccessForOrganization(c *echo.Context, body []byte) error {
var req listAWSServiceAccessRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
}
diff --git a/services/organizations/handler_tags.go b/services/organizations/handler_tags.go
index b417eec0ed..c6776b0736 100644
--- a/services/organizations/handler_tags.go
+++ b/services/organizations/handler_tags.go
@@ -1,11 +1,12 @@
package organizations
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -49,7 +50,7 @@ func (h *Handler) dispatchTags(c *echo.Context, op string, body []byte) (bool, e
func (h *Handler) handleTagResource(c *echo.Context, body []byte) error {
var req tagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -62,7 +63,7 @@ func (h *Handler) handleTagResource(c *echo.Context, body []byte) error {
func (h *Handler) handleUntagResource(c *echo.Context, body []byte) error {
var req untagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
@@ -75,7 +76,7 @@ func (h *Handler) handleUntagResource(c *echo.Context, body []byte) error {
func (h *Handler) handleListTagsForResource(c *echo.Context, body []byte) error {
var req listTagsForResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return h.writeError(c, http.StatusBadRequest, "SerializationException", "invalid request body")
}
diff --git a/services/organizations/handler_test.go b/services/organizations/handler_test.go
index 3b3559145b..ba36cb0908 100644
--- a/services/organizations/handler_test.go
+++ b/services/organizations/handler_test.go
@@ -2,7 +2,8 @@ package organizations_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -38,7 +40,7 @@ func doRequest(t *testing.T, h *organizations.Handler, op string, body any) *htt
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -81,7 +83,7 @@ func newHandlerWithOrg(t *testing.T) (*organizations.Handler, string) {
require.Equal(t, http.StatusOK, rec.Code)
var rootsResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&rootsResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &rootsResp, jsonx.V1()))
roots := rootsResp["Roots"].([]any)
rootID := roots[0].(map[string]any)["Id"].(string)
@@ -99,7 +101,7 @@ func createAccountViaHandler(t *testing.T, h *organizations.Handler, name, email
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
status := resp["CreateAccountStatus"].(map[string]any)
return status["AccountId"].(string)
@@ -118,7 +120,7 @@ func createPolicyViaHandler(t *testing.T, h *organizations.Handler, name string)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
policy := resp["Policy"].(map[string]any)
summary := policy["PolicySummary"].(map[string]any)
diff --git a/services/organizations/organization_test.go b/services/organizations/organization_test.go
index e87134f1ae..eeddca40df 100644
--- a/services/organizations/organization_test.go
+++ b/services/organizations/organization_test.go
@@ -1,13 +1,15 @@
package organizations_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -122,7 +124,7 @@ func TestHandler_CreateOrganization(t *testing.T) {
if tt.wantOrgID {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
org, ok := resp["Organization"].(map[string]any)
require.True(t, ok, "response should contain Organization")
assert.NotEmpty(t, org["Id"])
@@ -326,7 +328,7 @@ func TestAvailablePolicyTypes_ViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
org := resp["Organization"].(map[string]any)
pts, ok := org["AvailablePolicyTypes"].([]any)
@@ -549,7 +551,7 @@ func TestDeleteOrganization_WithMembers_ViaHandler(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "OrganizationNotEmptyException", errResp["__type"])
}
diff --git a/services/organizations/pagination_sort_totality_test.go b/services/organizations/pagination_sort_totality_test.go
index 3156b5640a..2aa46eb3ac 100644
--- a/services/organizations/pagination_sort_totality_test.go
+++ b/services/organizations/pagination_sort_totality_test.go
@@ -1,12 +1,13 @@
package organizations_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -94,7 +95,7 @@ func TestListPoliciesSortIsTotal(t *testing.T) {
ID string `json:"Id"`
} `json:"Policies"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ids := make([]string, len(resp.Policies))
for i, p := range resp.Policies {
diff --git a/services/organizations/paths_test.go b/services/organizations/paths_test.go
index c1054c9425..a4f0a7191c 100644
--- a/services/organizations/paths_test.go
+++ b/services/organizations/paths_test.go
@@ -1,7 +1,7 @@
package organizations_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -189,10 +190,10 @@ func TestPathsDetachedOrCyclic(t *testing.T) {
require.NotNil(t, data)
snap := organizations.NewBackendSnapshot()
- require.NoError(t, json.Unmarshal(data, snap))
+ require.NoError(t, json.Unmarshal(data, snap, jsonx.V1()))
snap.AccountParent[acctID] = "ou-doesnotexist-00000000"
- corrupted, err := json.Marshal(snap)
+ corrupted, err := json.Marshal(snap, jsonx.V1())
require.NoError(t, err)
fresh := organizations.NewInMemoryBackend("000000000000", "us-east-1")
@@ -219,10 +220,10 @@ func TestPathsDetachedOrCyclic(t *testing.T) {
require.NotNil(t, data)
snap := organizations.NewBackendSnapshot()
- require.NoError(t, json.Unmarshal(data, snap))
+ require.NoError(t, json.Unmarshal(data, snap, jsonx.V1()))
var ous []organizations.OrganizationalUnit
- require.NoError(t, json.Unmarshal(snap.Tables["ous"], &ous))
+ require.NoError(t, json.Unmarshal(snap.Tables["ous"], &ous, jsonx.V1()))
for i := range ous {
switch ous[i].ID {
@@ -233,14 +234,14 @@ func TestPathsDetachedOrCyclic(t *testing.T) {
}
}
- rawOUs, err := json.Marshal(ous)
+ rawOUs, err := json.Marshal(ous, jsonx.V1())
require.NoError(t, err)
snap.Tables["ous"] = rawOUs
snap.OUParent[ouA.ID] = ouB.ID
snap.OUParent[ouB.ID] = ouA.ID
- corrupted, err := json.Marshal(snap)
+ corrupted, err := json.Marshal(snap, jsonx.V1())
require.NoError(t, err)
fresh := organizations.NewInMemoryBackend("000000000000", "us-east-1")
diff --git a/services/organizations/persistence.go b/services/organizations/persistence.go
index f55a4e9068..bed68e487c 100644
--- a/services/organizations/persistence.go
+++ b/services/organizations/persistence.go
@@ -2,11 +2,13 @@ package organizations
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -67,7 +69,7 @@ func newDirtyDTORegistry() (*store.Registry, *store.Table[delegatedAdminSnapshot
// preserves that existing gap rather than fixing it as a side effect of the
// datalayer swap.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Org *Organization `json:"org"`
Root *Root `json:"root"`
ResourcePolicy *ResourcePolicy `json:"resourcePolicy,omitempty"`
@@ -159,7 +161,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
StatusCounter: b.statusCounter,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "organizations: failed to marshal snapshot", "error", err)
@@ -229,7 +231,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreDelegatedAdminsLocked restores the "dirty" delegatedAdmins table
// from tables via the ephemeral DTO registry, converting each DTO back to
// its live *DelegatedAdmin. The caller MUST hold b.mu for writing.
-func (b *InMemoryBackend) restoreDelegatedAdminsLocked(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDelegatedAdminsLocked(tables map[string]jsontext.Value) error {
dtoReg, daDTOs := newDirtyDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
diff --git a/services/organizations/persistence_test.go b/services/organizations/persistence_test.go
index d167e9c099..7db4d5f1b3 100644
--- a/services/organizations/persistence_test.go
+++ b/services/organizations/persistence_test.go
@@ -1,12 +1,13 @@
package organizations_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -245,7 +246,7 @@ func TestPersistence_EnsureNonNilMaps(t *testing.T) {
// After EnsureNonNilMaps all maps in the snapshot should be non-nil.
// We can verify via round-trip: marshal+unmarshal should not panic.
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
require.NoError(t, err)
assert.NotNil(t, data, tt.name)
})
diff --git a/services/organizations/policies.go b/services/organizations/policies.go
index eb307ce6f2..6fa76f43f7 100644
--- a/services/organizations/policies.go
+++ b/services/organizations/policies.go
@@ -2,8 +2,10 @@ package organizations
import (
"cmp"
- "encoding/json"
+ "encoding/json/jsontext"
"slices"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const policyStatusEnabled = "ENABLED"
@@ -103,7 +105,7 @@ func policyContentMaxSize(policyType string) (int, bool) {
// MalformedPolicyDocumentException and documents exceeding the per-type size
// quota with ConstraintViolationException(POLICY_CONTENT_LIMIT_EXCEEDED).
func validatePolicyContent(content, policyType string) error {
- if !json.Valid([]byte(content)) {
+ if !jsontext.Value([]byte(content)).IsValid(jsonx.V1()) {
return ErrMalformedPolicyDocument
}
diff --git a/services/organizations/policies_test.go b/services/organizations/policies_test.go
index 4d6b301258..73b0136636 100644
--- a/services/organizations/policies_test.go
+++ b/services/organizations/policies_test.go
@@ -1,7 +1,8 @@
package organizations_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"regexp"
"strings"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -91,7 +93,7 @@ func TestHandler_EnablePolicyType(t *testing.T) {
rootsRec := doRequest(t, h, "ListRoots", map[string]any{})
var rootsResp map[string]any
- require.NoError(t, json.NewDecoder(rootsRec.Body).Decode(&rootsResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rootsRec.Body), &rootsResp, jsonx.V1()))
roots := rootsResp["Roots"].([]any)
rootID := roots[0].(map[string]any)["Id"].(string)
@@ -545,7 +547,7 @@ func TestDeletePolicy_Attached_ViaHandler(t *testing.T) {
roots := doRequest(t, h, "ListRoots", nil)
var rootsResp map[string]any
- require.NoError(t, json.Unmarshal(roots.Body.Bytes(), &rootsResp))
+ require.NoError(t, json.Unmarshal(roots.Body.Bytes(), &rootsResp, jsonx.V1()))
rootID := rootsResp["Roots"].([]any)[0].(map[string]any)["Id"].(string)
doRequest(t, h, "EnablePolicyType", map[string]any{"RootId": rootID, "PolicyType": "SERVICE_CONTROL_POLICY"})
@@ -559,7 +561,7 @@ func TestDeletePolicy_Attached_ViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, cp.Code)
var cpResp map[string]any
- require.NoError(t, json.Unmarshal(cp.Body.Bytes(), &cpResp))
+ require.NoError(t, json.Unmarshal(cp.Body.Bytes(), &cpResp, jsonx.V1()))
policyID := cpResp["Policy"].(map[string]any)["PolicySummary"].(map[string]any)["Id"].(string)
doRequest(t, h, "AttachPolicy", map[string]any{"PolicyId": policyID, "TargetId": rootID})
@@ -568,7 +570,7 @@ func TestDeletePolicy_Attached_ViaHandler(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "PolicyInUseException", errResp["__type"])
}
@@ -689,7 +691,7 @@ func TestDisablePolicyType_WithAttached_ViaHandler(t *testing.T) {
roots := doRequest(t, h, "ListRoots", nil)
var rootsResp map[string]any
- require.NoError(t, json.Unmarshal(roots.Body.Bytes(), &rootsResp))
+ require.NoError(t, json.Unmarshal(roots.Body.Bytes(), &rootsResp, jsonx.V1()))
rootID := rootsResp["Roots"].([]any)[0].(map[string]any)["Id"].(string)
doRequest(t, h, "EnablePolicyType", map[string]any{"RootId": rootID, "PolicyType": "TAG_POLICY"})
@@ -703,7 +705,7 @@ func TestDisablePolicyType_WithAttached_ViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, cp.Code)
var cpResp map[string]any
- require.NoError(t, json.Unmarshal(cp.Body.Bytes(), &cpResp))
+ require.NoError(t, json.Unmarshal(cp.Body.Bytes(), &cpResp, jsonx.V1()))
policyID := cpResp["Policy"].(map[string]any)["PolicySummary"].(map[string]any)["Id"].(string)
doRequest(t, h, "AttachPolicy", map[string]any{"PolicyId": policyID, "TargetId": rootID})
@@ -712,7 +714,7 @@ func TestDisablePolicyType_WithAttached_ViaHandler(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ConstraintViolationException", errResp["__type"])
}
@@ -989,7 +991,7 @@ func TestCreatePolicy_MalformedContent_ViaHandler(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "MalformedPolicyDocumentException", errResp["__type"])
}
diff --git a/services/organizations/policy_attachments_test.go b/services/organizations/policy_attachments_test.go
index 1136def74e..c1390403a0 100644
--- a/services/organizations/policy_attachments_test.go
+++ b/services/organizations/policy_attachments_test.go
@@ -1,7 +1,8 @@
package organizations_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -418,7 +420,7 @@ func TestAttachPolicy_UnknownTarget_ViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var polResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&polResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &polResp, jsonx.V1()))
policyID := polResp["Policy"].(map[string]any)["PolicySummary"].(map[string]any)["Id"].(string)
// Attempt to attach to a non-existent target.
diff --git a/services/organizations/resource_policy_test.go b/services/organizations/resource_policy_test.go
index 34c9e64670..d6ee82f30b 100644
--- a/services/organizations/resource_policy_test.go
+++ b/services/organizations/resource_policy_test.go
@@ -2,7 +2,8 @@ package organizations_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -254,7 +256,7 @@ func TestHandler_ResourcePolicy(t *testing.T) {
if tt.op == "DescribeResourcePolicy" && tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
rp, ok := resp["ResourcePolicy"].(map[string]any)
require.True(t, ok, "response must have ResourcePolicy")
assert.NotEmpty(t, rp["Content"])
@@ -374,7 +376,7 @@ func TestPutResourcePolicy_Happy(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotNil(t, resp["ResourcePolicy"])
})
}
diff --git a/services/organizations/roots_test.go b/services/organizations/roots_test.go
index b8b9e9c91c..f955d95239 100644
--- a/services/organizations/roots_test.go
+++ b/services/organizations/roots_test.go
@@ -1,12 +1,15 @@
package organizations_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_ListRoots tests the HTTP handler for ListRoots.
@@ -38,7 +41,7 @@ func TestHandler_ListRoots(t *testing.T) {
if tt.wantRoots {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
roots, ok := resp["Roots"].([]any)
require.True(t, ok, "response should contain Roots")
assert.NotEmpty(t, roots)
diff --git a/services/organizations/scp_test.go b/services/organizations/scp_test.go
index 21c0fc92c8..89a3264ab6 100644
--- a/services/organizations/scp_test.go
+++ b/services/organizations/scp_test.go
@@ -2,12 +2,14 @@ package organizations_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/iam"
"github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -124,11 +126,11 @@ func TestRestoreMigratesMissingDefaultSCPs(t *testing.T) {
o := newSCPOrg(t)
- var snap map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(o.b.Snapshot(context.Background()), &snap))
+ var snap map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(o.b.Snapshot(context.Background()), &snap, jsonx.V1()))
oldShape := func(key string, v any) {
- raw, err := json.Marshal(v)
+ raw, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
snap[key] = raw
@@ -137,7 +139,7 @@ func TestRestoreMigratesMissingDefaultSCPs(t *testing.T) {
oldShape("targetPolicies", map[string][]string{o.rootID: {"p-FullAWSAccess"}})
oldShape("policyTargets", map[string][]string{"p-FullAWSAccess": {o.rootID}})
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
require.NoError(t, err)
restored := organizations.NewInMemoryBackend("000000000000", "us-east-1")
diff --git a/services/organizations/service_access_test.go b/services/organizations/service_access_test.go
index 3e108f3e42..79bc281f8b 100644
--- a/services/organizations/service_access_test.go
+++ b/services/organizations/service_access_test.go
@@ -1,12 +1,15 @@
package organizations_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestBackend_ServiceAccess tests enabling/disabling AWS service access.
@@ -181,7 +184,7 @@ func TestServiceAccess_Handler(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &resp, jsonx.V1()))
principals := resp["EnabledServicePrincipals"].([]any)
assert.Empty(t, principals, "disabled service should not appear")
diff --git a/services/organizations/tags_test.go b/services/organizations/tags_test.go
index 5842a509c8..0a0bd20058 100644
--- a/services/organizations/tags_test.go
+++ b/services/organizations/tags_test.go
@@ -1,7 +1,8 @@
package organizations_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/organizations"
)
@@ -325,7 +327,7 @@ func TestTagOps_NonExistentResource_ViaHandler(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "TargetNotFoundException", errResp["__type"],
"%s on unknown resource must return TargetNotFoundException", tt.name)
})
@@ -357,7 +359,7 @@ func TestHandler_TagOperations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&descResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &descResp, jsonx.V1()))
org := descResp["Organization"].(map[string]any)
orgID := org["Id"].(string)
@@ -373,7 +375,7 @@ func TestHandler_TagOperations(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var tagsResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&tagsResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &tagsResp, jsonx.V1()))
tags, ok := tagsResp["Tags"].([]any)
require.True(t, ok)
assert.NotEmpty(t, tags)
@@ -635,7 +637,7 @@ func TestTagResource_ReservedPrefix_ViaHandler(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidInputException", errResp["__type"])
}
diff --git a/services/outposts/capacity_ledger_delete_cleanup_test.go b/services/outposts/capacity_ledger_delete_cleanup_test.go
index bcc2ac96a4..00e689b6c5 100644
--- a/services/outposts/capacity_ledger_delete_cleanup_test.go
+++ b/services/outposts/capacity_ledger_delete_cleanup_test.go
@@ -1,17 +1,20 @@
package outposts_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
outpostssdk "github.com/aws/aws-sdk-go-v2/service/outposts"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// snapshotTables is a local decode of backendSnapshot's exported shape,
// just enough to inspect one table's raw contents in a black-box test.
type snapshotTables struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
}
// TestDeleteOutpost_CleansRunningInstanceLedger guards against a ghost-row
@@ -52,7 +55,7 @@ func TestDeleteOutpost_CleansRunningInstanceLedger(t *testing.T) {
require.NoError(t, err)
var snap snapshotTables
- require.NoError(t, json.Unmarshal(h.Snapshot(t.Context()), &snap))
+ require.NoError(t, json.Unmarshal(h.Snapshot(t.Context()), &snap, jsonx.V1()))
require.NotContains(t, string(snap.Tables["runningInstances"]), leakedInstanceID,
"DeleteOutpost must clean up runningInstances rows for its own Asset(s), not leak them")
diff --git a/services/outposts/handler.go b/services/outposts/handler.go
index fdb1152d8b..907b3ea887 100644
--- a/services/outposts/handler.go
+++ b/services/outposts/handler.go
@@ -2,7 +2,7 @@ package outposts
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -576,7 +577,7 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
status, errType = http.StatusNotFound, "NotFoundException"
}
- payload, _ := json.Marshal(body)
+ payload, _ := json.Marshal(body, jsonx.V1())
c.Response().Header().Set("X-Amzn-Errortype", errType)
@@ -662,7 +663,7 @@ func decodeJSONBody(body []byte, v any) error {
return nil
}
- if err := json.Unmarshal(body, v); err != nil {
+ if err := json.Unmarshal(body, v, jsonx.V1()); err != nil {
return validationError("malformed request body: " + err.Error())
}
@@ -672,7 +673,7 @@ func decodeJSONBody(body []byte, v any) error {
// marshalResponse marshals v, wrapping any error as an internal failure
// (marshaling our own well-typed wire structs should never fail).
func marshalResponse(v any) ([]byte, error) {
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("outposts: marshal response: %w", err)
}
diff --git a/services/outposts/multi_region_test.go b/services/outposts/multi_region_test.go
index 9e8d9f5237..c3d699d069 100644
--- a/services/outposts/multi_region_test.go
+++ b/services/outposts/multi_region_test.go
@@ -2,7 +2,8 @@ package outposts_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/outposts"
)
@@ -173,8 +175,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/outposts/persistence.go b/services/outposts/persistence.go
index d3b130eb5c..c0b8856888 100644
--- a/services/outposts/persistence.go
+++ b/services/outposts/persistence.go
@@ -2,7 +2,7 @@ package outposts
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -19,10 +19,10 @@ const outpostsSnapshotVersion = 1
// backendSnapshot is the top-level on-disk shape for the Outposts backend.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountId"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountId"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/personalize/handler.go b/services/personalize/handler.go
index 11ec7f9348..b00620272a 100644
--- a/services/personalize/handler.go
+++ b/services/personalize/handler.go
@@ -2,7 +2,7 @@ package personalize
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -230,7 +231,7 @@ func writeRuntimeRESTError(c *echo.Context, status int, errType, message string)
c.Response().Header().Set("Content-Type", personalizeRuntimeContentType)
c.Response().Header().Set("X-Amzn-Errortype", errType)
- payload, err := json.Marshal(map[string]string{"message": message})
+ payload, err := json.Marshal(map[string]string{"message": message}, jsonx.V1())
if err != nil {
return err
}
@@ -263,7 +264,7 @@ func (h *Handler) dispatch(_ context.Context, action string, body []byte) ([]byt
var input map[string]any
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON", ErrValidation)
}
}
@@ -280,7 +281,7 @@ func (h *Handler) dispatch(_ context.Context, action string, body []byte) ([]byt
return nil, err
}
- return json.Marshal(output)
+ return json.Marshal(output, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
@@ -302,7 +303,10 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
c.Response().Header().Set("Content-Type", personalizeContentType)
- payload, marshalErr := json.Marshal(map[string]string{"__type": errType, "message": errorMessage(errType, err)})
+ payload, marshalErr := json.Marshal(
+ map[string]string{"__type": errType, "message": errorMessage(errType, err)},
+ jsonx.V1(),
+ )
if marshalErr != nil {
return c.String(http.StatusInternalServerError, "internal server error")
}
@@ -497,13 +501,13 @@ func decodeConfig[T any](raw map[string]any) *T {
return nil
}
- b, marshalErr := json.Marshal(raw)
+ b, marshalErr := json.Marshal(raw, jsonx.V1())
if marshalErr != nil {
return nil
}
var cfg T
- if err := json.Unmarshal(b, &cfg); err != nil {
+ if err := json.Unmarshal(b, &cfg, jsonx.V1()); err != nil {
return nil
}
diff --git a/services/personalize/handler_test.go b/services/personalize/handler_test.go
index b4a6c14d4f..b2bba336c2 100644
--- a/services/personalize/handler_test.go
+++ b/services/personalize/handler_test.go
@@ -1,7 +1,7 @@
package personalize_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/personalize"
)
@@ -52,7 +53,7 @@ func personalizeDoWithPrefix(
) *httptest.ResponseRecorder {
t.Helper()
- payload, err := json.Marshal(body)
+ payload, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -70,7 +71,7 @@ func personalizeUnmarshal(t *testing.T, rec *httptest.ResponseRecorder) map[stri
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
diff --git a/services/personalize/multi_region_test.go b/services/personalize/multi_region_test.go
index f7b552ec40..95a0f1ecab 100644
--- a/services/personalize/multi_region_test.go
+++ b/services/personalize/multi_region_test.go
@@ -2,7 +2,8 @@ package personalize_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/personalize"
)
@@ -173,8 +175,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/personalize/persistence.go b/services/personalize/persistence.go
index 367e62bee6..752b95427c 100644
--- a/services/personalize/persistence.go
+++ b/services/personalize/persistence.go
@@ -2,7 +2,7 @@ package personalize
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -38,7 +38,7 @@ const personalizeSnapshotVersion = 1
// *T, so it does not fit store.Table's keyed-by-identity-value shape) and is
// persisted here directly instead.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
AccountID string `json:"accountId"`
Region string `json:"region"`
diff --git a/services/personalize/validate.go b/services/personalize/validate.go
index 7b4f839711..e630bc2022 100644
--- a/services/personalize/validate.go
+++ b/services/personalize/validate.go
@@ -2,11 +2,13 @@ package personalize
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"regexp"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -44,7 +46,7 @@ func validateSchemaDocument(schema string) error {
}
var doc map[string]any
- if err := json.Unmarshal([]byte(schema), &doc); err != nil {
+ if err := json.Unmarshal([]byte(schema), &doc, jsonx.V1()); err != nil {
return fmt.Errorf("%w: schema is not valid JSON", ErrValidation)
}
diff --git a/services/pinpoint/applications_settings_test.go b/services/pinpoint/applications_settings_test.go
index 855e991429..c529116937 100644
--- a/services/pinpoint/applications_settings_test.go
+++ b/services/pinpoint/applications_settings_test.go
@@ -1,12 +1,15 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestApplicationSettings_BodyPersistence(t *testing.T) {
@@ -82,7 +85,7 @@ func TestApplicationSettings_BodyPersistence(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
// Update settings.
@@ -90,14 +93,14 @@ func TestApplicationSettings_BodyPersistence(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
// GET settings back.
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/settings", nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
if tc.wantCampaignHook {
hook, ok := getResp["CampaignHook"].(map[string]any)
@@ -169,7 +172,7 @@ func TestApplicationSettings_CloudWatchMetrics(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotContains(t, resp, "CloudWatchMetricsEnabled")
})
}
@@ -229,14 +232,14 @@ func TestApplicationSettings_EventTaggingCases(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
assert.NotContains(t, putResp, "EventTaggingEnabled")
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/settings", nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.NotContains(t, getResp, "EventTaggingEnabled")
})
}
@@ -307,7 +310,7 @@ func TestApplicationSettings_LimitsCases(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
limits, ok := resp["Limits"].(map[string]any)
require.True(t, ok, "Limits must be an object")
assert.InDelta(t, tc.checkVal, limits[tc.checkKey], 0.001)
@@ -345,7 +348,7 @@ func TestApplicationDateRangeKpi(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, appID, resp["ApplicationId"])
assert.Equal(t, tc.kpiName, resp["KpiName"])
assert.NotNil(t, resp["KpiResult"])
@@ -464,7 +467,7 @@ func TestApplicationSettings_FullRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
assert.NotContains(t, putResp, "CloudWatchMetricsEnabled")
assert.NotContains(t, putResp, "EventTaggingEnabled")
assert.NotNil(t, putResp["CampaignHook"])
@@ -476,7 +479,7 @@ func TestApplicationSettings_FullRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.NotContains(t, getResp, "CloudWatchMetricsEnabled")
assert.NotContains(t, getResp, "EventTaggingEnabled")
})
@@ -505,7 +508,7 @@ func TestApplicationSettings_CampaignHook(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var settings map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &settings))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &settings, jsonx.V1()))
gotHook := settings["CampaignHook"].(map[string]any)
assert.Equal(t, "arn:aws:lambda:us-east-1:123456789012:function:SegmentHook", gotHook["LambdaFunctionName"])
@@ -547,7 +550,7 @@ func TestApplicationSettings_QuietTime(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var settings map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &settings))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &settings, jsonx.V1()))
qt := settings["QuietTime"].(map[string]any)
assert.Equal(t, tc.wantStart, qt["Start"])
@@ -577,7 +580,7 @@ func TestApplicationSettings_Limits(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var settings map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &settings))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &settings, jsonx.V1()))
got := settings["Limits"].(map[string]any)
assert.EqualValues(t, 500, got["Daily"])
@@ -620,7 +623,7 @@ func TestApplicationSettings_MultipleUpdates(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var settings map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &settings))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &settings, jsonx.V1()))
assert.NotContains(t, settings, "CloudWatchMetricsEnabled")
@@ -648,7 +651,7 @@ func TestApplicationSettings_EventTagging(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var settings map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &settings))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &settings, jsonx.V1()))
assert.NotContains(t, settings, "EventTaggingEnabled")
assert.NotContains(t, settings, "CloudWatchMetricsEnabled")
@@ -690,7 +693,7 @@ func TestHandler_ApplicationSettings(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var appResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&appResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &appResp, jsonx.V1()))
appID, _ := appResp["Id"].(string)
require.NotEmpty(t, appID)
@@ -706,7 +709,7 @@ func TestHandler_ApplicationSettings(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec2.Code)
var settingsResp map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&settingsResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &settingsResp, jsonx.V1()))
assert.Equal(t, appID, settingsResp["ApplicationId"])
// CampaignHook, Limits, and QuietTime must be non-nil empty objects so
// the Terraform provider flatten helpers don't panic on nil dereferences.
@@ -753,19 +756,19 @@ func TestAppSettings_LastModifiedDate_ConsistentAfterUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
putDate, _ := putResp["LastModifiedDate"].(string)
require.NotEmpty(t, putDate, "PUT must return non-empty LastModifiedDate")
getRec1 := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/settings", nil)
require.Equal(t, http.StatusOK, getRec1.Code)
var getResp1 map[string]any
- require.NoError(t, json.Unmarshal(getRec1.Body.Bytes(), &getResp1))
+ require.NoError(t, json.Unmarshal(getRec1.Body.Bytes(), &getResp1, jsonx.V1()))
getRec2 := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/settings", nil)
require.Equal(t, http.StatusOK, getRec2.Code)
var getResp2 map[string]any
- require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getResp2))
+ require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getResp2, jsonx.V1()))
date1, _ := getResp1["LastModifiedDate"].(string)
date2, _ := getResp2["LastModifiedDate"].(string)
@@ -786,7 +789,7 @@ func TestAppSettings_LastModifiedDate_UpdatesOnSecondPut(t *testing.T) {
map[string]any{"CloudWatchMetricsEnabled": true})
require.Equal(t, http.StatusOK, put1.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(put1.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(put1.Body.Bytes(), &r1, jsonx.V1()))
date1, _ := r1["LastModifiedDate"].(string)
require.NotEmpty(t, date1)
@@ -795,7 +798,7 @@ func TestAppSettings_LastModifiedDate_UpdatesOnSecondPut(t *testing.T) {
map[string]any{"EventTaggingEnabled": true})
require.Equal(t, http.StatusOK, put2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(put2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(put2.Body.Bytes(), &r2, jsonx.V1()))
date2, _ := r2["LastModifiedDate"].(string)
require.NotEmpty(t, date2)
@@ -803,7 +806,7 @@ func TestAppSettings_LastModifiedDate_UpdatesOnSecondPut(t *testing.T) {
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/settings", nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
getDate, _ := getResp["LastModifiedDate"].(string)
assert.Equal(t, date2, getDate, "GET must return the most recent PUT's LastModifiedDate")
diff --git a/services/pinpoint/apps_test.go b/services/pinpoint/apps_test.go
index de86fe4eee..b2ef7bbd34 100644
--- a/services/pinpoint/apps_test.go
+++ b/services/pinpoint/apps_test.go
@@ -1,13 +1,16 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestBackend_CreateApp(t *testing.T) {
@@ -284,7 +287,7 @@ func TestAppNotFound_Returns404(t *testing.T) {
assert.Equal(t, http.StatusNotFound, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "NotFoundException", resp["__type"])
})
}
@@ -301,7 +304,7 @@ func TestCreateApp_ResponseShape(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Id"], "Id must be present")
assert.NotEmpty(t, resp["Arn"], "Arn must be present")
@@ -325,7 +328,7 @@ func TestGetApps_ReflectsCreatedApps(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
item := resp["Item"].([]any)
assert.Len(t, item, len(names))
@@ -344,7 +347,7 @@ func TestDeleteApp_RemovesFromList(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
appID := created["Id"].(string)
doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "also-here"})
@@ -358,7 +361,7 @@ func TestDeleteApp_RemovesFromList(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
item := listResp["Item"].([]any)
assert.Len(t, item, 1)
}
@@ -377,7 +380,7 @@ func TestHandlerReset(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
assert.Empty(t, items)
}
@@ -439,7 +442,7 @@ func TestHandler_CreateApp(t *testing.T) {
if tt.wantID {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Id"])
assert.NotEmpty(t, resp["Arn"])
}
@@ -480,7 +483,7 @@ func TestHandler_GetApp(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
appID, _ = resp["Id"].(string)
} else {
@@ -527,7 +530,7 @@ func TestHandler_DeleteApp(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
appID, _ = resp["Id"].(string)
} else {
@@ -580,7 +583,7 @@ func TestHandler_GetApps(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
assert.Len(t, items, tt.wantCount)
@@ -649,7 +652,7 @@ func TestHandler_GetAppsPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
items, ok := resp["Item"].([]any)
if tt.wantCount > 0 {
@@ -679,7 +682,7 @@ func TestHandler_GetAppsContinuation(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.NewDecoder(rec1.Body).Decode(&resp1))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec1.Body), &resp1, jsonx.V1()))
page1, ok := resp1["Item"].([]any)
require.True(t, ok, "expected Item array in first-page response")
@@ -695,7 +698,7 @@ func TestHandler_GetAppsContinuation(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&resp2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &resp2, jsonx.V1()))
page2, ok := resp2["Item"].([]any)
require.True(t, ok, "expected Item array in second-page response")
@@ -740,7 +743,7 @@ func TestHandler_GetApps_DuplicateNames_NoDropOrDupAcrossPages(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
created[resp["Id"].(string)] = true
}
@@ -752,7 +755,7 @@ func TestHandler_GetApps_DuplicateNames_NoDropOrDupAcrossPages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
for _, item := range items {
diff --git a/services/pinpoint/attributes_test.go b/services/pinpoint/attributes_test.go
index 365ed0a2d4..e87d0aadce 100644
--- a/services/pinpoint/attributes_test.go
+++ b/services/pinpoint/attributes_test.go
@@ -1,12 +1,14 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestAudit6_RemoveAttributes_RemovesBlacklistedNamesOnly verifies
@@ -36,14 +38,14 @@ func TestRemoveAttributes_RemovesBlacklistedNamesOnly(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "endpoint-custom-attributes", resp["AttributeType"])
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/endpoints/ep-1", nil)
require.Equal(t, http.StatusOK, getRec.Code)
var ep map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ep))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ep, jsonx.V1()))
attrs, _ := ep["Attributes"].(map[string]any)
_, hasInterests := attrs["Interests"]
diff --git a/services/pinpoint/campaigns_fields_test.go b/services/pinpoint/campaigns_fields_test.go
index 6ce470a3c4..85119a1c42 100644
--- a/services/pinpoint/campaigns_fields_test.go
+++ b/services/pinpoint/campaigns_fields_test.go
@@ -2,7 +2,7 @@ package pinpoint_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
@@ -10,6 +10,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCampaign_AdditionalTreatments(t *testing.T) {
@@ -108,7 +110,7 @@ func TestCampaign_AdditionalTreatments(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tc.wantHasTreatment {
treatments, _ := resp["AdditionalTreatments"].([]any)
@@ -203,7 +205,7 @@ func TestCampaign_StateMachine(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
campaignID := createResp["Id"].(string)
if tc.updateBody == nil {
@@ -219,7 +221,7 @@ func TestCampaign_StateMachine(t *testing.T) {
assert.Equal(t, tc.wantStatus, updateRec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
state, _ := updateResp["State"].(map[string]any)
require.NotNil(t, state)
@@ -258,7 +260,7 @@ func TestCampaign_DateRangeKpi(t *testing.T) {
map[string]any{"Name": "kpi-camp", "SegmentId": "seg-1"})
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
campaignID := cr["Id"].(string)
kpiRec := doPinpointRequest(t, h, http.MethodGet,
@@ -266,7 +268,7 @@ func TestCampaign_DateRangeKpi(t *testing.T) {
require.Equal(t, http.StatusOK, kpiRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(kpiRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(kpiRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, appID, resp["ApplicationId"])
assert.Equal(t, campaignID, resp["CampaignId"])
assert.Equal(t, tc.kpiName, resp["KpiName"])
@@ -314,7 +316,7 @@ func TestCampaign_GetActivities(t *testing.T) {
"/v1/apps/"+appID+"/campaigns", tc.campaignBody)
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
campaignID := cr["Id"].(string)
actRec := doPinpointRequest(t, h, http.MethodGet,
@@ -322,7 +324,7 @@ func TestCampaign_GetActivities(t *testing.T) {
require.Equal(t, http.StatusOK, actRec.Code)
var actResp map[string]any
- require.NoError(t, json.Unmarshal(actRec.Body.Bytes(), &actResp))
+ require.NoError(t, json.Unmarshal(actRec.Body.Bytes(), &actResp, jsonx.V1()))
assert.Contains(t, actResp, "Item")
})
}
@@ -380,7 +382,7 @@ func TestCampaign_SegmentVersion(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tc.wantSegVersion != 0 {
assert.InDelta(t, tc.wantSegVersion, resp["SegmentVersion"], 0.001)
} else {
@@ -457,14 +459,14 @@ func TestCampaign_TemplateConfiguration_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
campID := cr["Id"].(string)
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/campaigns/"+campID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var c map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &c))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &c, jsonx.V1()))
tmplCfg, ok := c["TemplateConfiguration"].(map[string]any)
assert.True(t, ok, "TemplateConfiguration must be present")
@@ -502,14 +504,14 @@ func TestCampaign_Limits_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
campID := cr["Id"].(string)
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/campaigns/"+campID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var c map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &c))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &c, jsonx.V1()))
got := c["Limits"].(map[string]any)
assert.EqualValues(t, 200, got["Daily"])
@@ -540,14 +542,14 @@ func TestCampaign_Hook_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
campID := cr["Id"].(string)
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/campaigns/"+campID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var c map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &c))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &c, jsonx.V1()))
got := c["Hook"].(map[string]any)
assert.Equal(t, "arn:aws:lambda:us-east-1:123456789012:function:SegmentFilter", got["LambdaFunctionName"])
@@ -566,7 +568,7 @@ func TestCampaign_TemplateConfiguration_OnUpdate(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
campID := cr["Id"].(string)
// Update to set template configuration
@@ -584,7 +586,7 @@ func TestCampaign_TemplateConfiguration_OnUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var c map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &c))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &c, jsonx.V1()))
tmpl := c["TemplateConfiguration"].(map[string]any)
email := tmpl["EmailTemplate"].(map[string]any)
@@ -617,14 +619,14 @@ func TestCampaign_CustomDeliveryConfiguration(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
campID := cr["Id"].(string)
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/campaigns/"+campID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var c map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &c))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &c, jsonx.V1()))
got := c["CustomDeliveryConfiguration"].(map[string]any)
assert.Equal(t, "arn:aws:lambda:us-east-1:123456789012:function:CustomDelivery", got["DeliveryUri"])
@@ -655,7 +657,7 @@ func TestPagination_Campaigns_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, p1Rec.Code)
var p1 map[string]any
- require.NoError(t, json.Unmarshal(p1Rec.Body.Bytes(), &p1))
+ require.NoError(t, json.Unmarshal(p1Rec.Body.Bytes(), &p1, jsonx.V1()))
items1, _ := p1["Item"].([]any)
assert.Len(t, items1, 2, "page 1 should contain 2 items")
@@ -669,7 +671,7 @@ func TestPagination_Campaigns_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, p2Rec.Code)
var p2 map[string]any
- require.NoError(t, json.Unmarshal(p2Rec.Body.Bytes(), &p2))
+ require.NoError(t, json.Unmarshal(p2Rec.Body.Bytes(), &p2, jsonx.V1()))
items2, _ := p2["Item"].([]any)
assert.Len(t, items2, 2, "page 2 should contain 2 items")
@@ -682,7 +684,7 @@ func TestPagination_Campaigns_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, p3Rec.Code)
var p3 map[string]any
- require.NoError(t, json.Unmarshal(p3Rec.Body.Bytes(), &p3))
+ require.NoError(t, json.Unmarshal(p3Rec.Body.Bytes(), &p3, jsonx.V1()))
items3, _ := p3["Item"].([]any)
assert.Len(t, items3, 1, "page 3 should contain the remaining 1 item")
@@ -707,7 +709,7 @@ func TestPagination_Campaigns_NoToken_ReturnsAll(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
assert.Len(t, items, 3)
@@ -732,7 +734,7 @@ func TestPagination_TokenEncoding(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tok, _ := resp["NextToken"].(string)
require.NotEmpty(t, tok)
@@ -756,7 +758,7 @@ func TestPagination_EmptyList_NoToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
assert.Empty(t, items)
@@ -778,7 +780,7 @@ func TestGetCampaignVersions_CrossAppIsolation(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
campaignID, _ := createResp["Id"].(string)
require.NotEmpty(t, campaignID)
diff --git a/services/pinpoint/campaigns_test.go b/services/pinpoint/campaigns_test.go
index fa1bc26fe3..9ad07e45cb 100644
--- a/services/pinpoint/campaigns_test.go
+++ b/services/pinpoint/campaigns_test.go
@@ -1,13 +1,16 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCampaignFullDTO_Create(t *testing.T) {
@@ -169,7 +172,7 @@ func TestCampaignFullDTO_Create(t *testing.T) {
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
rec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps/"+appID+"/campaigns", tc.body)
@@ -180,7 +183,7 @@ func TestCampaignFullDTO_Create(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Id"])
assert.Equal(t, appID, resp["ApplicationId"])
@@ -299,13 +302,13 @@ func TestCampaignStateMachine_Update(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
createRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps/"+appID+"/campaigns", tc.createBody)
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
campaignID := created["Id"].(string)
rec := doPinpointRequest(t, h, http.MethodPut, "/v1/apps/"+appID+"/campaigns/"+campaignID, tc.updateBody)
@@ -316,7 +319,7 @@ func TestCampaignStateMachine_Update(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tc.wantState != "" {
state, _ := resp["State"].(map[string]any)
@@ -342,14 +345,14 @@ func TestCampaignVersioning(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
createRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps/"+appID+"/campaigns",
map[string]any{"Name": "versioned", "SegmentId": "seg-1"})
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
campaignID := created["Id"].(string)
// Do 3 updates.
@@ -363,7 +366,7 @@ func TestCampaignVersioning(t *testing.T) {
verRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/campaigns/"+campaignID+"/versions", nil)
require.Equal(t, http.StatusOK, verRec.Code)
var verResp map[string]any
- require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &verResp))
+ require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &verResp, jsonx.V1()))
items, _ := verResp["Item"].([]any)
assert.Len(t, items, 4, "should have 4 versions")
@@ -371,7 +374,7 @@ func TestCampaignVersioning(t *testing.T) {
v1Rec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/campaigns/"+campaignID+"/versions/1", nil)
require.Equal(t, http.StatusOK, v1Rec.Code)
var v1Resp map[string]any
- require.NoError(t, json.Unmarshal(v1Rec.Body.Bytes(), &v1Resp))
+ require.NoError(t, json.Unmarshal(v1Rec.Body.Bytes(), &v1Resp, jsonx.V1()))
assert.InDelta(t, float64(1), v1Resp["Version"], 0.001)
}
@@ -387,14 +390,14 @@ func TestCampaignPauseUnpauseCycle(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
createRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps/"+appID+"/campaigns",
map[string]any{"Name": "cycle", "SegmentId": "seg-1"})
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
campaignID := created["Id"].(string)
// Check initial state.
@@ -406,7 +409,7 @@ func TestCampaignPauseUnpauseCycle(t *testing.T) {
map[string]any{"IsPaused": true})
require.Equal(t, http.StatusOK, pauseRec.Code)
var paused map[string]any
- require.NoError(t, json.Unmarshal(pauseRec.Body.Bytes(), &paused))
+ require.NoError(t, json.Unmarshal(pauseRec.Body.Bytes(), &paused, jsonx.V1()))
pausedState, _ := paused["State"].(map[string]any)
assert.Equal(t, "PAUSED", pausedState["CampaignStatus"])
@@ -415,7 +418,7 @@ func TestCampaignPauseUnpauseCycle(t *testing.T) {
map[string]any{"IsPaused": false})
require.Equal(t, http.StatusOK, unpauseRec.Code)
var unpaused map[string]any
- require.NoError(t, json.Unmarshal(unpauseRec.Body.Bytes(), &unpaused))
+ require.NoError(t, json.Unmarshal(unpauseRec.Body.Bytes(), &unpaused, jsonx.V1()))
unpausedState, _ := unpaused["State"].(map[string]any)
assert.Equal(t, "SCHEDULED", unpausedState["CampaignStatus"])
}
@@ -440,7 +443,7 @@ func TestCampaign_ScheduleFrequencyRoundTrip(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
rec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps/"+appID+"/campaigns",
@@ -455,7 +458,7 @@ func TestCampaign_ScheduleFrequencyRoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
schedule, _ := resp["Schedule"].(map[string]any)
assert.Equal(t, freq, schedule["Frequency"])
@@ -471,7 +474,7 @@ func TestCampaign_AdditionalTreatments_RoundTrip(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
rec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps/"+appID+"/campaigns",
@@ -495,7 +498,7 @@ func TestCampaign_AdditionalTreatments_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
treatments, _ := resp["AdditionalTreatments"].([]any)
assert.Len(t, treatments, 2)
@@ -516,7 +519,7 @@ func TestCampaignCRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
campaignID, _ := createResp["Id"].(string)
require.NotEmpty(t, campaignID)
@@ -626,7 +629,7 @@ func TestHandler_CreateCampaign(t *testing.T) {
if tt.wantID {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Id"])
assert.NotEmpty(t, resp["Arn"])
assert.Equal(t, appID, resp["ApplicationId"])
@@ -652,7 +655,7 @@ func TestGetCampaignVersion_UnknownVersionNotFound(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.NewDecoder(createRec.Body).Decode(&created))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(createRec.Body), &created, jsonx.V1()))
campaignID, _ := created["Id"].(string)
require.NotEmpty(t, campaignID)
@@ -668,7 +671,7 @@ func TestGetCampaignVersion_UnknownVersionNotFound(t *testing.T) {
assert.Equal(t, http.StatusNotFound, missingRec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(missingRec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(missingRec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "NotFoundException", errResp["__type"])
}
@@ -696,7 +699,7 @@ func TestHandler_GetCampaigns_DuplicateNames_NoDropOrDupAcrossPages(t *testing.T
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
created[resp["Id"].(string)] = true
}
@@ -708,7 +711,7 @@ func TestHandler_GetCampaigns_DuplicateNames_NoDropOrDupAcrossPages(t *testing.T
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
for _, item := range items {
diff --git a/services/pinpoint/channel_credential_echo_test.go b/services/pinpoint/channel_credential_echo_test.go
index ee1740ef02..33a4e5039c 100644
--- a/services/pinpoint/channel_credential_echo_test.go
+++ b/services/pinpoint/channel_credential_echo_test.go
@@ -1,7 +1,7 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pinpoint"
)
@@ -207,7 +208,7 @@ func TestGetAdmChannel_NoRawSecretInBody(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &body, jsonx.V1()))
assert.NotContains(t, body, "ClientId")
assert.NotContains(t, body, "ClientSecret")
@@ -231,7 +232,7 @@ func TestGetApnsChannel_NoRawSecretInBody(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &body, jsonx.V1()))
for _, k := range []string{"BundleId", "Certificate", "TeamId", "TokenKey", "TokenKeyId"} {
assert.NotContains(t, body, k)
diff --git a/services/pinpoint/channels_test.go b/services/pinpoint/channels_test.go
index 7048dfa951..c8d40ab382 100644
--- a/services/pinpoint/channels_test.go
+++ b/services/pinpoint/channels_test.go
@@ -1,12 +1,14 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestChannel_PerTypeFields(t *testing.T) {
@@ -129,7 +131,7 @@ func TestChannel_PerTypeFields(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
rec := doPinpointRequest(t, h, http.MethodPut,
@@ -141,7 +143,7 @@ func TestChannel_PerTypeFields(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantEnabled, resp["Enabled"])
assert.NotEmpty(t, resp["CreationDate"])
@@ -160,7 +162,7 @@ func TestChannel_PerTypeFields(t *testing.T) {
"/v1/apps/"+appID+"/channels/"+tc.channelType, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, resp["Enabled"], getResp["Enabled"])
assert.Equal(t, resp["HasCredential"], getResp["HasCredential"])
})
@@ -175,7 +177,7 @@ func TestChannel_VersionIncrements(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
for i := 1; i <= 3; i++ {
@@ -184,7 +186,7 @@ func TestChannel_VersionIncrements(t *testing.T) {
map[string]any{"ApiKey": "key", "Enabled": true})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.InDelta(t, float64(i), resp["Version"], 0.001, "version should increment on each put")
}
}
@@ -197,7 +199,7 @@ func TestChannel_CreationDatePreserved(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
rec1 := doPinpointRequest(t, h, http.MethodPut,
@@ -205,7 +207,7 @@ func TestChannel_CreationDatePreserved(t *testing.T) {
map[string]any{"ApiKey": "key", "Enabled": true})
require.Equal(t, http.StatusOK, rec1.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1, jsonx.V1()))
creationDate1 := r1["CreationDate"].(string)
rec2 := doPinpointRequest(t, h, http.MethodPut,
@@ -213,7 +215,7 @@ func TestChannel_CreationDatePreserved(t *testing.T) {
map[string]any{"ApiKey": "key-updated", "Enabled": true})
require.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
creationDate2 := r2["CreationDate"].(string)
assert.Equal(t, creationDate1, creationDate2, "CreationDate should be preserved on update")
@@ -231,7 +233,7 @@ func TestChannel_AllChannelsListed(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
channelTypes := []string{"gcm", "email", "sms"}
@@ -250,7 +252,7 @@ func TestChannel_AllChannelsListed(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
channels, _ := listResp["Channels"].(map[string]any)
assert.Len(t, channels, 3)
@@ -324,7 +326,7 @@ func TestSMSChannel_Attributes(t *testing.T) {
"body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantEnabled, resp["Enabled"])
getRec := doPinpointRequest(t, h, http.MethodGet,
@@ -332,7 +334,7 @@ func TestSMSChannel_Attributes(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var gr map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &gr, jsonx.V1()))
chType, _ := gr["ChannelType"].(string)
assert.True(t, chType == "SMS" || chType == "sms", "expected SMS channel type, got %q", chType)
})
@@ -412,7 +414,7 @@ func TestEmailChannel_Identity(t *testing.T) {
"body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantEnabled, resp["Enabled"])
chType, _ := resp["ChannelType"].(string)
assert.True(t, chType == "EMAIL" || chType == "email", "expected EMAIL channel type, got %q", chType)
@@ -468,7 +470,7 @@ func TestChannel_Delete_ReturnsChannel(t *testing.T) {
require.Equal(t, http.StatusOK, delRec.Code)
var ch map[string]any
- require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &ch))
+ require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &ch, jsonx.V1()))
assert.Equal(t, tc.channelType, ch["ChannelType"])
})
}
@@ -495,7 +497,7 @@ func TestGetChannels_IncludesExtraFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
channels, ok := resp["Channels"].(map[string]any)
require.True(t, ok, "Channels must be a map")
diff --git a/services/pinpoint/channels_types_test.go b/services/pinpoint/channels_types_test.go
index d827e89af7..f54fedf3d5 100644
--- a/services/pinpoint/channels_types_test.go
+++ b/services/pinpoint/channels_types_test.go
@@ -1,12 +1,14 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestChannel_APNS_CertificateAuth(t *testing.T) {
@@ -28,7 +30,7 @@ func TestChannel_APNS_CertificateAuth(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var ch map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch, jsonx.V1()))
assert.Equal(t, true, ch["Enabled"])
assert.Equal(t, true, ch["HasCredential"], "certificate auth sets HasCredential")
@@ -56,7 +58,7 @@ func TestChannel_APNS_TokenAuth(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var ch map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch, jsonx.V1()))
assert.Equal(t, true, ch["Enabled"])
// BundleId sets HasCredential; TokenKey sets HasTokenKey
@@ -83,7 +85,7 @@ func TestChannel_APNS_Sandbox(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var ch map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch, jsonx.V1()))
assert.Equal(t, "apns_sandbox", ch["ChannelType"])
assert.Equal(t, true, ch["Enabled"])
@@ -108,7 +110,7 @@ func TestChannel_ADM_HasCredential(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var ch map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch, jsonx.V1()))
assert.Equal(t, "adm", ch["ChannelType"])
assert.Equal(t, true, ch["Enabled"])
@@ -133,7 +135,7 @@ func TestChannel_Baidu_HasCredential(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var ch map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch, jsonx.V1()))
assert.Equal(t, "baidu", ch["ChannelType"])
assert.Equal(t, true, ch["Enabled"])
@@ -158,7 +160,7 @@ func TestChannel_GCM_ServiceJSON(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var ch map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch, jsonx.V1()))
assert.Equal(t, "gcm", ch["ChannelType"])
assert.Equal(t, true, ch["Enabled"])
@@ -180,7 +182,7 @@ func TestChannel_Voice_EnableDisable(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var ch map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch, jsonx.V1()))
assert.Equal(t, "voice", ch["ChannelType"])
assert.Equal(t, true, ch["Enabled"])
@@ -196,7 +198,7 @@ func TestChannel_Voice_EnableDisable(t *testing.T) {
require.Equal(t, http.StatusOK, getRec2.Code)
var ch2 map[string]any
- require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &ch2))
+ require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &ch2, jsonx.V1()))
assert.Equal(t, false, ch2["Enabled"])
assert.EqualValues(t, 2, ch2["Version"])
@@ -216,7 +218,7 @@ func TestChannel_InApp_EnableDisable(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var ch map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch, jsonx.V1()))
assert.Equal(t, true, ch["Enabled"])
}
@@ -239,7 +241,7 @@ func TestChannel_SMS_ShortCode(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var ch map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ch, jsonx.V1()))
assert.Equal(t, "sms", ch["ChannelType"])
assert.Equal(t, true, ch["Enabled"])
@@ -304,7 +306,7 @@ func TestSMSChannel_PerTypeFields(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
assert.Equal(t, tc.wantEnabled, putResp["Enabled"])
assert.NotContains(t, putResp, "PromotionalMessagesPerSecond")
@@ -324,7 +326,7 @@ func TestSMSChannel_PerTypeFields(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, tc.wantEnabled, getResp["Enabled"])
@@ -385,7 +387,7 @@ func TestGCMChannel_PerTypeFields(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &resp, jsonx.V1()))
if tc.wantAuthMethod != "" {
assert.Equal(t, tc.wantAuthMethod, resp["DefaultAuthenticationMethod"])
@@ -427,7 +429,7 @@ func TestAPNSChannel_PerTypeFields(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &resp, jsonx.V1()))
// APNSChannelResponse has no BundleId/Certificate/TeamId members -- only
// HasCredential/HasTokenKey booleans -- so raw request secrets must never
@@ -460,7 +462,7 @@ func TestEmailChannel_PerTypeFields(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "noreply@example.com", resp["FromAddress"])
assert.Equal(t, "arn:aws:ses:us-east-1:123456789012:identity/example.com", resp["Identity"])
@@ -475,7 +477,7 @@ func TestEmailChannel_PerTypeFields(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "noreply@example.com", getResp["FromAddress"])
assert.Equal(t, "my-config-set", getResp["ConfigurationSet"])
@@ -499,7 +501,7 @@ func TestBaiduChannel_PerTypeFields(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &resp, jsonx.V1()))
// BaiduChannelResponse's credential member is "Credential" (the API key,
// required), not "ApiKey"; SecretKey has no response member at all.
@@ -525,7 +527,7 @@ func TestADMChannel_PerTypeFields(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &resp, jsonx.V1()))
// ADMChannelResponse has no ClientId/ClientSecret members -- only
// HasCredential -- so neither must be echoed back on the wire.
@@ -546,7 +548,7 @@ func TestVoiceChannel_BasicResponse(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, true, resp["Enabled"])
assert.NotEmpty(t, resp["ChannelType"])
@@ -573,6 +575,6 @@ func TestApnsChannel_DefaultAuthenticationMethod(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "TOKEN", resp["DefaultAuthenticationMethod"])
}
diff --git a/services/pinpoint/endpoints_test.go b/services/pinpoint/endpoints_test.go
index dab1f1ed60..db218f6848 100644
--- a/services/pinpoint/endpoints_test.go
+++ b/services/pinpoint/endpoints_test.go
@@ -1,13 +1,15 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestEndpoint_FullShape(t *testing.T) {
@@ -119,7 +121,7 @@ func TestEndpoint_FullShape(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
endpointID := "ep-" + tc.name
@@ -137,7 +139,7 @@ func TestEndpoint_FullShape(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
if tc.wantChannelType != "" {
assert.Equal(t, tc.wantChannelType, resp["ChannelType"])
@@ -174,7 +176,7 @@ func TestEndpoint_UserAttributes(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
rec := doPinpointRequest(t, h, http.MethodPut, "/v1/apps/"+appID+"/endpoints/ep-1",
@@ -196,7 +198,7 @@ func TestEndpoint_UserAttributes(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
user, ok := resp["User"].(map[string]any)
require.True(t, ok, "User field must be present")
@@ -217,7 +219,7 @@ func TestEndpointBatch_FullShape(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
// Real EndpointBatchRequest.Item is a JSON array of EndpointBatchItem,
@@ -258,7 +260,7 @@ func TestEndpointBatch_FullShape(t *testing.T) {
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/endpoints/ep-batch-1", nil)
require.Equal(t, http.StatusOK, getRec.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &r1, jsonx.V1()))
assert.NotNil(t, r1["Attributes"])
assert.NotNil(t, r1["Metrics"])
assert.NotNil(t, r1["Demographic"])
@@ -267,7 +269,7 @@ func TestEndpointBatch_FullShape(t *testing.T) {
getRec2 := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/endpoints/ep-batch-2", nil)
require.Equal(t, http.StatusOK, getRec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &r2, jsonx.V1()))
assert.Equal(t, "INACTIVE", r2["EndpointStatus"])
assert.Equal(t, "ALL", r2["OptOut"])
}
@@ -284,7 +286,7 @@ func TestEndpoint_EffectiveDate_RoundTrip(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
effectiveDate := "2026-01-15T10:30:00Z"
@@ -299,7 +301,7 @@ func TestEndpoint_EffectiveDate_RoundTrip(t *testing.T) {
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/endpoints/ep-eff", nil)
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, effectiveDate, resp["EffectiveDate"])
}
@@ -330,7 +332,7 @@ func TestEndpoint_UserAttributes_MultiValue(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var ep map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ep))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ep, jsonx.V1()))
user := ep["User"].(map[string]any)
assert.Equal(t, "user-001", user["UserId"])
@@ -390,7 +392,7 @@ func TestEndpoint_UserAttributes_UpdateMerge(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var ep map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ep))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ep, jsonx.V1()))
ua := ep["User"].(map[string]any)["UserAttributes"].(map[string]any)
assert.Equal(t, []any{"gold"}, ua["tier"])
@@ -435,7 +437,7 @@ func TestGetUserEndpoints_ByUserID(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["Item"].([]any)
assert.Len(t, items, 2, "only endpoints for shared-user")
@@ -489,7 +491,7 @@ func TestEndpoint_UserAttributes_BatchUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, getRec1.Code)
var ep1 map[string]any
- require.NoError(t, json.Unmarshal(getRec1.Body.Bytes(), &ep1))
+ require.NoError(t, json.Unmarshal(getRec1.Body.Bytes(), &ep1, jsonx.V1()))
ua1 := ep1["User"].(map[string]any)["UserAttributes"].(map[string]any)
assert.Equal(t, []any{"beta-testers"}, ua1["segment"])
@@ -499,7 +501,7 @@ func TestEndpoint_UserAttributes_BatchUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, getRec2.Code)
var ep2 map[string]any
- require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &ep2))
+ require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &ep2, jsonx.V1()))
ua2 := ep2["User"].(map[string]any)["UserAttributes"].(map[string]any)
assert.Equal(t, []any{"power-users"}, ua2["segment"])
}
@@ -532,7 +534,7 @@ func TestEndpoint_Metrics_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var ep map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ep))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ep, jsonx.V1()))
metrics := ep["Metrics"].(map[string]any)
assert.InDelta(t, 12.0, metrics["session_count"], 0.001)
@@ -577,7 +579,7 @@ func TestEndpoint_Demographic_Location(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var ep map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ep))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &ep, jsonx.V1()))
demo := ep["Demographic"].(map[string]any)
assert.Equal(t, "4.2.1", demo["AppVersion"])
@@ -730,7 +732,7 @@ func TestUpdateEndpoint_ReturnsMessageBody(t *testing.T) {
require.Equal(t, http.StatusAccepted, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
msg, _ := resp["Message"].(string)
assert.Equal(t, "Accepted", msg,
diff --git a/services/pinpoint/error_envelope_test.go b/services/pinpoint/error_envelope_test.go
index 6d933eab06..852db40172 100644
--- a/services/pinpoint/error_envelope_test.go
+++ b/services/pinpoint/error_envelope_test.go
@@ -1,7 +1,7 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/pinpoint"
@@ -90,7 +91,7 @@ func TestErrorEnvelope_GetAppNotFoundDecodesToTypedError(t *testing.T) {
require.NoError(t, err)
var envelope map[string]any
- require.NoError(t, json.Unmarshal(raw, &envelope))
+ require.NoError(t, json.Unmarshal(raw, &envelope, jsonx.V1()))
require.Equal(t, "NotFoundException", envelope["__type"],
"raw body must carry __type key restjson.GetErrorInfo's fallback reads: %s", raw)
diff --git a/services/pinpoint/event_streams_test.go b/services/pinpoint/event_streams_test.go
index 90f69a765a..cfb20696b1 100644
--- a/services/pinpoint/event_streams_test.go
+++ b/services/pinpoint/event_streams_test.go
@@ -1,13 +1,15 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestEventStream_PutAndGet_Fields(t *testing.T) {
@@ -30,7 +32,7 @@ func TestEventStream_PutAndGet_Fields(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var es map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &es))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &es, jsonx.V1()))
assert.Equal(t, destArn, es["DestinationStreamArn"])
assert.Equal(t, roleArn, es["RoleArn"])
@@ -59,7 +61,7 @@ func TestEventStream_Replace(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var es map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &es))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &es, jsonx.V1()))
assert.Equal(t, stream2, es["DestinationStreamArn"], "second PUT must replace first")
}
@@ -101,13 +103,13 @@ func TestEventStream_AppIsolation(t *testing.T) {
getA := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appA+"/eventstream", nil)
require.Equal(t, http.StatusOK, getA.Code)
var esA map[string]any
- require.NoError(t, json.Unmarshal(getA.Body.Bytes(), &esA))
+ require.NoError(t, json.Unmarshal(getA.Body.Bytes(), &esA, jsonx.V1()))
assert.Equal(t, streamA, esA["DestinationStreamArn"])
getB := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appB+"/eventstream", nil)
require.Equal(t, http.StatusOK, getB.Code)
var esB map[string]any
- require.NoError(t, json.Unmarshal(getB.Body.Bytes(), &esB))
+ require.NoError(t, json.Unmarshal(getB.Body.Bytes(), &esB, jsonx.V1()))
assert.Equal(t, streamB, esB["DestinationStreamArn"])
}
diff --git a/services/pinpoint/events_test.go b/services/pinpoint/events_test.go
index dfabb35397..6b49df5e61 100644
--- a/services/pinpoint/events_test.go
+++ b/services/pinpoint/events_test.go
@@ -1,12 +1,14 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestPutEvents_AppLifeCycleEvent(t *testing.T) {
@@ -150,7 +152,7 @@ func TestPutEvents_ReturnsEventResults(t *testing.T) {
require.Equal(t, http.StatusAccepted, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results, ok := resp["Results"].(map[string]any)
require.True(t, ok, "EventsResponse must have Results")
@@ -199,7 +201,7 @@ func TestPutEvents_MultipleEndpoints(t *testing.T) {
require.Equal(t, http.StatusAccepted, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results := resp["Results"].(map[string]any)
@@ -226,7 +228,7 @@ func TestPutEvents_EmptyBatch(t *testing.T) {
require.Equal(t, http.StatusAccepted, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results := resp["Results"].(map[string]any)
assert.Empty(t, results, "empty BatchItem → empty Results")
diff --git a/services/pinpoint/export_import_jobs_test.go b/services/pinpoint/export_import_jobs_test.go
index 041f33b3b7..295f738c2d 100644
--- a/services/pinpoint/export_import_jobs_test.go
+++ b/services/pinpoint/export_import_jobs_test.go
@@ -1,7 +1,8 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +11,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/pinpoint/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestImportJob_CreatesSegment(t *testing.T) {
@@ -33,7 +36,7 @@ func TestImportJob_CreatesSegment(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
items, _ := listResp["Item"].([]any)
require.Len(t, items, 1, "import job must materialise exactly one segment")
@@ -70,7 +73,7 @@ func TestImportJob_DefaultSegmentName(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
items, _ := listResp["Item"].([]any)
require.Len(t, items, 1)
@@ -97,7 +100,7 @@ func TestImportJob_SegmentGetByID(t *testing.T) {
listRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/segments", nil)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
items := listResp["Item"].([]any)
segID := items[0].(map[string]any)["Id"].(string)
@@ -106,7 +109,7 @@ func TestImportJob_SegmentGetByID(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var s map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &s))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &s, jsonx.V1()))
assert.Equal(t, "IMPORT", s["SegmentType"])
assert.Equal(t, "direct-import", s["Name"])
}
@@ -302,7 +305,7 @@ func TestHandler_CreateExportJob(t *testing.T) {
if tt.wantID {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Id"])
assert.Equal(t, appID, resp["ApplicationId"])
assert.Equal(t, "CREATED", resp["JobStatus"])
@@ -345,7 +348,7 @@ func TestHandler_CreateImportJob(t *testing.T) {
if tt.wantID {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Id"])
assert.Equal(t, appID, resp["ApplicationId"])
assert.Equal(t, "CREATED", resp["JobStatus"])
@@ -376,7 +379,7 @@ func TestGetSegmentImportJobs_FiltersBySegment(t *testing.T) {
segsRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/segments", nil)
require.Equal(t, http.StatusOK, segsRec.Code)
var segsResp map[string]any
- require.NoError(t, json.Unmarshal(segsRec.Body.Bytes(), &segsResp))
+ require.NoError(t, json.Unmarshal(segsRec.Body.Bytes(), &segsResp, jsonx.V1()))
items, _ := segsResp["Item"].([]any)
require.Len(t, items, 1, "import job must materialise exactly one segment")
@@ -388,7 +391,7 @@ func TestGetSegmentImportJobs_FiltersBySegment(t *testing.T) {
require.Equal(t, http.StatusOK, jobsRec.Code)
var jobsResp map[string]any
- require.NoError(t, json.Unmarshal(jobsRec.Body.Bytes(), &jobsResp))
+ require.NoError(t, json.Unmarshal(jobsRec.Body.Bytes(), &jobsResp, jsonx.V1()))
jobs, _ := jobsResp["Item"].([]any)
assert.Len(t, jobs, 1, "GetSegmentImportJobs must return the job that created the segment")
@@ -415,7 +418,7 @@ func TestGetSegmentImportJobs_OtherSegmentReturnsEmpty(t *testing.T) {
map[string]any{"Name": "unrelated-segment"})
require.Equal(t, http.StatusCreated, segRec.Code)
var segResp map[string]any
- require.NoError(t, json.Unmarshal(segRec.Body.Bytes(), &segResp))
+ require.NoError(t, json.Unmarshal(segRec.Body.Bytes(), &segResp, jsonx.V1()))
unrelatedSegID := segResp["Id"].(string)
// GetSegmentImportJobs for the unrelated segment → must return empty list.
@@ -424,7 +427,7 @@ func TestGetSegmentImportJobs_OtherSegmentReturnsEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, jobsRec.Code)
var jobsResp map[string]any
- require.NoError(t, json.Unmarshal(jobsRec.Body.Bytes(), &jobsResp))
+ require.NoError(t, json.Unmarshal(jobsRec.Body.Bytes(), &jobsResp, jsonx.V1()))
jobs, _ := jobsResp["Item"].([]any)
assert.Empty(t, jobs, "GetSegmentImportJobs must return empty for segments not created by an import job")
@@ -457,7 +460,7 @@ func TestGetSegmentImportJobs_MultipleJobs_IsolatedBySegment(t *testing.T) {
segsRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/segments", nil)
require.Equal(t, http.StatusOK, segsRec.Code)
var segsResp map[string]any
- require.NoError(t, json.Unmarshal(segsRec.Body.Bytes(), &segsResp))
+ require.NoError(t, json.Unmarshal(segsRec.Body.Bytes(), &segsResp, jsonx.V1()))
items, _ := segsResp["Item"].([]any)
require.Len(t, items, 2)
@@ -471,7 +474,7 @@ func TestGetSegmentImportJobs_MultipleJobs_IsolatedBySegment(t *testing.T) {
require.Equal(t, http.StatusOK, jobsRec.Code)
var jobsResp map[string]any
- require.NoError(t, json.Unmarshal(jobsRec.Body.Bytes(), &jobsResp))
+ require.NoError(t, json.Unmarshal(jobsRec.Body.Bytes(), &jobsResp, jsonx.V1()))
jobs, _ := jobsResp["Item"].([]any)
assert.Len(t, jobs, 1,
"each segment's GetSegmentImportJobs must return only its own job, segment %s", segID)
diff --git a/services/pinpoint/handler.go b/services/pinpoint/handler.go
index 22c0f037e8..89d7cf7d5f 100644
--- a/services/pinpoint/handler.go
+++ b/services/pinpoint/handler.go
@@ -2,7 +2,7 @@ package pinpoint
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -682,7 +683,7 @@ func unmarshalBody(c *echo.Context, dst any) bool {
return false
}
- if jsonErr := json.Unmarshal(body, dst); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, dst, jsonx.V1()); jsonErr != nil {
_ = writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
return false
diff --git a/services/pinpoint/handler_applications_settings.go b/services/pinpoint/handler_applications_settings.go
index 4afb694d98..62390b7cd8 100644
--- a/services/pinpoint/handler_applications_settings.go
+++ b/services/pinpoint/handler_applications_settings.go
@@ -1,7 +1,7 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
@@ -9,6 +9,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// extractSettingsOp returns the settings operation name.
@@ -74,7 +75,7 @@ func (h *Handler) handleUpdateApplicationSettings(c *echo.Context, appID string)
}
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &incoming); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &incoming, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
}
}
diff --git a/services/pinpoint/handler_apps.go b/services/pinpoint/handler_apps.go
index d77502e28b..688c57ab5c 100644
--- a/services/pinpoint/handler_apps.go
+++ b/services/pinpoint/handler_apps.go
@@ -1,7 +1,7 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// extractAppsCollectionOp returns the operation for the apps collection.
@@ -78,7 +79,7 @@ func (h *Handler) handleCreateApp(c *echo.Context) error {
var req createAppRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
}
diff --git a/services/pinpoint/handler_attributes.go b/services/pinpoint/handler_attributes.go
index 7a56616a01..4e4d789a1a 100644
--- a/services/pinpoint/handler_attributes.go
+++ b/services/pinpoint/handler_attributes.go
@@ -1,7 +1,7 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
@@ -9,6 +9,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleRemoveAttributes handles PUT /v1/apps/{appId}/attributes/{attributeType}.
@@ -21,7 +22,7 @@ func (h *Handler) handleRemoveAttributes(c *echo.Context, appID, attributeType s
var req removeAttributesRequest
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
}
}
diff --git a/services/pinpoint/handler_channels.go b/services/pinpoint/handler_channels.go
index dc3fa55213..a7b2e4abbc 100644
--- a/services/pinpoint/handler_channels.go
+++ b/services/pinpoint/handler_channels.go
@@ -1,7 +1,7 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"strings"
@@ -9,6 +9,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) extractChannelOp(method, channelType string) string {
@@ -186,7 +187,7 @@ func (h *Handler) handleGetChannels(c *echo.Context, appID string) error {
func parseGCMChannelExtra(body []byte) (bool, map[string]any) {
var req updateGCMChannelRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return false, nil
}
@@ -205,7 +206,7 @@ func parseGCMChannelExtra(body []byte) (bool, map[string]any) {
func parseAPNSChannelExtra(body []byte) (bool, map[string]any) {
var req updateAPNSChannelRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return false, nil
}
@@ -225,7 +226,7 @@ func parseAPNSChannelExtra(body []byte) (bool, map[string]any) {
func parseEmailChannelExtra(body []byte) (bool, map[string]any) {
var req updateEmailChannelRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return false, nil
}
@@ -246,7 +247,7 @@ func parseEmailChannelExtra(body []byte) (bool, map[string]any) {
func parseSMSChannelExtra(body []byte) (bool, map[string]any) {
var req updateSMSChannelRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return false, nil
}
@@ -276,7 +277,7 @@ func parseChannelExtra(channelType string, body []byte) (bool, map[string]any) {
return parseSMSChannelExtra(body)
case channelKeyADM:
var req updateADMChannelRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return false, nil
}
@@ -293,7 +294,7 @@ func parseChannelExtra(channelType string, body []byte) (bool, map[string]any) {
return req.Enabled, extra
case channelKeyBaidu:
var req updateBaiduChannelRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return false, nil
}
@@ -310,7 +311,7 @@ func parseChannelExtra(channelType string, body []byte) (bool, map[string]any) {
return req.Enabled, extra
default:
var req updateChannelRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return false, nil
}
diff --git a/services/pinpoint/handler_endpoints.go b/services/pinpoint/handler_endpoints.go
index 6e779891ef..a8686301cd 100644
--- a/services/pinpoint/handler_endpoints.go
+++ b/services/pinpoint/handler_endpoints.go
@@ -1,7 +1,7 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) extractEndpointSubOp(method, rest string) string {
@@ -136,7 +137,7 @@ func (h *Handler) handleUpdateEndpoint(c *echo.Context, appID, endpointID string
}
var req updateEndpointRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
}
@@ -221,7 +222,7 @@ func (h *Handler) handleUpdateEndpointsBatch(c *echo.Context, appID string) erro
}
var req updateEndpointsBatchRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
}
diff --git a/services/pinpoint/handler_event_streams.go b/services/pinpoint/handler_event_streams.go
index 91fe05dcb3..84416a2cba 100644
--- a/services/pinpoint/handler_event_streams.go
+++ b/services/pinpoint/handler_event_streams.go
@@ -1,7 +1,7 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
@@ -9,6 +9,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// extractEventStreamOp returns the event stream operation name.
@@ -71,7 +72,7 @@ func (h *Handler) handlePutEventStream(c *echo.Context, appID string) error {
}
var req putEventStreamRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
}
diff --git a/services/pinpoint/handler_events.go b/services/pinpoint/handler_events.go
index 53a9605954..3531a2359b 100644
--- a/services/pinpoint/handler_events.go
+++ b/services/pinpoint/handler_events.go
@@ -1,12 +1,13 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handlePutEvents handles POST /v1/apps/{appId}/events.
@@ -26,7 +27,7 @@ func (h *Handler) handlePutEvents(c *echo.Context, appID string) error {
}
var req putEventsRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
}
diff --git a/services/pinpoint/handler_export_import_jobs.go b/services/pinpoint/handler_export_import_jobs.go
index b8f83679c1..6d7f481826 100644
--- a/services/pinpoint/handler_export_import_jobs.go
+++ b/services/pinpoint/handler_export_import_jobs.go
@@ -1,7 +1,7 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -83,7 +84,7 @@ func (h *Handler) handleCreateExportJob(c *echo.Context, appID string) error {
}
var req createExportJobRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
}
@@ -119,7 +120,7 @@ func (h *Handler) handleCreateImportJob(c *echo.Context, appID string) error {
}
var req createImportJobRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
}
diff --git a/services/pinpoint/handler_journeys.go b/services/pinpoint/handler_journeys.go
index 1e90b20632..575a518fc8 100644
--- a/services/pinpoint/handler_journeys.go
+++ b/services/pinpoint/handler_journeys.go
@@ -1,7 +1,7 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// extractJourneysOp returns the journeys collection operation name.
@@ -149,7 +150,7 @@ func (h *Handler) dispatchJourneyRun(c *echo.Context, appID, journeyID, rest str
func (h *Handler) handleCreateJourney(c *echo.Context, appID string) error {
return h.handleCreateNamedAppResource(c, appID, func(body []byte, region, appID string) (any, error) {
var req createJourneyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, errInvalidRequestBody
}
@@ -249,7 +250,7 @@ func (h *Handler) handleUpdateJourneyState(c *echo.Context, appID, journeyID str
}
var req updateJourneyStateRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
}
diff --git a/services/pinpoint/handler_messages.go b/services/pinpoint/handler_messages.go
index be302bc67d..fa32ae0806 100644
--- a/services/pinpoint/handler_messages.go
+++ b/services/pinpoint/handler_messages.go
@@ -1,7 +1,7 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
@@ -9,6 +9,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleSendMessages handles POST /v1/apps/{appId}/messages.
@@ -23,7 +24,7 @@ func (h *Handler) handleSendMessages(c *echo.Context, appID string) error {
}
var req sendMessagesRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
}
@@ -54,7 +55,7 @@ func (h *Handler) handleSendUsersMessages(c *echo.Context, appID string) error {
var req sendUsersMessagesRequest
if len(body) > 0 {
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
}
}
@@ -105,7 +106,7 @@ func (h *Handler) handleVerifyOTPMessage(c *echo.Context, appID string) error {
var req verifyOTPMessageRequest
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
code := req.Otp
@@ -136,7 +137,7 @@ func (h *Handler) handlePhoneNumberValidate(c *echo.Context) error {
}
var req phoneNumberValidateRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
}
diff --git a/services/pinpoint/handler_recommender_configurations.go b/services/pinpoint/handler_recommender_configurations.go
index d38b01d5d8..fbdf8b93c4 100644
--- a/services/pinpoint/handler_recommender_configurations.go
+++ b/services/pinpoint/handler_recommender_configurations.go
@@ -1,7 +1,7 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// extractRecommendersCollectionOp returns the operation for the recommenders collection.
@@ -95,7 +96,7 @@ func (h *Handler) handleCreateRecommenderConfiguration(c *echo.Context) error {
}
var req createRecommenderConfigRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(
c,
http.StatusBadRequest,
@@ -236,7 +237,7 @@ func (h *Handler) handleUpdateRecommenderConfiguration(
}
var req createRecommenderConfigRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(
c,
http.StatusBadRequest,
diff --git a/services/pinpoint/handler_segments.go b/services/pinpoint/handler_segments.go
index 240d5c5e68..fa9fec7652 100644
--- a/services/pinpoint/handler_segments.go
+++ b/services/pinpoint/handler_segments.go
@@ -1,7 +1,7 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// extractSegmentsOp returns the segments collection operation name.
@@ -105,7 +106,7 @@ func (h *Handler) dispatchSegmentByID(c *echo.Context, appID, rest string) error
func (h *Handler) handleCreateSegment(c *echo.Context, appID string) error {
return h.handleCreateNamedAppResource(c, appID, func(body []byte, region, appID string) (any, error) {
var req createSegmentRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, errInvalidRequestBody
}
diff --git a/services/pinpoint/handler_tags.go b/services/pinpoint/handler_tags.go
index e9e47b700a..3d12cdc909 100644
--- a/services/pinpoint/handler_tags.go
+++ b/services/pinpoint/handler_tags.go
@@ -1,7 +1,7 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
@@ -11,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// extractTagOperation resolves the operation for tag-related paths.
@@ -59,7 +60,7 @@ func (h *Handler) handleTagResource(c *echo.Context, resourceARN string) error {
var req tagResourceRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
}
diff --git a/services/pinpoint/handler_templates.go b/services/pinpoint/handler_templates.go
index b44b480a96..1079ad76c8 100644
--- a/services/pinpoint/handler_templates.go
+++ b/services/pinpoint/handler_templates.go
@@ -1,7 +1,7 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// extractTemplateOperation resolves the operation name for paths under /v1/templates/.
@@ -221,7 +222,7 @@ func (h *Handler) createTemplateByType(body []byte, region, templateName, templa
func (h *Handler) createEmailTemplateARN(body []byte, region, templateName string) (string, error) {
var req createEmailTemplateRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return "", errInvalidRequestBody
}
@@ -235,7 +236,7 @@ func (h *Handler) createEmailTemplateARN(body []byte, region, templateName strin
func (h *Handler) createInAppTemplateARN(body []byte, region, templateName string) (string, error) {
var req createInAppTemplateRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return "", errInvalidRequestBody
}
@@ -249,7 +250,7 @@ func (h *Handler) createInAppTemplateARN(body []byte, region, templateName strin
func (h *Handler) createPushTemplateARN(body []byte, region, templateName string) (string, error) {
var req createPushTemplateRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return "", errInvalidRequestBody
}
@@ -263,7 +264,7 @@ func (h *Handler) createPushTemplateARN(body []byte, region, templateName string
func (h *Handler) createSMSTemplateARN(body []byte, region, templateName string) (string, error) {
var req createSmsTemplateRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return "", errInvalidRequestBody
}
@@ -277,7 +278,7 @@ func (h *Handler) createSMSTemplateARN(body []byte, region, templateName string)
func (h *Handler) createVoiceTemplateARN(body []byte, region, templateName string) (string, error) {
var req createVoiceTemplateRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return "", errInvalidRequestBody
}
@@ -381,7 +382,7 @@ func (h *Handler) applyTemplateUpdate(body []byte, templateName, templateType, v
// updateEmailTemplateFromBody parses and applies an email template update.
func (h *Handler) updateEmailTemplateFromBody(body []byte, name, version string, createNew bool) error {
var req createEmailTemplateRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return errInvalidRequestBody
}
@@ -396,7 +397,7 @@ func (h *Handler) updateEmailTemplateFromBody(body []byte, name, version string,
// updateInAppTemplateFromBody parses and applies an in-app template update.
func (h *Handler) updateInAppTemplateFromBody(body []byte, name, version string, createNew bool) error {
var req createInAppTemplateRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return errInvalidRequestBody
}
@@ -411,7 +412,7 @@ func (h *Handler) updateInAppTemplateFromBody(body []byte, name, version string,
// updatePushTemplateFromBody parses and applies a push template update.
func (h *Handler) updatePushTemplateFromBody(body []byte, name, version string, createNew bool) error {
var req createPushTemplateRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return errInvalidRequestBody
}
@@ -426,7 +427,7 @@ func (h *Handler) updatePushTemplateFromBody(body []byte, name, version string,
// updateSMSTemplateFromBody parses and applies an SMS template update.
func (h *Handler) updateSMSTemplateFromBody(body []byte, name, version string, createNew bool) error {
var req createSmsTemplateRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return errInvalidRequestBody
}
@@ -441,7 +442,7 @@ func (h *Handler) updateSMSTemplateFromBody(body []byte, name, version string, c
// updateVoiceTemplateFromBody parses and applies a voice template update.
func (h *Handler) updateVoiceTemplateFromBody(body []byte, name, version string, createNew bool) error {
var req createVoiceTemplateRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return errInvalidRequestBody
}
@@ -555,7 +556,7 @@ func (h *Handler) handleUpdateTemplateActiveVersion(c *echo.Context, templateNam
Version string `json:"Version"`
}
- if len(body) > 0 && json.Unmarshal(body, &req) != nil {
+ if len(body) > 0 && json.Unmarshal(body, &req, jsonx.V1()) != nil {
return writeErrorResponse(c, http.StatusBadRequest, "BadRequestException", "invalid request body")
}
diff --git a/services/pinpoint/handler_templates_rejection_test.go b/services/pinpoint/handler_templates_rejection_test.go
index 736a890c06..482b841c21 100644
--- a/services/pinpoint/handler_templates_rejection_test.go
+++ b/services/pinpoint/handler_templates_rejection_test.go
@@ -1,12 +1,14 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestUpdateTemplate_RejectedUpdate_DoesNotDoubleWrite is a regression test
@@ -40,7 +42,7 @@ func TestUpdateTemplate_RejectedUpdate_DoesNotDoubleWrite(t *testing.T) {
"a rejected update must not write a second (success) response onto the same body")
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp),
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()),
"response body must decode as exactly one JSON object -- a second "+
"write appended after the committed 404 corrupts it")
assert.Equal(t, "NotFoundException", resp["__type"])
diff --git a/services/pinpoint/handler_test.go b/services/pinpoint/handler_test.go
index 4332e4dcbf..df08cd14ea 100644
--- a/services/pinpoint/handler_test.go
+++ b/services/pinpoint/handler_test.go
@@ -2,7 +2,8 @@ package pinpoint_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pinpoint"
)
@@ -38,7 +40,7 @@ func doPinpointRequest(t *testing.T, h *pinpoint.Handler, method, path string, b
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -96,7 +98,7 @@ func createTestApp(t *testing.T, h *pinpoint.Handler, name string) string {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
id, _ := resp["Id"].(string)
require.NotEmpty(t, id)
@@ -109,7 +111,7 @@ func pinpointJSON(t *testing.T, body []byte) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(body, &m))
+ require.NoError(t, json.Unmarshal(body, &m, jsonx.V1()))
return m
}
@@ -563,7 +565,7 @@ func TestHandler_ErrorPaths(t *testing.T) {
bodyBytes = []byte(s)
} else if tt.body != nil {
var err error
- bodyBytes, err = json.Marshal(tt.body)
+ bodyBytes, err = json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
}
diff --git a/services/pinpoint/journeys_activities_test.go b/services/pinpoint/journeys_activities_test.go
index b20588ac00..35753a4636 100644
--- a/services/pinpoint/journeys_activities_test.go
+++ b/services/pinpoint/journeys_activities_test.go
@@ -1,12 +1,14 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestJourney_StateMachine(t *testing.T) {
@@ -74,7 +76,7 @@ func TestJourney_StateMachine(t *testing.T) {
map[string]any{"Name": "sm-journey"})
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
journeyID := cr["Id"].(string)
assert.Equal(t, "DRAFT", cr["State"])
@@ -100,7 +102,7 @@ func TestJourney_StateMachine(t *testing.T) {
"/v1/apps/"+appID+"/journeys/"+journeyID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var j map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j, jsonx.V1()))
assert.Equal(t, tc.wantFinal, j["State"])
}
})
@@ -126,7 +128,7 @@ func TestJourney_ListByState(t *testing.T) {
map[string]any{"Name": name})
require.Equal(t, http.StatusCreated, rec.Code)
var j map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &j))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &j, jsonx.V1()))
journeyIDs[i] = j["Id"].(string)
}
@@ -147,7 +149,7 @@ func TestJourney_ListByState(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
items, _ := listResp["Item"].([]any)
assert.Len(t, items, 3)
@@ -321,7 +323,7 @@ func TestJourney_Activities(t *testing.T) {
"body: %s", createRec.Body.String())
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
journeyID := cr["Id"].(string)
getRec := doPinpointRequest(t, h, http.MethodGet,
@@ -329,7 +331,7 @@ func TestJourney_Activities(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var j map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j, jsonx.V1()))
activities, _ := j["Activities"].(map[string]any)
assert.NotNil(t, activities, "Activities must be present")
@@ -369,7 +371,7 @@ func TestJourney_DateRangeKpi(t *testing.T) {
map[string]any{"Name": "kpi-journey"})
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
journeyID := cr["Id"].(string)
kpiRec := doPinpointRequest(t, h, http.MethodGet,
@@ -377,7 +379,7 @@ func TestJourney_DateRangeKpi(t *testing.T) {
require.Equal(t, http.StatusOK, kpiRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(kpiRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(kpiRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, appID, resp["ApplicationId"])
assert.Equal(t, journeyID, resp["JourneyId"])
assert.Equal(t, tc.kpiName, resp["KpiName"])
@@ -421,7 +423,7 @@ func TestJourney_RunsAfterActivation(t *testing.T) {
map[string]any{"Name": "runs-journey"})
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
journeyID := cr["Id"].(string)
if tc.activate {
@@ -436,7 +438,7 @@ func TestJourney_RunsAfterActivation(t *testing.T) {
require.Equal(t, http.StatusOK, runsRec.Code)
var runsResp map[string]any
- require.NoError(t, json.Unmarshal(runsRec.Body.Bytes(), &runsResp))
+ require.NoError(t, json.Unmarshal(runsRec.Body.Bytes(), &runsResp, jsonx.V1()))
items, _ := runsResp["Item"].([]any)
assert.Len(t, items, tc.wantRuns)
})
@@ -486,14 +488,14 @@ func TestJourney_LinkedActivityGraph(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
jID := cr["Id"].(string)
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/journeys/"+jID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var j map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j, jsonx.V1()))
assert.Equal(t, "act-wait", j["StartActivity"])
@@ -541,14 +543,14 @@ func TestJourney_QuietTimeAndOpenHours(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
jID := cr["Id"].(string)
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/journeys/"+jID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var j map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j, jsonx.V1()))
qt := j["QuietTime"].(map[string]any)
assert.Equal(t, "21:00", qt["Start"])
@@ -585,14 +587,14 @@ func TestJourney_LimitsRoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
jID := cr["Id"].(string)
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/journeys/"+jID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var j map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j, jsonx.V1()))
got := j["Limits"].(map[string]any)
assert.EqualValues(t, 100, got["DailyCap"])
@@ -645,14 +647,14 @@ func TestJourney_RandomSplitAndHoldout(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
jID := cr["Id"].(string)
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/journeys/"+jID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var j map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j, jsonx.V1()))
acts := j["Activities"].(map[string]any)
holdout := acts["act-holdout"].(map[string]any)["HOLDOUT"].(map[string]any)
@@ -681,7 +683,7 @@ func TestJourney_UpdateActivities(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
jID := cr["Id"].(string)
// Add a second activity by updating the journey
@@ -706,7 +708,7 @@ func TestJourney_UpdateActivities(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var j map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j, jsonx.V1()))
acts := j["Activities"].(map[string]any)
assert.Len(t, acts, 2)
@@ -741,14 +743,14 @@ func TestJourney_RefreshOnSegmentUpdate(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
jID := cr["Id"].(string)
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/journeys/"+jID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var j map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j, jsonx.V1()))
assert.Equal(t, true, j["RefreshOnSegmentUpdate"])
assert.Equal(t, "PT1H", j["RefreshFrequency"])
@@ -779,14 +781,14 @@ func TestJourney_StartCondition(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &cr, jsonx.V1()))
jID := cr["Id"].(string)
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/journeys/"+jID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var j map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &j, jsonx.V1()))
sc := j["StartCondition"].(map[string]any)
assert.Equal(t, "entry from segment", sc["Description"])
diff --git a/services/pinpoint/journeys_test.go b/services/pinpoint/journeys_test.go
index b3f91dcf43..628e44e0ec 100644
--- a/services/pinpoint/journeys_test.go
+++ b/services/pinpoint/journeys_test.go
@@ -1,7 +1,8 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/url"
@@ -12,6 +13,8 @@ import (
pinpointtypes "github.com/aws/aws-sdk-go-v2/service/pinpoint/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestJourneyFullDTO_Create(t *testing.T) {
@@ -146,7 +149,7 @@ func TestJourneyFullDTO_Create(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
rec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps/"+appID+"/journeys", tc.body)
@@ -157,7 +160,7 @@ func TestJourneyFullDTO_Create(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Id"])
assert.Equal(t, appID, resp["ApplicationId"])
@@ -285,14 +288,14 @@ func TestJourneyStateValidation(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
createRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps/"+appID+"/journeys",
map[string]any{"Name": "j"})
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
journeyID := created["Id"].(string)
for _, state := range tc.setupTransitions {
@@ -318,14 +321,14 @@ func TestJourneyUpdate_BlockedWhenActive(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
createRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps/"+appID+"/journeys",
map[string]any{"Name": "j"})
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
journeyID := created["Id"].(string)
// Activate the journey.
@@ -346,7 +349,7 @@ func TestJourneyUpdate_BlockedWhenActive(t *testing.T) {
require.Equal(t, http.StatusConflict, updateRec.Code, updateRec.Body.String())
var errResp map[string]string
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ConflictException", errResp["__type"])
}
@@ -469,14 +472,14 @@ func TestJourneyUpdate_RoundTrip(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
createRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps/"+appID+"/journeys",
map[string]any{"Name": "j"})
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
journeyID := created["Id"].(string)
rec := doPinpointRequest(t, h, http.MethodPut,
@@ -484,7 +487,7 @@ func TestJourneyUpdate_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp[tc.checkField], "%s field must be non-nil after update", tc.checkField)
})
@@ -503,7 +506,7 @@ func TestJourney_MultipleActivities_RoundTrip(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
activities := map[string]any{
@@ -538,7 +541,7 @@ func TestJourney_MultipleActivities_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &resp, jsonx.V1()))
returnedActivities, _ := resp["Activities"].(map[string]any)
assert.Len(t, returnedActivities, 3, "all 3 activities round-trip")
@@ -565,7 +568,7 @@ func TestPagination_Journeys_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, p1Rec.Code)
var p1 map[string]any
- require.NoError(t, json.Unmarshal(p1Rec.Body.Bytes(), &p1))
+ require.NoError(t, json.Unmarshal(p1Rec.Body.Bytes(), &p1, jsonx.V1()))
items1, _ := p1["Item"].([]any)
assert.Len(t, items1, 2)
@@ -576,7 +579,7 @@ func TestPagination_Journeys_NextToken(t *testing.T) {
p2Rec := doPinpointRequest(t, h, http.MethodGet,
"/v1/apps/"+appID+"/journeys?page-size=2&token="+tok, nil)
var p2 map[string]any
- require.NoError(t, json.Unmarshal(p2Rec.Body.Bytes(), &p2))
+ require.NoError(t, json.Unmarshal(p2Rec.Body.Bytes(), &p2, jsonx.V1()))
items2, _ := p2["Item"].([]any)
assert.Len(t, items2, 1, "last page returns remaining journey")
@@ -599,7 +602,7 @@ func TestJourneyCRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
journeyID, _ := createResp["Id"].(string)
require.NotEmpty(t, journeyID)
@@ -695,7 +698,7 @@ func TestJourneyARNPresent(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotContains(t, resp, "Arn")
}
@@ -737,7 +740,7 @@ func TestHandler_CreateJourney(t *testing.T) {
if tt.wantID {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Id"])
assert.Equal(t, appID, resp["ApplicationId"])
assert.Equal(t, "DRAFT", resp["State"])
@@ -770,7 +773,7 @@ func TestHandler_GetJourneys_DuplicateNames_NoDropOrDupAcrossPages(t *testing.T)
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
created[resp["Id"].(string)] = true
}
@@ -782,7 +785,7 @@ func TestHandler_GetJourneys_DuplicateNames_NoDropOrDupAcrossPages(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
for _, item := range items {
diff --git a/services/pinpoint/messages_test.go b/services/pinpoint/messages_test.go
index cc729048d2..29062bc1cc 100644
--- a/services/pinpoint/messages_test.go
+++ b/services/pinpoint/messages_test.go
@@ -1,13 +1,14 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pinpoint"
)
@@ -91,7 +92,7 @@ func TestOTP_SendAndVerify(t *testing.T) {
"verify body: %s", verifyRec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(verifyRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(verifyRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "Valid")
}
})
@@ -135,7 +136,7 @@ func TestOTP_SendAndVerify_CodeMatch(t *testing.T) {
require.Equal(t, http.StatusOK, sendRec.Code)
var sendResp map[string]any
- require.NoError(t, json.Unmarshal(sendRec.Body.Bytes(), &sendResp))
+ require.NoError(t, json.Unmarshal(sendRec.Body.Bytes(), &sendResp, jsonx.V1()))
results, _ := sendResp["Result"].(map[string]any)
assert.NotEmpty(t, results, "SendOTP should return results")
@@ -146,7 +147,7 @@ func TestOTP_SendAndVerify_CodeMatch(t *testing.T) {
require.Equal(t, http.StatusOK, verifyRec.Code)
var verifyResp map[string]any
- require.NoError(t, json.Unmarshal(verifyRec.Body.Bytes(), &verifyResp))
+ require.NoError(t, json.Unmarshal(verifyRec.Body.Bytes(), &verifyResp, jsonx.V1()))
assert.Equal(t, tc.wantValid, verifyResp["Valid"], "Valid field")
})
}
@@ -181,7 +182,7 @@ func TestOTP_CodeMatch_CorrectCode(t *testing.T) {
require.Equal(t, http.StatusOK, verifyNoCode.Code)
var noCodeResp map[string]any
- require.NoError(t, json.Unmarshal(verifyNoCode.Body.Bytes(), &noCodeResp))
+ require.NoError(t, json.Unmarshal(verifyNoCode.Body.Bytes(), &noCodeResp, jsonx.V1()))
assert.True(t, noCodeResp["Valid"].(bool), "empty code falls back to has-pending-OTP check")
}
@@ -204,7 +205,7 @@ func TestOTP_CodeMatch_WrongCode(t *testing.T) {
require.Equal(t, http.StatusOK, verifyRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(verifyRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(verifyRec.Body.Bytes(), &resp, jsonx.V1()))
assert.False(t, resp["Valid"].(bool), "wrong OTP code must return Valid=false")
}
@@ -222,7 +223,7 @@ func TestOTP_CodeMatch_NoOTPSent(t *testing.T) {
require.Equal(t, http.StatusOK, verifyRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(verifyRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(verifyRec.Body.Bytes(), &resp, jsonx.V1()))
assert.False(t, resp["Valid"].(bool), "no OTP sent → Valid=false")
}
@@ -275,7 +276,7 @@ func TestSendUsersMessages_WithEndpoints(t *testing.T) {
require.Equal(t, http.StatusOK, sendRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(sendRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(sendRec.Body.Bytes(), &resp, jsonx.V1()))
result, _ := resp["Result"].(map[string]any)
require.NotNil(t, result)
@@ -314,7 +315,7 @@ func TestSendUsersMessages_UnknownUser(t *testing.T) {
require.Equal(t, http.StatusOK, sendRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(sendRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(sendRec.Body.Bytes(), &resp, jsonx.V1()))
result, _ := resp["Result"].(map[string]any)
require.NotNil(t, result)
@@ -335,7 +336,7 @@ func TestSendUsersMessages_EmptyBody(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
result, _ := resp["Result"].(map[string]any)
assert.Empty(t, result, "no users in request → empty result")
}
@@ -402,7 +403,7 @@ func TestPhoneNumberValidate_E164Input(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, wrapped := resp["NumberValidateResponse"]
assert.False(t, wrapped, "NumberValidateResponse must not appear as a wrapper key")
@@ -438,7 +439,7 @@ func TestPhoneNumberValidate_UnknownCountry(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
inner := resp
assert.Equal(t, "ZZ", inner["CountryCodeIso2"], "unknown country returns ZZ")
@@ -459,7 +460,7 @@ func TestPhoneNumberValidate_NationalFormat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
inner := resp
assert.Equal(t, "+12125551234", inner["CleansedPhoneNumberE164"],
@@ -479,7 +480,7 @@ func TestPhoneNumberValidate_PhoneTypeAndCarrier(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
inner := resp
assert.NotEmpty(t, inner["PhoneType"], "PhoneType must be set")
@@ -514,7 +515,7 @@ func TestSendMessages_ResponseFlat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, wrapped := resp["MessageResponse"]
assert.False(t, wrapped, "MessageResponse must not appear as a wrapper key")
@@ -562,7 +563,7 @@ func createTestInAppCampaign(
require.Equal(t, http.StatusCreated, campRec.Code, campRec.Body.String())
var camp map[string]any
- require.NoError(t, json.Unmarshal(campRec.Body.Bytes(), &camp))
+ require.NoError(t, json.Unmarshal(campRec.Body.Bytes(), &camp, jsonx.V1()))
campaignID, _ := camp["Id"].(string)
require.NotEmpty(t, campaignID)
@@ -589,7 +590,7 @@ func TestGetInAppMessages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
campaigns, ok := resp["InAppMessageCampaigns"].([]any)
require.True(t, ok, "InAppMessageCampaigns must be present and be an array")
assert.Empty(t, campaigns)
@@ -607,7 +608,7 @@ func TestGetInAppMessages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
campaigns, ok := resp["InAppMessageCampaigns"].([]any)
require.True(t, ok)
require.Len(t, campaigns, 1)
@@ -641,7 +642,7 @@ func TestGetInAppMessages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
campaigns, ok := resp["InAppMessageCampaigns"].([]any)
require.True(t, ok)
assert.Empty(t, campaigns, "a paused campaign doesn't run, per real AWS's IsPaused semantics")
@@ -657,7 +658,7 @@ func TestGetInAppMessages(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "NotFoundException", resp["__type"])
})
}
diff --git a/services/pinpoint/multi_region_test.go b/services/pinpoint/multi_region_test.go
index 9e449aa507..b413eb2aef 100644
--- a/services/pinpoint/multi_region_test.go
+++ b/services/pinpoint/multi_region_test.go
@@ -2,7 +2,8 @@ package pinpoint_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/pinpoint"
)
@@ -184,8 +186,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/pinpoint/payload_size.go b/services/pinpoint/payload_size.go
index 647c1f0c1d..b396a5dee2 100644
--- a/services/pinpoint/payload_size.go
+++ b/services/pinpoint/payload_size.go
@@ -1,10 +1,13 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Payload size ceilings sourced from
@@ -54,10 +57,10 @@ func checkPayloadSize(c *echo.Context, body []byte, limit int) bool {
func putEventsExceedsEventQuota(body []byte) bool {
var raw struct {
BatchItem map[string]struct {
- Events map[string]json.RawMessage `json:"Events"`
+ Events map[string]jsontext.Value `json:"Events"`
} `json:"BatchItem"`
}
- if json.Unmarshal(body, &raw) != nil {
+ if json.Unmarshal(body, &raw, jsonx.V1()) != nil {
return false
}
diff --git a/services/pinpoint/payload_size_test.go b/services/pinpoint/payload_size_test.go
index 4ef39a6aa7..feffeb13d8 100644
--- a/services/pinpoint/payload_size_test.go
+++ b/services/pinpoint/payload_size_test.go
@@ -1,12 +1,15 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Mirrors the unexported limits in payload_size.go (7 MB / 4 MB / 15 KB from
@@ -28,7 +31,7 @@ func jsonBodyOfSize(t *testing.T, prefix, suffix string, targetLen int) []byte {
body := prefix + strings.Repeat("a", padLen) + suffix
require.Len(t, body, targetLen)
- require.True(t, json.Valid([]byte(body)), "constructed body must be valid JSON")
+ require.True(t, jsontext.Value([]byte(body)).IsValid(jsonx.V1()), "constructed body must be valid JSON")
return []byte(body)
}
@@ -37,7 +40,7 @@ func requirePayloadTooLarge(t *testing.T, body []byte) {
t.Helper()
var envelope map[string]any
- require.NoError(t, json.Unmarshal(body, &envelope))
+ require.NoError(t, json.Unmarshal(body, &envelope, jsonx.V1()))
require.Equal(t, "PayloadTooLargeException", envelope["__type"])
}
diff --git a/services/pinpoint/persistence.go b/services/pinpoint/persistence.go
index fcb72e06c7..1998caeab8 100644
--- a/services/pinpoint/persistence.go
+++ b/services/pinpoint/persistence.go
@@ -2,7 +2,7 @@ package pinpoint
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -48,21 +48,21 @@ const pinpointSnapshotVersion = 2
// plain JSON-friendly struct with no live/non-serialisable state, so a
// direct field-for-field mapping (no separate DTO type) is sufficient.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AppSettings map[string]*StoredAppSettings `json:"appSettings"`
- CampaignVersions map[string][]*Campaign `json:"campaignVersions"`
- SegmentVersions map[string][]*Segment `json:"segmentVersions"`
- TemplateVersionHistory map[string][]templateVersionItem `json:"templateVersionHistory"`
- TemplateVersionData map[string]map[string]json.RawMessage `json:"templateVersionData"`
- TemplateActiveVersion map[string]string `json:"templateActiveVersion"`
- CampaignActivities map[string][]campaignActivity `json:"campaignActivities"`
- JourneyRuns map[string][]*journeyRun `json:"journeyRuns"`
- AppEvents map[string][]storedPinpointEvent `json:"appEvents"`
- SentMessages map[string]int `json:"sentMessages"`
- OtpCodes map[string]string `json:"otpCodes"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AppSettings map[string]*StoredAppSettings `json:"appSettings"`
+ CampaignVersions map[string][]*Campaign `json:"campaignVersions"`
+ SegmentVersions map[string][]*Segment `json:"segmentVersions"`
+ TemplateVersionHistory map[string][]templateVersionItem `json:"templateVersionHistory"`
+ TemplateVersionData map[string]map[string]jsontext.Value `json:"templateVersionData"`
+ TemplateActiveVersion map[string]string `json:"templateActiveVersion"`
+ CampaignActivities map[string][]campaignActivity `json:"campaignActivities"`
+ JourneyRuns map[string][]*journeyRun `json:"journeyRuns"`
+ AppEvents map[string][]storedPinpointEvent `json:"appEvents"`
+ SentMessages map[string]int `json:"sentMessages"`
+ OtpCodes map[string]string `json:"otpCodes"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// persistRegistry builds an ephemeral [store.Registry] over every
@@ -183,7 +183,7 @@ func (b *InMemoryBackend) resetMapStateLocked() {
b.campaignVersions = make(map[string][]*Campaign)
b.segmentVersions = make(map[string][]*Segment)
b.templateVersionHistory = make(map[string][]templateVersionItem)
- b.templateVersionData = make(map[string]map[string]json.RawMessage)
+ b.templateVersionData = make(map[string]map[string]jsontext.Value)
b.templateActiveVersion = make(map[string]string)
b.campaignActivities = make(map[string][]campaignActivity)
b.journeyRuns = make(map[string][]*journeyRun)
@@ -327,9 +327,9 @@ func rebuildARNIndexLocked(b *InMemoryBackend) {
}
}
-func nonNilStringMap2(m map[string]map[string]json.RawMessage) map[string]map[string]json.RawMessage {
+func nonNilStringMap2(m map[string]map[string]jsontext.Value) map[string]map[string]jsontext.Value {
if m == nil {
- return make(map[string]map[string]json.RawMessage)
+ return make(map[string]map[string]jsontext.Value)
}
return m
diff --git a/services/pinpoint/recommender_configurations_test.go b/services/pinpoint/recommender_configurations_test.go
index 84cc1fc386..2b68006141 100644
--- a/services/pinpoint/recommender_configurations_test.go
+++ b/services/pinpoint/recommender_configurations_test.go
@@ -1,7 +1,8 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pinpoint"
)
@@ -282,7 +284,7 @@ func TestRecommenderConfiguration_FullCRUD(t *testing.T) {
"body: %s", createRec.Body.String())
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
recID := cr["Id"].(string)
require.NotEmpty(t, recID)
@@ -313,7 +315,7 @@ func TestRecommenderConfiguration_FullCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var gr map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &gr, jsonx.V1()))
assert.Equal(t, recID, gr["Id"])
updateRec := doPinpointRequest(t, h, http.MethodPut,
@@ -368,7 +370,7 @@ func TestRecommenderConfiguration_ListAll(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
assert.Len(t, items, tc.wantLen)
})
@@ -520,14 +522,14 @@ func TestRecommender_Attributes_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
recID := cr["Id"].(string)
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/recommenders/"+recID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var r map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &r))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &r, jsonx.V1()))
assert.Equal(t, "attr-recommender", r["Name"])
assert.EqualValues(t, 5, r["RecommendationsPerMessage"])
@@ -567,14 +569,14 @@ func TestRecommender_RecommendationsPerMessage(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
recID := cr["Id"].(string)
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/recommenders/"+recID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var r map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &r))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &r, jsonx.V1()))
assert.EqualValues(t, tc.recommendationsPerMsg, r["RecommendationsPerMessage"])
})
@@ -600,7 +602,7 @@ func TestRecommender_UpdateAttributes(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
recID := cr["Id"].(string)
putRec := doPinpointRequest(t, h, http.MethodPut, "/v1/recommenders/"+recID,
@@ -621,7 +623,7 @@ func TestRecommender_UpdateAttributes(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var r map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &r))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &r, jsonx.V1()))
assert.EqualValues(t, 7, r["RecommendationsPerMessage"])
gotAttrs := r["Attributes"].(map[string]any)
@@ -650,7 +652,7 @@ func TestRecommender_List_MultipleConfigs(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["Item"].([]any)
assert.Len(t, items, 3)
@@ -762,7 +764,7 @@ func TestHandler_CreateRecommenderConfiguration(t *testing.T) {
if tt.wantID {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Id"])
assert.Equal(t, "my-recommender", resp["Name"])
}
diff --git a/services/pinpoint/segments_test.go b/services/pinpoint/segments_test.go
index 039ed7e7a0..47fdaf9107 100644
--- a/services/pinpoint/segments_test.go
+++ b/services/pinpoint/segments_test.go
@@ -1,7 +1,8 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/url"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pinpoint"
)
@@ -23,7 +25,7 @@ func createTestSegment(t *testing.T, h *pinpoint.Handler, appID, name string) st
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
id, _ := resp["Id"].(string)
require.NotEmpty(t, id)
@@ -134,7 +136,7 @@ func TestSegmentFullDTO_Create(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
rec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps/"+appID+"/segments", tc.body)
@@ -145,7 +147,7 @@ func TestSegmentFullDTO_Create(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantSegmentType, resp["SegmentType"])
assert.NotEmpty(t, resp["Id"])
@@ -223,21 +225,21 @@ func TestSegmentUpdate_DimensionsRoundTrip(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
createRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps/"+appID+"/segments",
map[string]any{"Name": "seg"})
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
segID := created["Id"].(string)
rec := doPinpointRequest(t, h, http.MethodPut, "/v1/apps/"+appID+"/segments/"+segID, tc.updateBody)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantSegmentType, resp["SegmentType"])
assert.InDelta(t, tc.wantVersion, resp["Version"], 0.001)
@@ -258,14 +260,14 @@ func TestSegmentVersioning(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
createRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps/"+appID+"/segments",
map[string]any{"Name": "versioned-seg"})
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
segID := created["Id"].(string)
// 2 updates.
@@ -278,7 +280,7 @@ func TestSegmentVersioning(t *testing.T) {
verRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/segments/"+segID+"/versions", nil)
require.Equal(t, http.StatusOK, verRec.Code)
var verResp map[string]any
- require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &verResp))
+ require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &verResp, jsonx.V1()))
items, _ := verResp["Item"].([]any)
assert.Len(t, items, 3, "3 versions: 1 create + 2 updates")
}
@@ -295,14 +297,14 @@ func TestSegment_VersionRetrieval(t *testing.T) {
appRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps", map[string]any{"Name": "app"})
require.Equal(t, http.StatusCreated, appRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(appRec.Body.Bytes(), &appResp, jsonx.V1()))
appID := appResp["Id"].(string)
createRec := doPinpointRequest(t, h, http.MethodPost, "/v1/apps/"+appID+"/segments",
map[string]any{"Name": "seg-v"})
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
segID := created["Id"].(string)
updateRec := doPinpointRequest(t, h, http.MethodPut, "/v1/apps/"+appID+"/segments/"+segID,
@@ -314,7 +316,7 @@ func TestSegment_VersionRetrieval(t *testing.T) {
"/v1/apps/"+appID+"/segments/"+segID+"/versions/1", nil)
require.Equal(t, http.StatusOK, v1Rec.Code)
var v1 map[string]any
- require.NoError(t, json.Unmarshal(v1Rec.Body.Bytes(), &v1))
+ require.NoError(t, json.Unmarshal(v1Rec.Body.Bytes(), &v1, jsonx.V1()))
assert.InDelta(t, float64(1), v1["Version"], 0.001)
assert.Equal(t, "seg-v", v1["Name"])
}
@@ -366,7 +368,7 @@ func TestSegmentVersions_Deeper(t *testing.T) {
})
require.Equal(t, http.StatusCreated, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
segID := created["Id"].(string)
for i := range tc.updateCount {
@@ -381,7 +383,7 @@ func TestSegmentVersions_Deeper(t *testing.T) {
require.Equal(t, http.StatusOK, versionsRec.Code)
var versionsResp map[string]any
- require.NoError(t, json.Unmarshal(versionsRec.Body.Bytes(), &versionsResp))
+ require.NoError(t, json.Unmarshal(versionsRec.Body.Bytes(), &versionsResp, jsonx.V1()))
items, _ := versionsResp["Item"].([]any)
assert.Len(t, items, tc.wantVersions, "wrong version count")
@@ -390,7 +392,7 @@ func TestSegmentVersions_Deeper(t *testing.T) {
require.Equal(t, http.StatusOK, vRec.Code)
var vResp map[string]any
- require.NoError(t, json.Unmarshal(vRec.Body.Bytes(), &vResp))
+ require.NoError(t, json.Unmarshal(vRec.Body.Bytes(), &vResp, jsonx.V1()))
assert.InDelta(t, float64(tc.fetchVersion), vResp["Version"], 0.001)
})
}
@@ -435,7 +437,7 @@ func TestSegmentJobsDeeper(t *testing.T) {
map[string]any{"Name": "seg-for-jobs"})
require.Equal(t, http.StatusCreated, segRec.Code)
var segResp map[string]any
- require.NoError(t, json.Unmarshal(segRec.Body.Bytes(), &segResp))
+ require.NoError(t, json.Unmarshal(segRec.Body.Bytes(), &segResp, jsonx.V1()))
segID := segResp["Id"].(string)
if tc.jobType == "export" {
@@ -462,7 +464,7 @@ func TestSegmentJobsDeeper(t *testing.T) {
require.Equal(t, http.StatusOK, jobsRec.Code)
var jobsResp map[string]any
- require.NoError(t, json.Unmarshal(jobsRec.Body.Bytes(), &jobsResp))
+ require.NoError(t, json.Unmarshal(jobsRec.Body.Bytes(), &jobsResp, jsonx.V1()))
assert.Contains(t, jobsResp, "Item")
})
}
@@ -522,7 +524,7 @@ func TestSegment_ImportType(t *testing.T) {
require.Equal(t, http.StatusCreated, jobRec.Code)
var jr map[string]any
- require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &jr))
+ require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &jr, jsonx.V1()))
definition := jr["Definition"].(map[string]any)
segID := definition["SegmentId"].(string)
require.NotEmpty(t, segID)
@@ -532,7 +534,7 @@ func TestSegment_ImportType(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var s map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &s))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &s, jsonx.V1()))
assert.Equal(t, "IMPORT", s["SegmentType"])
@@ -577,7 +579,7 @@ func TestSegment_DynamicWithSegmentGroups(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
// No ImportDefinition → DIMENSIONAL type
assert.Equal(t, "DIMENSIONAL", cr["SegmentType"])
@@ -588,7 +590,7 @@ func TestSegment_DynamicWithSegmentGroups(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var s map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &s))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &s, jsonx.V1()))
assert.Equal(t, "DIMENSIONAL", s["SegmentType"])
@@ -639,14 +641,14 @@ func TestSegment_DimensionsAttributes(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
segID := cr["Id"].(string)
getRec := doPinpointRequest(t, h, http.MethodGet, "/v1/apps/"+appID+"/segments/"+segID, nil)
require.Equal(t, http.StatusOK, getRec.Code)
var s map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &s))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &s, jsonx.V1()))
d := s["Dimensions"].(map[string]any)
@@ -685,7 +687,7 @@ func TestSegment_UpdatePreservesType(t *testing.T) {
require.Equal(t, http.StatusCreated, jobRec.Code)
var jr map[string]any
- require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &jr))
+ require.NoError(t, json.Unmarshal(jobRec.Body.Bytes(), &jr, jsonx.V1()))
segID := jr["Definition"].(map[string]any)["SegmentId"].(string)
require.NotEmpty(t, segID)
@@ -695,7 +697,7 @@ func TestSegment_UpdatePreservesType(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var ur map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &ur))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &ur, jsonx.V1()))
assert.Equal(t, "IMPORT", ur["SegmentType"])
assert.Equal(t, "type-preserve-seg-v2", ur["Name"])
@@ -739,7 +741,7 @@ func TestSegment_SegmentGroups_UpdateReplaces(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
segID := cr["Id"].(string)
v2Groups := map[string]any{
@@ -770,7 +772,7 @@ func TestSegment_SegmentGroups_UpdateReplaces(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var s map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &s))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &s, jsonx.V1()))
sg := s["SegmentGroups"].(map[string]any)
assert.Equal(t, "ANY", sg["Include"])
@@ -797,7 +799,7 @@ func TestPagination_Segments_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, p1Rec.Code)
var p1 map[string]any
- require.NoError(t, json.Unmarshal(p1Rec.Body.Bytes(), &p1))
+ require.NoError(t, json.Unmarshal(p1Rec.Body.Bytes(), &p1, jsonx.V1()))
items1, _ := p1["Item"].([]any)
assert.Len(t, items1, 2)
@@ -807,7 +809,7 @@ func TestPagination_Segments_NextToken(t *testing.T) {
p2Rec := doPinpointRequest(t, h, http.MethodGet,
"/v1/apps/"+appID+"/segments?page-size=2&token="+tok, nil)
var p2 map[string]any
- require.NoError(t, json.Unmarshal(p2Rec.Body.Bytes(), &p2))
+ require.NoError(t, json.Unmarshal(p2Rec.Body.Bytes(), &p2, jsonx.V1()))
items2, _ := p2["Item"].([]any)
assert.Len(t, items2, 2)
@@ -828,7 +830,7 @@ func TestSegmentCRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
segmentID, _ := createResp["Id"].(string)
require.NotEmpty(t, segmentID)
@@ -910,7 +912,7 @@ func TestHandler_CreateSegment(t *testing.T) {
if tt.wantID {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Id"])
assert.NotEmpty(t, resp["Arn"])
assert.Equal(t, appID, resp["ApplicationId"])
@@ -945,7 +947,7 @@ func TestGetSegmentVersion_UnknownVersionNotFound(t *testing.T) {
assert.Equal(t, http.StatusNotFound, missingRec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(missingRec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(missingRec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "NotFoundException", errResp["__type"])
}
@@ -973,7 +975,7 @@ func TestHandler_GetSegments_DuplicateNames_NoDropOrDupAcrossPages(t *testing.T)
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
created[resp["Id"].(string)] = true
}
@@ -985,7 +987,7 @@ func TestHandler_GetSegments_DuplicateNames_NoDropOrDupAcrossPages(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
for _, item := range items {
diff --git a/services/pinpoint/store.go b/services/pinpoint/store.go
index e1f47359c2..46648f6e8a 100644
--- a/services/pinpoint/store.go
+++ b/services/pinpoint/store.go
@@ -1,7 +1,7 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/jsontext"
"maps"
"strings"
"time"
@@ -33,7 +33,7 @@ type InMemoryBackend struct {
campaignVersions map[string][]*Campaign
segmentVersions map[string][]*Segment
templateVersionHistory map[string][]templateVersionItem
- templateVersionData map[string]map[string]json.RawMessage
+ templateVersionData map[string]map[string]jsontext.Value
templateActiveVersion map[string]string
campaignActivities map[string][]campaignActivity
journeyRuns map[string][]*journeyRun
@@ -58,7 +58,7 @@ func NewInMemoryBackend(region, accountID string) *InMemoryBackend {
campaignVersions: make(map[string][]*Campaign),
segmentVersions: make(map[string][]*Segment),
templateVersionHistory: make(map[string][]templateVersionItem),
- templateVersionData: make(map[string]map[string]json.RawMessage),
+ templateVersionData: make(map[string]map[string]jsontext.Value),
templateActiveVersion: make(map[string]string),
campaignActivities: make(map[string][]campaignActivity),
journeyRuns: make(map[string][]*journeyRun),
@@ -84,7 +84,7 @@ func (b *InMemoryBackend) Reset() {
b.campaignVersions = make(map[string][]*Campaign)
b.segmentVersions = make(map[string][]*Segment)
b.templateVersionHistory = make(map[string][]templateVersionItem)
- b.templateVersionData = make(map[string]map[string]json.RawMessage)
+ b.templateVersionData = make(map[string]map[string]jsontext.Value)
b.templateActiveVersion = make(map[string]string)
b.campaignActivities = make(map[string][]campaignActivity)
b.journeyRuns = make(map[string][]*journeyRun)
diff --git a/services/pinpoint/tags_test.go b/services/pinpoint/tags_test.go
index 3e38a54a26..60d3f4615d 100644
--- a/services/pinpoint/tags_test.go
+++ b/services/pinpoint/tags_test.go
@@ -1,7 +1,8 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/url"
"strings"
@@ -9,6 +10,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// pinpointEncodeARN URL-encodes an ARN for use in URL paths.
@@ -66,7 +69,7 @@ func TestUntagResource_EmptyTagKeys(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &appResp, jsonx.V1()))
arn, _ := appResp["Arn"].(string)
require.NotEmpty(t, arn, "app ARN must be present")
@@ -121,7 +124,7 @@ func TestUntagResource_RemovesSpecificTags(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var appResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &appResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &appResp, jsonx.V1()))
arn, _ := appResp["Arn"].(string)
encodedARN := pinpointEncodeARN(arn)
@@ -142,7 +145,7 @@ func TestUntagResource_RemovesSpecificTags(t *testing.T) {
require.Equal(t, http.StatusOK, listTagRec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(listTagRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(listTagRec.Body.Bytes(), &tagsResp, jsonx.V1()))
remaining, _ := tagsResp["tags"].(map[string]any)
@@ -205,7 +208,7 @@ func TestTagCampaignViaARN(t *testing.T) {
require.Equal(t, http.StatusCreated, recC.Code)
var cResp map[string]any
- require.NoError(t, json.NewDecoder(recC.Body).Decode(&cResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(recC.Body), &cResp, jsonx.V1()))
campaignARN, _ := cResp["Arn"].(string)
require.NotEmpty(t, campaignARN)
@@ -219,7 +222,7 @@ func TestTagCampaignViaARN(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec.Code)
var tagsBody map[string]any
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&tagsBody))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &tagsBody, jsonx.V1()))
tagsMap, _ := tagsBody["tags"].(map[string]any)
assert.Equal(t, "42", tagsMap["cost-center"])
}
@@ -235,7 +238,7 @@ func TestTagJourneyViaARN(t *testing.T) {
require.Equal(t, http.StatusCreated, recJ.Code)
var jResp map[string]any
- require.NoError(t, json.NewDecoder(recJ.Body).Decode(&jResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(recJ.Body), &jResp, jsonx.V1()))
assert.NotContains(t, jResp, "Arn")
journeyARN := "arn:aws:mobiletargeting:" + testRegion + ":" + testAccountID +
@@ -260,7 +263,7 @@ func TestTagSegmentViaARN(t *testing.T) {
require.Equal(t, http.StatusCreated, recS.Code)
var sResp map[string]any
- require.NoError(t, json.NewDecoder(recS.Body).Decode(&sResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(recS.Body), &sResp, jsonx.V1()))
segmentARN, _ := sResp["Arn"].(string)
require.NotEmpty(t, segmentARN)
@@ -284,7 +287,7 @@ func TestTagEmailTemplateViaARN(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var tResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&tResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &tResp, jsonx.V1()))
templateARN, _ := tResp["Arn"].(string)
require.NotEmpty(t, templateARN)
@@ -298,7 +301,7 @@ func TestTagEmailTemplateViaARN(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec.Code)
var listBody map[string]any
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&listBody))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &listBody, jsonx.V1()))
tagsMap, _ := listBody["tags"].(map[string]any)
assert.Equal(t, "email", tagsMap["type"])
@@ -334,7 +337,7 @@ func TestHandler_TagOperations(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var appResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&appResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &appResp, jsonx.V1()))
appARN, _ := appResp["Arn"].(string)
require.NotEmpty(t, appARN)
@@ -353,7 +356,7 @@ func TestHandler_TagOperations(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec.Code)
var tagsResp map[string]any
- require.NoError(t, json.NewDecoder(listRec.Body).Decode(&tagsResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(listRec.Body), &tagsResp, jsonx.V1()))
tagsMap, _ := tagsResp["tags"].(map[string]any)
assert.Equal(t, "integration", tagsMap["owner"])
diff --git a/services/pinpoint/template_versions.go b/services/pinpoint/template_versions.go
index aca47d1d7b..a99ef33f25 100644
--- a/services/pinpoint/template_versions.go
+++ b/services/pinpoint/template_versions.go
@@ -1,10 +1,13 @@
package pinpoint
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const latestTemplateVersion = "latest"
@@ -49,13 +52,13 @@ func (b *InMemoryBackend) nextTemplateVersionLocked(
return current, nil
}
- raw, err := json.Marshal(live)
+ raw, err := json.Marshal(live, jsonx.V1())
if err != nil {
return "", fmt.Errorf("snapshot template version: %w", err)
}
if b.templateVersionData[key] == nil {
- b.templateVersionData[key] = make(map[string]json.RawMessage)
+ b.templateVersionData[key] = make(map[string]jsontext.Value)
}
b.templateVersionData[key][current] = raw
@@ -137,7 +140,7 @@ func pickTemplateVersion[T any](
}
var out T
- if err := json.Unmarshal(raw, &out); err != nil {
+ if err := json.Unmarshal(raw, &out, jsonx.V1()); err != nil {
return nil, false, fmt.Errorf("decode template version: %w", err)
}
@@ -345,7 +348,7 @@ func unlockedDelete[T any](
}
// restoreLatestLocked rewinds the live template to an older snapshot, keeping its tags.
-func (b *InMemoryBackend) restoreLatestLocked(name, templateType string, raw json.RawMessage) error {
+func (b *InMemoryBackend) restoreLatestLocked(name, templateType string, raw jsontext.Value) error {
switch strings.ToLower(templateType) {
case templateTypeEmail:
t, _ := b.emailTemplates.Get(name)
@@ -372,9 +375,9 @@ func (b *InMemoryBackend) restoreLatestLocked(name, templateType string, raw jso
return nil
}
-func restoreTemplate[T any](dst *T, raw json.RawMessage, tags func(*T) *map[string]string) error {
+func restoreTemplate[T any](dst *T, raw jsontext.Value, tags func(*T) *map[string]string) error {
var fresh T
- if err := json.Unmarshal(raw, &fresh); err != nil {
+ if err := json.Unmarshal(raw, &fresh, jsonx.V1()); err != nil {
return fmt.Errorf("restore template version: %w", err)
}
@@ -384,7 +387,7 @@ func restoreTemplate[T any](dst *T, raw json.RawMessage, tags func(*T) *map[stri
return nil
}
-func (b *InMemoryBackend) liveTemplateJSONLocked(name, templateType string) (string, json.RawMessage, bool) {
+func (b *InMemoryBackend) liveTemplateJSONLocked(name, templateType string) (string, jsontext.Value, bool) {
var (
version string
live any
@@ -417,7 +420,7 @@ func (b *InMemoryBackend) liveTemplateJSONLocked(name, templateType string) (str
return "", nil, false
}
- raw, err := json.Marshal(live)
+ raw, err := json.Marshal(live, jsonx.V1())
if err != nil {
return "", nil, false
}
@@ -452,7 +455,7 @@ func (b *InMemoryBackend) ListTemplateVersions(
var meta templateVersionMeta
if len(raw) > 0 {
- _ = json.Unmarshal(raw, &meta)
+ _ = json.Unmarshal(raw, &meta, jsonx.V1())
}
cp.CreationDate = meta.CreationDate
diff --git a/services/pinpoint/template_versions_test.go b/services/pinpoint/template_versions_test.go
index b372d01730..d81de1fe50 100644
--- a/services/pinpoint/template_versions_test.go
+++ b/services/pinpoint/template_versions_test.go
@@ -1,19 +1,21 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func decodeBody(t *testing.T, body []byte) map[string]any {
t.Helper()
var got map[string]any
- require.NoError(t, json.Unmarshal(body, &got))
+ require.NoError(t, json.Unmarshal(body, &got, jsonx.V1()))
return got
}
diff --git a/services/pinpoint/templates_create_new_version_test.go b/services/pinpoint/templates_create_new_version_test.go
index 8a7095b0ee..b8467baf1a 100644
--- a/services/pinpoint/templates_create_new_version_test.go
+++ b/services/pinpoint/templates_create_new_version_test.go
@@ -1,12 +1,14 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestUpdateTemplate_CreateNewVersion(t *testing.T) {
@@ -79,14 +81,14 @@ func TestUpdateTemplate_CreateNewVersion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, tt.wantVersion, got["Version"])
rec = doPinpointRequest(t, h, http.MethodGet, path+"/versions", nil)
require.Equal(t, http.StatusOK, rec.Code)
var vers map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &vers))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &vers, jsonx.V1()))
assert.Len(t, vers["Item"], tt.wantVersions)
})
}
diff --git a/services/pinpoint/templates_email_test.go b/services/pinpoint/templates_email_test.go
index 932640f4dd..ded66dce11 100644
--- a/services/pinpoint/templates_email_test.go
+++ b/services/pinpoint/templates_email_test.go
@@ -1,7 +1,8 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pinpoint"
)
@@ -25,7 +27,7 @@ func TestListTemplates_EmailListed(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
require.NotEmpty(t, items)
@@ -140,7 +142,7 @@ func TestEmailTemplate_BodyPersistence(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code, "get status")
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
if tc.wantSubject != "" {
assert.Equal(t, tc.wantSubject, resp["Subject"], "Subject")
@@ -219,7 +221,7 @@ func TestEmailTemplate_VersionBumpsOnUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantVersion, resp["Version"], "Version after updates")
})
}
@@ -281,7 +283,7 @@ func TestEmailTemplate_DefaultSubstitutions(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantSubs, resp["DefaultSubstitutions"], "DefaultSubstitutions")
})
@@ -377,7 +379,7 @@ func TestInAppTemplate_BodyPersistence(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code, "get status")
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
if tc.wantLayout != "" {
assert.Equal(t, tc.wantLayout, resp["Layout"])
@@ -508,7 +510,7 @@ func TestInAppTemplate_ContentStructure(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var tmpl map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl, jsonx.V1()))
assert.Equal(t, "BOTTOM_BANNER", tmpl["Layout"])
assert.Equal(t, "Upgrade promo", tmpl["TemplateDescription"])
@@ -564,7 +566,7 @@ func TestInAppTemplate_MultipleContentItems(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var tmpl map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl, jsonx.V1()))
gotContent := tmpl["Content"].([]any)
require.Len(t, gotContent, 3)
@@ -609,7 +611,7 @@ func TestInAppTemplate_UpdateContent(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var tmpl map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl, jsonx.V1()))
assert.Equal(t, "TOP_BANNER", tmpl["Layout"])
content := tmpl["Content"].([]any)
@@ -730,7 +732,7 @@ func TestHandler_CreateEmailTemplate(t *testing.T) {
if tt.wantARN {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Arn"])
assert.Equal(t, "Created", resp["Message"])
}
@@ -765,7 +767,7 @@ func TestHandler_CreateInAppTemplate(t *testing.T) {
if tt.wantARN {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Arn"])
assert.Equal(t, "Created", resp["Message"])
}
@@ -790,7 +792,7 @@ func TestEmailTemplate_TemplateType(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "EMAIL", resp["TemplateType"])
}
@@ -811,7 +813,7 @@ func TestInAppTemplate_TemplateTypeAndCustomConfig(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "INAPP", resp["TemplateType"])
customConfig, _ := resp["CustomConfig"].(map[string]any)
diff --git a/services/pinpoint/templates_push_test.go b/services/pinpoint/templates_push_test.go
index 7fafd588dc..8ddbc7dd3e 100644
--- a/services/pinpoint/templates_push_test.go
+++ b/services/pinpoint/templates_push_test.go
@@ -1,7 +1,8 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pinpoint"
)
@@ -25,7 +27,7 @@ func TestListTemplates_SMSListed(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
require.NotEmpty(t, items)
@@ -119,7 +121,7 @@ func TestSmsTemplate_BodyPersistence(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code, "get status")
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
if tc.wantBody != "" {
assert.Equal(t, tc.wantBody, resp["Body"], "Body")
@@ -231,7 +233,7 @@ func TestPushTemplate_BodyPersistence(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code, "get status")
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
if tc.wantDescription != "" {
assert.Equal(t, tc.wantDescription, resp["TemplateDescription"])
@@ -339,7 +341,7 @@ func TestPushTemplate_PerPlatformOverrides(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var tmpl map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl, jsonx.V1()))
assert.Equal(t, "PUSH", tmpl["TemplateType"])
assert.Equal(t, "reco-1", tmpl["RecommenderId"])
@@ -388,7 +390,7 @@ func TestPushTemplate_UpdatePerPlatform(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var tmpl map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl, jsonx.V1()))
def := tmpl["Default"].(map[string]any)
assert.Equal(t, "v2 body", def["Body"])
@@ -431,7 +433,7 @@ func TestSMSTemplate_RecommenderID(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var tmpl map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl, jsonx.V1()))
assert.Equal(t, "Hello from {{sender}}", tmpl["Body"])
@@ -458,7 +460,7 @@ func TestSMSTemplate_UpdateRecommenderID(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var tmpl map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl, jsonx.V1()))
assert.Equal(t, "Hello v2", tmpl["Body"])
assert.Equal(t, "reco-new", tmpl["RecommenderId"])
@@ -565,7 +567,7 @@ func TestHandler_CreatePushTemplate(t *testing.T) {
if tt.wantARN {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Arn"])
assert.Equal(t, "Created", resp["Message"])
}
@@ -600,7 +602,7 @@ func TestHandler_CreateSmsTemplate(t *testing.T) {
if tt.wantARN {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Arn"])
assert.Equal(t, "Created", resp["Message"])
}
diff --git a/services/pinpoint/templates_test.go b/services/pinpoint/templates_test.go
index 3c72455b17..db319eaeef 100644
--- a/services/pinpoint/templates_test.go
+++ b/services/pinpoint/templates_test.go
@@ -1,7 +1,7 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/url"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestVoiceTemplate_FullCRUD(t *testing.T) {
@@ -62,7 +64,7 @@ func TestVoiceTemplate_FullCRUD(t *testing.T) {
"body: %s", createRec.Body.String())
var cr map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &cr, jsonx.V1()))
assert.NotEmpty(t, cr["Message"])
getRec := doPinpointRequest(t, h, http.MethodGet,
@@ -70,7 +72,7 @@ func TestVoiceTemplate_FullCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var gr map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &gr, jsonx.V1()))
assert.Equal(t, templateName, gr["TemplateName"])
if tc.wantBody != "SKIP" {
assert.Equal(t, tc.wantBody, gr["Body"])
@@ -118,7 +120,7 @@ func TestVoiceTemplate_FullFieldSet(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var gr map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &gr))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &gr, jsonx.V1()))
assert.Equal(t, templateName, gr["TemplateName"])
assert.Equal(t, "VOICE", gr["TemplateType"])
@@ -139,7 +141,7 @@ func TestVoiceTemplate_FullFieldSet(t *testing.T) {
require.Equal(t, http.StatusOK, getRec2.Code)
var gr2 map[string]any
- require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &gr2))
+ require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &gr2, jsonx.V1()))
assert.Equal(t, "Matthew", gr2["VoiceId"])
assert.Equal(t, "en-GB", gr2["LanguageCode"])
@@ -190,7 +192,7 @@ func TestVoiceTemplate_VersionHistory(t *testing.T) {
require.Equal(t, http.StatusOK, versionsRec.Code)
var versionsResp map[string]any
- require.NoError(t, json.Unmarshal(versionsRec.Body.Bytes(), &versionsResp))
+ require.NoError(t, json.Unmarshal(versionsRec.Body.Bytes(), &versionsResp, jsonx.V1()))
items, _ := versionsResp["Item"].([]any)
assert.Len(t, items, tc.wantVersions)
})
@@ -214,7 +216,7 @@ func TestListTemplates_VoiceListed(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
require.NotEmpty(t, items)
@@ -243,7 +245,7 @@ func TestListTemplates_MixedTypes(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
assert.GreaterOrEqual(t, len(items), 3)
}
@@ -365,7 +367,7 @@ func TestListTemplates_PrefixFilter(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
assert.Len(t, items, tc.wantCount, "Item count with prefix=%q", tc.prefix)
@@ -456,7 +458,7 @@ func TestListTemplates_TypeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
assert.Len(t, items, tc.wantCount,
@@ -545,7 +547,7 @@ func TestListTemplates_PrefixAndTypeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
assert.Len(t, items, tc.wantCount,
@@ -622,7 +624,7 @@ func TestTemplateVersionHistory(t *testing.T) {
require.Equal(t, http.StatusOK, verRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(verRec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
assert.Len(t, items, tc.wantVersions,
@@ -687,7 +689,7 @@ func TestTemplate_LastModifiedDate(t *testing.T) {
require.Equal(t, http.StatusOK, getRec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(getRec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(getRec1.Body.Bytes(), &resp1, jsonx.V1()))
assert.NotEmpty(t, resp1["CreationDate"], "CreationDate should be set")
assert.NotEmpty(t, resp1["LastModifiedDate"], "LastModifiedDate should be set after create")
@@ -701,7 +703,7 @@ func TestTemplate_LastModifiedDate(t *testing.T) {
require.Equal(t, http.StatusOK, getRec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.NotEmpty(t, resp2["LastModifiedDate"], "LastModifiedDate should be present after update")
})
@@ -750,7 +752,7 @@ func TestListTemplates_SortedOutput(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
require.Len(t, items, len(tc.creates))
@@ -821,7 +823,7 @@ func TestTemplate_ARNPresent(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ := resp["Arn"].(string)
assert.NotEmpty(t, arn, "template ARN must be present in GET response")
@@ -888,7 +890,7 @@ func TestTemplate_TagsRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
tagsRaw, _ := resp["tags"].(map[string]any)
@@ -925,7 +927,7 @@ func TestListTemplates_FieldCompleteness(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Item"].([]any)
require.Len(t, items, 3)
@@ -964,7 +966,7 @@ func TestVoiceTemplate_TagsRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var tmpl map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &tmpl, jsonx.V1()))
assert.Equal(t, "Hello {{user.FirstName}}, your appointment is tomorrow.", tmpl["Body"])
assert.NotEmpty(t, tmpl["Arn"])
@@ -988,7 +990,7 @@ func TestVoiceTemplate_TagRoundTrip(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
templateARN, _ := createResp["Arn"].(string)
require.NotEmpty(t, templateARN)
@@ -1003,7 +1005,7 @@ func TestVoiceTemplate_TagRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags, _ := tagsResp["tags"].(map[string]any)
assert.Equal(t, "prod", tags["env"])
diff --git a/services/pinpoint/wire_field_fixes_test.go b/services/pinpoint/wire_field_fixes_test.go
index e9af4f80b0..bcc23fb650 100644
--- a/services/pinpoint/wire_field_fixes_test.go
+++ b/services/pinpoint/wire_field_fixes_test.go
@@ -1,7 +1,7 @@
package pinpoint_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -11,6 +11,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/pinpoint/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestApplicationDateRangeKpi_RequiredTimeRange covers gopherstack-6flj:
@@ -216,7 +218,7 @@ func TestCreateSegment_RawImportDefinitionFieldIgnored(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "DIMENSIONAL", out["SegmentType"],
"CreateSegment must not accept ImportDefinition; the real WriteSegmentRequest has no such member")
assert.Nil(t, out["ImportDefinition"])
@@ -245,7 +247,7 @@ func TestCreateJourney_RawTagsFieldIgnored(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Nil(t, out["tags"],
"CreateJourney must not accept or echo tags; the real WriteJourneyRequest/JourneyResponse have no such member")
diff --git a/services/pipes/enrichment_test.go b/services/pipes/enrichment_test.go
index 28778976b8..fdb351ce45 100644
--- a/services/pipes/enrichment_test.go
+++ b/services/pipes/enrichment_test.go
@@ -6,12 +6,13 @@ package pipes_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pipes"
)
@@ -297,7 +298,7 @@ func TestEnrichment_LambdaInvocation(t *testing.T) {
} else {
// Nil response: original records payload forwarded.
var event map[string]any
- require.NoError(t, json.Unmarshal(targetPayloads[0], &event))
+ require.NoError(t, json.Unmarshal(targetPayloads[0], &event, jsonx.V1()))
_, hasRecords := event["Records"]
assert.True(t, hasRecords, "original Records payload should be forwarded when enricher returns nil")
}
diff --git a/services/pipes/errsweep_wire_shape_test.go b/services/pipes/errsweep_wire_shape_test.go
index 9617e60448..4da8d5bfd4 100644
--- a/services/pipes/errsweep_wire_shape_test.go
+++ b/services/pipes/errsweep_wire_shape_test.go
@@ -1,7 +1,7 @@
package pipes_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/pipes"
@@ -58,7 +59,7 @@ func TestHandler_MalformedBodyCarriesTypedErrorCode(t *testing.T) {
require.NoError(t, err)
var decoded map[string]any
- require.NoError(t, json.Unmarshal(raw, &decoded))
+ require.NoError(t, json.Unmarshal(raw, &decoded, jsonx.V1()))
require.Equal(t, "ValidationException", decoded["__type"],
"raw response body must carry __type; got: %s", raw)
require.NotEmpty(t, decoded["message"])
diff --git a/services/pipes/filter.go b/services/pipes/filter.go
index b135ffe5ac..977e94f122 100644
--- a/services/pipes/filter.go
+++ b/services/pipes/filter.go
@@ -2,12 +2,14 @@ package pipes
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"reflect"
"slices"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/eventpattern"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// matchesAnyFilter returns true if body passes at least one of the given filters.
@@ -67,8 +69,8 @@ func matchesSingleFilter(body string, f Filter) bool {
// and/or content-filter objects) or a nested object; a bare scalar is not a
// valid EventBridge pattern value and never matches (see isJSONObject).
func matchesJSONPattern(msgBody, pattern string) bool {
- var patternMap map[string]json.RawMessage
- if err := json.Unmarshal([]byte(pattern), &patternMap); err != nil {
+ var patternMap map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(pattern), &patternMap, jsonx.V1()); err != nil {
// Malformed pattern: fall back to substring.
return strings.Contains(msgBody, pattern)
}
@@ -77,8 +79,8 @@ func matchesJSONPattern(msgBody, pattern string) bool {
return true
}
- var msgMap map[string]json.RawMessage
- if err := json.Unmarshal([]byte(msgBody), &msgMap); err != nil {
+ var msgMap map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(msgBody), &msgMap, jsonx.V1()); err != nil {
// Message body is not a JSON object; pattern cannot match.
return false
}
@@ -89,7 +91,7 @@ func matchesJSONPattern(msgBody, pattern string) bool {
// matchesPatternObject reports whether every field in patternMap is
// satisfied by msgMap (fields at one level are ANDed). "$or" is a
// combinator, not a message field, so it is dispatched separately.
-func matchesPatternObject(patternMap, msgMap map[string]json.RawMessage) bool {
+func matchesPatternObject(patternMap, msgMap map[string]jsontext.Value) bool {
for field, ruleRaw := range patternMap {
if field == "$or" {
if !matchesOrCombinator(ruleRaw, msgMap) {
@@ -111,9 +113,9 @@ func matchesPatternObject(patternMap, msgMap map[string]json.RawMessage) bool {
// matchesOrCombinator evaluates a "$or" combinator
// (eb-filtering-complex-example-or): ruleRaw must decode to an array of
// pattern objects, and msgMap matches if any one of them fully matches.
-func matchesOrCombinator(ruleRaw json.RawMessage, msgMap map[string]json.RawMessage) bool {
- var alternatives []json.RawMessage
- if err := json.Unmarshal(ruleRaw, &alternatives); err != nil {
+func matchesOrCombinator(ruleRaw jsontext.Value, msgMap map[string]jsontext.Value) bool {
+ var alternatives []jsontext.Value
+ if err := json.Unmarshal(ruleRaw, &alternatives, jsonx.V1()); err != nil {
return false
}
@@ -135,15 +137,15 @@ func matchesOrCombinator(ruleRaw json.RawMessage, msgMap map[string]json.RawMess
// its first non-whitespace byte rather than by attempting to unmarshal it --
// unmarshalling JSON `null` into a map succeeds with a nil map, which would
// otherwise misclassify a literal null rule element as an object.
-func isJSONObject(raw json.RawMessage) bool {
+func isJSONObject(raw jsontext.Value) bool {
trimmed := bytes.TrimSpace(raw)
return len(trimmed) > 0 && trimmed[0] == '{'
}
-func decodeJSONObject(raw json.RawMessage) (map[string]json.RawMessage, bool) {
- var obj map[string]json.RawMessage
- if err := json.Unmarshal(raw, &obj); err != nil {
+func decodeJSONObject(raw jsontext.Value) (map[string]jsontext.Value, bool) {
+ var obj map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &obj, jsonx.V1()); err != nil {
return nil, false
}
@@ -157,13 +159,13 @@ func decodeJSONObject(raw json.RawMessage) (map[string]json.RawMessage, bool) {
// needed because {"exists": false} matches precisely when the field is
// absent, so evaluation cannot short-circuit on absence the way every other
// operator does.
-func fieldMatchesRule(msgVal json.RawMessage, exists bool, ruleRaw json.RawMessage) bool {
+func fieldMatchesRule(msgVal jsontext.Value, exists bool, ruleRaw jsontext.Value) bool {
if isJSONObject(ruleRaw) {
return matchesNestedRule(msgVal, exists, ruleRaw)
}
- var rules []json.RawMessage
- if err := json.Unmarshal(ruleRaw, &rules); err != nil {
+ var rules []jsontext.Value
+ if err := json.Unmarshal(ruleRaw, &rules, jsonx.V1()); err != nil {
return false
}
@@ -179,7 +181,7 @@ func fieldMatchesRule(msgVal json.RawMessage, exists bool, ruleRaw json.RawMessa
// matchesNestedRule descends one level into the event for a nested pattern
// object. A nested pattern can only match a present, JSON-object-shaped
// field.
-func matchesNestedRule(msgVal json.RawMessage, exists bool, ruleRaw json.RawMessage) bool {
+func matchesNestedRule(msgVal jsontext.Value, exists bool, ruleRaw jsontext.Value) bool {
if !exists {
return false
}
@@ -216,7 +218,7 @@ func matchesNestedRule(msgVal json.RawMessage, exists bool, ruleRaw json.RawMess
// msgExists is false whenever the field was absent from the message; every
// operator besides exists requires a value to compare against, so absence
// fails them all except an explicit {"exists": false}.
-func matchesRule(msgVal json.RawMessage, msgExists bool, rule json.RawMessage) bool {
+func matchesRule(msgVal jsontext.Value, msgExists bool, rule jsontext.Value) bool {
if isJSONObject(rule) {
ruleObj, ok := decodeJSONObject(rule)
if !ok {
@@ -247,13 +249,13 @@ func matchesRule(msgVal json.RawMessage, msgExists bool, rule json.RawMessage) b
// numerically-equal event number). reflect.DeepEqual (not ==) is required
// because a malformed pattern or event can decode to a non-comparable type
// (a JSON array or object), which would panic under ==.
-func matchesExactRule(msgVal, rule json.RawMessage) bool {
+func matchesExactRule(msgVal, rule jsontext.Value) bool {
var ruleAny, msgAny any
- if err := json.Unmarshal(rule, &ruleAny); err != nil {
+ if err := json.Unmarshal(rule, &ruleAny, jsonx.V1()); err != nil {
return false
}
- if err := json.Unmarshal(msgVal, &msgAny); err != nil {
+ if err := json.Unmarshal(msgVal, &msgAny, jsonx.V1()); err != nil {
return false
}
@@ -263,7 +265,7 @@ func matchesExactRule(msgVal, rule json.RawMessage) bool {
// matchesRuleObject dispatches a single-key content-filter object to its
// matcher. Supported: prefix, suffix, numeric, cidr, anything-but,
// equals-ignore-case.
-func matchesRuleObject(msgVal json.RawMessage, ruleObj map[string]json.RawMessage) bool {
+func matchesRuleObject(msgVal jsontext.Value, ruleObj map[string]jsontext.Value) bool {
if prefixRaw, ok := ruleObj["prefix"]; ok {
return matchesPrefixRule(msgVal, prefixRaw)
}
@@ -291,18 +293,18 @@ func matchesRuleObject(msgVal json.RawMessage, ruleObj map[string]json.RawMessag
return false
}
-func decodeString(raw json.RawMessage) (string, bool) {
+func decodeString(raw jsontext.Value) (string, bool) {
var s string
- if err := json.Unmarshal(raw, &s); err != nil {
+ if err := json.Unmarshal(raw, &s, jsonx.V1()); err != nil {
return "", false
}
return s, true
}
-func decodeFloat(raw json.RawMessage) (float64, bool) {
+func decodeFloat(raw jsontext.Value) (float64, bool) {
var f float64
- if err := json.Unmarshal(raw, &f); err != nil {
+ if err := json.Unmarshal(raw, &f, jsonx.V1()); err != nil {
return 0, false
}
@@ -312,7 +314,7 @@ func decodeFloat(raw json.RawMessage) (float64, bool) {
// matchesPrefixRule matches a prefix rule value against msgVal. AWS supports
// both a plain string prefix and a case-insensitive nested form
// (eb-filtering-prefix-matching-ignore-case): {"prefix": {"equals-ignore-case": "foo"}}.
-func matchesPrefixRule(msgVal, prefixRaw json.RawMessage) bool {
+func matchesPrefixRule(msgVal, prefixRaw jsontext.Value) bool {
msgStr, ok := decodeString(msgVal)
if !ok {
return false
@@ -338,7 +340,7 @@ func matchesPrefixRule(msgVal, prefixRaw json.RawMessage) bool {
// matchesSuffixRule matches a suffix rule value against msgVal. AWS supports
// both a plain string suffix and a case-insensitive nested form
// (eb-filtering-suffix-matching-ignore-case): {"suffix": {"equals-ignore-case": "foo"}}.
-func matchesSuffixRule(msgVal, suffixRaw json.RawMessage) bool {
+func matchesSuffixRule(msgVal, suffixRaw jsontext.Value) bool {
msgStr, ok := decodeString(msgVal)
if !ok {
return false
@@ -363,7 +365,7 @@ func matchesSuffixRule(msgVal, suffixRaw json.RawMessage) bool {
// nestedEqualsIgnoreCase extracts the string value of a nested
// {"equals-ignore-case": "..."} object, as used inside prefix/suffix rules.
-func nestedEqualsIgnoreCase(nested map[string]json.RawMessage) (string, bool) {
+func nestedEqualsIgnoreCase(nested map[string]jsontext.Value) (string, bool) {
ciRaw, ok := nested["equals-ignore-case"]
if !ok {
return "", false
@@ -374,7 +376,7 @@ func nestedEqualsIgnoreCase(nested map[string]json.RawMessage) (string, bool) {
// matchesEqualsIgnoreCaseRule matches a standalone
// {"equals-ignore-case": "..."} rule (eb-filtering-equals-ignore-case-matching).
-func matchesEqualsIgnoreCaseRule(msgVal, valRaw json.RawMessage) bool {
+func matchesEqualsIgnoreCaseRule(msgVal, valRaw jsontext.Value) bool {
msgStr, ok := decodeString(msgVal)
if !ok {
return false
@@ -393,9 +395,9 @@ func matchesEqualsIgnoreCaseRule(msgVal, valRaw json.RawMessage) bool {
// services/eventbridge/pattern.go's matchNumeric. msgVal must decode as a
// JSON number; a string-encoded number (as DynamoDB Streams' "N" attribute
// wrapper produces) does not match.
-func matchesNumericRule(msgVal, rulesRaw json.RawMessage) bool {
+func matchesNumericRule(msgVal, rulesRaw jsontext.Value) bool {
var ruleList []any
- if err := json.Unmarshal(rulesRaw, &ruleList); err != nil {
+ if err := json.Unmarshal(rulesRaw, &ruleList, jsonx.V1()); err != nil {
return false
}
@@ -410,7 +412,7 @@ func matchesNumericRule(msgVal, rulesRaw json.RawMessage) bool {
// matchesCIDRRule reports whether the string msgVal is an IP address inside
// the given CIDR range (eb-filtering-ip-address), via pkgs/eventpattern,
// shared with services/eventbridge/pattern.go's matchCIDR.
-func matchesCIDRRule(msgVal, cidrRaw json.RawMessage) bool {
+func matchesCIDRRule(msgVal, cidrRaw jsontext.Value) bool {
ipStr, ok := decodeString(msgVal)
if !ok {
return false
@@ -426,14 +428,14 @@ func matchesCIDRRule(msgVal, cidrRaw json.RawMessage) bool {
// existsWant extracts {"exists": true/false}'s boolean, ok=false if the key
// is absent or not a bool.
-func existsWant(ruleObj map[string]json.RawMessage) (bool, bool) {
+func existsWant(ruleObj map[string]jsontext.Value) (bool, bool) {
existsRaw, hasKey := ruleObj["exists"]
if !hasKey {
return false, false
}
var want bool
- if err := json.Unmarshal(existsRaw, &want); err != nil {
+ if err := json.Unmarshal(existsRaw, &want, jsonx.V1()); err != nil {
return false, false
}
@@ -448,19 +450,19 @@ func existsWant(ruleObj map[string]json.RawMessage) (bool, bool) {
// records use, where every AttributeValue is string-wrapped); a non-string
// msgVal is never equal to a string exclusion value, so it always counts as
// "anything but" (matchesRuleObject negates this function's result).
-func matchesAnythingBut(anythingButRaw, msgVal json.RawMessage) bool {
+func matchesAnythingBut(anythingButRaw, msgVal jsontext.Value) bool {
msgStr, ok := decodeString(msgVal)
if !ok {
return false
}
var single string
- if err := json.Unmarshal(anythingButRaw, &single); err == nil {
+ if err := json.Unmarshal(anythingButRaw, &single, jsonx.V1()); err == nil {
return msgStr == single
}
var excluded []string
- if err := json.Unmarshal(anythingButRaw, &excluded); err == nil {
+ if err := json.Unmarshal(anythingButRaw, &excluded, jsonx.V1()); err == nil {
return slices.Contains(excluded, msgStr)
}
diff --git a/services/pipes/filter_validation.go b/services/pipes/filter_validation.go
index 4135b95d6c..0e4b5c9795 100644
--- a/services/pipes/filter_validation.go
+++ b/services/pipes/filter_validation.go
@@ -1,9 +1,11 @@
package pipes
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// isKnownPipeMatcher reports whether key is a matcher-object key that pipes'
@@ -54,7 +56,7 @@ func validateFilterPattern(pattern string) error {
}
var patternMap map[string]any
- if err := json.Unmarshal([]byte(trimmed), &patternMap); err != nil {
+ if err := json.Unmarshal([]byte(trimmed), &patternMap, jsonx.V1()); err != nil {
return fmt.Errorf("pattern: %w: not valid JSON: %w", ErrValidation, err)
}
diff --git a/services/pipes/handler.go b/services/pipes/handler.go
index f351c348a9..bce6f104a0 100644
--- a/services/pipes/handler.go
+++ b/services/pipes/handler.go
@@ -2,7 +2,7 @@ package pipes
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -350,40 +351,37 @@ func errMessage(err error) string {
}
func (h *Handler) handleError(c *echo.Context, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, ErrNotFound):
payload, _ := json.Marshal(map[string]string{
keyTypeField: "NotFoundException",
keyMessageField: errMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusNotFound, payload)
case errors.Is(err, ErrAlreadyExists), errors.Is(err, ErrConflict):
payload, _ := json.Marshal(map[string]string{
keyTypeField: "ConflictException",
keyMessageField: errMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusConflict, payload)
case errors.Is(err, ErrQuota):
payload, _ := json.Marshal(map[string]string{
keyTypeField: "ServiceQuotaExceededException",
keyMessageField: errMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
case errors.Is(err, ErrValidation):
payload, _ := json.Marshal(map[string]string{
keyTypeField: errCodeValidation,
keyMessageField: errMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
case errors.Is(err, errInvalidRequest), errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
// This branch used to omit keyTypeField entirely, so
// restjson.GetErrorInfo (aws-sdk-go-v2 aws/protocol/restjson/
// decoder_util.go:15) found no code in the header (unset) or the
@@ -398,7 +396,7 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
payload, _ := json.Marshal(map[string]string{
keyTypeField: "InternalException",
keyMessageField: errMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusInternalServerError, payload)
}
@@ -512,7 +510,7 @@ func (h *Handler) handleCreatePipe(ctx context.Context, path string, body []byte
}
var req createPipeRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -535,7 +533,7 @@ func (h *Handler) handleCreatePipe(ctx context.Context, path string, body []byte
return nil, err
}
- return json.Marshal(toPipeResponse(p))
+ return json.Marshal(toPipeResponse(p), jsonx.V1())
}
func (h *Handler) handleDescribePipe(ctx context.Context, path string) ([]byte, error) {
@@ -549,7 +547,7 @@ func (h *Handler) handleDescribePipe(ctx context.Context, path string) ([]byte,
return nil, err
}
- return json.Marshal(toPipeResponse(p))
+ return json.Marshal(toPipeResponse(p), jsonx.V1())
}
// pipeSummary matches types.Pipe (the summary shape ListPipesOutput.Pipes uses),
@@ -613,7 +611,7 @@ func (h *Handler) handleListPipes(ctx context.Context, query url.Values) ([]byte
})
}
- return json.Marshal(listPipesResponse{Pipes: items, NextToken: res.NextToken})
+ return json.Marshal(listPipesResponse{Pipes: items, NextToken: res.NextToken}, jsonx.V1())
}
func (h *Handler) handleDeletePipe(ctx context.Context, path string) ([]byte, error) {
@@ -627,7 +625,7 @@ func (h *Handler) handleDeletePipe(ctx context.Context, path string) ([]byte, er
return nil, err
}
- return json.Marshal(toPipeResponse(p))
+ return json.Marshal(toPipeResponse(p), jsonx.V1())
}
func (h *Handler) handleUpdatePipe(ctx context.Context, path string, body []byte) ([]byte, error) {
@@ -637,7 +635,7 @@ func (h *Handler) handleUpdatePipe(ctx context.Context, path string, body []byte
}
var req updatePipeRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -657,7 +655,7 @@ func (h *Handler) handleUpdatePipe(ctx context.Context, path string, body []byte
return nil, err
}
- return json.Marshal(toPipeResponse(p))
+ return json.Marshal(toPipeResponse(p), jsonx.V1())
}
func (h *Handler) handleStartPipe(ctx context.Context, path string) ([]byte, error) {
@@ -671,7 +669,7 @@ func (h *Handler) handleStartPipe(ctx context.Context, path string) ([]byte, err
return nil, err
}
- return json.Marshal(toPipeResponse(p))
+ return json.Marshal(toPipeResponse(p), jsonx.V1())
}
func (h *Handler) handleStopPipe(ctx context.Context, path string) ([]byte, error) {
@@ -685,7 +683,7 @@ func (h *Handler) handleStopPipe(ctx context.Context, path string) ([]byte, erro
return nil, err
}
- return json.Marshal(toPipeResponse(p))
+ return json.Marshal(toPipeResponse(p), jsonx.V1())
}
type tagResourceRequest struct {
@@ -699,7 +697,7 @@ func (h *Handler) handleTagResource(ctx context.Context, path string, body []byt
}
var req tagResourceRequest
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, unmarshalErr)
}
@@ -750,7 +748,7 @@ func (h *Handler) handleListTagsForResource(ctx context.Context, path string) ([
return nil, err
}
- return json.Marshal(listTagsResponse{Tags: tags})
+ return json.Marshal(listTagsResponse{Tags: tags}, jsonx.V1())
}
func extractTagsARN(path string) (string, error) {
diff --git a/services/pipes/handler_test.go b/services/pipes/handler_test.go
index a87aa2e720..3ca6f18f6e 100644
--- a/services/pipes/handler_test.go
+++ b/services/pipes/handler_test.go
@@ -3,7 +3,7 @@ package pipes_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pipes"
)
@@ -32,7 +33,7 @@ func doPipesRequest(t *testing.T, h *pipes.Handler, method, path string, body an
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -340,7 +341,7 @@ func createTestPipeWithARN(t *testing.T, h *pipes.Handler, pipeName string) stri
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["Arn"].(string)
}
@@ -529,7 +530,7 @@ func TestPipes_Handler_Reset(t *testing.T) {
var out struct {
Pipes []any `json:"Pipes"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Pipes, tt.wantAfter)
})
}
@@ -634,7 +635,7 @@ func TestHandler_ListPipesFiltering(t *testing.T) {
Name string `json:"Name"`
} `json:"Pipes"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Pipes, 2)
})
@@ -647,7 +648,7 @@ func TestHandler_ListPipesFiltering(t *testing.T) {
Name string `json:"Name"`
} `json:"Pipes"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Pipes, 1)
assert.Equal(t, "sqs-beta", out.Pipes[0].Name)
})
@@ -688,7 +689,7 @@ func TestHandler_SourceAndTargetParameters(t *testing.T) {
} `json:"SqsQueueParameters"`
} `json:"SourceParameters"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
assert.Equal(t, 5, created.SourceParameters.SqsQueueParameters.BatchSize)
assert.JSONEq(t, `{"fixed":"value"}`, created.TargetParameters.InputTemplate)
}
@@ -713,7 +714,7 @@ func TestHandler_ListPipesIncludesSourceTarget(t *testing.T) {
var out struct {
Pipes []map[string]any `json:"Pipes"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Pipes, 1)
assert.Equal(t, "arn:aws:sqs:us-east-1:000000000000:my-queue", out.Pipes[0]["Source"])
assert.Equal(t, "arn:aws:lambda:us-east-1:000000000000:function:my-fn", out.Pipes[0]["Target"])
@@ -740,7 +741,7 @@ func auditDo(t *testing.T, h *pipes.Handler, method, path string, body any) *htt
if body != nil {
var err error
- b, err = json.Marshal(body)
+ b, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -763,7 +764,7 @@ func auditCreate(t *testing.T, h *pipes.Handler, name string, body map[string]an
require.Equal(t, http.StatusOK, rec.Code, "create pipe %q: %s", name, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
@@ -775,7 +776,7 @@ func auditDescribe(t *testing.T, h *pipes.Handler, name string) map[string]any {
require.Equal(t, http.StatusOK, rec.Code, "describe pipe %q: %s", name, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
@@ -839,7 +840,7 @@ func TestStartStop_ConflictException(t *testing.T) {
"real AWS returns 409 ConflictException, not 400 ValidationException")
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ConflictException", resp["__type"],
"error type must be ConflictException; body: %s", rec.Body.String())
})
@@ -881,7 +882,7 @@ func TestServiceQuotaExceededException(t *testing.T) {
// The key assertion is the error type when it does fail.
if rec.Code != http.StatusOK {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ServiceQuotaExceededException", resp["__type"],
"quota exceeded must return ServiceQuotaExceededException; body: %s", rec.Body.String())
}
@@ -908,7 +909,7 @@ func TestListPipes_EnrichmentInSummary(t *testing.T) {
var out struct {
Pipes []map[string]any `json:"Pipes"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Pipes, 1)
enrichment, _ := out.Pipes[0]["Enrichment"].(string)
@@ -935,7 +936,7 @@ func TestListPipes_NoEnrichmentOmitted(t *testing.T) {
var out struct {
Pipes []map[string]any `json:"Pipes"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Pipes, 1)
_, hasEnrichment := out.Pipes[0]["Enrichment"]
@@ -954,7 +955,7 @@ func TestInvalidNextToken_ValidationException(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"],
"invalid NextToken must return ValidationException; body: %s", rec.Body.String())
}
@@ -1020,7 +1021,7 @@ func TestServiceQuotaErrorCode(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ServiceQuotaExceededException", resp["__type"],
"1001st pipe must fail with ServiceQuotaExceededException; body: %s", rec.Body.String())
}
@@ -1144,6 +1145,6 @@ func TestHandler_UpdatePipeDesiredState(t *testing.T) {
var out struct {
DesiredState string `json:"DesiredState"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "STOPPED", out.DesiredState)
}
diff --git a/services/pipes/persistence.go b/services/pipes/persistence.go
index 00808669a9..e2cd4d22ef 100644
--- a/services/pipes/persistence.go
+++ b/services/pipes/persistence.go
@@ -2,7 +2,7 @@ package pipes
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"strings"
@@ -39,10 +39,10 @@ const pipesSnapshotVersion = 3
// Restore must pre-register every (resource, region) tuple found in Tables
// before calling registry.RestoreAll, see preRegisterSnapshotTables below.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
@@ -82,7 +82,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
// "/" (see store_setup.go's pipesTable/
// enrichmentCountsTable); splitting on the first "/" recovers both parts
// (region names never contain "/").
-func (b *InMemoryBackend) preRegisterSnapshotTables(tables map[string]json.RawMessage) {
+func (b *InMemoryBackend) preRegisterSnapshotTables(tables map[string]jsontext.Value) {
for key := range tables {
name, region, found := strings.Cut(key, "/")
if !found {
@@ -128,7 +128,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
}
if snap.Tables == nil {
- snap.Tables = make(map[string]json.RawMessage)
+ snap.Tables = make(map[string]jsontext.Value)
}
b.preRegisterSnapshotTables(snap.Tables)
diff --git a/services/pipes/pipe_lifecycle_test.go b/services/pipes/pipe_lifecycle_test.go
index 96eba038ea..90a613a68a 100644
--- a/services/pipes/pipe_lifecycle_test.go
+++ b/services/pipes/pipe_lifecycle_test.go
@@ -8,7 +8,7 @@ package pipes_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -18,6 +18,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pipes"
)
@@ -247,7 +248,7 @@ func TestLifecycle_DeleteReturnsBody(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "DELETING", resp["CurrentState"])
assert.Equal(t, tt.pipeName, resp["Name"])
assert.NotEmpty(t, resp["Arn"])
diff --git a/services/pipes/pipes_test.go b/services/pipes/pipes_test.go
index 9d45152f60..c26edaf1a9 100644
--- a/services/pipes/pipes_test.go
+++ b/services/pipes/pipes_test.go
@@ -5,13 +5,14 @@ package pipes_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pipes"
)
@@ -57,7 +58,7 @@ func TestPagination_Limit(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pipeList, _ := resp["Pipes"].([]any)
assert.Len(t, pipeList, tt.wantCount)
})
@@ -87,7 +88,7 @@ func TestPagination_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, page1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(page1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(page1.Body.Bytes(), &resp1, jsonx.V1()))
list1 := resp1["Pipes"].([]any)
assert.Len(t, list1, 2)
assert.NotEmpty(t, resp1["NextToken"], "NextToken should be present when more pages exist")
@@ -98,7 +99,7 @@ func TestPagination_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, page2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(page2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(page2.Body.Bytes(), &resp2, jsonx.V1()))
list2 := resp2["Pipes"].([]any)
assert.NotEmpty(t, list2)
diff --git a/services/pipes/runner.go b/services/pipes/runner.go
index 7288d1c616..b641961b29 100644
--- a/services/pipes/runner.go
+++ b/services/pipes/runner.go
@@ -2,7 +2,7 @@ package pipes
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"strings"
@@ -10,6 +10,7 @@ import (
"time"
arnpkg "github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/roleauth"
@@ -367,7 +368,7 @@ func (r *Runner) pollSQSPipe(ctx context.Context, p *Pipe) {
return
}
- payload, err := json.Marshal(sqsPipeEvent{Records: buildSQSRecords(p, msgs)})
+ payload, err := json.Marshal(sqsPipeEvent{Records: buildSQSRecords(p, msgs)}, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "pipes: failed to marshal SQS records", "pipe", p.Name, "error", err)
diff --git a/services/pipes/runner_targets_test.go b/services/pipes/runner_targets_test.go
index a2691514bc..0390007edb 100644
--- a/services/pipes/runner_targets_test.go
+++ b/services/pipes/runner_targets_test.go
@@ -8,7 +8,7 @@ package pipes_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"sync"
@@ -18,6 +18,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pipes"
)
@@ -40,7 +41,7 @@ func b3Do(t *testing.T, h *pipes.Handler, method, path string, body any) *httpte
if body != nil {
var err error
- b, err = json.Marshal(body)
+ b, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -63,7 +64,7 @@ func b3Create(t *testing.T, h *pipes.Handler, name string, body map[string]any)
require.Equal(t, http.StatusOK, rec.Code, "create pipe %q: %s", name, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
@@ -75,7 +76,7 @@ func b3Describe(t *testing.T, h *pipes.Handler, name string) map[string]any {
require.Equal(t, http.StatusOK, rec.Code, "describe pipe %q: %s", name, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
@@ -276,7 +277,7 @@ func b4Do(t *testing.T, h *pipes.Handler, method, path string, body any) *httpte
var b []byte
if body != nil {
var err error
- b, err = json.Marshal(body)
+ b, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -297,7 +298,7 @@ func b4Create(t *testing.T, h *pipes.Handler, name string, body map[string]any)
rec := b4Do(t, h, http.MethodPost, "/v1/pipes/"+name, body)
require.Equal(t, http.StatusOK, rec.Code, "create pipe %q: %s", name, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
@@ -307,7 +308,7 @@ func b4Describe(t *testing.T, h *pipes.Handler, name string) map[string]any {
rec := b4Do(t, h, http.MethodGet, "/v1/pipes/"+name, nil)
require.Equal(t, http.StatusOK, rec.Code, "describe pipe %q: %s", name, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
@@ -925,7 +926,7 @@ func TestTarget_Firehose_InputTemplate(t *testing.T) {
} else {
// No template → SQS records JSON is forwarded.
var event map[string]any
- require.NoError(t, json.Unmarshal(records[0], &event))
+ require.NoError(t, json.Unmarshal(records[0], &event, jsonx.V1()))
_, hasRecords := event["Records"]
assert.True(t, hasRecords, "Firehose should receive SQS records payload")
}
diff --git a/services/pipes/runner_test.go b/services/pipes/runner_test.go
index 0f3e99d89b..5cb441616e 100644
--- a/services/pipes/runner_test.go
+++ b/services/pipes/runner_test.go
@@ -2,7 +2,7 @@ package pipes_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sync"
"testing"
"testing/synctest"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pipes"
)
@@ -138,7 +139,7 @@ func TestPipesRunner_SQSToLambda(t *testing.T) {
EventSource string `json:"eventSource"`
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(payloads[0], &event))
+ require.NoError(t, json.Unmarshal(payloads[0], &event, jsonx.V1()))
require.Len(t, event.Records, 2)
assert.Equal(t, "msg-1", event.Records[0].MessageID)
assert.Equal(t, "hello", event.Records[0].Body)
@@ -313,7 +314,7 @@ func TestPipesRunner_FilterCriteria(t *testing.T) {
require.Len(t, calls, 1)
var event map[string]any
- require.NoError(t, json.Unmarshal(payloads[0], &event))
+ require.NoError(t, json.Unmarshal(payloads[0], &event, jsonx.V1()))
require.Len(t, event, 1)
// payload is forwarded - just check Lambda was called once
diff --git a/services/pipes/runner_timestream.go b/services/pipes/runner_timestream.go
index 6676d8c05f..e3d92103af 100644
--- a/services/pipes/runner_timestream.go
+++ b/services/pipes/runner_timestream.go
@@ -3,12 +3,14 @@ package pipes
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -88,7 +90,7 @@ func (r *Runner) invokeTimestreamTarget(ctx context.Context, p *Pipe, payload []
// or the payload itself as a single event.
func splitPipeEvents(payload []byte) []any {
var v any
- if err := json.Unmarshal(payload, &v); err != nil {
+ if err := json.Unmarshal(payload, &v, jsonx.V1()); err != nil {
return nil
}
@@ -331,11 +333,11 @@ func decodeEmbeddedJSON(v any) any {
}
var out any
- if json.Unmarshal([]byte(s), &out) == nil {
+ if json.Unmarshal([]byte(s), &out, jsonx.V1()) == nil {
return out
}
- if raw, err := base64.StdEncoding.DecodeString(s); err == nil && json.Unmarshal(raw, &out) == nil {
+ if raw, err := base64.StdEncoding.DecodeString(s); err == nil && json.Unmarshal(raw, &out, jsonx.V1()) == nil {
return out
}
@@ -354,7 +356,7 @@ func pipeScalarString(v any) string {
return ""
}
- b, _ := json.Marshal(v)
+ b, _ := json.Marshal(v, jsonx.V1())
return string(b)
}
diff --git a/services/pipes/sources_poll.go b/services/pipes/sources_poll.go
index 508b443796..2ee04025e2 100644
--- a/services/pipes/sources_poll.go
+++ b/services/pipes/sources_poll.go
@@ -3,9 +3,10 @@ package pipes
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -148,7 +149,7 @@ func (r *Runner) pollKinesisShard(ctx context.Context, p *Pipe, shardID, startin
return
}
- payload, err := json.Marshal(kinesisPipeEvent{Records: buildKinesisRecords(p, shardID, records)})
+ payload, err := json.Marshal(kinesisPipeEvent{Records: buildKinesisRecords(p, shardID, records)}, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "pipes: failed to marshal Kinesis records", "pipe", p.Name, "error", err)
@@ -218,7 +219,7 @@ func (r *Runner) pollDynamoDBShard(ctx context.Context, p *Pipe, shardID, starti
return
}
- payload, err := json.Marshal(dynamoDBPipeEvent{Records: buildDynamoDBRecords(p, records)})
+ payload, err := json.Marshal(dynamoDBPipeEvent{Records: buildDynamoDBRecords(p, records)}, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "pipes: failed to marshal DynamoDB stream records",
"pipe", p.Name, "error", err)
@@ -312,7 +313,7 @@ func filterDynamoDBRecords(p *Pipe, records []DynamoDBStreamRecord) []DynamoDBSt
NewImage: rec.NewImage,
OldImage: rec.OldImage,
},
- })
+ }, jsonx.V1())
if err == nil && matchesAnyFilter(string(body), filters) {
out = append(out, rec)
}
diff --git a/services/pipes/sources_test.go b/services/pipes/sources_test.go
index bd36b94b67..70b31e069d 100644
--- a/services/pipes/sources_test.go
+++ b/services/pipes/sources_test.go
@@ -15,13 +15,14 @@ package pipes_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pipes"
)
@@ -828,7 +829,7 @@ func TestPipesRunner_StreamSourcePolling(t *testing.T) {
EventSource string `json:"eventSource"`
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(payloads[0], &event))
+ require.NoError(t, json.Unmarshal(payloads[0], &event, jsonx.V1()))
require.Len(t, event.Records, 1)
assert.Equal(t, "pk1", event.Records[0].Kinesis.PartitionKey)
assert.Equal(t, "aws:kinesis", event.Records[0].EventSource)
@@ -997,7 +998,7 @@ func TestPipesRunner_StreamSourcePolling(t *testing.T) {
EventName string `json:"eventName"`
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(payloads[0], &event))
+ require.NoError(t, json.Unmarshal(payloads[0], &event, jsonx.V1()))
require.Len(t, event.Records, 1)
assert.Equal(t, "INSERT", event.Records[0].EventName)
},
diff --git a/services/pipes/tags_test.go b/services/pipes/tags_test.go
index c92bd68b2d..7c524cf576 100644
--- a/services/pipes/tags_test.go
+++ b/services/pipes/tags_test.go
@@ -1,12 +1,14 @@
package pipes_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestTags_CreateAndDescribe verifies tags are stored on create and returned on describe.
@@ -96,7 +98,7 @@ func TestTags_UpdateViaTagResource(t *testing.T) {
tagsRec := auditDo(t, h, http.MethodGet, "/tags/"+pipeARN, nil)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags, _ := tagsResp["tags"].(map[string]any)
for k, v := range tt.wantTags {
diff --git a/services/pipes/targets_test.go b/services/pipes/targets_test.go
index 6ad1309a74..d0c4db5f2d 100644
--- a/services/pipes/targets_test.go
+++ b/services/pipes/targets_test.go
@@ -9,7 +9,7 @@ package pipes_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -18,6 +18,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pipes"
)
@@ -38,7 +39,7 @@ func b2Do(t *testing.T, h *pipes.Handler, method, path string, body any) *httpte
var b []byte
if body != nil {
var err error
- b, err = json.Marshal(body)
+ b, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
e := echo.New()
@@ -58,7 +59,7 @@ func b2Create(t *testing.T, h *pipes.Handler, name string, body map[string]any)
rec := b2Do(t, h, http.MethodPost, "/v1/pipes/"+name, body)
require.Equal(t, http.StatusOK, rec.Code, "create pipe %q: %s", name, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
@@ -68,7 +69,7 @@ func b2Describe(t *testing.T, h *pipes.Handler, name string) map[string]any {
rec := b2Do(t, h, http.MethodGet, "/v1/pipes/"+name, nil)
require.Equal(t, http.StatusOK, rec.Code, "describe pipe %q: %s", name, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
@@ -78,7 +79,7 @@ func b2Update(t *testing.T, h *pipes.Handler, name string, body map[string]any)
rec := b2Do(t, h, http.MethodPut, "/v1/pipes/"+name, body)
require.Equal(t, http.StatusOK, rec.Code, "update pipe %q: %s", name, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
diff --git a/services/pipes/wire_key_case_restore_test.go b/services/pipes/wire_key_case_restore_test.go
index b6f47fc77f..83969a1403 100644
--- a/services/pipes/wire_key_case_restore_test.go
+++ b/services/pipes/wire_key_case_restore_test.go
@@ -1,12 +1,13 @@
package pipes_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pipes"
)
@@ -41,7 +42,7 @@ func TestWireKeyCase_OldSnapshotKeysStillDecode(t *testing.T) {
var got pipes.ECSTaskTargetParameters
- require.NoError(t, json.Unmarshal([]byte(tt.raw), &got))
+ require.NoError(t, json.Unmarshal([]byte(tt.raw), &got, jsonx.V1()))
assert.Equal(t, []string{"s-1"}, got.NetworkConfiguration.AwsvpcConfiguration.Subnets)
assert.Equal(t, "FARGATE", got.CapacityProviderStrategy[0].CapacityProvider)
assert.Equal(t, 2, got.CapacityProviderStrategy[0].Weight)
diff --git a/services/pipes/wire_time.go b/services/pipes/wire_time.go
index 1940351891..d60d35497c 100644
--- a/services/pipes/wire_time.go
+++ b/services/pipes/wire_time.go
@@ -1,8 +1,10 @@
package pipes
import (
- "encoding/json"
+ "encoding/json/v2"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// epochSecondsPtr converts *time.Time into the epoch-seconds-with-millisecond-
@@ -44,7 +46,7 @@ func (k KinesisStreamSourceParameters) MarshalJSON() ([]byte, error) {
}{
StartingPositionTimestamp: epochSecondsPtr(k.StartingPositionTimestamp),
alias: alias(k),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of MarshalJSON. Without it, a real client's
@@ -59,7 +61,7 @@ func (k *KinesisStreamSourceParameters) UnmarshalJSON(data []byte) error {
*alias
}{alias: (*alias)(k)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/pipes/wire_time_test.go b/services/pipes/wire_time_test.go
index 2d7cbe608e..33d9914184 100644
--- a/services/pipes/wire_time_test.go
+++ b/services/pipes/wire_time_test.go
@@ -1,7 +1,7 @@
package pipes_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http/httptest"
"testing"
"time"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/pipes"
)
@@ -60,7 +61,7 @@ func TestKinesisStreamSourceParameters_UnmarshalJSON_TimeIsEpochSeconds(t *testi
t.Parallel()
var params pipes.KinesisStreamSourceParameters
- err := json.Unmarshal([]byte(tt.body), ¶ms)
+ err := json.Unmarshal([]byte(tt.body), ¶ms, jsonx.V1())
if tt.wantErr {
require.Error(t, err)
diff --git a/services/polly/handler.go b/services/polly/handler.go
index 89e09fc4cb..c4c7fa2672 100644
--- a/services/polly/handler.go
+++ b/services/polly/handler.go
@@ -2,7 +2,7 @@ package polly
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +16,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -318,7 +319,7 @@ func (h *Handler) startSpeechSynthesisStream(c *echo.Context) error {
return encodeErr
}
- closed, err := json.Marshal(map[string]int{"RequestCharacters": result.RequestCharacters})
+ closed, err := json.Marshal(map[string]int{"RequestCharacters": result.RequestCharacters}, jsonx.V1())
if err != nil {
return err
}
@@ -383,7 +384,7 @@ func decodeStreamText(body io.Reader) (string, string, error) {
}
var event streamTextEvent
- if jsonErr := json.Unmarshal(message.Payload, &event); jsonErr != nil {
+ if jsonErr := json.Unmarshal(message.Payload, &event, jsonx.V1()); jsonErr != nil {
return "", "", jsonErr
}
texts = append(texts, event.Text)
@@ -613,7 +614,7 @@ func decodeRequest(c *echo.Context, value any) error {
if err != nil {
return fmt.Errorf("%w: failed to read body", ErrValidation)
}
- if jsonErr := json.Unmarshal(body, value); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, value, jsonx.V1()); jsonErr != nil {
return fmt.Errorf("%w: invalid JSON: %w", ErrValidation, jsonErr)
}
diff --git a/services/polly/handler_test.go b/services/polly/handler_test.go
index ed29c5f91a..65775fb85c 100644
--- a/services/polly/handler_test.go
+++ b/services/polly/handler_test.go
@@ -2,7 +2,7 @@ package polly_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/polly"
)
@@ -30,7 +31,7 @@ func request(
var payload []byte
if body != nil {
var err error
- payload, err = json.Marshal(body)
+ payload, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -47,7 +48,7 @@ func responseMap(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/polly/multi_region_test.go b/services/polly/multi_region_test.go
index 9a1c0d2748..31f87290ac 100644
--- a/services/polly/multi_region_test.go
+++ b/services/polly/multi_region_test.go
@@ -2,7 +2,8 @@ package polly_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/polly"
)
@@ -174,8 +176,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/polly/persistence.go b/services/polly/persistence.go
index 4cb7d5c3da..2ebc758956 100644
--- a/services/polly/persistence.go
+++ b/services/polly/persistence.go
@@ -2,7 +2,7 @@ package polly
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
@@ -36,10 +36,10 @@ const pollySnapshotVersion = 2
// The built-in voice catalogue (InMemoryBackend.voices) is static read-only
// data, not a mutable resource collection, and is intentionally excluded.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountId"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountId"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serializes the backend state to JSON. It implements
diff --git a/services/polly/speech_test.go b/services/polly/speech_test.go
index 5e16aa19e0..28ddb2f189 100644
--- a/services/polly/speech_test.go
+++ b/services/polly/speech_test.go
@@ -2,7 +2,7 @@ package polly_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/polly"
)
@@ -190,7 +191,7 @@ func TestStartSpeechSynthesisStreamValidationExceptionTaxonomy(t *testing.T) {
// "ValidationException: EngineNotSupportedException: voice ..."), so
// assert on the parsed field rather than searching the raw body.
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "ValidationException", out["__type"])
})
}
@@ -240,7 +241,7 @@ func TestStartSpeechSynthesisStreamRequiresGenerativeEngine(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "ValidationException", out["__type"])
})
}
@@ -648,7 +649,7 @@ func TestSpeechMarksVisemeVariety(t *testing.T) {
}
var m map[string]any
- require.NoError(t, json.Unmarshal(line, &m))
+ require.NoError(t, json.Unmarshal(line, &m, jsonx.V1()))
if m["type"] == "viseme" {
marks = append(marks, m)
@@ -780,7 +781,7 @@ func TestSpeechMarkCounts(t *testing.T) {
continue
}
var ev map[string]any
- require.NoError(t, json.Unmarshal([]byte(line), &ev), "invalid JSON line: %s", line)
+ require.NoError(t, json.Unmarshal([]byte(line), &ev, jsonx.V1()), "invalid JSON line: %s", line)
typ, _ := ev["type"].(string)
counts[typ]++
}
@@ -836,7 +837,7 @@ func TestSpeechMarkTimeOrder(t *testing.T) {
continue
}
var ev map[string]any
- require.NoError(t, json.Unmarshal([]byte(line), &ev))
+ require.NoError(t, json.Unmarshal([]byte(line), &ev, jsonx.V1()))
timeVal, _ := ev["time"].(float64)
assert.GreaterOrEqual(t, timeVal, prevTime, "marks must be non-decreasing by time")
prevTime = timeVal
diff --git a/services/polly/throttle_test.go b/services/polly/throttle_test.go
index f4566ac17d..be817c5712 100644
--- a/services/polly/throttle_test.go
+++ b/services/polly/throttle_test.go
@@ -2,7 +2,7 @@ package polly_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/polly"
)
@@ -162,7 +163,7 @@ func TestStartSpeechSynthesisStream_ThrottlingHTTPWireShape(t *testing.T) {
require.Equal(t, http.StatusBadRequest, second.Code)
var out map[string]string
- require.NoError(t, json.Unmarshal(second.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(second.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "ThrottlingException", out["__type"])
}
@@ -189,6 +190,6 @@ func TestStartSpeechSynthesisStream_ServiceQuotaHTTPWireShape(t *testing.T) {
require.Equal(t, http.StatusPaymentRequired, rec.Code)
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "ServiceQuotaExceededException", out["__type"])
}
diff --git a/services/pricing/catalog.go b/services/pricing/catalog.go
index f4f90a6e0e..2e6380a2f6 100644
--- a/services/pricing/catalog.go
+++ b/services/pricing/catalog.go
@@ -3,13 +3,15 @@ package pricing
import (
"crypto/sha256"
"encoding/base32"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
"sort"
"strings"
"sync"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -129,7 +131,7 @@ func render(s *spec) *entry {
Product: productJSON{Attributes: attrs, ProductFamily: s.family, SKU: sku},
ServiceCode: s.service, Terms: terms, Version: catalogVersion, PublicationDate: publicationDate,
}
- raw, _ := json.Marshal(item)
+ raw, _ := json.Marshal(item, jsonx.V1())
return &entry{item: item, json: string(raw), region: s.region.code, service: s.service}
}
diff --git a/services/pricing/handler.go b/services/pricing/handler.go
index 0ad13cad0a..309551dd0c 100644
--- a/services/pricing/handler.go
+++ b/services/pricing/handler.go
@@ -2,7 +2,7 @@ package pricing
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"slices"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -33,7 +34,7 @@ func bind[I, O any](fn func(*Handler, context.Context, *I) (*O, error)) opFunc {
var in I
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fail(ErrInvalidParameter, "malformed request body: %v", err)
}
}
diff --git a/services/pricing/pricelists.go b/services/pricing/pricelists.go
index f176e1e331..d1fb720ec5 100644
--- a/services/pricing/pricelists.go
+++ b/services/pricing/pricelists.go
@@ -1,11 +1,13 @@
package pricing
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -213,7 +215,7 @@ func RenderFile(code, ver, region, kind string) ([]byte, string, bool) {
}
}
- raw, _ := json.Marshal(f)
+ raw, _ := json.Marshal(f, jsonx.V1())
return raw, "application/json", true
}
diff --git a/services/pricing/pricing_more_test.go b/services/pricing/pricing_more_test.go
index bc174a683e..0ca7c46d52 100644
--- a/services/pricing/pricing_more_test.go
+++ b/services/pricing/pricing_more_test.go
@@ -1,7 +1,7 @@
package pricing_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/pricing"
)
@@ -77,7 +78,7 @@ func TestReservedTermMatrix(t *testing.T) {
} `json:"terms"`
}
- require.NoError(t, json.Unmarshal([]byte(out.PriceList[0]), &item))
+ require.NoError(t, json.Unmarshal([]byte(out.PriceList[0]), &item, jsonx.V1()))
require.Len(t, item.Terms.Reserved, 12)
seen := map[string]bool{}
@@ -168,7 +169,7 @@ func TestSavingsPlansPriceLists(t *testing.T) {
} `json:"products"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &file))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &file, jsonx.V1()))
require.Len(t, file.Terms.SavingsPlan, 6)
assert.Len(t, file.Products, 6)
require.NotEmpty(t, file.Terms.SavingsPlan[0].Rates)
diff --git a/services/pricing/pricing_test.go b/services/pricing/pricing_test.go
index 55e19f5b40..d3732cf002 100644
--- a/services/pricing/pricing_test.go
+++ b/services/pricing/pricing_test.go
@@ -1,7 +1,7 @@
package pricing_test
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/http/httptest"
@@ -20,6 +20,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/sdktest"
"github.com/blackbirdworks/gopherstack/services/pricing"
)
@@ -206,7 +207,7 @@ func TestProductShape(t *testing.T) {
ServiceCode string `json:"serviceCode"`
}
- require.NoError(t, json.Unmarshal([]byte(out.PriceList[0]), &item))
+ require.NoError(t, json.Unmarshal([]byte(out.PriceList[0]), &item, jsonx.V1()))
assert.Equal(t, "AmazonEC2", item.ServiceCode)
assert.Equal(t, "Compute Instance", item.Product.ProductFamily)
assert.Equal(t, "US East (N. Virginia)", item.Product.Attributes["location"])
@@ -305,7 +306,7 @@ func TestPriceListsAndFile(t *testing.T) {
OfferCode string `json:"offerCode"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &file))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &file, jsonx.V1()))
assert.Equal(t, "AmazonS3", file.OfferCode)
assert.NotEmpty(t, file.Products)
} else {
diff --git a/services/pricing/savingsplans.go b/services/pricing/savingsplans.go
index 577b69e358..0db1c1188a 100644
--- a/services/pricing/savingsplans.go
+++ b/services/pricing/savingsplans.go
@@ -3,11 +3,13 @@ package pricing
import (
"crypto/sha256"
"encoding/base32"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -152,7 +154,7 @@ func renderSavingsPlans(region string) []byte {
})
}
- raw, _ := json.Marshal(f)
+ raw, _ := json.Marshal(f, jsonx.V1())
return raw
}
diff --git a/services/quicksight/dataset_security.go b/services/quicksight/dataset_security.go
index 94b3487486..9388e95989 100644
--- a/services/quicksight/dataset_security.go
+++ b/services/quicksight/dataset_security.go
@@ -1,8 +1,10 @@
package quicksight
import (
- "encoding/json"
+ "encoding/json/v2"
"slices"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const dataSetUseAsRLSRules = "RLS_RULES"
@@ -32,13 +34,13 @@ func validateDataSetSecurity(s DataSetSecurity, allowUseAs bool) error {
}
func cloneJSONValue[T any](v T) T {
- raw, err := json.Marshal(v)
+ raw, err := json.Marshal(v, jsonx.V1())
if err != nil {
return v
}
var out T
- if json.Unmarshal(raw, &out) != nil {
+ if json.Unmarshal(raw, &out, jsonx.V1()) != nil {
return v
}
diff --git a/services/quicksight/error_envelope_test.go b/services/quicksight/error_envelope_test.go
index ba7d52131a..2bafd09075 100644
--- a/services/quicksight/error_envelope_test.go
+++ b/services/quicksight/error_envelope_test.go
@@ -1,7 +1,7 @@
package quicksight_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/quicksight"
@@ -92,7 +93,7 @@ func TestErrorEnvelope_DescribeDataSetNotFoundDecodesToTypedError(t *testing.T)
require.NoError(t, err)
var envelope map[string]any
- require.NoError(t, json.Unmarshal(raw, &envelope))
+ require.NoError(t, json.Unmarshal(raw, &envelope, jsonx.V1()))
require.Equal(t, "ResourceNotFoundException", envelope["Code"],
"raw body must carry the Code key restjson.GetErrorInfo checks first: %s", raw)
diff --git a/services/quicksight/handler_paths.go b/services/quicksight/handler_paths.go
index 7b1404e484..47bcf7a7aa 100644
--- a/services/quicksight/handler_paths.go
+++ b/services/quicksight/handler_paths.go
@@ -1,7 +1,8 @@
package quicksight
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -14,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -971,7 +973,7 @@ func classifyRestorePaths(method string, segs []string, n int) (string, string)
func readBody(c *echo.Context) (map[string]any, error) {
var body map[string]any
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil {
return nil, err
}
@@ -1090,7 +1092,7 @@ func writeJSON(c *echo.Context, code int, body any) error {
c.Response().Header().Set("Content-Type", "application/json")
c.Response().WriteHeader(code)
- return json.NewEncoder(c.Response()).Encode(body)
+ return json.MarshalEncode(jsontext.NewEncoder(c.Response(), jsonx.V1()), body)
}
func writeError(c *echo.Context, code int, errCode, msg string) error {
diff --git a/services/quicksight/handler_test.go b/services/quicksight/handler_test.go
index 9a643a2a90..b7a8328d6e 100644
--- a/services/quicksight/handler_test.go
+++ b/services/quicksight/handler_test.go
@@ -2,7 +2,7 @@ package quicksight_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/quicksight"
)
@@ -187,7 +188,7 @@ func doRequest(t *testing.T, h *quicksight.Handler, method, path string, body an
var bodyBytes []byte
if body != nil {
var marshalErr error
- bodyBytes, marshalErr = json.Marshal(body)
+ bodyBytes, marshalErr = json.Marshal(body, jsonx.V1())
require.NoError(t, marshalErr)
}
@@ -209,7 +210,7 @@ func parseBody(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
diff --git a/services/quicksight/models.go b/services/quicksight/models.go
index b76543114f..8f6e3c3b27 100644
--- a/services/quicksight/models.go
+++ b/services/quicksight/models.go
@@ -1,7 +1,7 @@
package quicksight
import (
- "encoding/json"
+ "encoding/json/jsontext"
"regexp"
"time"
)
@@ -288,7 +288,7 @@ func (a *storedAnalysis) toAnalysis() *Analysis {
// field of its own (see store_setup.go's doc comment for the full list and
// rationale) -- these round-trip directly, same as before conversion.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
GroupMembers map[string]bool `json:"groupMembers"`
Tags map[string]map[string]string `json:"tags"`
diff --git a/services/quicksight/multi_region_test.go b/services/quicksight/multi_region_test.go
index a432ebd472..730bedf430 100644
--- a/services/quicksight/multi_region_test.go
+++ b/services/quicksight/multi_region_test.go
@@ -2,7 +2,8 @@ package quicksight_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/quicksight"
)
@@ -176,8 +178,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/ram/errsweep_wire_shape_test.go b/services/ram/errsweep_wire_shape_test.go
index 6a929860e5..1599357a91 100644
--- a/services/ram/errsweep_wire_shape_test.go
+++ b/services/ram/errsweep_wire_shape_test.go
@@ -1,7 +1,7 @@
package ram_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/ram"
@@ -61,7 +62,7 @@ func TestHandler_MalformedBodyCarriesTypedErrorCode(t *testing.T) {
require.NoError(t, err)
var decoded map[string]any
- require.NoError(t, json.Unmarshal(raw, &decoded))
+ require.NoError(t, json.Unmarshal(raw, &decoded, jsonx.V1()))
require.Equal(t, "InvalidParameterException", decoded["__type"],
"raw response body must carry __type; got: %s", raw)
require.NotEmpty(t, decoded["message"])
diff --git a/services/ram/handler.go b/services/ram/handler.go
index 59688b40cf..3e5c5eff49 100644
--- a/services/ram/handler.go
+++ b/services/ram/handler.go
@@ -3,7 +3,7 @@ package ram
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -17,6 +17,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/collections"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -669,7 +670,7 @@ func writeInternalServerError(c *echo.Context) error {
payload, err := json.Marshal(map[string]string{
keyTypeField: "ServerInternalException",
keyMessageField: "internal server error",
- })
+ }, jsonx.V1())
if err != nil {
return err
}
@@ -724,7 +725,7 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
if errors.Is(err, idempotency.ErrParamsMismatch) {
payload, _ := json.Marshal(map[string]string{
keyTypeField: "IdempotentParameterMismatchException", keyMessageField: err.Error(),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
}
@@ -732,19 +733,15 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
for _, e := range errCodeLookup {
if errors.Is(err, e.err) {
payload, _ := json.Marshal(
- map[string]string{keyTypeField: e.code, keyMessageField: errorMessage(err, e.code)},
+ map[string]string{keyTypeField: e.code, keyMessageField: errorMessage(err, e.code)}, jsonx.V1(),
)
return c.JSONBlob(http.StatusBadRequest, payload)
}
}
-
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, errInvalidRequest), errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
// These branches used to omit keyTypeField entirely, so
// restjson.GetErrorInfo (aws-sdk-go-v2 aws/protocol/restjson/
// decoder_util.go:15) found no code in the header (unset) or the
diff --git a/services/ram/handler_permission_versions.go b/services/ram/handler_permission_versions.go
index e944138c6a..8ec3b189bb 100644
--- a/services/ram/handler_permission_versions.go
+++ b/services/ram/handler_permission_versions.go
@@ -2,7 +2,7 @@ package ram
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strconv"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createPermissionVersionRequest struct {
@@ -29,7 +30,7 @@ type createPermissionVersionResponse struct {
func (h *Handler) handleCreatePermissionVersion(_ context.Context, body []byte) ([]byte, error) {
var req createPermissionVersionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -72,7 +73,7 @@ func (h *Handler) handleCreatePermissionVersion(_ context.Context, body []byte)
pv := p.Versions[version]
return json.Marshal(
- createPermissionVersionResponse{Permission: toPermissionDetailObject(p, pv)},
+ createPermissionVersionResponse{Permission: toPermissionDetailObject(p, pv)}, jsonx.V1(),
)
}
@@ -113,7 +114,7 @@ func (h *Handler) handleDeletePermissionVersion(
}
return json.Marshal(
- deletePermissionVersionResponse{ReturnValue: true, PermissionStatus: "DELETING"},
+ deletePermissionVersionResponse{ReturnValue: true, PermissionStatus: "DELETING"}, jsonx.V1(),
)
}
@@ -146,7 +147,7 @@ func toPermissionVersionSummaryObject(p *Permission, pv *PermissionVersion) perm
func (h *Handler) handleListPermissionVersions(_ context.Context, body []byte) ([]byte, error) {
var req listPermissionVersionsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -175,7 +176,7 @@ func (h *Handler) handleListPermissionVersions(_ context.Context, body []byte) (
return nil, pErr2
}
- return json.Marshal(listPermissionVersionsResponse{NextToken: nextToken, Permissions: paged})
+ return json.Marshal(listPermissionVersionsResponse{NextToken: nextToken, Permissions: paged}, jsonx.V1())
}
type setDefaultPermissionVersionRequest struct {
@@ -192,7 +193,7 @@ func (h *Handler) handleSetDefaultPermissionVersion(
body []byte,
) ([]byte, error) {
var req setDefaultPermissionVersionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -208,5 +209,5 @@ func (h *Handler) handleSetDefaultPermissionVersion(
return nil, err
}
- return json.Marshal(setDefaultPermissionVersionResponse{ReturnValue: true})
+ return json.Marshal(setDefaultPermissionVersionResponse{ReturnValue: true}, jsonx.V1())
}
diff --git a/services/ram/handler_permission_versions_test.go b/services/ram/handler_permission_versions_test.go
index 19147a50b4..ef1213231b 100644
--- a/services/ram/handler_permission_versions_test.go
+++ b/services/ram/handler_permission_versions_test.go
@@ -1,7 +1,7 @@
package ram_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ram"
)
@@ -44,7 +45,7 @@ func TestListPermissionVersions_BuiltIn(t *testing.T) {
Version string `json:"version"`
} `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Permissions, 1)
assert.Equal(t, "1", resp.Permissions[0].Version)
})
@@ -358,7 +359,7 @@ func TestListPermissionVersions_Pagination(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp["__type"])
return
@@ -367,7 +368,7 @@ func TestListPermissionVersions_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp respBody
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
totalSeen += len(resp.Permissions)
nextToken = resp.NextToken
diff --git a/services/ram/handler_permissions.go b/services/ram/handler_permissions.go
index 6f29b8afbc..0e3f9ebd33 100644
--- a/services/ram/handler_permissions.go
+++ b/services/ram/handler_permissions.go
@@ -2,13 +2,14 @@ package ram
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strconv"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// permissionStatusAttachable is the steady-state PermissionStatus for every
@@ -134,7 +135,7 @@ type createPermissionResponse struct {
func (h *Handler) handleCreatePermission(_ context.Context, body []byte) ([]byte, error) {
var req createPermissionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -169,7 +170,7 @@ func (h *Handler) handleCreatePermission(_ context.Context, body []byte) ([]byte
return nil, err
}
- return json.Marshal(createPermissionResponse{Permission: toPermissionSummaryObject(p)})
+ return json.Marshal(createPermissionResponse{Permission: toPermissionSummaryObject(p)}, jsonx.V1())
}
type deletePermissionResponse struct {
@@ -187,7 +188,7 @@ func (h *Handler) handleDeletePermission(_ context.Context, c *echo.Context) ([]
return nil, err
}
- return json.Marshal(deletePermissionResponse{ReturnValue: true, PermissionStatus: "DELETING"})
+ return json.Marshal(deletePermissionResponse{ReturnValue: true, PermissionStatus: "DELETING"}, jsonx.V1())
}
type getPermissionRequest struct {
@@ -201,7 +202,7 @@ type getPermissionResponse struct {
func (h *Handler) handleGetPermission(_ context.Context, body []byte) ([]byte, error) {
var req getPermissionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -214,7 +215,7 @@ func (h *Handler) handleGetPermission(_ context.Context, body []byte) ([]byte, e
return nil, err
}
- return json.Marshal(getPermissionResponse{Permission: toPermissionDetailObject(p, pv)})
+ return json.Marshal(getPermissionResponse{Permission: toPermissionDetailObject(p, pv)}, jsonx.V1())
}
type listPermissionsRequest struct {
@@ -231,7 +232,7 @@ type listPermissionsResponse struct {
func (h *Handler) handleListPermissions(_ context.Context, body []byte) ([]byte, error) {
var req listPermissionsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -252,7 +253,7 @@ func (h *Handler) handleListPermissions(_ context.Context, body []byte) ([]byte,
return nil, err
}
- return json.Marshal(listPermissionsResponse{NextToken: nextToken, Permissions: page})
+ return json.Marshal(listPermissionsResponse{NextToken: nextToken, Permissions: page}, jsonx.V1())
}
type promotePermissionCreatedFromPolicyRequest struct {
@@ -269,7 +270,7 @@ func (h *Handler) handlePromotePermissionCreatedFromPolicy(
body []byte,
) ([]byte, error) {
var req promotePermissionCreatedFromPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -283,6 +284,6 @@ func (h *Handler) handlePromotePermissionCreatedFromPolicy(
}
return json.Marshal(
- promotePermissionCreatedFromPolicyResponse{Permission: toPermissionSummaryObject(p)},
+ promotePermissionCreatedFromPolicyResponse{Permission: toPermissionSummaryObject(p)}, jsonx.V1(),
)
}
diff --git a/services/ram/handler_permissions_test.go b/services/ram/handler_permissions_test.go
index b5dee68c5d..82106b869e 100644
--- a/services/ram/handler_permissions_test.go
+++ b/services/ram/handler_permissions_test.go
@@ -1,7 +1,8 @@
package ram_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ram"
)
@@ -45,7 +47,7 @@ func TestBuiltInPermissions_Present(t *testing.T) {
PermissionType string `json:"permissionType"`
} `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotEmpty(t, resp.Permissions, "built-in permission for %s should exist", tt.resourceType)
assert.Equal(t, tt.resourceType, resp.Permissions[0].ResourceType)
assert.Equal(t, "AWS_MANAGED", resp.Permissions[0].PermissionType)
@@ -61,9 +63,9 @@ func TestBuiltInPermissions_Count(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp struct {
- Permissions []json.RawMessage `json:"permissions"`
+ Permissions []jsontext.Value `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Permissions, ram.BuiltInPermissionCount)
}
@@ -106,9 +108,9 @@ func TestListPermissions_TypeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp struct {
- Permissions []json.RawMessage `json:"permissions"`
+ Permissions []jsontext.Value `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Permissions, tt.wantCount)
})
}
@@ -174,9 +176,9 @@ func TestListPermissions_TypeFilter_WithCustom(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp struct {
- Permissions []json.RawMessage `json:"permissions"`
+ Permissions []jsontext.Value `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Permissions, tt.wantCount)
})
}
@@ -229,7 +231,7 @@ func TestBuiltInPermission_PermissionType(t *testing.T) {
IsResourceTypeDefault bool `json:"isResourceTypeDefault"`
} `json:"permission"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantPermissionType, resp.Permission.PermissionType)
assert.Equal(t, tt.wantIsDefault, resp.Permission.IsResourceTypeDefault)
})
@@ -289,7 +291,7 @@ func TestDeleteCustomPermission_Allowed(t *testing.T) {
Arn string `json:"arn"`
} `json:"permission"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
path := fmt.Sprintf("/deletepermission?permissionArn=%s", createResp.Permission.Arn)
rec := doRAMRawRequest(t, h, http.MethodDelete, path, nil)
@@ -338,12 +340,12 @@ func TestPermissionResponses_NoResourceRegionScopeField(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp struct {
- Permission json.RawMessage `json:"permission"`
+ Permission jsontext.Value `json:"permission"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
- var permFields map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(resp.Permission, &permFields))
+ var permFields map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(resp.Permission, &permFields, jsonx.V1()))
assert.NotContains(t, permFields, "resourceRegionScope")
})
}
@@ -379,7 +381,7 @@ func TestIsResourceTypeDefault(t *testing.T) {
IsResourceTypeDefault bool `json:"isResourceTypeDefault"`
} `json:"permission"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantDefault, resp.Permission.IsResourceTypeDefault)
})
}
@@ -402,7 +404,7 @@ func TestCustomerPermission_NotResourceTypeDefault(t *testing.T) {
IsResourceTypeDefault bool `json:"isResourceTypeDefault"`
} `json:"permission"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
assert.False(t, createResp.Permission.IsResourceTypeDefault)
}
@@ -446,7 +448,7 @@ func TestListPermissions_ResourceTypeFilter(t *testing.T) {
ResourceType string `json:"resourceType"`
} `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tt.wantMinCount == 0 {
assert.Empty(t, resp.Permissions)
@@ -480,7 +482,7 @@ func TestListPermissions_ResourceTypeFilter_CaseInsensitive(t *testing.T) {
ResourceType string `json:"resourceType"`
} `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotEmpty(
t, resp.Permissions,
"resourceType filter must match case-insensitively, per the doc's own example",
@@ -527,12 +529,12 @@ func TestGetPermission_BuiltIn_HasPolicy(t *testing.T) {
Permission string `json:"permission"`
} `json:"permission"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.Permission.Permission, "built-in permission should have non-empty policy")
// Policy should be valid JSON.
var policy map[string]any
- assert.NoError(t, json.Unmarshal([]byte(resp.Permission.Permission), &policy))
+ assert.NoError(t, json.Unmarshal([]byte(resp.Permission.Permission), &policy, jsonx.V1()))
})
}
}
@@ -558,7 +560,7 @@ func TestListPermissions_SummaryHasType(t *testing.T) {
PermissionType string `json:"permissionType"`
} `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for _, p := range resp.Permissions {
assert.NotEmpty(t, p.PermissionType, "permission %q should have PermissionType set", p.Name)
@@ -669,7 +671,7 @@ func TestPermissionNotFound_UsesUnknownResourceException(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "UnknownResourceException", resp["__type"])
}
@@ -949,7 +951,7 @@ func TestListPermissions_Pagination(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp["__type"])
return
@@ -958,7 +960,7 @@ func TestListPermissions_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp respBody
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pages++
totalSeen += len(resp.Permissions)
@@ -1069,7 +1071,7 @@ func TestPromotePermissionCreatedFromPolicy_Table(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
perm := resp["permission"].(map[string]any)
assert.Equal(t, tc.wantType, perm["permissionType"])
assert.Equal(t, tc.wantName, perm["name"])
@@ -1087,7 +1089,7 @@ func TestListPermissions_Smoke(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
perms, ok := listResp["permissions"].([]any)
require.True(t, ok)
assert.NotEmpty(t, perms, "ListPermissions must return the built-in AWS-managed permissions")
@@ -1099,7 +1101,7 @@ func TestListPermissions_Smoke(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var versionsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &versionsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &versionsResp, jsonx.V1()))
versions, ok := versionsResp["permissions"].([]any)
require.True(t, ok)
require.NotEmpty(t, versions, "AWSRAMDefaultPermissionEC2Subnet must have at least one version")
diff --git a/services/ram/handler_principals.go b/services/ram/handler_principals.go
index e554023d3d..b4f413b08b 100644
--- a/services/ram/handler_principals.go
+++ b/services/ram/handler_principals.go
@@ -2,9 +2,11 @@ package ram
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type principalObject struct {
@@ -42,7 +44,7 @@ type listPrincipalsResponse struct {
func (h *Handler) handleListPrincipals(_ context.Context, body []byte) ([]byte, error) {
var req listPrincipalsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -81,5 +83,5 @@ func (h *Handler) handleListPrincipals(_ context.Context, body []byte) ([]byte,
return nil, err
}
- return json.Marshal(listPrincipalsResponse{NextToken: nextToken, Principals: page})
+ return json.Marshal(listPrincipalsResponse{NextToken: nextToken, Principals: page}, jsonx.V1())
}
diff --git a/services/ram/handler_principals_test.go b/services/ram/handler_principals_test.go
index 81a7e21b78..8678f399c5 100644
--- a/services/ram/handler_principals_test.go
+++ b/services/ram/handler_principals_test.go
@@ -1,7 +1,7 @@
package ram_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ram"
)
@@ -31,7 +32,7 @@ func TestListPrincipals_Pagination(t *testing.T) {
ResourceShareArn string `json:"resourceShareArn"`
} `json:"resourceShare"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
shareARN := createResp.ResourceShare.ResourceShareArn
require.NotEmpty(t, shareARN)
@@ -104,7 +105,7 @@ func TestListPrincipals_Pagination(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp["__type"])
return
@@ -113,7 +114,7 @@ func TestListPrincipals_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp respBody
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pages++
totalSeen += len(resp.Principals)
@@ -151,7 +152,7 @@ func TestListPrincipals_ResourceShareArnsFilter(t *testing.T) {
ResourceShareArn string `json:"resourceShareArn"`
} `json:"resourceShare"`
}
- require.NoError(t, json.Unmarshal(createA.Body.Bytes(), &respA))
+ require.NoError(t, json.Unmarshal(createA.Body.Bytes(), &respA, jsonx.V1()))
shareArnA := respA.ResourceShare.ResourceShareArn
require.NotEmpty(t, shareArnA)
@@ -174,7 +175,7 @@ func TestListPrincipals_ResourceShareArnsFilter(t *testing.T) {
ResourceShareArn string `json:"resourceShareArn"`
} `json:"principals"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(
t, resp.Principals, 1,
"resourceShareArns must scope results to the requested share, not return every share",
@@ -229,7 +230,7 @@ func TestListPrincipals_ResourceOwnerFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
principals := resp["principals"].([]any)
assert.Len(t, principals, tc.wantCount)
})
diff --git a/services/ram/handler_resource_shares.go b/services/ram/handler_resource_shares.go
index 9e53ccad40..36d64430a3 100644
--- a/services/ram/handler_resource_shares.go
+++ b/services/ram/handler_resource_shares.go
@@ -2,13 +2,14 @@ package ram
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// resourceShareObject is the JSON representation of a ResourceShare.
@@ -76,7 +77,7 @@ type createResourceShareResponse struct {
func (h *Handler) handleCreateResourceShare(_ context.Context, body []byte) ([]byte, error) {
var req createResourceShareRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -109,7 +110,7 @@ func (h *Handler) handleCreateResourceShare(_ context.Context, body []byte) ([]b
return json.Marshal(createResourceShareResponse{
ResourceShare: toResourceShareObject(rs),
- })
+ }, jsonx.V1())
}
func (h *Handler) createResourceShare(req createResourceShareRequest) (*ResourceShare, error) {
@@ -173,7 +174,7 @@ type getResourceSharesResponse struct {
func (h *Handler) handleGetResourceShares(_ context.Context, body []byte) ([]byte, error) {
var req getResourceSharesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -201,7 +202,7 @@ func (h *Handler) handleGetResourceShares(_ context.Context, body []byte) ([]byt
return nil, err
}
- return json.Marshal(getResourceSharesResponse{NextToken: nextToken, ResourceShares: page})
+ return json.Marshal(getResourceSharesResponse{NextToken: nextToken, ResourceShares: page}, jsonx.V1())
}
// getResourceSharesByARN looks up each requested ARN individually, applying
@@ -322,7 +323,7 @@ type updateResourceShareResponse struct {
func (h *Handler) handleUpdateResourceShare(_ context.Context, body []byte) ([]byte, error) {
var req updateResourceShareRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -349,7 +350,7 @@ func (h *Handler) handleUpdateResourceShare(_ context.Context, body []byte) ([]b
return json.Marshal(updateResourceShareResponse{
ResourceShare: toResourceShareObject(rs),
- })
+ }, jsonx.V1())
}
type deleteResourceShareResponse struct {
@@ -373,7 +374,7 @@ func (h *Handler) handleDeleteResourceShare(_ context.Context, c *echo.Context)
return nil, err
}
- return json.Marshal(deleteResourceShareResponse{ReturnValue: true})
+ return json.Marshal(deleteResourceShareResponse{ReturnValue: true}, jsonx.V1())
}
type enableSharingWithAwsOrganizationResponse struct {
@@ -385,7 +386,7 @@ func (h *Handler) handleEnableSharingWithAwsOrganization() ([]byte, error) {
return nil, err
}
- return json.Marshal(enableSharingWithAwsOrganizationResponse{ReturnValue: true})
+ return json.Marshal(enableSharingWithAwsOrganizationResponse{ReturnValue: true}, jsonx.V1())
}
type promoteResourceShareCreatedFromPolicyResponse struct {
@@ -415,5 +416,5 @@ func (h *Handler) handlePromoteResourceShareCreatedFromPolicy(
return nil, err
}
- return json.Marshal(promoteResourceShareCreatedFromPolicyResponse{ReturnValue: true})
+ return json.Marshal(promoteResourceShareCreatedFromPolicyResponse{ReturnValue: true}, jsonx.V1())
}
diff --git a/services/ram/handler_resource_shares_test.go b/services/ram/handler_resource_shares_test.go
index 04648796c5..5602dc395e 100644
--- a/services/ram/handler_resource_shares_test.go
+++ b/services/ram/handler_resource_shares_test.go
@@ -1,7 +1,8 @@
package ram_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ram"
)
@@ -387,7 +389,7 @@ func TestCreateResourceShare_PermissionArns(t *testing.T) {
ResourceShareArn string `json:"resourceShareArn"`
} `json:"resourceShare"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
shareARN := createResp.ResourceShare.ResourceShareArn
// Verify permissions were associated.
@@ -397,9 +399,9 @@ func TestCreateResourceShare_PermissionArns(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp struct {
- Permissions []json.RawMessage `json:"permissions"`
+ Permissions []jsontext.Value `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp.Permissions, tt.wantPermCount)
})
}
@@ -455,9 +457,9 @@ func TestGetResourceShares_OtherAccounts(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp struct {
- ResourceShares []json.RawMessage `json:"resourceShares"`
+ ResourceShares []jsontext.Value `json:"resourceShares"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.ResourceShares, tt.wantCount)
})
}
@@ -499,7 +501,7 @@ func TestAllowExternalPrincipals_Default(t *testing.T) {
AllowExternalPrincipals bool `json:"allowExternalPrincipals"`
} `json:"resourceShare"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantExternal, resp.ResourceShare.AllowExternalPrincipals)
})
}
@@ -576,7 +578,7 @@ func TestCreateResourceShare_WithPrincipalsAndResources(t *testing.T) {
ResourceShareArn string `json:"resourceShareArn"`
} `json:"resourceShare"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &shareResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &shareResp, jsonx.V1()))
shareARN := shareResp.ResourceShare.ResourceShareArn
// Check principals.
@@ -587,9 +589,9 @@ func TestCreateResourceShare_WithPrincipalsAndResources(t *testing.T) {
require.Equal(t, http.StatusOK, principalRec.Code)
var principalResp struct {
- Principals []json.RawMessage `json:"principals"`
+ Principals []jsontext.Value `json:"principals"`
}
- require.NoError(t, json.Unmarshal(principalRec.Body.Bytes(), &principalResp))
+ require.NoError(t, json.Unmarshal(principalRec.Body.Bytes(), &principalResp, jsonx.V1()))
assert.Len(t, principalResp.Principals, tt.wantPrincipals)
// Check resources.
@@ -600,9 +602,9 @@ func TestCreateResourceShare_WithPrincipalsAndResources(t *testing.T) {
require.Equal(t, http.StatusOK, resourceRec.Code)
var resourceResp struct {
- Resources []json.RawMessage `json:"resources"`
+ Resources []jsontext.Value `json:"resources"`
}
- require.NoError(t, json.Unmarshal(resourceRec.Body.Bytes(), &resourceResp))
+ require.NoError(t, json.Unmarshal(resourceRec.Body.Bytes(), &resourceResp, jsonx.V1()))
assert.Len(t, resourceResp.Resources, tt.wantResources)
})
}
@@ -654,7 +656,7 @@ func TestCreateResourceShare_AutoAssociatesDefaultPermission(t *testing.T) {
ResourceShareArn string `json:"resourceShareArn"`
} `json:"resourceShare"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &shareResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &shareResp, jsonx.V1()))
listRec := doRAMRequest(t, h, "/listresourcesharepermissions", map[string]any{
"resourceShareArn": shareResp.ResourceShare.ResourceShareArn,
@@ -666,7 +668,7 @@ func TestCreateResourceShare_AutoAssociatesDefaultPermission(t *testing.T) {
Arn string `json:"arn"`
} `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Permissions, 1)
assert.Equal(t, "arn:aws:ram::aws:permission/AWSRAMDefaultPermissionEC2Subnet", listResp.Permissions[0].Arn)
})
@@ -689,7 +691,7 @@ func TestCreateResourceShare_NoAutoAttachWithoutResources(t *testing.T) {
ResourceShareArn string `json:"resourceShareArn"`
} `json:"resourceShare"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &shareResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &shareResp, jsonx.V1()))
listRec := doRAMRequest(t, h, "/listresourcesharepermissions", map[string]any{
"resourceShareArn": shareResp.ResourceShare.ResourceShareArn,
@@ -725,7 +727,7 @@ func TestGetResourceShares_PermissionArnFilter(t *testing.T) {
Name string `json:"name"`
} `json:"resourceShares"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.ResourceShares, 1)
assert.Equal(t, "with-perm-share", resp.ResourceShares[0].Name)
}
@@ -761,7 +763,7 @@ func TestGetResourceShares_TagFilters(t *testing.T) {
Name string `json:"name"`
} `json:"resourceShares"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.ResourceShares, 1)
assert.Equal(t, "tagged-share", resp.ResourceShares[0].Name)
}
@@ -811,9 +813,9 @@ func TestGetResourceShares_NameFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp struct {
- ResourceShares []json.RawMessage `json:"resourceShares"`
+ ResourceShares []jsontext.Value `json:"resourceShares"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.ResourceShares, tt.wantCount)
})
}
@@ -863,7 +865,7 @@ func TestResourceShare_TagsInResponse(t *testing.T) {
} `json:"tags"`
} `json:"resourceShare"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.ResourceShare.Tags, tt.wantTags)
})
}
@@ -883,7 +885,7 @@ func TestResourceShare_FeatureSetStandard(t *testing.T) {
FeatureSet string `json:"featureSet"`
} `json:"resourceShare"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "STANDARD", resp.ResourceShare.FeatureSet)
}
@@ -1046,7 +1048,7 @@ func TestGetResourceShares_Pagination(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp["__type"])
return
@@ -1055,7 +1057,7 @@ func TestGetResourceShares_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp respBody
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pages++
totalSeen += len(resp.ResourceShares)
@@ -1087,7 +1089,7 @@ func TestGetResourceShares_StatusFilter(t *testing.T) {
ResourceShareArn string `json:"resourceShareArn"`
} `json:"resourceShare"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
delRec := doRAMRawRequest(t, h, http.MethodDelete,
"/deleteresourceshare?resourceShareArn="+createResp.ResourceShare.ResourceShareArn, nil)
@@ -1109,7 +1111,7 @@ func TestGetResourceShares_StatusFilter(t *testing.T) {
Name string `json:"name"`
} `json:"resourceShares"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.ResourceShares, 1)
assert.Equal(t, "live-share", resp.ResourceShares[0].Name)
}
@@ -1149,7 +1151,7 @@ func TestDeleteResourceShare_RemovesFromMap(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.Code)
var createBody map[string]any
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createBody))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &createBody, jsonx.V1()))
shareARN := createBody["resourceShare"].(map[string]any)["resourceShareArn"].(string)
require.NotEmpty(t, shareARN)
@@ -1168,7 +1170,7 @@ func TestDeleteResourceShare_RemovesFromMap(t *testing.T) {
require.Equal(t, http.StatusOK, listResp.Code)
var listBody map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listBody))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listBody, jsonx.V1()))
shares := listBody["resourceShares"].([]any)
for _, s := range shares {
@@ -1200,14 +1202,14 @@ func TestPaginationTokenIsOpaque(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
nextToken, _ := resp["nextToken"].(string)
require.NotEmpty(t, nextToken, "expected a nextToken for multi-page result")
// The token must not be a plain integer.
var plainInt int
- err := json.Unmarshal([]byte(nextToken), &plainInt)
+ err := json.Unmarshal([]byte(nextToken), &plainInt, jsonx.V1())
require.Error(t, err, "nextToken should not be a plain integer")
// Following the token must yield the next page without overlap.
@@ -1219,7 +1221,7 @@ func TestPaginationTokenIsOpaque(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
page1Shares := resp["resourceShares"].([]any)
page2Shares := resp2["resourceShares"].([]any)
diff --git a/services/ram/handler_resources.go b/services/ram/handler_resources.go
index 60683074c9..10d493c367 100644
--- a/services/ram/handler_resources.go
+++ b/services/ram/handler_resources.go
@@ -2,11 +2,12 @@ package ram
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -52,7 +53,7 @@ type listResourcesResponse struct {
func (h *Handler) handleListResources(_ context.Context, body []byte) ([]byte, error) {
var req listResourcesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -85,7 +86,7 @@ func (h *Handler) handleListResources(_ context.Context, body []byte) ([]byte, e
return nil, err
}
- return json.Marshal(listResourcesResponse{NextToken: nextToken, Resources: page})
+ return json.Marshal(listResourcesResponse{NextToken: nextToken, Resources: page}, jsonx.V1())
}
type listPendingInvitationResourcesRequest struct {
@@ -104,7 +105,7 @@ func (h *Handler) handleListPendingInvitationResources(
body []byte,
) ([]byte, error) {
var req listPendingInvitationResourcesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -129,7 +130,7 @@ func (h *Handler) handleListPendingInvitationResources(
}
return json.Marshal(
- listPendingInvitationResourcesResponse{NextToken: nextToken, Resources: page},
+ listPendingInvitationResourcesResponse{NextToken: nextToken, Resources: page}, jsonx.V1(),
)
}
@@ -147,7 +148,7 @@ type getResourcePoliciesResponse struct {
func (h *Handler) handleGetResourcePolicies(_ context.Context, body []byte) ([]byte, error) {
var req getResourcePoliciesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -177,7 +178,7 @@ func (h *Handler) handleGetResourcePolicies(_ context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(getResourcePoliciesResponse{NextToken: nextToken, Policies: page})
+ return json.Marshal(getResourcePoliciesResponse{NextToken: nextToken, Policies: page}, jsonx.V1())
}
type resourceTypeObject struct {
@@ -320,7 +321,7 @@ type listResourceTypesRequest struct {
func (h *Handler) handleListResourceTypes(_ context.Context, body []byte) ([]byte, error) {
var req listResourceTypesRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -350,7 +351,7 @@ func (h *Handler) handleListResourceTypes(_ context.Context, body []byte) ([]byt
pg := page.New(types, req.NextToken, limit, len(awsShareableResourceTypes))
- return json.Marshal(listResourceTypesResponse{ResourceTypes: pg.Data, NextToken: pg.Next})
+ return json.Marshal(listResourceTypesResponse{ResourceTypes: pg.Data, NextToken: pg.Next}, jsonx.V1())
}
// associatedSourceObject is the JSON representation of an AssociatedSource (RAM's wire
@@ -386,7 +387,7 @@ type listSourceAssociationsRequest struct {
func (h *Handler) handleListSourceAssociations(_ context.Context, body []byte) ([]byte, error) {
var req listSourceAssociationsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -408,5 +409,5 @@ func (h *Handler) handleListSourceAssociations(_ context.Context, body []byte) (
return nil, err
}
- return json.Marshal(listSourceAssociationsResponse{NextToken: nextToken, SourceAssociations: page})
+ return json.Marshal(listSourceAssociationsResponse{NextToken: nextToken, SourceAssociations: page}, jsonx.V1())
}
diff --git a/services/ram/handler_resources_test.go b/services/ram/handler_resources_test.go
index ebbcbd2619..69ee1aa8a5 100644
--- a/services/ram/handler_resources_test.go
+++ b/services/ram/handler_resources_test.go
@@ -1,7 +1,8 @@
package ram_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ram"
)
@@ -28,7 +30,7 @@ func TestListSourceAssociations_WireShape(t *testing.T) {
SourceID string `json:"sourceId"`
} `json:"sourceAssociations"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp.SourceAssociations)
assert.NotContains(
t, rec.Body.String(), `"associations"`,
@@ -106,7 +108,7 @@ func TestResourceRegionScope_InListResources(t *testing.T) {
ResourceRegionScope string `json:"resourceRegionScope"`
} `json:"resources"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Resources, 1)
assert.Equal(t, tt.resourceARN, resp.Resources[0].Arn)
assert.Equal(t, tt.wantType, resp.Resources[0].Type)
@@ -148,7 +150,7 @@ func TestListResources_ResourceShareArnsFilter(t *testing.T) {
ResourceShareArn string `json:"resourceShareArn"`
} `json:"resources"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(
t, resp.Resources, 1,
"resourceShareArns must scope results to the requested share, not return every share",
@@ -215,7 +217,7 @@ func TestResourceTypeDerivation(t *testing.T) {
Type string `json:"type"`
} `json:"resources"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Resources, 1)
assert.Equal(t, tt.wantType, resp.Resources[0].Type)
})
@@ -291,9 +293,9 @@ func TestListPendingInvitationResources(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var resp struct {
- Resources []json.RawMessage `json:"resources"`
+ Resources []jsontext.Value `json:"resources"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Resources, tt.wantResources)
})
}
@@ -312,7 +314,7 @@ func TestListResourceTypes(t *testing.T) {
ServiceName string `json:"serviceName"`
} `json:"resourceTypes"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.ResourceTypes)
@@ -340,7 +342,7 @@ func TestListResourceTypes_ExpandedSet(t *testing.T) {
ResourceRegionScope string `json:"resourceRegionScope"`
} `json:"resourceTypes"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Greater(t, len(resp.ResourceTypes), 4, "should list more than the original 4 types")
@@ -490,7 +492,7 @@ func TestListResources_Pagination(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp["__type"])
return
@@ -499,7 +501,7 @@ func TestListResources_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp respBody
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pages++
totalSeen += len(resp.Resources)
@@ -580,7 +582,7 @@ func TestListPendingInvitationResources_Pagination(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp["__type"])
return
@@ -589,7 +591,7 @@ func TestListPendingInvitationResources_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp respBody
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp.Resources)
})
@@ -645,7 +647,7 @@ func TestGetResourcePolicies_Pagination(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp["__type"])
return
@@ -667,7 +669,7 @@ func TestGetResourcePolicies_EmptyArns(t *testing.T) {
var resp struct {
Policies []string `json:"policies"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp.Policies)
}
@@ -722,7 +724,7 @@ func TestListResources_ResourceOwnerFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
resources := resp["resources"].([]any)
assert.Len(t, resources, tc.wantCount)
})
@@ -788,7 +790,7 @@ func TestListResources_ResourceTypeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
resources := resp["resources"].([]any)
assert.Len(t, resources, tc.wantCount)
})
@@ -852,7 +854,7 @@ func TestGetResourcePolicies_OnlySharedResourcesGetPolicies(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
var policies []any
if p, ok := resp["policies"]; ok {
diff --git a/services/ram/handler_share_associations.go b/services/ram/handler_share_associations.go
index d4bdb79baa..c8f4df67bf 100644
--- a/services/ram/handler_share_associations.go
+++ b/services/ram/handler_share_associations.go
@@ -2,8 +2,10 @@ package ram
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// associationObject is the JSON representation of a ResourceShareAssociation.
@@ -46,7 +48,7 @@ type associateResourceShareResponse struct {
func (h *Handler) handleAssociateResourceShare(_ context.Context, body []byte) ([]byte, error) {
var req associateResourceShareRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -86,7 +88,7 @@ func (h *Handler) handleAssociateResourceShare(_ context.Context, body []byte) (
objs = append(objs, toAssociationObject(a))
}
- return json.Marshal(associateResourceShareResponse{ResourceShareAssociations: objs})
+ return json.Marshal(associateResourceShareResponse{ResourceShareAssociations: objs}, jsonx.V1())
}
type disassociateResourceShareRequest struct {
@@ -102,7 +104,7 @@ type disassociateResourceShareResponse struct {
func (h *Handler) handleDisassociateResourceShare(_ context.Context, body []byte) ([]byte, error) {
var req disassociateResourceShareRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -129,7 +131,7 @@ func (h *Handler) handleDisassociateResourceShare(_ context.Context, body []byte
objs = append(objs, toAssociationObject(a))
}
- return json.Marshal(disassociateResourceShareResponse{ResourceShareAssociations: objs})
+ return json.Marshal(disassociateResourceShareResponse{ResourceShareAssociations: objs}, jsonx.V1())
}
type getResourceShareAssociationsRequest struct {
@@ -152,7 +154,7 @@ func (h *Handler) handleGetResourceShareAssociations(
body []byte,
) ([]byte, error) {
var req getResourceShareAssociationsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -194,6 +196,6 @@ func (h *Handler) handleGetResourceShareAssociations(
}
return json.Marshal(
- getResourceShareAssociationsResponse{NextToken: nextToken, ResourceShareAssociations: page},
+ getResourceShareAssociationsResponse{NextToken: nextToken, ResourceShareAssociations: page}, jsonx.V1(),
)
}
diff --git a/services/ram/handler_share_associations_test.go b/services/ram/handler_share_associations_test.go
index a5b0568d58..e078029fa5 100644
--- a/services/ram/handler_share_associations_test.go
+++ b/services/ram/handler_share_associations_test.go
@@ -1,13 +1,15 @@
package ram_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ram"
)
@@ -271,9 +273,9 @@ func TestGetResourceShareAssociations_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp struct {
- ResourceShareAssociations []json.RawMessage `json:"resourceShareAssociations"`
+ ResourceShareAssociations []jsontext.Value `json:"resourceShareAssociations"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.ResourceShareAssociations, tt.wantCount)
})
}
@@ -315,15 +317,15 @@ func TestAssociationStatusMessage_InResponse(t *testing.T) {
StatusMessage string `json:"statusMessage"`
} `json:"resourceShareAssociations"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotEmpty(t, resp.ResourceShareAssociations)
// statusMessage is optional; verify the field exists in JSON (can be empty).
// We check by re-parsing as raw map.
var raw struct {
- ResourceShareAssociations []map[string]json.RawMessage `json:"resourceShareAssociations"`
+ ResourceShareAssociations []map[string]jsontext.Value `json:"resourceShareAssociations"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
require.NotEmpty(t, raw.ResourceShareAssociations)
// statusMessage should NOT appear when empty (omitempty).
_, present := raw.ResourceShareAssociations[0]["statusMessage"]
@@ -358,7 +360,7 @@ func TestAssociateResourceShare_AutoAssociatesDefaultPermission(t *testing.T) {
Arn string `json:"arn"`
} `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Permissions, 1)
assert.Equal(t, "arn:aws:ram::aws:permission/AWSRAMDefaultPermissionEC2Subnet", listResp.Permissions[0].Arn)
@@ -372,7 +374,7 @@ func TestAssociateResourceShare_AutoAssociatesDefaultPermission(t *testing.T) {
listRec = doRAMRequest(t, h, "/listresourcesharepermissions", map[string]any{
"resourceShareArn": rs.ARN,
})
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp.Permissions, 1, "adding a second resource of an already-covered type must not duplicate")
}
@@ -446,7 +448,7 @@ func TestGetResourceShareAssociations_Pagination(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp["__type"])
return
@@ -455,7 +457,7 @@ func TestGetResourceShareAssociations_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp respBody
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
totalSeen += len(resp.ResourceShareAssociations)
nextToken = resp.NextToken
diff --git a/services/ram/handler_share_invitations.go b/services/ram/handler_share_invitations.go
index 260c813ddd..02fc8f2f39 100644
--- a/services/ram/handler_share_invitations.go
+++ b/services/ram/handler_share_invitations.go
@@ -2,8 +2,10 @@ package ram
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// invitationObject is the JSON representation of a ResourceShareInvitation.
@@ -42,7 +44,7 @@ func (h *Handler) handleAcceptResourceShareInvitation(
body []byte,
) ([]byte, error) {
var req acceptResourceShareInvitationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -57,7 +59,7 @@ func (h *Handler) handleAcceptResourceShareInvitation(
return json.Marshal(acceptResourceShareInvitationResponse{
ResourceShareInvitation: toInvitationObject(inv),
- })
+ }, jsonx.V1())
}
type rejectResourceShareInvitationRequest struct {
@@ -73,7 +75,7 @@ func (h *Handler) handleRejectResourceShareInvitation(
body []byte,
) ([]byte, error) {
var req rejectResourceShareInvitationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -88,7 +90,7 @@ func (h *Handler) handleRejectResourceShareInvitation(
return json.Marshal(rejectResourceShareInvitationResponse{
ResourceShareInvitation: toInvitationObject(inv),
- })
+ }, jsonx.V1())
}
type getResourceShareInvitationsRequest struct {
@@ -108,7 +110,7 @@ func (h *Handler) handleGetResourceShareInvitations(
body []byte,
) ([]byte, error) {
var req getResourceShareInvitationsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -128,6 +130,6 @@ func (h *Handler) handleGetResourceShareInvitations(
}
return json.Marshal(
- getResourceShareInvitationsResponse{NextToken: nextToken, ResourceShareInvitations: page},
+ getResourceShareInvitationsResponse{NextToken: nextToken, ResourceShareInvitations: page}, jsonx.V1(),
)
}
diff --git a/services/ram/handler_share_invitations_test.go b/services/ram/handler_share_invitations_test.go
index 3681611d28..b7623c2207 100644
--- a/services/ram/handler_share_invitations_test.go
+++ b/services/ram/handler_share_invitations_test.go
@@ -1,7 +1,7 @@
package ram_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ram"
)
@@ -97,7 +98,7 @@ func TestGetResourceShareInvitations_Status(t *testing.T) {
Status string `json:"status"`
} `json:"resourceShareInvitations"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.ResourceShareInvitations, 1)
assert.Equal(t, tt.wantStatus, resp.ResourceShareInvitations[0].Status)
})
@@ -371,7 +372,7 @@ func TestGetResourceShareInvitations_Pagination(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp["__type"])
return
@@ -380,7 +381,7 @@ func TestGetResourceShareInvitations_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp respBody
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pages++
totalSeen += len(resp.ResourceShareInvitations)
@@ -417,7 +418,7 @@ func TestAcceptInvitation_HTTP(t *testing.T) {
Status string `json:"status"`
} `json:"resourceShareInvitation"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ACCEPTED", resp.ResourceShareInvitation.Status)
// Double-accept should return AlreadyAccepted.
diff --git a/services/ram/handler_share_permissions.go b/services/ram/handler_share_permissions.go
index 80ddf52212..dcac65641b 100644
--- a/services/ram/handler_share_permissions.go
+++ b/services/ram/handler_share_permissions.go
@@ -2,9 +2,11 @@ package ram
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strconv"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type listResourceSharePermissionsRequest struct {
@@ -24,7 +26,7 @@ func (h *Handler) handleListResourceSharePermissions(
body []byte,
) ([]byte, error) {
var req listResourceSharePermissionsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -45,7 +47,7 @@ func (h *Handler) handleListResourceSharePermissions(
}
return json.Marshal(
- listResourceSharePermissionsResponse{NextToken: nextToken, Permissions: page},
+ listResourceSharePermissionsResponse{NextToken: nextToken, Permissions: page}, jsonx.V1(),
)
}
@@ -65,7 +67,7 @@ func (h *Handler) handleAssociateResourceSharePermission(
body []byte,
) ([]byte, error) {
var req associateResourceSharePermissionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -83,7 +85,7 @@ func (h *Handler) handleAssociateResourceSharePermission(
return nil, err
}
- return json.Marshal(associateResourceSharePermissionResponse{ReturnValue: true})
+ return json.Marshal(associateResourceSharePermissionResponse{ReturnValue: true}, jsonx.V1())
}
type disassociateResourceSharePermissionRequest struct {
@@ -100,7 +102,7 @@ func (h *Handler) handleDisassociateResourceSharePermission(
body []byte,
) ([]byte, error) {
var req disassociateResourceSharePermissionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -116,7 +118,7 @@ func (h *Handler) handleDisassociateResourceSharePermission(
return nil, err
}
- return json.Marshal(disassociateResourceSharePermissionResponse{ReturnValue: true})
+ return json.Marshal(disassociateResourceSharePermissionResponse{ReturnValue: true}, jsonx.V1())
}
type replacePermissionAssociationsRequest struct {
@@ -166,7 +168,7 @@ func (h *Handler) handleReplacePermissionAssociations(
body []byte,
) ([]byte, error) {
var req replacePermissionAssociationsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -189,7 +191,7 @@ func (h *Handler) handleReplacePermissionAssociations(
return json.Marshal(replacePermissionAssociationsResponse{
ReplacePermissionAssociationsWork: toReplacePermissionAssociationsWorkObject(work),
- })
+ }, jsonx.V1())
}
// permissionAssociationObject matches types.AssociatedPermission
@@ -221,7 +223,7 @@ type listPermissionAssociationsResponse struct {
func (h *Handler) handleListPermissionAssociations(_ context.Context, body []byte) ([]byte, error) {
var req listPermissionAssociationsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -250,7 +252,7 @@ func (h *Handler) handleListPermissionAssociations(_ context.Context, body []byt
return nil, err
}
- return json.Marshal(listPermissionAssociationsResponse{NextToken: nextToken, Permissions: page})
+ return json.Marshal(listPermissionAssociationsResponse{NextToken: nextToken, Permissions: page}, jsonx.V1())
}
// shareMatchesPermissionFilter applies the featureSet and associationStatus filters.
@@ -288,7 +290,7 @@ func (h *Handler) handleListReplacePermissionAssociationsWork(
body []byte,
) ([]byte, error) {
var req listReplacePermissionAssociationsWorkRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -307,5 +309,5 @@ func (h *Handler) handleListReplacePermissionAssociationsWork(
return json.Marshal(listReplacePermissionAssociationsWorkResponse{
NextToken: nextToken,
ReplacePermissionAssociationsWorks: page,
- })
+ }, jsonx.V1())
}
diff --git a/services/ram/handler_share_permissions_test.go b/services/ram/handler_share_permissions_test.go
index 2f3ef71190..91e2f03040 100644
--- a/services/ram/handler_share_permissions_test.go
+++ b/services/ram/handler_share_permissions_test.go
@@ -1,7 +1,7 @@
package ram_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ram"
)
@@ -75,7 +76,7 @@ func TestAssociateBuiltInPermission(t *testing.T) {
PermissionType string `json:"permissionType"`
} `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Permissions, 1)
assert.Equal(t, tt.permARN, resp.Permissions[0].Arn)
assert.Equal(t, "AWS_MANAGED", resp.Permissions[0].PermissionType)
@@ -145,7 +146,7 @@ func TestReplacePermissionAssociations(t *testing.T) {
ID string `json:"id"`
} `json:"replacePermissionAssociationsWork"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.ReplacePermissionAssociationsWork.ID)
// This mock performs the association swap synchronously (no async worker),
// so the work item is already COMPLETED by the time the call returns.
@@ -181,7 +182,7 @@ func TestListReplacePermissionAssociationsWork(t *testing.T) {
ID string `json:"id"`
} `json:"replacePermissionAssociationsWork"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
workID := createResp.ReplacePermissionAssociationsWork.ID
require.NotEmpty(t, workID)
@@ -203,7 +204,7 @@ func TestListReplacePermissionAssociationsWork(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp listResp
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.ReplacePermissionAssociationsWorks, 1)
w := resp.ReplacePermissionAssociationsWorks[0]
@@ -224,7 +225,7 @@ func TestListReplacePermissionAssociationsWork(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp listResp
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp.ReplacePermissionAssociationsWorks)
})
@@ -237,7 +238,7 @@ func TestListReplacePermissionAssociationsWork(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp listResp
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp.ReplacePermissionAssociationsWorks)
})
}
@@ -415,7 +416,7 @@ func TestDisassociateResourceSharePermission_BlockedWhileResourceOfTypeAttached(
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "OperationNotPermittedException", errResp["__type"])
// The permission must still be associated -- the disassociation was refused, not
@@ -516,7 +517,7 @@ func TestListResourceSharePermissions_Pagination(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp["__type"])
return
@@ -525,7 +526,7 @@ func TestListResourceSharePermissions_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp respBody
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pages++
nextToken = resp.NextToken
@@ -580,7 +581,7 @@ func TestListPermissionAssociations_PermissionVersionFilter(t *testing.T) {
PermissionVersion string `json:"permissionVersion"`
} `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(
t, resp.Permissions, 1,
"permissionVersion must scope results to shares pinned to that version",
@@ -670,7 +671,7 @@ func TestListPermissionAssociations_Pagination(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp["__type"])
return
@@ -679,7 +680,7 @@ func TestListPermissionAssociations_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp respBody
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pages++
totalSeen += len(resp.Permissions)
@@ -712,7 +713,7 @@ func TestListResourceSharePermissions_RealData(t *testing.T) {
ResourceShareArn string `json:"resourceShareArn"`
} `json:"resourceShare"`
}
- require.NoError(t, json.Unmarshal(shareRec.Body.Bytes(), &shareResp))
+ require.NoError(t, json.Unmarshal(shareRec.Body.Bytes(), &shareResp, jsonx.V1()))
shareARN := shareResp.ResourceShare.ResourceShareArn
permRec := doRAMRequest(t, h, "/createpermission", map[string]any{
@@ -727,7 +728,7 @@ func TestListResourceSharePermissions_RealData(t *testing.T) {
Arn string `json:"arn"`
} `json:"permission"`
}
- require.NoError(t, json.Unmarshal(permRec.Body.Bytes(), &permResp))
+ require.NoError(t, json.Unmarshal(permRec.Body.Bytes(), &permResp, jsonx.V1()))
permARN := permResp.Permission.Arn
// Associate permission with share.
@@ -748,7 +749,7 @@ func TestListResourceSharePermissions_RealData(t *testing.T) {
Arn string `json:"arn"`
} `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Permissions, 1)
assert.Equal(t, permARN, listResp.Permissions[0].Arn)
}
@@ -781,7 +782,7 @@ func TestListResourceSharePermissions_ReportsAssociatedVersion(t *testing.T) {
ResourceShareArn string `json:"resourceShareArn"`
} `json:"resourceShare"`
}
- require.NoError(t, json.Unmarshal(shareRec.Body.Bytes(), &shareResp))
+ require.NoError(t, json.Unmarshal(shareRec.Body.Bytes(), &shareResp, jsonx.V1()))
shareARN := shareResp.ResourceShare.ResourceShareArn
permRec := doRAMRequest(t, h, "/createpermission", map[string]any{
@@ -796,7 +797,7 @@ func TestListResourceSharePermissions_ReportsAssociatedVersion(t *testing.T) {
Arn string `json:"arn"`
} `json:"permission"`
}
- require.NoError(t, json.Unmarshal(permRec.Body.Bytes(), &permResp))
+ require.NoError(t, json.Unmarshal(permRec.Body.Bytes(), &permResp, jsonx.V1()))
permARN := permResp.Permission.Arn
// Create a second version and make it the default, then pin the share to
@@ -833,7 +834,7 @@ func TestListResourceSharePermissions_ReportsAssociatedVersion(t *testing.T) {
DefaultVersion bool `json:"defaultVersion"`
} `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Permissions, 1)
assert.Equal(
t, "1", listResp.Permissions[0].Version,
diff --git a/services/ram/handler_tags.go b/services/ram/handler_tags.go
index 1a696f54b5..07afce382c 100644
--- a/services/ram/handler_tags.go
+++ b/services/ram/handler_tags.go
@@ -2,8 +2,10 @@ package ram
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type tagResourceRequest struct {
@@ -14,7 +16,7 @@ type tagResourceRequest struct {
func (h *Handler) handleTagResource(_ context.Context, body []byte) error {
var req tagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -42,7 +44,7 @@ type untagResourceRequest struct {
func (h *Handler) handleUntagResource(_ context.Context, body []byte) error {
var req untagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -73,7 +75,7 @@ type listTagsForResourceResponse struct {
func (h *Handler) handleListTagsForResource(_ context.Context, body []byte) ([]byte, error) {
var req listTagsForResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -91,5 +93,5 @@ func (h *Handler) handleListTagsForResource(_ context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(listTagsForResourceResponse{Tags: toTagObjects(tags)})
+ return json.Marshal(listTagsForResourceResponse{Tags: toTagObjects(tags)}, jsonx.V1())
}
diff --git a/services/ram/handler_tags_test.go b/services/ram/handler_tags_test.go
index 95ac326fd6..6c546df151 100644
--- a/services/ram/handler_tags_test.go
+++ b/services/ram/handler_tags_test.go
@@ -1,13 +1,14 @@
package ram_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ram"
)
@@ -221,7 +222,7 @@ func TestToTagObjectsSorted(t *testing.T) {
} `json:"tags"`
} `json:"resourceShares"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.ResourceShares, 1)
tags := resp.ResourceShares[0].Tags
@@ -259,7 +260,7 @@ func TestTagsRoundTrip(t *testing.T) {
Value string `json:"value"`
} `json:"tags"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Tags, 1)
assert.Equal(t, "env", listResp.Tags[0].Key)
assert.Equal(t, "prod", listResp.Tags[0].Value)
@@ -276,6 +277,6 @@ func TestTagsRoundTrip(t *testing.T) {
"resourceShareArn": rs.ARN,
})
require.Equal(t, http.StatusOK, listRec2.Code)
- require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &listResp, jsonx.V1()))
assert.Empty(t, listResp.Tags)
}
diff --git a/services/ram/handler_test.go b/services/ram/handler_test.go
index 56b3781ab5..7efb50f55c 100644
--- a/services/ram/handler_test.go
+++ b/services/ram/handler_test.go
@@ -2,7 +2,8 @@ package ram_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ram"
)
@@ -29,7 +31,7 @@ func doRAMRequest(t *testing.T, h *ram.Handler, path string, body any) *httptest
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -249,7 +251,7 @@ func TestHandler_Reset(t *testing.T) {
ResourceShares []any `json:"resourceShares"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.ResourceShares)
}
@@ -286,9 +288,9 @@ func TestReset_ReSeedsBuiltIns(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var custResp struct {
- Permissions []json.RawMessage `json:"permissions"`
+ Permissions []jsontext.Value `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &custResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &custResp, jsonx.V1()))
assert.Empty(t, custResp.Permissions, "customer permissions cleared after reset")
rec2 := doRAMRequest(t, h, "/listpermissions", map[string]any{
@@ -297,9 +299,9 @@ func TestReset_ReSeedsBuiltIns(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var awsResp struct {
- Permissions []json.RawMessage `json:"permissions"`
+ Permissions []jsontext.Value `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &awsResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &awsResp, jsonx.V1()))
assert.Len(t, awsResp.Permissions, ram.BuiltInPermissionCount,
"built-in permissions re-seeded after reset")
})
diff --git a/services/ram/idempotent_ops.go b/services/ram/idempotent_ops.go
index bb26a8dab5..294dd9f4ff 100644
--- a/services/ram/idempotent_ops.go
+++ b/services/ram/idempotent_ops.go
@@ -2,11 +2,12 @@ package ram
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func usesClientToken(op string) bool {
@@ -32,7 +33,7 @@ func (h *Handler) replayClientToken(
token := query.Get("clientToken")
var fields map[string]any
- if len(body) > 0 && json.Unmarshal(body, &fields) == nil {
+ if len(body) > 0 && json.Unmarshal(body, &fields, jsonx.V1()) == nil {
if t, ok := fields["clientToken"].(string); ok && token == "" {
token = t
}
diff --git a/services/ram/multi_region_test.go b/services/ram/multi_region_test.go
index a808a85ea5..c6ef35283a 100644
--- a/services/ram/multi_region_test.go
+++ b/services/ram/multi_region_test.go
@@ -2,7 +2,8 @@ package ram_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/ram"
)
@@ -178,8 +180,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/ram/permission_version_shape_test.go b/services/ram/permission_version_shape_test.go
index b23b59ec55..f14b14bc28 100644
--- a/services/ram/permission_version_shape_test.go
+++ b/services/ram/permission_version_shape_test.go
@@ -1,7 +1,7 @@
package ram_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/ram"
)
@@ -104,7 +105,7 @@ func Test_ListPermissionVersions_OmitsPolicyDocumentField(t *testing.T) {
var raw struct {
Permissions []map[string]any `json:"permissions"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
require.Len(t, raw.Permissions, 2)
for _, item := range raw.Permissions {
diff --git a/services/ram/persistence.go b/services/ram/persistence.go
index e2796621c3..5914e499fb 100644
--- a/services/ram/persistence.go
+++ b/services/ram/persistence.go
@@ -2,10 +2,12 @@ package ram
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -35,7 +37,7 @@ const ramSnapshotVersion = 1
// newer) build of this backend as though it were the current shape; see
// Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
SharePermissions map[string]map[string]int32 `json:"sharePermissions"`
AccountID string `json:"accountID"`
Region string `json:"region"`
@@ -88,7 +90,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.region,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "ram: failed to snapshot backend state", "error", err)
diff --git a/services/ram/policy_shares.go b/services/ram/policy_shares.go
index 9edd05861c..25cfa942bb 100644
--- a/services/ram/policy_shares.go
+++ b/services/ram/policy_shares.go
@@ -1,7 +1,7 @@
package ram
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// policyShareNamePrefix names every CREATED_FROM_POLICY share this backend creates via
@@ -227,7 +228,7 @@ func policyTemplateFromActions(actions []string) string {
"Effect": "Allow",
"Action": actions,
"Resource": "*",
- })
+ }, jsonx.V1())
if err != nil {
return `{"Effect":"Allow","Action":[],"Resource":"*"}`
}
diff --git a/services/rds/engine_dumpstore.go b/services/rds/engine_dumpstore.go
index bb85a6e748..e810e4cb6a 100644
--- a/services/rds/engine_dumpstore.go
+++ b/services/rds/engine_dumpstore.go
@@ -2,12 +2,14 @@ package rds
import (
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"os"
"path/filepath"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -47,7 +49,7 @@ func (s *dumpStore) save(key string, at time.Time, d engineData) error {
rec := dumpRecord{At: at, Key: key, Dump: d.dump, Password: d.password, DBName: d.dbName}
- raw, err := json.Marshal(rec) //nolint:gosec // the master password must come back with the restored data
+ raw, err := json.Marshal(rec, jsonx.V1())
if err != nil {
return fmt.Errorf("marshal dump: %w", err)
}
@@ -102,7 +104,7 @@ func (s *dumpStore) load() []dumpRecord {
}
var rec dumpRecord
- if json.Unmarshal(raw, &rec) == nil && rec.Key != "" {
+ if json.Unmarshal(raw, &rec, jsonx.V1()) == nil && rec.Key != "" {
out = append(out, rec)
}
}
diff --git a/services/rds/engine_pgext_docker_test.go b/services/rds/engine_pgext_docker_test.go
index bcba13989a..8fa2c9234a 100644
--- a/services/rds/engine_pgext_docker_test.go
+++ b/services/rds/engine_pgext_docker_test.go
@@ -3,7 +3,8 @@ package rds_test
import (
"context"
"database/sql"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/rds"
"github.com/blackbirdworks/gopherstack/services/s3"
@@ -71,7 +73,7 @@ func startPgExtEnv(t *testing.T) *pgExtEnv {
return
}
- out, _ := json.Marshal(map[string]any{"fn": r.PathValue("name"), "echo": json.RawMessage(in)})
+ out, _ := json.Marshal(map[string]any{"fn": r.PathValue("name"), "echo": jsontext.Value(in)}, jsonx.V1())
_, _ = w.Write(out)
})
mux.Handle("/", s3e)
diff --git a/services/rds/master_secret.go b/services/rds/master_secret.go
index 1a5e5f6202..119680524a 100644
--- a/services/rds/master_secret.go
+++ b/services/rds/master_secret.go
@@ -3,13 +3,14 @@ package rds
import (
"crypto/rand"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/url"
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -86,7 +87,7 @@ func masterSecretBody(user string) (string, error) {
return "", fmt.Errorf("generate master password: %w", err)
}
- body, err := json.Marshal(map[string]string{"username": user, "password": hex.EncodeToString(raw)})
+ body, err := json.Marshal(map[string]string{"username": user, "password": hex.EncodeToString(raw)}, jsonx.V1())
if err != nil {
return "", fmt.Errorf("encode master user secret: %w", err)
}
diff --git a/services/rds/persistence.go b/services/rds/persistence.go
index 9d0e03bcc1..f24572656d 100644
--- a/services/rds/persistence.go
+++ b/services/rds/persistence.go
@@ -2,11 +2,13 @@ package rds
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"slices"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -30,7 +32,7 @@ import (
const rdsSnapshotVersion = 3
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string][]Tag `json:"tags"`
ClusterRoles map[string][]DBClusterRole `json:"clusterRoles"`
InstanceRoles map[string]map[string]string `json:"instanceRoles"`
@@ -86,7 +88,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
DefaultCACertificateID: b.defaultCACertificateID,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "rds: failed to marshal snapshot", "error", err)
@@ -235,11 +237,11 @@ func (h *Handler) Snapshot(ctx context.Context) []byte {
return h.Backend.Snapshot(ctx)
}
- snaps := make(map[string]json.RawMessage)
+ snaps := make(map[string]jsontext.Value)
for region, b := range h.backends {
snaps[region] = b.Snapshot(ctx)
}
- data, _ := json.Marshal(snaps)
+ data, _ := json.Marshal(snaps, jsonx.V1())
return data
}
@@ -253,8 +255,8 @@ func (h *Handler) Restore(ctx context.Context, data []byte) error {
return h.Backend.Restore(ctx, data)
}
- var snaps map[string]json.RawMessage
- if err := json.Unmarshal(data, &snaps); err != nil {
+ var snaps map[string]jsontext.Value
+ if err := json.Unmarshal(data, &snaps, jsonx.V1()); err != nil {
// Fallback for backwards compatibility with single-region snapshots.
return h.Backend.Restore(ctx, data)
}
diff --git a/services/rds/persistence_test.go b/services/rds/persistence_test.go
index bf73b2a246..e99badbeaf 100644
--- a/services/rds/persistence_test.go
+++ b/services/rds/persistence_test.go
@@ -1,13 +1,15 @@
package rds_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rds"
)
@@ -21,16 +23,16 @@ func TestPersistence_SnapshotJSON_ContainsExtendedKeys(t *testing.T) {
snap := b.Snapshot(t.Context())
require.NotNil(t, snap)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &raw, jsonx.V1()))
// As of the Phase 3.3 pkgs/store conversion, resource collections backed by
// a store.Table are serialized under the nested "tables" blob (produced by
// store.Registry.SnapshotAll) rather than as top-level snapshot keys.
require.Contains(t, raw, "tables")
- var tables map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(raw["tables"], &tables))
+ var tables map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(raw["tables"], &tables, jsonx.V1()))
for _, key := range []string{
"customEngineVersions",
diff --git a/services/rdsdata/column_origin_realclient_test.go b/services/rdsdata/column_origin_realclient_test.go
index a9a36efccb..83227f7c8f 100644
--- a/services/rdsdata/column_origin_realclient_test.go
+++ b/services/rdsdata/column_origin_realclient_test.go
@@ -1,7 +1,7 @@
package rdsdata_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rdsdata"
)
@@ -93,7 +94,7 @@ func TestExecuteStatement_ColumnMetadata_TableOrigin_InsideTransaction(t *testin
require.Equal(t, http.StatusOK, rec.Code)
var begin map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &begin))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &begin, jsonx.V1()))
txID, ok := begin["transactionId"].(string)
require.True(t, ok)
@@ -107,7 +108,7 @@ func TestExecuteStatement_ColumnMetadata_TableOrigin_InsideTransaction(t *testin
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cols, ok := resp["columnMetadata"].([]any)
require.True(t, ok)
diff --git a/services/rdsdata/errsweep_wire_shape_test.go b/services/rdsdata/errsweep_wire_shape_test.go
index 892396fd70..e4a4d72ce3 100644
--- a/services/rdsdata/errsweep_wire_shape_test.go
+++ b/services/rdsdata/errsweep_wire_shape_test.go
@@ -1,7 +1,7 @@
package rdsdata_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/rdsdata"
@@ -61,7 +62,7 @@ func TestHandler_MalformedBodyCarriesTypedErrorCode(t *testing.T) {
require.NoError(t, err)
var decoded map[string]any
- require.NoError(t, json.Unmarshal(raw, &decoded))
+ require.NoError(t, json.Unmarshal(raw, &decoded, jsonx.V1()))
require.Equal(t, "BadRequestException", decoded["__type"],
"raw response body must carry __type; got: %s", raw)
require.NotEmpty(t, decoded["message"])
diff --git a/services/rdsdata/handler.go b/services/rdsdata/handler.go
index 7b8ef820ae..fea68e1acc 100644
--- a/services/rdsdata/handler.go
+++ b/services/rdsdata/handler.go
@@ -3,7 +3,7 @@ package rdsdata
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -257,7 +258,7 @@ func writeInternalServerError(c *echo.Context) error {
payload, err := json.Marshal(map[string]string{
keyTypeField: "InternalServerErrorException",
keyMessageField: "internal server error",
- })
+ }, jsonx.V1())
if err != nil {
return err
}
@@ -270,32 +271,32 @@ func stripCode(err error, code string) string {
}
func (h *Handler) handleError(c *echo.Context, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
var apiErr *apiError
switch {
case errors.As(err, &apiErr):
- payload, _ := json.Marshal(map[string]string{keyTypeField: apiErr.code, keyMessageField: apiErr.msg})
+ payload, _ := json.Marshal(
+ map[string]string{keyTypeField: apiErr.code, keyMessageField: apiErr.msg},
+ jsonx.V1(),
+ )
return c.JSONBlob(apiErr.status, payload)
case errors.Is(err, ErrTransactionNotFound):
payload, _ := json.Marshal(map[string]string{
keyTypeField: "TransactionNotFoundException",
keyMessageField: stripCode(err, "TransactionNotFoundException"),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusNotFound, payload)
case errIsValidation(err):
payload, _ := json.Marshal(map[string]string{
keyTypeField: "BadRequestException",
keyMessageField: stripCode(err, "BadRequestException"),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
case errors.Is(err, errInvalidRequest), errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
// These branches used to omit keyTypeField entirely, so
// restjson.GetErrorInfo (aws-sdk-go-v2 aws/protocol/restjson/
// decoder_util.go:15) found no code in the header (unset) or the
@@ -410,7 +411,7 @@ func validateRequiredFields(fields ...requiredField) error {
func (h *Handler) handleExecuteStatement(ctx context.Context, body []byte) ([]byte, error) {
var req executeStatementRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -489,7 +490,7 @@ func marshalExecuteResponse(
}
}
- out, err := json.Marshal(resp)
+ out, err := json.Marshal(resp, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("marshal response: %w", err)
}
@@ -532,7 +533,7 @@ func formatRecordsAsJSONString(records [][]Field, columns []ColumnMetadata) (str
rows[i] = row
}
- out, err := json.Marshal(rows)
+ out, err := json.Marshal(rows, jsonx.V1())
if err != nil {
return "", fmt.Errorf("marshal formatted records: %w", err)
}
@@ -571,7 +572,7 @@ type batchExecuteStatementResponse struct {
func (h *Handler) handleBatchExecuteStatement(ctx context.Context, body []byte) ([]byte, error) {
var req batchExecuteStatementRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -600,7 +601,7 @@ func (h *Handler) handleBatchExecuteStatement(ctx context.Context, body []byte)
return nil, err
}
- return json.Marshal(batchExecuteStatementResponse{UpdateResults: results})
+ return json.Marshal(batchExecuteStatementResponse{UpdateResults: results}, jsonx.V1())
}
type beginTransactionRequest struct {
@@ -616,7 +617,7 @@ type beginTransactionResponse struct {
func (h *Handler) handleBeginTransaction(ctx context.Context, body []byte) ([]byte, error) {
var req beginTransactionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -634,7 +635,7 @@ func (h *Handler) handleBeginTransaction(ctx context.Context, body []byte) ([]by
return nil, err
}
- return json.Marshal(beginTransactionResponse{TransactionID: txID})
+ return json.Marshal(beginTransactionResponse{TransactionID: txID}, jsonx.V1())
}
type commitTransactionRequest struct {
@@ -649,7 +650,7 @@ type commitTransactionResponse struct {
func (h *Handler) handleCommitTransaction(ctx context.Context, body []byte) ([]byte, error) {
var req commitTransactionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -666,7 +667,7 @@ func (h *Handler) handleCommitTransaction(ctx context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(commitTransactionResponse{TransactionStatus: status})
+ return json.Marshal(commitTransactionResponse{TransactionStatus: status}, jsonx.V1())
}
type rollbackTransactionRequest struct {
@@ -681,7 +682,7 @@ type rollbackTransactionResponse struct {
func (h *Handler) handleRollbackTransaction(ctx context.Context, body []byte) ([]byte, error) {
var req rollbackTransactionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -698,7 +699,7 @@ func (h *Handler) handleRollbackTransaction(ctx context.Context, body []byte) ([
return nil, err
}
- return json.Marshal(rollbackTransactionResponse{TransactionStatus: status})
+ return json.Marshal(rollbackTransactionResponse{TransactionStatus: status}, jsonx.V1())
}
type executeSQLRequest struct {
@@ -715,7 +716,7 @@ type executeSQLResponse struct {
func (h *Handler) handleExecuteSQL(ctx context.Context, body []byte) ([]byte, error) {
var req executeSQLRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -734,7 +735,7 @@ func (h *Handler) handleExecuteSQL(ctx context.Context, body []byte) ([]byte, er
return nil, err
}
- return json.Marshal(executeSQLResponse{SQLStatementResults: results})
+ return json.Marshal(executeSQLResponse{SQLStatementResults: results}, jsonx.V1())
}
func arrayToJSONValue(a *ArrayValue) any {
diff --git a/services/rdsdata/handler_test.go b/services/rdsdata/handler_test.go
index f1c565618a..9d59e1c8d9 100644
--- a/services/rdsdata/handler_test.go
+++ b/services/rdsdata/handler_test.go
@@ -2,7 +2,7 @@ package rdsdata_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/rdsdata"
)
@@ -30,7 +31,7 @@ func doRDSDataRequest(t *testing.T, h *rdsdata.Handler, path string, body any) *
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -348,7 +349,7 @@ func TestHandler_ConcurrentRequests_Race(t *testing.T) {
"resourceArn": "arn:aws:rds:us-east-1:000000000000:cluster:c",
"secretArn": "arn:aws:secretsmanager:us-east-1:000000000000:secret:s",
"sql": "SELECT " + string(rune('0'+i%10)),
- })
+ }, jsonx.V1())
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/Execute", bytes.NewReader(body))
diff --git a/services/rdsdata/real_docker_test.go b/services/rdsdata/real_docker_test.go
index e38ce4eb3e..8579fdabc9 100644
--- a/services/rdsdata/real_docker_test.go
+++ b/services/rdsdata/real_docker_test.go
@@ -1,7 +1,7 @@
package rdsdata_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"os/exec"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rds"
"github.com/blackbirdworks/gopherstack/services/rdsdata"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
@@ -95,7 +96,7 @@ func (d *dockerDataAPI) call(path string, body map[string]any) (int, map[string]
out := map[string]any{}
if rec.Body.Len() > 0 {
- require.NoError(d.t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(d.t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
}
return rec.Code, out
diff --git a/services/rdsdata/real_engine.go b/services/rdsdata/real_engine.go
index 1bb884cc44..6163d0156e 100644
--- a/services/rdsdata/real_engine.go
+++ b/services/rdsdata/real_engine.go
@@ -3,7 +3,7 @@ package rdsdata
import (
"context"
"database/sql"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/url"
@@ -15,6 +15,7 @@ import (
"github.com/google/uuid"
_ "github.com/jackc/pgx/v5/stdlib" // registers the "pgx" database/sql driver
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
)
@@ -127,7 +128,7 @@ func (e *realEngine) loginFor(ctx context.Context, resourceARN string) (realLogi
Password string `json:"password"`
}
- if json.Unmarshal([]byte(raw), &creds) != nil || creds.Username == "" || creds.Password == "" {
+ if json.Unmarshal([]byte(raw), &creds, jsonx.V1()) != nil || creds.Username == "" || creds.Password == "" {
return realLogin{}, false, errInvalidSecret("the secret must be a JSON object with username and password")
}
diff --git a/services/rdsdata/real_handler_test.go b/services/rdsdata/real_handler_test.go
index c4ac215514..3080618e3e 100644
--- a/services/rdsdata/real_handler_test.go
+++ b/services/rdsdata/real_handler_test.go
@@ -2,7 +2,7 @@ package rdsdata //nolint:testpackage // white-box tests of the unexported placeh
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandlerMapsModeledExceptions(t *testing.T) {
@@ -50,7 +52,7 @@ func TestHandlerMapsModeledExceptions(t *testing.T) {
body, err := json.Marshal(map[string]string{
"resourceArn": testClusterARN, "secretArn": testSecretARN, "sql": "SELECT 1",
- })
+ }, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/Execute", bytes.NewReader(body))
@@ -63,7 +65,7 @@ func TestHandlerMapsModeledExceptions(t *testing.T) {
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out["__type"])
})
}
diff --git a/services/rdsdata/sql_test.go b/services/rdsdata/sql_test.go
index d5af4b393f..41d1645149 100644
--- a/services/rdsdata/sql_test.go
+++ b/services/rdsdata/sql_test.go
@@ -2,7 +2,7 @@ package rdsdata_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rdsdata"
)
@@ -125,7 +126,7 @@ func TestHandler_ExecuteSQL_Response(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results, ok := resp["sqlStatementResults"]
require.True(t, ok)
@@ -148,6 +149,6 @@ func TestHandler_ExecuteSQL_Response_NotNull(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["sqlStatementResults"])
}
diff --git a/services/rdsdata/statements_test.go b/services/rdsdata/statements_test.go
index f9b662525b..9af0f197a1 100644
--- a/services/rdsdata/statements_test.go
+++ b/services/rdsdata/statements_test.go
@@ -2,7 +2,7 @@ package rdsdata_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rdsdata"
)
@@ -93,7 +94,7 @@ func TestHandler_ExecuteStatement_WithTransaction(t *testing.T) {
require.Equal(t, http.StatusOK, beginRec.Code)
var beginResp map[string]any
- require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp))
+ require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp, jsonx.V1()))
txID := beginResp["transactionId"].(string)
rec := doRDSDataRequest(t, h, "/Execute", map[string]any{
@@ -150,7 +151,7 @@ func TestHandler_ExecuteStatement_Response(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasRecords := resp["records"]
assert.True(t, hasRecords, "records key must be present")
@@ -202,7 +203,7 @@ func TestHandler_ExecuteStatement_IncludeResultMetadata(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasCols := resp["columnMetadata"]
assert.Equal(t, tt.wantColumnMetadata, hasCols,
@@ -246,7 +247,7 @@ func TestHandler_ExecuteStatement_ResponseShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for _, field := range tt.fields {
_, ok := resp[field]
@@ -274,7 +275,7 @@ func TestHandler_ExecuteStatement_FormatRecordsAsJSON(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasRecords := resp["records"]
assert.False(t, hasRecords, "records must be omitted when formatRecordsAs=JSON")
@@ -285,7 +286,7 @@ func TestHandler_ExecuteStatement_FormatRecordsAsJSON(t *testing.T) {
require.True(t, ok, "formattedRecords must be a JSON string")
var rows []map[string]any
- require.NoError(t, json.Unmarshal([]byte(formatted), &rows))
+ require.NoError(t, json.Unmarshal([]byte(formatted), &rows, jsonx.V1()))
require.Len(t, rows, 1)
require.Len(t, rows[0], 1, "row must have exactly one column")
@@ -311,7 +312,7 @@ func TestHandler_ExecuteStatement_FormatRecordsAsIgnoredForDML(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasFormatted := resp["formattedRecords"]
assert.False(t, hasFormatted, "formattedRecords must not appear for a DML statement")
@@ -336,7 +337,7 @@ func TestHandler_ExecuteStatement_FormatRecordsAsInvalid(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "BadRequestException", resp["__type"])
}
@@ -357,7 +358,7 @@ func TestHandler_ExecuteStatement_ColumnMetadataFullShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cols, ok := resp["columnMetadata"].([]any)
require.True(t, ok)
@@ -421,7 +422,7 @@ func TestHandler_ExecuteStatement_ArrayParameterRejected(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "BadRequestException", resp["__type"])
}
@@ -472,7 +473,7 @@ func TestHandler_ExecuteStatement_ResultSetOptions_LongReturnType(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
records := resp["records"].([]any)
require.Len(t, records, 1)
@@ -531,7 +532,7 @@ func TestHandler_ExecuteStatement_ResultSetOptions_DecimalReturnType(t *testing.
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
records := resp["records"].([]any)
require.Len(t, records, 1)
@@ -570,7 +571,7 @@ func TestHandler_ExecuteStatement_ResultSetOptions_InvalidEnum(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "BadRequestException", resp["__type"])
})
}
@@ -675,7 +676,7 @@ func TestHandler_BatchExecuteStatement_EmptyParamSets(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results, ok := resp["updateResults"]
require.True(t, ok, "updateResults key must exist")
@@ -703,7 +704,7 @@ func TestHandler_BatchExecuteStatement_ArrayParameterRejected(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "BadRequestException", resp["__type"])
}
@@ -746,7 +747,7 @@ func TestHandler_BatchExecuteStatement_MultipleParamSets(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results := resp["updateResults"].([]any)
assert.Len(t, results, 3)
}
@@ -795,7 +796,7 @@ func TestHandler_BatchExecuteStatement_OneResultPerParamSet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results, ok := resp["updateResults"].([]any)
require.True(t, ok, "updateResults must be an array")
diff --git a/services/rdsdata/transactions_test.go b/services/rdsdata/transactions_test.go
index 7c69ab5127..f2c85b1bdc 100644
--- a/services/rdsdata/transactions_test.go
+++ b/services/rdsdata/transactions_test.go
@@ -2,7 +2,7 @@ package rdsdata_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rdsdata"
)
@@ -81,7 +82,7 @@ func TestHandler_BeginTransaction_ReturnsTransactionID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
txID, ok := resp["transactionId"].(string)
require.True(t, ok)
assert.NotEmpty(t, txID)
@@ -103,7 +104,7 @@ func TestHandler_BeginTransaction_UniqueIDs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
txID, ok := resp["transactionId"].(string)
require.True(t, ok)
@@ -169,7 +170,7 @@ func TestHandler_CommitTransaction(t *testing.T) {
require.Equal(t, http.StatusOK, beginRec.Code)
var beginResp map[string]any
- require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp))
+ require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp, jsonx.V1()))
txID := beginResp["transactionId"].(string)
rec = doRDSDataRequest(t, h, "/CommitTransaction", map[string]any{
@@ -247,7 +248,7 @@ func TestHandler_RollbackTransaction(t *testing.T) {
require.Equal(t, http.StatusOK, beginRec.Code)
var beginResp map[string]any
- require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp))
+ require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp, jsonx.V1()))
txID := beginResp["transactionId"].(string)
rec = doRDSDataRequest(t, h, "/RollbackTransaction", map[string]any{
@@ -286,7 +287,7 @@ func TestHandler_TransactionLifecycle(t *testing.T) {
assert.Equal(t, 1, rdsdata.TransactionCount(b))
var beginResp map[string]any
- require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp))
+ require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp, jsonx.V1()))
txID := beginResp["transactionId"].(string)
// Execute
@@ -308,7 +309,7 @@ func TestHandler_TransactionLifecycle(t *testing.T) {
assert.Equal(t, 0, rdsdata.TransactionCount(b))
var commitResp map[string]any
- require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitResp))
+ require.NoError(t, json.Unmarshal(commitRec.Body.Bytes(), &commitResp, jsonx.V1()))
assert.Equal(t, "Transaction committed", commitResp["transactionStatus"])
}
@@ -325,7 +326,7 @@ func TestHandler_TransactionRollbackLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, beginRec.Code)
var beginResp map[string]any
- require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp))
+ require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp, jsonx.V1()))
txID := beginResp["transactionId"].(string)
rollbackRec := doRDSDataRequest(t, h, "/RollbackTransaction", map[string]any{
@@ -336,7 +337,7 @@ func TestHandler_TransactionRollbackLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rollbackRec.Code)
var rollbackResp map[string]any
- require.NoError(t, json.Unmarshal(rollbackRec.Body.Bytes(), &rollbackResp))
+ require.NoError(t, json.Unmarshal(rollbackRec.Body.Bytes(), &rollbackResp, jsonx.V1()))
assert.Equal(t, "Transaction rolled back", rollbackResp["transactionStatus"])
}
@@ -353,7 +354,7 @@ func TestHandler_CommitAfterRollback_Fails(t *testing.T) {
require.Equal(t, http.StatusOK, beginRec.Code)
var beginResp map[string]any
- require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp))
+ require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp, jsonx.V1()))
txID := beginResp["transactionId"].(string)
// Rollback first
@@ -401,7 +402,7 @@ func TestHandler_CommitThenReuse(t *testing.T) {
require.Equal(t, http.StatusOK, beginRec.Code)
var beginResp map[string]any
- require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp))
+ require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp, jsonx.V1()))
txID := beginResp["transactionId"].(string)
commitRec := doRDSDataRequest(t, h, "/CommitTransaction", map[string]any{
@@ -421,7 +422,7 @@ func TestHandler_CommitThenReuse(t *testing.T) {
"path %s: reusing committed txID must fail", tt.path)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "TransactionNotFoundException", errResp["__type"])
})
}
@@ -463,7 +464,7 @@ func TestHandler_ExecuteAfterTransactionClose(t *testing.T) {
require.Equal(t, http.StatusOK, beginRec.Code)
var beginResp map[string]any
- require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp))
+ require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp, jsonx.V1()))
txID := beginResp["transactionId"].(string)
// Execute within transaction — should succeed.
@@ -508,7 +509,7 @@ func TestHandler_BatchExecuteWithTransaction(t *testing.T) {
require.Equal(t, http.StatusOK, beginRec.Code)
var beginResp map[string]any
- require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp))
+ require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp, jsonx.V1()))
txID := beginResp["transactionId"].(string)
rec := doRDSDataRequest(t, h, "/BatchExecute", map[string]any{
@@ -558,7 +559,7 @@ func TestHandler_TransactionStatusConstants(t *testing.T) {
require.Equal(t, http.StatusOK, beginRec.Code)
var beginResp map[string]any
- require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp))
+ require.NoError(t, json.Unmarshal(beginRec.Body.Bytes(), &beginResp, jsonx.V1()))
txID := beginResp["transactionId"].(string)
rec := doRDSDataRequest(t, h, tt.path, map[string]any{
@@ -569,7 +570,7 @@ func TestHandler_TransactionStatusConstants(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantStatus, resp["transactionStatus"],
"transactionStatus must match exact AWS string")
})
@@ -608,7 +609,7 @@ func TestHandler_TransactionNotFoundErrorType(t *testing.T) {
assert.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "TransactionNotFoundException", resp["__type"],
"path %s: error type must be TransactionNotFoundException", tt.path)
})
@@ -629,7 +630,7 @@ func TestHandler_TransactionNotFoundErrorShape(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "TransactionNotFoundException", errResp["__type"])
assert.NotEmpty(t, errResp["message"])
}
diff --git a/services/rdsdata/typehints.go b/services/rdsdata/typehints.go
index b8b05bac86..4e082b335d 100644
--- a/services/rdsdata/typehints.go
+++ b/services/rdsdata/typehints.go
@@ -1,11 +1,13 @@
package rdsdata
import (
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
"regexp"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// SqlParameter.TypeHint enum values (rdsdata@v1.35.4 types/enums.go's
@@ -89,7 +91,7 @@ func validateTypeHintFormat(hint, value string) error {
return fmt.Errorf("%w: %s requires a decimal number", errBadTypeHintValue, typeHintDecimal)
}
case typeHintJSON:
- if !json.Valid([]byte(value)) {
+ if !jsontext.Value([]byte(value)).IsValid(jsonx.V1()) {
return fmt.Errorf("%w: %s requires valid JSON", errBadTypeHintValue, typeHintJSON)
}
case typeHintUUID:
diff --git a/services/redshift/handler_serverless.go b/services/redshift/handler_serverless.go
index e08b248748..6effc32229 100644
--- a/services/redshift/handler_serverless.go
+++ b/services/redshift/handler_serverless.go
@@ -2,7 +2,8 @@ package redshift
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -13,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -197,7 +199,7 @@ func (h *ServerlessHandler) ExtractResource(c *echo.Context) string {
WorkgroupName string `json:"workgroupName"`
}
- if json.Unmarshal(body, &probe) != nil {
+ if json.Unmarshal(body, &probe, jsonx.V1()) != nil {
return ""
}
@@ -348,7 +350,7 @@ func (h *ServerlessHandler) handleCreateNamespace(c *echo.Context, body []byte)
ManageAdminPassword bool `json:"manageAdminPassword"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -382,7 +384,7 @@ func (h *ServerlessHandler) handleGetNamespace(c *echo.Context, body []byte) err
NamespaceName string `json:"namespaceName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -401,7 +403,7 @@ func (h *ServerlessHandler) handleListNamespaces(c *echo.Context, body []byte) e
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
}
@@ -429,7 +431,7 @@ func (h *ServerlessHandler) handleUpdateNamespace(c *echo.Context, body []byte)
LogExports []string `json:"logExports"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -457,7 +459,7 @@ func (h *ServerlessHandler) handleDeleteNamespace(c *echo.Context, body []byte)
FinalSnapshotRetentionPeriod int `json:"finalSnapshotRetentionPeriod"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -514,7 +516,7 @@ func (r slWorkgroupReq) toParams() WorkgroupParams {
func (h *ServerlessHandler) handleCreateWorkgroup(c *echo.Context, body []byte) error {
var req slWorkgroupReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -535,7 +537,7 @@ func (h *ServerlessHandler) handleGetWorkgroup(c *echo.Context, body []byte) err
WorkgroupName string `json:"workgroupName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -555,7 +557,7 @@ func (h *ServerlessHandler) handleListWorkgroups(c *echo.Context, body []byte) e
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
}
@@ -573,7 +575,7 @@ func (h *ServerlessHandler) handleListWorkgroups(c *echo.Context, body []byte) e
func (h *ServerlessHandler) handleUpdateWorkgroup(c *echo.Context, body []byte) error {
var req slWorkgroupReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -590,7 +592,7 @@ func (h *ServerlessHandler) handleDeleteWorkgroup(c *echo.Context, body []byte)
WorkgroupName string `json:"workgroupName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -610,7 +612,7 @@ func (h *ServerlessHandler) handleGetCredentials(c *echo.Context, body []byte) e
DurationSeconds int `json:"durationSeconds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -659,7 +661,7 @@ func (h *ServerlessHandler) handleCreateSnapshot(c *echo.Context, body []byte) e
RetentionPeriod int `json:"retentionPeriod"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -680,7 +682,7 @@ func (h *ServerlessHandler) handleGetSnapshot(c *echo.Context, body []byte) erro
OwnerAccount string `json:"ownerAccount"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -709,7 +711,7 @@ func (h *ServerlessHandler) handleListSnapshots(c *echo.Context, body []byte) er
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
}
@@ -737,7 +739,7 @@ func (h *ServerlessHandler) handleDeleteSnapshot(c *echo.Context, body []byte) e
SnapshotName string `json:"snapshotName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -755,7 +757,7 @@ func (h *ServerlessHandler) handleUpdateSnapshot(c *echo.Context, body []byte) e
SnapshotName string `json:"snapshotName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -784,7 +786,7 @@ func (h *ServerlessHandler) handleCreateUsageLimit(c *echo.Context, body []byte)
Amount int64 `json:"amount"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -807,7 +809,7 @@ func (h *ServerlessHandler) handleGetUsageLimit(c *echo.Context, body []byte) er
UsageLimitID string `json:"usageLimitId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -828,7 +830,7 @@ func (h *ServerlessHandler) handleListUsageLimits(c *echo.Context, body []byte)
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
}
@@ -850,7 +852,7 @@ func (h *ServerlessHandler) handleUpdateUsageLimit(c *echo.Context, body []byte)
Amount int64 `json:"amount"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -867,7 +869,7 @@ func (h *ServerlessHandler) handleDeleteUsageLimit(c *echo.Context, body []byte)
UsageLimitID string `json:"usageLimitId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -892,17 +894,17 @@ func (h *ServerlessHandler) handleDeleteUsageLimit(c *echo.Context, body []byte)
// conversion at the response boundary, same pattern as
// applicationautoscaling's epochSecondsPtr.
type slScheduledActionWire struct {
- StartTime *float64 `json:"startTime,omitempty"`
- EndTime *float64 `json:"endTime,omitempty"`
- NamespaceName string `json:"namespaceName,omitempty"`
- RoleArn string `json:"roleArn,omitempty"`
- ScheduledActionDescription string `json:"scheduledActionDescription,omitempty"`
- ScheduledActionName string `json:"scheduledActionName"`
- ScheduledActionUUID string `json:"scheduledActionUuid,omitempty"`
- State string `json:"state"`
- NextInvocations []float64 `json:"nextInvocations,omitempty"`
- Schedule json.RawMessage `json:"schedule,omitempty"`
- TargetAction json.RawMessage `json:"targetAction,omitempty"`
+ StartTime *float64 `json:"startTime,omitempty"`
+ EndTime *float64 `json:"endTime,omitempty"`
+ NamespaceName string `json:"namespaceName,omitempty"`
+ RoleArn string `json:"roleArn,omitempty"`
+ ScheduledActionDescription string `json:"scheduledActionDescription,omitempty"`
+ ScheduledActionName string `json:"scheduledActionName"`
+ ScheduledActionUUID string `json:"scheduledActionUuid,omitempty"`
+ State string `json:"state"`
+ NextInvocations []float64 `json:"nextInvocations,omitempty"`
+ Schedule jsontext.Value `json:"schedule,omitempty"`
+ TargetAction jsontext.Value `json:"targetAction,omitempty"`
}
func slEpochPtr(t time.Time) *float64 {
@@ -933,18 +935,18 @@ func toScheduledActionWire(sa *ServerlessScheduledAction) *slScheduledActionWire
func (h *ServerlessHandler) handleCreateScheduledAction(c *echo.Context, body []byte) error {
var req struct {
- Enabled *bool `json:"enabled"`
- StartTime *float64 `json:"startTime"`
- EndTime *float64 `json:"endTime"`
- ScheduledActionName string `json:"scheduledActionName"`
- NamespaceName string `json:"namespaceName"`
- RoleArn string `json:"roleArn"`
- ScheduledActionDescription string `json:"scheduledActionDescription"`
- Schedule json.RawMessage `json:"schedule"`
- TargetAction json.RawMessage `json:"targetAction"`
- }
-
- if err := json.Unmarshal(body, &req); err != nil {
+ Enabled *bool `json:"enabled"`
+ StartTime *float64 `json:"startTime"`
+ EndTime *float64 `json:"endTime"`
+ ScheduledActionName string `json:"scheduledActionName"`
+ NamespaceName string `json:"namespaceName"`
+ RoleArn string `json:"roleArn"`
+ ScheduledActionDescription string `json:"scheduledActionDescription"`
+ Schedule jsontext.Value `json:"schedule"`
+ TargetAction jsontext.Value `json:"targetAction"`
+ }
+
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -983,7 +985,7 @@ func (h *ServerlessHandler) handleGetScheduledAction(c *echo.Context, body []byt
ScheduledActionName string `json:"scheduledActionName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -1014,7 +1016,7 @@ func (h *ServerlessHandler) handleListScheduledActions(c *echo.Context, body []b
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
}
@@ -1040,17 +1042,17 @@ func (h *ServerlessHandler) handleListScheduledActions(c *echo.Context, body []b
func (h *ServerlessHandler) handleUpdateScheduledAction(c *echo.Context, body []byte) error {
var req struct {
- ScheduledActionDescription *string `json:"scheduledActionDescription"`
- Enabled *bool `json:"enabled"`
- StartTime *float64 `json:"startTime"`
- EndTime *float64 `json:"endTime"`
- ScheduledActionName string `json:"scheduledActionName"`
- RoleArn string `json:"roleArn"`
- Schedule json.RawMessage `json:"schedule"`
- TargetAction json.RawMessage `json:"targetAction"`
+ ScheduledActionDescription *string `json:"scheduledActionDescription"`
+ Enabled *bool `json:"enabled"`
+ StartTime *float64 `json:"startTime"`
+ EndTime *float64 `json:"endTime"`
+ ScheduledActionName string `json:"scheduledActionName"`
+ RoleArn string `json:"roleArn"`
+ Schedule jsontext.Value `json:"schedule"`
+ TargetAction jsontext.Value `json:"targetAction"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -1075,7 +1077,7 @@ func (h *ServerlessHandler) handleDeleteScheduledAction(c *echo.Context, body []
ScheduledActionName string `json:"scheduledActionName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -1096,7 +1098,7 @@ func (h *ServerlessHandler) handleGetTrack(c *echo.Context, body []byte) error {
TrackName string `json:"trackName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -1119,7 +1121,7 @@ func (h *ServerlessHandler) handleListTracks(c *echo.Context, body []byte) error
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
}
@@ -1148,7 +1150,7 @@ func (h *ServerlessHandler) handleUpdateLakehouseConfiguration(c *echo.Context,
DryRun bool `json:"dryRun"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -1180,7 +1182,7 @@ func (h *ServerlessHandler) handleGetIdentityCenterAuthTokenSL(c *echo.Context,
WorkgroupNames []string `json:"workgroupNames"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -1209,7 +1211,7 @@ func (h *ServerlessHandler) handleCreateReservation(c *echo.Context, body []byte
Capacity int32 `json:"capacity"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -1226,7 +1228,7 @@ func (h *ServerlessHandler) handleGetReservation(c *echo.Context, body []byte) e
ReservationID string `json:"reservationId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -1245,7 +1247,7 @@ func (h *ServerlessHandler) handleListReservations(c *echo.Context, body []byte)
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
}
@@ -1265,7 +1267,7 @@ func (h *ServerlessHandler) handleGetReservationOffering(c *echo.Context, body [
OfferingID string `json:"offeringId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -1284,7 +1286,7 @@ func (h *ServerlessHandler) handleListReservationOfferings(c *echo.Context, body
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
}
diff --git a/services/redshift/handler_serverless_custom_domains.go b/services/redshift/handler_serverless_custom_domains.go
index 87d206127f..16bebe65b2 100644
--- a/services/redshift/handler_serverless_custom_domains.go
+++ b/services/redshift/handler_serverless_custom_domains.go
@@ -1,10 +1,12 @@
package redshift
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Create/Get/UpdateCustomDomainAssociationResponse all serialize the
@@ -22,7 +24,7 @@ func (h *ServerlessHandler) handleCreateCustomDomainAssociation(c *echo.Context,
WorkgroupName string `json:"workgroupName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -46,7 +48,7 @@ func (h *ServerlessHandler) handleGetCustomDomainAssociation(c *echo.Context, bo
WorkgroupName string `json:"workgroupName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -67,7 +69,7 @@ func (h *ServerlessHandler) handleListCustomDomainAssociations(c *echo.Context,
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
}
@@ -91,7 +93,7 @@ func (h *ServerlessHandler) handleUpdateCustomDomainAssociation(c *echo.Context,
WorkgroupName string `json:"workgroupName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -115,7 +117,7 @@ func (h *ServerlessHandler) handleDeleteCustomDomainAssociation(c *echo.Context,
WorkgroupName string `json:"workgroupName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
diff --git a/services/redshift/handler_serverless_custom_domains_test.go b/services/redshift/handler_serverless_custom_domains_test.go
index b51fe9013c..b27e0ee913 100644
--- a/services/redshift/handler_serverless_custom_domains_test.go
+++ b/services/redshift/handler_serverless_custom_domains_test.go
@@ -1,12 +1,15 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestServerless_CustomDomainAssociationCRUD covers Create/Get/List/Update/
@@ -29,7 +32,7 @@ func TestServerless_CustomDomainAssociationCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
assert.Equal(t, "db.example.com", createResp["customDomainName"])
assert.Equal(t, "cd-wg", createResp["workgroupName"])
assert.NotContains(t, createResp, "association", "response is flat, not wrapped in an envelope key")
@@ -39,7 +42,7 @@ func TestServerless_CustomDomainAssociationCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var wgResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&wgResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &wgResp, jsonx.V1()))
wgData, _ := wgResp["workgroup"].(map[string]any)
require.NotNil(t, wgData)
assert.Equal(t, "db.example.com", wgData["customDomainName"])
@@ -56,7 +59,7 @@ func TestServerless_CustomDomainAssociationCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &listResp, jsonx.V1()))
assocs, _ := listResp["associations"].([]any)
require.Len(t, assocs, 1)
@@ -65,7 +68,7 @@ func TestServerless_CustomDomainAssociationCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var credResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&credResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &credResp, jsonx.V1()))
dbUser, _ := credResp["dbUser"].(string)
assert.NotEmpty(t, dbUser)
@@ -77,7 +80,7 @@ func TestServerless_CustomDomainAssociationCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&updateResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &updateResp, jsonx.V1()))
assert.Equal(t, "arn:aws:acm:us-east-1:000000000000:certificate/def", updateResp["customDomainCertificateArn"])
rec = doServerlessOp(t, h, "DeleteCustomDomainAssociation", map[string]any{
@@ -87,13 +90,13 @@ func TestServerless_CustomDomainAssociationCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var deleteResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&deleteResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &deleteResp, jsonx.V1()))
assert.Empty(t, deleteResp, "DeleteCustomDomainAssociationResponse has zero members on the real wire")
// The mirrored workgroup fields are cleared too.
rec = doServerlessOp(t, h, "GetWorkgroup", map[string]any{"workgroupName": "cd-wg"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&wgResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &wgResp, jsonx.V1()))
wgData, _ = wgResp["workgroup"].(map[string]any)
assert.NotContains(t, wgData, "customDomainName")
@@ -143,7 +146,7 @@ func TestServerless_CustomDomainAssociation_Errors(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
})
}
@@ -172,7 +175,7 @@ func TestServerless_CustomDomainAssociation_Errors(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "ConflictException", errResp["__type"])
})
@@ -185,7 +188,7 @@ func TestServerless_CustomDomainAssociation_Errors(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "ValidationException", errResp["__type"])
})
@@ -198,7 +201,7 @@ func TestServerless_CustomDomainAssociation_Errors(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp["__type"])
})
}
diff --git a/services/redshift/handler_serverless_endpoint_access.go b/services/redshift/handler_serverless_endpoint_access.go
index 7e6e8c360a..52a84a61ee 100644
--- a/services/redshift/handler_serverless_endpoint_access.go
+++ b/services/redshift/handler_serverless_endpoint_access.go
@@ -1,11 +1,13 @@
package redshift
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// slVpcSecurityGroupMembership mirrors types.VpcSecurityGroupMembership --
@@ -61,7 +63,7 @@ func (h *ServerlessHandler) handleCreateEndpointAccessSL(c *echo.Context, body [
VpcSecurityGroupIDs []string `json:"vpcSecurityGroupIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -80,7 +82,7 @@ func (h *ServerlessHandler) handleGetEndpointAccessSL(c *echo.Context, body []by
EndpointName string `json:"endpointName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -108,7 +110,7 @@ func (h *ServerlessHandler) handleListEndpointAccessSL(c *echo.Context, body []b
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
}
@@ -135,7 +137,7 @@ func (h *ServerlessHandler) handleUpdateEndpointAccessSL(c *echo.Context, body [
VpcSecurityGroupIDs []string `json:"vpcSecurityGroupIds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -152,7 +154,7 @@ func (h *ServerlessHandler) handleDeleteEndpointAccessSL(c *echo.Context, body [
EndpointName string `json:"endpointName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -175,7 +177,7 @@ func (h *ServerlessHandler) handleListManagedWorkgroups(c *echo.Context, body []
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
}
diff --git a/services/redshift/handler_serverless_endpoint_access_test.go b/services/redshift/handler_serverless_endpoint_access_test.go
index aab95da2f0..d54f9e2917 100644
--- a/services/redshift/handler_serverless_endpoint_access_test.go
+++ b/services/redshift/handler_serverless_endpoint_access_test.go
@@ -1,12 +1,15 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestServerless_EndpointAccess_CRUD(t *testing.T) {
@@ -27,7 +30,7 @@ func TestServerless_EndpointAccess_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
ep, _ := createResp["endpoint"].(map[string]any)
require.NotNil(t, ep)
assert.Equal(t, "ea-endpoint", ep["endpointName"])
@@ -46,7 +49,7 @@ func TestServerless_EndpointAccess_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&getResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &getResp, jsonx.V1()))
got, _ := getResp["endpoint"].(map[string]any)
require.NotNil(t, got)
assert.Equal(t, "ea-endpoint", got["endpointName"])
@@ -55,7 +58,7 @@ func TestServerless_EndpointAccess_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &listResp, jsonx.V1()))
list, _ := listResp["endpoints"].([]any)
require.Len(t, list, 1)
@@ -66,7 +69,7 @@ func TestServerless_EndpointAccess_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&updateResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &updateResp, jsonx.V1()))
updated, _ := updateResp["endpoint"].(map[string]any)
require.NotNil(t, updated)
groups, _ = updated["vpcSecurityGroups"].([]any)
@@ -76,7 +79,7 @@ func TestServerless_EndpointAccess_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var deleteResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&deleteResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &deleteResp, jsonx.V1()))
deleted, _ := deleteResp["endpoint"].(map[string]any)
require.NotNil(t, deleted, "DeleteEndpointAccessResponse echoes the deleted object on the real wire")
assert.Equal(t, "ea-endpoint", deleted["endpointName"])
@@ -149,7 +152,7 @@ func TestServerless_EndpointAccess_Errors(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
})
}
@@ -173,7 +176,7 @@ func TestServerless_EndpointAccess_DuplicateName(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "ConflictException", errResp["__type"])
}
@@ -186,7 +189,7 @@ func TestServerless_ListManagedWorkgroups_AlwaysEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
list, ok := resp["managedWorkgroups"].([]any)
require.True(t, ok, "managedWorkgroups must be a correctly-shaped (empty) array, not absent")
assert.Empty(t, list)
diff --git a/services/redshift/handler_serverless_gaps_test.go b/services/redshift/handler_serverless_gaps_test.go
index fe8b4e9af7..b005ec26ba 100644
--- a/services/redshift/handler_serverless_gaps_test.go
+++ b/services/redshift/handler_serverless_gaps_test.go
@@ -1,7 +1,8 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/redshift"
)
@@ -36,7 +38,7 @@ func TestServerless_UpdateNamespace_DBNameNotMutated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
ns, _ := resp["namespace"].(map[string]any)
require.NotNil(t, ns)
assert.Equal(t, "original", ns["dbName"], "UpdateNamespace's dbName must be silently ignored, not applied")
@@ -64,7 +66,7 @@ func TestServerless_Namespace_AdminUserPassword_NeverEchoed(t *testing.T) {
assert.NotContains(t, body, "s3cr3t-Passw0rd")
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
ns, _ := createResp["namespace"].(map[string]any)
require.NotNil(t, ns)
_, hasPassword := ns["adminUserPassword"]
@@ -155,7 +157,7 @@ func TestServerless_ListSnapshots_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var nsResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&nsResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &nsResp, jsonx.V1()))
ns, _ := nsResp["namespace"].(map[string]any)
require.NotNil(t, ns)
nsArn, _ := ns["namespaceArn"].(string)
@@ -165,7 +167,7 @@ func TestServerless_ListSnapshots_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var allResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&allResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &allResp, jsonx.V1()))
allSnaps, _ := allResp["snapshots"].([]any)
require.Len(t, allSnaps, 3, "sanity: three snapshots exist before filtering")
@@ -173,7 +175,7 @@ func TestServerless_ListSnapshots_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var filteredResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&filteredResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &filteredResp, jsonx.V1()))
filtered, _ := filteredResp["snapshots"].([]any)
assert.Len(t, filtered, 2, "namespaceArn filter must narrow to only lsf-ns-a's snapshots")
@@ -186,7 +188,7 @@ func TestServerless_ListSnapshots_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var otherAcctResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&otherAcctResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &otherAcctResp, jsonx.V1()))
otherAcctSnaps, _ := otherAcctResp["snapshots"].([]any)
assert.Empty(t, otherAcctSnaps, "no snapshot in a single-account backend can be owned by another account")
@@ -194,7 +196,7 @@ func TestServerless_ListSnapshots_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var ownAcctResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&ownAcctResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &ownAcctResp, jsonx.V1()))
ownAcctSnaps, _ := ownAcctResp["snapshots"].([]any)
assert.Len(t, ownAcctSnaps, 3)
}
@@ -220,7 +222,7 @@ func TestServerless_GetSnapshot_OwnerAccount(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp["__type"])
}
@@ -239,7 +241,7 @@ func TestServerless_ListWorkgroups_OwnerAccount(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var ownResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&ownResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &ownResp, jsonx.V1()))
own, _ := ownResp["workgroups"].([]any)
require.Len(t, own, 2, "sanity: two workgroups exist and match this backend's own account")
@@ -247,7 +249,7 @@ func TestServerless_ListWorkgroups_OwnerAccount(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var otherResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&otherResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &otherResp, jsonx.V1()))
other, _ := otherResp["workgroups"].([]any)
assert.Empty(t, other, "ownerAccount filter must narrow to zero for an account this backend never owns")
}
@@ -275,7 +277,7 @@ func TestServerless_ListUsageLimits_UsageType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var allResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&allResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &allResp, jsonx.V1()))
all, _ := allResp["usageLimits"].([]any)
require.Len(t, all, 2, "sanity: two usage limits exist before filtering")
@@ -283,7 +285,7 @@ func TestServerless_ListUsageLimits_UsageType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var filteredResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&filteredResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &filteredResp, jsonx.V1()))
filtered, _ := filteredResp["usageLimits"].([]any)
require.Len(t, filtered, 1, "usageType filter must narrow to the single matching limit")
got, _ := filtered[0].(map[string]any)
diff --git a/services/redshift/handler_serverless_idc_token_test.go b/services/redshift/handler_serverless_idc_token_test.go
index 1eed87a897..bd8a95be45 100644
--- a/services/redshift/handler_serverless_idc_token_test.go
+++ b/services/redshift/handler_serverless_idc_token_test.go
@@ -1,12 +1,15 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestServerless_GetIdentityCenterAuthToken(t *testing.T) {
@@ -28,7 +31,7 @@ func TestServerless_GetIdentityCenterAuthToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
token1, _ := resp["token"].(string)
require.NotEmpty(t, token1)
@@ -40,7 +43,7 @@ func TestServerless_GetIdentityCenterAuthToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp2 map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp2, jsonx.V1()))
token2, _ := resp2["token"].(string)
require.NotEmpty(t, token2)
@@ -86,7 +89,7 @@ func TestServerless_GetIdentityCenterAuthToken_Errors(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
})
}
@@ -113,6 +116,6 @@ func TestServerless_GetIdentityCenterAuthToken_OneUnknownAmongMany(t *testing.T)
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp["__type"])
}
diff --git a/services/redshift/handler_serverless_lakehouse_test.go b/services/redshift/handler_serverless_lakehouse_test.go
index 8eb1b09501..734613e0d3 100644
--- a/services/redshift/handler_serverless_lakehouse_test.go
+++ b/services/redshift/handler_serverless_lakehouse_test.go
@@ -1,12 +1,15 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestServerless_UpdateLakehouseConfiguration_Errors(t *testing.T) {
@@ -42,7 +45,7 @@ func TestServerless_UpdateLakehouseConfiguration_Errors(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
})
}
@@ -68,14 +71,14 @@ func TestServerless_UpdateLakehouseConfiguration_DryRun(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "DryRunException", errResp["__type"])
rec = doServerlessOp(t, h, "GetNamespace", map[string]any{"namespaceName": "lh-dryrun-ns"})
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&getResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &getResp, jsonx.V1()))
ns, _ := getResp["namespace"].(map[string]any)
require.NotNil(t, ns)
assert.Empty(t, ns["catalogArn"], "dry run must not mutate the namespace's catalogArn")
@@ -104,7 +107,7 @@ func TestServerless_UpdateLakehouseConfiguration_RegisterAndAssociate(t *testing
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, "lh-ns", resp["namespaceName"])
assert.Contains(t, resp["catalogArn"], "mycatalog")
assert.Equal(t, "Registered", resp["lakehouseRegistrationStatus"])
@@ -114,7 +117,7 @@ func TestServerless_UpdateLakehouseConfiguration_RegisterAndAssociate(t *testing
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&getResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &getResp, jsonx.V1()))
ns, _ := getResp["namespace"].(map[string]any)
require.NotNil(t, ns)
assert.Contains(t, ns["catalogArn"], "mycatalog")
@@ -131,7 +134,7 @@ func TestServerless_UpdateLakehouseConfiguration_RegisterAndAssociate(t *testing
require.Equal(t, http.StatusOK, rec.Code)
var resp2 map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp2, jsonx.V1()))
assert.Equal(t, "Deregistered", resp2["lakehouseRegistrationStatus"])
assert.Equal(t, "arn:aws:sso::000000000000:application/idc-app-1", resp2["lakehouseIdcApplicationArn"])
@@ -143,6 +146,6 @@ func TestServerless_UpdateLakehouseConfiguration_RegisterAndAssociate(t *testing
require.Equal(t, http.StatusOK, rec.Code)
var resp3 map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp3))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp3, jsonx.V1()))
assert.NotContains(t, resp3, "lakehouseIdcApplicationArn")
}
diff --git a/services/redshift/handler_serverless_recovery.go b/services/redshift/handler_serverless_recovery.go
index f60c2f48c4..712ad998c8 100644
--- a/services/redshift/handler_serverless_recovery.go
+++ b/services/redshift/handler_serverless_recovery.go
@@ -1,10 +1,12 @@
package redshift
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *ServerlessHandler) handleGetRecoveryPoint(c *echo.Context, body []byte) error {
@@ -12,7 +14,7 @@ func (h *ServerlessHandler) handleGetRecoveryPoint(c *echo.Context, body []byte)
RecoveryPointID string `json:"recoveryPointId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -39,7 +41,7 @@ func (h *ServerlessHandler) handleListRecoveryPoints(c *echo.Context, body []byt
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
}
@@ -69,7 +71,7 @@ func (h *ServerlessHandler) handleRestoreFromRecoveryPoint(c *echo.Context, body
WorkgroupName string `json:"workgroupName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
diff --git a/services/redshift/handler_serverless_recovery_test.go b/services/redshift/handler_serverless_recovery_test.go
index 27357e2920..a4c2b5cc2a 100644
--- a/services/redshift/handler_serverless_recovery_test.go
+++ b/services/redshift/handler_serverless_recovery_test.go
@@ -1,13 +1,15 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/redshift"
)
@@ -31,7 +33,7 @@ func seedNamespaceAndWorkgroup(
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &listResp, jsonx.V1()))
points, _ := listResp["recoveryPoints"].([]any)
require.Len(t, points, 1, "CreateWorkgroup must generate exactly one recovery point")
@@ -57,7 +59,7 @@ func TestServerless_RecoveryPoint_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&getResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &getResp, jsonx.V1()))
got, _ := getResp["recoveryPoint"].(map[string]any)
require.NotNil(t, got)
assert.Equal(t, recoveryPointID, got["recoveryPointId"])
@@ -70,7 +72,7 @@ func TestServerless_RecoveryPoint_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var restoreResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&restoreResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &restoreResp, jsonx.V1()))
assert.Equal(t, recoveryPointID, restoreResp["recoveryPointId"])
ns, _ := restoreResp["namespace"].(map[string]any)
require.NotNil(t, ns)
@@ -129,7 +131,7 @@ func TestServerless_RecoveryPoint_Errors(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
})
}
@@ -154,7 +156,7 @@ func TestServerless_RestoreFromRecoveryPoint_WorkgroupNamespaceMismatch(t *testi
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &listResp, jsonx.V1()))
points, _ := listResp["recoveryPoints"].([]any)
require.Len(t, points, 1)
rp, _ := points[0].(map[string]any)
@@ -168,6 +170,6 @@ func TestServerless_RestoreFromRecoveryPoint_WorkgroupNamespaceMismatch(t *testi
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "ValidationException", errResp["__type"])
}
diff --git a/services/redshift/handler_serverless_resource_policy.go b/services/redshift/handler_serverless_resource_policy.go
index cec9c04f2b..18a039986a 100644
--- a/services/redshift/handler_serverless_resource_policy.go
+++ b/services/redshift/handler_serverless_resource_policy.go
@@ -1,10 +1,12 @@
package redshift
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *ServerlessHandler) handleGetResourcePolicySL(c *echo.Context, body []byte) error {
@@ -12,7 +14,7 @@ func (h *ServerlessHandler) handleGetResourcePolicySL(c *echo.Context, body []by
ResourceArn string `json:"resourceArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -34,7 +36,7 @@ func (h *ServerlessHandler) handlePutResourcePolicySL(c *echo.Context, body []by
Policy string `json:"policy"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -55,7 +57,7 @@ func (h *ServerlessHandler) handleDeleteResourcePolicySL(c *echo.Context, body [
ResourceArn string `json:"resourceArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
diff --git a/services/redshift/handler_serverless_resource_policy_test.go b/services/redshift/handler_serverless_resource_policy_test.go
index 8769de09bf..60f753262f 100644
--- a/services/redshift/handler_serverless_resource_policy_test.go
+++ b/services/redshift/handler_serverless_resource_policy_test.go
@@ -1,12 +1,15 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestServerless_ResourcePolicy_CRUD(t *testing.T) {
@@ -23,7 +26,7 @@ func TestServerless_ResourcePolicy_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var putResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&putResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &putResp, jsonx.V1()))
rp, _ := putResp["resourcePolicy"].(map[string]any)
require.NotNil(t, rp)
assert.Equal(t, resourceArn, rp["resourceArn"])
@@ -33,7 +36,7 @@ func TestServerless_ResourcePolicy_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&getResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &getResp, jsonx.V1()))
rp, _ = getResp["resourcePolicy"].(map[string]any)
require.NotNil(t, rp)
assert.Equal(t, policy, rp["policy"])
@@ -42,7 +45,7 @@ func TestServerless_ResourcePolicy_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var deleteResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&deleteResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &deleteResp, jsonx.V1()))
assert.Empty(t, deleteResp, "DeleteResourcePolicyResponse has zero members on the real wire")
rec = doServerlessOp(t, h, "GetResourcePolicy", map[string]any{"resourceArn": resourceArn})
@@ -105,7 +108,7 @@ func TestServerless_ResourcePolicy_Errors(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
})
}
diff --git a/services/redshift/handler_serverless_restore.go b/services/redshift/handler_serverless_restore.go
index 6736681278..efd10066a4 100644
--- a/services/redshift/handler_serverless_restore.go
+++ b/services/redshift/handler_serverless_restore.go
@@ -1,10 +1,12 @@
package redshift
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *ServerlessHandler) handleRestoreFromSnapshot(c *echo.Context, body []byte) error {
@@ -19,7 +21,7 @@ func (h *ServerlessHandler) handleRestoreFromSnapshot(c *echo.Context, body []by
ManageAdminPassword bool `json:"manageAdminPassword"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -64,7 +66,7 @@ func (h *ServerlessHandler) handleConvertRecoveryPointToSnapshot(c *echo.Context
RetentionPeriod int `json:"retentionPeriod"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
diff --git a/services/redshift/handler_serverless_restore_test.go b/services/redshift/handler_serverless_restore_test.go
index a859a476ca..7b2b95ea90 100644
--- a/services/redshift/handler_serverless_restore_test.go
+++ b/services/redshift/handler_serverless_restore_test.go
@@ -1,12 +1,15 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestServerless_RestoreFromSnapshot(t *testing.T) {
@@ -26,7 +29,7 @@ func TestServerless_RestoreFromSnapshot(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, "rfs-snap", resp["snapshotName"])
ns, _ := resp["namespace"].(map[string]any)
require.NotNil(t, ns)
@@ -76,7 +79,7 @@ func TestServerless_RestoreFromSnapshot_Errors(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
})
}
@@ -104,7 +107,7 @@ func TestServerless_RestoreFromSnapshot_WorkgroupNamespaceMismatch(t *testing.T)
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "ValidationException", errResp["__type"])
}
@@ -123,7 +126,7 @@ func TestServerless_ConvertRecoveryPointToSnapshot(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
snap, _ := resp["snapshot"].(map[string]any)
require.NotNil(t, snap)
assert.Equal(t, "crp-snap", snap["snapshotName"])
@@ -167,7 +170,7 @@ func TestServerless_ConvertRecoveryPointToSnapshot_Errors(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
})
}
@@ -191,6 +194,6 @@ func TestServerless_ConvertRecoveryPointToSnapshot_DuplicateName(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "ConflictException", errResp["__type"])
}
diff --git a/services/redshift/handler_serverless_snapshot_copy_config.go b/services/redshift/handler_serverless_snapshot_copy_config.go
index 60a0c02140..0ed92be44d 100644
--- a/services/redshift/handler_serverless_snapshot_copy_config.go
+++ b/services/redshift/handler_serverless_snapshot_copy_config.go
@@ -1,10 +1,12 @@
package redshift
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *ServerlessHandler) handleCreateSnapshotCopyConfigurationSL(c *echo.Context, body []byte) error {
@@ -15,7 +17,7 @@ func (h *ServerlessHandler) handleCreateSnapshotCopyConfigurationSL(c *echo.Cont
SnapshotRetentionPeriod int `json:"snapshotRetentionPeriod"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -39,7 +41,7 @@ func (h *ServerlessHandler) handleUpdateSnapshotCopyConfigurationSL(c *echo.Cont
SnapshotRetentionPeriod int `json:"snapshotRetentionPeriod"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -62,7 +64,7 @@ func (h *ServerlessHandler) handleDeleteSnapshotCopyConfigurationSL(c *echo.Cont
SnapshotCopyConfigurationID string `json:"snapshotCopyConfigurationId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -89,7 +91,7 @@ func (h *ServerlessHandler) handleListSnapshotCopyConfigurationsSL(c *echo.Conte
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
}
diff --git a/services/redshift/handler_serverless_snapshot_copy_config_test.go b/services/redshift/handler_serverless_snapshot_copy_config_test.go
index 9642aad5c9..f318a6aafb 100644
--- a/services/redshift/handler_serverless_snapshot_copy_config_test.go
+++ b/services/redshift/handler_serverless_snapshot_copy_config_test.go
@@ -1,12 +1,15 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestServerless_SnapshotCopyConfiguration_CRUD(t *testing.T) {
@@ -23,7 +26,7 @@ func TestServerless_SnapshotCopyConfiguration_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
cfg, _ := createResp["snapshotCopyConfiguration"].(map[string]any)
require.NotNil(t, cfg)
assert.Equal(t, "scc-ns", cfg["namespaceName"])
@@ -37,7 +40,7 @@ func TestServerless_SnapshotCopyConfiguration_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &listResp, jsonx.V1()))
list, _ := listResp["snapshotCopyConfigurations"].([]any)
require.Len(t, list, 1)
@@ -48,7 +51,7 @@ func TestServerless_SnapshotCopyConfiguration_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&updateResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &updateResp, jsonx.V1()))
cfg, _ = updateResp["snapshotCopyConfiguration"].(map[string]any)
require.NotNil(t, cfg)
assert.InEpsilon(t, float64(30), cfg["snapshotRetentionPeriod"], 0)
@@ -59,14 +62,14 @@ func TestServerless_SnapshotCopyConfiguration_CRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var deleteResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&deleteResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &deleteResp, jsonx.V1()))
cfg, _ = deleteResp["snapshotCopyConfiguration"].(map[string]any)
require.NotNil(t, cfg, "DeleteSnapshotCopyConfigurationResponse echoes the deleted object on the real wire")
assert.Equal(t, id, cfg["snapshotCopyConfigurationId"])
rec = doServerlessOp(t, h, "ListSnapshotCopyConfigurations", map[string]any{"namespaceName": "scc-ns"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &listResp, jsonx.V1()))
list, _ = listResp["snapshotCopyConfigurations"].([]any)
assert.Empty(t, list)
}
@@ -114,7 +117,7 @@ func TestServerless_SnapshotCopyConfiguration_Errors(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
})
}
diff --git a/services/redshift/handler_serverless_table_restore.go b/services/redshift/handler_serverless_table_restore.go
index a063770ca7..0b42f554f8 100644
--- a/services/redshift/handler_serverless_table_restore.go
+++ b/services/redshift/handler_serverless_table_restore.go
@@ -1,10 +1,12 @@
package redshift
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// slTableRestoreStatusWire is the wire shape for ServerlessTableRestoreStatus
@@ -73,7 +75,7 @@ func (r slTableRestoreReq) toParams() RestoreTableFromSnapshotParams {
func (h *ServerlessHandler) handleRestoreTableFromSnapshot(c *echo.Context, body []byte) error {
var req slTableRestoreReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -96,7 +98,7 @@ func (h *ServerlessHandler) handleRestoreTableFromSnapshot(c *echo.Context, body
func (h *ServerlessHandler) handleRestoreTableFromRecoveryPoint(c *echo.Context, body []byte) error {
var req slTableRestoreReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -121,7 +123,7 @@ func (h *ServerlessHandler) handleGetTableRestoreStatus(c *echo.Context, body []
TableRestoreRequestID string `json:"tableRestoreRequestId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -146,7 +148,7 @@ func (h *ServerlessHandler) handleListTableRestoreStatus(c *echo.Context, body [
}
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
}
diff --git a/services/redshift/handler_serverless_table_restore_test.go b/services/redshift/handler_serverless_table_restore_test.go
index e9e5fff896..6fcadcb5b8 100644
--- a/services/redshift/handler_serverless_table_restore_test.go
+++ b/services/redshift/handler_serverless_table_restore_test.go
@@ -1,12 +1,15 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestServerless_TableRestore_FromSnapshotAndRecoveryPoint(t *testing.T) {
@@ -32,7 +35,7 @@ func TestServerless_TableRestore_FromSnapshotAndRecoveryPoint(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var fromSnapResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fromSnapResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fromSnapResp, jsonx.V1()))
status1, _ := fromSnapResp["tableRestoreStatus"].(map[string]any)
require.NotNil(t, status1)
assert.Equal(t, "SUCCEEDED", status1["status"])
@@ -53,7 +56,7 @@ func TestServerless_TableRestore_FromSnapshotAndRecoveryPoint(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var fromRPResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&fromRPResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &fromRPResp, jsonx.V1()))
status2, _ := fromRPResp["tableRestoreStatus"].(map[string]any)
require.NotNil(t, status2)
assert.Equal(t, recoveryPointID, status2["recoveryPointId"])
@@ -65,7 +68,7 @@ func TestServerless_TableRestore_FromSnapshotAndRecoveryPoint(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&getResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &getResp, jsonx.V1()))
got, _ := getResp["tableRestoreStatus"].(map[string]any)
require.NotNil(t, got)
assert.Equal(t, requestID1, got["tableRestoreRequestId"])
@@ -74,7 +77,7 @@ func TestServerless_TableRestore_FromSnapshotAndRecoveryPoint(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &listResp, jsonx.V1()))
list, _ := listResp["tableRestoreStatuses"].([]any)
assert.Len(t, list, 2)
}
@@ -149,7 +152,7 @@ func TestServerless_TableRestore_Errors(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
})
}
diff --git a/services/redshift/handler_serverless_tagging.go b/services/redshift/handler_serverless_tagging.go
index c8486e2849..f0a9ee7a5f 100644
--- a/services/redshift/handler_serverless_tagging.go
+++ b/services/redshift/handler_serverless_tagging.go
@@ -1,11 +1,13 @@
package redshift
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"sort"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// slTagWire is the wire shape of a single Redshift Serverless tag -- a
@@ -56,7 +58,7 @@ func (h *ServerlessHandler) handleTagResource(c *echo.Context, body []byte) erro
Tags []slTagWire `json:"tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -77,7 +79,7 @@ func (h *ServerlessHandler) handleUntagResource(c *echo.Context, body []byte) er
TagKeys []string `json:"tagKeys"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
@@ -97,7 +99,7 @@ func (h *ServerlessHandler) handleListTagsForResource(c *echo.Context, body []by
ResourceArn string `json:"resourceArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return slBadRequest(c, "invalid request body")
}
diff --git a/services/redshift/handler_serverless_tagging_test.go b/services/redshift/handler_serverless_tagging_test.go
index 33564331a0..ea6254f83e 100644
--- a/services/redshift/handler_serverless_tagging_test.go
+++ b/services/redshift/handler_serverless_tagging_test.go
@@ -1,12 +1,15 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestServerless_TagResource_RoundTrip proves creation-time Tags on
@@ -28,7 +31,7 @@ func TestServerless_TagResource_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
nsData, _ := createResp["namespace"].(map[string]any)
require.NotNil(t, nsData)
nsArn, _ := nsData["namespaceArn"].(string)
@@ -42,7 +45,7 @@ func TestServerless_TagResource_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &listResp, jsonx.V1()))
tagList, _ := listResp["tags"].([]any)
require.Len(t, tagList, 1)
@@ -61,7 +64,7 @@ func TestServerless_TagResource_RoundTrip(t *testing.T) {
rec = doServerlessOp(t, h, "ListTagsForResource", map[string]any{"resourceArn": nsArn})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &listResp, jsonx.V1()))
tagList, _ = listResp["tags"].([]any)
assert.Len(t, tagList, 2)
@@ -73,7 +76,7 @@ func TestServerless_TagResource_RoundTrip(t *testing.T) {
rec = doServerlessOp(t, h, "ListTagsForResource", map[string]any{"resourceArn": nsArn})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &listResp, jsonx.V1()))
tagList, _ = listResp["tags"].([]any)
require.Len(t, tagList, 1)
@@ -99,7 +102,7 @@ func TestServerless_TagResource_WorkgroupAndSnapshot(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var wgResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&wgResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &wgResp, jsonx.V1()))
wgData, _ := wgResp["workgroup"].(map[string]any)
wgArn, _ := wgData["workgroupArn"].(string)
require.NotEmpty(t, wgArn)
@@ -108,7 +111,7 @@ func TestServerless_TagResource_WorkgroupAndSnapshot(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &listResp, jsonx.V1()))
tagList, _ := listResp["tags"].([]any)
require.Len(t, tagList, 1)
@@ -120,14 +123,14 @@ func TestServerless_TagResource_WorkgroupAndSnapshot(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var snapResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&snapResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &snapResp, jsonx.V1()))
snapData, _ := snapResp["snapshot"].(map[string]any)
snapArn, _ := snapData["snapshotArn"].(string)
require.NotEmpty(t, snapArn)
rec = doServerlessOp(t, h, "ListTagsForResource", map[string]any{"resourceArn": snapArn})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &listResp, jsonx.V1()))
tagList, _ = listResp["tags"].([]any)
assert.Len(t, tagList, 1)
}
@@ -174,7 +177,7 @@ func TestServerless_TagResource_Errors(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp["__type"])
})
}
@@ -190,7 +193,7 @@ func TestServerless_TagResource_Errors(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "ValidationException", errResp["__type"])
})
@@ -205,7 +208,7 @@ func TestServerless_TagResource_Errors(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "ValidationException", errResp["__type"])
})
}
diff --git a/services/redshift/handler_serverless_test.go b/services/redshift/handler_serverless_test.go
index cb961f6311..2d26eaf7af 100644
--- a/services/redshift/handler_serverless_test.go
+++ b/services/redshift/handler_serverless_test.go
@@ -2,7 +2,8 @@ package redshift_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/redshift"
)
@@ -34,7 +36,7 @@ func doServerlessOp(
var bodyReader *bytes.Reader
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
bodyReader = bytes.NewReader(b)
} else {
@@ -108,7 +110,7 @@ func TestServerless_NamespaceCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
nsData, _ := createResp["namespace"].(map[string]any)
require.NotNil(t, nsData)
assert.Equal(t, "arn:aws:iam::000000000000:role/default", nsData["defaultIamRoleArn"])
@@ -132,7 +134,7 @@ func TestServerless_NamespaceCRUD(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp["__type"])
}
@@ -155,7 +157,7 @@ func TestServerless_DeleteNamespace_WithFinalSnapshot(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
snap, _ := resp["snapshot"].(map[string]any)
require.NotNil(t, snap)
assert.InEpsilon(t, float64(14), snap["snapshotRetentionPeriod"], 0)
@@ -182,7 +184,7 @@ func TestServerless_WorkgroupCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
wgData, _ := createResp["workgroup"].(map[string]any)
require.NotNil(t, wgData)
assert.InEpsilon(t, float64(64), wgData["maxCapacity"], 0)
@@ -200,7 +202,7 @@ func TestServerless_WorkgroupCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var credResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&credResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &credResp, jsonx.V1()))
// Expiration is wire-encoded as a JSON number (epoch seconds), not an
// RFC3339 string -- see the wire-shape audit note in handler_serverless.go.
_, isNumber := credResp["expiration"].(float64)
@@ -247,7 +249,7 @@ func TestServerless_SnapshotCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&updateResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &updateResp, jsonx.V1()))
updated, _ := updateResp["snapshot"].(map[string]any)
require.NotNil(t, updated)
assert.InEpsilon(t, float64(30), updated["snapshotRetentionPeriod"], 0)
@@ -256,7 +258,7 @@ func TestServerless_SnapshotCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&getResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &getResp, jsonx.V1()))
got, _ := getResp["snapshot"].(map[string]any)
require.NotNil(t, got)
assert.InEpsilon(t, float64(30), got["snapshotRetentionPeriod"], 0,
@@ -285,7 +287,7 @@ func TestServerless_UsageLimitsCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
ulData, _ := createResp["usageLimit"].(map[string]any)
ulID := ""
@@ -333,7 +335,7 @@ func TestServerless_ScheduledActionCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&createResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &createResp, jsonx.V1()))
saData, _ := createResp["scheduledAction"].(map[string]any)
require.NotNil(t, saData)
assert.Equal(t, "DISABLED", saData["state"], "state (not status) is the real wire key")
@@ -362,7 +364,7 @@ func TestServerless_ScheduledActionCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&updateResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &updateResp, jsonx.V1()))
updated, _ := updateResp["scheduledAction"].(map[string]any)
require.NotNil(t, updated)
assert.Equal(t, "ACTIVE", updated["state"])
diff --git a/services/redshift/handler_serverless_tracks_test.go b/services/redshift/handler_serverless_tracks_test.go
index bde435ea76..70a0603c1a 100644
--- a/services/redshift/handler_serverless_tracks_test.go
+++ b/services/redshift/handler_serverless_tracks_test.go
@@ -1,12 +1,15 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestServerless_GetTrack(t *testing.T) {
@@ -55,7 +58,7 @@ func TestServerless_GetTrack(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
if tt.wantFound {
track, _ := resp["track"].(map[string]any)
@@ -80,7 +83,7 @@ func TestServerless_ListTracks(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
tracks, _ := resp["tracks"].([]any)
require.Len(t, tracks, 2, "real Redshift Serverless exposes exactly two maintenance tracks")
@@ -105,7 +108,7 @@ func TestServerless_ListTracks_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page1 map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&page1))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &page1, jsonx.V1()))
tracks1, _ := page1["tracks"].([]any)
require.Len(t, tracks1, 1)
@@ -117,7 +120,7 @@ func TestServerless_ListTracks_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page2 map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&page2))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &page2, jsonx.V1()))
tracks2, _ := page2["tracks"].([]any)
require.Len(t, tracks2, 1)
diff --git a/services/redshift/handler_serverless_update_snapshot_test.go b/services/redshift/handler_serverless_update_snapshot_test.go
index 1d9f741653..12ec5f3e04 100644
--- a/services/redshift/handler_serverless_update_snapshot_test.go
+++ b/services/redshift/handler_serverless_update_snapshot_test.go
@@ -1,12 +1,15 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestServerless_UpdateSnapshot_Errors(t *testing.T) {
@@ -42,7 +45,7 @@ func TestServerless_UpdateSnapshot_Errors(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
})
}
@@ -70,7 +73,7 @@ func TestServerless_UpdateSnapshot_OmittedRetentionPeriodUnchanged(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
snap, _ := resp["snapshot"].(map[string]any)
require.NotNil(t, snap)
assert.InEpsilon(t, float64(14), snap["snapshotRetentionPeriod"], 0)
diff --git a/services/redshift/multi_region_test.go b/services/redshift/multi_region_test.go
index ff5d188dd0..999acf08be 100644
--- a/services/redshift/multi_region_test.go
+++ b/services/redshift/multi_region_test.go
@@ -1,7 +1,8 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/redshift"
)
@@ -129,9 +131,9 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(t.Context())
- var doc map[string]json.RawMessage
+ var doc map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(snap, &doc))
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
_, hasRegions := doc["regions"]
assert.Equal(t, tc.wantRegions, hasRegions)
diff --git a/services/redshift/persistence.go b/services/redshift/persistence.go
index 3585be85c0..3703325374 100644
--- a/services/redshift/persistence.go
+++ b/services/redshift/persistence.go
@@ -2,9 +2,11 @@ package redshift
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -34,7 +36,7 @@ const redshiftSnapshotVersion = 1
// each is keyed externally by clusterID rather than by a field on the value
// itself), plus backend-level scalar configuration.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ActiveResizes map[string]*ResizeProgress `json:"activeResizes"`
SnapshotCopyConfigs map[string]*SnapshotCopyConfig `json:"snapshotCopyConfigs"`
LoggingStatuses map[string]*LoggingStatus `json:"loggingStatuses"`
@@ -84,7 +86,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.region,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "redshift: failed to marshal snapshot", "error", err)
diff --git a/services/redshift/persistence_test.go b/services/redshift/persistence_test.go
index e2c497e10a..a43db5f883 100644
--- a/services/redshift/persistence_test.go
+++ b/services/redshift/persistence_test.go
@@ -1,7 +1,8 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/redshift"
)
@@ -334,8 +336,8 @@ func TestInMemoryBackend_FullStateRoundTrip(t *testing.T) {
ScheduledActionName: "rt-slscheduledaction",
NamespaceName: "rt-namespace",
RoleArn: "arn:aws:iam::000000000000:role/scheduler",
- Schedule: json.RawMessage(`{"at":1893456000}`),
- TargetAction: json.RawMessage(
+ Schedule: jsontext.Value(`{"at":1893456000}`),
+ TargetAction: jsontext.Value(
`{"createSnapshot":{"namespaceName":"rt-namespace","snapshotName":"rt-slsnapshot2"}}`,
),
StartTime: time.Now(),
@@ -661,8 +663,8 @@ func TestPersistence_ScheduledActionWindowSurvivesRestore(t *testing.T) {
ScheduledActionName: "sa-rt-action",
NamespaceName: "sa-rt-ns",
RoleArn: "arn:aws:iam::000000000000:role/scheduler",
- Schedule: json.RawMessage(`{"cron":"0 10 ? * MON *"}`),
- TargetAction: json.RawMessage(
+ Schedule: jsontext.Value(`{"cron":"0 10 ? * MON *"}`),
+ TargetAction: jsontext.Value(
`{"createSnapshot":{"namespaceName":"sa-rt-ns","snapshotName":"sa-rt-snap"}}`,
),
StartTime: tt.startTime,
@@ -686,7 +688,7 @@ func TestPersistence_ScheduledActionWindowSurvivesRestore(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
saData, _ := resp["scheduledAction"].(map[string]any)
require.NotNil(t, saData)
diff --git a/services/redshift/schedule.go b/services/redshift/schedule.go
index f1867e9c02..ce7b195268 100644
--- a/services/redshift/schedule.go
+++ b/services/redshift/schedule.go
@@ -1,12 +1,13 @@
package redshift
import (
- "encoding/json"
+ "encoding/json/v2"
"strings"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awscron"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// maxNextInvocations bounds how many upcoming ScheduledActionTime entries this
@@ -133,7 +134,7 @@ func slNextInvocations(sa *ServerlessScheduledAction, now time.Time) []float64 {
Cron string `json:"cron"`
}
- if json.Unmarshal(sa.Schedule, &sched) != nil {
+ if json.Unmarshal(sa.Schedule, &sched, jsonx.V1()) != nil {
return nil
}
diff --git a/services/redshift/serverless.go b/services/redshift/serverless.go
index 63ff14b396..ffa73f7439 100644
--- a/services/redshift/serverless.go
+++ b/services/redshift/serverless.go
@@ -3,7 +3,7 @@ package redshift
import (
"crypto/rand"
"encoding/hex"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"strconv"
"time"
@@ -253,16 +253,16 @@ type ServerlessUsageLimit struct {
// unlike opensearch's DataSourceAttachment) and the registry marshals the live
// struct with its own tags (gopherstack-n746d).
type ServerlessScheduledAction struct {
- StartTime time.Time `json:"startTime,omitzero"`
- EndTime time.Time `json:"endTime,omitzero"`
- NamespaceName string `json:"namespaceName,omitempty"`
- RoleArn string `json:"roleArn,omitempty"`
- ScheduledActionDescription string `json:"scheduledActionDescription,omitempty"`
- ScheduledActionName string `json:"scheduledActionName"`
- ScheduledActionUUID string `json:"scheduledActionUuid,omitempty"`
- State string `json:"state"`
- Schedule json.RawMessage `json:"schedule,omitempty"`
- TargetAction json.RawMessage `json:"targetAction,omitempty"`
+ StartTime time.Time `json:"startTime,omitzero"`
+ EndTime time.Time `json:"endTime,omitzero"`
+ NamespaceName string `json:"namespaceName,omitempty"`
+ RoleArn string `json:"roleArn,omitempty"`
+ ScheduledActionDescription string `json:"scheduledActionDescription,omitempty"`
+ ScheduledActionName string `json:"scheduledActionName"`
+ ScheduledActionUUID string `json:"scheduledActionUuid,omitempty"`
+ State string `json:"state"`
+ Schedule jsontext.Value `json:"schedule,omitempty"`
+ TargetAction jsontext.Value `json:"targetAction,omitempty"`
}
// ServerlessCustomDomainAssociation represents a Redshift Serverless custom
diff --git a/services/redshift/serverless_index_test.go b/services/redshift/serverless_index_test.go
index b5325daeac..2ca8122b65 100644
--- a/services/redshift/serverless_index_test.go
+++ b/services/redshift/serverless_index_test.go
@@ -1,7 +1,7 @@
package redshift_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"sort"
"testing"
@@ -254,8 +254,8 @@ func TestServerlessScheduledActionIndex_OrderedWithFilter(t *testing.T) {
ScheduledActionName: s.name,
NamespaceName: s.ns,
RoleArn: "arn:aws:iam::000000000000:role/scheduler",
- Schedule: json.RawMessage(`{"cron":"0 10 ? * MON *"}`),
- TargetAction: json.RawMessage(
+ Schedule: jsontext.Value(`{"cron":"0 10 ? * MON *"}`),
+ TargetAction: jsontext.Value(
`{"createSnapshot":{"namespaceName":"` + s.ns + `","snapshotName":"snap"}}`,
),
StartTime: start,
diff --git a/services/redshift/serverless_scheduled_actions.go b/services/redshift/serverless_scheduled_actions.go
index 31b0134829..a7a7178a4c 100644
--- a/services/redshift/serverless_scheduled_actions.go
+++ b/services/redshift/serverless_scheduled_actions.go
@@ -1,7 +1,7 @@
package redshift
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"strconv"
"time"
@@ -23,8 +23,8 @@ type CreateScheduledActionParams struct {
NamespaceName string
RoleArn string
ScheduledActionDescription string
- Schedule json.RawMessage
- TargetAction json.RawMessage
+ Schedule jsontext.Value
+ TargetAction jsontext.Value
}
// UpdateScheduledActionParams holds the mutable fields accepted by
@@ -35,8 +35,8 @@ type UpdateScheduledActionParams struct {
ScheduledActionDescription *string
Enabled *bool
RoleArn string
- Schedule json.RawMessage
- TargetAction json.RawMessage
+ Schedule jsontext.Value
+ TargetAction jsontext.Value
}
// CreateServerlessScheduledAction creates a serverless scheduled action.
@@ -220,11 +220,11 @@ func (b *InMemoryBackend) DeleteServerlessScheduledAction(
func cloneServerlessScheduledAction(sa *ServerlessScheduledAction) *ServerlessScheduledAction {
cp := *sa
if sa.Schedule != nil {
- cp.Schedule = append(json.RawMessage(nil), sa.Schedule...)
+ cp.Schedule = append(jsontext.Value(nil), sa.Schedule...)
}
if sa.TargetAction != nil {
- cp.TargetAction = append(json.RawMessage(nil), sa.TargetAction...)
+ cp.TargetAction = append(jsontext.Value(nil), sa.TargetAction...)
}
return &cp
diff --git a/services/redshiftdata/engine.go b/services/redshiftdata/engine.go
index 9d415b17ac..1df1644fe6 100644
--- a/services/redshiftdata/engine.go
+++ b/services/redshiftdata/engine.go
@@ -2,11 +2,13 @@ package redshiftdata
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"strconv"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// EngineLogin identifies the master login of a cluster's live SQL engine.
@@ -196,7 +198,7 @@ func resultSize(rs *ResultSet) int64 {
return 0
}
- raw, err := json.Marshal(rs.Rows)
+ raw, err := json.Marshal(rs.Rows, jsonx.V1())
if err != nil {
return 0
}
diff --git a/services/redshiftdata/engine_pgx.go b/services/redshiftdata/engine_pgx.go
index b170f4ff37..7db0d3069c 100644
--- a/services/redshiftdata/engine_pgx.go
+++ b/services/redshiftdata/engine_pgx.go
@@ -4,7 +4,7 @@ import (
"context"
"database/sql"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"math"
@@ -17,6 +17,8 @@ import (
"github.com/jackc/pgx/v5/pgconn"
_ "github.com/jackc/pgx/v5/stdlib" // registers the "pgx" database/sql driver
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -314,7 +316,7 @@ func bytesField(dbType string, b []byte) Field {
func compositeString(v any) string {
switch reflect.ValueOf(v).Kind() {
case reflect.Map, reflect.Slice, reflect.Array, reflect.Struct:
- if raw, err := json.Marshal(v); err == nil {
+ if raw, err := json.Marshal(v, jsonx.V1()); err == nil {
return string(raw)
}
default:
diff --git a/services/redshiftdata/engine_result.go b/services/redshiftdata/engine_result.go
index 19b066b898..7a282ac192 100644
--- a/services/redshiftdata/engine_result.go
+++ b/services/redshiftdata/engine_result.go
@@ -4,9 +4,11 @@ import (
"bytes"
"context"
"encoding/csv"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strconv"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -45,7 +47,7 @@ func (h *Handler) engineStatementResult(
keyColumnMetadata: rs.Columns,
keyTotalNumRows: int64(len(rs.Rows)),
keyNextToken: "",
- })
+ }, jsonx.V1())
}
func renderCSVResult(rs *ResultSet) ([]byte, error) {
@@ -76,7 +78,7 @@ func renderCSVResult(rs *ResultSet) ([]byte, error) {
keyTotalNumRows: int64(len(rs.Rows)),
keyResultFormat: resultFormatCSV,
keyNextToken: "",
- })
+ }, jsonx.V1())
}
func csvCell(f Field) string {
diff --git a/services/redshiftdata/handler.go b/services/redshiftdata/handler.go
index d20a4f3750..025ee8d4b5 100644
--- a/services/redshiftdata/handler.go
+++ b/services/redshiftdata/handler.go
@@ -2,7 +2,7 @@ package redshiftdata
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -178,7 +179,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
var data map[string]any
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
@@ -262,7 +263,7 @@ func writeInternalServerError(c *echo.Context) error {
payload, err := json.Marshal(map[string]string{
keyTypeField: "InternalServerException",
keyMessageField: "internal server error",
- })
+ }, jsonx.V1())
if err != nil {
return err
}
@@ -271,15 +272,12 @@ func writeInternalServerError(c *echo.Context) error {
}
func (h *Handler) handleError(c *echo.Context, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, ErrNotFound):
payload, _ := json.Marshal(map[string]string{
keyTypeField: "ResourceNotFoundException",
keyMessageField: errMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
case errors.Is(err, ErrTerminalState),
@@ -288,7 +286,7 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
payload, _ := json.Marshal(map[string]string{
keyTypeField: errValidationException,
keyMessageField: errMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
case errors.Is(err, errUnknownAction):
@@ -297,7 +295,7 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
keyMessageField: err.Error(),
})
case errors.Is(err, errInvalidRequest), errors.Is(err, errMissingID),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
return c.JSON(http.StatusBadRequest, map[string]string{
keyTypeField: errValidationException,
keyMessageField: err.Error(),
diff --git a/services/redshiftdata/handler_databases.go b/services/redshiftdata/handler_databases.go
index 31bcb0ffce..38b254465e 100644
--- a/services/redshiftdata/handler_databases.go
+++ b/services/redshiftdata/handler_databases.go
@@ -2,8 +2,10 @@ package redshiftdata
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleListDatabases(ctx context.Context, body []byte) ([]byte, error) {
@@ -17,7 +19,7 @@ func (h *Handler) handleListDatabases(ctx context.Context, body []byte) ([]byte,
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -40,7 +42,7 @@ func (h *Handler) handleListDatabases(ctx context.Context, body []byte) ([]byte,
resp[keyNextToken] = next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
func (h *Handler) handleListSchemas(ctx context.Context, body []byte) ([]byte, error) {
@@ -62,7 +64,7 @@ func (h *Handler) handleListSchemas(ctx context.Context, body []byte) ([]byte, e
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -90,7 +92,7 @@ func (h *Handler) handleListSchemas(ctx context.Context, body []byte) ([]byte, e
resp[keyNextToken] = next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// paginateStrings applies cursor-based pagination to a sorted string slice.
diff --git a/services/redshiftdata/handler_databases_test.go b/services/redshiftdata/handler_databases_test.go
index f025636bf1..c7f35e6ad6 100644
--- a/services/redshiftdata/handler_databases_test.go
+++ b/services/redshiftdata/handler_databases_test.go
@@ -1,12 +1,14 @@
package redshiftdata_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_ListDatabases(t *testing.T) {
@@ -17,7 +19,7 @@ func TestHandler_ListDatabases(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Databases"])
}
@@ -38,7 +40,7 @@ func TestHandler_ListSchemas(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Schemas"])
}
@@ -59,7 +61,7 @@ func TestHandler_ListDatabases_ReturnsNonEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dbs, ok := resp["Databases"].([]any)
require.True(t, ok)
@@ -74,7 +76,7 @@ func TestHandler_ListDatabases_NextTokenOmittedWhenComplete(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp["NextToken"]
assert.False(t, ok, "NextToken should be omitted when the demo database list fits on one page")
@@ -88,7 +90,7 @@ func TestHandler_ListDatabases_MaxResults1_PaginatesWithToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dbs, ok := resp["Databases"].([]any)
require.True(t, ok)
@@ -117,7 +119,7 @@ func TestHandler_ListDatabases_NextToken_ResumesFromCursor(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1, jsonx.V1()))
token, _ := page1["NextToken"].(string)
require.NotEmpty(t, token)
@@ -127,7 +129,7 @@ func TestHandler_ListDatabases_NextToken_ResumesFromCursor(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var page2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
dbs2, _ := page2["Databases"].([]any)
dbs1, _ := page1["Databases"].([]any)
@@ -143,7 +145,7 @@ func TestHandler_ListSchemas_ReturnsNonEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
schemas, ok := resp["Schemas"].([]any)
require.True(t, ok)
@@ -175,7 +177,7 @@ func TestHandler_ListSchemas_SchemaPattern_WildcardMatchesAll(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
schemas, ok := resp["Schemas"].([]any)
require.True(t, ok)
@@ -195,7 +197,7 @@ func TestHandler_ListSchemas_SchemaPattern_NoMatch(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
schemas, _ := resp["Schemas"].([]any)
assert.Empty(t, schemas, "non-matching pattern should return empty schemas")
@@ -237,7 +239,7 @@ func TestHandler_ListSchemas_SQLLike_UnderscoreWildcard(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
schemas, _ := resp["Schemas"].([]any)
if tt.want {
@@ -262,7 +264,7 @@ func TestListDatabases_ReturnsDemoData(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dbs, ok := resp["Databases"].([]any)
require.True(t, ok)
@@ -283,7 +285,7 @@ func TestListSchemas_ReturnsDemoData(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
schemas, ok := resp["Schemas"].([]any)
require.True(t, ok)
@@ -303,7 +305,7 @@ func TestListDatabases_HasNextToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
token, ok := resp["NextToken"]
assert.True(t, ok, "NextToken should be present when more pages remain")
diff --git a/services/redshiftdata/handler_sessions.go b/services/redshiftdata/handler_sessions.go
index 2529ca95d9..fa48d11e72 100644
--- a/services/redshiftdata/handler_sessions.go
+++ b/services/redshiftdata/handler_sessions.go
@@ -2,8 +2,10 @@ package redshiftdata
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleListSessions handles ListSessions. Field names verified against
@@ -22,7 +24,7 @@ func (h *Handler) handleListSessions(ctx context.Context, body []byte) ([]byte,
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -59,7 +61,7 @@ func (h *Handler) handleListSessions(ctx context.Context, body []byte) ([]byte,
resp[keyNextToken] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// sessionToListItem converts a derived SessionData to the SessionData wire shape
diff --git a/services/redshiftdata/handler_statements.go b/services/redshiftdata/handler_statements.go
index 2af3f445f5..13a793e35b 100644
--- a/services/redshiftdata/handler_statements.go
+++ b/services/redshiftdata/handler_statements.go
@@ -2,11 +2,13 @@ package redshiftdata
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// handleExecuteStatement: a positive SessionKeepAliveSeconds without SessionId mints a session
@@ -29,7 +31,7 @@ func (h *Handler) handleExecuteStatement(ctx context.Context, body []byte) ([]by
WithEvent bool `json:"WithEvent"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -50,7 +52,7 @@ func (h *Handler) handleExecuteStatement(ctx context.Context, body []byte) ([]by
if id, ok := h.lookupIdempotentStatement(tokenKey); ok {
if stmt, descErr := h.Backend.DescribeStatement(ctx, id); descErr == nil {
- return json.Marshal(statementCreateResponse(stmt))
+ return json.Marshal(statementCreateResponse(stmt), jsonx.V1())
}
}
@@ -78,7 +80,7 @@ func (h *Handler) handleExecuteStatement(ctx context.Context, body []byte) ([]by
}
}
- return json.Marshal(statementCreateResponse(stmt))
+ return json.Marshal(statementCreateResponse(stmt), jsonx.V1())
}
const maxSessionKeepAliveSeconds = 24 * 60 * 60
@@ -117,7 +119,7 @@ func (h *Handler) handleBatchExecuteStatement(ctx context.Context, body []byte)
WithEvent bool `json:"WithEvent"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -138,7 +140,7 @@ func (h *Handler) handleBatchExecuteStatement(ctx context.Context, body []byte)
if id, ok := h.lookupIdempotentStatement(tokenKey); ok {
if stmt, descErr := h.Backend.DescribeStatement(ctx, id); descErr == nil {
- return json.Marshal(statementCreateResponse(stmt))
+ return json.Marshal(statementCreateResponse(stmt), jsonx.V1())
}
}
@@ -166,7 +168,7 @@ func (h *Handler) handleBatchExecuteStatement(ctx context.Context, body []byte)
}
}
- return json.Marshal(statementCreateResponse(stmt))
+ return json.Marshal(statementCreateResponse(stmt), jsonx.V1())
}
// statementCreateResponse builds the shared ExecuteStatementOutput/
@@ -205,7 +207,7 @@ func (h *Handler) handleDescribeStatement(ctx context.Context, body []byte) ([]b
WaitTimeSeconds int32 `json:"WaitTimeSeconds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -222,7 +224,7 @@ func (h *Handler) handleDescribeStatement(ctx context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(statementToDescribeResponse(stmt))
+ return json.Marshal(statementToDescribeResponse(stmt), jsonx.V1())
}
func (h *Handler) handleGetStatementResult(ctx context.Context, body []byte) ([]byte, error) {
@@ -232,7 +234,7 @@ func (h *Handler) handleGetStatementResult(ctx context.Context, body []byte) ([]
WaitTimeSeconds int32 `json:"WaitTimeSeconds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -282,7 +284,7 @@ func (h *Handler) handleGetStatementResult(ctx context.Context, body []byte) ([]
},
keyTotalNumRows: int64(1),
keyNextToken: "",
- })
+ }, jsonx.V1())
}
func (h *Handler) handleGetStatementResultV2(ctx context.Context, body []byte) ([]byte, error) {
@@ -292,7 +294,7 @@ func (h *Handler) handleGetStatementResultV2(ctx context.Context, body []byte) (
WaitTimeSeconds int32 `json:"WaitTimeSeconds"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -348,7 +350,7 @@ func (h *Handler) handleGetStatementResultV2(ctx context.Context, body []byte) (
keyTotalNumRows: int64(1),
keyResultFormat: resultFormatCSV,
keyNextToken: "",
- })
+ }, jsonx.V1())
}
func (h *Handler) handleListStatements(ctx context.Context, body []byte) ([]byte, error) {
@@ -363,7 +365,7 @@ func (h *Handler) handleListStatements(ctx context.Context, body []byte) ([]byte
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -410,7 +412,7 @@ func (h *Handler) handleListStatements(ctx context.Context, body []byte) ([]byte
resp[keyNextToken] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
func (h *Handler) handleCancelStatement(ctx context.Context, body []byte) ([]byte, error) {
@@ -418,7 +420,7 @@ func (h *Handler) handleCancelStatement(ctx context.Context, body []byte) ([]byt
ID string `json:"Id"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -432,7 +434,7 @@ func (h *Handler) handleCancelStatement(ctx context.Context, body []byte) ([]byt
return json.Marshal(map[string]any{
keyStatusField: true,
- })
+ }, jsonx.V1())
}
// epochSeconds converts a [time.Time] to Unix epoch seconds as float64,
diff --git a/services/redshiftdata/handler_statements_lifecycle_test.go b/services/redshiftdata/handler_statements_lifecycle_test.go
index 9f604016dc..123834f347 100644
--- a/services/redshiftdata/handler_statements_lifecycle_test.go
+++ b/services/redshiftdata/handler_statements_lifecycle_test.go
@@ -2,7 +2,7 @@ package redshiftdata_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"sync"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/redshiftdata"
)
@@ -27,7 +28,7 @@ func TestCancelStatement_SuccessStatusBoolean(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
status, ok := resp["Status"].(bool)
require.True(t, ok, "Status should be a boolean")
@@ -50,7 +51,7 @@ func TestBatchExecuteStatement_QueryStringIsFirstSQL(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
@@ -58,7 +59,7 @@ func TestBatchExecuteStatement_QueryStringIsFirstSQL(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "SELECT 1", resp["QueryString"], "QueryString should equal the first SQL")
}
@@ -78,7 +79,7 @@ func TestDescribeStatement_ResultSizeAndRows(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
@@ -86,7 +87,7 @@ func TestDescribeStatement_ResultSizeAndRows(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["ResultRows"], "ResultRows should be present")
assert.NotNil(t, resp["ResultSize"], "ResultSize should be present")
@@ -109,7 +110,7 @@ func TestDescribeStatement_SubStatements(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
@@ -117,7 +118,7 @@ func TestDescribeStatement_SubStatements(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
subs, ok := resp["SubStatements"].([]any)
require.True(t, ok, "SubStatements should be present")
@@ -143,7 +144,7 @@ func TestGetStatementResult_ReturnsDemoRow(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
@@ -151,7 +152,7 @@ func TestGetStatementResult_ReturnsDemoRow(t *testing.T) {
require.Equal(t, http.StatusOK, resultRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(resultRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(resultRec.Body.Bytes(), &resp, jsonx.V1()))
records, ok := resp["Records"].([]any)
require.True(t, ok, "Records should be a slice")
@@ -191,7 +192,7 @@ func TestListStatements_MaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts := resp["Statements"].([]any)
assert.Len(t, stmts, testMaxResultsLimit, "should be truncated to MaxResults")
@@ -212,7 +213,7 @@ func TestListStatements_MaxResultsTooLarge(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
@@ -239,7 +240,7 @@ func TestWithEvent_AcceptedButNotEchoed(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.NotContains(t, createResp, "WithEvent")
id := createResp["Id"].(string)
@@ -248,7 +249,7 @@ func TestWithEvent_AcceptedButNotEchoed(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotContains(t, resp, "WithEvent")
}
@@ -270,7 +271,7 @@ func TestListStatements_StatusFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts := resp["Statements"].([]any)
require.Len(t, stmts, 1)
@@ -373,7 +374,7 @@ func TestListStatements_WorkgroupFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts := resp["Statements"].([]any)
require.Len(t, stmts, 1, "should only return statements for wg-a")
@@ -396,7 +397,7 @@ func TestGetStatementResultV2_DemoRow(t *testing.T) {
require.Equal(t, http.StatusOK, execRec.Code)
var execResp map[string]any
- require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp))
+ require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp, jsonx.V1()))
id := execResp["Id"].(string)
@@ -404,7 +405,7 @@ func TestGetStatementResultV2_DemoRow(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
records, ok := resp["Records"].([]any)
require.True(t, ok, "Records should be present")
@@ -427,7 +428,7 @@ func TestCancelStatement_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
}
@@ -442,7 +443,7 @@ func TestGetStatementResult_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
}
@@ -460,7 +461,7 @@ func TestBatchExecuteStatement_EmptySqls(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
@@ -475,7 +476,7 @@ func TestDescribeStatement_MissingID(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
@@ -494,7 +495,7 @@ func TestDescribeStatement_RedshiftQueryId(t *testing.T) {
require.Equal(t, http.StatusOK, execRec.Code)
var execResp map[string]any
- require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp))
+ require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp, jsonx.V1()))
id := execResp["Id"].(string)
@@ -502,7 +503,7 @@ func TestDescribeStatement_RedshiftQueryId(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp["RedshiftQueryId"]
assert.True(t, ok, "RedshiftQueryId should be present in DescribeStatement response")
@@ -547,7 +548,7 @@ func TestGetStatementResult_HasNextToken(t *testing.T) {
require.Equal(t, http.StatusOK, execRec.Code)
var execResp map[string]any
- require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp))
+ require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp, jsonx.V1()))
id := execResp["Id"].(string)
@@ -555,7 +556,7 @@ func TestGetStatementResult_HasNextToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp["NextToken"]
assert.True(t, ok, "NextToken field should always be present in GetStatementResult response")
diff --git a/services/redshiftdata/handler_statements_semantics_test.go b/services/redshiftdata/handler_statements_semantics_test.go
index 7b7fe90ee0..88b5c09225 100644
--- a/services/redshiftdata/handler_statements_semantics_test.go
+++ b/services/redshiftdata/handler_statements_semantics_test.go
@@ -1,13 +1,15 @@
package redshiftdata_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/redshiftdata"
)
@@ -63,14 +65,14 @@ func TestResultFormatControlsResultAPI(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var execResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &execResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &execResp, jsonx.V1()))
rec = doRequest(t, h, tt.getOperation, map[string]any{"Id": execResp["Id"]})
assert.Equal(t, tt.wantStatus, rec.Code)
if tt.wantResultFmt != "" {
var resultResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resultResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resultResp, jsonx.V1()))
assert.Equal(t, tt.wantResultFmt, resultResp["ResultFormat"])
}
})
@@ -109,13 +111,13 @@ func TestResultFormatMetadataAndValidation(t *testing.T) {
}
var execResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &execResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &execResp, jsonx.V1()))
rec = doRequest(t, h, "DescribeStatement", map[string]any{"Id": execResp["Id"]})
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
wantFormat := tt.format
if wantFormat == "" {
wantFormat = "JSON"
@@ -159,7 +161,7 @@ func TestListStatementsFilters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["Statements"], tt.wantCount)
})
}
@@ -181,7 +183,7 @@ func TestListStatementsNextToken(t *testing.T) {
require.Equal(t, http.StatusOK, first.Code)
var firstResp map[string]any
- require.NoError(t, json.Unmarshal(first.Body.Bytes(), &firstResp))
+ require.NoError(t, json.Unmarshal(first.Body.Bytes(), &firstResp, jsonx.V1()))
token, ok := firstResp["NextToken"].(string)
require.True(t, ok)
@@ -252,7 +254,7 @@ func TestHasResultSet_BySQL(t *testing.T) {
require.Equal(t, http.StatusOK, execRec.Code)
var execResp map[string]any
- require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp))
+ require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp, jsonx.V1()))
id := execResp["Id"].(string)
@@ -260,7 +262,7 @@ func TestHasResultSet_BySQL(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, tt.wantResultSet, descResp["HasResultSet"],
"SQL %q: HasResultSet mismatch", tt.sql)
@@ -298,7 +300,7 @@ func TestGetStatementResult_RequiresResultSet(t *testing.T) {
require.Equal(t, http.StatusOK, execRec.Code)
var execResp map[string]any
- require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp))
+ require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp, jsonx.V1()))
id := execResp["Id"].(string)
@@ -308,7 +310,7 @@ func TestGetStatementResult_RequiresResultSet(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
})
@@ -373,7 +375,7 @@ func TestParameters_AcceptedAndStored(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Id"], "Id should be returned")
// DescribeStatementOutput.QueryParameters must round-trip whatever
@@ -385,7 +387,7 @@ func TestParameters_AcceptedAndStored(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
if len(tt.params) == 0 {
assert.Nil(t, descResp["QueryParameters"],
@@ -430,13 +432,13 @@ func TestBatchExecuteStatement_ParametersAcceptedAndStored(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
descRec := doRequest(t, h, "DescribeStatement", map[string]any{"Id": createResp["Id"]})
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
gotParams, ok := descResp["QueryParameters"].([]any)
require.True(t, ok, "QueryParameters should be a list")
@@ -470,21 +472,21 @@ func TestSessionID_EchoedByExecuteStatement(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var execResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &execResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &execResp, jsonx.V1()))
assert.Equal(t, "sess-abc123", execResp["SessionId"])
descRec := doRequest(t, h, "DescribeStatement", map[string]any{"Id": execResp["Id"]})
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "sess-abc123", descResp["SessionId"])
listRec := doRequest(t, h, "ListStatements", map[string]any{"Status": "ALL"})
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
stmts, ok := listResp["Statements"].([]any)
require.True(t, ok)
@@ -505,14 +507,14 @@ func TestSessionID_EchoedByExecuteStatement(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var execResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &execResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &execResp, jsonx.V1()))
assert.Nil(t, execResp["SessionId"], "SessionId should be omitted, not empty-string, when not supplied")
descRec := doRequest(t, h, "DescribeStatement", map[string]any{"Id": execResp["Id"]})
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Nil(t, descResp["SessionId"])
})
}
@@ -533,14 +535,14 @@ func TestSessionID_EchoedByBatchExecuteStatement(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var execResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &execResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &execResp, jsonx.V1()))
assert.Equal(t, "sess-batch-1", execResp["SessionId"])
descRec := doRequest(t, h, "DescribeStatement", map[string]any{"Id": execResp["Id"]})
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "sess-batch-1", descResp["SessionId"])
}
@@ -574,7 +576,7 @@ func TestExecuteStatement_CaseInsensitiveSQL(t *testing.T) {
require.Equal(t, http.StatusOK, execRec.Code)
var execResp map[string]any
- require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp))
+ require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp, jsonx.V1()))
id := execResp["Id"].(string)
@@ -582,7 +584,7 @@ func TestExecuteStatement_CaseInsensitiveSQL(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, tt.wantResultSet, descResp["HasResultSet"],
"SQL %q: HasResultSet mismatch", tt.sql)
@@ -619,9 +621,9 @@ func TestListStatements_NoFabricatedFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp struct {
- Statements []map[string]json.RawMessage `json:"Statements"`
+ Statements []map[string]jsontext.Value `json:"Statements"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotEmpty(t, resp.Statements)
assert.NotContains(t, resp.Statements[0], field)
})
diff --git a/services/redshiftdata/handler_statements_test.go b/services/redshiftdata/handler_statements_test.go
index fa38c42830..67422bd7ff 100644
--- a/services/redshiftdata/handler_statements_test.go
+++ b/services/redshiftdata/handler_statements_test.go
@@ -2,13 +2,14 @@ package redshiftdata_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/redshiftdata"
)
@@ -64,7 +65,7 @@ func TestHandler_ExecuteStatement(t *testing.T) {
if tt.wantID {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Id"])
}
})
@@ -107,7 +108,7 @@ func TestHandler_BatchExecuteStatement(t *testing.T) {
if tt.wantID {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Id"])
}
})
@@ -134,7 +135,7 @@ func TestHandler_DescribeStatement(t *testing.T) {
"Database": "testdb",
})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["Id"].(string)
},
@@ -171,7 +172,7 @@ func TestHandler_DescribeStatement(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tt.wantStatus2 != "" {
assert.Equal(t, tt.wantStatus2, resp["Status"])
@@ -201,7 +202,7 @@ func TestHandler_GetStatementResult(t *testing.T) {
"Database": "testdb",
})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["Id"].(string)
},
@@ -225,7 +226,7 @@ func TestHandler_GetStatementResult(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Records"])
}
})
@@ -252,7 +253,7 @@ func TestHandler_GetStatementResultV2(t *testing.T) {
"ResultFormat": "CSV",
})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["Id"].(string)
},
@@ -291,7 +292,7 @@ func TestHandler_GetStatementResultV2(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tt.checkRecords {
assert.NotNil(t, resp["Records"])
@@ -349,7 +350,7 @@ func TestHandler_ListStatements(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts, ok := resp["Statements"].([]any)
require.True(t, ok)
@@ -399,7 +400,7 @@ func TestHandler_CancelStatement(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, resp["__type"])
})
}
@@ -419,7 +420,7 @@ func TestHandler_CancelStatement_AlreadyFinished(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id := resp["Id"].(string)
@@ -428,7 +429,7 @@ func TestHandler_CancelStatement_AlreadyFinished(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, cancelRec.Code)
var cancelResp map[string]any
- require.NoError(t, json.Unmarshal(cancelRec.Body.Bytes(), &cancelResp))
+ require.NoError(t, json.Unmarshal(cancelRec.Body.Bytes(), &cancelResp, jsonx.V1()))
assert.Equal(t, "ValidationException", cancelResp["__type"])
}
@@ -456,7 +457,7 @@ func TestHandler_ListStatements_WithFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts := resp["Statements"].([]any)
assert.Len(t, stmts, 1)
@@ -484,7 +485,7 @@ func TestHandler_ListStatements_WithWorkgroupFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts := resp["Statements"].([]any)
assert.Len(t, stmts, 1)
@@ -507,7 +508,7 @@ func TestHandler_DescribeStatement_AllFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
@@ -515,7 +516,7 @@ func TestHandler_DescribeStatement_AllFields(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, id, resp["Id"])
assert.Equal(t, "FINISHED", resp["Status"])
@@ -545,7 +546,7 @@ func TestHandler_BatchExecuteStatement_AllFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
@@ -553,7 +554,7 @@ func TestHandler_BatchExecuteStatement_AllFields(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, id, resp["Id"])
// IsBatchStatement/QueryStrings are real StatementData (ListStatements
@@ -569,7 +570,7 @@ func TestHandler_BatchExecuteStatement_AllFields(t *testing.T) {
var listResp struct {
Statements []map[string]any `json:"Statements"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.NotEmpty(t, listResp.Statements)
assert.Equal(t, true, listResp.Statements[0]["IsBatchStatement"])
@@ -595,7 +596,7 @@ func TestHandler_ListStatements_WithSecretARN(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts := resp["Statements"].([]any)
require.Len(t, stmts, 1)
@@ -782,7 +783,7 @@ func TestHandler_ListSessions(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tt.wantErrType != "" {
assert.Equal(t, tt.wantErrType, resp["__type"])
diff --git a/services/redshiftdata/handler_statements_validation_test.go b/services/redshiftdata/handler_statements_validation_test.go
index 620a72f2af..7a3c0ab92c 100644
--- a/services/redshiftdata/handler_statements_validation_test.go
+++ b/services/redshiftdata/handler_statements_validation_test.go
@@ -2,7 +2,7 @@ package redshiftdata_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/redshiftdata"
)
@@ -210,7 +211,7 @@ func TestHandler_ListStatements_MaxResults_Paginates(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts, _ := resp["Statements"].([]any)
assert.Len(t, stmts, 2)
@@ -336,14 +337,14 @@ func TestGetStatementResult_NoResultSet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var batchResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &batchResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &batchResp, jsonx.V1()))
id := batchResp["Id"].(string)
rec = doRequest(t, h, "GetStatementResult", map[string]any{"Id": id})
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
@@ -362,14 +363,14 @@ func TestGetStatementResultV2_NoResultSet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var batchResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &batchResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &batchResp, jsonx.V1()))
id := batchResp["Id"].(string)
rec = doRequest(t, h, "GetStatementResultV2", map[string]any{"Id": id})
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
@@ -389,14 +390,14 @@ func TestGetStatementResultV2_ResultFormat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
rec = doRequest(t, h, "GetStatementResultV2", map[string]any{"Id": id})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "CSV", resp["ResultFormat"])
assert.EqualValues(t, 1, resp["TotalNumRows"])
assert.NotNil(t, resp["Records"])
@@ -417,7 +418,7 @@ func TestCancelStatement_AbortedIsTerminal(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
@@ -435,7 +436,7 @@ func TestCancelStatement_FailedIsTerminal(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
@@ -459,7 +460,7 @@ func TestListStatements_SortedNewestFirst(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts := resp["Statements"].([]any)
require.Len(t, stmts, 3)
@@ -487,7 +488,7 @@ func TestExecuteStatement_HasResultSet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
@@ -495,7 +496,7 @@ func TestExecuteStatement_HasResultSet(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, true, descResp["HasResultSet"])
}
@@ -513,7 +514,7 @@ func TestBatchExecuteStatement_HasNoResultSet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Id"].(string)
@@ -521,7 +522,7 @@ func TestBatchExecuteStatement_HasNoResultSet(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, false, descResp["HasResultSet"])
}
diff --git a/services/redshiftdata/handler_tables.go b/services/redshiftdata/handler_tables.go
index 881fdcb86f..3936c63313 100644
--- a/services/redshiftdata/handler_tables.go
+++ b/services/redshiftdata/handler_tables.go
@@ -2,9 +2,11 @@ package redshiftdata
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleListTables(ctx context.Context, body []byte) ([]byte, error) {
@@ -25,7 +27,7 @@ func (h *Handler) handleListTables(ctx context.Context, body []byte) ([]byte, er
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -49,7 +51,7 @@ func (h *Handler) handleListTables(ctx context.Context, body []byte) ([]byte, er
resp[keyNextToken] = next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// filterDemoTables applies SchemaPattern and TablePattern filters to the demo table list.
@@ -99,7 +101,7 @@ func (h *Handler) handleDescribeTable(ctx context.Context, body []byte) ([]byte,
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -130,7 +132,7 @@ func (h *Handler) handleDescribeTable(ctx context.Context, body []byte) ([]byte,
resp[keyNextToken] = next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// paginateMaps applies cursor-based pagination to a slice of maps keyed by "name".
diff --git a/services/redshiftdata/handler_tables_test.go b/services/redshiftdata/handler_tables_test.go
index 07fa661a52..943767167d 100644
--- a/services/redshiftdata/handler_tables_test.go
+++ b/services/redshiftdata/handler_tables_test.go
@@ -1,12 +1,14 @@
package redshiftdata_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_ListTables(t *testing.T) {
@@ -17,7 +19,7 @@ func TestHandler_ListTables(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Tables"])
}
@@ -38,7 +40,7 @@ func TestHandler_DescribeTable(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["ColumnList"])
}
@@ -59,7 +61,7 @@ func TestHandler_ListTables_ReturnsNonEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tables, ok := resp["Tables"].([]any)
require.True(t, ok)
@@ -77,7 +79,7 @@ func TestHandler_ListTables_SchemaPattern_WildcardMatchesAll(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tables, ok := resp["Tables"].([]any)
require.True(t, ok)
@@ -95,7 +97,7 @@ func TestHandler_ListTables_TablePattern_WildcardMatchesAll(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tables, ok := resp["Tables"].([]any)
require.True(t, ok)
@@ -113,7 +115,7 @@ func TestHandler_ListTables_TablePattern_PrefixMatch(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tables, _ := resp["Tables"].([]any)
require.NotEmpty(t, tables, "user% should match 'users'")
@@ -141,7 +143,7 @@ func TestHandler_ListTables_MaxResults1_Paginates(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tables, ok := resp["Tables"].([]any)
require.True(t, ok)
@@ -166,7 +168,7 @@ func TestDescribeTable_ReturnsDemoColumns(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cols, ok := resp["ColumnList"].([]any)
require.True(t, ok, "ColumnList should be a slice")
@@ -192,7 +194,7 @@ func TestListTables_ReturnsDemoData(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tables, ok := resp["Tables"].([]any)
require.True(t, ok)
diff --git a/services/redshiftdata/handler_test.go b/services/redshiftdata/handler_test.go
index 9ff56e5773..52e639db20 100644
--- a/services/redshiftdata/handler_test.go
+++ b/services/redshiftdata/handler_test.go
@@ -3,7 +3,7 @@ package redshiftdata_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/redshiftdata"
)
@@ -37,7 +38,7 @@ func doRequest(t *testing.T, h *redshiftdata.Handler, op string, body any) *http
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -257,7 +258,7 @@ func TestHandler_UnknownOperation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
@@ -272,7 +273,7 @@ func TestUnknownOperation_Returns400(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
diff --git a/services/redshiftdata/idempotency_test.go b/services/redshiftdata/idempotency_test.go
index c806124d77..f30e30f553 100644
--- a/services/redshiftdata/idempotency_test.go
+++ b/services/redshiftdata/idempotency_test.go
@@ -1,12 +1,14 @@
package redshiftdata_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestExecuteStatement_ClientToken_ReplaysOnRetry verifies that retrying an
@@ -35,13 +37,13 @@ func TestExecuteStatement_ClientToken_ReplaysOnRetry(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
rec2 := doRequest(t, h, "ExecuteStatement", req)
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
assert.Equal(t, out1["Id"], out2["Id"], "retry with the same ClientToken must replay the same statement Id")
@@ -51,7 +53,7 @@ func TestExecuteStatement_ClientToken_ReplaysOnRetry(t *testing.T) {
var list struct {
Statements []map[string]any `json:"Statements"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &list))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &list, jsonx.V1()))
assert.Len(t, list.Statements, 1, "exactly one statement should actually be created")
}
@@ -74,13 +76,13 @@ func TestExecuteStatement_NoClientToken_CreatesDistinctStatements(t *testing.T)
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
rec2 := doRequest(t, h, "ExecuteStatement", req)
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
assert.NotEqual(t, out1["Id"], out2["Id"], "without a ClientToken each call must create a distinct statement")
}
@@ -105,13 +107,13 @@ func TestBatchExecuteStatement_ClientToken_ReplaysOnRetry(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
rec2 := doRequest(t, h, "BatchExecuteStatement", req)
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
assert.Equal(t, out1["Id"], out2["Id"], "retry with the same ClientToken must replay the same batch statement Id")
}
diff --git a/services/redshiftdata/persistence.go b/services/redshiftdata/persistence.go
index a39f8d820f..c64f021e3f 100644
--- a/services/redshiftdata/persistence.go
+++ b/services/redshiftdata/persistence.go
@@ -2,8 +2,9 @@ package redshiftdata
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -72,7 +73,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.defaultRegion,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "redshiftdata: failed to marshal snapshot", "error", err)
diff --git a/services/redshiftdata/persistence_test.go b/services/redshiftdata/persistence_test.go
index 6d0a03793a..ecd6a85ad8 100644
--- a/services/redshiftdata/persistence_test.go
+++ b/services/redshiftdata/persistence_test.go
@@ -2,12 +2,14 @@ package redshiftdata //nolint:testpackage // needs the unexported region ctx key
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const persistenceTestAccountID = "000000000000"
@@ -155,10 +157,10 @@ func runVersionMismatchCase(t *testing.T, mutate func(raw map[string]any)) {
require.NotNil(t, snap)
var raw map[string]any
- require.NoError(t, json.Unmarshal(snap, &raw))
+ require.NoError(t, json.Unmarshal(snap, &raw, jsonx.V1()))
mutate(raw)
- data, err := json.Marshal(raw)
+ data, err := json.Marshal(raw, jsonx.V1())
require.NoError(t, err)
require.NoError(t, target.Restore(t.Context(), data))
diff --git a/services/redshiftdata/s3_copy.go b/services/redshiftdata/s3_copy.go
index 2855f8ab00..3ff848bd2e 100644
--- a/services/redshiftdata/s3_copy.go
+++ b/services/redshiftdata/s3_copy.go
@@ -6,7 +6,8 @@ import (
"compress/gzip"
"context"
"database/sql"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +17,7 @@ import (
"github.com/jackc/pgx/v5/stdlib"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/parquetio"
)
@@ -166,7 +168,7 @@ func (r *s3Runner) manifestObjects(ctx context.Context, bucket, key string) ([]s
} `json:"entries"`
}
- if err = json.Unmarshal(raw, &m); err != nil {
+ if err = json.Unmarshal(raw, &m, jsonx.V1()); err != nil {
return nil, stmtErrorf("Manifest file is not in correct json format")
}
@@ -331,12 +333,11 @@ func appendJSON(
return err
}
- dec := json.NewDecoder(bytes.NewReader(data))
- dec.UseNumber()
+ dec := jsontext.NewDecoder(bytes.NewReader(data))
for {
var rec any
- if err = dec.Decode(&rec); errors.Is(err, io.EOF) {
+ if err = json.UnmarshalDecode(dec, &rec, jsonx.V1(), jsonx.UseNumber()); errors.Is(err, io.EOF) {
return nil
} else if err != nil {
return stmtErrorf("Invalid JSON record in S3 object: %v", err)
@@ -379,7 +380,7 @@ func jsonPathsFor(s *copySpec, cols []string, fetch func(string) ([]byte, error)
JSONPaths []string `json:"jsonpaths"`
}
- if err = json.Unmarshal(raw, &doc); err != nil {
+ if err = json.Unmarshal(raw, &doc, jsonx.V1()); err != nil {
return nil, stmtErrorf("The JSONPaths file is not valid JSON: %v", err)
}
@@ -504,14 +505,14 @@ func textCell(v any) *string {
return nil
case string:
s = x
- case json.Number:
+ case jsonx.Number:
s = x.String()
case bool:
s = strconv.FormatBool(x)
case int64, float64, int32, int:
s = fmt.Sprint(x)
default:
- raw, err := json.Marshal(x)
+ raw, err := json.Marshal(x, jsonx.V1())
if err != nil {
s = fmt.Sprint(x)
} else {
diff --git a/services/redshiftdata/s3_unload.go b/services/redshiftdata/s3_unload.go
index 1cc9be978f..dc134f53ce 100644
--- a/services/redshiftdata/s3_unload.go
+++ b/services/redshiftdata/s3_unload.go
@@ -7,12 +7,13 @@ import (
"encoding/base64"
"encoding/csv"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"net/url"
"strconv"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/parquetio"
)
@@ -402,12 +403,12 @@ func encodeJSONLines(cols []ColumnMetadata, rows [][]Field) ([]byte, error) {
buf.WriteByte('{')
for i, f := range row {
- name, err := json.Marshal(cols[i].Name)
+ name, err := json.Marshal(cols[i].Name, jsonx.V1())
if err != nil {
return nil, stmtErrorf("could not write JSON: %v", err)
}
- val, err := json.Marshal(jsonValue(f))
+ val, err := json.Marshal(jsonValue(f), jsonx.V1())
if err != nil {
return nil, stmtErrorf("could not write JSON: %v", err)
}
@@ -559,7 +560,7 @@ func unloadManifest(s *unloadSpec, rs *ResultSet, files []unloadFile) []byte {
doc["schema"] = map[string]any{"elements": elems}
}
- raw, err := json.Marshal(doc)
+ raw, err := json.Marshal(doc, jsonx.V1())
if err != nil {
return []byte("{}")
}
diff --git a/services/redshiftdata/session_keepalive_test.go b/services/redshiftdata/session_keepalive_test.go
index ab0cfda77e..6420108f43 100644
--- a/services/redshiftdata/session_keepalive_test.go
+++ b/services/redshiftdata/session_keepalive_test.go
@@ -1,7 +1,7 @@
package redshiftdata_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestSessionKeepAlive_Expiry(t *testing.T) {
@@ -82,7 +84,7 @@ func decodeBody(t *testing.T, rec interface{ Bytes() []byte }) map[string]any {
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/redshiftdata/wire_sdk_roundtrip_test.go b/services/redshiftdata/wire_sdk_roundtrip_test.go
index e874bebf33..e845b0db11 100644
--- a/services/redshiftdata/wire_sdk_roundtrip_test.go
+++ b/services/redshiftdata/wire_sdk_roundtrip_test.go
@@ -1,7 +1,8 @@
package redshiftdata_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -15,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/redshiftdata"
)
@@ -219,13 +221,13 @@ func TestDescribeStatement_NoFabricatedFields(t *testing.T) {
var execResp struct {
ID string `json:"Id"`
}
- require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp))
+ require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp, jsonx.V1()))
rec := doRequest(t, h, "DescribeStatement", map[string]any{"Id": execResp.ID})
require.Equal(t, http.StatusOK, rec.Code)
- var resp map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ var resp map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotContains(t, resp, tt.field)
})
}
diff --git a/services/rekognition/datasets.go b/services/rekognition/datasets.go
index 8fd91ba972..c05e49d956 100644
--- a/services/rekognition/datasets.go
+++ b/services/rekognition/datasets.go
@@ -2,7 +2,8 @@ package rekognition
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
@@ -11,6 +12,8 @@ import (
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (b *InMemoryBackend) datasetARN(projectARN, datasetType string) string {
@@ -161,8 +164,8 @@ func matchesDatasetEntryFilter(entry string, filter ListDatasetEntriesFilter) bo
// name found across its "-metadata" blocks, plus whether it carried at
// least one such block (i.e. is "labeled").
func entryLabels(entry string) ([]string, bool) {
- var obj map[string]json.RawMessage
- if err := json.Unmarshal([]byte(entry), &obj); err != nil {
+ var obj map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(entry), &obj, jsonx.V1()); err != nil {
return nil, false
}
@@ -186,8 +189,8 @@ func entryLabels(entry string) ([]string, bool) {
// entrySourceRef parses one JSON-lines dataset entry and returns its
// "source-ref" field (the image's S3 location), or "" if absent/malformed.
func entrySourceRef(entry string) string {
- var obj map[string]json.RawMessage
- if err := json.Unmarshal([]byte(entry), &obj); err != nil {
+ var obj map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(entry), &obj, jsonx.V1()); err != nil {
return ""
}
@@ -197,7 +200,7 @@ func entrySourceRef(entry string) string {
}
var ref string
- _ = json.Unmarshal(raw, &ref)
+ _ = json.Unmarshal(raw, &ref, jsonx.V1())
return ref
}
@@ -292,9 +295,9 @@ func computeDatasetStats(entries []string) DatasetStats {
}
// labelNamesFromMeta parses a -metadata block and returns its label names.
-func labelNamesFromMeta(raw json.RawMessage) []string {
- var meta map[string]json.RawMessage
- if err := json.Unmarshal(raw, &meta); err != nil {
+func labelNamesFromMeta(raw jsontext.Value) []string {
+ var meta map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &meta, jsonx.V1()); err != nil {
return nil
}
@@ -303,15 +306,15 @@ func labelNamesFromMeta(raw json.RawMessage) []string {
// Single-label: "class-name"
if cn, ok := meta["class-name"]; ok {
var name string
- if err := json.Unmarshal(cn, &name); err == nil && name != "" {
+ if err := json.Unmarshal(cn, &name, jsonx.V1()); err == nil && name != "" {
names = append(names, name)
}
}
// Multi-label: "class-map": {"LabelA": ..., "LabelB": ...}
if cm, ok := meta["class-map"]; ok {
- var classMap map[string]json.RawMessage
- if err := json.Unmarshal(cm, &classMap); err == nil {
+ var classMap map[string]jsontext.Value
+ if err := json.Unmarshal(cm, &classMap, jsonx.V1()); err == nil {
for name := range classMap {
names = append(names, name)
}
@@ -333,7 +336,7 @@ func decodeDatasetPageToken(token string) int {
}
var tok datasetPaginationToken
- if err = json.Unmarshal(decoded, &tok); err != nil || tok.Offset <= 0 {
+ if err = json.Unmarshal(decoded, &tok, jsonx.V1()); err != nil || tok.Offset <= 0 {
return 0
}
@@ -395,7 +398,7 @@ func (b *InMemoryBackend) ListDatasetLabels(
var outToken string
if end < len(names) {
- tok, _ := json.Marshal(datasetPaginationToken{Offset: end})
+ tok, _ := json.Marshal(datasetPaginationToken{Offset: end}, jsonx.V1())
outToken = base64.RawURLEncoding.EncodeToString(tok)
}
diff --git a/services/rekognition/handler.go b/services/rekognition/handler.go
index ce06aa2530..4b04241d02 100644
--- a/services/rekognition/handler.go
+++ b/services/rekognition/handler.go
@@ -2,7 +2,7 @@ package rekognition
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -15,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -140,7 +141,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
diff --git a/services/rekognition/handler_async_video_fields_test.go b/services/rekognition/handler_async_video_fields_test.go
index b47437d86d..97b5cf7fc0 100644
--- a/services/rekognition/handler_async_video_fields_test.go
+++ b/services/rekognition/handler_async_video_fields_test.go
@@ -1,13 +1,15 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -80,7 +82,7 @@ func TestAsyncVideoJob_JobTagAndVideoRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, tc.startAction)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID, ok := startResp["JobId"].(string)
require.True(t, ok)
@@ -88,7 +90,7 @@ func TestAsyncVideoJob_JobTagAndVideoRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, tc.getAction)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "my-job-tag", getResp["JobTag"])
video, _ := getResp["Video"].(map[string]any)
@@ -111,7 +113,7 @@ func TestAsyncVideoJob_NoVideoOmitsVideoField(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID, ok := startResp["JobId"].(string)
require.True(t, ok)
@@ -119,7 +121,7 @@ func TestAsyncVideoJob_NoVideoOmitsVideoField(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.NotContains(t, getResp, "Video")
assert.NotContains(t, getResp, "JobTag")
}
@@ -142,7 +144,7 @@ func TestGetSegmentDetection_SelectedSegmentTypesEchoesRequest(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID, ok := startResp["JobId"].(string)
require.True(t, ok)
@@ -150,7 +152,7 @@ func TestGetSegmentDetection_SelectedSegmentTypesEchoesRequest(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
selected, ok := getResp["SelectedSegmentTypes"].([]any)
require.True(t, ok)
require.Len(t, selected, 2)
@@ -199,7 +201,7 @@ func TestGetLabelDetection_GetRequestMetadata(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID, ok := startResp["JobId"].(string)
require.True(t, ok)
@@ -210,7 +212,7 @@ func TestGetLabelDetection_GetRequestMetadata(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
metadata, _ := getResp["GetRequestMetadata"].(map[string]any)
require.NotNil(t, metadata)
assert.Equal(t, tc.wantSortBy, metadata["SortBy"])
@@ -233,7 +235,7 @@ func TestGetContentModeration_GetRequestMetadataDefaults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID, ok := startResp["JobId"].(string)
require.True(t, ok)
@@ -241,7 +243,7 @@ func TestGetContentModeration_GetRequestMetadataDefaults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
metadata, _ := getResp["GetRequestMetadata"].(map[string]any)
require.NotNil(t, metadata)
assert.Equal(t, "TIMESTAMP", metadata["SortBy"])
diff --git a/services/rekognition/handler_celebrities_test.go b/services/rekognition/handler_celebrities_test.go
index 0ecdeedcfa..1896aa676c 100644
--- a/services/rekognition/handler_celebrities_test.go
+++ b/services/rekognition/handler_celebrities_test.go
@@ -1,12 +1,14 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCelebrities_ImageAnalysis(t *testing.T) {
@@ -29,7 +31,7 @@ func TestCelebrities_ImageAnalysis(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotNil(t, resp["CelebrityFaces"])
},
},
@@ -41,7 +43,7 @@ func TestCelebrities_ImageAnalysis(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["Name"])
assert.NotNil(t, resp["KnownGender"])
},
@@ -76,7 +78,7 @@ func TestAsyncVideoJobs_CelebrityRecognition(t *testing.T) { //nolint:parallelte
require.Equal(t, http.StatusOK, rec.Code, "StartCelebrityRecognition")
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID, ok := startResp["JobId"].(string)
require.True(t, ok)
assert.NotEmpty(t, jobID)
@@ -86,7 +88,7 @@ func TestAsyncVideoJobs_CelebrityRecognition(t *testing.T) { //nolint:parallelte
require.Equal(t, http.StatusOK, rec.Code, "GetCelebrityRecognition")
var firstResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp, jsonx.V1()))
assert.Equal(t, "IN_PROGRESS", firstResp["JobStatus"])
// Second poll returns SUCCEEDED
@@ -94,7 +96,7 @@ func TestAsyncVideoJobs_CelebrityRecognition(t *testing.T) { //nolint:parallelte
require.Equal(t, http.StatusOK, rec.Code, "GetCelebrityRecognition")
var secondResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &secondResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &secondResp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", secondResp["JobStatus"])
assert.NotNil(t, secondResp["Celebrities"])
}
diff --git a/services/rekognition/handler_collections_test.go b/services/rekognition/handler_collections_test.go
index 133e8a8155..397d9d6cde 100644
--- a/services/rekognition/handler_collections_test.go
+++ b/services/rekognition/handler_collections_test.go
@@ -1,7 +1,8 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rekognition"
)
@@ -31,7 +33,7 @@ func TestRekognition_Collections(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp["CollectionArn"], "arn:aws:rekognition:")
assert.Equal(t, "7.0", resp["FaceModelVersion"])
},
@@ -47,7 +49,7 @@ func TestRekognition_Collections(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "ResourceAlreadyExistsException", resp["__type"])
},
},
@@ -62,7 +64,7 @@ func TestRekognition_Collections(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.InDelta(t, float64(200), resp["StatusCode"], 0)
},
},
@@ -74,7 +76,7 @@ func TestRekognition_Collections(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
},
},
@@ -89,7 +91,7 @@ func TestRekognition_Collections(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp["CollectionARN"], "arn:aws:rekognition:")
assert.InDelta(t, float64(0), resp["FaceCount"], 0)
},
@@ -102,7 +104,7 @@ func TestRekognition_Collections(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
ids, _ := resp["CollectionIds"].([]any)
assert.Empty(t, ids)
},
@@ -118,7 +120,7 @@ func TestRekognition_Collections(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
ids, _ := resp["CollectionIds"].([]any)
assert.Len(t, ids, 1)
assert.Equal(t, "list-coll", ids[0])
@@ -165,7 +167,7 @@ func TestListCollections_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
ids1, _ := page1["CollectionIds"].([]any)
require.Len(t, ids1, 2)
@@ -180,7 +182,7 @@ func TestListCollections_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
ids2, _ := page2["CollectionIds"].([]any)
require.Len(t, ids2, 1)
@@ -205,7 +207,7 @@ func TestDeleteCollection_CascadesToFaces(t *testing.T) {
rec := doRequest(t, h, "ListFaces", map[string]any{"CollectionId": "cascade-coll"})
require.Equal(t, http.StatusOK, rec.Code)
var lf map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lf))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lf, jsonx.V1()))
faces, _ := lf["Faces"].([]any)
require.Len(t, faces, 2)
@@ -218,7 +220,7 @@ func TestDeleteCollection_CascadesToFaces(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp["__type"])
}
@@ -234,7 +236,7 @@ func TestDescribeCollection_NotFound_Returns400(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
}
@@ -344,13 +346,13 @@ func TestListCollections_EmptyReturnsSlices(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
// Decode raw to check for non-null slice presence.
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
_, hasIDs := raw["CollectionIds"]
assert.True(t, hasIDs, "CollectionIds must be present")
var ids []any
- require.NoError(t, json.Unmarshal(raw["CollectionIds"], &ids))
+ require.NoError(t, json.Unmarshal(raw["CollectionIds"], &ids, jsonx.V1()))
assert.NotNil(t, ids, "CollectionIds must not be null")
}
diff --git a/services/rekognition/handler_custom_labels_test.go b/services/rekognition/handler_custom_labels_test.go
index dbbeb4bee9..8eb25cdb9d 100644
--- a/services/rekognition/handler_custom_labels_test.go
+++ b/services/rekognition/handler_custom_labels_test.go
@@ -1,12 +1,14 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDetectCustomLabels_ReturnsEmptyList(t *testing.T) {
@@ -20,6 +22,6 @@ func TestDetectCustomLabels_ReturnsEmptyList(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["CustomLabels"])
}
diff --git a/services/rekognition/handler_datasets_test.go b/services/rekognition/handler_datasets_test.go
index 12012eb510..3493972cd3 100644
--- a/services/rekognition/handler_datasets_test.go
+++ b/services/rekognition/handler_datasets_test.go
@@ -2,12 +2,14 @@ package rekognition_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// extractLabels extracts the label list from a ListDatasetLabels response
@@ -18,7 +20,7 @@ func extractLabels(t *testing.T, body []byte) []any {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
v, _ := resp["DatasetLabelDescriptions"].([]any)
@@ -36,7 +38,7 @@ func TestListDatasetLabels_SingleLabel(t *testing.T) { //nolint:paralleltest //
rec := doRequest(t, h, "CreateProject", map[string]any{"ProjectName": "lbl-proj"})
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projARN := projResp["ProjectArn"].(string)
rec = doRequest(t, h, "CreateDataset", map[string]any{
@@ -45,7 +47,7 @@ func TestListDatasetLabels_SingleLabel(t *testing.T) { //nolint:paralleltest //
})
require.Equal(t, http.StatusOK, rec.Code)
var dsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dsResp, jsonx.V1()))
dsARN := dsResp["DatasetArn"].(string)
// Add two entries: cat appears twice, dog once
@@ -83,7 +85,7 @@ func TestListDatasetLabels_MultiLabel(t *testing.T) { //nolint:paralleltest // s
rec := doRequest(t, h, "CreateProject", map[string]any{"ProjectName": "ml-proj"})
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
rec = doRequest(t, h, "CreateDataset", map[string]any{
"ProjectArn": projResp["ProjectArn"].(string),
@@ -91,7 +93,7 @@ func TestListDatasetLabels_MultiLabel(t *testing.T) { //nolint:paralleltest // s
})
require.Equal(t, http.StatusOK, rec.Code)
var dsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dsResp, jsonx.V1()))
dsARN := dsResp["DatasetArn"].(string)
// Multi-label entry: sunglasses + hat
@@ -121,7 +123,7 @@ func TestListDatasetLabels_OpaqueToken(t *testing.T) { //nolint:paralleltest //
rec := doRequest(t, h, "CreateProject", map[string]any{"ProjectName": "page-proj"})
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
rec = doRequest(t, h, "CreateDataset", map[string]any{
"ProjectArn": projResp["ProjectArn"].(string),
@@ -129,7 +131,7 @@ func TestListDatasetLabels_OpaqueToken(t *testing.T) { //nolint:paralleltest //
})
require.Equal(t, http.StatusOK, rec.Code)
var dsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dsResp, jsonx.V1()))
dsARN := dsResp["DatasetArn"].(string)
// Add 3 entries with distinct labels
@@ -137,7 +139,7 @@ func TestListDatasetLabels_OpaqueToken(t *testing.T) { //nolint:paralleltest //
entry, _ := json.Marshal(map[string]any{
"source-ref": "s3://b/img" + string(rune('0'+i)) + ".jpg",
"labels-metadata": map[string]any{"class-name": lbl},
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "UpdateDatasetEntries", map[string]any{
"DatasetArn": dsARN,
"Changes": map[string]any{"GroundTruth": entry},
@@ -156,7 +158,7 @@ func TestListDatasetLabels_OpaqueToken(t *testing.T) { //nolint:paralleltest //
require.Len(t, firstPageLabels, 2)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
nextToken, hasToken := page1["NextToken"].(string)
require.True(t, hasToken, "expected NextToken for more pages")
@@ -166,7 +168,7 @@ func TestListDatasetLabels_OpaqueToken(t *testing.T) { //nolint:paralleltest //
decoded, err := base64.RawURLEncoding.DecodeString(nextToken)
require.NoError(t, err)
var tok map[string]any
- require.NoError(t, json.Unmarshal(decoded, &tok))
+ require.NoError(t, json.Unmarshal(decoded, &tok, jsonx.V1()))
assert.InDelta(t, float64(2), tok["o"], 0)
// Second page
@@ -181,7 +183,7 @@ func TestListDatasetLabels_OpaqueToken(t *testing.T) { //nolint:paralleltest //
require.Len(t, secondPageLabels, 1, "expected 1 label on second page")
var page2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
_, hasMore := page2["NextToken"].(string)
assert.False(t, hasMore, "should not have next token on last page")
}
@@ -192,7 +194,7 @@ func TestListDatasetLabels_InvalidToken(t *testing.T) { //nolint:paralleltest //
rec := doRequest(t, h, "CreateProject", map[string]any{"ProjectName": "inv-proj"})
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
rec = doRequest(t, h, "CreateDataset", map[string]any{
"ProjectArn": projResp["ProjectArn"].(string),
@@ -200,7 +202,7 @@ func TestListDatasetLabels_InvalidToken(t *testing.T) { //nolint:paralleltest //
})
require.Equal(t, http.StatusOK, rec.Code)
var dsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dsResp, jsonx.V1()))
// Invalid token treated as offset 0 (returns from start, no error)
rec = doRequest(t, h, "ListDatasetLabels", map[string]any{
@@ -220,7 +222,7 @@ func TestDistributeDatasetEntries_SetsInProgress(t *testing.T) { //nolint:parall
rec := doRequest(t, h, "CreateProject", map[string]any{"ProjectName": "dist-proj"})
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projARN := projResp["ProjectArn"].(string)
rec = doRequest(t, h, "CreateDataset", map[string]any{
@@ -229,7 +231,7 @@ func TestDistributeDatasetEntries_SetsInProgress(t *testing.T) { //nolint:parall
})
require.Equal(t, http.StatusOK, rec.Code)
var dsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dsResp, jsonx.V1()))
dsARN := dsResp["DatasetArn"].(string)
rec = doRequest(t, h, "DistributeDatasetEntries", map[string]any{
@@ -244,7 +246,7 @@ func TestDistributeDatasetEntries_SetsInProgress(t *testing.T) { //nolint:parall
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
desc := descResp["DatasetDescription"].(map[string]any)
assert.Equal(t, "UPDATE_IN_PROGRESS", desc["Status"])
}
@@ -260,7 +262,7 @@ func TestDeleteDataset_WhileUpdating_Rejected(t *testing.T) { //nolint:parallelt
rec := doRequest(t, h, "CreateProject", map[string]any{"ProjectName": "del-ds-proj"})
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projARN := projResp["ProjectArn"].(string)
rec = doRequest(t, h, "CreateDataset", map[string]any{
@@ -269,7 +271,7 @@ func TestDeleteDataset_WhileUpdating_Rejected(t *testing.T) { //nolint:parallelt
})
require.Equal(t, http.StatusOK, rec.Code)
var dsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dsResp, jsonx.V1()))
dsARN := dsResp["DatasetArn"].(string)
rec = doRequest(t, h, "DistributeDatasetEntries", map[string]any{
@@ -283,7 +285,7 @@ func TestDeleteDataset_WhileUpdating_Rejected(t *testing.T) { //nolint:parallelt
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceInUseException", errResp["__type"])
}
@@ -308,7 +310,7 @@ func TestDatasets(t *testing.T) { //nolint:paralleltest // existing issue.
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projectARN := projResp["ProjectArn"].(string)
tests := []struct {
@@ -329,7 +331,7 @@ func TestDatasets(t *testing.T) { //nolint:paralleltest // existing issue.
check: func(t *testing.T, body []byte) string {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
arn := resp["DatasetArn"].(string)
assert.Contains(t, arn, "arn:aws:rekognition:")
@@ -362,7 +364,7 @@ func TestDatasets(t *testing.T) { //nolint:paralleltest // existing issue.
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
desc := resp["DatasetDescription"].(map[string]any)
assert.Equal(t, "CREATE_COMPLETE", desc["Status"])
assert.Equal(t, "TRAIN", desc["DatasetType"])
@@ -388,7 +390,7 @@ func TestDatasets(t *testing.T) { //nolint:paralleltest // existing issue.
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["DatasetEntries"])
})
@@ -403,7 +405,7 @@ func TestDatasets(t *testing.T) { //nolint:paralleltest // existing issue.
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["DatasetLabelDescriptions"])
})
@@ -429,7 +431,7 @@ func TestDatasets(t *testing.T) { //nolint:paralleltest // existing issue.
require.Equal(t, http.StatusOK, rec.Code)
var dsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dsResp, jsonx.V1()))
freshARN := dsResp["DatasetArn"].(string)
rec = doRequest(t, h, "DeleteDataset", map[string]any{"DatasetArn": freshARN})
@@ -456,7 +458,7 @@ func TestCreateDataset_DuplicateProjectAndType_Rejected(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projARN := projResp["ProjectArn"].(string)
rec = doRequest(t, h, "CreateDataset", map[string]any{
@@ -473,7 +475,7 @@ func TestCreateDataset_DuplicateProjectAndType_Rejected(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceAlreadyExistsException", errResp["__type"])
// Same project, different DatasetType -> allowed.
diff --git a/services/rekognition/handler_face_liveness_test.go b/services/rekognition/handler_face_liveness_test.go
index d7869673e5..cb98ad81b9 100644
--- a/services/rekognition/handler_face_liveness_test.go
+++ b/services/rekognition/handler_face_liveness_test.go
@@ -1,12 +1,14 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// =============================================================================
@@ -25,14 +27,14 @@ func TestFaceLiveness_ConfidenceRange(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
sessionID := createResp["SessionId"].(string)
rec = doRequest(t, h, "GetFaceLivenessSessionResults", map[string]any{"SessionId": sessionID})
require.Equal(t, http.StatusOK, rec.Code)
var resultResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resultResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resultResp, jsonx.V1()))
confidence := resultResp["Confidence"].(float64)
assert.GreaterOrEqual(t, confidence, float64(75), "confidence must be >= 75")
assert.Less(t, confidence, float64(100), "confidence must be < 100")
@@ -47,7 +49,7 @@ func TestFaceLiveness_TwoSessionsDifferentConfidence(t *testing.T) { //nolint:pa
rec := doRequest(t, h, "GetFaceLivenessSessionResults", map[string]any{"SessionId": sessionID})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["Confidence"].(float64)
}
@@ -58,7 +60,7 @@ func TestFaceLiveness_TwoSessionsDifferentConfidence(t *testing.T) { //nolint:pa
rec := doRequest(t, h, "CreateFaceLivenessSession", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
c := getConfidence(resp["SessionId"].(string))
confidences[c] = true
}
@@ -84,7 +86,7 @@ func TestFaceLiveness(t *testing.T) { //nolint:paralleltest // existing issue.
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["SessionId"])
},
},
@@ -103,7 +105,7 @@ func TestFaceLiveness(t *testing.T) { //nolint:paralleltest // existing issue.
if tc.action == "CreateFaceLivenessSession" && rec.Code == http.StatusOK {
var resp map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
sessionID = resp["SessionId"].(string)
}
})
@@ -114,7 +116,7 @@ func TestFaceLiveness(t *testing.T) { //nolint:paralleltest // existing issue.
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", resp["Status"])
assert.Equal(t, sessionID, resp["SessionId"])
})
diff --git a/services/rekognition/handler_faces_test.go b/services/rekognition/handler_faces_test.go
index 473a3cba4c..a91482ca8a 100644
--- a/services/rekognition/handler_faces_test.go
+++ b/services/rekognition/handler_faces_test.go
@@ -1,13 +1,15 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rekognition"
)
@@ -33,7 +35,7 @@ func TestRekognition_Faces(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
records, _ := resp["FaceRecords"].([]any)
assert.Len(t, records, 1)
},
@@ -46,7 +48,7 @@ func TestRekognition_Faces(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
},
},
@@ -62,7 +64,7 @@ func TestRekognition_Faces(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
faces, _ := resp["Faces"].([]any)
assert.Len(t, faces, 1)
},
@@ -82,7 +84,7 @@ func TestRekognition_Faces(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotNil(t, resp["DeletedFaces"])
},
},
@@ -100,7 +102,7 @@ func TestRekognition_Faces(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", resp["__type"])
},
},
@@ -116,7 +118,7 @@ func TestRekognition_Faces(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
matches, _ := resp["FaceMatches"].([]any)
assert.Len(t, matches, 1)
},
@@ -167,7 +169,7 @@ func TestListFaces_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
faces1, _ := page1["Faces"].([]any)
require.Len(t, faces1, 2)
@@ -183,7 +185,7 @@ func TestListFaces_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
faces2, _ := page2["Faces"].([]any)
require.Len(t, faces2, 1)
@@ -216,7 +218,7 @@ func TestSearchFaces_RealFaceId_ReturnsMatches(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var idx1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &idx1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &idx1, jsonx.V1()))
records, _ := idx1["FaceRecords"].([]any)
require.Len(t, records, 1)
rec1, _ := records[0].(map[string]any)
@@ -241,7 +243,7 @@ func TestSearchFaces_RealFaceId_ReturnsMatches(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var sf map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &sf))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &sf, jsonx.V1()))
matches, _ := sf["FaceMatches"].([]any)
assert.Len(t, matches, 2)
assert.Equal(t, faceID, sf["SearchedFaceId"])
@@ -264,7 +266,7 @@ func TestSearchFaces_UnknownFaceId_Returns404(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
}
@@ -286,7 +288,7 @@ func TestDeleteFaces_BySpecificIDs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var idx map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &idx))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &idx, jsonx.V1()))
records, _ := idx["FaceRecords"].([]any)
rec0, _ := records[0].(map[string]any)
face, _ := rec0["Face"].(map[string]any)
@@ -301,7 +303,7 @@ func TestDeleteFaces_BySpecificIDs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var delResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &delResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &delResp, jsonx.V1()))
deleted, _ := delResp["DeletedFaces"].([]any)
assert.Len(t, deleted, 2)
@@ -310,7 +312,7 @@ func TestDeleteFaces_BySpecificIDs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var lf map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lf))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &lf, jsonx.V1()))
remaining, _ := lf["Faces"].([]any)
require.Len(t, remaining, 1)
remainFace, _ := remaining[0].(map[string]any)
@@ -329,7 +331,7 @@ func TestListFaces_UnknownCollection_Returns400(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
}
@@ -346,14 +348,14 @@ func TestListFaces_EmptyCollection_ReturnsNonNullSlice(t *testing.T) {
rec := doRequest(t, h, "ListFaces", map[string]any{"CollectionId": "empty-faces-coll"})
require.Equal(t, http.StatusOK, rec.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
_, hasFaces := raw["Faces"]
assert.True(t, hasFaces, "Faces field must be present")
var faces []any
- require.NoError(t, json.Unmarshal(raw["Faces"], &faces))
+ require.NoError(t, json.Unmarshal(raw["Faces"], &faces, jsonx.V1()))
assert.NotNil(t, faces, "Faces must not be null")
}
@@ -374,7 +376,7 @@ func TestIndexFaces_ExternalImageId_RoundTrips(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var idx map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &idx))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &idx, jsonx.V1()))
records, _ := idx["FaceRecords"].([]any)
require.Len(t, records, 1)
rec0, _ := records[0].(map[string]any)
@@ -386,7 +388,7 @@ func TestIndexFaces_ExternalImageId_RoundTrips(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var lf map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &lf))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &lf, jsonx.V1()))
faces, _ := lf["Faces"].([]any)
require.Len(t, faces, 1)
f0, _ := faces[0].(map[string]any)
@@ -404,7 +406,7 @@ func decodeFaceIDs(t *testing.T, body []byte) []string {
} `json:"Face"`
} `json:"FaceRecords"`
}
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
ids := make([]string, 0, len(resp.FaceRecords))
for _, r := range resp.FaceRecords {
@@ -437,7 +439,7 @@ func TestIndexFaces_ConfidenceDeterministicAndPlausible(t *testing.T) {
} `json:"Face"`
} `json:"FaceRecords"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.FaceRecords, 1)
conf := resp.FaceRecords[0].Face.Confidence
@@ -487,7 +489,7 @@ func TestSearchFaces_SimilarityDeterministic(t *testing.T) {
Similarity float64 `json:"Similarity"`
} `json:"FaceMatches"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.FaceMatches, 1)
return resp.FaceMatches[0].Similarity
@@ -522,7 +524,7 @@ func TestSearchFaces_SameExternalImageId_ScoresExactMatch(t *testing.T) {
Similarity float64 `json:"Similarity"`
} `json:"FaceMatches"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.FaceMatches, 1)
assert.InDelta(t, 100.0, resp.FaceMatches[0].Similarity, 1e-9,
"faces sharing an ExternalImageId must score an exact match")
@@ -549,7 +551,7 @@ func TestSearchFacesByImage_SimilarityDependsOnImage(t *testing.T) {
Similarity float64 `json:"Similarity"`
} `json:"FaceMatches"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.FaceMatches, 1)
return resp.FaceMatches[0].Similarity
@@ -587,7 +589,7 @@ func TestFaceRoundTrip_IndexSearchListDelete(t *testing.T) {
FaceID string `json:"FaceId"`
} `json:"Faces"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &list))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &list, jsonx.V1()))
require.Len(t, list.Faces, 2)
// SearchFaces from id1 finds id2. FaceMatchThreshold explicitly 0: this
@@ -608,7 +610,7 @@ func TestFaceRoundTrip_IndexSearchListDelete(t *testing.T) {
} `json:"Face"`
} `json:"FaceMatches"`
}
- require.NoError(t, json.Unmarshal(searchRec.Body.Bytes(), &search))
+ require.NoError(t, json.Unmarshal(searchRec.Body.Bytes(), &search, jsonx.V1()))
require.Len(t, search.FaceMatches, 1)
assert.Equal(t, id2, search.FaceMatches[0].Face.FaceID)
@@ -621,9 +623,9 @@ func TestFaceRoundTrip_IndexSearchListDelete(t *testing.T) {
require.Equal(t, http.StatusOK, searchRec2.Code)
var search2 struct {
- FaceMatches []json.RawMessage `json:"FaceMatches"`
+ FaceMatches []jsontext.Value `json:"FaceMatches"`
}
- require.NoError(t, json.Unmarshal(searchRec2.Body.Bytes(), &search2))
+ require.NoError(t, json.Unmarshal(searchRec2.Body.Bytes(), &search2, jsonx.V1()))
assert.Empty(t, search2.FaceMatches)
// SearchFaces for the deleted id returns ResourceNotFoundException.
@@ -647,7 +649,7 @@ func TestImageAnalysis_CompareFaces(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["FaceMatches"])
assert.NotNil(t, resp["SourceImageFace"])
}
@@ -660,7 +662,7 @@ func TestImageAnalysis_DetectFaces(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["FaceDetails"])
}
@@ -713,7 +715,7 @@ func TestImageAnalysis_EnumValidation(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", resp["__type"])
})
}
@@ -779,7 +781,7 @@ func TestAsyncVideoJobs_Faces(t *testing.T) { //nolint:paralleltest // stateful
checkGet: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", resp["JobStatus"])
assert.NotNil(t, resp["Faces"])
},
@@ -791,7 +793,7 @@ func TestAsyncVideoJobs_Faces(t *testing.T) { //nolint:paralleltest // stateful
checkGet: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", resp["JobStatus"])
assert.NotNil(t, resp["Persons"])
},
@@ -805,7 +807,7 @@ func TestAsyncVideoJobs_Faces(t *testing.T) { //nolint:paralleltest // stateful
require.Equal(t, http.StatusOK, rec.Code, flow.startAction)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID, ok := startResp["JobId"].(string)
require.True(t, ok)
assert.NotEmpty(t, jobID)
@@ -815,7 +817,7 @@ func TestAsyncVideoJobs_Faces(t *testing.T) { //nolint:paralleltest // stateful
require.Equal(t, http.StatusOK, rec.Code, flow.getAction)
var firstResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp, jsonx.V1()))
assert.Equal(t, "IN_PROGRESS", firstResp["JobStatus"])
// Second poll returns SUCCEEDED
diff --git a/services/rekognition/handler_labels_test.go b/services/rekognition/handler_labels_test.go
index 1b5cb1b78f..2a80a9dc7f 100644
--- a/services/rekognition/handler_labels_test.go
+++ b/services/rekognition/handler_labels_test.go
@@ -1,13 +1,14 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rekognition"
)
@@ -32,7 +33,7 @@ func TestDetectLabels_ReturnsEmptyList(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Labels"])
}
@@ -44,7 +45,7 @@ func TestAsyncVideoJobs_LabelDetection(t *testing.T) { //nolint:paralleltest //
require.Equal(t, http.StatusOK, rec.Code, "StartLabelDetection")
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID, ok := startResp["JobId"].(string)
require.True(t, ok)
assert.NotEmpty(t, jobID)
@@ -54,7 +55,7 @@ func TestAsyncVideoJobs_LabelDetection(t *testing.T) { //nolint:paralleltest //
require.Equal(t, http.StatusOK, rec.Code, "GetLabelDetection")
var firstResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp, jsonx.V1()))
assert.Equal(t, "IN_PROGRESS", firstResp["JobStatus"])
// Second poll returns SUCCEEDED
@@ -62,7 +63,7 @@ func TestAsyncVideoJobs_LabelDetection(t *testing.T) { //nolint:paralleltest //
require.Equal(t, http.StatusOK, rec.Code, "GetLabelDetection")
var secondResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &secondResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &secondResp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", secondResp["JobStatus"])
assert.NotNil(t, secondResp["Labels"])
}
diff --git a/services/rekognition/handler_media_analysis_test.go b/services/rekognition/handler_media_analysis_test.go
index 3d5ba8ac89..fdc33a3478 100644
--- a/services/rekognition/handler_media_analysis_test.go
+++ b/services/rekognition/handler_media_analysis_test.go
@@ -1,12 +1,14 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// =============================================================================
@@ -36,7 +38,7 @@ func TestMediaAnalysisJob_Lifecycle(t *testing.T) { //nolint:paralleltest // sta
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID, ok := startResp["JobId"].(string)
require.True(t, ok)
assert.NotEmpty(t, jobID)
@@ -46,7 +48,7 @@ func TestMediaAnalysisJob_Lifecycle(t *testing.T) { //nolint:paralleltest // sta
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, jobID, getResp["JobId"])
assert.NotEmpty(t, getResp["Status"])
@@ -73,7 +75,7 @@ func TestMediaAnalysisJob_Lifecycle(t *testing.T) { //nolint:paralleltest // sta
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
jobs, ok := listResp["MediaAnalysisJobs"].([]any)
require.True(t, ok)
require.GreaterOrEqual(t, len(jobs), 1)
@@ -168,7 +170,7 @@ func TestAsyncVideoJobs_PersonTrackingAndSegmentDetection(t *testing.T) { //noli
checkGet: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", resp["JobStatus"])
assert.NotNil(t, resp["Persons"])
},
@@ -180,7 +182,7 @@ func TestAsyncVideoJobs_PersonTrackingAndSegmentDetection(t *testing.T) { //noli
checkGet: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", resp["JobStatus"])
assert.NotNil(t, resp["Segments"])
},
@@ -194,7 +196,7 @@ func TestAsyncVideoJobs_PersonTrackingAndSegmentDetection(t *testing.T) { //noli
require.Equal(t, http.StatusOK, rec.Code, flow.startAction)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID, ok := startResp["JobId"].(string)
require.True(t, ok)
assert.NotEmpty(t, jobID)
@@ -204,7 +206,7 @@ func TestAsyncVideoJobs_PersonTrackingAndSegmentDetection(t *testing.T) { //noli
require.Equal(t, http.StatusOK, rec.Code, flow.getAction)
var firstResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp, jsonx.V1()))
assert.Equal(t, "IN_PROGRESS", firstResp["JobStatus"])
// Second poll returns SUCCEEDED
@@ -265,7 +267,7 @@ func TestMediaAnalysisJobs(t *testing.T) { //nolint:paralleltest // existing iss
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"].(string)
assert.NotEmpty(t, jobID)
@@ -275,7 +277,7 @@ func TestMediaAnalysisJobs(t *testing.T) { //nolint:paralleltest // existing iss
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, jobID, resp["JobId"])
assert.Equal(t, "my-analysis", resp["JobName"])
assert.Equal(t, "SUCCEEDED", resp["Status"])
@@ -287,7 +289,7 @@ func TestMediaAnalysisJobs(t *testing.T) { //nolint:paralleltest // existing iss
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
jobs, ok := resp["MediaAnalysisJobs"].([]any)
require.True(t, ok)
assert.Len(t, jobs, 1)
diff --git a/services/rekognition/handler_moderation_test.go b/services/rekognition/handler_moderation_test.go
index 18fbb736ad..ceac653a91 100644
--- a/services/rekognition/handler_moderation_test.go
+++ b/services/rekognition/handler_moderation_test.go
@@ -1,12 +1,14 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestModeration_ImageAnalysis(t *testing.T) {
@@ -29,7 +31,7 @@ func TestModeration_ImageAnalysis(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotNil(t, resp["ModerationLabels"])
},
},
@@ -41,7 +43,7 @@ func TestModeration_ImageAnalysis(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotNil(t, resp["Persons"])
},
},
@@ -125,7 +127,7 @@ func TestDetectProtectiveEquipment_SummarizationAttributesValidation(t *testing.
if tc.wantType != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantType, resp["__type"])
}
})
@@ -140,7 +142,7 @@ func TestAsyncVideoJobs_ContentModeration(t *testing.T) { //nolint:paralleltest
require.Equal(t, http.StatusOK, rec.Code, "StartContentModeration")
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID, ok := startResp["JobId"].(string)
require.True(t, ok)
assert.NotEmpty(t, jobID)
@@ -150,7 +152,7 @@ func TestAsyncVideoJobs_ContentModeration(t *testing.T) { //nolint:paralleltest
require.Equal(t, http.StatusOK, rec.Code, "GetContentModeration")
var firstResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp, jsonx.V1()))
assert.Equal(t, "IN_PROGRESS", firstResp["JobStatus"])
// Second poll returns SUCCEEDED
@@ -158,7 +160,7 @@ func TestAsyncVideoJobs_ContentModeration(t *testing.T) { //nolint:paralleltest
require.Equal(t, http.StatusOK, rec.Code, "GetContentModeration")
var secondResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &secondResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &secondResp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", secondResp["JobStatus"])
assert.NotNil(t, secondResp["ModerationLabels"])
}
diff --git a/services/rekognition/handler_project_versions.go b/services/rekognition/handler_project_versions.go
index fd63079794..74a72e8e3f 100644
--- a/services/rekognition/handler_project_versions.go
+++ b/services/rekognition/handler_project_versions.go
@@ -2,7 +2,7 @@ package rekognition
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -53,8 +53,8 @@ type createProjectVersionReq struct {
VersionName string `json:"VersionName"`
KmsKeyID string `json:"KmsKeyId"`
VersionDescription string `json:"VersionDescription"`
- TrainingData json.RawMessage `json:"TrainingData"`
- TestingData json.RawMessage `json:"TestingData"`
+ TrainingData jsontext.Value `json:"TrainingData"`
+ TestingData jsontext.Value `json:"TestingData"`
}
type createProjectVersionResp struct {
diff --git a/services/rekognition/handler_project_versions_test.go b/services/rekognition/handler_project_versions_test.go
index 0d43d9474b..075dc12e5c 100644
--- a/services/rekognition/handler_project_versions_test.go
+++ b/services/rekognition/handler_project_versions_test.go
@@ -1,7 +1,7 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rekognition"
)
@@ -31,7 +32,7 @@ func TestProjectVersions(t *testing.T) { //nolint:paralleltest // existing issue
rec := doRequest(t, h, "CreateProject", map[string]any{"ProjectName": "ver-proj"})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["ProjectArn"].(string)
},
@@ -39,7 +40,7 @@ func TestProjectVersions(t *testing.T) { //nolint:paralleltest // existing issue
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp["ProjectVersionArn"], "arn:aws:rekognition:")
},
},
@@ -55,7 +56,7 @@ func TestProjectVersions(t *testing.T) { //nolint:paralleltest // existing issue
rec := doRequest(t, h, "CreateProject", map[string]any{"ProjectName": "no-output-config-proj"})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["ProjectArn"].(string)
},
@@ -97,7 +98,7 @@ func TestProjectVersion_Lifecycle(t *testing.T) { //nolint:paralleltest // exist
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projectARN := projResp["ProjectArn"].(string)
// Create version
@@ -109,7 +110,7 @@ func TestProjectVersion_Lifecycle(t *testing.T) { //nolint:paralleltest // exist
require.Equal(t, http.StatusOK, rec.Code)
var verResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verResp, jsonx.V1()))
versionARN := verResp["ProjectVersionArn"].(string)
// DescribeProjectVersions
@@ -117,7 +118,7 @@ func TestProjectVersion_Lifecycle(t *testing.T) { //nolint:paralleltest // exist
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
versions := descResp["ProjectVersionDescriptions"].([]any)
assert.Len(t, versions, 1)
@@ -129,7 +130,7 @@ func TestProjectVersion_Lifecycle(t *testing.T) { //nolint:paralleltest // exist
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
assert.Equal(t, "RUNNING", startResp["Status"])
// StopProjectVersion
@@ -139,7 +140,7 @@ func TestProjectVersion_Lifecycle(t *testing.T) { //nolint:paralleltest // exist
require.Equal(t, http.StatusOK, rec.Code)
var stopResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stopResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stopResp, jsonx.V1()))
assert.Equal(t, "STOPPED", stopResp["Status"])
// DeleteProjectVersion
@@ -167,7 +168,7 @@ func TestProjectVersion_StateTransitions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projectARN := projResp["ProjectArn"].(string)
rec = doRequest(t, h, "CreateProjectVersion", map[string]any{
@@ -178,13 +179,13 @@ func TestProjectVersion_StateTransitions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var verResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verResp, jsonx.V1()))
versionARN := verResp["ProjectVersionArn"].(string)
status := func() string {
out := doRequest(t, h, "DescribeProjectVersions", map[string]any{"ProjectArn": projectARN})
var m map[string]any
- require.NoError(t, json.Unmarshal(out.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(out.Body.Bytes(), &m, jsonx.V1()))
return m["ProjectVersionDescriptions"].([]any)[0].(map[string]any)["Status"].(string)
}
@@ -192,7 +193,7 @@ func TestProjectVersion_StateTransitions(t *testing.T) {
out := doRequest(t, h, action, body)
require.Equal(t, http.StatusBadRequest, out.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(out.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(out.Body.Bytes(), &m, jsonx.V1()))
return m["__type"].(string)
}
@@ -239,7 +240,7 @@ func TestCreateProjectVersion_FullFieldsRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projectARN := projResp["ProjectArn"].(string)
rec = doRequest(t, h, "CreateProjectVersion", map[string]any{
@@ -256,14 +257,14 @@ func TestCreateProjectVersion_FullFieldsRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var verResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verResp, jsonx.V1()))
versionARN := verResp["ProjectVersionArn"].(string)
rec = doRequest(t, h, "DescribeProjectVersions", map[string]any{"ProjectArn": projectARN})
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
versions := descResp["ProjectVersionDescriptions"].([]any)
require.Len(t, versions, 1)
@@ -282,7 +283,7 @@ func TestCreateProjectVersion_FullFieldsRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags, _ := tagsResp["Tags"].(map[string]any)
assert.Equal(t, "vision", tags["team"])
}
@@ -295,7 +296,7 @@ func TestCopyProjectVersion(t *testing.T) { //nolint:paralleltest // existing is
require.Equal(t, http.StatusOK, rec.Code)
var srcProjResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &srcProjResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &srcProjResp, jsonx.V1()))
srcProjectARN := srcProjResp["ProjectArn"].(string)
rec = doRequest(t, h, "CreateProjectVersion", map[string]any{
@@ -306,7 +307,7 @@ func TestCopyProjectVersion(t *testing.T) { //nolint:paralleltest // existing is
require.Equal(t, http.StatusOK, rec.Code)
var srcVerResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &srcVerResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &srcVerResp, jsonx.V1()))
sourceVersionARN := srcVerResp["ProjectVersionArn"].(string)
// Create destination project
@@ -314,7 +315,7 @@ func TestCopyProjectVersion(t *testing.T) { //nolint:paralleltest // existing is
require.Equal(t, http.StatusOK, rec.Code)
var dstProjResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dstProjResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dstProjResp, jsonx.V1()))
dstProjectARN := dstProjResp["ProjectArn"].(string)
// Copy version fails when SourceProjectArn doesn't match the project
@@ -339,7 +340,7 @@ func TestCopyProjectVersion(t *testing.T) { //nolint:paralleltest // existing is
require.Equal(t, http.StatusOK, rec.Code)
var copyResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ©Resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ©Resp, jsonx.V1()))
assert.Contains(t, copyResp["ProjectVersionArn"], "dst-proj")
// SourceProjectVersionArn and OutputConfig are echoed back on the
@@ -348,7 +349,7 @@ func TestCopyProjectVersion(t *testing.T) { //nolint:paralleltest // existing is
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
versions := descResp["ProjectVersionDescriptions"].([]any)
require.Len(t, versions, 1)
copied := versions[0].(map[string]any)
@@ -421,7 +422,7 @@ func TestCreateProjectVersion_FeatureConfigRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projectARN := projResp["ProjectArn"].(string)
rec = doRequest(t, h, "CreateProjectVersion", map[string]any{
@@ -438,7 +439,7 @@ func TestCreateProjectVersion_FeatureConfigRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
versions := descResp["ProjectVersionDescriptions"].([]any)
require.Len(t, versions, 1)
@@ -507,7 +508,7 @@ func TestCreateProjectVersion_TrainingTestingDataMustBothBeSpecified(t *testing.
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projectARN := projResp["ProjectArn"].(string)
body := map[string]any{
@@ -536,7 +537,7 @@ func TestStartProjectVersion_MaxInferenceUnitsRoundTrip(t *testing.T) { //nolint
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projectARN := projResp["ProjectArn"].(string)
rec = doRequest(t, h, "CreateProjectVersion", map[string]any{
@@ -547,7 +548,7 @@ func TestStartProjectVersion_MaxInferenceUnitsRoundTrip(t *testing.T) { //nolint
require.Equal(t, http.StatusOK, rec.Code)
var verResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verResp, jsonx.V1()))
versionARN := verResp["ProjectVersionArn"].(string)
rec = doRequest(t, h, "StartProjectVersion", map[string]any{
@@ -561,7 +562,7 @@ func TestStartProjectVersion_MaxInferenceUnitsRoundTrip(t *testing.T) { //nolint
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
versions := descResp["ProjectVersionDescriptions"].([]any)
require.Len(t, versions, 1)
desc := versions[0].(map[string]any)
diff --git a/services/rekognition/handler_projects_test.go b/services/rekognition/handler_projects_test.go
index 47d4906388..fbf03de8ff 100644
--- a/services/rekognition/handler_projects_test.go
+++ b/services/rekognition/handler_projects_test.go
@@ -1,13 +1,14 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rekognition"
)
@@ -28,7 +29,7 @@ func TestProjects(t *testing.T) { //nolint:paralleltest // existing issue.
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp["ProjectArn"], "arn:aws:rekognition:")
assert.Contains(t, resp["ProjectArn"], "project/my-project")
},
@@ -51,7 +52,7 @@ func TestProjects(t *testing.T) { //nolint:paralleltest // existing issue.
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["message"])
},
},
@@ -72,7 +73,7 @@ func TestProjects(t *testing.T) { //nolint:paralleltest // existing issue.
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
descriptions, ok := resp["ProjectDescriptions"].([]any)
require.True(t, ok)
assert.GreaterOrEqual(t, len(descriptions), 1)
@@ -86,7 +87,7 @@ func TestProjects(t *testing.T) { //nolint:paralleltest // existing issue.
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
descriptions, ok := resp["ProjectDescriptions"].([]any)
require.True(t, ok)
assert.Empty(t, descriptions)
@@ -119,7 +120,7 @@ func TestProject_DeleteSuccess(t *testing.T) { //nolint:paralleltest // existing
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
projectARN := createResp["ProjectArn"].(string)
// Delete it
@@ -127,7 +128,7 @@ func TestProject_DeleteSuccess(t *testing.T) { //nolint:paralleltest // existing
assert.Equal(t, http.StatusOK, rec.Code)
var deleteResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &deleteResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &deleteResp, jsonx.V1()))
assert.Equal(t, "DELETING", deleteResp["Status"])
// DescribeProjects should no longer return it
@@ -135,7 +136,7 @@ func TestProject_DeleteSuccess(t *testing.T) { //nolint:paralleltest // existing
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
descriptions := descResp["ProjectDescriptions"].([]any)
assert.Empty(t, descriptions)
}
@@ -153,7 +154,7 @@ func TestProject_DeleteWithVersions_Rejected(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projectARN := projResp["ProjectArn"].(string)
rec = doRequest(t, h, "CreateProjectVersion", map[string]any{
@@ -167,7 +168,7 @@ func TestProject_DeleteWithVersions_Rejected(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceInUseException", errResp["__type"])
}
@@ -179,7 +180,7 @@ func TestProjectPolicies(t *testing.T) { //nolint:paralleltest // existing issue
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projectARN := projResp["ProjectArn"].(string)
tests := []struct {
@@ -201,7 +202,7 @@ func TestProjectPolicies(t *testing.T) { //nolint:paralleltest // existing issue
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.NotEmpty(t, resp["PolicyRevisionId"])
},
},
@@ -213,7 +214,7 @@ func TestProjectPolicies(t *testing.T) { //nolint:paralleltest // existing issue
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
policies, ok := resp["ProjectPolicies"].([]any)
require.True(t, ok)
assert.Len(t, policies, 1)
@@ -254,7 +255,7 @@ func TestDeleteProject_CascadesProjectPolicies(t *testing.T) { //nolint:parallel
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projectARN := projResp["ProjectArn"].(string)
rec = doRequest(t, h, "PutProjectPolicy", map[string]any{
@@ -271,7 +272,7 @@ func TestDeleteProject_CascadesProjectPolicies(t *testing.T) { //nolint:parallel
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
policies, ok := listResp["ProjectPolicies"].([]any)
require.True(t, ok)
assert.Empty(t, policies, "ProjectPolicies must be cascade-deleted with the project")
diff --git a/services/rekognition/handler_stream_processors_test.go b/services/rekognition/handler_stream_processors_test.go
index 4d11a1fd83..132b434e4c 100644
--- a/services/rekognition/handler_stream_processors_test.go
+++ b/services/rekognition/handler_stream_processors_test.go
@@ -1,7 +1,8 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rekognition"
)
@@ -31,7 +33,7 @@ func TestRekognition_StreamProcessors(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Contains(t, resp["StreamProcessorArn"], "arn:aws:rekognition:")
},
},
@@ -46,7 +48,7 @@ func TestRekognition_StreamProcessors(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
// AWS reports a duplicate stream processor name as
// ResourceInUseException, not ResourceAlreadyExistsException
// (verified against aws-sdk-go-v2/service/rekognition's
@@ -65,7 +67,7 @@ func TestRekognition_StreamProcessors(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "STOPPED", resp["Status"])
assert.Contains(t, resp["StreamProcessorArn"], "arn:aws:rekognition:")
},
@@ -81,7 +83,7 @@ func TestRekognition_StreamProcessors(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
procs, _ := resp["StreamProcessors"].([]any)
assert.Len(t, procs, 1)
},
@@ -122,7 +124,7 @@ func TestRekognition_StreamProcessors(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
},
},
@@ -176,7 +178,7 @@ func TestListStreamProcessors_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
procs1, _ := page1["StreamProcessors"].([]any)
require.Len(t, procs1, 2)
@@ -189,7 +191,7 @@ func TestListStreamProcessors_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var page2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
procs2, _ := page2["StreamProcessors"].([]any)
require.Len(t, procs2, 1)
@@ -207,14 +209,14 @@ func TestListStreamProcessors_EmptyReturnsNonNullSlice(t *testing.T) {
rec := doRequest(t, h, "ListStreamProcessors", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
_, hasProcs := raw["StreamProcessors"]
assert.True(t, hasProcs, "StreamProcessors field must be present")
var procs []any
- require.NoError(t, json.Unmarshal(raw["StreamProcessors"], &procs))
+ require.NoError(t, json.Unmarshal(raw["StreamProcessors"], &procs, jsonx.V1()))
assert.NotNil(t, procs, "StreamProcessors must not be null")
}
@@ -237,7 +239,7 @@ func TestStopStreamProcessor_AlreadyStopped_IsIdempotent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var desc map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc, jsonx.V1()))
assert.Equal(t, "STOPPED", desc["Status"])
}
@@ -253,7 +255,7 @@ func TestStartStreamProcessor_Unknown_Returns400(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
}
@@ -269,7 +271,7 @@ func TestUpdateStreamProcessor_Unknown_Returns400(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
}
@@ -287,7 +289,7 @@ func TestStreamProcessor_StatusTransitions(t *testing.T) {
rec := doRequest(t, h, "DescribeStreamProcessor", map[string]any{"Name": "status-proc"})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["Status"].(string)
}
@@ -423,7 +425,7 @@ func TestCreateStreamProcessor_FullFieldsRoundTripThroughDescribe(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "arn:aws:kms:us-east-1:000000000000:key/abc", resp["KmsKeyId"])
@@ -488,7 +490,7 @@ func TestUpdateStreamProcessor_AppliesFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
@@ -566,7 +568,7 @@ func TestUpdateStreamProcessor_RegionsOfInterestForUpdate_Replaces(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rois, _ := resp["RegionsOfInterest"].([]any)
assert.Len(t, rois, 2)
diff --git a/services/rekognition/handler_tags_test.go b/services/rekognition/handler_tags_test.go
index 48632affae..2988826b99 100644
--- a/services/rekognition/handler_tags_test.go
+++ b/services/rekognition/handler_tags_test.go
@@ -1,7 +1,7 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rekognition"
)
@@ -29,7 +30,7 @@ func TestRekognition_Tags(t *testing.T) {
setup: func(h *rekognition.Handler) string {
rec := doRequest(t, h, "CreateCollection", map[string]any{"CollectionId": "tag-coll"})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["CollectionArn"].(string)
},
@@ -49,7 +50,7 @@ func TestRekognition_Tags(t *testing.T) {
map[string]any{"CollectionId": "ltfr-coll", "Tags": map[string]string{"k": "v"}},
)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["CollectionArn"].(string)
},
@@ -60,7 +61,7 @@ func TestRekognition_Tags(t *testing.T) {
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
tags, _ := resp["Tags"].(map[string]any)
assert.Equal(t, "v", tags["k"])
},
@@ -76,7 +77,7 @@ func TestRekognition_Tags(t *testing.T) {
map[string]any{"CollectionId": "untag-coll", "Tags": map[string]string{"k": "v"}},
)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp["CollectionArn"].(string)
doRequest(t, h, "TagResource", map[string]any{"ResourceArn": arn, "Tags": map[string]string{"k": "v"}})
@@ -125,7 +126,7 @@ func TestUntagResource_UnknownARN_Returns400(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
}
@@ -144,7 +145,7 @@ func TestListTagsForResource_StreamProcessor_ReturnsTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
arn := createResp["StreamProcessorArn"].(string)
// Tag the stream processor.
@@ -158,7 +159,7 @@ func TestListTagsForResource_StreamProcessor_ReturnsTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var tagResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagResp, jsonx.V1()))
tags, _ := tagResp["Tags"].(map[string]any)
assert.Equal(t, "staging", tags["env"])
assert.Equal(t, "ml", tags["team"])
@@ -222,7 +223,7 @@ func TestTagResource_KeyValueValidation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp["CollectionArn"].(string)
rec = doRequest(t, h, "TagResource", map[string]any{"ResourceArn": arn, "Tags": tc.tags})
@@ -239,7 +240,7 @@ func TestTagResource_KeyValidation_ErrorType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp["CollectionArn"].(string)
rec = doRequest(t, h, "TagResource", map[string]any{
@@ -249,7 +250,7 @@ func TestTagResource_KeyValidation_ErrorType(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp["__type"])
}
@@ -265,7 +266,7 @@ func TestTagResource_CountLimit_Enforced(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp["CollectionArn"].(string)
// Add 200 tags in batches of 50 (keys are "tag-000" through "tag-199").
@@ -297,7 +298,7 @@ func TestTagResource_UpdateExisting_DoesNotCountAgain(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp["CollectionArn"].(string)
// Add one tag, then update it — should not fail due to count.
@@ -322,7 +323,7 @@ func TestTagResource_UnknownARN_Returns400(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
}
@@ -341,7 +342,7 @@ func TestTagResource_StreamProcessor_TagsValidated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp["StreamProcessorArn"].(string)
// Valid tag.
diff --git a/services/rekognition/handler_test.go b/services/rekognition/handler_test.go
index 501a15517e..f94e25a1e5 100644
--- a/services/rekognition/handler_test.go
+++ b/services/rekognition/handler_test.go
@@ -2,7 +2,7 @@ package rekognition_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rekognition"
)
@@ -40,7 +41,7 @@ func doRequest(t *testing.T, h *rekognition.Handler, action string, body any) *h
if body != nil {
var marshalErr error
- bodyBytes, marshalErr = json.Marshal(body)
+ bodyBytes, marshalErr = json.Marshal(body, jsonx.V1())
require.NoError(t, marshalErr)
}
@@ -65,6 +66,6 @@ func TestRekognition_UnknownOperation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "UnknownOperationException", resp["__type"])
}
diff --git a/services/rekognition/handler_text_detection_test.go b/services/rekognition/handler_text_detection_test.go
index 7b4ca8f2aa..0d7734d15f 100644
--- a/services/rekognition/handler_text_detection_test.go
+++ b/services/rekognition/handler_text_detection_test.go
@@ -1,12 +1,14 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestDetectText_ReturnsEmptyList(t *testing.T) {
@@ -17,7 +19,7 @@ func TestDetectText_ReturnsEmptyList(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["TextDetections"])
}
@@ -29,7 +31,7 @@ func TestAsyncVideoJobs_TextDetection(t *testing.T) { //nolint:paralleltest // s
require.Equal(t, http.StatusOK, rec.Code, "StartTextDetection")
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID, ok := startResp["JobId"].(string)
require.True(t, ok)
assert.NotEmpty(t, jobID)
@@ -39,7 +41,7 @@ func TestAsyncVideoJobs_TextDetection(t *testing.T) { //nolint:paralleltest // s
require.Equal(t, http.StatusOK, rec.Code, "GetTextDetection")
var firstResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &firstResp, jsonx.V1()))
assert.Equal(t, "IN_PROGRESS", firstResp["JobStatus"])
// Second poll returns SUCCEEDED
@@ -47,7 +49,7 @@ func TestAsyncVideoJobs_TextDetection(t *testing.T) { //nolint:paralleltest // s
require.Equal(t, http.StatusOK, rec.Code, "GetTextDetection")
var secondResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &secondResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &secondResp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", secondResp["JobStatus"])
assert.NotNil(t, secondResp["TextDetections"])
}
diff --git a/services/rekognition/handler_users_test.go b/services/rekognition/handler_users_test.go
index ca9142f337..a6d2252259 100644
--- a/services/rekognition/handler_users_test.go
+++ b/services/rekognition/handler_users_test.go
@@ -1,12 +1,14 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestUsers(t *testing.T) { //nolint:paralleltest // existing issue.
@@ -43,7 +45,7 @@ func TestUsers(t *testing.T) { //nolint:paralleltest // existing issue.
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
users, ok := resp["Users"].([]any)
require.True(t, ok)
assert.Len(t, users, 1)
@@ -91,7 +93,7 @@ func TestAssociateFaces(t *testing.T) { //nolint:paralleltest // existing issue.
require.Equal(t, http.StatusOK, rec.Code)
var indexResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &indexResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &indexResp, jsonx.V1()))
faceRecords := indexResp["FaceRecords"].([]any)
require.Len(t, faceRecords, 1)
faceID := faceRecords[0].(map[string]any)["Face"].(map[string]any)["FaceId"].(string)
@@ -109,7 +111,7 @@ func TestAssociateFaces(t *testing.T) { //nolint:paralleltest // existing issue.
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
associated, ok := resp["AssociatedFaces"].([]any)
require.True(t, ok)
assert.Len(t, associated, 1)
@@ -125,7 +127,7 @@ func TestAssociateFaces(t *testing.T) { //nolint:paralleltest // existing issue.
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
unsuccessful, ok := resp["UnsuccessfulFaceAssociations"].([]any)
require.True(t, ok)
assert.Len(t, unsuccessful, 1)
@@ -141,7 +143,7 @@ func TestAssociateFaces(t *testing.T) { //nolint:paralleltest // existing issue.
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
disassociated, ok := resp["DisassociatedFaces"].([]any)
require.True(t, ok)
assert.Len(t, disassociated, 1)
@@ -179,7 +181,7 @@ func TestSearchUsers(t *testing.T) { //nolint:paralleltest // existing issue.
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, faceModelVersion, resp["FaceModelVersion"])
matches, ok := resp["UserMatches"].([]any)
require.True(t, ok)
@@ -201,7 +203,7 @@ func TestSearchUsers(t *testing.T) { //nolint:paralleltest // existing issue.
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
assert.Equal(t, faceModelVersion, resp["FaceModelVersion"])
matches, ok := resp["UserMatches"].([]any)
require.True(t, ok)
@@ -220,7 +222,7 @@ func TestSearchUsers(t *testing.T) { //nolint:paralleltest // existing issue.
check: func(t *testing.T, body []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
matches, ok := resp["UserMatches"].([]any)
require.True(t, ok)
assert.Len(t, matches, 2)
diff --git a/services/rekognition/job_notification.go b/services/rekognition/job_notification.go
index dc70b2a528..29df224f8e 100644
--- a/services/rekognition/job_notification.go
+++ b/services/rekognition/job_notification.go
@@ -2,10 +2,11 @@ package rekognition
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/sns"
@@ -94,7 +95,7 @@ func buildJobNotification(jobID string, p StartAsyncJobParams) jobNotification {
"Timestamp": time.Now().UnixMilli(),
"Video": map[string]string{"S3ObjectName": p.VideoS3Name, "S3Bucket": p.VideoS3Bucket},
}
- data, _ := json.Marshal(msg)
+ data, _ := json.Marshal(msg, jsonx.V1())
return jobNotification{topic: p.NotificationTopicARN, message: data}
}
diff --git a/services/rekognition/job_notification_test.go b/services/rekognition/job_notification_test.go
index 2890de65b4..fa5d64ffa4 100644
--- a/services/rekognition/job_notification_test.go
+++ b/services/rekognition/job_notification_test.go
@@ -1,7 +1,7 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"sync"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rekognition"
)
@@ -99,7 +100,7 @@ func TestStartJobNotificationChannel(t *testing.T) {
}
var start map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &start))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &start, jsonx.V1()))
if !tt.wantNotify {
assert.Empty(t, n.messages)
@@ -111,7 +112,7 @@ func TestStartJobNotificationChannel(t *testing.T) {
assert.Equal(t, topic, n.topics[0])
var msg map[string]any
- require.NoError(t, json.Unmarshal([]byte(n.messages[0]), &msg))
+ require.NoError(t, json.Unmarshal([]byte(n.messages[0]), &msg, jsonx.V1()))
assert.Equal(t, start["JobId"], msg["JobId"])
assert.Equal(t, "SUCCEEDED", msg["Status"])
assert.Equal(t, tt.wantAPI, msg["API"])
@@ -122,7 +123,7 @@ func TestStartJobNotificationChannel(t *testing.T) {
require.Equal(t, http.StatusOK, get.Code)
var got map[string]any
- require.NoError(t, json.Unmarshal(get.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(get.Body.Bytes(), &got, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", got["JobStatus"])
})
}
diff --git a/services/rekognition/multi_region_test.go b/services/rekognition/multi_region_test.go
index 31c654b1a2..5b20a48f67 100644
--- a/services/rekognition/multi_region_test.go
+++ b/services/rekognition/multi_region_test.go
@@ -2,7 +2,8 @@ package rekognition_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/rekognition"
)
@@ -168,8 +170,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/rekognition/omission_defaults_test.go b/services/rekognition/omission_defaults_test.go
index 7dfd34e884..5398d1f3bd 100644
--- a/services/rekognition/omission_defaults_test.go
+++ b/services/rekognition/omission_defaults_test.go
@@ -1,13 +1,15 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestListProjectPolicies_DefaultPageSize locks in the real SDK's documented
@@ -24,7 +26,7 @@ func TestListProjectPolicies_DefaultPageSize(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projectARN := projResp["ProjectArn"].(string) //nolint:forcetypeassert // test
for i := range 6 {
@@ -40,7 +42,7 @@ func TestListProjectPolicies_DefaultPageSize(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policies, _ := resp["ProjectPolicies"].([]any)
assert.Len(t, policies, 5, "ListProjectPolicies omits MaxResults => real SDK defaults to 5 per response")
diff --git a/services/rekognition/persistence.go b/services/rekognition/persistence.go
index 25e2ae52b4..555c00adb5 100644
--- a/services/rekognition/persistence.go
+++ b/services/rekognition/persistence.go
@@ -2,7 +2,7 @@ package rekognition
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"slices"
@@ -35,7 +35,7 @@ const rekognitionSnapshotVersion = 1
// a snapshot from an incompatible (older/newer/absent) build of this backend
// as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
DatasetEntries map[string][]string `json:"datasetEntries"`
AccountID string `json:"accountId"`
diff --git a/services/rekognition/persistence_test.go b/services/rekognition/persistence_test.go
index 535689e6c2..c5233379ad 100644
--- a/services/rekognition/persistence_test.go
+++ b/services/rekognition/persistence_test.go
@@ -1,13 +1,14 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rekognition"
)
@@ -346,7 +347,7 @@ func TestSnapshotRestore_PreservesAllState(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.InDelta(t, float64(1), descResp["FaceCount"], 0)
// Verify face survives.
@@ -354,7 +355,7 @@ func TestSnapshotRestore_PreservesAllState(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var facesResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &facesResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &facesResp, jsonx.V1()))
faces, _ := facesResp["Faces"].([]any)
assert.Len(t, faces, 1)
@@ -382,7 +383,7 @@ func TestSnapshotRestore_ProjectVersionAndAsyncJobNewFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var projResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &projResp, jsonx.V1()))
projectARN := projResp["ProjectArn"].(string)
rec = doRequest(t, h, "CreateProjectVersion", map[string]any{
@@ -396,7 +397,7 @@ func TestSnapshotRestore_ProjectVersionAndAsyncJobNewFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var verResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &verResp, jsonx.V1()))
versionARN := verResp["ProjectVersionArn"].(string)
rec = doRequest(t, h, "StartProjectVersion", map[string]any{
@@ -410,7 +411,7 @@ func TestSnapshotRestore_ProjectVersionAndAsyncJobNewFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var dstProjResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dstProjResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &dstProjResp, jsonx.V1()))
dstProjectARN := dstProjResp["ProjectArn"].(string)
rec = doRequest(t, h, "CopyProjectVersion", map[string]any{
@@ -432,7 +433,7 @@ func TestSnapshotRestore_ProjectVersionAndAsyncJobNewFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"].(string)
snap := backend.Snapshot(t.Context())
@@ -446,7 +447,7 @@ func TestSnapshotRestore_ProjectVersionAndAsyncJobNewFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
versions := descResp["ProjectVersionDescriptions"].([]any)
require.Len(t, versions, 1)
desc := versions[0].(map[string]any)
@@ -461,7 +462,7 @@ func TestSnapshotRestore_ProjectVersionAndAsyncJobNewFields(t *testing.T) {
rec = doRequest(t, h2, "DescribeProjectVersions", map[string]any{"ProjectArn": dstProjectARN})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
dstVersions := descResp["ProjectVersionDescriptions"].([]any)
require.Len(t, dstVersions, 1)
assert.Equal(t, versionARN, dstVersions[0].(map[string]any)["SourceProjectVersionArn"])
@@ -470,7 +471,7 @@ func TestSnapshotRestore_ProjectVersionAndAsyncJobNewFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "persist-tag", getResp["JobTag"])
video, _ := getResp["Video"].(map[string]any)
@@ -493,7 +494,7 @@ func TestSnapshotRestore_TagsSurvive(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
arn := createResp["CollectionArn"].(string)
doRequest(t, h, "TagResource", map[string]any{
@@ -511,7 +512,7 @@ func TestSnapshotRestore_TagsSurvive(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var tagResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagResp, jsonx.V1()))
tags, _ := tagResp["Tags"].(map[string]any)
assert.Equal(t, "val1", tags["key1"])
assert.Equal(t, "val2", tags["key2"])
diff --git a/services/rekognition/realism_test.go b/services/rekognition/realism_test.go
index 61f629651a..ed724ff305 100644
--- a/services/rekognition/realism_test.go
+++ b/services/rekognition/realism_test.go
@@ -1,7 +1,7 @@
package rekognition_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rekognition"
)
@@ -17,7 +18,7 @@ func decodeMap(t *testing.T, body []byte) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(body, &m))
+ require.NoError(t, json.Unmarshal(body, &m, jsonx.V1()))
return m
}
diff --git a/services/rekognition/s3_object_test.go b/services/rekognition/s3_object_test.go
index ec2378aecf..2b572afcae 100644
--- a/services/rekognition/s3_object_test.go
+++ b/services/rekognition/s3_object_test.go
@@ -2,7 +2,7 @@ package rekognition_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rekognition"
)
@@ -82,7 +83,7 @@ func TestImageOps_S3ObjectValidation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidS3ObjectException", resp["__type"])
rec = doRequest(t, h, tt.action, map[string]any{
@@ -148,7 +149,7 @@ func TestSearchByImageOps_S3ObjectValidation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidS3ObjectException", resp["__type"])
rec = doRequest(t, h, tt.action, map[string]any{
@@ -191,7 +192,7 @@ func TestStartVideoOps_S3ObjectValidation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidS3ObjectException", resp["__type"])
rec = doRequest(t, h, tt.action, map[string]any{
@@ -221,7 +222,7 @@ func TestStartMediaAnalysisJob_S3ObjectValidation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidS3ObjectException", resp["__type"])
rec = doRequest(t, h, "StartMediaAnalysisJob", body("good-bucket"))
diff --git a/services/resiliencehub/handler.go b/services/resiliencehub/handler.go
index e5bd8b8c90..804005a026 100644
--- a/services/resiliencehub/handler.go
+++ b/services/resiliencehub/handler.go
@@ -2,7 +2,7 @@ package resiliencehub
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -283,7 +284,7 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
status, errType = http.StatusNotFound, "ResourceNotFoundException"
}
- payload, _ := json.Marshal(body)
+ payload, _ := json.Marshal(body, jsonx.V1())
c.Response().Header().Set("X-Amzn-Errortype", errType)
@@ -395,7 +396,7 @@ func decodeJSONBody(body []byte, v any) error {
return nil
}
- if err := json.Unmarshal(body, v); err != nil {
+ if err := json.Unmarshal(body, v, jsonx.V1()); err != nil {
return validationError("malformed request body: " + err.Error())
}
@@ -405,7 +406,7 @@ func decodeJSONBody(body []byte, v any) error {
// marshalResponse marshals v, wrapping any error as an internal failure
// (marshaling our own well-typed wire structs should never fail).
func marshalResponse(v any) ([]byte, error) {
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("resiliencehub: marshal response: %w", err)
}
diff --git a/services/resiliencehub/multi_region_test.go b/services/resiliencehub/multi_region_test.go
index a551f69200..2f1d7c066e 100644
--- a/services/resiliencehub/multi_region_test.go
+++ b/services/resiliencehub/multi_region_test.go
@@ -2,7 +2,8 @@ package resiliencehub_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/resiliencehub"
)
@@ -173,8 +175,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/resiliencehub/persistence.go b/services/resiliencehub/persistence.go
index b12bf6f188..2aeb85ae28 100644
--- a/services/resiliencehub/persistence.go
+++ b/services/resiliencehub/persistence.go
@@ -2,7 +2,7 @@ package resiliencehub
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -20,10 +20,10 @@ const resiliencehubSnapshotVersion = 1
// backendSnapshot is the top-level on-disk shape for the Resilience Hub
// backend.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountId"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountId"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/resourceexplorer2/catalog.go b/services/resourceexplorer2/catalog.go
index 0e6e55fed1..65058ea066 100644
--- a/services/resourceexplorer2/catalog.go
+++ b/services/resourceexplorer2/catalog.go
@@ -2,7 +2,7 @@ package resourceexplorer2
import (
_ "embed" // documented resource type list
- "encoding/json"
+ "encoding/json/v2"
"slices"
"strings"
"sync"
@@ -10,6 +10,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -93,7 +94,7 @@ var supportedTypes = sync.OnceValue(func() []SupportedType { //nolint:gochecknog
Types []string `json:"types"`
}
- if err := json.Unmarshal(documentedTypesJSON, &documented); err != nil {
+ if err := json.Unmarshal(documentedTypesJSON, &documented, jsonx.V1()); err != nil {
panic("resourceexplorer2: embedded supported types are invalid: " + err.Error())
}
diff --git a/services/resourceexplorer2/persistence.go b/services/resourceexplorer2/persistence.go
index b15d542242..5e3052b481 100644
--- a/services/resourceexplorer2/persistence.go
+++ b/services/resourceexplorer2/persistence.go
@@ -2,7 +2,7 @@ package resourceexplorer2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,8 +12,8 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/resourceexplorer2/types_catalog_test.go b/services/resourceexplorer2/types_catalog_test.go
index 8fcbbd829a..c3b2560d58 100644
--- a/services/resourceexplorer2/types_catalog_test.go
+++ b/services/resourceexplorer2/types_catalog_test.go
@@ -2,12 +2,14 @@ package resourceexplorer2_test
import (
_ "embed"
- "encoding/json"
+ "encoding/json/v2"
"slices"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
//go:embed supported_types.json
@@ -21,7 +23,7 @@ func TestDocumentedTypesCatalog(t *testing.T) {
Types []string `json:"types"`
}
- require.NoError(t, json.Unmarshal(documentedTypes, &c))
+ require.NoError(t, json.Unmarshal(documentedTypes, &c, jsonx.V1()))
tests := []struct {
check func(t *testing.T)
diff --git a/services/resourcegroups/groups.go b/services/resourcegroups/groups.go
index 8e448d40da..20678349cc 100644
--- a/services/resourcegroups/groups.go
+++ b/services/resourcegroups/groups.go
@@ -2,7 +2,8 @@ package resourcegroups
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"regexp"
"slices"
@@ -11,6 +12,7 @@ import (
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -147,8 +149,8 @@ func validateResourceQuery(q *ResourceQuery) error {
return fmt.Errorf("%w: ResourceQuery.Query must be a non-empty JSON string", ErrValidation)
}
- var raw json.RawMessage
- if err := json.Unmarshal([]byte(q.Query), &raw); err != nil {
+ var raw jsontext.Value
+ if err := json.Unmarshal([]byte(q.Query), &raw, jsonx.V1()); err != nil {
return fmt.Errorf(
"%w: ResourceQuery.Query is not valid JSON: %s",
ErrValidation,
diff --git a/services/resourcegroups/handler.go b/services/resourcegroups/handler.go
index c432a7fd47..cbc8812630 100644
--- a/services/resourcegroups/handler.go
+++ b/services/resourcegroups/handler.go
@@ -3,7 +3,7 @@ package resourcegroups
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -16,6 +16,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -258,7 +259,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var req extractResourceNameInput
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if req.Name != "" {
return req.Name
@@ -339,7 +340,7 @@ func mergeListGroupsQuery(body []byte, q url.Values) ([]byte, error) {
doc := map[string]any{}
if len(bytes.TrimSpace(body)) > 0 {
- if err := json.Unmarshal(body, &doc); err != nil {
+ if err := json.Unmarshal(body, &doc, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -357,7 +358,7 @@ func mergeListGroupsQuery(body []byte, q url.Values) ([]byte, error) {
doc["NextToken"] = token
}
- return json.Marshal(doc)
+ return json.Marshal(doc, jsonx.V1())
}
func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]byte, error) {
@@ -371,7 +372,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// amznErrorTypeHeader carries the modeled exception type for the restjson1
@@ -388,15 +389,12 @@ const errTypeBadRequest = "BadRequestException"
// NotFoundException, BadRequestException, ForbiddenException,
// InternalServerErrorException, MethodNotAllowedException, TooManyRequestsException).
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
code := http.StatusInternalServerError
wireType := "InternalServerErrorException"
switch {
case errors.Is(err, errInvalidRequest), errors.Is(err, ErrUnknownOperation),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
code, wireType = http.StatusBadRequest, errTypeBadRequest
case errors.Is(err, ErrAlreadyExists):
code, wireType = http.StatusBadRequest, errTypeBadRequest
diff --git a/services/resourcegroups/handler_groups_config_test.go b/services/resourcegroups/handler_groups_config_test.go
index 3bc43c7fd5..6b19a50221 100644
--- a/services/resourcegroups/handler_groups_config_test.go
+++ b/services/resourcegroups/handler_groups_config_test.go
@@ -1,13 +1,14 @@
package resourcegroups_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/resourcegroups"
)
@@ -102,7 +103,7 @@ func TestGetGroupQuery_ConfigVsQueryGroup(t *testing.T) {
} `json:"ResourceQuery"`
} `json:"GroupQuery"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotNil(t, out.GroupQuery.ResourceQuery)
})
}
@@ -141,7 +142,7 @@ func TestResourceGroupsHandler_GetGroupConfiguration_Status(t *testing.T) {
Status string `json:"Status"`
} `json:"GroupConfiguration"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "UPDATE_COMPLETE", out.GroupConfiguration.Status)
assert.NotContains(t, rec.Body.String(), "GroupName")
}
@@ -542,7 +543,7 @@ func TestCreateGroup_ResponseShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
group, ok := out["Group"].(map[string]any)
require.True(t, ok)
diff --git a/services/resourcegroups/handler_groups_test.go b/services/resourcegroups/handler_groups_test.go
index 5e764c8cfa..dc4b7e16f5 100644
--- a/services/resourcegroups/handler_groups_test.go
+++ b/services/resourcegroups/handler_groups_test.go
@@ -2,7 +2,7 @@ package resourcegroups_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/resourcegroups"
)
@@ -230,7 +231,7 @@ func TestListGroups_PaginationViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
groups1 := out1["Groups"].([]any)
assert.Len(t, groups1, 3)
token1, _ := out1["NextToken"].(string)
@@ -244,7 +245,7 @@ func TestListGroups_PaginationViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
groups2 := out2["Groups"].([]any)
assert.Len(t, groups2, 3)
assert.Empty(t, out2["NextToken"])
@@ -766,7 +767,7 @@ func TestResourceGroupsHandler_DeleteGroup_EchoesGroup(t *testing.T) {
Description string `json:"Description"`
} `json:"Group"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "echo-group", out.Group.Name)
assert.Equal(t, "echo desc", out.Group.Description)
}
@@ -791,7 +792,7 @@ func TestResourceGroupsHandler_ListGroups_IdentifierFields(t *testing.T) {
var out struct {
GroupIdentifiers []map[string]any `json:"GroupIdentifiers"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.GroupIdentifiers, 1)
assert.Equal(t, "Ident Group", out.GroupIdentifiers[0]["DisplayName"])
criticality, ok := out.GroupIdentifiers[0]["Criticality"].(float64)
diff --git a/services/resourcegroups/handler_groups_validation_test.go b/services/resourcegroups/handler_groups_validation_test.go
index d3a15bf87f..ee04712fda 100644
--- a/services/resourcegroups/handler_groups_validation_test.go
+++ b/services/resourcegroups/handler_groups_validation_test.go
@@ -1,7 +1,7 @@
package resourcegroups_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/resourcegroups"
)
@@ -396,7 +397,7 @@ func TestListGroupsOutputShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
groups, ok := out["Groups"].([]any)
require.True(t, ok, "Groups must be a list")
@@ -506,7 +507,7 @@ func TestOwnerId(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
group, ok := out["Group"].(map[string]any)
require.True(t, ok)
@@ -532,7 +533,7 @@ func TestCreateGroupIdentityFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
group, ok := out["Group"].(map[string]any)
require.True(t, ok)
@@ -555,7 +556,7 @@ func TestListGroupsGroupIdentifiersShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
identifiers := out["GroupIdentifiers"].([]any)
require.Len(t, identifiers, 1)
diff --git a/services/resourcegroups/handler_query_test.go b/services/resourcegroups/handler_query_test.go
index 1e0be1e46e..beecad7322 100644
--- a/services/resourcegroups/handler_query_test.go
+++ b/services/resourcegroups/handler_query_test.go
@@ -1,7 +1,7 @@
package resourcegroups_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/resourcegroups"
)
@@ -88,7 +89,7 @@ func TestSearchResources_QueryErrorsShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotContains(t, out, "QueryErrors", "QueryErrors must be omitted when empty")
}
@@ -137,7 +138,7 @@ func TestSearchResources_PaginationViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
ids1 := out1["ResourceIdentifiers"].([]any)
assert.Len(t, ids1, 3)
tok1, _ := out1["NextToken"].(string)
@@ -148,7 +149,7 @@ func TestSearchResources_PaginationViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
ids2 := out2["ResourceIdentifiers"].([]any)
assert.Len(t, ids2, 2)
assert.Empty(t, out2["NextToken"])
diff --git a/services/resourcegroups/handler_resources_test.go b/services/resourcegroups/handler_resources_test.go
index 4e9c1bd287..0191ad94ed 100644
--- a/services/resourcegroups/handler_resources_test.go
+++ b/services/resourcegroups/handler_resources_test.go
@@ -1,7 +1,7 @@
package resourcegroups_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/resourcegroups"
)
@@ -154,7 +155,7 @@ func TestGroupResources_InvalidARNReturnsFailed(t *testing.T) {
ErrorMessage string `json:"ErrorMessage"`
} `json:"Failed"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(
t,
@@ -195,7 +196,7 @@ func TestGroupResources_InvalidARNNotAddedToGroup(t *testing.T) {
} `json:"Identifier"`
} `json:"Resources"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Resources, 1, "only the valid ARN should be in group")
assert.Equal(t, "arn:aws:s3:::real-bucket", out.Resources[0].Identifier.ResourceArn)
@@ -318,7 +319,7 @@ func TestUngroupResourcesFailures(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
succeeded, _ := out["Succeeded"].([]any)
failed, _ := out["Failed"].([]any)
@@ -480,14 +481,14 @@ func TestListGroupResources_ResourceIdentifiersDeprecatedField(t *testing.T) {
ResourceType string `json:"ResourceType"`
} `json:"ResourceIdentifiers"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.ResourceIdentifiers, 1)
assert.Equal(t, out.Resources[0].Identifier.ResourceArn, out.ResourceIdentifiers[0].ResourceArn)
assert.Equal(t, out.Resources[0].Identifier.ResourceType, out.ResourceIdentifiers[0].ResourceType)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
assert.NotContains(t, raw, "QueryErrors", "QueryErrors must be omitted when empty")
}
@@ -543,7 +544,7 @@ func TestListGroupResources_PaginationViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
resources1 := out1["Resources"].([]any)
assert.Len(t, resources1, 2)
token1, _ := out1["NextToken"].(string)
@@ -557,7 +558,7 @@ func TestListGroupResources_PaginationViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
resources2 := out2["Resources"].([]any)
assert.Len(t, resources2, 3)
assert.Empty(t, out2["NextToken"])
@@ -684,7 +685,7 @@ func TestResourceGroupsHandler_GroupingStatuses_UpdatedAtIsEpochSeconds(t *testi
var out struct {
GroupingStatuses []map[string]any `json:"GroupingStatuses"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.GroupingStatuses, 1)
_, isNumber := out.GroupingStatuses[0]["UpdatedAt"].(float64)
assert.True(t, isNumber, "ListGroupingStatuses UpdatedAt must be a JSON number: %s", rec.Body.String())
@@ -713,7 +714,7 @@ func TestListGroupingStatuses_PaginationViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
statuses1 := out1["GroupingStatuses"].([]any)
assert.Len(t, statuses1, 3)
tok1, _ := out1["NextToken"].(string)
@@ -727,7 +728,7 @@ func TestListGroupingStatuses_PaginationViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
statuses2 := out2["GroupingStatuses"].([]any)
assert.Len(t, statuses2, 2)
assert.Empty(t, out2["NextToken"])
diff --git a/services/resourcegroups/handler_tags.go b/services/resourcegroups/handler_tags.go
index a22dd6417d..464ee129ca 100644
--- a/services/resourcegroups/handler_tags.go
+++ b/services/resourcegroups/handler_tags.go
@@ -2,7 +2,7 @@ package resourcegroups
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -38,7 +39,7 @@ func (h *Handler) handleTagRequest(ctx context.Context, c *echo.Context, log *sl
var in tagResourceInput
- if err = json.Unmarshal(body, &in); err != nil {
+ if err = json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.handleError(ctx, c, "Tag", errInvalidRequest)
}
@@ -96,7 +97,7 @@ func (h *Handler) extractUntagKeys(ctx context.Context, c *echo.Context, log *sl
}
var in untagResourceInput
- if err = json.Unmarshal(body, &in); err != nil {
+ if err = json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, errInvalidRequest
}
@@ -138,7 +139,7 @@ func (h *Handler) handleResourceTags(ctx context.Context, c *echo.Context) error
var in untagResourceInput
- if err = json.Unmarshal(body, &in); err != nil {
+ if err = json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return h.handleError(ctx, c, "Untag", errInvalidRequest)
}
diff --git a/services/resourcegroups/handler_tags_test.go b/services/resourcegroups/handler_tags_test.go
index 42b6b055f6..344b1f9f37 100644
--- a/services/resourcegroups/handler_tags_test.go
+++ b/services/resourcegroups/handler_tags_test.go
@@ -2,7 +2,7 @@ package resourcegroups_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/resourcegroups"
)
@@ -36,7 +37,7 @@ func TestResourceGroupsHandler_GetTags(t *testing.T) {
GroupArn string `json:"GroupArn"`
} `json:"Group"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.Group.GroupArn
},
@@ -95,7 +96,7 @@ func TestResourceGroupsHandler_Tag(t *testing.T) {
GroupArn string `json:"GroupArn"`
} `json:"Group"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
groupARN = out.Group.GroupArn
} else {
groupARN = "arn:aws:resource-groups:us-east-1:000000000000:group/nonexistent"
@@ -142,7 +143,7 @@ func TestResourceGroupsHandler_Untag(t *testing.T) {
GroupArn string `json:"GroupArn"`
} `json:"Group"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
groupARN = out.Group.GroupArn
} else {
groupARN = "arn:aws:resource-groups:us-east-1:000000000000:group/nonexistent"
@@ -170,7 +171,7 @@ func TestUntagViaDeleteVerb(t *testing.T) {
// DELETE /resources/{Arn}/tags with JSON body.
e := echo.New()
- bodyBytes, _ := json.Marshal(map[string]any{"Keys": []string{"env"}})
+ bodyBytes, _ := json.Marshal(map[string]any{"Keys": []string{"env"}}, jsonx.V1())
req := httptest.NewRequest(
http.MethodDelete,
"/resources/"+g.ARN+"/tags",
diff --git a/services/resourcegroups/handler_tagsync_test.go b/services/resourcegroups/handler_tagsync_test.go
index 4f3ec883a4..d571050bbb 100644
--- a/services/resourcegroups/handler_tagsync_test.go
+++ b/services/resourcegroups/handler_tagsync_test.go
@@ -1,7 +1,7 @@
package resourcegroups_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/resourcegroups"
)
@@ -182,7 +183,7 @@ func TestResourceGroupsHandler_GetTagSyncTask(t *testing.T) {
var out struct {
TaskArn string `json:"TaskArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.TaskArn
},
@@ -252,7 +253,7 @@ func TestResourceGroupsHandler_CancelTagSyncTask(t *testing.T) {
var out struct {
TaskArn string `json:"TaskArn"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.TaskArn
},
@@ -315,7 +316,7 @@ func TestCancelTagSyncTaskState(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
taskARN := startOut["TaskArn"].(string)
cancelRec := doResourceGroupsRequest(
@@ -441,7 +442,7 @@ func TestListTagSyncTasks_PaginationViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
tasks1 := out1["TagSyncTasks"].([]any)
assert.Len(t, tasks1, 2)
tok1, _ := out1["NextToken"].(string)
@@ -454,7 +455,7 @@ func TestListTagSyncTasks_PaginationViaHandler(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
tasks2 := out2["TagSyncTasks"].([]any)
assert.Len(t, tasks2, 2)
assert.Empty(t, out2["NextToken"])
@@ -480,13 +481,13 @@ func TestResourceGroupsHandler_TagSyncTask_CreatedAtIsEpochSeconds(t *testing.T)
var startOut struct {
TaskArn string `json:"TaskArn"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startOut, jsonx.V1()))
getRec := doResourceGroupsRequest(t, h, "GetTagSyncTask", map[string]any{"TaskArn": startOut.TaskArn})
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
_, isNumber := getOut["CreatedAt"].(float64)
assert.True(t, isNumber, "GetTagSyncTask CreatedAt must be a JSON number: %s", getRec.Body.String())
@@ -496,7 +497,7 @@ func TestResourceGroupsHandler_TagSyncTask_CreatedAtIsEpochSeconds(t *testing.T)
var listOut struct {
TagSyncTasks []map[string]any `json:"TagSyncTasks"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.TagSyncTasks, 1)
_, isNumber = listOut.TagSyncTasks[0]["CreatedAt"].(float64)
assert.True(t, isNumber, "ListTagSyncTasks CreatedAt must be a JSON number: %s", listRec.Body.String())
diff --git a/services/resourcegroups/handler_test.go b/services/resourcegroups/handler_test.go
index 30d178f8f9..cd50022c83 100644
--- a/services/resourcegroups/handler_test.go
+++ b/services/resourcegroups/handler_test.go
@@ -2,7 +2,7 @@ package resourcegroups_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/resourcegroups"
)
@@ -34,7 +35,7 @@ func doResourceGroupsRequest(
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -88,7 +89,7 @@ func doResourceTagsRequest(
var bodyReader *bytes.Reader
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
bodyReader = bytes.NewReader(b)
} else {
@@ -122,7 +123,7 @@ func doResourceGroupsRESTRequest(
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
diff --git a/services/resourcegroups/handler_untag_rejection_test.go b/services/resourcegroups/handler_untag_rejection_test.go
index bc6e23e978..498a5683b7 100644
--- a/services/resourcegroups/handler_untag_rejection_test.go
+++ b/services/resourcegroups/handler_untag_rejection_test.go
@@ -2,7 +2,7 @@ package resourcegroups_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/resourcegroups"
)
@@ -89,7 +90,7 @@ func TestResourceGroupsHandler_Untag_RejectionNotBypassed(t *testing.T) {
GroupArn string `json:"GroupArn"`
} `json:"Group"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
groupARN := created.Group.GroupArn
untagRec := doResourceTagsRequestRaw(t, h, http.MethodDelete, groupARN, tt.body)
@@ -98,7 +99,7 @@ func TestResourceGroupsHandler_Untag_RejectionNotBypassed(t *testing.T) {
var resp map[string]any
require.NoError(
- t, json.Unmarshal(untagRec.Body.Bytes(), &resp),
+ t, json.Unmarshal(untagRec.Body.Bytes(), &resp, jsonx.V1()),
"response body must be a single well-formed JSON document, not two concatenated: %s",
untagRec.Body.String(),
)
@@ -109,7 +110,7 @@ func TestResourceGroupsHandler_Untag_RejectionNotBypassed(t *testing.T) {
var tagsResp struct {
Tags map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp, jsonx.V1()))
assert.Equal(t, map[string]string{"env": "prod"}, tagsResp.Tags, "rejected Untag must not remove any tags")
})
}
diff --git a/services/resourcegroups/persistence.go b/services/resourcegroups/persistence.go
index b01d1f8762..661566e9e3 100644
--- a/services/resourcegroups/persistence.go
+++ b/services/resourcegroups/persistence.go
@@ -2,7 +2,7 @@ package resourcegroups
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -108,7 +108,7 @@ func buildPersistenceDTORegistry() (*store.Registry, *store.Table[groupSnapshot]
// strings, or status records), so there is nothing for store.Table to key on,
// and each is persisted here directly instead.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
GroupConfigurations map[string]map[string][]GroupConfigurationItem `json:"groupConfigurations"`
GroupResources map[string]map[string][]string `json:"groupResources"`
GroupingStatuses map[string]map[string][]GroupingStatusItem `json:"groupingStatuses"`
@@ -217,7 +217,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreGroups rebuilds the "dirty" groups store.Table from snap's DTO
// table, factored out of Restore to keep Restore's own cognitive complexity
// low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreGroups(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreGroups(tables map[string]jsontext.Value) error {
dtoReg, groupDTOs := buildPersistenceDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
return fmt.Errorf("resourcegroups: restore snapshot groups table: %w", err)
diff --git a/services/resourcegroups/persistence_test.go b/services/resourcegroups/persistence_test.go
index 93b7d2586d..399744c748 100644
--- a/services/resourcegroups/persistence_test.go
+++ b/services/resourcegroups/persistence_test.go
@@ -2,12 +2,14 @@ package resourcegroups_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
"github.com/blackbirdworks/gopherstack/services/resourcegroups"
)
@@ -295,8 +297,8 @@ func Test_PersistenceFullStateRoundTrip(t *testing.T) {
snap := b.Snapshot(t.Context())
require.NotNil(t, snap)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &raw, jsonx.V1()))
assert.Contains(t, raw, "version")
b2 := resourcegroups.NewInMemoryBackend("123456789012", "us-east-1")
diff --git a/services/resourcegroups/query.go b/services/resourcegroups/query.go
index 01baa5e124..f4e5cfe62e 100644
--- a/services/resourcegroups/query.go
+++ b/services/resourcegroups/query.go
@@ -2,10 +2,12 @@ package resourcegroups
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"slices"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// arnSplitParts is the number of colon-separated segments in a well-formed AWS ARN.
@@ -126,7 +128,7 @@ var arnServiceTypeMap = map[string]string{ //nolint:gochecknoglobals,gosec // st
// The special value "AWS::AllSupported" means match all types and returns nil.
func parseResourceTypeFilters(queryJSON string) map[string]bool {
var tfq tagFilterQuery
- if err := json.Unmarshal([]byte(queryJSON), &tfq); err != nil {
+ if err := json.Unmarshal([]byte(queryJSON), &tfq, jsonx.V1()); err != nil {
return nil
}
diff --git a/services/resourcegroups/query_validation.go b/services/resourcegroups/query_validation.go
index bd076b6b91..b3d635615a 100644
--- a/services/resourcegroups/query_validation.go
+++ b/services/resourcegroups/query_validation.go
@@ -2,9 +2,11 @@ package resourcegroups
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -42,7 +44,7 @@ func validateQueryShape(queryType, query string) error {
func validateTagFilterQuery(raw []byte) error {
var q tagFilterQuery
- if err := json.Unmarshal(raw, &q); err != nil {
+ if err := json.Unmarshal(raw, &q, jsonx.V1()); err != nil {
return fmt.Errorf("%w: ResourceQuery.Query is not a valid TAG_FILTERS_1_0 query: %w", ErrValidation, err)
}
@@ -62,7 +64,7 @@ func validateTagFilterQuery(raw []byte) error {
func validateStackQuery(raw []byte) error {
var q cfnQuery
- if err := json.Unmarshal(raw, &q); err != nil {
+ if err := json.Unmarshal(raw, &q, jsonx.V1()); err != nil {
return fmt.Errorf(
"%w: ResourceQuery.Query is not a valid CLOUDFORMATION_STACK_1_0 query: %w", ErrValidation, err)
}
diff --git a/services/resourcegroups/source.go b/services/resourcegroups/source.go
index 78f0209f8c..fdba608019 100644
--- a/services/resourcegroups/source.go
+++ b/services/resourcegroups/source.go
@@ -2,10 +2,11 @@ package resourcegroups
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"slices"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/cloudformation"
"github.com/blackbirdworks/gopherstack/services/resourcegroupstaggingapi"
@@ -167,7 +168,7 @@ func typeAllowed(want map[string]bool, resType string) bool {
func evalTagFilterQuery(ctx context.Context, src ResourceSource, queryJSON string) []ResourceIdentifier {
var q tagFilterQuery
- if err := json.Unmarshal([]byte(queryJSON), &q); err != nil {
+ if err := json.Unmarshal([]byte(queryJSON), &q, jsonx.V1()); err != nil {
return nil
}
@@ -190,7 +191,7 @@ func evalStackQuery(
ctx context.Context, src ResourceSource, queryJSON string,
) ([]ResourceIdentifier, []queryErrorWire) {
var q cfnQuery
- if err := json.Unmarshal([]byte(queryJSON), &q); err != nil {
+ if err := json.Unmarshal([]byte(queryJSON), &q, jsonx.V1()); err != nil {
return nil, nil
}
diff --git a/services/resourcegroupstaggingapi/compliance.go b/services/resourcegroupstaggingapi/compliance.go
index 46dc942cd9..991ed58cc5 100644
--- a/services/resourcegroupstaggingapi/compliance.go
+++ b/services/resourcegroupstaggingapi/compliance.go
@@ -2,10 +2,11 @@ package resourcegroupstaggingapi
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -189,7 +190,7 @@ type tagPolicyStringListAssign struct {
// fabricate an unverified mapping.
func requiredTagsFromPolicy(policyJSON string) []RequiredTag {
var doc tagPolicyDocument
- if err := json.Unmarshal([]byte(policyJSON), &doc); err != nil {
+ if err := json.Unmarshal([]byte(policyJSON), &doc, jsonx.V1()); err != nil {
return nil
}
diff --git a/services/resourcegroupstaggingapi/compliance_test.go b/services/resourcegroupstaggingapi/compliance_test.go
index 58d086e8f5..48fa5c3d39 100644
--- a/services/resourcegroupstaggingapi/compliance_test.go
+++ b/services/resourcegroupstaggingapi/compliance_test.go
@@ -2,13 +2,14 @@ package resourcegroupstaggingapi_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/resourcegroupstaggingapi"
)
@@ -184,7 +185,7 @@ func TestGetComplianceSummaryJSONShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
list, ok := out["SummaryList"].([]any)
require.True(t, ok, "SummaryList should be a JSON array")
@@ -312,7 +313,7 @@ func TestListRequiredTagsJSONShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
list, ok := out["RequiredTags"].([]any)
require.True(t, ok, "RequiredTags should be a JSON array")
diff --git a/services/resourcegroupstaggingapi/get_resources_test.go b/services/resourcegroupstaggingapi/get_resources_test.go
index ffaedbaa37..aa1a10ecc7 100644
--- a/services/resourcegroupstaggingapi/get_resources_test.go
+++ b/services/resourcegroupstaggingapi/get_resources_test.go
@@ -2,7 +2,8 @@ package resourcegroupstaggingapi_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/resourcegroupstaggingapi"
)
@@ -668,7 +670,7 @@ func TestGetResources_TagsPerPage_SplitsPages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out resourcegroupstaggingapi.GetResourcesOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
require.NotNil(t, out.PaginationToken, "TagsPerPage must force an early page break")
assert.LessOrEqual(t, len(out.ResourceTagMappingList), 2,
@@ -699,7 +701,7 @@ func TestGetResources_TagsPerPage_OversizedFirstResourceStillReturnedAlone(t *te
require.Equal(t, http.StatusOK, rec.Code)
var out resourcegroupstaggingapi.GetResourcesOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
// GetResources never splits a single resource and its tags across pages, so the
// oversized resource is still returned alone rather than yielding zero results.
@@ -729,7 +731,7 @@ func TestGetResources_NoTagsPerPage_Unconstrained(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out resourcegroupstaggingapi.GetResourcesOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.ResourceTagMappingList, 5, "without TagsPerPage all resources fit on one page")
assert.Nil(t, out.PaginationToken)
@@ -768,7 +770,7 @@ func TestGetResources_ResourceARNList_FiltersToSpecificARNs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
list, ok := out["ResourceTagMappingList"].([]any)
require.True(t, ok)
@@ -809,7 +811,7 @@ func TestGetResources_ResourceARNList_EmptyReturnsAll(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
list, ok := out["ResourceTagMappingList"].([]any)
require.True(t, ok)
@@ -1097,7 +1099,7 @@ func TestComplianceDetailsFieldName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
list, ok := out["ResourceTagMappingList"].([]any)
require.True(t, ok)
@@ -1140,7 +1142,7 @@ func TestHandler_GetResources_IncludeComplianceDetails(t *testing.T) {
} `json:"ResourceTagMappingList"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&body))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &body, jsonx.V1()))
require.Len(t, body.ResourceTagMappingList, 1)
require.NotNil(t, body.ResourceTagMappingList[0].ComplianceDetails)
assert.True(t, body.ResourceTagMappingList[0].ComplianceDetails.ComplianceStatus)
diff --git a/services/resourcegroupstaggingapi/handler.go b/services/resourcegroupstaggingapi/handler.go
index 87a5ea964e..b3a4697da7 100644
--- a/services/resourcegroupstaggingapi/handler.go
+++ b/services/resourcegroupstaggingapi/handler.go
@@ -2,7 +2,7 @@ package resourcegroupstaggingapi
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -140,13 +141,10 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
code := http.StatusInternalServerError
errType := "InternalFailure"
@@ -166,7 +164,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
case errors.Is(err, ErrPaginationTokenExpired):
code = http.StatusBadRequest
errType = "PaginationTokenExpiredException"
- case errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ case jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
// Malformed request bodies are a protocol-level failure, not a modeled
// operation error; the AWS JSON 1.1 protocol reports these as
// SerializationException (matches services/organizations' convention for
@@ -178,7 +176,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
payload, _ := json.Marshal(service.JSONErrorResponse{
Type: errType,
Message: err.Error(),
- })
+ }, jsonx.V1())
c.Response().Header().Set("Content-Type", "application/x-amz-json-1.1")
diff --git a/services/resourcegroupstaggingapi/handler_test.go b/services/resourcegroupstaggingapi/handler_test.go
index 2ec70fcd56..5f489062f4 100644
--- a/services/resourcegroupstaggingapi/handler_test.go
+++ b/services/resourcegroupstaggingapi/handler_test.go
@@ -3,7 +3,8 @@ package resourcegroupstaggingapi_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -15,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/resourcegroupstaggingapi"
)
@@ -102,7 +104,7 @@ func doTaggingRequest(
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -598,7 +600,7 @@ func TestValidationFailures_UseInvalidParameterException(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code, "body: %s", rec.Body.String())
var body map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&body))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &body, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", body["__type"],
"real resourcegroupstaggingapi has no ValidationException shape")
assert.NotContains(t, rec.Body.String(), "ValidationException")
@@ -631,7 +633,7 @@ func TestMalformedBody_UsesSerializationException(t *testing.T) {
assert.Contains(t, rec.Body.String(), "SerializationException")
var body map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&body))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &body, jsonx.V1()))
assert.Equal(t, "SerializationException", body["__type"])
})
}
@@ -772,7 +774,7 @@ func TestHandler_ValidationError_HasInvalidParameterExceptionBody(t *testing.T)
require.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&body))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &body, jsonx.V1()))
// Real resourcegroupstaggingapi has no ValidationException shape; parameter
// validation failures are modeled as InvalidParameterException.
assert.Equal(t, "InvalidParameterException", body["__type"])
diff --git a/services/resourcegroupstaggingapi/persistence.go b/services/resourcegroupstaggingapi/persistence.go
index cc7c495d56..fd3a56c7a7 100644
--- a/services/resourcegroupstaggingapi/persistence.go
+++ b/services/resourcegroupstaggingapi/persistence.go
@@ -2,7 +2,7 @@ package resourcegroupstaggingapi
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"time"
@@ -98,10 +98,10 @@ func buildPersistenceDTORegistry() (*store.Registry, *store.Table[reportStateSna
// resource cache is likewise never persisted -- it is a rebuildable TTL
// cache, not part of durable state.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serializes the backend state to JSON.
@@ -198,7 +198,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// store_setup.go's registerAllTables doc) from tables via the ephemeral DTO
// registry, factored out of Restore to keep Restore's own cognitive
// complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreDirtyTables(ctx context.Context, tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTables(ctx context.Context, tables map[string]jsontext.Value) error {
dtoReg, reportStateDTOs := buildPersistenceDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
return fmt.Errorf("resourcegroupstaggingapi: restore snapshot DTO tables: %w", err)
diff --git a/services/resourcegroupstaggingapi/report_test.go b/services/resourcegroupstaggingapi/report_test.go
index b87022aaa2..ecc47a77a3 100644
--- a/services/resourcegroupstaggingapi/report_test.go
+++ b/services/resourcegroupstaggingapi/report_test.go
@@ -2,7 +2,7 @@ package resourcegroupstaggingapi_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/resourcegroupstaggingapi"
)
@@ -92,7 +93,7 @@ func TestStartReportCreationInput_HasNoS3BucketRegionField(t *testing.T) {
// A request body containing S3BucketRegion (as some non-AWS client might send) must
// simply be ignored as an unknown field, not silently accepted as a modeled member.
- marshaled, err := json.Marshal(resourcegroupstaggingapi.StartReportCreationInput{S3Bucket: "b"})
+ marshaled, err := json.Marshal(resourcegroupstaggingapi.StartReportCreationInput{S3Bucket: "b"}, jsonx.V1())
require.NoError(t, err)
assert.NotContains(t, string(marshaled), "S3BucketRegion")
}
@@ -331,7 +332,7 @@ func TestDescribeReportCreationJSONShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", out["Status"])
assert.Equal(t, "s3://bkt/path.csv", out["S3Location"])
diff --git a/services/rolesanywhere/handler.go b/services/rolesanywhere/handler.go
index 78edc33d9b..b024dfcfc1 100644
--- a/services/rolesanywhere/handler.go
+++ b/services/rolesanywhere/handler.go
@@ -2,7 +2,7 @@ package rolesanywhere
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
@@ -12,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -227,7 +228,7 @@ func (h *Handler) handleREST(c *echo.Context) error {
return c.JSON(statusCode, struct{}{})
}
- data, jsonErr := json.Marshal(result)
+ data, jsonErr := json.Marshal(result, jsonx.V1())
if jsonErr != nil {
return c.JSON(http.StatusInternalServerError, errBody("InternalFailure", "serialization failed"))
}
diff --git a/services/rolesanywhere/handler_attribute_mappings.go b/services/rolesanywhere/handler_attribute_mappings.go
index 924558c7fd..79ff16ced0 100644
--- a/services/rolesanywhere/handler_attribute_mappings.go
+++ b/services/rolesanywhere/handler_attribute_mappings.go
@@ -2,10 +2,12 @@ package rolesanywhere
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- Attribute mapping handlers ----
@@ -18,7 +20,7 @@ func (h *Handler) handlePutAttributeMapping(ctx context.Context, path string, bo
MappingRules []MappingRule `json:"mappingRules"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
diff --git a/services/rolesanywhere/handler_attribute_mappings_test.go b/services/rolesanywhere/handler_attribute_mappings_test.go
index 9e1b0326bf..4ddba6fbd7 100644
--- a/services/rolesanywhere/handler_attribute_mappings_test.go
+++ b/services/rolesanywhere/handler_attribute_mappings_test.go
@@ -2,7 +2,7 @@ package rolesanywhere_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- Attribute Mapping HTTP ----
@@ -38,7 +40,7 @@ func TestHandler_AttributeMapping_CRUD(t *testing.T) {
require.Equal(t, http.StatusCreated, recProfile.Code)
var profileResp map[string]any
- require.NoError(t, json.Unmarshal(recProfile.Body.Bytes(), &profileResp))
+ require.NoError(t, json.Unmarshal(recProfile.Body.Bytes(), &profileResp, jsonx.V1()))
p := profileResp["profile"].(map[string]any)
profileID := p["profileId"].(string)
@@ -56,7 +58,7 @@ func TestHandler_AttributeMapping_CRUD(t *testing.T) {
assert.Equal(t, tt.wantStatus, recPut.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(recPut.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(recPut.Body.Bytes(), &putResp, jsonx.V1()))
profileInResp := putResp["profile"].(map[string]any)
assert.Equal(t, profileID, profileInResp["profileId"])
diff --git a/services/rolesanywhere/handler_crls.go b/services/rolesanywhere/handler_crls.go
index 53d9d8f7ef..96123f2d32 100644
--- a/services/rolesanywhere/handler_crls.go
+++ b/services/rolesanywhere/handler_crls.go
@@ -2,9 +2,11 @@ package rolesanywhere
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- CRL handlers ----
@@ -18,7 +20,7 @@ func (h *Handler) handleImportCrl(ctx context.Context, body []byte) (any, int, e
Tags []TagEntry `json:"tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
@@ -84,7 +86,7 @@ func (h *Handler) handleUpdateCrl(ctx context.Context, path string, body []byte)
CrlData []byte `json:"crlData"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
diff --git a/services/rolesanywhere/handler_crls_test.go b/services/rolesanywhere/handler_crls_test.go
index 2e21d0af4b..3cc129e9bd 100644
--- a/services/rolesanywhere/handler_crls_test.go
+++ b/services/rolesanywhere/handler_crls_test.go
@@ -2,7 +2,7 @@ package rolesanywhere_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- CRL HTTP CRUD ----
@@ -44,7 +46,7 @@ func TestHandler_CRL_CRUD(t *testing.T) {
assert.Equal(t, tt.wantCreate, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
crl := createResp["crl"].(map[string]any)
id := crl["crlId"].(string)
assert.NotEmpty(t, id)
@@ -205,8 +207,8 @@ func TestHandler_CRL_DuplicateNameAllowed(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec2.Code)
var resp1, resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
c1 := resp1["crl"].(map[string]any)
c2 := resp2["crl"].(map[string]any)
diff --git a/services/rolesanywhere/handler_notification_settings.go b/services/rolesanywhere/handler_notification_settings.go
index 88d9266d9f..c79cf80f2d 100644
--- a/services/rolesanywhere/handler_notification_settings.go
+++ b/services/rolesanywhere/handler_notification_settings.go
@@ -2,8 +2,10 @@ package rolesanywhere
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- Notification settings handlers ----
@@ -14,7 +16,7 @@ func (h *Handler) handlePutNotificationSettings(ctx context.Context, body []byte
NotificationSettings []NotificationSetting `json:"notificationSettings"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
@@ -32,7 +34,7 @@ func (h *Handler) handleResetNotificationSettings(ctx context.Context, body []by
NotificationSettingKeys []NotificationSettingKey `json:"notificationSettingKeys"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
diff --git a/services/rolesanywhere/handler_notification_settings_test.go b/services/rolesanywhere/handler_notification_settings_test.go
index 3b517c5256..4767c8d4d0 100644
--- a/services/rolesanywhere/handler_notification_settings_test.go
+++ b/services/rolesanywhere/handler_notification_settings_test.go
@@ -2,7 +2,7 @@ package rolesanywhere_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- Notification Settings HTTP ----
@@ -38,7 +40,7 @@ func TestHandler_NotificationSettings_PutReset(t *testing.T) {
require.Equal(t, http.StatusCreated, recTA.Code)
var taResp map[string]any
- require.NoError(t, json.Unmarshal(recTA.Body.Bytes(), &taResp))
+ require.NoError(t, json.Unmarshal(recTA.Body.Bytes(), &taResp, jsonx.V1()))
ta := taResp["trustAnchor"].(map[string]any)
taID := ta["trustAnchorId"].(string)
@@ -52,7 +54,7 @@ func TestHandler_NotificationSettings_PutReset(t *testing.T) {
assert.Equal(t, tt.wantStatus, recPut.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(recPut.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(recPut.Body.Bytes(), &putResp, jsonx.V1()))
assert.Contains(t, putResp, "trustAnchor")
// Reset notification settings.
@@ -91,7 +93,7 @@ func TestHandler_NotificationSettings_ConfiguredBy(t *testing.T) {
require.Equal(t, http.StatusCreated, recTA.Code)
var taResp map[string]any
- require.NoError(t, json.Unmarshal(recTA.Body.Bytes(), &taResp))
+ require.NoError(t, json.Unmarshal(recTA.Body.Bytes(), &taResp, jsonx.V1()))
ta := taResp["trustAnchor"].(map[string]any)
taID := ta["trustAnchorId"].(string)
@@ -104,7 +106,7 @@ func TestHandler_NotificationSettings_ConfiguredBy(t *testing.T) {
require.Equal(t, http.StatusOK, recPut.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(recPut.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(recPut.Body.Bytes(), &putResp, jsonx.V1()))
updatedTA := putResp["trustAnchor"].(map[string]any)
settings := updatedTA["notificationSettings"].([]any)
diff --git a/services/rolesanywhere/handler_pagination_restart_test.go b/services/rolesanywhere/handler_pagination_restart_test.go
index e993867812..2788766812 100644
--- a/services/rolesanywhere/handler_pagination_restart_test.go
+++ b/services/rolesanywhere/handler_pagination_restart_test.go
@@ -1,12 +1,14 @@
package rolesanywhere_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_ListTrustAnchors_Pagination_DeletedMidPage proves that deleting
@@ -29,7 +31,7 @@ func TestHandler_ListTrustAnchors_Pagination_DeletedMidPage(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp1, jsonx.V1()))
items1, _ := resp1["trustAnchors"].([]any)
require.Len(t, items1, 2)
@@ -50,7 +52,7 @@ func TestHandler_ListTrustAnchors_Pagination_DeletedMidPage(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp2, jsonx.V1()))
items2, _ := resp2["trustAnchors"].([]any)
for _, item := range items2 {
@@ -90,7 +92,7 @@ func TestHandler_ListTrustAnchors_Pagination_TiedNamesTotalOrder(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["trustAnchors"].([]any)
for _, item := range items {
diff --git a/services/rolesanywhere/handler_profiles.go b/services/rolesanywhere/handler_profiles.go
index 86dd4071a0..7ccb59b026 100644
--- a/services/rolesanywhere/handler_profiles.go
+++ b/services/rolesanywhere/handler_profiles.go
@@ -2,9 +2,11 @@ package rolesanywhere
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- Profile handlers ----
@@ -22,7 +24,7 @@ func (h *Handler) handleCreateProfile(ctx context.Context, body []byte) (any, in
RequireInstanceProperties bool `json:"requireInstanceProperties"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
@@ -104,7 +106,7 @@ func (h *Handler) handleUpdateProfile(ctx context.Context, path string, body []b
ManagedPolicyArns []string `json:"managedPolicyArns"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
diff --git a/services/rolesanywhere/handler_profiles_test.go b/services/rolesanywhere/handler_profiles_test.go
index caeb1debbb..6bac1bbd8f 100644
--- a/services/rolesanywhere/handler_profiles_test.go
+++ b/services/rolesanywhere/handler_profiles_test.go
@@ -2,7 +2,7 @@ package rolesanywhere_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- Profile HTTP CRUD ----
@@ -43,7 +45,7 @@ func TestHandler_Profile_CRUD(t *testing.T) {
assert.Equal(t, tt.wantCreate, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
p := createResp["profile"].(map[string]any)
id := p["profileId"].(string)
assert.NotEmpty(t, id)
@@ -240,8 +242,8 @@ func TestHandler_Profile_DuplicateNameAllowed(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec2.Code)
var resp1, resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
p1 := resp1["profile"].(map[string]any)
p2 := resp2["profile"].(map[string]any)
@@ -284,7 +286,7 @@ func TestHandler_CreateProfile_EnabledFieldHonored(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
p := resp["profile"].(map[string]any)
assert.Equal(t, tt.wantEnabled, p["enabled"])
})
@@ -307,7 +309,7 @@ func TestHandler_Profile_AcceptRoleSessionName(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
p := resp["profile"].(map[string]any)
require.Equal(t, true, p["acceptRoleSessionName"])
@@ -319,7 +321,7 @@ func TestHandler_Profile_AcceptRoleSessionName(t *testing.T) {
require.Equal(t, http.StatusOK, recUpdate.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(recUpdate.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(recUpdate.Body.Bytes(), &updateResp, jsonx.V1()))
updated := updateResp["profile"].(map[string]any)
assert.NotContains(t, updated, "acceptRoleSessionName",
"false is the zero value and is omitted, matching requireInstanceProperties' existing convention")
@@ -339,7 +341,7 @@ func TestHandler_Profile_CreatedBy(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
p := resp["profile"].(map[string]any)
assert.Equal(t, "000000000000", p["createdBy"])
}
@@ -360,7 +362,7 @@ func TestHandler_Profile_NoTagsFieldOnResponse(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
p := resp["profile"].(map[string]any)
assert.NotContains(t, p, "tags")
}
@@ -388,7 +390,7 @@ func TestHandler_ListProfiles_PageSizeQueryParam(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["profiles"].([]any)
assert.Len(t, items, 1, "pageSize=1 must truncate the page to 1 item")
}
diff --git a/services/rolesanywhere/handler_tags.go b/services/rolesanywhere/handler_tags.go
index e6bb6da809..70f5745808 100644
--- a/services/rolesanywhere/handler_tags.go
+++ b/services/rolesanywhere/handler_tags.go
@@ -2,9 +2,11 @@ package rolesanywhere
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- Tag handlers ----
@@ -15,7 +17,7 @@ func (h *Handler) handleTagResource(ctx context.Context, body []byte) (any, int,
Tags []TagEntry `json:"tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
@@ -39,7 +41,7 @@ func (h *Handler) handleUntagResource(ctx context.Context, body []byte) (any, in
TagKeys []string `json:"tagKeys"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
diff --git a/services/rolesanywhere/handler_tags_test.go b/services/rolesanywhere/handler_tags_test.go
index 1e2b94c184..96ced2460e 100644
--- a/services/rolesanywhere/handler_tags_test.go
+++ b/services/rolesanywhere/handler_tags_test.go
@@ -2,7 +2,7 @@ package rolesanywhere_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rolesanywhere"
)
@@ -31,7 +32,7 @@ func createTestTrustAnchor(t *testing.T, h *rolesanywhere.Handler) string {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ta, ok := resp["trustAnchor"].(map[string]any)
require.True(t, ok, "response missing trustAnchor: %s", rec.Body.String())
@@ -76,7 +77,7 @@ func TestHandler_Tags_HTTP(t *testing.T) {
assert.Equal(t, tt.wantListStatus, recList.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listResp, jsonx.V1()))
tags := listResp["tags"].([]any)
assert.Len(t, tags, 2)
@@ -102,7 +103,7 @@ func TestHandler_Tags_HTTP(t *testing.T) {
"/ListTagsForResource?resourceArn="+resARN,
nil,
)
- require.NoError(t, json.Unmarshal(recList2.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(recList2.Body.Bytes(), &listResp, jsonx.V1()))
tags2 := listResp["tags"].([]any)
assert.Len(t, tags2, 1)
})
@@ -169,7 +170,7 @@ func TestHandler_Tags_UnknownResource(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
})
}
@@ -188,7 +189,7 @@ func TestHandler_Tags_EmptyList(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags := resp["tags"].([]any)
assert.Empty(t, tags)
}
diff --git a/services/rolesanywhere/handler_test.go b/services/rolesanywhere/handler_test.go
index aba4ab88cd..ab479a61eb 100644
--- a/services/rolesanywhere/handler_test.go
+++ b/services/rolesanywhere/handler_test.go
@@ -11,7 +11,7 @@ package rolesanywhere_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -20,6 +20,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rolesanywhere"
)
@@ -44,7 +45,7 @@ func doREST(
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -94,7 +95,7 @@ func TestHandler_Reset(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["trustAnchors"].([]any)
assert.Empty(t, items)
}
@@ -458,7 +459,7 @@ func TestHandler_CreateTrustAnchor_EnabledFalse(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ta := resp["trustAnchor"].(map[string]any)
assert.Equal(t, tt.wantEnabled, ta["enabled"])
})
@@ -476,14 +477,14 @@ func TestHandler_DeleteTrustAnchor_ReturnsResource(t *testing.T) {
require.Equal(t, http.StatusCreated, recCreate.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["trustAnchor"].(map[string]any)["trustAnchorId"].(string)
recDelete := doREST(t, h, http.MethodDelete, "/trustanchor/"+id, nil)
require.Equal(t, http.StatusOK, recDelete.Code)
var deleteResp map[string]any
- require.NoError(t, json.Unmarshal(recDelete.Body.Bytes(), &deleteResp))
+ require.NoError(t, json.Unmarshal(recDelete.Body.Bytes(), &deleteResp, jsonx.V1()))
ta, ok := deleteResp["trustAnchor"].(map[string]any)
require.True(t, ok,
"DeleteTrustAnchor response must carry the deleted trustAnchor, got: %s", recDelete.Body.String())
@@ -501,14 +502,14 @@ func TestHandler_DeleteProfile_ReturnsResource(t *testing.T) {
require.Equal(t, http.StatusCreated, recCreate.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["profile"].(map[string]any)["profileId"].(string)
recDelete := doREST(t, h, http.MethodDelete, "/profile/"+id, nil)
require.Equal(t, http.StatusOK, recDelete.Code)
var deleteResp map[string]any
- require.NoError(t, json.Unmarshal(recDelete.Body.Bytes(), &deleteResp))
+ require.NoError(t, json.Unmarshal(recDelete.Body.Bytes(), &deleteResp, jsonx.V1()))
p, ok := deleteResp["profile"].(map[string]any)
require.True(t, ok, "DeleteProfile response must carry the deleted profile, got: %s", recDelete.Body.String())
assert.Equal(t, id, p["profileId"])
@@ -538,7 +539,7 @@ func TestHandler_GetTrustAnchor_ShowsNotificationSettings(t *testing.T) {
require.Equal(t, http.StatusCreated, recCreate.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["trustAnchor"].(map[string]any)["trustAnchorId"].(string)
recPut := doREST(t, h, http.MethodPatch, "/put-notifications-settings", map[string]any{
@@ -556,7 +557,7 @@ func TestHandler_GetTrustAnchor_ShowsNotificationSettings(t *testing.T) {
require.Equal(t, http.StatusOK, recGet.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &getResp, jsonx.V1()))
ta := getResp["trustAnchor"].(map[string]any)
settings, ok := ta["notificationSettings"].([]any)
require.True(t, ok, "GetTrustAnchor response must carry notificationSettings, got: %s", recGet.Body.String())
@@ -567,7 +568,7 @@ func TestHandler_GetTrustAnchor_ShowsNotificationSettings(t *testing.T) {
require.Equal(t, http.StatusOK, recList.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listResp, jsonx.V1()))
items := listResp["trustAnchors"].([]any)
require.Len(t, items, 1)
listedTA := items[0].(map[string]any)
@@ -585,7 +586,7 @@ func TestHandler_GetProfile_ShowsAttributeMappings(t *testing.T) {
require.Equal(t, http.StatusCreated, recCreate.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["profile"].(map[string]any)["profileId"].(string)
recPut := doREST(t, h, http.MethodPut, "/profiles/"+id+"/mappings", map[string]any{
@@ -601,7 +602,7 @@ func TestHandler_GetProfile_ShowsAttributeMappings(t *testing.T) {
require.Equal(t, http.StatusOK, recGet.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &getResp, jsonx.V1()))
p := getResp["profile"].(map[string]any)
mappings, ok := p["attributeMappings"].([]any)
require.True(t, ok, "GetProfile response must carry attributeMappings, got: %s", recGet.Body.String())
@@ -612,7 +613,7 @@ func TestHandler_GetProfile_ShowsAttributeMappings(t *testing.T) {
require.Equal(t, http.StatusOK, recList.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listResp, jsonx.V1()))
items := listResp["profiles"].([]any)
require.Len(t, items, 1)
listedProfile := items[0].(map[string]any)
diff --git a/services/rolesanywhere/handler_trust_anchors.go b/services/rolesanywhere/handler_trust_anchors.go
index 1b7d9c1b4a..7df589eb13 100644
--- a/services/rolesanywhere/handler_trust_anchors.go
+++ b/services/rolesanywhere/handler_trust_anchors.go
@@ -2,9 +2,11 @@ package rolesanywhere
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- Trust Anchor handlers ----
@@ -18,7 +20,7 @@ func (h *Handler) handleCreateTrustAnchor(ctx context.Context, body []byte) (any
NotificationSettings []NotificationSetting `json:"notificationSettings"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
@@ -90,7 +92,7 @@ func (h *Handler) handleUpdateTrustAnchor(ctx context.Context, path string, body
Name string `json:"name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, 0, ErrValidation
}
diff --git a/services/rolesanywhere/handler_trust_anchors_test.go b/services/rolesanywhere/handler_trust_anchors_test.go
index a85dce9782..7c770115ba 100644
--- a/services/rolesanywhere/handler_trust_anchors_test.go
+++ b/services/rolesanywhere/handler_trust_anchors_test.go
@@ -2,7 +2,7 @@ package rolesanywhere_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- Trust Anchor HTTP CRUD ----
@@ -45,7 +47,7 @@ func TestHandler_TrustAnchor_CRUD(t *testing.T) {
assert.Equal(t, tt.wantCreate, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
ta := createResp["trustAnchor"].(map[string]any)
id := ta["trustAnchorId"].(string)
assert.NotEmpty(t, id)
@@ -220,8 +222,8 @@ func TestHandler_TrustAnchor_DuplicateNameAllowed(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec2.Code)
var resp1, resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
ta1 := resp1["trustAnchor"].(map[string]any)
ta2 := resp2["trustAnchor"].(map[string]any)
@@ -246,7 +248,7 @@ func TestHandler_TrustAnchor_NoTagsFieldOnResponse(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ta := resp["trustAnchor"].(map[string]any)
assert.NotContains(t, ta, "tags")
@@ -256,7 +258,7 @@ func TestHandler_TrustAnchor_NoTagsFieldOnResponse(t *testing.T) {
require.Equal(t, http.StatusOK, recTags.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(recTags.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(recTags.Body.Bytes(), &tagsResp, jsonx.V1()))
tags := tagsResp["tags"].([]any)
require.Len(t, tags, 1)
tag := tags[0].(map[string]any)
@@ -282,7 +284,7 @@ func TestHandler_CreateTrustAnchor_NotificationSettings(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ta := resp["trustAnchor"].(map[string]any)
settings, ok := ta["notificationSettings"].([]any)
@@ -323,7 +325,7 @@ func TestHandler_ListTrustAnchors_Pagination(t *testing.T) {
rec := doREST(t, h, http.MethodGet, "/trustanchors"+tt.query, nil)
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["trustAnchors"].([]any)
assert.Len(t, items, tt.wantCount)
})
diff --git a/services/rolesanywhere/persistence.go b/services/rolesanywhere/persistence.go
index c24c2d0828..005b2022d1 100644
--- a/services/rolesanywhere/persistence.go
+++ b/services/rolesanywhere/persistence.go
@@ -2,7 +2,7 @@ package rolesanywhere
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -72,7 +72,7 @@ func buildPersistenceDTORegistry() (
// maps left unconverted by Phase 3.3 (see store_setup.go) and are persisted
// here directly, as before.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string][]TagEntry `json:"tags"`
AttributeMappings map[string]map[string][]AttributeMapping `json:"attributeMappings"`
NotificationSettings map[string]map[string][]NotificationSetting `json:"notificationSettings"`
@@ -181,7 +181,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// profiles, crls, subjects) from tables via the ephemeral DTO registry,
// factored out of Restore to keep Restore's own cognitive complexity low.
// Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
dtoReg, taDTOs, profileDTOs, crlDTOs, subjectDTOs := buildPersistenceDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
return fmt.Errorf("rolesanywhere: restore snapshot resource tables: %w", err)
diff --git a/services/rolesanywhere/persistence_test.go b/services/rolesanywhere/persistence_test.go
index 1e86b77a2d..7d64e0c75a 100644
--- a/services/rolesanywhere/persistence_test.go
+++ b/services/rolesanywhere/persistence_test.go
@@ -5,11 +5,13 @@ package rolesanywhere //nolint:testpackage // needs unexported Subject.region ac
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestPersistence_SnapshotRestoreRoundTrip proves that a full Snapshot →
@@ -247,10 +249,10 @@ func TestPersistence_RestoreVersionMismatch(t *testing.T) {
require.NoError(t, err)
var snapMap map[string]any
- require.NoError(t, json.Unmarshal(donor.Snapshot(ctx), &snapMap))
+ require.NoError(t, json.Unmarshal(donor.Snapshot(ctx), &snapMap, jsonx.V1()))
tt.mutate(snapMap)
- mutatedSnap, err := json.Marshal(snapMap)
+ mutatedSnap, err := json.Marshal(snapMap, jsonx.V1())
require.NoError(t, err)
// The restore target starts with its own, different state so we
diff --git a/services/route53/persistence.go b/services/route53/persistence.go
index a2ff297afe..3767382c02 100644
--- a/services/route53/persistence.go
+++ b/services/route53/persistence.go
@@ -2,9 +2,11 @@ package route53
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -51,7 +53,7 @@ func zoneDataSnapshotKey(zs *zoneDataSnapshot) string { return zs.Zone.ID }
// newer) build of this backend as though it were the current shape; see
// Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
TrafficPolicies map[string][]*TrafficPolicy `json:"trafficPolicies,omitempty"`
VPCAssociations map[string][]vpcAssociation `json:"vpcAssociations,omitempty"`
VPCAssocAuthorizations map[string][]VPCAssociationAuthorization `json:"vpcAssocAuthorizations,omitempty"`
@@ -108,7 +110,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Tags: b.tags,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "route53 Snapshot failed", "error", err)
@@ -157,7 +159,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// silently produce zero-valued garbage (every entry keying to the same
// empty zone ID). zones is restored separately below via its own DTO
// registry, so it is excluded here.
- cleanTables := make(map[string]json.RawMessage, len(snap.Tables))
+ cleanTables := make(map[string]jsontext.Value, len(snap.Tables))
for name, raw := range snap.Tables {
if name == "zones" {
diff --git a/services/route53recoverycluster/misc_test.go b/services/route53recoverycluster/misc_test.go
index 127e6aee29..d9ac0c4011 100644
--- a/services/route53recoverycluster/misc_test.go
+++ b/services/route53recoverycluster/misc_test.go
@@ -1,7 +1,7 @@
package route53recoverycluster_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/sdkcheck"
cluster "github.com/blackbirdworks/gopherstack/services/route53recoverycluster"
)
@@ -96,7 +97,7 @@ func TestErrorBodyShape(t *testing.T) {
assert.GreaterOrEqual(t, rec.Code, http.StatusBadRequest)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.NotEmpty(t, body["message"])
assert.Equal(t, body["message"], body["Message"])
assert.False(
diff --git a/services/route53recoverycluster/ops.go b/services/route53recoverycluster/ops.go
index e9b0857b66..fad4437ed4 100644
--- a/services/route53recoverycluster/ops.go
+++ b/services/route53recoverycluster/ops.go
@@ -2,9 +2,10 @@ package route53recoverycluster
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/restroute"
cc "github.com/blackbirdworks/gopherstack/services/route53recoverycontrolconfig"
)
@@ -48,7 +49,7 @@ func (h *Handler) dispatch(_ context.Context, action, clusterID string, body []b
return nil, err
}
- return json.Marshal(out)
+ return json.Marshal(out, jsonx.V1())
}
func decode(body []byte, v any) error {
@@ -56,7 +57,7 @@ func decode(body []byte, v any) error {
return nil
}
- if err := json.Unmarshal(body, v); err != nil {
+ if err := json.Unmarshal(body, v, jsonx.V1()); err != nil {
return restroute.Validation("request body is not valid JSON for this operation")
}
diff --git a/services/route53recoverycontrolconfig/misc_test.go b/services/route53recoverycontrolconfig/misc_test.go
index 4be704046e..2bb4f6d4ca 100644
--- a/services/route53recoverycontrolconfig/misc_test.go
+++ b/services/route53recoverycontrolconfig/misc_test.go
@@ -2,7 +2,7 @@ package route53recoverycontrolconfig_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/sdkcheck"
cc "github.com/blackbirdworks/gopherstack/services/route53recoverycontrolconfig"
)
@@ -156,7 +157,7 @@ func TestErrorBodyShape(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Header().Get("X-Amzn-Errortype"))
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.NotEmpty(t, body["message"])
assert.Equal(t, body["message"], body["Message"])
assert.False(
diff --git a/services/route53recoverycontrolconfig/persistence.go b/services/route53recoverycontrolconfig/persistence.go
index 865a6b511d..853018e5ff 100644
--- a/services/route53recoverycontrolconfig/persistence.go
+++ b/services/route53recoverycontrolconfig/persistence.go
@@ -2,7 +2,7 @@ package route53recoverycontrolconfig
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
@@ -13,9 +13,9 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Tokens map[string]string `json:"tokens,omitempty"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Tokens map[string]string `json:"tokens,omitempty"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state. It implements persistence.Persistable.
diff --git a/services/route53recoveryreadiness/misc_test.go b/services/route53recoveryreadiness/misc_test.go
index 50b1aa7d2f..b77c0257b2 100644
--- a/services/route53recoveryreadiness/misc_test.go
+++ b/services/route53recoveryreadiness/misc_test.go
@@ -1,7 +1,7 @@
package route53recoveryreadiness_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/sdkcheck"
rr "github.com/blackbirdworks/gopherstack/services/route53recoveryreadiness"
)
@@ -174,7 +175,7 @@ func TestErrorBodyShape(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Header().Get("X-Amzn-Errortype"))
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.NotEmpty(t, body["message"])
assert.Equal(t, body["message"], body["Message"])
assert.False(t, strings.HasPrefix(body["message"], tt.wantCode))
diff --git a/services/route53recoveryreadiness/persistence.go b/services/route53recoveryreadiness/persistence.go
index 169f339698..403d50fe68 100644
--- a/services/route53recoveryreadiness/persistence.go
+++ b/services/route53recoveryreadiness/persistence.go
@@ -2,7 +2,7 @@ package route53recoveryreadiness
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
@@ -13,8 +13,8 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state. It implements persistence.Persistable.
diff --git a/services/route53resolver/configs_test.go b/services/route53resolver/configs_test.go
index fabb04c2c4..4257c9bd29 100644
--- a/services/route53resolver/configs_test.go
+++ b/services/route53resolver/configs_test.go
@@ -2,7 +2,7 @@ package route53resolver_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/route53resolver"
)
@@ -90,7 +91,7 @@ func TestResolverDnssecConfig_StatusValues(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cfg := resp["ResolverDNSSECConfig"].(map[string]any)
assert.Equal(t, tt.wantStatus, cfg["ValidationStatus"])
}
@@ -108,7 +109,7 @@ func TestFirewallConfig_NoArn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cfg := resp["FirewallConfig"].(map[string]any)
// AWS does not return Arn for FirewallConfig.
@@ -629,7 +630,7 @@ func TestListFirewallConfigs_Pagination(t *testing.T) {
listRec := doRequest(t, h, "ListFirewallConfigs", tt.body)
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
cfgs, _ := out["FirewallConfigs"].([]any)
assert.Len(t, cfgs, tt.wantLen)
if tt.wantNextToken {
@@ -681,7 +682,7 @@ func TestListResolverConfigs_Pagination(t *testing.T) {
listRec := doRequest(t, h, "ListResolverConfigs", tt.body)
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
cfgs, _ := out["ResolverConfigs"].([]any)
assert.Len(t, cfgs, tt.wantLen)
if tt.wantNextToken {
@@ -733,7 +734,7 @@ func TestListResolverDnssecConfigs_Pagination(t *testing.T) {
listRec := doRequest(t, h, "ListResolverDnssecConfigs", tt.body)
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
cfgs, _ := out["ResolverDnssecConfigs"].([]any)
assert.Len(t, cfgs, tt.wantLen)
if tt.wantNextToken {
@@ -764,7 +765,7 @@ func TestResolverDnssecConfig_ValidationField(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
cfg, ok := out["ResolverDNSSECConfig"].(map[string]any)
require.True(t, ok, "ResolverDNSSECConfig must be present")
diff --git a/services/route53resolver/dns_querylog.go b/services/route53resolver/dns_querylog.go
index e33858d796..de280ec157 100644
--- a/services/route53resolver/dns_querylog.go
+++ b/services/route53resolver/dns_querylog.go
@@ -2,13 +2,14 @@ package route53resolver
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net"
"strings"
"time"
"github.com/miekg/dns"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
cwlogsbackend "github.com/blackbirdworks/gopherstack/services/cloudwatchlogs"
)
@@ -152,7 +153,7 @@ func (b *InMemoryBackend) queryLogJSON(e queryLogEntry) string {
})
}
- out, _ := json.Marshal(rec)
+ out, _ := json.Marshal(rec, jsonx.V1())
return string(out)
}
diff --git a/services/route53resolver/dns_resolver_log_destinations_test.go b/services/route53resolver/dns_resolver_log_destinations_test.go
index fbd2b0a11b..a6ba049b1a 100644
--- a/services/route53resolver/dns_resolver_log_destinations_test.go
+++ b/services/route53resolver/dns_resolver_log_destinations_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"compress/gzip"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"regexp"
@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
)
@@ -147,7 +148,7 @@ func TestDNSResolverQueryLogS3Destination(t *testing.T) {
for _, l := range lines {
var rec map[string]any
- require.NoError(t, json.Unmarshal([]byte(l), &rec))
+ require.NoError(t, json.Unmarshal([]byte(l), &rec, jsonx.V1()))
assert.Equal(t, vpc1, rec["vpc_id"])
names = append(names, rec["query_name"].(string))
}
@@ -205,7 +206,7 @@ func TestDNSResolverQueryLogFirehoseDestination(t *testing.T) {
assert.True(t, bytes.HasSuffix(rec.data, []byte("\n")))
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.data, &body))
+ require.NoError(t, json.Unmarshal(rec.data, &body, jsonx.V1()))
assert.Equal(t, "q.example.", body["query_name"])
assert.Equal(t, vpc1, body["vpc_id"])
})
diff --git a/services/route53resolver/dns_resolver_log_test.go b/services/route53resolver/dns_resolver_log_test.go
index cb5f2f5a29..02523d24ab 100644
--- a/services/route53resolver/dns_resolver_log_test.go
+++ b/services/route53resolver/dns_resolver_log_test.go
@@ -2,7 +2,7 @@ package route53resolver_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
ec2backend "github.com/blackbirdworks/gopherstack/services/ec2"
"github.com/blackbirdworks/gopherstack/services/route53resolver"
)
@@ -117,7 +118,7 @@ func TestDNSResolverQueryLogging(t *testing.T) {
}
var rec map[string]any
- require.NoError(t, json.Unmarshal([]byte(msgs[0]), &rec))
+ require.NoError(t, json.Unmarshal([]byte(msgs[0]), &rec, jsonx.V1()))
assert.Equal(t, vpc1, rec["vpc_id"])
assert.Equal(t, "q.example.", rec["query_name"])
assert.Equal(t, "A", rec["query_type"])
diff --git a/services/route53resolver/firewall_domain_lists_test.go b/services/route53resolver/firewall_domain_lists_test.go
index 6c8d7373aa..5ca48a369b 100644
--- a/services/route53resolver/firewall_domain_lists_test.go
+++ b/services/route53resolver/firewall_domain_lists_test.go
@@ -1,13 +1,14 @@
package route53resolver_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/route53resolver"
)
@@ -22,7 +23,7 @@ func TestFirewallDomainList_ManagedOwnerName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dl := resp["FirewallDomainList"].(map[string]any)
// ManagedOwnerName should be empty for user-created domain lists.
@@ -78,7 +79,7 @@ func TestCreateFirewallDomainList(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dl, ok := resp["FirewallDomainList"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantName, dl["Name"])
@@ -108,7 +109,7 @@ func TestDeleteFirewallDomainList(t *testing.T) {
rec := doRequest(t, h, "CreateFirewallDomainList", map[string]any{"Name": "dl-del"})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["FirewallDomainList"].(map[string]any)["Id"].(string)
},
@@ -149,7 +150,7 @@ func TestDeleteFirewallDomainList(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dl, ok := resp["FirewallDomainList"].(map[string]any)
require.True(t, ok)
assert.Equal(t, id, dl["Id"])
@@ -170,7 +171,7 @@ func TestListFirewallDomains_Pagination(t *testing.T) {
dlRec := doRequest(t, h, "CreateFirewallDomainList", map[string]any{"Name": "dl-paginate"})
require.Equal(t, http.StatusOK, dlRec.Code)
var dlOut map[string]any
- require.NoError(t, json.Unmarshal(dlRec.Body.Bytes(), &dlOut))
+ require.NoError(t, json.Unmarshal(dlRec.Body.Bytes(), &dlOut, jsonx.V1()))
dlID := dlOut["FirewallDomainList"].(map[string]any)["Id"].(string)
domains := []string{"example.com", "foo.com", "bar.com", "baz.com"}
@@ -207,7 +208,7 @@ func TestListFirewallDomains_Pagination(t *testing.T) {
listRec := doRequest(t, h, "ListFirewallDomains", tt.body)
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
domList, _ := out["Domains"].([]any)
assert.Len(t, domList, tt.wantLen)
if tt.wantNextToken {
@@ -296,7 +297,7 @@ func TestListFirewallDomainLists_NoOverwideLeak(t *testing.T) {
var raw struct {
FirewallDomainLists []map[string]any `json:"FirewallDomainLists"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
require.Len(t, raw.FirewallDomainLists, 1)
item := raw.FirewallDomainLists[0]
diff --git a/services/route53resolver/firewall_rule_groups_test.go b/services/route53resolver/firewall_rule_groups_test.go
index 849495e23b..756f1e89c6 100644
--- a/services/route53resolver/firewall_rule_groups_test.go
+++ b/services/route53resolver/firewall_rule_groups_test.go
@@ -2,7 +2,7 @@ package route53resolver_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/route53resolver"
)
@@ -24,7 +25,7 @@ func TestFirewallRuleGroup_ShareStatusAndTimestamps(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rg := resp["FirewallRuleGroup"].(map[string]any)
assert.Equal(t, "NOT_SHARED", rg["ShareStatus"])
@@ -80,7 +81,7 @@ func TestFirewallRuleGroupAssociation_MutationProtection(t *testing.T) {
)
require.Equal(t, http.StatusOK, grpRec.Code)
var grpResp map[string]any
- require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp))
+ require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp, jsonx.V1()))
groupID := grpResp["FirewallRuleGroup"].(map[string]any)["Id"].(string)
assocBody := map[string]any{
@@ -98,7 +99,7 @@ func TestFirewallRuleGroupAssociation_MutationProtection(t *testing.T) {
if tt.wantAssocCode == http.StatusOK && tt.tryDisassociate {
var assocResp map[string]any
- require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp))
+ require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp, jsonx.V1()))
assocID := assocResp["FirewallRuleGroupAssociation"].(map[string]any)["Id"].(string)
disassocRec := doRequest(t, h, "DisassociateFirewallRuleGroup", map[string]any{
@@ -139,7 +140,7 @@ func TestUpdateFirewallRuleGroupAssociation_MutationProtection(t *testing.T) {
)
require.Equal(t, http.StatusOK, grpRec.Code)
var grpResp map[string]any
- require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp))
+ require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp, jsonx.V1()))
groupID := grpResp["FirewallRuleGroup"].(map[string]any)["Id"].(string)
assocRec := doRequest(t, h, "AssociateFirewallRuleGroup", map[string]any{
@@ -150,7 +151,7 @@ func TestUpdateFirewallRuleGroupAssociation_MutationProtection(t *testing.T) {
})
require.Equal(t, http.StatusOK, assocRec.Code)
var assocResp map[string]any
- require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp))
+ require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp, jsonx.V1()))
assocID := assocResp["FirewallRuleGroupAssociation"].(map[string]any)["Id"].(string)
rec := doRequest(t, h, "UpdateFirewallRuleGroupAssociation", map[string]any{
@@ -161,7 +162,7 @@ func TestUpdateFirewallRuleGroupAssociation_MutationProtection(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assoc := resp["FirewallRuleGroupAssociation"].(map[string]any)
assert.Equal(t, tt.mutationProtection, assoc["MutationProtection"])
}
@@ -184,7 +185,7 @@ func TestUpdateFirewallRuleGroupAssociation_RejectedMutationProtectionLeavesStat
grpRec := doRequest(t, h, "CreateFirewallRuleGroup", map[string]any{"Name": "upd-mp-partial-grp"})
require.Equal(t, http.StatusOK, grpRec.Code)
var grpResp map[string]any
- require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp))
+ require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp, jsonx.V1()))
groupID := grpResp["FirewallRuleGroup"].(map[string]any)["Id"].(string)
assocRec := doRequest(t, h, "AssociateFirewallRuleGroup", map[string]any{
@@ -195,7 +196,7 @@ func TestUpdateFirewallRuleGroupAssociation_RejectedMutationProtectionLeavesStat
})
require.Equal(t, http.StatusOK, assocRec.Code)
var assocResp map[string]any
- require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp))
+ require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp, jsonx.V1()))
assocID := assocResp["FirewallRuleGroupAssociation"].(map[string]any)["Id"].(string)
updateRec := doRequest(t, h, "UpdateFirewallRuleGroupAssociation", map[string]any{
@@ -211,7 +212,7 @@ func TestUpdateFirewallRuleGroupAssociation_RejectedMutationProtectionLeavesStat
})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assoc := getResp["FirewallRuleGroupAssociation"].(map[string]any)
assert.Equal(t, "original-name", assoc["Name"])
assert.InDelta(t, float64(100), assoc["Priority"], 0)
@@ -227,7 +228,7 @@ func TestFirewallRuleGroupAssociation_CreatorAndTimestamps(t *testing.T) {
grpRec := doRequest(t, h, "CreateFirewallRuleGroup", map[string]any{"Name": "assoc-ts-grp"})
require.Equal(t, http.StatusOK, grpRec.Code)
var grpResp map[string]any
- require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp))
+ require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp, jsonx.V1()))
groupID := grpResp["FirewallRuleGroup"].(map[string]any)["Id"].(string)
assocRec := doRequest(t, h, "AssociateFirewallRuleGroup", map[string]any{
@@ -240,7 +241,7 @@ func TestFirewallRuleGroupAssociation_CreatorAndTimestamps(t *testing.T) {
require.Equal(t, http.StatusOK, assocRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &resp, jsonx.V1()))
assoc := resp["FirewallRuleGroupAssociation"].(map[string]any)
assert.Equal(t, "req-assoc-ts-1", assoc["CreatorRequestId"])
@@ -314,7 +315,7 @@ func TestListFirewallRuleGroups_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups, _ := resp["FirewallRuleGroups"].([]any)
assert.Len(t, groups, tt.wantLen)
nextToken, _ := resp["NextToken"].(string)
@@ -362,7 +363,7 @@ func TestCreateFirewallRuleGroup(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
grp, ok := resp["FirewallRuleGroup"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantName, grp["Name"])
@@ -393,7 +394,7 @@ func TestAssociateFirewallRuleGroup(t *testing.T) {
rec := doRequest(t, h, "CreateFirewallRuleGroup", map[string]any{"Name": "grp-assoc"})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["FirewallRuleGroup"].(map[string]any)["Id"].(string)
},
@@ -449,7 +450,7 @@ func TestAssociateFirewallRuleGroup(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assoc, ok := resp["FirewallRuleGroupAssociation"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantStatus, assoc["Status"])
@@ -511,7 +512,7 @@ func TestListFirewallRuleGroupAssociations_Pagination(t *testing.T) {
})
require.Equal(t, http.StatusOK, grpRec.Code)
var grpOut map[string]any
- require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpOut))
+ require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpOut, jsonx.V1()))
grpID := grpOut["FirewallRuleGroup"].(map[string]any)["Id"].(string)
assocRec := doRequest(t, h, "AssociateFirewallRuleGroup", map[string]any{
@@ -548,7 +549,7 @@ func TestListFirewallRuleGroupAssociations_Pagination(t *testing.T) {
listRec := doRequest(t, h, "ListFirewallRuleGroupAssociations", tt.body)
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
assocs, _ := out["FirewallRuleGroupAssociations"].([]any)
assert.Len(t, assocs, tt.wantLen)
if tt.wantNextToken {
@@ -575,7 +576,7 @@ func TestListFirewallRuleGroupAssociations_PriorityStatusFilters(t *testing.T) {
})
require.Equal(t, http.StatusOK, grpRec.Code)
var grpOut map[string]any
- require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpOut))
+ require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpOut, jsonx.V1()))
grpID := grpOut["FirewallRuleGroup"].(map[string]any)["Id"].(string)
assocRec := doRequest(t, h, "AssociateFirewallRuleGroup", map[string]any{
@@ -603,7 +604,7 @@ func TestListFirewallRuleGroupAssociations_PriorityStatusFilters(t *testing.T) {
listRec := doRequest(t, h, "ListFirewallRuleGroupAssociations", tt.body)
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
assocs, _ := out["FirewallRuleGroupAssociations"].([]any)
assert.Len(t, assocs, tt.wantLen)
})
diff --git a/services/route53resolver/firewall_rules_test.go b/services/route53resolver/firewall_rules_test.go
index b59d118db8..6eb64cd7d8 100644
--- a/services/route53resolver/firewall_rules_test.go
+++ b/services/route53resolver/firewall_rules_test.go
@@ -2,7 +2,7 @@ package route53resolver_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/route53resolver"
)
@@ -135,7 +136,7 @@ func TestFirewallRule_BlockOverrideFields(t *testing.T) {
)
require.Equal(t, http.StatusOK, grpRec.Code)
var grpResp map[string]any
- require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp))
+ require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp, jsonx.V1()))
groupID := grpResp["FirewallRuleGroup"].(map[string]any)["Id"].(string)
dlRec := doRequest(
@@ -146,7 +147,7 @@ func TestFirewallRule_BlockOverrideFields(t *testing.T) {
)
require.Equal(t, http.StatusOK, dlRec.Code)
var dlResp map[string]any
- require.NoError(t, json.Unmarshal(dlRec.Body.Bytes(), &dlResp))
+ require.NoError(t, json.Unmarshal(dlRec.Body.Bytes(), &dlResp, jsonx.V1()))
dlID := dlResp["FirewallDomainList"].(map[string]any)["Id"].(string)
rec := doRequest(t, h, "CreateFirewallRule", tt.body(groupID, dlID))
@@ -154,7 +155,7 @@ func TestFirewallRule_BlockOverrideFields(t *testing.T) {
if tt.wantCode == http.StatusOK && tt.checkFn != nil {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rule := resp["FirewallRule"].(map[string]any)
tt.checkFn(t, rule)
}
@@ -215,13 +216,13 @@ func TestUpdateFirewallRule_ExtendedFields(t *testing.T) {
)
require.Equal(t, http.StatusOK, grpRec2.Code)
var grpR2 map[string]any
- require.NoError(t, json.Unmarshal(grpRec2.Body.Bytes(), &grpR2))
+ require.NoError(t, json.Unmarshal(grpRec2.Body.Bytes(), &grpR2, jsonx.V1()))
gID := grpR2["FirewallRuleGroup"].(map[string]any)["Id"].(string)
dlRec2 := doRequest(t, h2, "CreateFirewallDomainList", map[string]any{"Name": "upd-dl"})
require.Equal(t, http.StatusOK, dlRec2.Code)
var dlR2 map[string]any
- require.NoError(t, json.Unmarshal(dlRec2.Body.Bytes(), &dlR2))
+ require.NoError(t, json.Unmarshal(dlRec2.Body.Bytes(), &dlR2, jsonx.V1()))
dID := dlR2["FirewallDomainList"].(map[string]any)["Id"].(string)
createRec2 := doRequest(t, h2, "CreateFirewallRule", map[string]any{
@@ -240,7 +241,7 @@ func TestUpdateFirewallRule_ExtendedFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rule := resp["FirewallRule"].(map[string]any)
tt.checkFn(t, rule)
})
@@ -257,13 +258,13 @@ func TestFirewallRule_MetadataFields(t *testing.T) {
grpRec := doRequest(t, h, "CreateFirewallRuleGroup", map[string]any{"Name": "meta-grp"})
require.Equal(t, http.StatusOK, grpRec.Code)
var grpResp map[string]any
- require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp))
+ require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp, jsonx.V1()))
groupID := grpResp["FirewallRuleGroup"].(map[string]any)["Id"].(string)
dlRec := doRequest(t, h, "CreateFirewallDomainList", map[string]any{"Name": "meta-dl"})
require.Equal(t, http.StatusOK, dlRec.Code)
var dlResp map[string]any
- require.NoError(t, json.Unmarshal(dlRec.Body.Bytes(), &dlResp))
+ require.NoError(t, json.Unmarshal(dlRec.Body.Bytes(), &dlResp, jsonx.V1()))
dlID := dlResp["FirewallDomainList"].(map[string]any)["Id"].(string)
createRec := doRequest(t, h, "CreateFirewallRule", map[string]any{
@@ -277,7 +278,7 @@ func TestFirewallRule_MetadataFields(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &resp, jsonx.V1()))
rule := resp["FirewallRule"].(map[string]any)
assert.Equal(t, "req-fw-rule-1", rule["CreatorRequestId"])
@@ -347,7 +348,7 @@ func TestFirewallRule_BlockResponseStoredOnCreate(t *testing.T) {
)
require.Equal(t, http.StatusOK, grpRec.Code)
var grpResp map[string]any
- require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp))
+ require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp, jsonx.V1()))
groupID := grpResp["FirewallRuleGroup"].(map[string]any)["Id"].(string)
dlRec := doRequest(
@@ -358,7 +359,7 @@ func TestFirewallRule_BlockResponseStoredOnCreate(t *testing.T) {
)
require.Equal(t, http.StatusOK, dlRec.Code)
var dlResp map[string]any
- require.NoError(t, json.Unmarshal(dlRec.Body.Bytes(), &dlResp))
+ require.NoError(t, json.Unmarshal(dlRec.Body.Bytes(), &dlResp, jsonx.V1()))
dlID := dlResp["FirewallDomainList"].(map[string]any)["Id"].(string)
body := map[string]any{
@@ -377,7 +378,7 @@ func TestFirewallRule_BlockResponseStoredOnCreate(t *testing.T) {
if tt.wantCode == http.StatusOK && tt.blockResponse != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rule := resp["FirewallRule"].(map[string]any)
assert.Equal(t, tt.blockResponse, rule["BlockResponse"])
}
@@ -414,7 +415,7 @@ func TestCreateFirewallRule_ActionValidation(t *testing.T) {
require.Equal(t, http.StatusOK, grpRec.Code)
var grpResp map[string]any
- require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp))
+ require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp, jsonx.V1()))
grpID := grpResp["FirewallRuleGroup"].(map[string]any)["Id"].(string)
rec := doRequest(t, h, "CreateFirewallRule", map[string]any{
@@ -447,13 +448,13 @@ func TestCreateFirewallRule_NoIdOrArnInOutput(t *testing.T) {
require.Equal(t, http.StatusOK, grpRec.Code)
var grpResp map[string]any
- require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp))
+ require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp, jsonx.V1()))
grpID := grpResp["FirewallRuleGroup"].(map[string]any)["Id"].(string)
dlRec := doRequest(t, h, "CreateFirewallDomainList", map[string]any{"Name": "id-test-dl"})
require.Equal(t, http.StatusOK, dlRec.Code)
var dlResp map[string]any
- require.NoError(t, json.Unmarshal(dlRec.Body.Bytes(), &dlResp))
+ require.NoError(t, json.Unmarshal(dlRec.Body.Bytes(), &dlResp, jsonx.V1()))
dlID := dlResp["FirewallDomainList"].(map[string]any)["Id"].(string)
ruleRec := doRequest(t, h, "CreateFirewallRule", map[string]any{
@@ -466,7 +467,7 @@ func TestCreateFirewallRule_NoIdOrArnInOutput(t *testing.T) {
require.Equal(t, http.StatusOK, ruleRec.Code)
var ruleResp map[string]any
- require.NoError(t, json.Unmarshal(ruleRec.Body.Bytes(), &ruleResp))
+ require.NoError(t, json.Unmarshal(ruleRec.Body.Bytes(), &ruleResp, jsonx.V1()))
rule, ok := ruleResp["FirewallRule"].(map[string]any)
require.True(t, ok)
_, hasID := rule["Id"]
@@ -488,7 +489,7 @@ func TestCreateFirewallRule_IncrementsRuleCount(t *testing.T) {
require.Equal(t, http.StatusOK, grpRec.Code)
var grpResp map[string]any
- require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp))
+ require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp, jsonx.V1()))
grp := grpResp["FirewallRuleGroup"].(map[string]any)
grpID := grp["Id"].(string)
assert.InDelta(t, 0, grp["RuleCount"], 0)
@@ -526,13 +527,13 @@ func TestCreateFirewallRule(t *testing.T) {
grpRec := doRequest(t, h, "CreateFirewallRuleGroup", map[string]any{"Name": "grp-rule"})
require.Equal(t, http.StatusOK, grpRec.Code)
var grpResp map[string]any
- require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp))
+ require.NoError(t, json.Unmarshal(grpRec.Body.Bytes(), &grpResp, jsonx.V1()))
groupID := grpResp["FirewallRuleGroup"].(map[string]any)["Id"].(string)
dlRec := doRequest(t, h, "CreateFirewallDomainList", map[string]any{"Name": "dl-for-rule"})
require.Equal(t, http.StatusOK, dlRec.Code)
var dlResp map[string]any
- require.NoError(t, json.Unmarshal(dlRec.Body.Bytes(), &dlResp))
+ require.NoError(t, json.Unmarshal(dlRec.Body.Bytes(), &dlResp, jsonx.V1()))
dlID := dlResp["FirewallDomainList"].(map[string]any)["Id"].(string)
return groupID, dlID
@@ -600,7 +601,7 @@ func TestCreateFirewallRule(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rule, ok := resp["FirewallRule"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantAction, rule["Action"])
diff --git a/services/route53resolver/handler.go b/services/route53resolver/handler.go
index ecf37d0fa3..54743e838b 100644
--- a/services/route53resolver/handler.go
+++ b/services/route53resolver/handler.go
@@ -2,7 +2,7 @@ package route53resolver
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/collections"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -121,7 +122,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
return ""
}
var req extractResolverResourceInput
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return req.Name
}
@@ -153,14 +154,11 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
msg := wireMessage(err)
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, ErrNotFound):
payload := errorPayload("ResourceNotFoundException", msg)
@@ -188,7 +186,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
// FirewallRule ops model ValidationException (see ErrBatchValidation) --
// left untyped rather than guessing which family reached this path.
case errors.Is(err, errInvalidRequest), errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
return c.JSON(http.StatusBadRequest, map[string]string{keyMessageField: msg})
default:
// InternalServiceErrorException is modeled on all 72 operations
@@ -309,7 +307,7 @@ func updateSimpleConfig[TConfig, TOutput any](
// errorPayload carries the message under both casings: schema-driven SDK deserializers
// (route53resolver v1.53+) only match the modeled member name "Message".
func errorPayload(errType, msg string) []byte {
- payload, _ := json.Marshal(map[string]string{"__type": errType, "message": msg, "Message": msg})
+ payload, _ := json.Marshal(map[string]string{"__type": errType, "message": msg, "Message": msg}, jsonx.V1())
return payload
}
diff --git a/services/route53resolver/handler_error_type_internal_test.go b/services/route53resolver/handler_error_type_internal_test.go
index 9c8958e356..9925628592 100644
--- a/services/route53resolver/handler_error_type_internal_test.go
+++ b/services/route53resolver/handler_error_type_internal_test.go
@@ -2,7 +2,7 @@ package route53resolver
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -43,6 +44,6 @@ func TestHandleError_GenericErrorSurfacesInternalServiceErrorException(t *testin
assert.Equal(t, http.StatusInternalServerError, rec.Code)
var payload service.JSONErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload, jsonx.V1()))
assert.Equal(t, "InternalServiceErrorException", payload.Type)
}
diff --git a/services/route53resolver/handler_firewall_rules.go b/services/route53resolver/handler_firewall_rules.go
index eba373348d..2cfc82b74d 100644
--- a/services/route53resolver/handler_firewall_rules.go
+++ b/services/route53resolver/handler_firewall_rules.go
@@ -2,7 +2,7 @@ package route53resolver
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"errors"
"fmt"
"sort"
@@ -40,9 +40,9 @@ type dnsThreatProtectionRuleTypeConfigInput struct {
type firewallRuleTypeInput struct {
DNSThreatProtection *dnsThreatProtectionRuleTypeConfigInput `json:"DnsThreatProtection,omitempty"`
- FirewallAdvancedContentCategory json.RawMessage `json:"FirewallAdvancedContentCategory,omitempty"`
- FirewallAdvancedThreatCategory json.RawMessage `json:"FirewallAdvancedThreatCategory,omitempty"`
- PartnerThreatProtection json.RawMessage `json:"PartnerThreatProtection,omitempty"`
+ FirewallAdvancedContentCategory jsontext.Value `json:"FirewallAdvancedContentCategory,omitempty"`
+ FirewallAdvancedThreatCategory jsontext.Value `json:"FirewallAdvancedThreatCategory,omitempty"`
+ PartnerThreatProtection jsontext.Value `json:"PartnerThreatProtection,omitempty"`
}
// resolveFirewallRuleType validates a FirewallRuleType tagged union (exactly
diff --git a/services/route53resolver/handler_test.go b/services/route53resolver/handler_test.go
index 39ef2b21bd..ac3844ac73 100644
--- a/services/route53resolver/handler_test.go
+++ b/services/route53resolver/handler_test.go
@@ -2,7 +2,8 @@ package route53resolver_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/route53resolver"
)
@@ -34,7 +36,7 @@ func doRequest(
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -309,7 +311,7 @@ func TestHandlerReset(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
endpoints, ok := resp["ResolverEndpoints"].([]any)
require.True(t, ok)
assert.Empty(t, endpoints)
@@ -326,7 +328,7 @@ func decodeJSON(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&m))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &m, jsonx.V1()))
return m
}
diff --git a/services/route53resolver/outpost_resolvers_test.go b/services/route53resolver/outpost_resolvers_test.go
index 374137af65..987ccb105b 100644
--- a/services/route53resolver/outpost_resolvers_test.go
+++ b/services/route53resolver/outpost_resolvers_test.go
@@ -1,13 +1,15 @@
package route53resolver_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestOutpostResolverCRUD(t *testing.T) {
@@ -143,7 +145,7 @@ func TestCreateOutpostResolver_DefaultInstanceCount(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
resolver, ok := resp["OutpostResolver"].(map[string]any)
require.True(t, ok)
assert.InDelta(t, 4, resolver["InstanceCount"], 0)
@@ -213,7 +215,7 @@ func TestCreateOutpostResolver(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
r, ok := resp["OutpostResolver"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantName, r["Name"])
@@ -336,7 +338,7 @@ func TestListOutpostResolvers_Pagination(t *testing.T) {
listRec := doRequest(t, h, "ListOutpostResolvers", tt.body)
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
resolvers, _ := out["OutpostResolvers"].([]any)
assert.Len(t, resolvers, tt.wantLen)
if tt.wantNextToken {
@@ -389,7 +391,7 @@ func TestListOutpostResolvers_OutpostArnFilter(t *testing.T) {
listRec := doRequest(t, h, "ListOutpostResolvers", map[string]any{"OutpostArn": tt.outpostArn})
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
resolvers, _ := out["OutpostResolvers"].([]any)
assert.Len(t, resolvers, tt.wantLen)
})
diff --git a/services/route53resolver/persistence.go b/services/route53resolver/persistence.go
index f83005c724..2684925934 100644
--- a/services/route53resolver/persistence.go
+++ b/services/route53resolver/persistence.go
@@ -2,9 +2,11 @@ package route53resolver
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
@@ -21,7 +23,7 @@ import (
const route53resolverSnapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string][]svcTags.KV `json:"tags"`
FirewallRuleGroupPolicies map[string]map[string]string `json:"firewallRuleGroupPolicies"`
QueryLogConfigPolicies map[string]map[string]string `json:"queryLogConfigPolicies"`
@@ -59,7 +61,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.region,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "route53resolver: failed to snapshot backend", "error", err)
diff --git a/services/route53resolver/persistence_test.go b/services/route53resolver/persistence_test.go
index 084af0dcd5..55267d80e7 100644
--- a/services/route53resolver/persistence_test.go
+++ b/services/route53resolver/persistence_test.go
@@ -2,13 +2,14 @@ package route53resolver_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
svcTags "github.com/blackbirdworks/gopherstack/pkgs/tags"
"github.com/blackbirdworks/gopherstack/services/route53resolver"
)
@@ -322,7 +323,7 @@ func TestPersistenceRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, epRec.Code)
var epResp map[string]any
- require.NoError(t, json.Unmarshal(epRec.Body.Bytes(), &epResp))
+ require.NoError(t, json.Unmarshal(epRec.Body.Bytes(), &epResp, jsonx.V1()))
ep := epResp["ResolverEndpoint"].(map[string]any)
epARN := ep["Arn"].(string)
@@ -347,7 +348,7 @@ func TestPersistenceRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags := listResp["Tags"].([]any)
assert.Len(t, tags, 1)
}
diff --git a/services/route53resolver/query_log_associations_test.go b/services/route53resolver/query_log_associations_test.go
index 381741bac6..92ddda93bd 100644
--- a/services/route53resolver/query_log_associations_test.go
+++ b/services/route53resolver/query_log_associations_test.go
@@ -1,7 +1,7 @@
package route53resolver_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/route53resolver"
)
@@ -23,7 +24,7 @@ func TestResolverQueryLogConfigAssociation_Fields(t *testing.T) {
})
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
cfgID := createResp["ResolverQueryLogConfig"].(map[string]any)["Id"].(string)
assocRec := doRequest(t, h, "AssociateResolverQueryLogConfig", map[string]any{
@@ -33,7 +34,7 @@ func TestResolverQueryLogConfigAssociation_Fields(t *testing.T) {
require.Equal(t, http.StatusOK, assocRec.Code)
var assocResp map[string]any
- require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp))
+ require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp, jsonx.V1()))
assoc := assocResp["ResolverQueryLogConfigAssociation"].(map[string]any)
assert.NotEmpty(t, assoc["Id"])
@@ -163,7 +164,7 @@ func TestAssociateResolverQueryLogConfig(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["ResolverQueryLogConfig"].(map[string]any)["Id"].(string)
},
@@ -214,7 +215,7 @@ func TestAssociateResolverQueryLogConfig(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assoc, ok := resp["ResolverQueryLogConfigAssociation"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantStatus, assoc["Status"])
@@ -286,7 +287,7 @@ func TestListResolverQueryLogConfigAssociations_Pagination(t *testing.T) {
})
require.Equal(t, http.StatusOK, cfgRec.Code)
var cfgOut map[string]any
- require.NoError(t, json.Unmarshal(cfgRec.Body.Bytes(), &cfgOut))
+ require.NoError(t, json.Unmarshal(cfgRec.Body.Bytes(), &cfgOut, jsonx.V1()))
cfgID := cfgOut["ResolverQueryLogConfig"].(map[string]any)["Id"].(string)
for i := range 3 {
@@ -323,7 +324,7 @@ func TestListResolverQueryLogConfigAssociations_Pagination(t *testing.T) {
listRec := doRequest(t, h, "ListResolverQueryLogConfigAssociations", tt.body)
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
assocs, _ := out["ResolverQueryLogConfigAssociations"].([]any)
assert.Len(t, assocs, tt.wantLen)
if tt.wantNextToken {
@@ -348,7 +349,7 @@ func TestListResolverQueryLogConfigAssociations_Filters(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["ResolverQueryLogConfig"].(map[string]any)["Id"].(string)
}
@@ -413,7 +414,7 @@ func TestListResolverQueryLogConfigAssociations_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assocs, _ := resp["ResolverQueryLogConfigAssociations"].([]any)
gotResIDs := make([]string, len(assocs))
for i, a := range assocs {
@@ -567,6 +568,6 @@ func TestListResolverQueryLogConfigAssociations_UnknownFilterNameRejected(t *tes
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", resp["__type"])
}
diff --git a/services/route53resolver/query_log_configs_test.go b/services/route53resolver/query_log_configs_test.go
index 28387dbdbb..b02b5b6229 100644
--- a/services/route53resolver/query_log_configs_test.go
+++ b/services/route53resolver/query_log_configs_test.go
@@ -2,7 +2,7 @@ package route53resolver_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/route53resolver"
)
@@ -25,7 +26,7 @@ func TestResolverQueryLogConfig_AssociationCount(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
cfg := createResp["ResolverQueryLogConfig"].(map[string]any)
cfgID := cfg["Id"].(string)
assert.EqualValues(t, 0, cfg["AssociationCount"])
@@ -55,7 +56,7 @@ func TestResolverQueryLogConfig_AssociationCount(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
cfg = getResp["ResolverQueryLogConfig"].(map[string]any)
assert.EqualValues(t, 2, cfg["AssociationCount"])
}
@@ -133,7 +134,7 @@ func TestResolverQueryLogConfig_AssociationCountDecrement(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
cfgID := createResp["ResolverQueryLogConfig"].(map[string]any)["Id"].(string)
// Associate.
@@ -160,7 +161,7 @@ func TestResolverQueryLogConfig_AssociationCountDecrement(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
cfg := getResp["ResolverQueryLogConfig"].(map[string]any)
assert.EqualValues(t, 0, cfg["AssociationCount"])
}
@@ -410,7 +411,7 @@ func TestCreateResolverQueryLogConfig(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cfg, ok := resp["ResolverQueryLogConfig"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantName, cfg["Name"])
@@ -465,7 +466,7 @@ func TestListResolverQueryLogConfigs_Pagination(t *testing.T) {
listRec := doRequest(t, h, "ListResolverQueryLogConfigs", tt.body)
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
cfgs, _ := out["ResolverQueryLogConfigs"].([]any)
assert.Len(t, cfgs, tt.wantLen)
if tt.wantNextToken {
@@ -538,7 +539,7 @@ func TestListResolverQueryLogConfigs_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cfgs, _ := resp["ResolverQueryLogConfigs"].([]any)
gotNames := make([]string, len(cfgs))
for i, c := range cfgs {
@@ -698,6 +699,6 @@ func TestListResolverQueryLogConfigs_UnknownFilterNameRejected(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", resp["__type"])
}
diff --git a/services/route53resolver/resolver_endpoints_test.go b/services/route53resolver/resolver_endpoints_test.go
index 1cfe201b57..a449e75abf 100644
--- a/services/route53resolver/resolver_endpoints_test.go
+++ b/services/route53resolver/resolver_endpoints_test.go
@@ -2,7 +2,7 @@ package route53resolver_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/route53resolver"
)
@@ -77,7 +78,7 @@ func TestResolverEndpoint_IPv6IPAddress(t *testing.T) {
if tt.wantCode == http.StatusOK && tt.checkFn != nil {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ep := resp["ResolverEndpoint"].(map[string]any)
_, hasIPs := ep["IpAddresses"]
assert.False(t, hasIPs, "ResolverEndpoint response must not carry an IpAddresses field")
@@ -88,7 +89,7 @@ func TestResolverEndpoint_IPv6IPAddress(t *testing.T) {
})
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
ips, _ := listResp["IpAddresses"].([]any)
tt.checkFn(t, ips)
}
@@ -148,7 +149,7 @@ func TestResolverEndpoint_Protocols(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ep := resp["ResolverEndpoint"].(map[string]any)
assert.Equal(t, tt.wantProtocols, ep["Protocols"])
}
@@ -196,7 +197,7 @@ func TestResolverEndpoint_OutpostFields(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ep := resp["ResolverEndpoint"].(map[string]any)
if outpostArn, ok := tt.body["OutpostArn"]; ok {
@@ -222,7 +223,7 @@ func TestResolverEndpoint_CreatorRequestIdAndTimestamps(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ep := resp["ResolverEndpoint"].(map[string]any)
assert.Equal(t, "req-abc-123", ep["CreatorRequestId"])
@@ -265,7 +266,7 @@ func TestResolverEndpoint_TypeValidation(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ep := resp["ResolverEndpoint"].(map[string]any)
if tt.epType == "" {
assert.Equal(t, "IPV4", ep["ResolverEndpointType"])
@@ -334,7 +335,7 @@ func TestUpdateResolverEndpoint_Extended(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
epID := createResp["ResolverEndpoint"].(map[string]any)["Id"].(string)
tt.updateBody["ResolverEndpointId"] = epID
@@ -343,7 +344,7 @@ func TestUpdateResolverEndpoint_Extended(t *testing.T) {
if tt.wantCode == http.StatusOK && tt.checkFn != nil {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ep := resp["ResolverEndpoint"].(map[string]any)
tt.checkFn(t, ep)
}
@@ -367,7 +368,7 @@ func TestUpdateResolverEndpoint_RejectedTypeLeavesNameUnchanged(t *testing.T) {
})
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
epID := createResp["ResolverEndpoint"].(map[string]any)["Id"].(string)
updateRec := doRequest(t, h, "UpdateResolverEndpoint", map[string]any{
@@ -380,7 +381,7 @@ func TestUpdateResolverEndpoint_RejectedTypeLeavesNameUnchanged(t *testing.T) {
getRec := doRequest(t, h, "GetResolverEndpoint", map[string]any{"ResolverEndpointId": epID})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
ep := getResp["ResolverEndpoint"].(map[string]any)
assert.Equal(t, "orig-ep-partial", ep["Name"])
}
@@ -615,7 +616,7 @@ func TestAssociateResolverEndpointIpAddress_IPv6(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
epID := createResp["ResolverEndpoint"].(map[string]any)["Id"].(string)
assocRec := doRequest(t, h, "AssociateResolverEndpointIpAddress", map[string]any{
@@ -626,7 +627,7 @@ func TestAssociateResolverEndpointIpAddress_IPv6(t *testing.T) {
if tt.wantCode == http.StatusOK && tt.checkFn != nil {
var resp map[string]any
- require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &resp, jsonx.V1()))
ep := resp["ResolverEndpoint"].(map[string]any)
_, hasIPs := ep["IpAddresses"]
assert.False(t, hasIPs, "ResolverEndpoint response must not carry an IpAddresses field")
@@ -636,7 +637,7 @@ func TestAssociateResolverEndpointIpAddress_IPv6(t *testing.T) {
})
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
ips, _ := listResp["IpAddresses"].([]any)
tt.checkFn(t, ips)
}
@@ -985,7 +986,7 @@ func TestCreateResolverEndpoint_HostVPCIdAndSecurityGroups(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ep, ok := resp["ResolverEndpoint"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "vpc-abc123", ep["HostVPCId"])
@@ -1009,7 +1010,7 @@ func TestListResolverEndpoints_SortedByName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
endpoints, ok := resp["ResolverEndpoints"].([]any)
require.True(t, ok)
require.Len(t, endpoints, 3)
@@ -1044,7 +1045,7 @@ func TestCreateResolverEndpoint_WithTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ep := resp["ResolverEndpoint"].(map[string]any)
epARN := ep["Arn"].(string)
@@ -1052,7 +1053,7 @@ func TestCreateResolverEndpoint_WithTags(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags, ok := listResp["Tags"].([]any)
require.True(t, ok)
assert.Len(t, tags, 1)
@@ -1084,7 +1085,7 @@ func TestAssociateResolverEndpointIPAddress_Success(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
ep := createResp["ResolverEndpoint"].(map[string]any)
epID := ep["Id"].(string)
@@ -1098,7 +1099,7 @@ func TestAssociateResolverEndpointIPAddress_Success(t *testing.T) {
require.Equal(t, http.StatusOK, assocRec.Code)
var assocResp map[string]any
- require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp))
+ require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp, jsonx.V1()))
updatedEP := assocResp["ResolverEndpoint"].(map[string]any)
_, hasIPs := updatedEP["IpAddresses"]
assert.False(t, hasIPs, "ResolverEndpoint response must not carry an IpAddresses field")
@@ -1108,7 +1109,7 @@ func TestAssociateResolverEndpointIPAddress_Success(t *testing.T) {
})
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
ips, ok := listResp["IpAddresses"].([]any)
require.True(t, ok)
assert.Len(t, ips, 1)
@@ -1142,7 +1143,7 @@ func TestListResolverEndpoints_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
eps, _ := resp["ResolverEndpoints"].([]any)
assert.Len(t, eps, tt.wantLen)
nextToken, _ := resp["NextToken"].(string)
@@ -1166,7 +1167,7 @@ func TestListResolverEndpoints_NextTokenContinuation(t *testing.T) {
rec1 := doRequest(t, h, "ListResolverEndpoints", map[string]any{"MaxResults": 2})
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
eps1, _ := resp1["ResolverEndpoints"].([]any)
require.Len(t, eps1, 2)
nextToken, _ := resp1["NextToken"].(string)
@@ -1178,7 +1179,7 @@ func TestListResolverEndpoints_NextTokenContinuation(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
eps2, _ := resp2["ResolverEndpoints"].([]any)
assert.NotEmpty(t, eps2)
@@ -1227,7 +1228,7 @@ func TestDeleteResolverEndpoint_CascadeDeletesRules(t *testing.T) {
require.Equal(t, http.StatusOK, epRec.Code)
var epResp map[string]any
- require.NoError(t, json.Unmarshal(epRec.Body.Bytes(), &epResp))
+ require.NoError(t, json.Unmarshal(epRec.Body.Bytes(), &epResp, jsonx.V1()))
ep := epResp["ResolverEndpoint"].(map[string]any)
epID := ep["Id"].(string)
@@ -1254,7 +1255,7 @@ func TestDeleteResolverEndpoint_CascadeDeletesRules(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
rules := listResp["ResolverRules"].([]any)
assert.Len(t, rules, tt.wantRulesAfter)
})
@@ -1306,7 +1307,7 @@ func TestAssociateResolverEndpointIpAddress(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
endpointID = resp["ResolverEndpoint"].(map[string]any)["Id"].(string)
}
@@ -1318,7 +1319,7 @@ func TestAssociateResolverEndpointIpAddress(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ep, ok := resp["ResolverEndpoint"].(map[string]any)
require.True(t, ok)
_, hasIPs := ep["IpAddresses"]
@@ -1329,7 +1330,7 @@ func TestAssociateResolverEndpointIpAddress(t *testing.T) {
})
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
ips, _ := listResp["IpAddresses"].([]any)
assert.Len(t, ips, tt.wantIPCount)
}
@@ -1353,7 +1354,7 @@ func TestListResolverEndpointIpAddresses_Pagination(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
epID := createOut["ResolverEndpoint"].(map[string]any)["Id"].(string)
for i := 2; i <= 4; i++ {
@@ -1393,7 +1394,7 @@ func TestListResolverEndpointIpAddresses_Pagination(t *testing.T) {
listRec := doRequest(t, h, "ListResolverEndpointIpAddresses", tt.body)
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
ips, _ := out["IpAddresses"].([]any)
assert.Len(t, ips, tt.wantLen)
if tt.wantNextToken {
@@ -1419,7 +1420,7 @@ func TestCreateResolverEndpoint(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ep, ok := resp["ResolverEndpoint"].(map[string]any)
require.True(t, ok)
assert.Contains(t, ep["Arn"], "arn:aws:route53resolver:")
@@ -1437,7 +1438,7 @@ func TestGetResolverEndpoint(t *testing.T) {
"Direction": "OUTBOUND",
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
ep := createResp["ResolverEndpoint"].(map[string]any)
id := ep["Id"].(string)
@@ -1447,7 +1448,7 @@ func TestGetResolverEndpoint(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
got := resp["ResolverEndpoint"].(map[string]any)
assert.Equal(t, id, got["Id"])
}
@@ -1468,7 +1469,7 @@ func TestListResolverEndpoints(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
endpoints, ok := resp["ResolverEndpoints"].([]any)
require.True(t, ok)
assert.Len(t, endpoints, 2)
@@ -1545,7 +1546,7 @@ func TestListResolverEndpoints_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
eps, _ := resp["ResolverEndpoints"].([]any)
gotNames := make([]string, len(eps))
for i, e := range eps {
@@ -1576,7 +1577,7 @@ func TestListResolverEndpoints_FiltersANDedTogether(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
eps, _ := resp["ResolverEndpoints"].([]any)
require.Len(t, eps, 1)
assert.Equal(t, "in-vpc2", eps[0].(map[string]any)["Name"])
@@ -1596,7 +1597,7 @@ func TestListResolverEndpoints_UnknownFilterNameRejected(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", resp["__type"])
}
@@ -1609,7 +1610,7 @@ func TestDeleteResolverEndpoint(t *testing.T) {
"Direction": "INBOUND",
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
ep := createResp["ResolverEndpoint"].(map[string]any)
id := ep["Id"].(string)
diff --git a/services/route53resolver/resolver_rules_test.go b/services/route53resolver/resolver_rules_test.go
index e69b3934f1..9fc0729af6 100644
--- a/services/route53resolver/resolver_rules_test.go
+++ b/services/route53resolver/resolver_rules_test.go
@@ -2,13 +2,14 @@ package route53resolver_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/route53resolver"
)
@@ -69,7 +70,7 @@ func TestResolverRule_CreatorAndTimestamps(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rule := resp["ResolverRule"].(map[string]any)
assert.Equal(t, "req-rule-001", rule["CreatorRequestId"])
@@ -307,7 +308,7 @@ func TestUpdateResolverRule(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["ResolverRule"].(map[string]any)["Id"].(string)
},
@@ -445,7 +446,7 @@ func TestListResolverRules_SortedByName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rules, ok := resp["ResolverRules"].([]any)
require.True(t, ok)
require.Len(t, rules, 2)
@@ -469,7 +470,7 @@ func TestDeleteResolverRule_CascadesAssociations(t *testing.T) {
require.Equal(t, http.StatusOK, ruleRec.Code)
var ruleResp map[string]any
- require.NoError(t, json.Unmarshal(ruleRec.Body.Bytes(), &ruleResp))
+ require.NoError(t, json.Unmarshal(ruleRec.Body.Bytes(), &ruleResp, jsonx.V1()))
ruleID := ruleResp["ResolverRule"].(map[string]any)["Id"].(string)
// Associate with a VPC.
@@ -531,7 +532,7 @@ func TestListResolverRules_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rules, _ := resp["ResolverRules"].([]any)
assert.Len(t, rules, tt.wantLen)
nextToken, _ := resp["NextToken"].(string)
@@ -585,7 +586,7 @@ func TestCreateResolverRule_EndpointValidation(t *testing.T) {
require.Equal(t, http.StatusOK, epRec.Code)
var epResp map[string]any
- require.NoError(t, json.Unmarshal(epRec.Body.Bytes(), &epResp))
+ require.NoError(t, json.Unmarshal(epRec.Body.Bytes(), &epResp, jsonx.V1()))
ep := epResp["ResolverEndpoint"].(map[string]any)
endpointID = ep["Id"].(string)
}
@@ -615,7 +616,7 @@ func TestCreateResolverRule(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rule, ok := resp["ResolverRule"].(map[string]any)
require.True(t, ok)
assert.Contains(t, rule["Arn"], "arn:aws:route53resolver:")
@@ -681,7 +682,7 @@ func TestListResolverRules(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rules, ok := resp["ResolverRules"].([]any)
require.True(t, ok)
assert.Len(t, rules, 2)
@@ -748,7 +749,7 @@ func TestListResolverRules_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rules, _ := resp["ResolverRules"].([]any)
gotNames := make([]string, len(rules))
for i, r := range rules {
@@ -775,7 +776,7 @@ func TestListResolverRules_UnknownFilterNameRejected(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", resp["__type"])
}
@@ -790,7 +791,7 @@ func TestDeleteResolverRule(t *testing.T) {
"TargetIps": []map[string]any{{"Ip": "10.0.0.1", "Port": 53}},
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
rule := createResp["ResolverRule"].(map[string]any)
id := rule["Id"].(string)
@@ -811,7 +812,7 @@ func TestGetResolverRule(t *testing.T) {
"TargetIps": []map[string]any{{"Ip": "10.0.0.1", "Port": 53}},
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
rule := createResp["ResolverRule"].(map[string]any)
id := rule["Id"].(string)
@@ -821,7 +822,7 @@ func TestGetResolverRule(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
got, ok := resp["ResolverRule"].(map[string]any)
require.True(t, ok)
assert.Equal(t, id, got["Id"])
diff --git a/services/route53resolver/rule_associations_test.go b/services/route53resolver/rule_associations_test.go
index c06ab7ed85..ad6399c3c6 100644
--- a/services/route53resolver/rule_associations_test.go
+++ b/services/route53resolver/rule_associations_test.go
@@ -1,7 +1,7 @@
package route53resolver_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/route53resolver"
)
@@ -174,7 +175,7 @@ func TestAssociateResolverRule(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["ResolverRule"].(map[string]any)["Id"].(string)
},
@@ -226,7 +227,7 @@ func TestAssociateResolverRule(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assoc, ok := resp["ResolverRuleAssociation"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantStatus, assoc["Status"])
@@ -254,7 +255,7 @@ func TestListResolverRuleAssociations_Pagination(t *testing.T) {
})
require.Equal(t, http.StatusOK, ruleRec.Code)
var ruleOut map[string]any
- require.NoError(t, json.Unmarshal(ruleRec.Body.Bytes(), &ruleOut))
+ require.NoError(t, json.Unmarshal(ruleRec.Body.Bytes(), &ruleOut, jsonx.V1()))
ruleID := ruleOut["ResolverRule"].(map[string]any)["Id"].(string)
for i := range 3 {
@@ -292,7 +293,7 @@ func TestListResolverRuleAssociations_Pagination(t *testing.T) {
listRec := doRequest(t, h, "ListResolverRuleAssociations", tt.body)
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
assocs, _ := out["ResolverRuleAssociations"].([]any)
assert.Len(t, assocs, tt.wantLen)
if tt.wantNextToken {
@@ -318,7 +319,7 @@ func TestListResolverRuleAssociations_Filters(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["ResolverRule"].(map[string]any)["Id"].(string)
}
@@ -390,7 +391,7 @@ func TestListResolverRuleAssociations_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assocs, _ := resp["ResolverRuleAssociations"].([]any)
gotNames := make([]string, len(assocs))
for i, a := range assocs {
@@ -413,6 +414,6 @@ func TestListResolverRuleAssociations_UnknownFilterNameRejected(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", resp["__type"])
}
diff --git a/services/route53resolver/store_test.go b/services/route53resolver/store_test.go
index 7ad5af5bda..f78decb005 100644
--- a/services/route53resolver/store_test.go
+++ b/services/route53resolver/store_test.go
@@ -2,7 +2,7 @@ package route53resolver_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"sync"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/route53resolver"
)
@@ -29,7 +30,7 @@ func TestFullCRUDLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, epRec.Code)
var epResp map[string]any
- require.NoError(t, json.Unmarshal(epRec.Body.Bytes(), &epResp))
+ require.NoError(t, json.Unmarshal(epRec.Body.Bytes(), &epResp, jsonx.V1()))
ep := epResp["ResolverEndpoint"].(map[string]any)
epID := ep["Id"].(string)
assert.NotEmpty(t, ep["CreationTime"])
@@ -48,7 +49,7 @@ func TestFullCRUDLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, ruleRec.Code)
var ruleResp map[string]any
- require.NoError(t, json.Unmarshal(ruleRec.Body.Bytes(), &ruleResp))
+ require.NoError(t, json.Unmarshal(ruleRec.Body.Bytes(), &ruleResp, jsonx.V1()))
rule := ruleResp["ResolverRule"].(map[string]any)
ruleID := rule["Id"].(string)
assert.Equal(t, "req-rule-lifecycle", rule["CreatorRequestId"])
@@ -61,7 +62,7 @@ func TestFullCRUDLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, qlcRec.Code)
var qlcResp map[string]any
- require.NoError(t, json.Unmarshal(qlcRec.Body.Bytes(), &qlcResp))
+ require.NoError(t, json.Unmarshal(qlcRec.Body.Bytes(), &qlcResp, jsonx.V1()))
qlc := qlcResp["ResolverQueryLogConfig"].(map[string]any)
qlcID := qlc["Id"].(string)
assert.EqualValues(t, 0, qlc["AssociationCount"])
@@ -79,7 +80,7 @@ func TestFullCRUDLifecycle(t *testing.T) {
)
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.EqualValues(t, 1, getResp["ResolverQueryLogConfig"].(map[string]any)["AssociationCount"])
// 5. Firewall rule group with timestamps.
@@ -89,7 +90,7 @@ func TestFullCRUDLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, frgRec.Code)
var frgResp map[string]any
- require.NoError(t, json.Unmarshal(frgRec.Body.Bytes(), &frgResp))
+ require.NoError(t, json.Unmarshal(frgRec.Body.Bytes(), &frgResp, jsonx.V1()))
frg := frgResp["FirewallRuleGroup"].(map[string]any)
frgID := frg["Id"].(string)
assert.Equal(t, "NOT_SHARED", frg["ShareStatus"])
diff --git a/services/route53resolver/tags_test.go b/services/route53resolver/tags_test.go
index 339a806bab..ff85d1dbbc 100644
--- a/services/route53resolver/tags_test.go
+++ b/services/route53resolver/tags_test.go
@@ -1,12 +1,14 @@
package route53resolver_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestTagResource(t *testing.T) {
@@ -72,7 +74,7 @@ func TestListTagsForResource(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
ep := createResp["ResolverEndpoint"].(map[string]any)
epARN := ep["Arn"].(string)
@@ -90,7 +92,7 @@ func TestListTagsForResource(t *testing.T) {
assert.Equal(t, tt.wantCode, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
tags := resp["Tags"].([]any)
assert.Len(t, tags, tt.wantTags)
})
@@ -115,7 +117,7 @@ func TestListTagsForResource_Pagination(t *testing.T) {
})
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
epARN := createResp["ResolverEndpoint"].(map[string]any)["Arn"].(string)
tagRec := doRequest(t, h, "TagResource", map[string]any{
@@ -134,7 +136,7 @@ func TestListTagsForResource_Pagination(t *testing.T) {
})
require.Equal(t, http.StatusOK, page1Rec.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(page1Rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(page1Rec.Body.Bytes(), &page1, jsonx.V1()))
page1Tags := page1["Tags"].([]any)
assert.Len(t, page1Tags, 2)
nextToken, ok := page1["NextToken"].(string)
@@ -148,7 +150,7 @@ func TestListTagsForResource_Pagination(t *testing.T) {
})
require.Equal(t, http.StatusOK, page2Rec.Code)
var page2 map[string]any
- require.NoError(t, json.Unmarshal(page2Rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(page2Rec.Body.Bytes(), &page2, jsonx.V1()))
page2Tags := page2["Tags"].([]any)
assert.Len(t, page2Tags, 1)
_, hasNext := page2["NextToken"]
@@ -192,7 +194,7 @@ func TestUntagResource(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
ep := createResp["ResolverEndpoint"].(map[string]any)
epARN := ep["Arn"].(string)
@@ -220,7 +222,7 @@ func TestUntagResource(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
tags := resp["Tags"].([]any)
assert.Len(t, tags, tt.wantTags)
})
diff --git a/services/rum/events.go b/services/rum/events.go
index 88d70ec2fd..c0c3af5ef8 100644
--- a/services/rum/events.go
+++ b/services/rum/events.go
@@ -3,12 +3,14 @@ package rum
import (
"cmp"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"slices"
"strconv"
"github.com/blackbirdworks/gopherstack/pkgs/cwmetric"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/restroute"
@@ -79,11 +81,11 @@ func validateEvents(in *PutEventsInput) error {
return invalid("RumEvents[%d] requires id, type and a timestamp", i)
}
- if !json.Valid([]byte(e.Details)) {
+ if !jsontext.Value([]byte(e.Details)).IsValid(jsonx.V1()) {
return invalid("RumEvents[%d].details must be valid JSON", i)
}
- if e.Metadata != "" && !json.Valid([]byte(e.Metadata)) {
+ if e.Metadata != "" && !jsontext.Value([]byte(e.Metadata)).IsValid(jsonx.V1()) {
return invalid("RumEvents[%d].metadata must be valid JSON", i)
}
}
@@ -98,7 +100,7 @@ func decodeJSON(s string) any {
var v any
- _ = json.Unmarshal([]byte(s), &v)
+ _ = json.Unmarshal([]byte(s), &v, jsonx.V1())
return v
}
@@ -202,7 +204,7 @@ func (b *InMemoryBackend) storeEvents(in *PutEventsInput) (delivery, error) {
doc := eventDoc(m, in, e)
- raw, merr := json.Marshal(doc)
+ raw, merr := json.Marshal(doc, jsonx.V1())
if merr != nil {
return delivery{}, internalError("could not encode event %s", e.ID)
}
diff --git a/services/rum/metrics.go b/services/rum/metrics.go
index c19985d6b6..15e5fea410 100644
--- a/services/rum/metrics.go
+++ b/services/rum/metrics.go
@@ -3,13 +3,14 @@ package rum
import (
"cmp"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"slices"
"strings"
"github.com/google/uuid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/restroute"
)
@@ -184,7 +185,7 @@ func (b *InMemoryBackend) ListRumMetricsDestinations(
func jsonObjectOf(s string) (map[string]any, error) {
var out map[string]any
- if err := json.Unmarshal([]byte(s), &out); err != nil || out == nil {
+ if err := json.Unmarshal([]byte(s), &out, jsonx.V1()); err != nil || out == nil {
return nil, invalid("EventPattern must be a JSON object")
}
diff --git a/services/rum/pattern.go b/services/rum/pattern.go
index dcbf6f5110..c9da1400e6 100644
--- a/services/rum/pattern.go
+++ b/services/rum/pattern.go
@@ -1,13 +1,14 @@
package rum
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"slices"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/eventpattern"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// matchPattern reports whether doc satisfies an EventBridge-style event pattern.
@@ -176,7 +177,7 @@ func pathString(doc map[string]any, path string) (string, bool) {
func parseObject(s string) (map[string]any, error) {
var out map[string]any
- if err := json.Unmarshal([]byte(s), &out); err != nil || out == nil {
+ if err := json.Unmarshal([]byte(s), &out, jsonx.V1()); err != nil || out == nil {
return nil, fmt.Errorf("%w: %q", errNotObject, s)
}
diff --git a/services/rum/pattern_test.go b/services/rum/pattern_test.go
index 3692f0295b..e3569071f0 100644
--- a/services/rum/pattern_test.go
+++ b/services/rum/pattern_test.go
@@ -1,12 +1,13 @@
package rum_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/rum"
)
@@ -42,14 +43,14 @@ func TestMatchPattern(t *testing.T) {
}
var doc map[string]any
- require.NoError(t, json.Unmarshal([]byte(event), &doc))
+ require.NoError(t, json.Unmarshal([]byte(event), &doc, jsonx.V1()))
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
var pattern map[string]any
- require.NoError(t, json.Unmarshal([]byte(tc.pattern), &pattern))
+ require.NoError(t, json.Unmarshal([]byte(tc.pattern), &pattern, jsonx.V1()))
assert.Equal(t, tc.want, rum.MatchPattern(pattern, doc))
})
}
diff --git a/services/rum/persistence.go b/services/rum/persistence.go
index a44c7f3227..0d0e27f1e3 100644
--- a/services/rum/persistence.go
+++ b/services/rum/persistence.go
@@ -2,7 +2,7 @@ package rum
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,11 +12,11 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Seq int64 `json:"seq"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Seq int64 `json:"seq"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/rum/policies.go b/services/rum/policies.go
index 6af42d3703..e62550a6a8 100644
--- a/services/rum/policies.go
+++ b/services/rum/policies.go
@@ -2,9 +2,11 @@ package rum
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// PutResourcePolicy attaches or replaces the resource policy of an app monitor.
@@ -16,10 +18,10 @@ func (b *InMemoryBackend) PutResourcePolicy(
}
var parsed struct {
- Statement json.RawMessage `json:"Statement"`
+ Statement jsontext.Value `json:"Statement"`
}
- if doc == "" || json.Unmarshal([]byte(doc), &parsed) != nil || len(parsed.Statement) == 0 {
+ if doc == "" || json.Unmarshal([]byte(doc), &parsed, jsonx.V1()) != nil || len(parsed.Statement) == 0 {
return "", "", malformedPolicy("PolicyDocument must be a JSON policy with a Statement")
}
diff --git a/services/rum/realclient_helpers_test.go b/services/rum/realclient_helpers_test.go
index 8feffb1fdc..d1b3d58bce 100644
--- a/services/rum/realclient_helpers_test.go
+++ b/services/rum/realclient_helpers_test.go
@@ -2,7 +2,7 @@ package rum_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net"
"net/http"
@@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/cwmetric"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/sdktest"
"github.com/blackbirdworks/gopherstack/services/rum"
)
@@ -250,7 +251,7 @@ func mustJSON(t *testing.T, s string) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal([]byte(s), &m))
+ require.NoError(t, json.Unmarshal([]byte(s), &m, jsonx.V1()))
return m
}
diff --git a/services/s3/authz.go b/services/s3/authz.go
index 7787606535..6082f8ccac 100644
--- a/services/s3/authz.go
+++ b/services/s3/authz.go
@@ -2,11 +2,12 @@ package s3
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Data-plane access control. This file evaluates the stored bucket policy + ACL
@@ -109,7 +110,7 @@ func (h *S3Handler) evaluateBucketPolicy(
}
var doc bucketPolicyDocument
- if uErr := json.Unmarshal([]byte(arn.Canonical(policyJSON)), &doc); uErr != nil {
+ if uErr := json.Unmarshal([]byte(arn.Canonical(policyJSON)), &doc, jsonx.V1()); uErr != nil {
return effectDefault
}
diff --git a/services/s3/bucket_ops_acl_policy.go b/services/s3/bucket_ops_acl_policy.go
index f4418ed9b6..06f2d2662f 100644
--- a/services/s3/bucket_ops_acl_policy.go
+++ b/services/s3/bucket_ops_acl_policy.go
@@ -2,7 +2,8 @@ package s3
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"encoding/xml"
"errors"
"net/http"
@@ -11,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// validCannedACLs is the complete set of canned ACL strings that AWS S3 accepts
@@ -153,7 +155,7 @@ func (h *S3Handler) putBucketPolicy(
// Real S3 rejects a policy body that isn't valid JSON with 400
// MalformedPolicy before ever persisting it.
- if !json.Valid(body) {
+ if !jsontext.Value(body).IsValid(jsonx.V1()) {
WriteError(ctx, w, r, ErrMalformedPolicy)
return
@@ -501,7 +503,7 @@ func (h *S3Handler) handleGetBucketPolicyStatus(
// contains an Allow statement that grants s3:GetObject to the wildcard principal "*".
func policyGrantsPublicGetObject(policyJSON string) bool {
var doc policyDoc
- if err := json.Unmarshal([]byte(policyJSON), &doc); err != nil {
+ if err := json.Unmarshal([]byte(policyJSON), &doc, jsonx.V1()); err != nil {
return false
}
diff --git a/services/s3/bucket_policy_validation.go b/services/s3/bucket_policy_validation.go
index 27ebc6ecc2..90c19f72f3 100644
--- a/services/s3/bucket_policy_validation.go
+++ b/services/s3/bucket_policy_validation.go
@@ -2,7 +2,11 @@ package s3
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
+
+ "encoding/json/jsontext"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// validateBucketPolicyDocument checks a PutBucketPolicy request body against the
@@ -20,8 +24,8 @@ import (
// else *ErrorResponse{Code: "MalformedPolicy"} describing the first violation
// (real S3's fail-fast behavior).
func validateBucketPolicyDocument(body []byte) *ErrorResponse {
- var top map[string]json.RawMessage
- if err := json.Unmarshal(body, &top); err != nil {
+ var top map[string]jsontext.Value
+ if err := json.Unmarshal(body, &top, jsonx.V1()); err != nil {
// json.Valid() already gated non-JSON bodies before this is called;
// this branch covers valid-JSON-but-not-an-object bodies (e.g. a bare
// JSON array, string, or number), which real S3 also rejects.
@@ -61,14 +65,14 @@ func validateBucketPolicyDocument(body []byte) *ErrorResponse {
// validatePolicyVersion validates the optional top-level Version element. AWS
// accepts a policy with no Version at all (version_block is marked `?` in the
// grammar), but if present it must be exactly "2008-10-17" or "2012-10-17".
-func validatePolicyVersion(top map[string]json.RawMessage) *ErrorResponse {
+func validatePolicyVersion(top map[string]jsontext.Value) *ErrorResponse {
raw, ok := top["Version"]
if !ok {
return nil
}
var version string
- if err := json.Unmarshal(raw, &version); err != nil {
+ if err := json.Unmarshal(raw, &version, jsonx.V1()); err != nil {
return &ErrorResponse{
Code: errMalformedPolicy,
Message: "The policy must contain a valid version string.",
@@ -88,7 +92,7 @@ func validatePolicyVersion(top map[string]json.RawMessage) *ErrorResponse {
// decodePolicyStatements decodes the "Statement" value, which per the IAM
// grammar convention may be either a single statement object or a JSON array
// of statement objects.
-func decodePolicyStatements(raw json.RawMessage) ([]map[string]json.RawMessage, *ErrorResponse) {
+func decodePolicyStatements(raw jsontext.Value) ([]map[string]jsontext.Value, *ErrorResponse) {
trimmed := bytes.TrimSpace(raw)
shapeErr := &ErrorResponse{
Code: errMalformedPolicy,
@@ -100,27 +104,27 @@ func decodePolicyStatements(raw json.RawMessage) ([]map[string]json.RawMessage,
}
if trimmed[0] == '[' {
- var arr []map[string]json.RawMessage
- if err := json.Unmarshal(trimmed, &arr); err != nil {
+ var arr []map[string]jsontext.Value
+ if err := json.Unmarshal(trimmed, &arr, jsonx.V1()); err != nil {
return nil, shapeErr
}
return arr, nil
}
- var single map[string]json.RawMessage
- if err := json.Unmarshal(trimmed, &single); err != nil {
+ var single map[string]jsontext.Value
+ if err := json.Unmarshal(trimmed, &single, jsonx.V1()); err != nil {
return nil, shapeErr
}
- return []map[string]json.RawMessage{single}, nil
+ return []map[string]jsontext.Value{single}, nil
}
// validatePolicyStatementShape validates a single statement object against
// the required grammar elements for a resource-based (bucket)
// policy: Effect, Principal/NotPrincipal, Action/NotAction, and
// Resource/NotResource.
-func validatePolicyStatementShape(stmt map[string]json.RawMessage) *ErrorResponse {
+func validatePolicyStatementShape(stmt map[string]jsontext.Value) *ErrorResponse {
if effectErr := validatePolicyEffect(stmt); effectErr != nil {
return effectErr
}
@@ -150,14 +154,14 @@ func validatePolicyStatementShape(stmt map[string]json.RawMessage) *ErrorRespons
}
// validatePolicyEffect validates the required Effect element of a statement.
-func validatePolicyEffect(stmt map[string]json.RawMessage) *ErrorResponse {
+func validatePolicyEffect(stmt map[string]jsontext.Value) *ErrorResponse {
effectRaw, ok := stmt["Effect"]
if !ok {
return &ErrorResponse{Code: errMalformedPolicy, Message: "Missing required field Effect"}
}
var effect string
- if err := json.Unmarshal(effectRaw, &effect); err != nil || (effect != "Allow" && effect != "Deny") {
+ if err := json.Unmarshal(effectRaw, &effect, jsonx.V1()); err != nil || (effect != "Allow" && effect != "Deny") {
return &ErrorResponse{
Code: errMalformedPolicy,
Message: "Invalid effect: " + string(effectRaw),
diff --git a/services/s3/express_session.go b/services/s3/express_session.go
index 11582ee7f0..4c39c08fed 100644
--- a/services/s3/express_session.go
+++ b/services/s3/express_session.go
@@ -5,7 +5,7 @@ import (
"crypto/rand"
"encoding/base64"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
@@ -13,6 +13,8 @@ import (
"time"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// s3ExpressSessionTTL matches real S3 Express One Zone: CreateSession
@@ -254,7 +256,7 @@ func validateSessionEncryptionContext(encoded, bucket string) error {
}
var kv map[string]string
- if err = json.Unmarshal(raw, &kv); err != nil {
+ if err = json.Unmarshal(raw, &kv, jsonx.V1()); err != nil {
return fmt.Errorf("%w: encryption context must be a JSON object of strings", ErrInvalidArgument)
}
diff --git a/services/s3/golden_perf_test.go b/services/s3/golden_perf_test.go
index 5d0548b539..cc8b47a66d 100644
--- a/services/s3/golden_perf_test.go
+++ b/services/s3/golden_perf_test.go
@@ -11,7 +11,8 @@ package s3_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"encoding/xml"
"net/http"
"net/http/httptest"
@@ -23,6 +24,7 @@ import (
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/s3"
)
@@ -35,9 +37,9 @@ func goldenPath(name string) string {
func checkGolden(t *testing.T, name string, got []byte) {
t.Helper()
- var buf bytes.Buffer
- require.NoError(t, json.Indent(&buf, got, "", " "))
- pretty := buf.Bytes()
+ indented := jsontext.Value(bytes.Clone(got))
+ require.NoError(t, indented.Indent(jsonx.V1(), jsontext.WithIndent(" ")))
+ pretty := []byte(indented)
path := goldenPath(name)
if os.Getenv("S3_GOLDEN_UPDATE") != "" {
@@ -79,7 +81,7 @@ func snapshotResponse(rec *httptest.ResponseRecorder, headerNames []string) gold
func marshalGolden(t *testing.T, v any) []byte {
t.Helper()
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
return data
diff --git a/services/s3/notification.go b/services/s3/notification.go
index 583cb795e5..54678cb210 100644
--- a/services/s3/notification.go
+++ b/services/s3/notification.go
@@ -2,7 +2,7 @@ package s3
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"fmt"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/apptrace"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// notificationConfiguration mirrors the AWS S3 XML notification configuration
@@ -204,7 +205,7 @@ func buildS3EventPayload(
}
envelope := map[string]any{"Records": []s3EventRecord{record}}
- b, err := json.Marshal(envelope)
+ b, err := json.Marshal(envelope, jsonx.V1())
if err != nil {
return "", err
}
@@ -249,7 +250,7 @@ func buildEventBridgeDetail(bucket, key, etag, reason string, size int64) (strin
Reason: reason,
}
- b, err := json.Marshal(detail)
+ b, err := json.Marshal(detail, jsonx.V1())
if err != nil {
return "", err
}
diff --git a/services/s3/object_lambda.go b/services/s3/object_lambda.go
index d9c5d136ba..a705247ae9 100644
--- a/services/s3/object_lambda.go
+++ b/services/s3/object_lambda.go
@@ -2,7 +2,7 @@ package s3
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -191,7 +192,7 @@ func (h *S3Handler) handleObjectLambdaGetObject(
OutputRoute: "gopherstack",
OutputToken: token,
}
- payload, err := json.Marshal(event)
+ payload, err := json.Marshal(event, jsonx.V1())
if err != nil {
h.pendingObjectLambdaRequests.Delete(token)
WriteError(ctx, w, r, err)
diff --git a/services/s3/object_lambda_features_test.go b/services/s3/object_lambda_features_test.go
index be8abb4878..582adff6c8 100644
--- a/services/s3/object_lambda_features_test.go
+++ b/services/s3/object_lambda_features_test.go
@@ -2,7 +2,7 @@ package s3_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/s3"
)
@@ -118,7 +119,7 @@ func TestObjectLambdaEventUserIdentity(t *testing.T) {
select {
case p := <-rec.payload:
- require.NoError(t, json.Unmarshal(p, &event))
+ require.NoError(t, json.Unmarshal(p, &event, jsonx.V1()))
case <-t.Context().Done():
t.Fatal("lambda not invoked")
}
diff --git a/services/s3/object_lambda_head_list.go b/services/s3/object_lambda_head_list.go
index dd74360ca2..c6612da5e4 100644
--- a/services/s3/object_lambda_head_list.go
+++ b/services/s3/object_lambda_head_list.go
@@ -2,7 +2,7 @@ package s3
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"errors"
"fmt"
@@ -17,6 +17,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -137,7 +138,7 @@ func (h *S3Handler) invokeObjectLambda(ctx context.Context, lambdaARN string, ev
return nil, ErrNoSuchKey
}
- payload, err := json.Marshal(ev)
+ payload, err := json.Marshal(ev, jsonx.V1())
if err != nil {
return nil, err
}
@@ -200,7 +201,7 @@ func (h *S3Handler) callObjectLambda(
var res objectLambdaResult
out, err := h.invokeObjectLambda(ctx, route.lambdaARN, ev)
- if err != nil || json.Unmarshal(out, &res) != nil || res.StatusCode == 0 ||
+ if err != nil || json.Unmarshal(out, &res, jsonx.V1()) != nil || res.StatusCode == 0 ||
(res.ListResultXML != "" && res.ListBucketResult != nil) {
WriteError(ctx, w, r, errObjectLambdaFailed)
diff --git a/services/s3/object_lambda_head_list_test.go b/services/s3/object_lambda_head_list_test.go
index 0adf068c16..6579bb6d41 100644
--- a/services/s3/object_lambda_head_list_test.go
+++ b/services/s3/object_lambda_head_list_test.go
@@ -2,7 +2,7 @@ package s3_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/s3"
)
@@ -23,7 +24,7 @@ type recordingObjectLambda struct {
func (l *recordingObjectLambda) InvokeFunction(_ context.Context, _, _ string, payload []byte) ([]byte, int, error) {
var ev map[string]any
- _ = json.Unmarshal(payload, &ev)
+ _ = json.Unmarshal(payload, &ev, jsonx.V1())
l.mu.Lock()
l.events = append(l.events, ev)
diff --git a/services/s3/object_lambda_test.go b/services/s3/object_lambda_test.go
index a65e2c99d7..d631e2019c 100644
--- a/services/s3/object_lambda_test.go
+++ b/services/s3/object_lambda_test.go
@@ -2,7 +2,7 @@ package s3_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/s3"
)
@@ -42,7 +43,7 @@ func (l *staticObjectLambda) InvokeFunction(
OutputToken string `json:"outputToken"`
} `json:"getObjectContext"`
}
- if err := json.Unmarshal(payload, &event); err != nil {
+ if err := json.Unmarshal(payload, &event, jsonx.V1()); err != nil {
return nil, 0, err
}
diff --git a/services/s3/persistence.go b/services/s3/persistence.go
index 6a3c554175..f7b7757461 100644
--- a/services/s3/persistence.go
+++ b/services/s3/persistence.go
@@ -2,11 +2,13 @@ package s3
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
@@ -32,10 +34,10 @@ const s3SnapshotVersion = 1
// unexported, so encoding/json already skips them. Restore re-initialises those
// skipped fields via reinitBucketMutexes/reinitUploadMutexes below.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Tags map[string][]types.Tag `json:"tags"`
- DefaultRegion string `json:"defaultRegion"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Tags map[string][]types.Tag `json:"tags"`
+ DefaultRegion string `json:"defaultRegion"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
@@ -175,7 +177,7 @@ func (bucket *StoredBucket) MarshalJSON() ([]byte, error) {
bucket.mu.RLock("Snapshot")
defer bucket.mu.RUnlock()
- return json.Marshal((*storedBucketAlias)(bucket))
+ return json.Marshal((*storedBucketAlias)(bucket), jsonx.V1())
}
// storedObjectAlias avoids infinite recursion from StoredObject.MarshalJSON.
@@ -187,7 +189,7 @@ func (obj *StoredObject) MarshalJSON() ([]byte, error) {
obj.mu.RLock("Snapshot")
defer obj.mu.RUnlock()
- return json.Marshal((*storedObjectAlias)(obj))
+ return json.Marshal((*storedObjectAlias)(obj), jsonx.V1())
}
// storedMultipartUploadAlias avoids infinite recursion from
@@ -200,7 +202,7 @@ func (u *StoredMultipartUpload) MarshalJSON() ([]byte, error) {
u.mu.RLock("Snapshot")
defer u.mu.RUnlock()
- return json.Marshal((*storedMultipartUploadAlias)(u))
+ return json.Marshal((*storedMultipartUploadAlias)(u), jsonx.V1())
}
// Snapshot implements persistence.Persistable by delegating to the backend.
diff --git a/services/s3/realclient_bucket_config_and_object_ops_test.go b/services/s3/realclient_bucket_config_and_object_ops_test.go
index 0f07fdb6cf..c87b14972f 100644
--- a/services/s3/realclient_bucket_config_and_object_ops_test.go
+++ b/services/s3/realclient_bucket_config_and_object_ops_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"crypto/sha256"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -20,6 +20,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/s3"
)
@@ -1136,7 +1137,7 @@ func (l *typedWriteGetObjectResponseLambda) InvokeFunction(
OutputToken string `json:"outputToken"`
} `json:"getObjectContext"`
}
- if err := json.Unmarshal(payload, &event); err != nil {
+ if err := json.Unmarshal(payload, &event, jsonx.V1()); err != nil {
l.setResult(true, err)
return nil, 0, err
diff --git a/services/s3/select_csv.go b/services/s3/select_csv.go
index c810961071..7cf29a2716 100644
--- a/services/s3/select_csv.go
+++ b/services/s3/select_csv.go
@@ -3,11 +3,13 @@ package s3
import (
"bytes"
"encoding/csv"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errMissingSQLColumn = errors.New("MissingSQLColumn")
@@ -205,7 +207,7 @@ func serializeCSVRowsAsJSON(rows []map[string]string, jsonOut *selectJSONOutput)
var buf bytes.Buffer
for _, row := range rows {
- jsonBytes, err := json.Marshal(mapStringToAny(row))
+ jsonBytes, err := json.Marshal(mapStringToAny(row), jsonx.V1())
if err != nil {
return nil, err
}
diff --git a/services/s3/select_json.go b/services/s3/select_json.go
index e226781c65..64bdaea521 100644
--- a/services/s3/select_json.go
+++ b/services/s3/select_json.go
@@ -3,10 +3,12 @@ package s3
import (
"bufio"
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// evaluateJSONQuery reads JSON from data, applies the SQL query, and streams results.
@@ -52,7 +54,7 @@ func evaluateJSONLinesQuery(
}
var row map[string]any
- if err := json.Unmarshal(line, &row); err != nil {
+ if err := json.Unmarshal(line, &row, jsonx.V1()); err != nil {
return 0, fmt.Errorf("parsing JSON line: %w", err)
}
@@ -95,7 +97,7 @@ func evaluateJSONDocumentQuery(
req *selectRequest,
) (int64, error) {
var doc any
- if err := json.Unmarshal(data, &doc); err != nil {
+ if err := json.Unmarshal(data, &doc, jsonx.V1()); err != nil {
return 0, fmt.Errorf("parsing JSON document: %w", err)
}
@@ -192,7 +194,7 @@ func serializeJSONRows(rows []map[string]any, jsonOut *selectJSONOutput) ([]byte
var buf bytes.Buffer
for _, row := range rows {
- jsonBytes, err := json.Marshal(row)
+ jsonBytes, err := json.Marshal(row, jsonx.V1())
if err != nil {
return nil, err
}
diff --git a/services/s3control/handler_access_points.go b/services/s3control/handler_access_points.go
index e8707a74eb..7caac15787 100644
--- a/services/s3control/handler_access_points.go
+++ b/services/s3control/handler_access_points.go
@@ -1,7 +1,7 @@
package s3control
import (
- "encoding/json"
+ "encoding/json/jsontext"
"encoding/xml"
"net/http"
"slices"
@@ -9,6 +9,8 @@ import (
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -421,7 +423,7 @@ func (h *Handler) handlePutAccessPointPolicy(c *echo.Context) error {
return writeXMLErrorCode(c, http.StatusBadRequest, "MalformedXML", "invalid request body")
}
- if !json.Valid([]byte(body.Policy)) {
+ if !jsontext.Value([]byte(body.Policy)).IsValid(jsonx.V1()) {
return writeXMLErrorCode(c, http.StatusBadRequest, "MalformedPolicy", "Policy is not valid JSON")
}
diff --git a/services/s3control/persistence.go b/services/s3control/persistence.go
index a18e4984bb..56cbf29618 100644
--- a/services/s3control/persistence.go
+++ b/services/s3control/persistence.go
@@ -2,10 +2,12 @@ package s3control
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -72,7 +74,7 @@ func newDirtyDTORegistry() (
// incompatible (older or newer) build of this backend as though it were the
// current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
// batch2 additions (raw, non-*T maps -- never converted to store.Table)
BucketReplication map[string]string `json:"bucketReplication"`
StorageLensConfigs map[string]string `json:"storageLensConfigs"`
@@ -153,7 +155,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
NextID: b.nextID,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "s3control: failed to snapshot backend", "error", err)
@@ -341,7 +343,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreDirtyTablesLocked restores the "dirty" tables (mrapRequests,
// accessPointPABs) from tables via the ephemeral DTO registry, converting
// each DTO back to its live type. The caller MUST hold b.mu for writing.
-func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]jsontext.Value) error {
dtoReg, mrapDTOs, pabDTOs := newDirtyDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
diff --git a/services/s3files/file_systems.go b/services/s3files/file_systems.go
index 27464785e8..fe0651a5e2 100644
--- a/services/s3files/file_systems.go
+++ b/services/s3files/file_systems.go
@@ -2,7 +2,8 @@ package s3files
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
@@ -10,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -287,8 +289,8 @@ func validatePolicy(policy string) error {
return validation("Policy must be between 1 and 20480 bytes")
}
- var doc map[string]json.RawMessage
- if err := json.Unmarshal([]byte(policy), &doc); err != nil {
+ var doc map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(policy), &doc, jsonx.V1()); err != nil {
return validation("Policy is not a valid JSON document")
}
diff --git a/services/s3files/handler.go b/services/s3files/handler.go
index 759ea63e04..5c7e92cfe4 100644
--- a/services/s3files/handler.go
+++ b/services/s3files/handler.go
@@ -2,7 +2,7 @@ package s3files
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
@@ -12,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -285,7 +286,7 @@ func decode(body []byte, v any) error {
return nil
}
- if err := json.Unmarshal(body, v); err != nil {
+ if err := json.Unmarshal(body, v, jsonx.V1()); err != nil {
return validation("malformed request body")
}
diff --git a/services/s3files/persistence.go b/services/s3files/persistence.go
index 213c401a5a..adc74ec5d9 100644
--- a/services/s3files/persistence.go
+++ b/services/s3files/persistence.go
@@ -2,7 +2,7 @@ package s3files
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -13,10 +13,10 @@ import (
const s3filesSnapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/s3tables/handler.go b/services/s3tables/handler.go
index 17839dc3f9..ef785435a9 100644
--- a/services/s3tables/handler.go
+++ b/services/s3tables/handler.go
@@ -2,7 +2,7 @@ package s3tables
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -530,7 +531,7 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
payload, _ := json.Marshal(map[string]string{
"message": msg,
- })
+ }, jsonx.V1())
c.Response().Header().Set("X-Amzn-Errortype", errType)
diff --git a/services/s3tables/handler_namespaces.go b/services/s3tables/handler_namespaces.go
index 0425243eb2..d47716290b 100644
--- a/services/s3tables/handler_namespaces.go
+++ b/services/s3tables/handler_namespaces.go
@@ -2,10 +2,11 @@ package s3tables
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -23,7 +24,7 @@ func (h *Handler) handleCreateNamespace(ctx context.Context, r *http.Request, bo
bucketARN := segs[1]
var req createNamespaceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -42,7 +43,7 @@ func (h *Handler) handleCreateNamespace(ctx context.Context, r *http.Request, bo
return json.Marshal(map[string]any{
keyNamespace: ns.Namespace,
keyTableBucketARN: ns.TableBucketARN,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleGetNamespace(ctx context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -73,7 +74,7 @@ func (h *Handler) handleGetNamespace(ctx context.Context, r *http.Request, _ []b
keyCreatedBy: ns.CreatedBy,
keyOwnerAccountID: ns.OwnerAccountID,
keyNamespaceID: ns.NamespaceID,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleDeleteNamespace(ctx context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -143,5 +144,5 @@ func (h *Handler) handleListNamespaces(ctx context.Context, r *http.Request, _ [
resp[keyContinuationToken] = pg.Next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/s3tables/handler_namespaces_test.go b/services/s3tables/handler_namespaces_test.go
index 08b4f54d45..73bbd59550 100644
--- a/services/s3tables/handler_namespaces_test.go
+++ b/services/s3tables/handler_namespaces_test.go
@@ -1,13 +1,15 @@
package s3tables_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_Namespace_CRUD(t *testing.T) {
@@ -153,7 +155,7 @@ func TestHandler_ListNamespacesIncludesTableBucketId(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
namespaces, ok := out["namespaces"].([]any)
require.True(t, ok)
diff --git a/services/s3tables/handler_replication.go b/services/s3tables/handler_replication.go
index 04c9a74988..1aba17cb07 100644
--- a/services/s3tables/handler_replication.go
+++ b/services/s3tables/handler_replication.go
@@ -2,10 +2,11 @@ package s3tables
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -60,7 +61,7 @@ func (h *Handler) handleGetTableRecordExpirationConfiguration(
return json.Marshal(map[string]any{
keyConfiguration: configuration,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleGetTableReplication(ctx context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -83,7 +84,7 @@ func (h *Handler) handleGetTableReplication(ctx context.Context, r *http.Request
"rules": cfg.Rules,
},
keyVersionToken: cfg.VersionToken,
- })
+ }, jsonx.V1())
}
// putTableReplicationRequest is the request body for PutTableReplication.
@@ -100,7 +101,7 @@ func (h *Handler) handlePutTableReplication(ctx context.Context, r *http.Request
versionToken := r.URL.Query().Get(keyVersionToken)
var req putTableReplicationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -117,7 +118,7 @@ func (h *Handler) handlePutTableReplication(ctx context.Context, r *http.Request
return json.Marshal(map[string]any{
keyStatusField: replicationStatusCompleted,
keyVersionToken: cfg.VersionToken,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleGetTableReplicationStatus(ctx context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -149,7 +150,7 @@ func (h *Handler) handleGetTableReplicationStatus(ctx context.Context, r *http.R
return json.Marshal(map[string]any{
"sourceTableArn": tableArn,
"destinations": destinations,
- })
+ }, jsonx.V1())
}
// putTableRecordExpirationRequest is the request body for PutTableRecordExpirationConfiguration.
@@ -168,7 +169,7 @@ func (h *Handler) handlePutTableRecordExpirationConfiguration(
}
var req putTableRecordExpirationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -225,5 +226,5 @@ func (h *Handler) handleGetTableRecordExpirationJobStatus(
return json.Marshal(map[string]any{
keyStatusField: status,
- })
+ }, jsonx.V1())
}
diff --git a/services/s3tables/handler_table_bucket_config_test.go b/services/s3tables/handler_table_bucket_config_test.go
index 906f19a6bf..1c6bd2730c 100644
--- a/services/s3tables/handler_table_bucket_config_test.go
+++ b/services/s3tables/handler_table_bucket_config_test.go
@@ -1,7 +1,7 @@
package s3tables_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/s3tables"
)
@@ -227,7 +228,7 @@ func TestHandler_ListTableBucketsIncludesType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
buckets, ok := out["tableBuckets"].([]any)
require.True(t, ok)
@@ -274,7 +275,7 @@ func TestHandler_GetTableBucketReplication_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
_, hasTopLevelDests := out["destinations"]
_, hasFabricatedWrapper := out["replicationConfiguration"]
diff --git a/services/s3tables/handler_table_buckets.go b/services/s3tables/handler_table_buckets.go
index fd6e08c0b6..4f2bcd7b67 100644
--- a/services/s3tables/handler_table_buckets.go
+++ b/services/s3tables/handler_table_buckets.go
@@ -2,11 +2,12 @@ package s3tables
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -23,7 +24,7 @@ type createTableBucketRequest struct {
func (h *Handler) handleCreateTableBucket(ctx context.Context, _ *http.Request, body []byte) ([]byte, error) {
var req createTableBucketRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -45,7 +46,7 @@ func (h *Handler) handleCreateTableBucket(ctx context.Context, _ *http.Request,
return json.Marshal(map[string]string{
keyArn: tb.ARN,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleGetTableBucket(ctx context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -71,7 +72,7 @@ func (h *Handler) handleGetTableBucket(ctx context.Context, r *http.Request, _ [
keyCreatedAt: tb.CreatedAt.UTC().Format("2006-01-02T15:04:05.999Z"),
keyType: bucketTypeCustomer,
keyTableBucketID: tb.BucketID,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleDeleteTableBucket(ctx context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -137,7 +138,7 @@ func (h *Handler) handleListTableBuckets(ctx context.Context, r *http.Request, _
resp[keyContinuationToken] = pg.Next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
func (h *Handler) handleGetTableBucketMaintenanceConfiguration(
@@ -167,7 +168,7 @@ func (h *Handler) handleGetTableBucketMaintenanceConfiguration(
return json.Marshal(map[string]any{
keyTableBucketARN: bucketARN,
keyConfiguration: cfg,
- })
+ }, jsonx.V1())
}
// putTableBucketMaintenanceRequest is the request body for PutTableBucketMaintenanceConfiguration.
@@ -189,7 +190,7 @@ func (h *Handler) handlePutTableBucketMaintenanceConfiguration(
maintenanceType := segs[3]
var req putTableBucketMaintenanceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -244,7 +245,7 @@ func (h *Handler) handleGetTableBucketEncryption(ctx context.Context, r *http.Re
return json.Marshal(map[string]any{
"encryptionConfiguration": tb.Encryption,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleDeleteTableBucketEncryption(ctx context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -292,7 +293,7 @@ func (h *Handler) handleGetTableBucketMetricsConfiguration(
resp["id"] = configID
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
func (h *Handler) handleDeleteTableBucketMetricsConfiguration(
@@ -347,7 +348,7 @@ func (h *Handler) handleGetTableBucketStorageClass(ctx context.Context, r *http.
"storageClassConfiguration": map[string]any{
"storageClass": sc,
},
- })
+ }, jsonx.V1())
}
func (h *Handler) handleGetTableBucketReplication(ctx context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -370,7 +371,7 @@ func (h *Handler) handleGetTableBucketReplication(ctx context.Context, r *http.R
"rules": cfg.Rules,
},
keyVersionToken: cfg.VersionToken,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleDeleteTableBucketReplication(ctx context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -409,7 +410,7 @@ func (h *Handler) handleGetTableBucketPolicy(ctx context.Context, r *http.Reques
return json.Marshal(map[string]string{
"resourcePolicy": policy,
- })
+ }, jsonx.V1())
}
// putTableBucketPolicyRequest is the request body for PutTableBucketPolicy.
@@ -426,7 +427,7 @@ func (h *Handler) handlePutTableBucketPolicy(ctx context.Context, r *http.Reques
bucketARN := segs[1]
var req putTableBucketPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -476,7 +477,7 @@ func (h *Handler) handlePutTableBucketEncryption(ctx context.Context, r *http.Re
bucketARN := segs[1]
var req putTableBucketEncryptionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -530,7 +531,7 @@ func (h *Handler) handlePutTableBucketStorageClass(ctx context.Context, r *http.
bucketARN := segs[1]
var req putTableBucketStorageClassRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -563,7 +564,7 @@ func (h *Handler) handlePutTableBucketReplication(ctx context.Context, r *http.R
versionToken := r.URL.Query().Get(keyVersionToken)
var req putTableBucketReplicationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -580,5 +581,5 @@ func (h *Handler) handlePutTableBucketReplication(ctx context.Context, r *http.R
return json.Marshal(map[string]any{
keyStatusField: replicationStatusCompleted,
keyVersionToken: cfg.VersionToken,
- })
+ }, jsonx.V1())
}
diff --git a/services/s3tables/handler_table_maintenance_test.go b/services/s3tables/handler_table_maintenance_test.go
index 21e030bb96..841f74e807 100644
--- a/services/s3tables/handler_table_maintenance_test.go
+++ b/services/s3tables/handler_table_maintenance_test.go
@@ -1,13 +1,16 @@
package s3tables_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_MaintenanceConfiguration(t *testing.T) {
@@ -329,12 +332,12 @@ func TestHandler_MetadataLocationVsUpdate(t *testing.T) {
updateBody, err := json.Marshal(map[string]string{
"metadataLocation": tt.metaLocation,
"versionToken": table["versionToken"].(string),
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := doS3TablesRequest(t, h, http.MethodPut,
"/tables/"+encodedARN+"/ns1/t1/metadata-location",
- json.RawMessage(updateBody))
+ jsontext.Value(updateBody))
require.Equal(t, http.StatusOK, rec.Code)
rec2 := doS3TablesRequest(t, h, http.MethodGet,
diff --git a/services/s3tables/handler_tables.go b/services/s3tables/handler_tables.go
index 95ddfd2166..8c96c648c3 100644
--- a/services/s3tables/handler_tables.go
+++ b/services/s3tables/handler_tables.go
@@ -2,10 +2,11 @@ package s3tables
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -48,7 +49,7 @@ func (h *Handler) handleGetTableMaintenanceJobStatus(
return json.Marshal(map[string]any{
keyTableARN: table.ARN,
keyStatusField: jobStatus,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleGetTableMetadataLocation(ctx context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -73,7 +74,7 @@ func (h *Handler) handleGetTableMetadataLocation(ctx context.Context, r *http.Re
keyVersionToken: table.VersionToken,
"warehouseLocation": table.WarehouseLocation,
keyMetadataLocation: table.MetadataLocation,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleGetTableEncryption(ctx context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -96,7 +97,7 @@ func (h *Handler) handleGetTableEncryption(ctx context.Context, r *http.Request,
return json.Marshal(map[string]any{
"encryptionConfiguration": encCfg,
- })
+ }, jsonx.V1())
}
// createTableRequest is the request body for CreateTable.
@@ -123,7 +124,7 @@ func (h *Handler) handleCreateTable(ctx context.Context, r *http.Request, body [
nsName := segs[2]
var req createTableRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -155,7 +156,7 @@ func (h *Handler) handleCreateTable(ctx context.Context, r *http.Request, body [
return json.Marshal(map[string]string{
keyTableARN: table.ARN,
keyVersionToken: table.VersionToken,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleGetTable(ctx context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -211,7 +212,7 @@ func (h *Handler) handleGetTable(ctx context.Context, r *http.Request, _ []byte)
keyCreatedBy: table.OwnerAccountID,
"modifiedBy": table.OwnerAccountID,
keyOwnerAccountID: table.OwnerAccountID,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleDeleteTable(ctx context.Context, r *http.Request, _ []byte) ([]byte, error) {
@@ -286,7 +287,7 @@ func (h *Handler) handleListTables(ctx context.Context, r *http.Request, _ []byt
resp[keyContinuationToken] = pg.Next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// renameTableRequest is the request body for RenameTable.
@@ -307,7 +308,7 @@ func (h *Handler) handleRenameTable(ctx context.Context, r *http.Request, body [
name := segs[3]
var req renameTableRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -353,7 +354,7 @@ func (h *Handler) handleUpdateTableMetadataLocation(ctx context.Context, r *http
name := segs[3]
var req updateTableMetadataLocationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -387,7 +388,7 @@ func (h *Handler) handleUpdateTableMetadataLocation(ctx context.Context, r *http
keyNamespace: table.Namespace,
keyVersionToken: table.VersionToken,
keyMetadataLocation: table.MetadataLocation,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleGetTableMaintenanceConfiguration(
@@ -419,7 +420,7 @@ func (h *Handler) handleGetTableMaintenanceConfiguration(
return json.Marshal(map[string]any{
keyTableARN: tableARN,
keyConfiguration: cfg,
- })
+ }, jsonx.V1())
}
// putTableMaintenanceRequest is the request body for PutTableMaintenanceConfiguration.
@@ -443,7 +444,7 @@ func (h *Handler) handlePutTableMaintenanceConfiguration(
maintenanceType := segs[5]
var req putTableMaintenanceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -500,7 +501,7 @@ func (h *Handler) handleGetTablePolicy(ctx context.Context, r *http.Request, _ [
return json.Marshal(map[string]string{
"resourcePolicy": policy,
- })
+ }, jsonx.V1())
}
// putTablePolicyRequest is the request body for PutTablePolicy.
@@ -519,7 +520,7 @@ func (h *Handler) handlePutTablePolicy(ctx context.Context, r *http.Request, bod
name := segs[3]
var req putTablePolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -579,5 +580,5 @@ func (h *Handler) handleGetTableStorageClass(ctx context.Context, r *http.Reques
"storageClassConfiguration": map[string]any{
"storageClass": sc,
},
- })
+ }, jsonx.V1())
}
diff --git a/services/s3tables/handler_tables_test.go b/services/s3tables/handler_tables_test.go
index 04fe46da34..880a2b86ed 100644
--- a/services/s3tables/handler_tables_test.go
+++ b/services/s3tables/handler_tables_test.go
@@ -1,7 +1,7 @@
package s3tables_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/url"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_Table_Create(t *testing.T) {
@@ -518,7 +520,7 @@ func TestHandler_ListTablesResponseUsesLowercaseArns(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tables, ok := out["tables"].([]any)
require.True(t, ok)
diff --git a/services/s3tables/handler_tags.go b/services/s3tables/handler_tags.go
index e9e5b3276e..551bf60571 100644
--- a/services/s3tables/handler_tags.go
+++ b/services/s3tables/handler_tags.go
@@ -2,10 +2,11 @@ package s3tables
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -27,7 +28,7 @@ func (h *Handler) handleListTagsForResource(ctx context.Context, r *http.Request
return json.Marshal(map[string]any{
"tags": tags,
- })
+ }, jsonx.V1())
}
// tagResourceRequest is the request body for TagResource.
@@ -44,7 +45,7 @@ func (h *Handler) handleTagResource(ctx context.Context, r *http.Request, body [
resourceArn := segs[1]
var req tagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/s3tables/handler_test.go b/services/s3tables/handler_test.go
index 40f8002b1a..861b966b0b 100644
--- a/services/s3tables/handler_test.go
+++ b/services/s3tables/handler_test.go
@@ -2,7 +2,8 @@ package s3tables_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -14,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/s3tables"
)
@@ -46,7 +48,7 @@ func doS3TablesRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -68,7 +70,7 @@ func parseResponse(t *testing.T, rec *httptest.ResponseRecorder) map[string]any
t.Helper()
var result map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&result))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &result, jsonx.V1()))
return result
}
diff --git a/services/s3tables/metadata_writer.go b/services/s3tables/metadata_writer.go
index 4e4ab31f52..25933b799c 100644
--- a/services/s3tables/metadata_writer.go
+++ b/services/s3tables/metadata_writer.go
@@ -2,7 +2,7 @@ package s3tables
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/iceberg"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -181,7 +182,7 @@ func buildIcebergTableMetadata(t *Table, now time.Time) (*iceberg.TableMetadata,
return nil, err
}
- orderJSON, err := json.Marshal(order)
+ orderJSON, err := json.Marshal(order, jsonx.V1())
if err != nil {
return nil, err
}
@@ -203,10 +204,10 @@ func buildIcebergTableMetadata(t *Table, now time.Time) (*iceberg.TableMetadata,
}
func roundTrip(in map[string]any, out any) error {
- b, err := json.Marshal(in)
+ b, err := json.Marshal(in, jsonx.V1())
if err != nil {
return err
}
- return json.Unmarshal(b, out)
+ return json.Unmarshal(b, out, jsonx.V1())
}
diff --git a/services/s3tables/multi_region_test.go b/services/s3tables/multi_region_test.go
index cd88cdb18f..f7963aeed4 100644
--- a/services/s3tables/multi_region_test.go
+++ b/services/s3tables/multi_region_test.go
@@ -2,7 +2,8 @@ package s3tables_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/s3tables"
)
@@ -173,8 +175,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/s3tables/persistence.go b/services/s3tables/persistence.go
index a166d4f092..fc4ddf8946 100644
--- a/services/s3tables/persistence.go
+++ b/services/s3tables/persistence.go
@@ -2,7 +2,7 @@ package s3tables
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -87,7 +87,7 @@ func buildPersistenceDTORegistry() persistenceDTOTables {
// against decoding a snapshot from an incompatible (older or newer) build
// of this backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
AccountID string `json:"accountID"`
Region string `json:"region"`
@@ -198,7 +198,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreResourceTables rebuilds every store.Table on b from snap's
// per-table JSON, factored out of Restore to keep Restore's own cognitive
// complexity low. Callers must hold every mutex.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
if err := b.registry.RestoreAll(tables); err != nil {
return fmt.Errorf("s3tables: restore clean tables: %w", err)
}
diff --git a/services/s3tables/validation.go b/services/s3tables/validation.go
index 5ec7dda3bd..bed100b367 100644
--- a/services/s3tables/validation.go
+++ b/services/s3tables/validation.go
@@ -1,10 +1,12 @@
package s3tables
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Real S3 Tables naming rules, confirmed against
@@ -133,7 +135,7 @@ func validateNamespaceParts(namespace []string) error {
// validatePolicyDocument rejects a resource policy that is not a JSON object.
func validatePolicyDocument(policy string) error {
var doc map[string]any
- if err := json.Unmarshal([]byte(policy), &doc); err != nil || doc == nil {
+ if err := json.Unmarshal([]byte(policy), &doc, jsonx.V1()); err != nil || doc == nil {
return fmt.Errorf("%w: resourcePolicy must be a valid JSON policy document", errInvalidRequest)
}
diff --git a/services/s3vectors/buckets.go b/services/s3vectors/buckets.go
index 333cb8e18e..116733a8e5 100644
--- a/services/s3vectors/buckets.go
+++ b/services/s3vectors/buckets.go
@@ -2,11 +2,12 @@ package s3vectors
import (
"cmp"
- "encoding/json"
+ "encoding/json/v2"
"slices"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -238,7 +239,7 @@ func (b *InMemoryBackend) putBucketPolicy(sc scope, in *putPolicyIn) (any, error
return nil, invalid("policy is required")
}
- if err := json.Unmarshal([]byte(in.Policy), &doc); err != nil || doc.Statement == nil {
+ if err := json.Unmarshal([]byte(in.Policy), &doc, jsonx.V1()); err != nil || doc.Statement == nil {
return nil, invalid("policy must be a JSON policy document with a Statement")
}
diff --git a/services/s3vectors/models.go b/services/s3vectors/models.go
index bbcd2594ac..5c4728a1f2 100644
--- a/services/s3vectors/models.go
+++ b/services/s3vectors/models.go
@@ -3,10 +3,12 @@ package s3vectors
import (
"encoding/base64"
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"math"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// f32s stores a vector as float32 and serialises as base64 little-endian bytes, 4 bytes per dimension.
@@ -23,13 +25,13 @@ func (v f32s) MarshalJSON() ([]byte, error) {
binary.LittleEndian.PutUint32(buf[bytesPerFloat*i:], math.Float32bits(f))
}
- return json.Marshal(base64.StdEncoding.EncodeToString(buf))
+ return json.Marshal(base64.StdEncoding.EncodeToString(buf), jsonx.V1())
}
// UnmarshalJSON implements json.Unmarshaler.
func (v *f32s) UnmarshalJSON(data []byte) error {
var s string
- if err := json.Unmarshal(data, &s); err != nil {
+ if err := json.Unmarshal(data, &s, jsonx.V1()); err != nil {
return err
}
diff --git a/services/s3vectors/persistence.go b/services/s3vectors/persistence.go
index cc414f37ba..a045579e09 100644
--- a/services/s3vectors/persistence.go
+++ b/services/s3vectors/persistence.go
@@ -2,7 +2,7 @@ package s3vectors
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,8 +12,8 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/s3vectors/vectors.go b/services/s3vectors/vectors.go
index 9920f587bc..f304d79f14 100644
--- a/services/s3vectors/vectors.go
+++ b/services/s3vectors/vectors.go
@@ -1,11 +1,12 @@
package s3vectors
import (
- "encoding/json"
+ "encoding/json/v2"
"hash/fnv"
"maps"
"slices"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -51,7 +52,7 @@ func validateMetadata(ix *Index, meta any) (map[string]any, error) {
return nil, err
}
- raw, _ := json.Marshal(v)
+ raw, _ := json.Marshal(v, jsonx.V1())
total += len(k) + len(raw)
if ix.filterable(k) {
@@ -136,7 +137,7 @@ func (b *InMemoryBackend) putVectors(sc scope, in *putVectorsIn) (any, error) {
}
func itoa(i int) string {
- b, _ := json.Marshal(i)
+ b, _ := json.Marshal(i, jsonx.V1())
return string(b)
}
diff --git a/services/sagemaker/ai_benchmark_jobs.go b/services/sagemaker/ai_benchmark_jobs.go
index cbbe082e70..81634fc004 100644
--- a/services/sagemaker/ai_benchmark_jobs.go
+++ b/services/sagemaker/ai_benchmark_jobs.go
@@ -2,7 +2,8 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -11,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/store"
)
@@ -49,9 +51,9 @@ type AIBenchmarkJob struct {
AIWorkloadConfigIdentifier string `json:"AIWorkloadConfigIdentifier"`
RoleArn string `json:"RoleArn"`
FailureReason string `json:"FailureReason,omitempty"`
- BenchmarkTarget json.RawMessage `json:"BenchmarkTarget"`
- OutputConfig json.RawMessage `json:"OutputConfig"`
- NetworkConfig json.RawMessage `json:"NetworkConfig,omitempty"`
+ BenchmarkTarget jsontext.Value `json:"BenchmarkTarget"`
+ OutputConfig jsontext.Value `json:"OutputConfig"`
+ NetworkConfig jsontext.Value `json:"NetworkConfig,omitempty"`
}
// MarshalJSON emits CreationTime/StartTime/EndTime as AWS awsjson1.1
@@ -75,7 +77,7 @@ func (j *AIBenchmarkJob) MarshalJSON() ([]byte, error) {
StartTime: epochSecondsPtr(j.StartTime),
EndTime: epochSecondsPtr(j.EndTime),
Tags: toTagObjects(j.Tags),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [AIBenchmarkJob.MarshalJSON], used by
@@ -90,7 +92,7 @@ func (j *AIBenchmarkJob) UnmarshalJSON(data []byte) error {
CreationTime float64 `json:"CreationTime"`
}{alias: (*alias)(j)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -104,9 +106,9 @@ func (j *AIBenchmarkJob) UnmarshalJSON(data []byte) error {
func cloneAIBenchmarkJob(j *AIBenchmarkJob) *AIBenchmarkJob {
cp := *j
cp.Tags = maps.Clone(j.Tags)
- cp.BenchmarkTarget = append(json.RawMessage(nil), j.BenchmarkTarget...)
- cp.OutputConfig = append(json.RawMessage(nil), j.OutputConfig...)
- cp.NetworkConfig = append(json.RawMessage(nil), j.NetworkConfig...)
+ cp.BenchmarkTarget = append(jsontext.Value(nil), j.BenchmarkTarget...)
+ cp.OutputConfig = append(jsontext.Value(nil), j.OutputConfig...)
+ cp.NetworkConfig = append(jsontext.Value(nil), j.NetworkConfig...)
if j.StartTime != nil {
t := *j.StartTime
@@ -152,9 +154,9 @@ type CreateAIBenchmarkJobOptions struct {
AIBenchmarkJobName string
AIWorkloadConfigIdentifier string
RoleArn string
- BenchmarkTarget json.RawMessage
- OutputConfig json.RawMessage
- NetworkConfig json.RawMessage
+ BenchmarkTarget jsontext.Value
+ OutputConfig jsontext.Value
+ NetworkConfig jsontext.Value
}
// CreateAIBenchmarkJob creates an AI benchmark job and schedules its
diff --git a/services/sagemaker/ai_recommendation_jobs.go b/services/sagemaker/ai_recommendation_jobs.go
index f7dc069dfd..503eff4eec 100644
--- a/services/sagemaker/ai_recommendation_jobs.go
+++ b/services/sagemaker/ai_recommendation_jobs.go
@@ -2,7 +2,8 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -11,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/store"
)
@@ -53,12 +55,12 @@ type AIRecommendationJob struct {
AIWorkloadConfigIdentifier string `json:"AIWorkloadConfigIdentifier"`
RoleArn string `json:"RoleArn"`
FailureReason string `json:"FailureReason,omitempty"`
- ModelSource json.RawMessage `json:"ModelSource"`
- OutputConfig json.RawMessage `json:"OutputConfig"`
- PerformanceTarget json.RawMessage `json:"PerformanceTarget,omitempty"`
- ComputeSpec json.RawMessage `json:"ComputeSpec,omitempty"`
- InferenceSpecification json.RawMessage `json:"InferenceSpecification,omitempty"`
- AdapterSource json.RawMessage `json:"AdapterSource,omitempty"`
+ ModelSource jsontext.Value `json:"ModelSource"`
+ OutputConfig jsontext.Value `json:"OutputConfig"`
+ PerformanceTarget jsontext.Value `json:"PerformanceTarget,omitempty"`
+ ComputeSpec jsontext.Value `json:"ComputeSpec,omitempty"`
+ InferenceSpecification jsontext.Value `json:"InferenceSpecification,omitempty"`
+ AdapterSource jsontext.Value `json:"AdapterSource,omitempty"`
}
// MarshalJSON emits CreationTime/StartTime/EndTime as AWS awsjson1.1
@@ -83,7 +85,7 @@ func (j *AIRecommendationJob) MarshalJSON() ([]byte, error) {
StartTime: epochSecondsPtr(j.StartTime),
EndTime: epochSecondsPtr(j.EndTime),
Tags: toTagObjects(j.Tags),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [AIRecommendationJob.MarshalJSON], used by
@@ -98,7 +100,7 @@ func (j *AIRecommendationJob) UnmarshalJSON(data []byte) error {
CreationTime float64 `json:"CreationTime"`
}{alias: (*alias)(j)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -112,12 +114,12 @@ func (j *AIRecommendationJob) UnmarshalJSON(data []byte) error {
func cloneAIRecommendationJob(j *AIRecommendationJob) *AIRecommendationJob {
cp := *j
cp.Tags = maps.Clone(j.Tags)
- cp.ModelSource = append(json.RawMessage(nil), j.ModelSource...)
- cp.OutputConfig = append(json.RawMessage(nil), j.OutputConfig...)
- cp.PerformanceTarget = append(json.RawMessage(nil), j.PerformanceTarget...)
- cp.ComputeSpec = append(json.RawMessage(nil), j.ComputeSpec...)
- cp.InferenceSpecification = append(json.RawMessage(nil), j.InferenceSpecification...)
- cp.AdapterSource = append(json.RawMessage(nil), j.AdapterSource...)
+ cp.ModelSource = append(jsontext.Value(nil), j.ModelSource...)
+ cp.OutputConfig = append(jsontext.Value(nil), j.OutputConfig...)
+ cp.PerformanceTarget = append(jsontext.Value(nil), j.PerformanceTarget...)
+ cp.ComputeSpec = append(jsontext.Value(nil), j.ComputeSpec...)
+ cp.InferenceSpecification = append(jsontext.Value(nil), j.InferenceSpecification...)
+ cp.AdapterSource = append(jsontext.Value(nil), j.AdapterSource...)
if j.StartTime != nil {
t := *j.StartTime
@@ -169,12 +171,12 @@ type CreateAIRecommendationJobOptions struct {
AIRecommendationJobName string
AIWorkloadConfigIdentifier string
RoleArn string
- ModelSource json.RawMessage
- OutputConfig json.RawMessage
- PerformanceTarget json.RawMessage
- ComputeSpec json.RawMessage
- InferenceSpecification json.RawMessage
- AdapterSource json.RawMessage
+ ModelSource jsontext.Value
+ OutputConfig jsontext.Value
+ PerformanceTarget jsontext.Value
+ ComputeSpec jsontext.Value
+ InferenceSpecification jsontext.Value
+ AdapterSource jsontext.Value
}
// CreateAIRecommendationJob creates an AI recommendation job and schedules
diff --git a/services/sagemaker/ai_workload_configs.go b/services/sagemaker/ai_workload_configs.go
index 1b03a81c39..44fa4c4c1e 100644
--- a/services/sagemaker/ai_workload_configs.go
+++ b/services/sagemaker/ai_workload_configs.go
@@ -2,7 +2,8 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -11,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/store"
)
@@ -49,8 +51,8 @@ type AIWorkloadConfig struct {
Tags map[string]string `json:"Tags,omitempty"`
AIWorkloadConfigName string `json:"AIWorkloadConfigName"`
AIWorkloadConfigArn string `json:"AIWorkloadConfigArn"`
- WorkloadSpec json.RawMessage `json:"AIWorkloadConfigs,omitempty"`
- DatasetConfig json.RawMessage `json:"DatasetConfig,omitempty"`
+ WorkloadSpec jsontext.Value `json:"AIWorkloadConfigs,omitempty"`
+ DatasetConfig jsontext.Value `json:"DatasetConfig,omitempty"`
}
// MarshalJSON emits CreationTime as an AWS awsjson1.1 epoch-seconds number
@@ -69,7 +71,7 @@ func (c *AIWorkloadConfig) MarshalJSON() ([]byte, error) {
alias: (*alias)(c),
Tags: toTagObjects(c.Tags),
CreationTime: epochSeconds(c.CreationTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [AIWorkloadConfig.MarshalJSON], used by
@@ -82,7 +84,7 @@ func (c *AIWorkloadConfig) UnmarshalJSON(data []byte) error {
CreationTime float64 `json:"CreationTime"`
}{alias: (*alias)(c)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -94,8 +96,8 @@ func (c *AIWorkloadConfig) UnmarshalJSON(data []byte) error {
func cloneAIWorkloadConfig(c *AIWorkloadConfig) *AIWorkloadConfig {
cp := *c
cp.Tags = maps.Clone(c.Tags)
- cp.WorkloadSpec = append(json.RawMessage(nil), c.WorkloadSpec...)
- cp.DatasetConfig = append(json.RawMessage(nil), c.DatasetConfig...)
+ cp.WorkloadSpec = append(jsontext.Value(nil), c.WorkloadSpec...)
+ cp.DatasetConfig = append(jsontext.Value(nil), c.DatasetConfig...)
return &cp
}
@@ -170,8 +172,8 @@ func (b *InMemoryBackend) aiWorkloadConfigARNIndexStoreRO(r string) map[string]s
type CreateAIWorkloadConfigOptions struct {
Tags map[string]string
AIWorkloadConfigName string
- WorkloadSpec json.RawMessage
- DatasetConfig json.RawMessage
+ WorkloadSpec jsontext.Value
+ DatasetConfig jsontext.Value
}
// CreateAIWorkloadConfig creates an AI workload configuration.
diff --git a/services/sagemaker/algorithms.go b/services/sagemaker/algorithms.go
index 519f2870c4..9aba6255aa 100644
--- a/services/sagemaker/algorithms.go
+++ b/services/sagemaker/algorithms.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -22,9 +22,9 @@ type CreateAlgorithmOptions struct {
Tags map[string]string
AlgorithmName string
AlgorithmDescription string
- TrainingSpecification json.RawMessage
- InferenceSpecification json.RawMessage
- ValidationSpecification json.RawMessage
+ TrainingSpecification jsontext.Value
+ InferenceSpecification jsontext.Value
+ ValidationSpecification jsontext.Value
CertifyForMarketplace bool
}
diff --git a/services/sagemaker/app_image_configs.go b/services/sagemaker/app_image_configs.go
index b6cf47d81e..624e139573 100644
--- a/services/sagemaker/app_image_configs.go
+++ b/services/sagemaker/app_image_configs.go
@@ -2,7 +2,8 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -11,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrAppImageConfigNotFound is returned when an app image config does not exist.
@@ -32,17 +34,17 @@ type AppImageConfig struct {
Tags map[string]string `json:"Tags,omitempty"`
AppImageConfigName string `json:"AppImageConfigName"`
AppImageConfigArn string `json:"AppImageConfigArn"`
- KernelGatewayImageConfig json.RawMessage `json:"KernelGatewayImageConfig,omitempty"`
- JupyterLabAppImageConfig json.RawMessage `json:"JupyterLabAppImageConfig,omitempty"`
- CodeEditorAppImageConfig json.RawMessage `json:"CodeEditorAppImageConfig,omitempty"`
+ KernelGatewayImageConfig jsontext.Value `json:"KernelGatewayImageConfig,omitempty"`
+ JupyterLabAppImageConfig jsontext.Value `json:"JupyterLabAppImageConfig,omitempty"`
+ CodeEditorAppImageConfig jsontext.Value `json:"CodeEditorAppImageConfig,omitempty"`
}
func cloneAppImageConfig(a *AppImageConfig) *AppImageConfig {
cp := *a
cp.Tags = maps.Clone(a.Tags)
- cp.KernelGatewayImageConfig = append(json.RawMessage(nil), a.KernelGatewayImageConfig...)
- cp.JupyterLabAppImageConfig = append(json.RawMessage(nil), a.JupyterLabAppImageConfig...)
- cp.CodeEditorAppImageConfig = append(json.RawMessage(nil), a.CodeEditorAppImageConfig...)
+ cp.KernelGatewayImageConfig = append(jsontext.Value(nil), a.KernelGatewayImageConfig...)
+ cp.JupyterLabAppImageConfig = append(jsontext.Value(nil), a.JupyterLabAppImageConfig...)
+ cp.CodeEditorAppImageConfig = append(jsontext.Value(nil), a.CodeEditorAppImageConfig...)
return &cp
}
@@ -61,7 +63,7 @@ func (a *AppImageConfig) MarshalJSON() ([]byte, error) {
alias: (*alias)(a),
CreationTime: epochSeconds(a.CreationTime),
LastModifiedTime: epochSeconds(a.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [AppImageConfig.MarshalJSON], read by
@@ -75,7 +77,7 @@ func (a *AppImageConfig) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(a)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -90,9 +92,9 @@ func (a *AppImageConfig) UnmarshalJSON(data []byte) error {
type CreateAppImageConfigOptions struct {
AppImageConfigName string
Tags map[string]string
- KernelGatewayImageConfig json.RawMessage
- JupyterLabAppImageConfig json.RawMessage
- CodeEditorAppImageConfig json.RawMessage
+ KernelGatewayImageConfig jsontext.Value
+ JupyterLabAppImageConfig jsontext.Value
+ CodeEditorAppImageConfig jsontext.Value
}
// CreateAppImageConfig creates an app image config.
@@ -154,9 +156,9 @@ func (b *InMemoryBackend) DescribeAppImageConfig(ctx context.Context, name strin
// an image's kernel/container/filesystem config had every field silently
// dropped -- the same "accept and drop" bug class as UpdateTrainingJob.
type UpdateAppImageConfigOptions struct {
- KernelGatewayImageConfig json.RawMessage
- JupyterLabAppImageConfig json.RawMessage
- CodeEditorAppImageConfig json.RawMessage
+ KernelGatewayImageConfig jsontext.Value
+ JupyterLabAppImageConfig jsontext.Value
+ CodeEditorAppImageConfig jsontext.Value
}
// UpdateAppImageConfig updates an app image config's kernel/container
diff --git a/services/sagemaker/apps.go b/services/sagemaker/apps.go
index 7af757fe6f..434272f753 100644
--- a/services/sagemaker/apps.go
+++ b/services/sagemaker/apps.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"sort"
@@ -50,14 +50,14 @@ type App struct {
AppName string `json:"AppName"`
AppArn string `json:"AppArn"`
Status string `json:"Status"`
- ResourceSpec json.RawMessage `json:"ResourceSpec,omitempty"`
+ ResourceSpec jsontext.Value `json:"ResourceSpec,omitempty"`
RecoveryMode bool `json:"RecoveryMode,omitempty"`
}
func cloneApp(a *App) *App {
cp := *a
cp.Tags = maps.Clone(a.Tags)
- cp.ResourceSpec = append(json.RawMessage(nil), a.ResourceSpec...)
+ cp.ResourceSpec = append(jsontext.Value(nil), a.ResourceSpec...)
return &cp
}
@@ -65,7 +65,7 @@ func cloneApp(a *App) *App {
// CreateAppOptions bundles CreateApp's optional fields.
type CreateAppOptions struct {
SpaceName string
- ResourceSpec json.RawMessage
+ ResourceSpec jsontext.Value
RecoveryMode bool
}
diff --git a/services/sagemaker/automl.go b/services/sagemaker/automl.go
index 069e3b042f..1163878579 100644
--- a/services/sagemaker/automl.go
+++ b/services/sagemaker/automl.go
@@ -2,7 +2,8 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -11,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -100,17 +102,17 @@ type AutoMLJob struct {
AutoMLJobName string `json:"AutoMLJobName"`
InputDataConfig []AutoMLChannel `json:"InputDataConfig"`
AutoMLJobInputDataConfig []AutoMLJobChannel `json:"AutoMLJobInputDataConfig,omitempty"`
- AutoMLProblemTypeConfig json.RawMessage `json:"AutoMLProblemTypeConfig,omitempty"`
+ AutoMLProblemTypeConfig jsontext.Value `json:"AutoMLProblemTypeConfig,omitempty"`
}
// AutoMLV1Options holds the V1 CreateAutoMLJob members that only round-trip
// through DescribeAutoMLJob; none of them drives a simulated training run.
type AutoMLV1Options struct {
- ProblemType string `json:"ProblemType,omitempty"`
- Mode string `json:"Mode,omitempty"`
- CandidateGenerationConfig json.RawMessage `json:"CandidateGenerationConfig,omitempty"`
- CompletionCriteria json.RawMessage `json:"CompletionCriteria,omitempty"`
- GenerateCandidateDefinitionsOnly bool `json:"GenerateCandidateDefinitionsOnly,omitempty"`
+ ProblemType string `json:"ProblemType,omitempty"`
+ Mode string `json:"Mode,omitempty"`
+ CandidateGenerationConfig jsontext.Value `json:"CandidateGenerationConfig,omitempty"`
+ CompletionCriteria jsontext.Value `json:"CompletionCriteria,omitempty"`
+ GenerateCandidateDefinitionsOnly bool `json:"GenerateCandidateDefinitionsOnly,omitempty"`
}
// SetAutoMLJobV1Options stores the V1-only round-trip members on a created job.
@@ -157,13 +159,13 @@ func cloneAutoMLJob(j *AutoMLJob) *AutoMLJob {
if j.V1Options != nil {
v1 := *j.V1Options
- v1.CandidateGenerationConfig = append(json.RawMessage(nil), v1.CandidateGenerationConfig...)
- v1.CompletionCriteria = append(json.RawMessage(nil), v1.CompletionCriteria...)
+ v1.CandidateGenerationConfig = append(jsontext.Value(nil), v1.CandidateGenerationConfig...)
+ v1.CompletionCriteria = append(jsontext.Value(nil), v1.CompletionCriteria...)
cp.V1Options = &v1
}
if j.AutoMLProblemTypeConfig != nil {
- cp.AutoMLProblemTypeConfig = append(json.RawMessage{}, j.AutoMLProblemTypeConfig...)
+ cp.AutoMLProblemTypeConfig = append(jsontext.Value{}, j.AutoMLProblemTypeConfig...)
}
if j.AutoMLComputeConfig != nil {
@@ -205,7 +207,7 @@ func (j *AutoMLJob) MarshalJSON() ([]byte, error) {
EndTime: epochSecondsPtr(j.EndTime),
CreationTime: epochSeconds(j.CreationTime),
LastModifiedTime: epochSeconds(j.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [AutoMLJob.MarshalJSON], read by
@@ -220,7 +222,7 @@ func (j *AutoMLJob) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(j)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/sagemaker/automl_search.go b/services/sagemaker/automl_search.go
index 8500ec2765..540c7eedf3 100644
--- a/services/sagemaker/automl_search.go
+++ b/services/sagemaker/automl_search.go
@@ -2,13 +2,15 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"sort"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Resource-type / property-name string constants shared by Search,
@@ -69,7 +71,7 @@ func (c *AutoMLCandidate) MarshalJSON() ([]byte, error) {
alias: (*alias)(c),
CreationTime: epochSeconds(c.CreationTime),
LastModifiedTime: epochSeconds(c.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// generateAutoMLCandidates deterministically derives a set of candidates for
@@ -361,13 +363,13 @@ func matchesSearchFilter(flat map[string]any, f SearchFilter) bool {
// []map[string]any via a JSON round trip, so its fields can be addressed by
// the JSON dotted path a NestedFilters entry documents.
func toObjectList(raw any) ([]map[string]any, bool) {
- b, marshalErr := json.Marshal(raw)
+ b, marshalErr := json.Marshal(raw, jsonx.V1())
if marshalErr != nil {
return nil, false
}
var items []map[string]any
- if unmarshalErr := json.Unmarshal(b, &items); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(b, &items, jsonx.V1()); unmarshalErr != nil {
return nil, false
}
diff --git a/services/sagemaker/automl_v2.go b/services/sagemaker/automl_v2.go
index a5765d30bf..fb08f0c0ce 100644
--- a/services/sagemaker/automl_v2.go
+++ b/services/sagemaker/automl_v2.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// AutoMLJobChannel is a named input source for CreateAutoMLJobV2/
@@ -63,13 +66,13 @@ var automlProblemTypeConfigNames = map[string]string{
// automlProblemTypeConfigName derives AutoMLProblemTypeConfigName from the
// single member key present in an opaque AutoMLProblemTypeConfig payload.
-func automlProblemTypeConfigName(raw json.RawMessage) string {
+func automlProblemTypeConfigName(raw jsontext.Value) string {
if len(raw) == 0 {
return ""
}
- var members map[string]json.RawMessage
- if err := json.Unmarshal(raw, &members); err != nil {
+ var members map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &members, jsonx.V1()); err != nil {
return ""
}
@@ -85,7 +88,7 @@ func automlProblemTypeConfigName(raw json.RawMessage) string {
// AutoMLJobV2Extras bundles the CreateAutoMLJobV2-only fields set after the
// shared CreateAutoMLJob record is created.
type AutoMLJobV2Extras struct {
- AutoMLProblemTypeConfig json.RawMessage
+ AutoMLProblemTypeConfig jsontext.Value
OutputDataConfig *AutoMLOutputDataConfig
AutoMLJobObjective *AutoMLJobObjective
AutoMLComputeConfig *AutoMLComputeConfig
@@ -113,7 +116,7 @@ func (b *InMemoryBackend) SetAutoMLJobV2Extras(ctx context.Context, name string,
j.AutoMLJobInputDataConfig = append([]AutoMLJobChannel(nil), extras.AutoMLJobInputDataConfig...)
if extras.AutoMLProblemTypeConfig != nil {
- j.AutoMLProblemTypeConfig = append(json.RawMessage(nil), extras.AutoMLProblemTypeConfig...)
+ j.AutoMLProblemTypeConfig = append(jsontext.Value(nil), extras.AutoMLProblemTypeConfig...)
}
if extras.OutputDataConfig != nil {
diff --git a/services/sagemaker/code_repositories.go b/services/sagemaker/code_repositories.go
index 12babd9ec7..8453584687 100644
--- a/services/sagemaker/code_repositories.go
+++ b/services/sagemaker/code_repositories.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -11,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrCodeRepositoryNotFound is returned when a code repository does not exist.
@@ -55,7 +56,7 @@ func (r *CodeRepository) MarshalJSON() ([]byte, error) {
alias: (*alias)(r),
CreationTime: epochSeconds(r.CreationTime),
LastModifiedTime: epochSeconds(r.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [CodeRepository.MarshalJSON], read by
@@ -69,7 +70,7 @@ func (r *CodeRepository) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(r)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/sagemaker/compilation_jobs.go b/services/sagemaker/compilation_jobs.go
index 9367943c1e..ca56d54625 100644
--- a/services/sagemaker/compilation_jobs.go
+++ b/services/sagemaker/compilation_jobs.go
@@ -2,7 +2,8 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -11,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -87,9 +89,9 @@ type CompilationJob struct {
// DescribeCompilationJobOutput (api_op_DescribeCompilationJob.go:67-69)
// even though it is legitimately empty for every non-failed job -- the
// common case.
- FailureReason string `json:"FailureReason"`
- ModelPackageVersionArn string `json:"ModelPackageVersionArn,omitempty"`
- VpcConfig json.RawMessage `json:"VpcConfig,omitempty"`
+ FailureReason string `json:"FailureReason"`
+ ModelPackageVersionArn string `json:"ModelPackageVersionArn,omitempty"`
+ VpcConfig jsontext.Value `json:"VpcConfig,omitempty"`
}
func cloneCompilationJob(j *CompilationJob) *CompilationJob {
@@ -121,7 +123,7 @@ func cloneCompilationJob(j *CompilationJob) *CompilationJob {
cp.ModelArtifacts = &ma
}
- cp.VpcConfig = append(json.RawMessage(nil), j.VpcConfig...)
+ cp.VpcConfig = append(jsontext.Value(nil), j.VpcConfig...)
if j.CompilationStartTime != nil {
t := *j.CompilationStartTime
@@ -155,7 +157,7 @@ func (j *CompilationJob) MarshalJSON() ([]byte, error) {
LastModifiedTime: epochSeconds(j.LastModifiedTime),
CompilationStartTime: epochSecondsPtr(j.CompilationStartTime),
CompilationEndTime: epochSecondsPtr(j.CompilationEndTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [CompilationJob.MarshalJSON], read by
@@ -171,7 +173,7 @@ func (j *CompilationJob) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(j)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -431,7 +433,7 @@ func (b *InMemoryBackend) SetCompilationJobExtras(
outputConfig *CompilationOutputConfig,
stoppingCondition *StoppingCondition,
modelPackageVersionArn string,
- vpcConfig json.RawMessage,
+ vpcConfig jsontext.Value,
) error {
b.mu.Lock("SetCompilationJobExtras")
defer b.mu.Unlock()
diff --git a/services/sagemaker/device_fleets.go b/services/sagemaker/device_fleets.go
index 1abc1a9e37..cccb94320a 100644
--- a/services/sagemaker/device_fleets.go
+++ b/services/sagemaker/device_fleets.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -11,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/store"
)
@@ -76,7 +77,7 @@ func (f *DeviceFleet) MarshalJSON() ([]byte, error) {
alias: (*alias)(f),
CreationTime: epochSeconds(f.CreationTime),
LastModifiedTime: epochSeconds(f.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [DeviceFleet.MarshalJSON], read by
@@ -90,7 +91,7 @@ func (f *DeviceFleet) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(f)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -379,7 +380,7 @@ func (d *Device) MarshalJSON() ([]byte, error) {
alias: (*alias)(d),
RegistrationTime: epochSeconds(d.RegistrationTime),
LastModifiedTime: epochSeconds(d.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [Device.MarshalJSON], read by
@@ -393,7 +394,7 @@ func (d *Device) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(d)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/sagemaker/domains.go b/services/sagemaker/domains.go
index 0e56cc0652..38b4360ae5 100644
--- a/services/sagemaker/domains.go
+++ b/services/sagemaker/domains.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"sort"
@@ -43,18 +43,18 @@ type Domain struct {
VpcID string `json:"VpcId,omitempty"`
TagPropagation string `json:"TagPropagation,omitempty"`
SubnetIDs []string `json:"SubnetIds,omitempty"`
- DefaultUserSettings json.RawMessage `json:"DefaultUserSettings,omitempty"`
- DefaultSpaceSettings json.RawMessage `json:"DefaultSpaceSettings,omitempty"`
- DomainSettings json.RawMessage `json:"DomainSettings,omitempty"`
+ DefaultUserSettings jsontext.Value `json:"DefaultUserSettings,omitempty"`
+ DefaultSpaceSettings jsontext.Value `json:"DefaultSpaceSettings,omitempty"`
+ DomainSettings jsontext.Value `json:"DomainSettings,omitempty"`
}
func cloneDomain(d *Domain) *Domain {
cp := *d
cp.Tags = maps.Clone(d.Tags)
cp.SubnetIDs = append([]string(nil), d.SubnetIDs...)
- cp.DefaultUserSettings = append(json.RawMessage(nil), d.DefaultUserSettings...)
- cp.DefaultSpaceSettings = append(json.RawMessage(nil), d.DefaultSpaceSettings...)
- cp.DomainSettings = append(json.RawMessage(nil), d.DomainSettings...)
+ cp.DefaultUserSettings = append(jsontext.Value(nil), d.DefaultUserSettings...)
+ cp.DefaultSpaceSettings = append(jsontext.Value(nil), d.DefaultSpaceSettings...)
+ cp.DomainSettings = append(jsontext.Value(nil), d.DomainSettings...)
return &cp
}
@@ -72,9 +72,9 @@ type CreateDomainOptions struct {
VpcID string
TagPropagation string
SubnetIDs []string
- DefaultUserSettings json.RawMessage
- DefaultSpaceSettings json.RawMessage
- DomainSettings json.RawMessage
+ DefaultUserSettings jsontext.Value
+ DefaultSpaceSettings jsontext.Value
+ DomainSettings jsontext.Value
}
// CreateDomain creates a new SageMaker Studio domain.
@@ -198,9 +198,9 @@ type UpdateDomainOptions struct {
TagPropagation string
VpcID string
SubnetIDs []string
- DefaultUserSettings json.RawMessage
- DefaultSpaceSettings json.RawMessage
- DomainSettingsForUpdate json.RawMessage
+ DefaultUserSettings jsontext.Value
+ DefaultSpaceSettings jsontext.Value
+ DomainSettingsForUpdate jsontext.Value
}
// applyUpdateDomainOptions overwrites d's overridable fields with any
diff --git a/services/sagemaker/edge_deployment.go b/services/sagemaker/edge_deployment.go
index 61441d1d36..d9388eb908 100644
--- a/services/sagemaker/edge_deployment.go
+++ b/services/sagemaker/edge_deployment.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"sort"
@@ -43,20 +43,20 @@ type EdgeDeploymentModelConfig struct {
// device selection and deployment configs are stored opaquely (as submitted)
// since this emulator does not act on them beyond round-tripping.
type EdgeDeploymentStage struct {
- StageName string `json:"StageName"`
- DeploymentStatus string `json:"DeploymentStatus"`
- DeviceSelectionConfig json.RawMessage `json:"DeviceSelectionConfig,omitempty"`
- DeploymentConfig json.RawMessage `json:"DeploymentConfig,omitempty"`
+ StageName string `json:"StageName"`
+ DeploymentStatus string `json:"DeploymentStatus"`
+ DeviceSelectionConfig jsontext.Value `json:"DeviceSelectionConfig,omitempty"`
+ DeploymentConfig jsontext.Value `json:"DeploymentConfig,omitempty"`
}
func cloneEdgeDeploymentStage(s *EdgeDeploymentStage) *EdgeDeploymentStage {
cp := *s
if s.DeviceSelectionConfig != nil {
- cp.DeviceSelectionConfig = append(json.RawMessage(nil), s.DeviceSelectionConfig...)
+ cp.DeviceSelectionConfig = append(jsontext.Value(nil), s.DeviceSelectionConfig...)
}
if s.DeploymentConfig != nil {
- cp.DeploymentConfig = append(json.RawMessage(nil), s.DeploymentConfig...)
+ cp.DeploymentConfig = append(jsontext.Value(nil), s.DeploymentConfig...)
}
return &cp
@@ -96,8 +96,8 @@ func cloneEdgeDeploymentPlan(p *EdgeDeploymentPlan) *EdgeDeploymentPlan {
// CreateEdgeDeploymentPlan or CreateEdgeDeploymentStage.
type EdgeDeploymentStageInput struct {
StageName string
- DeviceSelectionConfig json.RawMessage
- DeploymentConfig json.RawMessage
+ DeviceSelectionConfig jsontext.Value
+ DeploymentConfig jsontext.Value
}
func buildEdgeDeploymentStages(inputs []EdgeDeploymentStageInput) []*EdgeDeploymentStage {
diff --git a/services/sagemaker/endpoint_configs.go b/services/sagemaker/endpoint_configs.go
index 1e83328fa2..9fb6ddadb6 100644
--- a/services/sagemaker/endpoint_configs.go
+++ b/services/sagemaker/endpoint_configs.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -143,7 +143,7 @@ func (b *InMemoryBackend) SetEndpointConfigExtras(
kmsKeyID string,
shadowProductionVariants []ProductionVariant,
enableNetworkIsolation bool,
- explainerConfig, metricsConfig json.RawMessage,
+ explainerConfig, metricsConfig jsontext.Value,
) error {
b.mu.Lock("SetEndpointConfigExtras")
defer b.mu.Unlock()
@@ -177,11 +177,11 @@ func (b *InMemoryBackend) SetEndpointConfigExtras(
ec.EnableNetworkIsolation = enableNetworkIsolation
if len(explainerConfig) > 0 {
- ec.ExplainerConfig = append(json.RawMessage(nil), explainerConfig...)
+ ec.ExplainerConfig = append(jsontext.Value(nil), explainerConfig...)
}
if len(metricsConfig) > 0 {
- ec.MetricsConfig = append(json.RawMessage(nil), metricsConfig...)
+ ec.MetricsConfig = append(jsontext.Value(nil), metricsConfig...)
}
if len(shadowProductionVariants) > 0 {
diff --git a/services/sagemaker/endpoints.go b/services/sagemaker/endpoints.go
index 35f6bf0395..1eb2edd2a3 100644
--- a/services/sagemaker/endpoints.go
+++ b/services/sagemaker/endpoints.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"slices"
@@ -40,7 +40,7 @@ type Endpoint struct {
FailureReason string `json:"FailureReason,omitempty"`
ProductionVariants []ProductionVariantSummary `json:"ProductionVariants,omitempty"`
ShadowProductionVariants []ProductionVariantSummary `json:"ShadowProductionVariants,omitempty"`
- DeploymentConfig json.RawMessage `json:"DeploymentConfig,omitempty"`
+ DeploymentConfig jsontext.Value `json:"DeploymentConfig,omitempty"`
}
// ProductionVariantStatus describes the current deployment stage of a
@@ -172,7 +172,7 @@ type CreateEndpointOptions struct {
Tags map[string]string
Name string
EndpointConfigName string
- DeploymentConfig json.RawMessage
+ DeploymentConfig jsontext.Value
}
// CreateEndpoint creates a new SageMaker endpoint.
@@ -379,7 +379,7 @@ func (b *InMemoryBackend) finishEndpointDelete(region, name string) {
// DataCaptureConfig is endpoint-wide here, not per-variant, so that
// VariantPropertyType value has no effect to exclude.
type UpdateEndpointOptions struct {
- DeploymentConfig json.RawMessage
+ DeploymentConfig jsontext.Value
EndpointConfigName string
ExcludeRetainedVariantProperties []string
RetainAllVariantProperties bool
diff --git a/services/sagemaker/engine_async_test.go b/services/sagemaker/engine_async_test.go
index c9c950688f..797cf4c414 100644
--- a/services/sagemaker/engine_async_test.go
+++ b/services/sagemaker/engine_async_test.go
@@ -1,13 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -137,7 +138,7 @@ func TestEngineAsync_ResultsAndNotifications(t *testing.T) {
assert.Contains(t, string(data), tt.wantOutValue)
var note map[string]any
- require.NoError(t, json.Unmarshal([]byte(rig.sns.messages(tt.wantTopic)[0]), ¬e))
+ require.NoError(t, json.Unmarshal([]byte(rig.sns.messages(tt.wantTopic)[0]), ¬e, jsonx.V1()))
assert.Equal(t, tt.wantStatus, note["invocationStatus"])
assert.Equal(t, tt.req.InferenceID, note["inferenceId"])
assert.Equal(t, "us-east-1", note["awsRegion"])
diff --git a/services/sagemaker/engine_files.go b/services/sagemaker/engine_files.go
index 8c1d08278f..0f3527e7fe 100644
--- a/services/sagemaker/engine_files.go
+++ b/services/sagemaker/engine_files.go
@@ -5,7 +5,8 @@ import (
"bytes"
"compress/gzip"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +17,8 @@ import (
"path/filepath"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -254,8 +257,8 @@ func stageManifest(ctx context.Context, store ObjectStore, bucket, key, dir stri
return fmt.Errorf("read manifest s3://%s/%s: %w", bucket, key, err)
}
- var entries []json.RawMessage
- if err = json.Unmarshal(raw, &entries); err != nil {
+ var entries []jsontext.Value
+ if err = json.Unmarshal(raw, &entries, jsonx.V1()); err != nil {
return fmt.Errorf("parse manifest s3://%s/%s: %w", bucket, key, err)
}
@@ -263,14 +266,14 @@ func stageManifest(ctx context.Context, store ObjectStore, bucket, key, dir stri
for i, ent := range entries {
var obj map[string]string
- if json.Unmarshal(ent, &obj) == nil {
+ if json.Unmarshal(ent, &obj, jsonx.V1()) == nil {
prefix = obj[manifestPrefixK]
continue
}
var name string
- if err = json.Unmarshal(ent, &name); err != nil || prefix == "" {
+ if err = json.Unmarshal(ent, &name, jsonx.V1()); err != nil || prefix == "" {
return fmt.Errorf("manifest s3://%s/%s: entry %d: %w", bucket, key, i, errManifestEntry)
}
@@ -397,7 +400,7 @@ func getS3URI(ctx context.Context, store ObjectStore, uri string) ([]byte, error
// writeJSONFile marshals v to dir/name.
func writeJSONFile(dir, name string, v any) error {
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Errorf("marshal %s: %w", name, err)
}
diff --git a/services/sagemaker/engine_invoke.go b/services/sagemaker/engine_invoke.go
index 71a2f49a3f..c060f98b43 100644
--- a/services/sagemaker/engine_invoke.go
+++ b/services/sagemaker/engine_invoke.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -12,6 +12,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const maxResponseBytes = 25 << 20
@@ -315,7 +316,7 @@ func (b *InMemoryBackend) notifyAsync(
msg["eventSource"] = "aws:sagemaker"
msg["eventName"] = "InferenceResult"
- data, err := json.Marshal(msg)
+ data, err := json.Marshal(msg, jsonx.V1())
if err != nil {
return
}
diff --git a/services/sagemaker/engine_jobs_test.go b/services/sagemaker/engine_jobs_test.go
index 95310d7c70..c0dbec6043 100644
--- a/services/sagemaker/engine_jobs_test.go
+++ b/services/sagemaker/engine_jobs_test.go
@@ -5,7 +5,7 @@ import (
"bytes"
"compress/gzip"
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"os"
@@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -215,11 +216,17 @@ func TestEngineTraining_ConfigFilesAndStop(t *testing.T) {
cfg := filepath.Join(mountSource(spec, "/opt/ml/input"), "config")
var res map[string]any
- require.NoError(t, json.Unmarshal([]byte(readFileOrEmpty(filepath.Join(cfg, "resourceconfig.json"))), &res))
+ require.NoError(
+ t,
+ json.Unmarshal([]byte(readFileOrEmpty(filepath.Join(cfg, "resourceconfig.json"))), &res, jsonx.V1()),
+ )
assert.Equal(t, "algo-1", res["current_host"])
var chans map[string]map[string]string
- require.NoError(t, json.Unmarshal([]byte(readFileOrEmpty(filepath.Join(cfg, "inputdataconfig.json"))), &chans))
+ require.NoError(
+ t,
+ json.Unmarshal([]byte(readFileOrEmpty(filepath.Join(cfg, "inputdataconfig.json"))), &chans, jsonx.V1()),
+ )
assert.Equal(t, "text/csv", chans["train"]["ContentType"])
assert.Equal(t, "File", chans["train"]["TrainingInputMode"])
diff --git a/services/sagemaker/engine_training.go b/services/sagemaker/engine_training.go
index 1a4572e393..cbeb40ee24 100644
--- a/services/sagemaker/engine_training.go
+++ b/services/sagemaker/engine_training.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"os"
@@ -11,6 +11,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -94,7 +95,7 @@ func (b *InMemoryBackend) trainingImage(region string, spec AlgorithmSpecificati
TrainingImage string `json:"TrainingImage"`
}
- if err := json.Unmarshal(alg.TrainingSpecification, &ts); err != nil || ts.TrainingImage == "" {
+ if err := json.Unmarshal(alg.TrainingSpecification, &ts, jsonx.V1()); err != nil || ts.TrainingImage == "" {
return "", fmt.Errorf("%w: %s", errAlgorithmNoImage, spec.AlgorithmName)
}
diff --git a/services/sagemaker/flow_definitions.go b/services/sagemaker/flow_definitions.go
index 6db6dcec01..67359b2a6c 100644
--- a/services/sagemaker/flow_definitions.go
+++ b/services/sagemaker/flow_definitions.go
@@ -2,13 +2,14 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrFlowDefinitionNotFound is returned when a flow definition does not exist.
@@ -128,7 +129,7 @@ func (f *FlowDefinition) MarshalJSON() ([]byte, error) {
}
}
- return json.Marshal(aux)
+ return json.Marshal(aux, jsonx.V1())
}
// UnmarshalJSON is the inverse of [FlowDefinition.MarshalJSON], read by
@@ -145,7 +146,7 @@ func (f *FlowDefinition) UnmarshalJSON(data []byte) error {
CreationTime float64 `json:"CreationTime"`
}{alias: (*alias)(f)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/sagemaker/handler.go b/services/sagemaker/handler.go
index 267516f8eb..850a15de22 100644
--- a/services/sagemaker/handler.go
+++ b/services/sagemaker/handler.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -908,7 +909,7 @@ func writeInternalServerError(c *echo.Context) error {
payload, err := json.Marshal(map[string]string{
keyTypeField: "InternalFailure",
keyMessageField: "internal server error",
- })
+ }, jsonx.V1())
if err != nil {
return err
}
@@ -925,15 +926,12 @@ func wireMessage(err error) string {
}
func (h *Handler) handleError(c *echo.Context, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, awserr.ErrInvalidParameter):
payload, _ := json.Marshal(map[string]string{
keyTypeField: errValidationException,
keyMessageField: wireMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
case errors.Is(err, ErrResourceNotFound):
@@ -945,14 +943,14 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
payload, _ := json.Marshal(map[string]string{
keyTypeField: "ResourceNotFound",
keyMessageField: wireMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
case errors.Is(err, awserr.ErrNotFound):
payload, _ := json.Marshal(map[string]string{
keyTypeField: errValidationException,
keyMessageField: wireMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
case errors.Is(err, ErrConflictException):
@@ -964,21 +962,21 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
payload, _ := json.Marshal(map[string]string{
keyTypeField: "ConflictException",
keyMessageField: wireMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
case errors.Is(err, awserr.ErrConflict):
payload, _ := json.Marshal(map[string]string{
keyTypeField: "ResourceInUse",
keyMessageField: wireMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
- case errors.Is(err, errInvalidRequest), errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ case errors.Is(err, errInvalidRequest), jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
payload, _ := json.Marshal(map[string]string{
keyTypeField: errValidationException,
keyMessageField: wireMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
case errors.Is(err, errUnknownAction):
@@ -1072,5 +1070,5 @@ func listResp(key string, items []map[string]any, nextToken string) ([]byte, err
resp[keyNextToken] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/sagemaker/handler_ai_benchmark_jobs.go b/services/sagemaker/handler_ai_benchmark_jobs.go
index 4f47d79673..088b23c7d1 100644
--- a/services/sagemaker/handler_ai_benchmark_jobs.go
+++ b/services/sagemaker/handler_ai_benchmark_jobs.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// aiBenchmarkJobOpsSupported returns the operations handled by
@@ -51,19 +54,19 @@ func (h *Handler) dispatchAIBenchmarkJobOps(
// createAIBenchmarkJobInput mirrors CreateAIBenchmarkJobInput
// (api_op_CreateAIBenchmarkJob.go:33-73).
type createAIBenchmarkJobInput struct {
- BenchmarkTarget json.RawMessage `json:"BenchmarkTarget"`
- OutputConfig json.RawMessage `json:"OutputConfig"`
- NetworkConfig json.RawMessage `json:"NetworkConfig"`
- AIBenchmarkJobName string `json:"AIBenchmarkJobName"`
- AIWorkloadConfigIdentifier string `json:"AIWorkloadConfigIdentifier"`
- RoleArn string `json:"RoleArn"`
- Tags []tagObject `json:"Tags"`
+ BenchmarkTarget jsontext.Value `json:"BenchmarkTarget"`
+ OutputConfig jsontext.Value `json:"OutputConfig"`
+ NetworkConfig jsontext.Value `json:"NetworkConfig"`
+ AIBenchmarkJobName string `json:"AIBenchmarkJobName"`
+ AIWorkloadConfigIdentifier string `json:"AIWorkloadConfigIdentifier"`
+ RoleArn string `json:"RoleArn"`
+ Tags []tagObject `json:"Tags"`
}
func (h *Handler) handleCreateAIBenchmarkJob(ctx context.Context, body []byte) ([]byte, error) {
var req createAIBenchmarkJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -84,7 +87,7 @@ func (h *Handler) handleCreateAIBenchmarkJob(ctx context.Context, body []byte) (
return nil, err
}
- return json.Marshal(map[string]string{keyAIBenchmarkJobArn: j.AIBenchmarkJobArn})
+ return json.Marshal(map[string]string{keyAIBenchmarkJobArn: j.AIBenchmarkJobArn}, jsonx.V1())
}
// describeAIBenchmarkJobInput mirrors DescribeAIBenchmarkJobInput
@@ -96,7 +99,7 @@ type describeAIBenchmarkJobInput struct {
func (h *Handler) handleDescribeAIBenchmarkJob(ctx context.Context, body []byte) ([]byte, error) {
var req describeAIBenchmarkJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -109,7 +112,7 @@ func (h *Handler) handleDescribeAIBenchmarkJob(ctx context.Context, body []byte)
return nil, err
}
- return json.Marshal(j)
+ return json.Marshal(j, jsonx.V1())
}
// deleteAIBenchmarkJobInput mirrors DeleteAIBenchmarkJobInput
@@ -121,7 +124,7 @@ type deleteAIBenchmarkJobInput struct {
func (h *Handler) handleDeleteAIBenchmarkJob(ctx context.Context, body []byte) ([]byte, error) {
var req deleteAIBenchmarkJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -134,7 +137,7 @@ func (h *Handler) handleDeleteAIBenchmarkJob(ctx context.Context, body []byte) (
return nil, err
}
- return json.Marshal(map[string]string{keyAIBenchmarkJobArn: jobARN})
+ return json.Marshal(map[string]string{keyAIBenchmarkJobArn: jobARN}, jsonx.V1())
}
// stopAIBenchmarkJobInput mirrors StopAIBenchmarkJobInput
@@ -146,7 +149,7 @@ type stopAIBenchmarkJobInput struct {
func (h *Handler) handleStopAIBenchmarkJob(ctx context.Context, body []byte) ([]byte, error) {
var req stopAIBenchmarkJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -159,7 +162,7 @@ func (h *Handler) handleStopAIBenchmarkJob(ctx context.Context, body []byte) ([]
return nil, err
}
- return json.Marshal(map[string]string{keyAIBenchmarkJobArn: jobARN})
+ return json.Marshal(map[string]string{keyAIBenchmarkJobArn: jobARN}, jsonx.V1())
}
// listAIBenchmarkJobsInput mirrors ListAIBenchmarkJobsInput
@@ -178,7 +181,7 @@ type listAIBenchmarkJobsInput struct {
func (h *Handler) handleListAIBenchmarkJobs(ctx context.Context, body []byte) ([]byte, error) {
var req listAIBenchmarkJobsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_ai_recommendation_jobs.go b/services/sagemaker/handler_ai_recommendation_jobs.go
index 6c4114078b..af4b151d5a 100644
--- a/services/sagemaker/handler_ai_recommendation_jobs.go
+++ b/services/sagemaker/handler_ai_recommendation_jobs.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// aiRecommendationJobOpsSupported returns the operations handled by
@@ -51,23 +54,23 @@ func (h *Handler) dispatchAIRecommendationJobOps(
// createAIRecommendationJobInput mirrors CreateAIRecommendationJobInput
// (api_op_CreateAIRecommendationJob.go:30-90).
type createAIRecommendationJobInput struct {
- ModelSource json.RawMessage `json:"ModelSource"`
- OutputConfig json.RawMessage `json:"OutputConfig"`
- PerformanceTarget json.RawMessage `json:"PerformanceTarget"`
- ComputeSpec json.RawMessage `json:"ComputeSpec"`
- InferenceSpecification json.RawMessage `json:"InferenceSpecification"`
- AdapterSource json.RawMessage `json:"AdapterSource"`
- OptimizeModel *bool `json:"OptimizeModel"`
- AIRecommendationJobName string `json:"AIRecommendationJobName"`
- AIWorkloadConfigIdentifier string `json:"AIWorkloadConfigIdentifier"`
- RoleArn string `json:"RoleArn"`
- Tags []tagObject `json:"Tags"`
+ ModelSource jsontext.Value `json:"ModelSource"`
+ OutputConfig jsontext.Value `json:"OutputConfig"`
+ PerformanceTarget jsontext.Value `json:"PerformanceTarget"`
+ ComputeSpec jsontext.Value `json:"ComputeSpec"`
+ InferenceSpecification jsontext.Value `json:"InferenceSpecification"`
+ AdapterSource jsontext.Value `json:"AdapterSource"`
+ OptimizeModel *bool `json:"OptimizeModel"`
+ AIRecommendationJobName string `json:"AIRecommendationJobName"`
+ AIWorkloadConfigIdentifier string `json:"AIWorkloadConfigIdentifier"`
+ RoleArn string `json:"RoleArn"`
+ Tags []tagObject `json:"Tags"`
}
func (h *Handler) handleCreateAIRecommendationJob(ctx context.Context, body []byte) ([]byte, error) {
var req createAIRecommendationJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -92,7 +95,7 @@ func (h *Handler) handleCreateAIRecommendationJob(ctx context.Context, body []by
return nil, err
}
- return json.Marshal(map[string]string{keyAIRecommendationJobArn: j.AIRecommendationJobArn})
+ return json.Marshal(map[string]string{keyAIRecommendationJobArn: j.AIRecommendationJobArn}, jsonx.V1())
}
// describeAIRecommendationJobInput mirrors DescribeAIRecommendationJobInput
@@ -104,7 +107,7 @@ type describeAIRecommendationJobInput struct {
func (h *Handler) handleDescribeAIRecommendationJob(ctx context.Context, body []byte) ([]byte, error) {
var req describeAIRecommendationJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -117,7 +120,7 @@ func (h *Handler) handleDescribeAIRecommendationJob(ctx context.Context, body []
return nil, err
}
- return json.Marshal(j)
+ return json.Marshal(j, jsonx.V1())
}
// deleteAIRecommendationJobInput mirrors DeleteAIRecommendationJobInput
@@ -129,7 +132,7 @@ type deleteAIRecommendationJobInput struct {
func (h *Handler) handleDeleteAIRecommendationJob(ctx context.Context, body []byte) ([]byte, error) {
var req deleteAIRecommendationJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -142,7 +145,7 @@ func (h *Handler) handleDeleteAIRecommendationJob(ctx context.Context, body []by
return nil, err
}
- return json.Marshal(map[string]string{keyAIRecommendationJobArn: jobARN})
+ return json.Marshal(map[string]string{keyAIRecommendationJobArn: jobARN}, jsonx.V1())
}
// stopAIRecommendationJobInput mirrors StopAIRecommendationJobInput
@@ -154,7 +157,7 @@ type stopAIRecommendationJobInput struct {
func (h *Handler) handleStopAIRecommendationJob(ctx context.Context, body []byte) ([]byte, error) {
var req stopAIRecommendationJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -167,7 +170,7 @@ func (h *Handler) handleStopAIRecommendationJob(ctx context.Context, body []byte
return nil, err
}
- return json.Marshal(map[string]string{keyAIRecommendationJobArn: jobARN})
+ return json.Marshal(map[string]string{keyAIRecommendationJobArn: jobARN}, jsonx.V1())
}
// listAIRecommendationJobsInput mirrors ListAIRecommendationJobsInput
@@ -186,7 +189,7 @@ type listAIRecommendationJobsInput struct {
func (h *Handler) handleListAIRecommendationJobs(ctx context.Context, body []byte) ([]byte, error) {
var req listAIRecommendationJobsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_ai_workload_configs.go b/services/sagemaker/handler_ai_workload_configs.go
index 17ea80b720..2fa8fe35c1 100644
--- a/services/sagemaker/handler_ai_workload_configs.go
+++ b/services/sagemaker/handler_ai_workload_configs.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// aiWorkloadConfigOpsSupported returns the operations handled by
@@ -44,16 +47,16 @@ func (h *Handler) dispatchAIWorkloadConfigOps(
}
type createAIWorkloadConfigRequest struct {
- WorkloadSpec json.RawMessage `json:"AIWorkloadConfigs"`
- DatasetConfig json.RawMessage `json:"DatasetConfig"`
- AIWorkloadConfigName string `json:"AIWorkloadConfigName"`
- Tags []tagObject `json:"Tags"`
+ WorkloadSpec jsontext.Value `json:"AIWorkloadConfigs"`
+ DatasetConfig jsontext.Value `json:"DatasetConfig"`
+ AIWorkloadConfigName string `json:"AIWorkloadConfigName"`
+ Tags []tagObject `json:"Tags"`
}
func (h *Handler) handleCreateAIWorkloadConfig(ctx context.Context, body []byte) ([]byte, error) {
var req createAIWorkloadConfigRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -71,7 +74,7 @@ func (h *Handler) handleCreateAIWorkloadConfig(ctx context.Context, body []byte)
return nil, err
}
- return json.Marshal(map[string]string{keyAIWorkloadConfigArn: c.AIWorkloadConfigArn})
+ return json.Marshal(map[string]string{keyAIWorkloadConfigArn: c.AIWorkloadConfigArn}, jsonx.V1())
}
type describeAIWorkloadConfigRequest struct {
@@ -81,7 +84,7 @@ type describeAIWorkloadConfigRequest struct {
func (h *Handler) handleDescribeAIWorkloadConfig(ctx context.Context, body []byte) ([]byte, error) {
var req describeAIWorkloadConfigRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -94,7 +97,7 @@ func (h *Handler) handleDescribeAIWorkloadConfig(ctx context.Context, body []byt
return nil, err
}
- return json.Marshal(c)
+ return json.Marshal(c, jsonx.V1())
}
type deleteAIWorkloadConfigRequest struct {
@@ -104,7 +107,7 @@ type deleteAIWorkloadConfigRequest struct {
func (h *Handler) handleDeleteAIWorkloadConfig(ctx context.Context, body []byte) ([]byte, error) {
var req deleteAIWorkloadConfigRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -117,7 +120,7 @@ func (h *Handler) handleDeleteAIWorkloadConfig(ctx context.Context, body []byte)
return nil, err
}
- return json.Marshal(map[string]string{keyAIWorkloadConfigArn: configARN})
+ return json.Marshal(map[string]string{keyAIWorkloadConfigArn: configARN}, jsonx.V1())
}
type listAIWorkloadConfigsRequest struct {
@@ -133,7 +136,7 @@ type listAIWorkloadConfigsRequest struct {
func (h *Handler) handleListAIWorkloadConfigs(ctx context.Context, body []byte) ([]byte, error) {
var req listAIWorkloadConfigsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_algorithms.go b/services/sagemaker/handler_algorithms.go
index 50a8ea696a..31eb49941a 100644
--- a/services/sagemaker/handler_algorithms.go
+++ b/services/sagemaker/handler_algorithms.go
@@ -2,26 +2,28 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
// createAlgorithmRequest is the request body for CreateAlgorithm.
type createAlgorithmRequest struct {
- TrainingSpecification json.RawMessage `json:"TrainingSpecification,omitempty"`
- InferenceSpecification json.RawMessage `json:"InferenceSpecification,omitempty"`
- ValidationSpecification json.RawMessage `json:"ValidationSpecification,omitempty"`
- AlgorithmName string `json:"AlgorithmName"`
- AlgorithmDescription string `json:"AlgorithmDescription,omitempty"`
- Tags []tagObject `json:"Tags"`
- CertifyForMarketplace bool `json:"CertifyForMarketplace,omitempty"`
+ TrainingSpecification jsontext.Value `json:"TrainingSpecification,omitempty"`
+ InferenceSpecification jsontext.Value `json:"InferenceSpecification,omitempty"`
+ ValidationSpecification jsontext.Value `json:"ValidationSpecification,omitempty"`
+ AlgorithmName string `json:"AlgorithmName"`
+ AlgorithmDescription string `json:"AlgorithmDescription,omitempty"`
+ Tags []tagObject `json:"Tags"`
+ CertifyForMarketplace bool `json:"CertifyForMarketplace,omitempty"`
}
func (h *Handler) handleCreateAlgorithm(ctx context.Context, body []byte) ([]byte, error) {
var req createAlgorithmRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -55,7 +57,7 @@ func (h *Handler) handleCreateAlgorithm(ctx context.Context, body []byte) ([]byt
log := logger.Load(ctx)
log.InfoContext(ctx, "sagemaker: created algorithm", "name", al.AlgorithmName)
- return json.Marshal(map[string]string{"AlgorithmArn": al.AlgorithmArn})
+ return json.Marshal(map[string]string{"AlgorithmArn": al.AlgorithmArn}, jsonx.V1())
}
// describeAlgorithmResponse is the response body for DescribeAlgorithm.
@@ -71,9 +73,9 @@ type describeAlgorithmResponse struct {
AlgorithmDescription string `json:"AlgorithmDescription,omitempty"`
ProductID string `json:"ProductId,omitempty"`
AlgorithmStatusDetails AlgorithmStatusDetails `json:"AlgorithmStatusDetails"`
- TrainingSpecification json.RawMessage `json:"TrainingSpecification"`
- InferenceSpecification json.RawMessage `json:"InferenceSpecification,omitempty"`
- ValidationSpecification json.RawMessage `json:"ValidationSpecification,omitempty"`
+ TrainingSpecification jsontext.Value `json:"TrainingSpecification"`
+ InferenceSpecification jsontext.Value `json:"InferenceSpecification,omitempty"`
+ ValidationSpecification jsontext.Value `json:"ValidationSpecification,omitempty"`
CreationTime float64 `json:"CreationTime"`
CertifyForMarketplace bool `json:"CertifyForMarketplace,omitempty"`
}
@@ -86,7 +88,7 @@ type describeAlgorithmRequest struct {
func (h *Handler) handleDescribeAlgorithm(ctx context.Context, body []byte) ([]byte, error) {
var req describeAlgorithmRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -111,7 +113,7 @@ func (h *Handler) handleDescribeAlgorithm(ctx context.Context, body []byte) ([]b
TrainingSpecification: al.TrainingSpecification,
InferenceSpecification: al.InferenceSpecification,
ValidationSpecification: al.ValidationSpecification,
- })
+ }, jsonx.V1())
}
// deleteAlgorithmRequest is the request body for DeleteAlgorithm.
@@ -122,7 +124,7 @@ type deleteAlgorithmRequest struct {
func (h *Handler) handleDeleteAlgorithm(ctx context.Context, body []byte) error {
var req deleteAlgorithmRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -136,7 +138,7 @@ func (h *Handler) handleDeleteAlgorithm(ctx context.Context, body []byte) error
func (h *Handler) handleListAlgorithms(ctx context.Context, body []byte) ([]byte, error) {
var req nameTimeListRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_algorithms_test.go b/services/sagemaker/handler_algorithms_test.go
index 1cb68c2ce7..d1a996bc73 100644
--- a/services/sagemaker/handler_algorithms_test.go
+++ b/services/sagemaker/handler_algorithms_test.go
@@ -2,7 +2,7 @@ package sagemaker_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -52,7 +53,7 @@ func TestHandler_DescribeAlgorithm(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "my-algo", resp["AlgorithmName"])
assert.Equal(t, "a description", resp["AlgorithmDescription"])
assert.Equal(t, "Completed", resp["AlgorithmStatus"])
@@ -114,7 +115,7 @@ func TestHandler_ListAlgorithms(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["AlgorithmSummaryList"])
doSageMakerRequest(t, h, "CreateAlgorithm", map[string]any{
@@ -129,7 +130,7 @@ func TestHandler_ListAlgorithms(t *testing.T) {
rec = doSageMakerRequest(t, h, "ListAlgorithms", map[string]any{})
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp["AlgorithmSummaryList"].([]any)
require.True(t, ok)
assert.Len(t, list, 2)
@@ -196,7 +197,7 @@ func TestCreateAlgorithm_TagsPresent(t *testing.T) {
if tt.wantTags {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arnStr := resp["AlgorithmArn"]
require.NotEmpty(t, arnStr)
@@ -272,7 +273,7 @@ func TestHandler_CreateAlgorithm(t *testing.T) {
if tt.wantARN {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["AlgorithmArn"], "arn:aws:sagemaker")
assert.Contains(t, resp["AlgorithmArn"], "algorithm/")
}
@@ -303,7 +304,7 @@ func TestHandler_ListAlgorithms_FilterSort(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp["AlgorithmSummaryList"].([]any)
require.True(t, ok)
@@ -357,6 +358,6 @@ func TestHandler_ListAlgorithms_CreationTimeAfterExclusive(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["AlgorithmSummaryList"])
}
diff --git a/services/sagemaker/handler_app_image_configs.go b/services/sagemaker/handler_app_image_configs.go
index 246193557c..9895958987 100644
--- a/services/sagemaker/handler_app_image_configs.go
+++ b/services/sagemaker/handler_app_image_configs.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -13,17 +16,17 @@ import (
// createAppImageConfigInput mirrors CreateAppImageConfigInput
// (api_op_CreateAppImageConfig.go:28-52).
type createAppImageConfigInput struct {
- AppImageConfigName string `json:"AppImageConfigName"`
- KernelGatewayImageConfig json.RawMessage `json:"KernelGatewayImageConfig"`
- JupyterLabAppImageConfig json.RawMessage `json:"JupyterLabAppImageConfig"`
- CodeEditorAppImageConfig json.RawMessage `json:"CodeEditorAppImageConfig"`
- Tags []tagObject `json:"Tags"`
+ AppImageConfigName string `json:"AppImageConfigName"`
+ KernelGatewayImageConfig jsontext.Value `json:"KernelGatewayImageConfig"`
+ JupyterLabAppImageConfig jsontext.Value `json:"JupyterLabAppImageConfig"`
+ CodeEditorAppImageConfig jsontext.Value `json:"CodeEditorAppImageConfig"`
+ Tags []tagObject `json:"Tags"`
}
func (h *Handler) handleCreateAppImageConfig(ctx context.Context, body []byte) ([]byte, error) {
var req createAppImageConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -42,7 +45,7 @@ func (h *Handler) handleCreateAppImageConfig(ctx context.Context, body []byte) (
return nil, err
}
- return json.Marshal(map[string]any{keyAppImageConfigArn: result.AppImageConfigArn})
+ return json.Marshal(map[string]any{keyAppImageConfigArn: result.AppImageConfigArn}, jsonx.V1())
}
// describeAppImageConfigInput mirrors DescribeAppImageConfigInput
@@ -54,7 +57,7 @@ type describeAppImageConfigInput struct {
func (h *Handler) handleDescribeAppImageConfig(ctx context.Context, body []byte) ([]byte, error) {
var req describeAppImageConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -67,7 +70,7 @@ func (h *Handler) handleDescribeAppImageConfig(ctx context.Context, body []byte)
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// deleteAppImageConfigInput mirrors DeleteAppImageConfigInput
@@ -79,7 +82,7 @@ type deleteAppImageConfigInput struct {
func (h *Handler) handleDeleteAppImageConfig(ctx context.Context, body []byte) error {
var req deleteAppImageConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -93,16 +96,16 @@ func (h *Handler) handleDeleteAppImageConfig(ctx context.Context, body []byte) e
// updateAppImageConfigInput mirrors UpdateAppImageConfigInput
// (api_op_UpdateAppImageConfig.go:28-45).
type updateAppImageConfigInput struct {
- AppImageConfigName string `json:"AppImageConfigName"`
- KernelGatewayImageConfig json.RawMessage `json:"KernelGatewayImageConfig"`
- JupyterLabAppImageConfig json.RawMessage `json:"JupyterLabAppImageConfig"`
- CodeEditorAppImageConfig json.RawMessage `json:"CodeEditorAppImageConfig"`
+ AppImageConfigName string `json:"AppImageConfigName"`
+ KernelGatewayImageConfig jsontext.Value `json:"KernelGatewayImageConfig"`
+ JupyterLabAppImageConfig jsontext.Value `json:"JupyterLabAppImageConfig"`
+ CodeEditorAppImageConfig jsontext.Value `json:"CodeEditorAppImageConfig"`
}
func (h *Handler) handleUpdateAppImageConfig(ctx context.Context, body []byte) ([]byte, error) {
var req updateAppImageConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -119,7 +122,7 @@ func (h *Handler) handleUpdateAppImageConfig(ctx context.Context, body []byte) (
return nil, err
}
- return json.Marshal(map[string]any{keyAppImageConfigArn: result.AppImageConfigArn})
+ return json.Marshal(map[string]any{keyAppImageConfigArn: result.AppImageConfigArn}, jsonx.V1())
}
// listAppImageConfigsInput mirrors ListAppImageConfigsInput
@@ -139,7 +142,7 @@ type listAppImageConfigsInput struct {
func (h *Handler) handleListAppImageConfigs(ctx context.Context, body []byte) ([]byte, error) {
var req listAppImageConfigsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_app_image_configs_test.go b/services/sagemaker/handler_app_image_configs_test.go
index 084dd70e97..5289ff9ca8 100644
--- a/services/sagemaker/handler_app_image_configs_test.go
+++ b/services/sagemaker/handler_app_image_configs_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateAppImageConfig(t *testing.T) {
@@ -23,7 +25,7 @@ func TestHandler_CreateAppImageConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["AppImageConfigArn"], "my-config")
}
@@ -37,7 +39,7 @@ func TestHandler_DescribeAppImageConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "aic-1", resp["AppImageConfigName"])
}
@@ -64,7 +66,7 @@ func TestHandler_UpdateAppImageConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["AppImageConfigArn"], "aic-upd")
}
@@ -81,7 +83,7 @@ func TestHandler_ListAppImageConfigs(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["AppImageConfigs"])
doSageMakerRequest(t, h, "CreateAppImageConfig", map[string]any{
@@ -89,7 +91,7 @@ func TestHandler_ListAppImageConfigs(t *testing.T) {
})
rec = doSageMakerRequest(t, h, "ListAppImageConfigs", map[string]any{})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
configs := resp["AppImageConfigs"].([]any)
assert.Len(t, configs, 1)
c := configs[0].(map[string]any)
diff --git a/services/sagemaker/handler_apps.go b/services/sagemaker/handler_apps.go
index ec378570c0..21c935d782 100644
--- a/services/sagemaker/handler_apps.go
+++ b/services/sagemaker/handler_apps.go
@@ -2,9 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -16,20 +18,20 @@ import (
// wire-field-audit tooling that only inspects named types can see it — see
// gopherstack-oc9v).
type createAppInput struct {
- DomainID string `json:"DomainId"`
- UserProfileName string `json:"UserProfileName"`
- SpaceName string `json:"SpaceName"`
- AppType string `json:"AppType"`
- AppName string `json:"AppName"`
- ResourceSpec json.RawMessage `json:"ResourceSpec"`
- Tags []tagObject `json:"Tags"`
- RecoveryMode bool `json:"RecoveryMode"`
+ DomainID string `json:"DomainId"`
+ UserProfileName string `json:"UserProfileName"`
+ SpaceName string `json:"SpaceName"`
+ AppType string `json:"AppType"`
+ AppName string `json:"AppName"`
+ ResourceSpec jsontext.Value `json:"ResourceSpec"`
+ Tags []tagObject `json:"Tags"`
+ RecoveryMode bool `json:"RecoveryMode"`
}
func (h *Handler) handleCreateApp(ctx context.Context, body []byte) ([]byte, error) {
var req createAppInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -81,7 +83,7 @@ func (h *Handler) handleCreateApp(ctx context.Context, body []byte) ([]byte, err
logger.Load(ctx).InfoContext(ctx, "sagemaker: created app", "name", a.AppName)
- return json.Marshal(map[string]string{keyAppArn: a.AppArn})
+ return json.Marshal(map[string]string{keyAppArn: a.AppArn}, jsonx.V1())
}
type describeAppInput struct {
@@ -95,7 +97,7 @@ type describeAppInput struct {
func (h *Handler) handleDescribeApp(ctx context.Context, body []byte) ([]byte, error) {
var req describeAppInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -138,7 +140,7 @@ func (h *Handler) handleDescribeApp(ctx context.Context, body []byte) ([]byte, e
resp["ResourceSpec"] = a.ResourceSpec
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type appSummary struct {
@@ -165,7 +167,7 @@ type listAppsInput struct {
func (h *Handler) handleListApps(ctx context.Context, body []byte) ([]byte, error) {
var req listAppsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -201,7 +203,7 @@ func (h *Handler) handleListApps(ctx context.Context, body []byte) ([]byte, erro
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type deleteAppInput struct {
@@ -215,7 +217,7 @@ type deleteAppInput struct {
func (h *Handler) handleDeleteApp(ctx context.Context, body []byte) error {
var req deleteAppInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_apps_test.go b/services/sagemaker/handler_apps_test.go
index 5ee8a3f949..234be4e334 100644
--- a/services/sagemaker/handler_apps_test.go
+++ b/services/sagemaker/handler_apps_test.go
@@ -1,12 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_AppLifecycle(t *testing.T) {
@@ -24,7 +26,7 @@ func TestHandler_AppLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recDomain.Code)
var domainOut map[string]any
- require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut))
+ require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut, jsonx.V1()))
domainID := domainOut["DomainId"].(string)
recUser := doSageMakerRequest(t, h, "CreateUserProfile", map[string]any{
@@ -43,7 +45,7 @@ func TestHandler_AppLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recCreate.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut, jsonx.V1()))
assert.NotEmpty(t, createOut["AppArn"])
// Describe app.
@@ -60,7 +62,7 @@ func TestHandler_AppLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut["Apps"].([]any), 1)
// Delete app.
@@ -117,7 +119,7 @@ func TestHandler_CreateApp_SpaceOwned(t *testing.T) {
require.Equal(t, http.StatusOK, recDomain.Code)
var domainOut map[string]any
- require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut))
+ require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut, jsonx.V1()))
domainID := domainOut["DomainId"].(string)
doSageMakerRequest(t, h, "CreateSpace", map[string]any{"DomainId": domainID, "SpaceName": "app-space"})
@@ -142,7 +144,7 @@ func TestHandler_CreateApp_SpaceOwned(t *testing.T) {
require.Equal(t, http.StatusOK, recDesc.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, "app-space", descOut["SpaceName"])
assert.Nil(t, descOut["UserProfileName"])
@@ -167,7 +169,7 @@ func TestHandler_ListApps_FiltersAndMaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, recDomain.Code)
var domainOut map[string]any
- require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut))
+ require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut, jsonx.V1()))
domainID := domainOut["DomainId"].(string)
doSageMakerRequest(t, h, "CreateUserProfile", map[string]any{"DomainId": domainID, "UserProfileName": "u1"})
@@ -198,7 +200,7 @@ func TestHandler_ListApps_FiltersAndMaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out["Apps"].([]any), 2)
})
@@ -212,7 +214,7 @@ func TestHandler_ListApps_FiltersAndMaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out["Apps"].([]any), 1, "MaxResults must cap the page, not just be parsed and ignored")
assert.NotEmpty(t, out["NextToken"])
})
diff --git a/services/sagemaker/handler_automl.go b/services/sagemaker/handler_automl.go
index 9b3537f11d..d4e17f6f61 100644
--- a/services/sagemaker/handler_automl.go
+++ b/services/sagemaker/handler_automl.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -16,8 +19,8 @@ type autoMLJobConfigRequest struct {
DataSplitConfig *AutoMLDataSplitConfig `json:"DataSplitConfig,omitempty"`
SecurityConfig *AutoMLSecurityConfig `json:"SecurityConfig,omitempty"`
Mode string `json:"Mode,omitempty"`
- CandidateGenerationConfig json.RawMessage `json:"CandidateGenerationConfig,omitempty"`
- CompletionCriteria json.RawMessage `json:"CompletionCriteria,omitempty"`
+ CandidateGenerationConfig jsontext.Value `json:"CandidateGenerationConfig,omitempty"`
+ CompletionCriteria jsontext.Value `json:"CompletionCriteria,omitempty"`
}
type createAutoMLJobRequest struct {
@@ -37,7 +40,7 @@ type createAutoMLJobRequest struct {
func (h *Handler) handleCreateAutoMLJob(ctx context.Context, body []byte) ([]byte, error) {
var req createAutoMLJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -97,7 +100,7 @@ func (h *Handler) handleCreateAutoMLJob(ctx context.Context, body []byte) ([]byt
return nil, optErr
}
- return json.Marshal(map[string]any{keyAutoMLJobArn: result.AutoMLJobArn})
+ return json.Marshal(map[string]any{keyAutoMLJobArn: result.AutoMLJobArn}, jsonx.V1())
}
type describeAutoMLJobRequest struct {
@@ -107,7 +110,7 @@ type describeAutoMLJobRequest struct {
func (h *Handler) handleDescribeAutoMLJob(ctx context.Context, body []byte) ([]byte, error) {
var req describeAutoMLJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -154,7 +157,7 @@ func (h *Handler) handleDescribeAutoMLJob(ctx context.Context, body []byte) ([]b
describeAutoMLV1Config(resp, j)
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type stopAutoMLJobRequest struct {
@@ -164,7 +167,7 @@ type stopAutoMLJobRequest struct {
func (h *Handler) handleStopAutoMLJob(ctx context.Context, body []byte) error {
var req stopAutoMLJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -191,7 +194,7 @@ type listAutoMLJobsRequest struct {
func (h *Handler) handleListAutoMLJobs(ctx context.Context, body []byte) ([]byte, error) {
var req listAutoMLJobsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -228,7 +231,7 @@ func (h *Handler) handleListAutoMLJobs(ctx context.Context, body []byte) ([]byte
return json.Marshal(map[string]any{
"AutoMLJobSummaries": summaries,
keyNextToken: next,
- })
+ }, jsonx.V1())
}
func describeAutoMLV1Config(resp map[string]any, j *AutoMLJob) {
diff --git a/services/sagemaker/handler_automl_search.go b/services/sagemaker/handler_automl_search.go
index 89c691b69b..19aa3060c2 100644
--- a/services/sagemaker/handler_automl_search.go
+++ b/services/sagemaker/handler_automl_search.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// automlSearchExtOpsSupported returns the real stateful operations
@@ -66,7 +68,7 @@ type listCandidatesForAutoMLJobInput struct {
func (h *Handler) handleListCandidatesForAutoMLJob(ctx context.Context, body []byte) ([]byte, error) {
var req listCandidatesForAutoMLJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -88,7 +90,7 @@ func (h *Handler) handleListCandidatesForAutoMLJob(ctx context.Context, body []b
return nil, err
}
- return json.Marshal(map[string]any{"Candidates": candidates, keyNextToken: next})
+ return json.Marshal(map[string]any{"Candidates": candidates, keyNextToken: next}, jsonx.V1())
}
// searchInput mirrors SearchInput (api_op_Search.go:27-63). VisibilityConditions
@@ -108,7 +110,7 @@ type searchInput struct {
func (h *Handler) handleSearch(ctx context.Context, body []byte) ([]byte, error) {
var req searchInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -133,7 +135,7 @@ func (h *Handler) handleSearch(ctx context.Context, body []byte) ([]byte, error)
"Results": results,
"TotalHits": map[string]any{"Value": total, "Relation": "EqualTo"},
keyNextToken: next,
- })
+ }, jsonx.V1())
}
// listModelMetadataInput mirrors ListModelMetadataInput
@@ -149,7 +151,7 @@ type listModelMetadataInput struct {
func (h *Handler) handleListModelMetadata(body []byte) ([]byte, error) {
var req listModelMetadataInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -166,7 +168,7 @@ func (h *Handler) handleListModelMetadata(body []byte) ([]byte, error) {
})
}
- return json.Marshal(map[string]any{"ModelMetadataSummaries": summaries, keyNextToken: next})
+ return json.Marshal(map[string]any{"ModelMetadataSummaries": summaries, keyNextToken: next}, jsonx.V1())
}
// getSearchSuggestionsInput mirrors GetSearchSuggestionsInput
@@ -183,7 +185,7 @@ type getSearchSuggestionsInput struct {
func (h *Handler) handleGetSearchSuggestions(_ context.Context, body []byte) ([]byte, error) {
var req getSearchSuggestionsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -201,7 +203,7 @@ func (h *Handler) handleGetSearchSuggestions(_ context.Context, body []byte) ([]
suggestions = append(suggestions, map[string]string{"PropertyName": n})
}
- return json.Marshal(map[string]any{"PropertyNameSuggestions": suggestions})
+ return json.Marshal(map[string]any{"PropertyNameSuggestions": suggestions}, jsonx.V1())
}
// getScalingConfigurationRecommendationInput mirrors
@@ -220,7 +222,7 @@ type getScalingConfigurationRecommendationInput struct {
func (h *Handler) handleGetScalingConfigurationRecommendation(ctx context.Context, body []byte) ([]byte, error) {
var req getScalingConfigurationRecommendationInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -256,5 +258,5 @@ func (h *Handler) handleGetScalingConfigurationRecommendation(ctx context.Contex
resp["ScalingPolicyObjective"] = req.ScalingPolicyObjective
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/sagemaker/handler_automl_search_test.go b/services/sagemaker/handler_automl_search_test.go
index 585954efb8..41489b6aa0 100644
--- a/services/sagemaker/handler_automl_search_test.go
+++ b/services/sagemaker/handler_automl_search_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_ListCandidatesForAutoMLJob(t *testing.T) {
@@ -25,7 +27,7 @@ func TestHandler_ListCandidatesForAutoMLJob(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
candidates, ok := resp["Candidates"].([]any)
require.True(t, ok)
assert.Len(t, candidates, 3)
@@ -76,7 +78,7 @@ func TestHandler_Search_TrainingJob(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results, ok := resp["Results"].([]any)
require.True(t, ok)
require.Len(t, results, 1)
@@ -98,7 +100,7 @@ func TestHandler_Search_TrainingJob(t *testing.T) {
})
var noMatchResp map[string]any
- require.NoError(t, json.Unmarshal(noMatchRec.Body.Bytes(), &noMatchResp))
+ require.NoError(t, json.Unmarshal(noMatchRec.Body.Bytes(), &noMatchResp, jsonx.V1()))
assert.Empty(t, noMatchResp["Results"])
}
@@ -122,7 +124,7 @@ func TestHandler_ListModelMetadata(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["ModelMetadataSummaries"].([]any)
require.True(t, ok)
assert.NotEmpty(t, summaries)
@@ -134,7 +136,7 @@ func TestHandler_ListModelMetadata(t *testing.T) {
})
var filteredResp map[string]any
- require.NoError(t, json.Unmarshal(filteredRec.Body.Bytes(), &filteredResp))
+ require.NoError(t, json.Unmarshal(filteredRec.Body.Bytes(), &filteredResp, jsonx.V1()))
filtered, ok := filteredResp["ModelMetadataSummaries"].([]any)
require.True(t, ok)
require.Len(t, filtered, 1)
@@ -157,7 +159,7 @@ func TestHandler_GetSearchSuggestions(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
suggestions, ok := resp["PropertyNameSuggestions"].([]any)
require.True(t, ok)
require.Len(t, suggestions, 1)
@@ -185,7 +187,7 @@ func TestHandler_GetScalingConfigurationRecommendation(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.InEpsilon(t, float64(60), resp["TargetCpuUtilizationPerCore"], 0.001)
dyn, ok := resp["DynamicScalingConfiguration"].(map[string]any)
require.True(t, ok)
@@ -220,7 +222,7 @@ func TestHandler_ListCandidatesForAutoMLJob_SortByStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
candidates := resp["Candidates"].([]any)
require.Len(t, candidates, 3)
diff --git a/services/sagemaker/handler_automl_test.go b/services/sagemaker/handler_automl_test.go
index a843e27cfc..2b0c5fb9fb 100644
--- a/services/sagemaker/handler_automl_test.go
+++ b/services/sagemaker/handler_automl_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// autoMLJobRequestBody returns a minimal-but-complete CreateAutoMLJob request
@@ -46,7 +48,7 @@ func TestHandler_CreateAutoMLJob(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["AutoMLJobArn"], "my-job")
}
@@ -106,7 +108,7 @@ func TestHandler_CreateAutoMLJob_InputDataConfigRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
channels, ok := descResp["InputDataConfig"].([]any)
require.True(t, ok, "DescribeAutoMLJob must always emit InputDataConfig (required field)")
@@ -134,7 +136,7 @@ func TestHandler_DescribeAutoMLJob(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "job-1", resp["AutoMLJobName"])
assert.Equal(t, "InProgress", resp["AutoMLJobStatus"])
}
@@ -154,7 +156,7 @@ func TestHandler_DescribeAutoMLJob_ModelDeployConfigRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
deployConfig, ok := resp["ModelDeployConfig"].(map[string]any)
require.True(t, ok, "DescribeAutoMLJob must return ModelDeployConfig when set")
@@ -221,7 +223,7 @@ func TestHandler_StopAutoMLJob(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeAutoMLJob", map[string]any{"AutoMLJobName": "job-stop"})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "Stopping", resp["AutoMLJobStatus"])
}
@@ -237,7 +239,7 @@ func TestHandler_ListAutoMLJobs(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["AutoMLJobSummaries"].([]any)
assert.Len(t, items, 2)
}
@@ -257,7 +259,7 @@ func TestHandler_ListAutoMLJobs_FilterSort(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["AutoMLJobSummaries"].([]any)
require.Len(t, items, 1)
assert.Equal(t, "job-alpha", items[0].(map[string]any)["AutoMLJobName"])
@@ -267,7 +269,7 @@ func TestHandler_ListAutoMLJobs_FilterSort(t *testing.T) {
"SortOrder": "Descending",
})
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items = resp["AutoMLJobSummaries"].([]any)
require.Len(t, items, 2)
assert.Equal(t, "job-beta", items[0].(map[string]any)["AutoMLJobName"])
@@ -308,7 +310,7 @@ func TestAutoMLJob_OutputDataConfigRoundtrip(t *testing.T) {
MetricName string `json:"MetricName"`
} `json:"AutoMLJobObjective"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.OutputDataConfig, "OutputDataConfig must be returned by DescribeAutoMLJob")
assert.Equal(t, "s3://my-bucket/automl-output/", out.OutputDataConfig.S3OutputPath)
@@ -334,7 +336,7 @@ func TestAutoMLJob_InitialStatus_InProgress(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InProgress", resp["AutoMLJobStatus"])
}
diff --git a/services/sagemaker/handler_automl_v2.go b/services/sagemaker/handler_automl_v2.go
index a34aa13582..d821e63324 100644
--- a/services/sagemaker/handler_automl_v2.go
+++ b/services/sagemaker/handler_automl_v2.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -23,7 +26,7 @@ import (
// enforced.
type createAutoMLJobV2Input struct {
Tags []tagObject `json:"Tags"`
- AutoMLProblemTypeConfig json.RawMessage `json:"AutoMLProblemTypeConfig"`
+ AutoMLProblemTypeConfig jsontext.Value `json:"AutoMLProblemTypeConfig"`
OutputDataConfig *AutoMLOutputDataConfig `json:"OutputDataConfig"`
AutoMLJobObjective *AutoMLJobObjective `json:"AutoMLJobObjective"`
AutoMLComputeConfig *AutoMLComputeConfig `json:"AutoMLComputeConfig"`
@@ -38,7 +41,7 @@ type createAutoMLJobV2Input struct {
func (h *Handler) handleCreateAutoMLJobV2(ctx context.Context, body []byte) ([]byte, error) {
var req createAutoMLJobV2Input
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -85,7 +88,7 @@ func (h *Handler) handleCreateAutoMLJobV2(ctx context.Context, body []byte) ([]b
return nil, extErr
}
- return json.Marshal(map[string]any{keyAutoMLJobArn: result.AutoMLJobArn})
+ return json.Marshal(map[string]any{keyAutoMLJobArn: result.AutoMLJobArn}, jsonx.V1())
}
// describeAutoMLJobV2Input mirrors DescribeAutoMLJobV2Input
@@ -98,7 +101,7 @@ type describeAutoMLJobV2Input struct {
func (h *Handler) handleDescribeAutoMLJobV2(ctx context.Context, body []byte) ([]byte, error) {
var req describeAutoMLJobV2Input
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -163,5 +166,5 @@ func (h *Handler) handleDescribeAutoMLJobV2(ctx context.Context, body []byte) ([
resp["ModelDeployConfig"] = j.ModelDeployConfig
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/sagemaker/handler_automl_v2_test.go b/services/sagemaker/handler_automl_v2_test.go
index d99d7d6a04..5a18c11dcb 100644
--- a/services/sagemaker/handler_automl_v2_test.go
+++ b/services/sagemaker/handler_automl_v2_test.go
@@ -1,12 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateAutoMLJobV2_RoundTrip(t *testing.T) {
@@ -61,7 +63,7 @@ func TestHandler_CreateAutoMLJobV2_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code, descRec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
channels, ok := out["AutoMLJobInputDataConfig"].([]any)
require.True(t, ok, "DescribeAutoMLJobV2 must always emit AutoMLJobInputDataConfig")
@@ -170,7 +172,7 @@ func TestHandler_DescribeAutoMLJobV1_OmitsV2Fields(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code, descRec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
_, hasV2Field := out["AutoMLJobInputDataConfig"]
assert.False(t, hasV2Field, "DescribeAutoMLJob (V1) must not emit the V2-only AutoMLJobInputDataConfig field")
diff --git a/services/sagemaker/handler_cluster.go b/services/sagemaker/handler_cluster.go
index aa6bad0f67..4effff5279 100644
--- a/services/sagemaker/handler_cluster.go
+++ b/services/sagemaker/handler_cluster.go
@@ -2,10 +2,12 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -109,7 +111,7 @@ type startClusterHealthCheckInput struct {
func (h *Handler) handleStartClusterHealthCheck(ctx context.Context, body []byte) ([]byte, error) {
var req startClusterHealthCheckInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -126,7 +128,7 @@ func (h *Handler) handleStartClusterHealthCheck(ctx context.Context, body []byte
return nil, err
}
- return json.Marshal(map[string]string{keyClusterArn: clusterARN})
+ return json.Marshal(map[string]string{keyClusterArn: clusterARN}, jsonx.V1())
}
// clusterInstanceGroupRequest is the wire shape for a
@@ -310,7 +312,7 @@ type createClusterRequest struct {
func (h *Handler) handleCreateCluster(ctx context.Context, body []byte) ([]byte, error) {
var req createClusterRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -338,7 +340,7 @@ func (h *Handler) handleCreateCluster(ctx context.Context, body []byte) ([]byte,
logger.Load(ctx).InfoContext(ctx, "sagemaker: created cluster", "name", c.ClusterName)
- return json.Marshal(map[string]string{keyClusterArn: c.ClusterArn})
+ return json.Marshal(map[string]string{keyClusterArn: c.ClusterArn}, jsonx.V1())
}
// clusterAutoScalingStatusInService is the (only) status this emulator
@@ -396,7 +398,7 @@ func (h *Handler) describeClusterResponse(c *Cluster) []byte {
resp["RestrictedInstanceGroupsConfig"] = rc
}
- b, _ := json.Marshal(resp)
+ b, _ := json.Marshal(resp, jsonx.V1())
return b
}
@@ -410,7 +412,7 @@ type describeClusterInput struct {
func (h *Handler) handleDescribeCluster(ctx context.Context, body []byte) ([]byte, error) {
var req describeClusterInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -451,7 +453,7 @@ type listClustersInput struct {
func (h *Handler) handleListClusters(ctx context.Context, body []byte) ([]byte, error) {
var req listClustersInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -480,7 +482,7 @@ func (h *Handler) handleListClusters(ctx context.Context, body []byte) ([]byte,
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// deleteClusterInput is DeleteCluster's request shape
@@ -492,7 +494,7 @@ type deleteClusterInput struct {
func (h *Handler) handleDeleteCluster(ctx context.Context, body []byte) ([]byte, error) {
var req deleteClusterInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -507,7 +509,7 @@ func (h *Handler) handleDeleteCluster(ctx context.Context, body []byte) ([]byte,
logger.Load(ctx).InfoContext(ctx, "sagemaker: deleted cluster", "name", req.ClusterName)
- return json.Marshal(map[string]string{keyClusterArn: clusterArn})
+ return json.Marshal(map[string]string{keyClusterArn: clusterArn}, jsonx.V1())
}
// updateClusterRequest is the request body for UpdateCluster.
@@ -527,7 +529,7 @@ type updateClusterRequest struct {
func (h *Handler) handleUpdateCluster(ctx context.Context, body []byte) ([]byte, error) {
var req updateClusterRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -554,7 +556,7 @@ func (h *Handler) handleUpdateCluster(ctx context.Context, body []byte) ([]byte,
logger.Load(ctx).InfoContext(ctx, "sagemaker: updated cluster", "name", c.ClusterName)
- return json.Marshal(map[string]string{keyClusterArn: c.ClusterArn})
+ return json.Marshal(map[string]string{keyClusterArn: c.ClusterArn}, jsonx.V1())
}
// updateClusterSoftwareInstanceGroupRequest is the wire shape for one entry
@@ -569,7 +571,7 @@ type updateClusterSoftwareInstanceGroupRequest struct {
// updateClusterSoftwareInput mirrors api_op_UpdateClusterSoftware.go:28-63;
// DeploymentConfig/ImageId are decoded but have no state to act on.
type updateClusterSoftwareInput struct {
- DeploymentConfig json.RawMessage `json:"DeploymentConfig"`
+ DeploymentConfig jsontext.Value `json:"DeploymentConfig"`
ClusterName string `json:"ClusterName"`
ImageID string `json:"ImageId"`
InstanceGroups []updateClusterSoftwareInstanceGroupRequest `json:"InstanceGroups"`
@@ -578,7 +580,7 @@ type updateClusterSoftwareInput struct {
func (h *Handler) handleUpdateClusterSoftware(ctx context.Context, body []byte) ([]byte, error) {
var req updateClusterSoftwareInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -599,7 +601,7 @@ func (h *Handler) handleUpdateClusterSoftware(ctx context.Context, body []byte)
logger.Load(ctx).
InfoContext(ctx, "sagemaker: updated cluster software", "name", req.ClusterName)
- return json.Marshal(map[string]string{keyClusterArn: clusterArn})
+ return json.Marshal(map[string]string{keyClusterArn: clusterArn}, jsonx.V1())
}
// clusterInstanceStatusDetails mirrors the AWS ClusterInstanceStatusDetails shape.
@@ -661,7 +663,7 @@ type describeClusterNodeInput struct {
func (h *Handler) handleDescribeClusterNode(ctx context.Context, body []byte) ([]byte, error) {
var req describeClusterNodeInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -678,7 +680,7 @@ func (h *Handler) handleDescribeClusterNode(ctx context.Context, body []byte) ([
return nil, err
}
- return json.Marshal(map[string]any{"NodeDetails": toClusterNodeDetails(n)})
+ return json.Marshal(map[string]any{"NodeDetails": toClusterNodeDetails(n)}, jsonx.V1())
}
// clusterNodeSummary is the wire shape for a ListClusterNodes entry
@@ -720,7 +722,7 @@ type listClusterNodesInput struct {
func (h *Handler) handleListClusterNodes(ctx context.Context, body []byte) ([]byte, error) {
var req listClusterNodesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -760,7 +762,7 @@ func (h *Handler) handleListClusterNodes(ctx context.Context, body []byte) ([]by
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// describeClusterEventInput is DescribeClusterEvent's request shape
@@ -773,7 +775,7 @@ type describeClusterEventInput struct {
func (h *Handler) handleDescribeClusterEvent(ctx context.Context, body []byte) ([]byte, error) {
var req describeClusterEventInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -791,7 +793,7 @@ func (h *Handler) handleDescribeClusterEvent(ctx context.Context, body []byte) (
// Unreachable in practice: DescribeClusterEvent always errors above because
// this emulator never generates cluster events.
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
// listClusterEventsInput is ListClusterEvents' request shape
@@ -816,7 +818,7 @@ type listClusterEventsInput struct {
func (h *Handler) handleListClusterEvents(ctx context.Context, body []byte) ([]byte, error) {
var req listClusterEventsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -828,7 +830,7 @@ func (h *Handler) handleListClusterEvents(ctx context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(map[string]any{"Events": []any{}})
+ return json.Marshal(map[string]any{"Events": []any{}}, jsonx.V1())
}
// detachClusterNodeVolumeRequest is the request body for DetachClusterNodeVolume.
@@ -840,7 +842,7 @@ type detachClusterNodeVolumeRequest struct {
func (h *Handler) handleDetachClusterNodeVolume(ctx context.Context, body []byte) ([]byte, error) {
var req detachClusterNodeVolumeRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -861,7 +863,7 @@ func (h *Handler) handleDetachClusterNodeVolume(ctx context.Context, body []byte
"DeviceName": dv.DeviceName,
keyStatus: dv.Status,
"AttachTime": epochSeconds(dv.AttachTime),
- })
+ }, jsonx.V1())
}
// attachClusterNodeVolumeRequest is the request body for
@@ -882,7 +884,7 @@ type attachClusterNodeNetworkInterfaceRequest struct {
func (h *Handler) handleAttachClusterNodeNetworkInterface(ctx context.Context, body []byte) ([]byte, error) {
var req attachClusterNodeNetworkInterfaceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -904,12 +906,12 @@ func (h *Handler) handleAttachClusterNodeNetworkInterface(ctx context.Context, b
keyNodeID: out.NodeID,
"NetworkInterfaceId": out.NetworkInterfaceID,
"AttachmentId": out.AttachmentID,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleAttachClusterNodeVolume(ctx context.Context, body []byte) ([]byte, error) {
var req attachClusterNodeVolumeRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -942,7 +944,7 @@ func (h *Handler) handleAttachClusterNodeVolume(ctx context.Context, body []byte
"DeviceName": av.DeviceName,
keyStatus: av.Status,
"AttachTime": epochSeconds(av.AttachTime),
- })
+ }, jsonx.V1())
}
// batchAddClusterNodesError mirrors types.BatchAddClusterNodesError
@@ -1058,7 +1060,7 @@ type batchAddClusterNodesRequest struct {
func (h *Handler) handleBatchAddClusterNodes(ctx context.Context, body []byte) ([]byte, error) {
var req batchAddClusterNodesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -1104,7 +1106,7 @@ func (h *Handler) handleBatchAddClusterNodes(ctx context.Context, body []byte) (
return json.Marshal(map[string]any{
keyFailed: failedResults,
statusSuccessful: successResults,
- })
+ }, jsonx.V1())
}
// batchNodeIDsRequest is the request body shared by BatchDeleteClusterNodes,
@@ -1213,7 +1215,7 @@ func (h *Handler) handleBatchNodeIDsOp(
buildFailedLogical func(nodeLogicalID string) any,
) ([]byte, error) {
var req batchNodeIDsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -1246,7 +1248,7 @@ func (h *Handler) handleBatchNodeIDsOp(
resp["SuccessfulNodeLogicalIds"] = successfulLogicalResults
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
func (h *Handler) handleBatchDeleteClusterNodes(ctx context.Context, body []byte) ([]byte, error) {
diff --git a/services/sagemaker/handler_cluster_test.go b/services/sagemaker/handler_cluster_test.go
index 899c52fb9a..b930244ed8 100644
--- a/services/sagemaker/handler_cluster_test.go
+++ b/services/sagemaker/handler_cluster_test.go
@@ -2,7 +2,7 @@ package sagemaker_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -38,7 +39,7 @@ func TestHandler_ClusterLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
clusterArn := createResp["ClusterArn"]
assert.Contains(t, clusterArn, "arn:aws:sagemaker")
assert.Contains(t, clusterArn, "cluster/my-cluster")
@@ -48,7 +49,7 @@ func TestHandler_ClusterLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "my-cluster", descResp["ClusterName"])
assert.Equal(t, "InService", descResp["ClusterStatus"])
assert.Equal(t, "Automatic", descResp["NodeRecovery"])
@@ -70,7 +71,7 @@ func TestHandler_ClusterLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
summaries, ok := listResp["ClusterSummaries"].([]any)
require.True(t, ok)
assert.Len(t, summaries, 1)
@@ -80,7 +81,7 @@ func TestHandler_ClusterLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var nodesResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &nodesResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &nodesResp, jsonx.V1()))
nodeSummaries, ok := nodesResp["ClusterNodeSummaries"].([]any)
require.True(t, ok)
require.Len(t, nodeSummaries, 2)
@@ -102,7 +103,7 @@ func TestHandler_ClusterLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var nodeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &nodeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &nodeResp, jsonx.V1()))
nodeDetails, ok := nodeResp["NodeDetails"].(map[string]any)
require.True(t, ok)
assert.Equal(t, nodeID, nodeDetails["InstanceId"])
@@ -121,7 +122,7 @@ func TestHandler_ClusterLifecycle(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeCluster", map[string]any{"ClusterName": "my-cluster"})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "None", descResp["NodeRecovery"])
groups, ok = descResp["InstanceGroups"].([]any)
require.True(t, ok)
@@ -134,7 +135,7 @@ func TestHandler_ClusterLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var softwareResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &softwareResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &softwareResp, jsonx.V1()))
assert.Equal(t, clusterArn, softwareResp["ClusterArn"])
// ListClusterEvents / DescribeClusterEvent: correct empty shape, no events ever exist.
@@ -142,7 +143,7 @@ func TestHandler_ClusterLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var eventsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &eventsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &eventsResp, jsonx.V1()))
events, ok := eventsResp["Events"].([]any)
require.True(t, ok)
assert.Empty(t, events)
@@ -162,7 +163,7 @@ func TestHandler_ClusterLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var attachResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &attachResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &attachResp, jsonx.V1()))
assert.Equal(t, clusterArn, attachResp["ClusterArn"])
assert.Equal(t, nodeID, attachResp["NodeId"])
assert.Equal(t, "vol-123", attachResp["VolumeId"])
@@ -178,7 +179,7 @@ func TestHandler_ClusterLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var detachResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &detachResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &detachResp, jsonx.V1()))
assert.Equal(t, clusterArn, detachResp["ClusterArn"])
assert.Equal(t, nodeID, detachResp["NodeId"])
assert.Equal(t, "vol-123", detachResp["VolumeId"])
@@ -198,7 +199,7 @@ func TestHandler_ClusterLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var deleteResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &deleteResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &deleteResp, jsonx.V1()))
assert.Equal(t, clusterArn, deleteResp["ClusterArn"])
// Verify deleted.
@@ -225,7 +226,7 @@ func TestHandler_CreateCluster_ClusterRoleAndVpcConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "arn:aws:iam::000000000000:role/HyperPodClusterRole", descResp["ClusterRole"])
vpcConfig, ok := descResp["VpcConfig"].(map[string]any)
@@ -309,7 +310,7 @@ func TestHandler_UpdateCluster_DeleteInstanceGroup(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups, ok := resp["InstanceGroups"].([]any)
require.True(t, ok)
assert.Len(t, groups, 1)
@@ -318,7 +319,7 @@ func TestHandler_UpdateCluster_DeleteInstanceGroup(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var nodesResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &nodesResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &nodesResp, jsonx.V1()))
nodes, ok := nodesResp["ClusterNodeSummaries"].([]any)
require.True(t, ok)
assert.Len(t, nodes, 2) // only group-a's 2 nodes remain
@@ -427,7 +428,7 @@ func TestHandler_ListClusters_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["ClusterSummaries"].([]any)
require.True(t, ok)
@@ -463,7 +464,7 @@ func TestHandler_ListClusters_NameContains(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["ClusterSummaries"].([]any)
require.True(t, ok)
require.Len(t, summaries, 1)
@@ -672,7 +673,7 @@ func TestHandler_AttachClusterNodeVolume(t *testing.T) {
if tt.wantARN {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["ClusterArn"], "arn:aws:sagemaker")
assert.Equal(t, "node-1", resp["NodeId"])
assert.Equal(t, "vol-1", resp["VolumeId"])
@@ -761,7 +762,7 @@ func TestHandler_BatchAddClusterNodes(t *testing.T) {
if tt.wantARN {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotContains(t, resp, "ClusterArn")
assert.IsType(t, []any{}, resp["Failed"])
assert.IsType(t, []any{}, resp["Successful"])
@@ -844,7 +845,7 @@ func TestHandler_BatchDeleteClusterNodes(t *testing.T) {
if tt.wantARN {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotContains(t, resp, "ClusterArn")
assert.Contains(t, resp, "Failed")
assert.Contains(t, resp, "Successful")
@@ -924,7 +925,7 @@ func TestHandler_BatchRebootClusterNodes(t *testing.T) {
if tt.wantARN {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotContains(t, resp, "ClusterArn")
assert.Contains(t, resp, "Failed")
assert.Contains(t, resp, "Successful")
@@ -1004,7 +1005,7 @@ func TestHandler_BatchReplaceClusterNodes(t *testing.T) {
if tt.wantARN {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotContains(t, resp, "ClusterArn")
assert.Contains(t, resp, "Failed")
assert.Contains(t, resp, "Successful")
@@ -1154,7 +1155,7 @@ func TestHandler_CreateCluster_OrchestratorValidation(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
})
}
@@ -1433,7 +1434,7 @@ func TestHandler_CreateCluster_InstanceStorageConfigUnion_Validation(t *testing.
require.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
})
}
@@ -1489,7 +1490,7 @@ func TestHandler_CreateCluster_RestrictedInstanceGroupsConfig_Validation(t *test
require.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
})
}
@@ -1548,7 +1549,7 @@ func TestHandler_StartClusterHealthCheck(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["ClusterArn"], "hc-cluster")
}
})
diff --git a/services/sagemaker/handler_code_repositories.go b/services/sagemaker/handler_code_repositories.go
index cafe8f86e3..8e756a614f 100644
--- a/services/sagemaker/handler_code_repositories.go
+++ b/services/sagemaker/handler_code_repositories.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -21,7 +23,7 @@ type createCodeRepositoryInput struct {
func (h *Handler) handleCreateCodeRepository(ctx context.Context, body []byte) ([]byte, error) {
var req createCodeRepositoryInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -34,7 +36,7 @@ func (h *Handler) handleCreateCodeRepository(ctx context.Context, body []byte) (
return nil, err
}
- return json.Marshal(map[string]any{keyCodeRepositoryArn: result.CodeRepositoryArn})
+ return json.Marshal(map[string]any{keyCodeRepositoryArn: result.CodeRepositoryArn}, jsonx.V1())
}
// describeCodeRepositoryInput mirrors DescribeCodeRepositoryInput
@@ -46,7 +48,7 @@ type describeCodeRepositoryInput struct {
func (h *Handler) handleDescribeCodeRepository(ctx context.Context, body []byte) ([]byte, error) {
var req describeCodeRepositoryInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -59,7 +61,7 @@ func (h *Handler) handleDescribeCodeRepository(ctx context.Context, body []byte)
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// updateCodeRepositoryInput mirrors UpdateCodeRepositoryInput
@@ -76,7 +78,7 @@ type updateCodeRepositoryInput struct {
func (h *Handler) handleUpdateCodeRepository(ctx context.Context, body []byte) ([]byte, error) {
var req updateCodeRepositoryInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -89,7 +91,7 @@ func (h *Handler) handleUpdateCodeRepository(ctx context.Context, body []byte) (
return nil, err
}
- return json.Marshal(map[string]any{keyCodeRepositoryArn: result.CodeRepositoryArn})
+ return json.Marshal(map[string]any{keyCodeRepositoryArn: result.CodeRepositoryArn}, jsonx.V1())
}
// deleteCodeRepositoryInput mirrors DeleteCodeRepositoryInput
@@ -101,7 +103,7 @@ type deleteCodeRepositoryInput struct {
func (h *Handler) handleDeleteCodeRepository(ctx context.Context, body []byte) error {
var req deleteCodeRepositoryInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -129,7 +131,7 @@ type listCodeRepositoriesInput struct {
func (h *Handler) handleListCodeRepositories(ctx context.Context, body []byte) ([]byte, error) {
var req listCodeRepositoriesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -162,5 +164,5 @@ func (h *Handler) handleListCodeRepositories(ctx context.Context, body []byte) (
return json.Marshal(map[string]any{
"CodeRepositorySummaryList": summaries,
keyNextToken: next,
- })
+ }, jsonx.V1())
}
diff --git a/services/sagemaker/handler_code_repositories_test.go b/services/sagemaker/handler_code_repositories_test.go
index d4d5e93c17..b56247354a 100644
--- a/services/sagemaker/handler_code_repositories_test.go
+++ b/services/sagemaker/handler_code_repositories_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateCodeRepository(t *testing.T) {
@@ -24,7 +26,7 @@ func TestHandler_CreateCodeRepository(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["CodeRepositoryArn"], "my-repo")
}
@@ -73,7 +75,7 @@ func TestHandler_DescribeCodeRepository(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "repo-1", resp["CodeRepositoryName"])
// The awsjson1.1 protocol encodes timestamps as epoch-seconds numbers, not
@@ -108,12 +110,12 @@ func TestHandler_UpdateCodeRepository(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["CodeRepositoryArn"], "repo-upd")
descRec := doSageMakerRequest(t, h, "DescribeCodeRepository", map[string]any{"CodeRepositoryName": "repo-upd"})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
gitConfig, ok := descResp["GitConfig"].(map[string]any)
require.True(t, ok, "GitConfig must be present")
assert.Equal(t, "https://github.com/test/repo", gitConfig["RepositoryUrl"],
@@ -155,7 +157,7 @@ func TestHandler_ListCodeRepositories(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["CodeRepositorySummaryList"].([]any)
assert.Len(t, items, 2)
}
diff --git a/services/sagemaker/handler_compilation_jobs.go b/services/sagemaker/handler_compilation_jobs.go
index 3bd98e551f..2a79925690 100644
--- a/services/sagemaker/handler_compilation_jobs.go
+++ b/services/sagemaker/handler_compilation_jobs.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -19,14 +22,14 @@ type createCompilationJobInput struct {
CompilationJobName string `json:"CompilationJobName"`
RoleArn string `json:"RoleArn"`
ModelPackageVersionArn string `json:"ModelPackageVersionArn"`
- VpcConfig json.RawMessage `json:"VpcConfig"`
+ VpcConfig jsontext.Value `json:"VpcConfig"`
Tags []tagObject `json:"Tags"`
}
func (h *Handler) handleCreateCompilationJob(ctx context.Context, body []byte) ([]byte, error) {
var req createCompilationJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -75,7 +78,7 @@ func (h *Handler) handleCreateCompilationJob(ctx context.Context, body []byte) (
return nil, extErr
}
- return json.Marshal(map[string]any{"CompilationJobArn": result.CompilationJobArn})
+ return json.Marshal(map[string]any{"CompilationJobArn": result.CompilationJobArn}, jsonx.V1())
}
// describeCompilationJobInput mirrors DescribeCompilationJobInput
@@ -87,7 +90,7 @@ type describeCompilationJobInput struct {
func (h *Handler) handleDescribeCompilationJob(ctx context.Context, body []byte) ([]byte, error) {
var req describeCompilationJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -100,7 +103,7 @@ func (h *Handler) handleDescribeCompilationJob(ctx context.Context, body []byte)
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// deleteCompilationJobInput mirrors DeleteCompilationJobInput
@@ -112,7 +115,7 @@ type deleteCompilationJobInput struct {
func (h *Handler) handleDeleteCompilationJob(ctx context.Context, body []byte) error {
var req deleteCompilationJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -132,7 +135,7 @@ type stopCompilationJobInput struct {
func (h *Handler) handleStopCompilationJob(ctx context.Context, body []byte) error {
var req stopCompilationJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -161,7 +164,7 @@ type listCompilationJobsInput struct {
func (h *Handler) handleListCompilationJobs(ctx context.Context, body []byte) ([]byte, error) {
var req listCompilationJobsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -214,7 +217,7 @@ func (h *Handler) handleListCompilationJobs(ctx context.Context, body []byte) ([
return json.Marshal(map[string]any{
"CompilationJobSummaries": summaries,
keyNextToken: next,
- })
+ }, jsonx.V1())
}
func compilationTargetPlatform(j *CompilationJob) *CompilationTargetPlatform {
diff --git a/services/sagemaker/handler_compilation_jobs_test.go b/services/sagemaker/handler_compilation_jobs_test.go
index d55ccad4ac..91ae896818 100644
--- a/services/sagemaker/handler_compilation_jobs_test.go
+++ b/services/sagemaker/handler_compilation_jobs_test.go
@@ -2,7 +2,7 @@ package sagemaker_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -32,7 +33,7 @@ func TestHandler_CreateCompilationJob(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["CompilationJobArn"], "my-compile")
}
@@ -103,7 +104,7 @@ func TestHandler_DescribeCompilationJob(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "cj-1", resp["CompilationJobName"])
assert.Equal(t, "INPROGRESS", resp["CompilationJobStatus"])
}
@@ -138,14 +139,14 @@ func TestHandler_StopCompilationJob(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeCompilationJob", map[string]any{"CompilationJobName": "cj-stop"})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "STOPPING", resp["CompilationJobStatus"])
time.Sleep(time.Second)
synctest.Wait()
rec = doSageMakerRequest(t, h, "DescribeCompilationJob", map[string]any{"CompilationJobName": "cj-stop"})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "STOPPED", resp["CompilationJobStatus"])
})
}
@@ -174,7 +175,7 @@ func TestHandler_ListCompilationJobs(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["CompilationJobSummaries"].([]any)
assert.Len(t, items, 2)
}
@@ -264,7 +265,7 @@ func TestCompilationJob_HandlerCapturesInputOutputConfig(t *testing.T) {
MaxRuntimeInSeconds int32 `json:"MaxRuntimeInSeconds"`
} `json:"StoppingCondition"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
require.NotNil(t, out.InputConfig, "InputConfig must be returned by DescribeCompilationJob")
assert.Equal(t, "s3://bucket/model.tar.gz", out.InputConfig.S3Uri)
@@ -301,7 +302,7 @@ func TestCompilationJob_InitialStatus_InProgress(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "INPROGRESS", resp["CompilationJobStatus"])
}
@@ -384,7 +385,7 @@ func TestHandler_AIBenchmarkJobLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Contains(t, createResp["AIBenchmarkJobArn"], "bench-2")
rec = doSageMakerRequest(
@@ -396,7 +397,7 @@ func TestHandler_AIBenchmarkJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "InProgress", descResp["AIBenchmarkJobStatus"])
assert.Equal(t, "wc-1", descResp["AIWorkloadConfigIdentifier"])
assert.NotEmpty(t, descResp["BenchmarkTarget"])
@@ -411,7 +412,7 @@ func TestHandler_AIBenchmarkJobLifecycle(t *testing.T) {
"DescribeAIBenchmarkJob",
map[string]any{"AIBenchmarkJobName": "bench-2"},
)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Stopping", descResp["AIBenchmarkJobStatus"])
},
},
@@ -472,7 +473,7 @@ func TestHandler_AIBenchmarkJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["AIBenchmarkJobs"].([]any)
require.Len(t, items, 1)
item := items[0].(map[string]any)
diff --git a/services/sagemaker/handler_device_fleets.go b/services/sagemaker/handler_device_fleets.go
index eb35eb3dec..de5df622aa 100644
--- a/services/sagemaker/handler_device_fleets.go
+++ b/services/sagemaker/handler_device_fleets.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -45,7 +47,7 @@ type createDeviceFleetInput struct {
func (h *Handler) handleCreateDeviceFleet(ctx context.Context, body []byte) ([]byte, error) {
var req createDeviceFleetInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -71,7 +73,7 @@ func (h *Handler) handleCreateDeviceFleet(ctx context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
type describeDeviceFleetInput struct {
@@ -81,7 +83,7 @@ type describeDeviceFleetInput struct {
func (h *Handler) handleDescribeDeviceFleet(ctx context.Context, body []byte) ([]byte, error) {
var req describeDeviceFleetInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -90,7 +92,7 @@ func (h *Handler) handleDescribeDeviceFleet(ctx context.Context, body []byte) ([
return nil, err
}
- return json.Marshal(f)
+ return json.Marshal(f, jsonx.V1())
}
// listDeviceFleetsInput is ListDeviceFleets' request shape
@@ -110,7 +112,7 @@ type listDeviceFleetsInput struct {
func (h *Handler) handleListDeviceFleets(ctx context.Context, body []byte) ([]byte, error) {
var req listDeviceFleetsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -150,7 +152,7 @@ type updateDeviceFleetInput struct {
func (h *Handler) handleUpdateDeviceFleet(ctx context.Context, body []byte) ([]byte, error) {
var req updateDeviceFleetInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -165,7 +167,7 @@ func (h *Handler) handleUpdateDeviceFleet(ctx context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
type deleteDeviceFleetInput struct {
@@ -175,7 +177,7 @@ type deleteDeviceFleetInput struct {
func (h *Handler) handleDeleteDeviceFleet(ctx context.Context, body []byte) ([]byte, error) {
var req deleteDeviceFleetInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -183,7 +185,7 @@ func (h *Handler) handleDeleteDeviceFleet(ctx context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -213,7 +215,7 @@ type registerDevicesInput struct {
func (h *Handler) handleRegisterDevices(ctx context.Context, body []byte) ([]byte, error) {
var req registerDevicesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -226,7 +228,7 @@ func (h *Handler) handleRegisterDevices(ctx context.Context, body []byte) ([]byt
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
type deregisterDevicesInput struct {
@@ -237,7 +239,7 @@ type deregisterDevicesInput struct {
func (h *Handler) handleDeregisterDevices(ctx context.Context, body []byte) ([]byte, error) {
var req deregisterDevicesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -245,7 +247,7 @@ func (h *Handler) handleDeregisterDevices(ctx context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
// describeDeviceInput is DescribeDevice's request shape
@@ -262,7 +264,7 @@ type describeDeviceInput struct {
func (h *Handler) handleDescribeDevice(ctx context.Context, body []byte) ([]byte, error) {
var req describeDeviceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -271,7 +273,7 @@ func (h *Handler) handleDescribeDevice(ctx context.Context, body []byte) ([]byte
return nil, err
}
- return json.Marshal(d)
+ return json.Marshal(d, jsonx.V1())
}
// listDevicesInput is ListDevices' request shape (api_op_ListDevices.go:
@@ -288,7 +290,7 @@ type listDevicesInput struct {
func (h *Handler) handleListDevices(ctx context.Context, body []byte) ([]byte, error) {
var req listDevicesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/sagemaker/handler_device_fleets_test.go b/services/sagemaker/handler_device_fleets_test.go
index 85363f5f82..58d7e11531 100644
--- a/services/sagemaker/handler_device_fleets_test.go
+++ b/services/sagemaker/handler_device_fleets_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -11,6 +11,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_DeviceFleetLifecycle(t *testing.T) {
@@ -30,7 +32,7 @@ func TestHandler_DeviceFleetLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Empty(t, createResp)
// Describe
@@ -40,7 +42,7 @@ func TestHandler_DeviceFleetLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "my-fleet", descResp["DeviceFleetName"])
assert.Equal(t, "test fleet", descResp["Description"])
assert.Contains(t, descResp["DeviceFleetArn"], "my-fleet")
@@ -56,7 +58,7 @@ func TestHandler_DeviceFleetLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
summaries := listResp["DeviceFleetSummaries"].([]any)
assert.Len(t, summaries, 1)
@@ -73,7 +75,7 @@ func TestHandler_DeviceFleetLifecycle(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeDeviceFleet", map[string]any{
"DeviceFleetName": "my-fleet",
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "updated description", descResp["Description"])
outCfg, ok = descResp["OutputConfig"].(map[string]any)
require.True(t, ok)
@@ -151,7 +153,7 @@ func TestHandler_DeviceLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "dev-1", descResp["DeviceName"])
assert.Equal(t, "fleet-a", descResp["DeviceFleetName"])
@@ -162,7 +164,7 @@ func TestHandler_DeviceLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
summaries := listResp["DeviceSummaries"].([]any)
assert.Len(t, summaries, 2)
@@ -175,7 +177,7 @@ func TestHandler_DeviceLifecycle(t *testing.T) {
// Verify only one remains
rec = doSageMakerRequest(t, h, "ListDevices", map[string]any{"DeviceFleetName": "fleet-a"})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
summaries = listResp["DeviceSummaries"].([]any)
assert.Len(t, summaries, 1)
}
@@ -207,7 +209,7 @@ func TestHandler_ListDevices_NoFleetFilter(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["DeviceSummaries"].([]any)
assert.Len(t, summaries, 2)
}
diff --git a/services/sagemaker/handler_domains.go b/services/sagemaker/handler_domains.go
index 78d735f2cd..7d4a0af87e 100644
--- a/services/sagemaker/handler_domains.go
+++ b/services/sagemaker/handler_domains.go
@@ -2,9 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -18,26 +20,26 @@ import (
// are carried as opaque json.RawMessage passthrough per this file's
// established convention (algorithms.go, ai_workload_configs.go).
type createDomainInput struct {
- DomainName string `json:"DomainName"`
- AuthMode string `json:"AuthMode"`
- AppNetworkAccessType string `json:"AppNetworkAccessType"`
- AppSecurityGroupManagement string `json:"AppSecurityGroupManagement"`
- HomeEfsFileSystemCreation string `json:"HomeEfsFileSystemCreation"`
- KmsKeyID string `json:"KmsKeyId"`
- HomeEfsFileSystemKmsKeyID string `json:"HomeEfsFileSystemKmsKeyId"`
- VpcID string `json:"VpcId"`
- TagPropagation string `json:"TagPropagation"`
- SubnetIDs []string `json:"SubnetIds"`
- DefaultUserSettings json.RawMessage `json:"DefaultUserSettings"`
- DefaultSpaceSettings json.RawMessage `json:"DefaultSpaceSettings"`
- DomainSettings json.RawMessage `json:"DomainSettings"`
- Tags []tagObject `json:"Tags"`
+ DomainName string `json:"DomainName"`
+ AuthMode string `json:"AuthMode"`
+ AppNetworkAccessType string `json:"AppNetworkAccessType"`
+ AppSecurityGroupManagement string `json:"AppSecurityGroupManagement"`
+ HomeEfsFileSystemCreation string `json:"HomeEfsFileSystemCreation"`
+ KmsKeyID string `json:"KmsKeyId"`
+ HomeEfsFileSystemKmsKeyID string `json:"HomeEfsFileSystemKmsKeyId"`
+ VpcID string `json:"VpcId"`
+ TagPropagation string `json:"TagPropagation"`
+ SubnetIDs []string `json:"SubnetIds"`
+ DefaultUserSettings jsontext.Value `json:"DefaultUserSettings"`
+ DefaultSpaceSettings jsontext.Value `json:"DefaultSpaceSettings"`
+ DomainSettings jsontext.Value `json:"DomainSettings"`
+ Tags []tagObject `json:"Tags"`
}
func (h *Handler) handleCreateDomain(ctx context.Context, body []byte) ([]byte, error) {
var req createDomainInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -76,7 +78,7 @@ func (h *Handler) handleCreateDomain(ctx context.Context, body []byte) ([]byte,
InfoContext(ctx, "sagemaker: created domain", "name", d.DomainName, "id", d.DomainID)
return json.Marshal(
- map[string]string{keyDomainArn: d.DomainArn, keyDomainID: d.DomainID, keyURL: d.URL},
+ map[string]string{keyDomainArn: d.DomainArn, keyDomainID: d.DomainID, keyURL: d.URL}, jsonx.V1(),
)
}
@@ -87,7 +89,7 @@ type describeDomainInput struct {
func (h *Handler) handleDescribeDomain(ctx context.Context, body []byte) ([]byte, error) {
var req describeDomainInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -155,7 +157,7 @@ func (h *Handler) handleDescribeDomain(ctx context.Context, body []byte) ([]byte
resp["DomainSettings"] = d.DomainSettings
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type domainSummary struct {
@@ -174,7 +176,7 @@ type listDomainsInput struct {
func (h *Handler) handleListDomains(ctx context.Context, body []byte) ([]byte, error) {
var req listDomainsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -196,7 +198,7 @@ func (h *Handler) handleListDomains(ctx context.Context, body []byte) ([]byte, e
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type deleteDomainInput struct {
@@ -209,7 +211,7 @@ type deleteDomainInput struct {
func (h *Handler) handleDeleteDomain(ctx context.Context, body []byte) error {
var req deleteDomainInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -237,22 +239,22 @@ func (h *Handler) handleDeleteDomain(ctx context.Context, body []byte) error {
// distinct, separately-shaped type from Create's DomainSettings, so it gets
// its own opaque passthrough field on the wire.
type updateDomainInput struct {
- DomainID string `json:"DomainId"`
- AppNetworkAccessType string `json:"AppNetworkAccessType"`
- AppSecurityGroupManagement string `json:"AppSecurityGroupManagement"`
- HomeEfsFileSystemCreation string `json:"HomeEfsFileSystemCreation"`
- TagPropagation string `json:"TagPropagation"`
- VpcID string `json:"VpcId"`
- SubnetIDs []string `json:"SubnetIds"`
- DefaultUserSettings json.RawMessage `json:"DefaultUserSettings"`
- DefaultSpaceSettings json.RawMessage `json:"DefaultSpaceSettings"`
- DomainSettingsForUpdate json.RawMessage `json:"DomainSettingsForUpdate"`
+ DomainID string `json:"DomainId"`
+ AppNetworkAccessType string `json:"AppNetworkAccessType"`
+ AppSecurityGroupManagement string `json:"AppSecurityGroupManagement"`
+ HomeEfsFileSystemCreation string `json:"HomeEfsFileSystemCreation"`
+ TagPropagation string `json:"TagPropagation"`
+ VpcID string `json:"VpcId"`
+ SubnetIDs []string `json:"SubnetIds"`
+ DefaultUserSettings jsontext.Value `json:"DefaultUserSettings"`
+ DefaultSpaceSettings jsontext.Value `json:"DefaultSpaceSettings"`
+ DomainSettingsForUpdate jsontext.Value `json:"DomainSettingsForUpdate"`
}
func (h *Handler) handleUpdateDomain(ctx context.Context, body []byte) ([]byte, error) {
var req updateDomainInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -281,5 +283,5 @@ func (h *Handler) handleUpdateDomain(ctx context.Context, body []byte) ([]byte,
logger.Load(ctx).InfoContext(ctx, "sagemaker: updated domain", "id", req.DomainID)
- return json.Marshal(map[string]string{keyDomainArn: d.DomainArn})
+ return json.Marshal(map[string]string{keyDomainArn: d.DomainArn}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_domains_test.go b/services/sagemaker/handler_domains_test.go
index 824e38851e..72347207cf 100644
--- a/services/sagemaker/handler_domains_test.go
+++ b/services/sagemaker/handler_domains_test.go
@@ -1,12 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateDomain(t *testing.T) {
@@ -50,7 +52,7 @@ func TestHandler_CreateDomain(t *testing.T) {
if tt.wantID {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["DomainId"])
assert.NotEmpty(t, resp["Url"])
}
@@ -76,7 +78,7 @@ func TestHandler_DomainLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recCreate.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut, jsonx.V1()))
domainID := createOut["DomainId"].(string)
// Describe domain by ID — the fields accepted on Create must round-trip,
@@ -86,7 +88,7 @@ func TestHandler_DomainLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recDesc.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, "test-domain", descOut["DomainName"])
assert.Equal(t, "VpcOnly", descOut["AppNetworkAccessType"])
assert.Equal(t, "vpc-1", descOut["VpcId"])
@@ -98,7 +100,7 @@ func TestHandler_DomainLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut["Domains"].([]any), 1)
// Update domain — a partial update must apply the overridable field
@@ -113,7 +115,7 @@ func TestHandler_DomainLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recDescAfterUpdate.Code)
var descAfterUpdate map[string]any
- require.NoError(t, json.Unmarshal(recDescAfterUpdate.Body.Bytes(), &descAfterUpdate))
+ require.NoError(t, json.Unmarshal(recDescAfterUpdate.Body.Bytes(), &descAfterUpdate, jsonx.V1()))
assert.Equal(t, "PublicInternetOnly", descAfterUpdate["AppNetworkAccessType"])
assert.Equal(t, "vpc-1", descAfterUpdate["VpcId"])
@@ -170,7 +172,7 @@ func TestHandler_ListDomains_MaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out["Domains"].([]any), 2, "MaxResults must cap the page, not just be parsed and ignored")
assert.NotEmpty(t, out["NextToken"])
}
diff --git a/services/sagemaker/handler_edge_deployment.go b/services/sagemaker/handler_edge_deployment.go
index 106ea8028b..13d33a486a 100644
--- a/services/sagemaker/handler_edge_deployment.go
+++ b/services/sagemaker/handler_edge_deployment.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// edge deployment operation name constants.
@@ -105,9 +108,9 @@ func (h *Handler) dispatchEdgeDeploymentOps(
// ---------------------------------------------------------------------------
type edgeDeploymentStageRequest struct {
- StageName string `json:"StageName"`
- DeviceSelectionConfig json.RawMessage `json:"DeviceSelectionConfig,omitempty"`
- DeploymentConfig json.RawMessage `json:"DeploymentConfig,omitempty"`
+ StageName string `json:"StageName"`
+ DeviceSelectionConfig jsontext.Value `json:"DeviceSelectionConfig,omitempty"`
+ DeploymentConfig jsontext.Value `json:"DeploymentConfig,omitempty"`
}
func toEdgeDeploymentStageInputs(reqs []edgeDeploymentStageRequest) []EdgeDeploymentStageInput {
@@ -133,7 +136,7 @@ type createEdgeDeploymentPlanInput struct {
func (h *Handler) handleCreateEdgeDeploymentPlan(ctx context.Context, body []byte) ([]byte, error) {
var req createEdgeDeploymentPlanInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -156,7 +159,7 @@ func (h *Handler) handleCreateEdgeDeploymentPlan(ctx context.Context, body []byt
return nil, err
}
- return json.Marshal(map[string]string{keyEdgeDeploymentPlanArn: p.EdgeDeploymentPlanArn})
+ return json.Marshal(map[string]string{keyEdgeDeploymentPlanArn: p.EdgeDeploymentPlanArn}, jsonx.V1())
}
// stageStatusSummary builds the DeploymentStageStatusSummary shape for a
@@ -193,7 +196,7 @@ type describeEdgeDeploymentPlanInput struct {
func (h *Handler) handleDescribeEdgeDeploymentPlan(ctx context.Context, body []byte) ([]byte, error) {
var req describeEdgeDeploymentPlanInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -234,7 +237,7 @@ func (h *Handler) handleDescribeEdgeDeploymentPlan(ctx context.Context, body []b
resp[keyNextToken] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type deleteEdgeDeploymentPlanInput struct {
@@ -244,7 +247,7 @@ type deleteEdgeDeploymentPlanInput struct {
func (h *Handler) handleDeleteEdgeDeploymentPlan(ctx context.Context, body []byte) error {
var req deleteEdgeDeploymentPlanInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -267,7 +270,7 @@ type listEdgeDeploymentPlansInput struct {
func (h *Handler) handleListEdgeDeploymentPlans(ctx context.Context, body []byte) ([]byte, error) {
var req listEdgeDeploymentPlansInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -313,7 +316,7 @@ type createEdgeDeploymentStageInput struct {
func (h *Handler) handleCreateEdgeDeploymentStage(ctx context.Context, body []byte) error {
var req createEdgeDeploymentStageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -328,7 +331,7 @@ type deleteEdgeDeploymentStageInput struct {
func (h *Handler) handleDeleteEdgeDeploymentStage(ctx context.Context, body []byte) error {
var req deleteEdgeDeploymentStageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -343,7 +346,7 @@ type startEdgeDeploymentStageInput struct {
func (h *Handler) handleStartEdgeDeploymentStage(ctx context.Context, body []byte) error {
var req startEdgeDeploymentStageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -358,7 +361,7 @@ type stopEdgeDeploymentStageInput struct {
func (h *Handler) handleStopEdgeDeploymentStage(ctx context.Context, body []byte) error {
var req stopEdgeDeploymentStageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -376,7 +379,7 @@ type getDeviceFleetReportInput struct {
func (h *Handler) handleGetDeviceFleetReport(ctx context.Context, body []byte) ([]byte, error) {
var req getDeviceFleetReportInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -410,7 +413,7 @@ func (h *Handler) handleGetDeviceFleetReport(ctx context.Context, body []byte) (
resp["OutputConfig"] = outputConfig
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type listStageDevicesInput struct {
@@ -424,7 +427,7 @@ type listStageDevicesInput struct {
func (h *Handler) handleListStageDevices(ctx context.Context, body []byte) ([]byte, error) {
var req listStageDevicesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -470,7 +473,7 @@ type updateDevicesInput struct {
func (h *Handler) handleUpdateDevices(ctx context.Context, body []byte) error {
var req updateDevicesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_edge_deployment_test.go b/services/sagemaker/handler_edge_deployment_test.go
index 08f133fd23..34f47447e9 100644
--- a/services/sagemaker/handler_edge_deployment_test.go
+++ b/services/sagemaker/handler_edge_deployment_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -49,7 +50,7 @@ func TestHandler_EdgeDeploymentPlanLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Contains(t, createResp["EdgeDeploymentPlanArn"], "my-plan")
// Describe
@@ -59,7 +60,7 @@ func TestHandler_EdgeDeploymentPlanLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "my-plan", descResp["EdgeDeploymentPlanName"])
assert.Equal(t, "plan-fleet", descResp["DeviceFleetName"])
@@ -91,7 +92,7 @@ func TestHandler_EdgeDeploymentPlanLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
summaries, ok := listResp["EdgeDeploymentPlanSummaries"].([]any)
require.True(t, ok)
assert.Len(t, summaries, 1)
@@ -181,7 +182,7 @@ func TestHandler_EdgeDeploymentStageLifecycle(t *testing.T) {
"EdgeDeploymentPlanName": "stage-plan",
})
var resp map[string]any
- require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(descResp.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
@@ -293,7 +294,7 @@ func TestHandler_GetDeviceFleetReport(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "report-fleet", resp["DeviceFleetName"])
assert.Contains(t, resp["DeviceFleetArn"], "report-fleet")
assert.Equal(t, "fleet for reports", resp["Description"])
@@ -353,7 +354,7 @@ func TestHandler_ListStageDevices(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["DeviceDeploymentSummaries"].([]any)
require.True(t, ok)
require.Len(t, summaries, 2)
@@ -422,7 +423,7 @@ func TestHandler_UpdateDevices(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "updated", resp["Description"])
assert.Equal(t, "thing-1", resp["IotThingName"])
}
@@ -478,7 +479,7 @@ func TestHandler_EdgeDeploymentPlan_SnapshotRestore(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
summaries, ok := listResp["EdgeDeploymentPlanSummaries"].([]any)
require.True(t, ok)
assert.Len(t, summaries, tt.wantCount)
@@ -493,7 +494,7 @@ func TestHandler_EdgeDeploymentPlan_SnapshotRestore(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "persist-fleet", descResp["DeviceFleetName"])
stages, ok := descResp["Stages"].([]any)
diff --git a/services/sagemaker/handler_edge_packaging_jobs.go b/services/sagemaker/handler_edge_packaging_jobs.go
index 279fc8da47..5232504cc2 100644
--- a/services/sagemaker/handler_edge_packaging_jobs.go
+++ b/services/sagemaker/handler_edge_packaging_jobs.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -34,7 +36,7 @@ type createEdgePackagingJobRequest struct {
func (h *Handler) handleCreateEdgePackagingJob(ctx context.Context, body []byte) ([]byte, error) {
var req createEdgePackagingJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -81,7 +83,7 @@ func (h *Handler) handleCreateEdgePackagingJob(ctx context.Context, body []byte)
return nil, err
}
- return json.Marshal(map[string]string{keyEdgePackagingJobArn: j.EdgePackagingJobArn})
+ return json.Marshal(map[string]string{keyEdgePackagingJobArn: j.EdgePackagingJobArn}, jsonx.V1())
}
type describeEdgePackagingJobRequest struct {
@@ -91,7 +93,7 @@ type describeEdgePackagingJobRequest struct {
func (h *Handler) handleDescribeEdgePackagingJob(ctx context.Context, body []byte) ([]byte, error) {
var req describeEdgePackagingJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -137,7 +139,7 @@ func (h *Handler) handleDescribeEdgePackagingJob(ctx context.Context, body []byt
resp["ModelArtifact"] = j.ModelArtifact
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type stopEdgePackagingJobRequest struct {
@@ -147,7 +149,7 @@ type stopEdgePackagingJobRequest struct {
func (h *Handler) handleStopEdgePackagingJob(ctx context.Context, body []byte) error {
var req stopEdgePackagingJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -186,7 +188,7 @@ type listEdgePackagingJobsRequest struct {
func (h *Handler) handleListEdgePackagingJobs(ctx context.Context, body []byte) ([]byte, error) {
var req listEdgePackagingJobsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -222,5 +224,5 @@ func (h *Handler) handleListEdgePackagingJobs(ctx context.Context, body []byte)
resp[keyNextToken] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/sagemaker/handler_edge_packaging_jobs_test.go b/services/sagemaker/handler_edge_packaging_jobs_test.go
index 14a71cabfa..852f960061 100644
--- a/services/sagemaker/handler_edge_packaging_jobs_test.go
+++ b/services/sagemaker/handler_edge_packaging_jobs_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_DescribeEdgePackagingJob_FailureReasonNotOnWire proves
@@ -28,7 +30,7 @@ func TestHandler_DescribeEdgePackagingJob_FailureReasonNotOnWire(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
_, present := out["FailureReason"]
assert.False(t, present, "FailureReason is not a real DescribeEdgePackagingJobOutput field")
}
@@ -55,7 +57,7 @@ func TestHandler_EdgePackagingJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Contains(t, createResp["EdgePackagingJobArn"], "my-edge-job")
// Describe
@@ -65,7 +67,7 @@ func TestHandler_EdgePackagingJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "my-edge-job", descResp["EdgePackagingJobName"])
assert.Equal(t, "my-model", descResp["ModelName"])
assert.Equal(t, "1.0", descResp["ModelVersion"])
@@ -80,7 +82,7 @@ func TestHandler_EdgePackagingJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
summaries := listResp["EdgePackagingJobSummaries"].([]any)
assert.Len(t, summaries, 1)
@@ -95,7 +97,7 @@ func TestHandler_EdgePackagingJobLifecycle(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeEdgePackagingJob", map[string]any{
"EdgePackagingJobName": "my-edge-job",
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "STOPPING", descResp["EdgePackagingJobStatus"])
// Confirm it actually reaches STOPPED.
@@ -105,7 +107,7 @@ func TestHandler_EdgePackagingJobLifecycle(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeEdgePackagingJob", map[string]any{
"EdgePackagingJobName": "my-edge-job",
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "STOPPED", descResp["EdgePackagingJobStatus"])
})
}
@@ -137,7 +139,7 @@ func TestHandler_EdgePackagingJob_ReachesCompleted(t *testing.T) {
"EdgePackagingJobName": "edge-job-completes",
})
var out map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "COMPLETED", out["EdgePackagingJobStatus"])
assert.Equal(t, "s3://bucket/edge-out/model.tar.gz", out["ModelArtifact"],
"ModelArtifact must be derived from OutputConfig.S3OutputLocation once the job completes")
@@ -227,7 +229,7 @@ func TestHandler_ListEdgePackagingJobs_StatusFilter(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["EdgePackagingJobSummaries"].([]any)
assert.Len(t, summaries, 1)
}
@@ -248,7 +250,7 @@ func TestHandler_ListEdgePackagingJobs_FilterSort(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["EdgePackagingJobSummaries"].([]any))
// SortBy=NAME, SortOrder=Descending reverses this backend's ascending
@@ -258,7 +260,7 @@ func TestHandler_ListEdgePackagingJobs_FilterSort(t *testing.T) {
"SortOrder": "Descending",
})
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["EdgePackagingJobSummaries"].([]any)
require.Len(t, summaries, 2)
assert.Equal(t, "job-beta", summaries[0].(map[string]any)["EdgePackagingJobName"])
@@ -316,7 +318,7 @@ func TestHandler_JobLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Contains(t, createResp["JobArn"], "job-2")
rec = doSageMakerRequest(t, h, "DescribeJob", map[string]any{
@@ -325,7 +327,7 @@ func TestHandler_JobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "InProgress", descResp["JobStatus"])
assert.Equal(t, "AgentRFT", descResp["JobCategory"])
assert.NotEmpty(t, descResp["SecondaryStatusTransitions"])
@@ -346,7 +348,7 @@ func TestHandler_JobLifecycle(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeJob", map[string]any{
"JobCategory": "AgentRFT", "JobName": "job-2",
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Stopping", descResp["JobStatus"])
},
},
@@ -403,7 +405,7 @@ func TestHandler_JobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["JobSummaries"].([]any)
require.Len(t, items, 1)
assert.Equal(t, "job-cat-a", items[0].(map[string]any)["JobName"])
@@ -440,7 +442,7 @@ func TestHandler_JobSchemaVersionLookups(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "1.0", resp["JobConfigSchemaVersion"])
assert.NotEmpty(t, resp["JobConfigSchema"])
},
@@ -472,7 +474,7 @@ func TestHandler_JobSchemaVersionLookups(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["JobConfigSchemas"].([]any)
require.Len(t, items, 1)
assert.Equal(t, "1.0", items[0].(map[string]any)["JobConfigSchemaVersion"])
diff --git a/services/sagemaker/handler_endpoint_configs.go b/services/sagemaker/handler_endpoint_configs.go
index 97f293fe29..5229554264 100644
--- a/services/sagemaker/handler_endpoint_configs.go
+++ b/services/sagemaker/handler_endpoint_configs.go
@@ -2,9 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -13,8 +15,8 @@ type createEndpointConfigRequest struct {
DataCaptureConfig *DataCaptureConfig `json:"DataCaptureConfig,omitempty"`
AsyncInferenceConfig *AsyncInferenceConfig `json:"AsyncInferenceConfig,omitempty"`
VpcConfig *VpcConfig `json:"VpcConfig,omitempty"`
- ExplainerConfig json.RawMessage `json:"ExplainerConfig,omitempty"`
- MetricsConfig json.RawMessage `json:"MetricsConfig,omitempty"`
+ ExplainerConfig jsontext.Value `json:"ExplainerConfig,omitempty"`
+ MetricsConfig jsontext.Value `json:"MetricsConfig,omitempty"`
EndpointConfigName string `json:"EndpointConfigName"`
ExecutionRoleArn string `json:"ExecutionRoleArn,omitempty"`
KmsKeyID string `json:"KmsKeyId,omitempty"`
@@ -36,8 +38,8 @@ type describeEndpointConfigResponse struct {
DataCaptureConfig *DataCaptureConfig `json:"DataCaptureConfig,omitempty"`
AsyncInferenceConfig *AsyncInferenceConfig `json:"AsyncInferenceConfig,omitempty"`
VpcConfig *VpcConfig `json:"VpcConfig,omitempty"`
- ExplainerConfig json.RawMessage `json:"ExplainerConfig,omitempty"`
- MetricsConfig json.RawMessage `json:"MetricsConfig,omitempty"`
+ ExplainerConfig jsontext.Value `json:"ExplainerConfig,omitempty"`
+ MetricsConfig jsontext.Value `json:"MetricsConfig,omitempty"`
EndpointConfigArn string `json:"EndpointConfigArn"`
EndpointConfigName string `json:"EndpointConfigName"`
ExecutionRoleArn string `json:"ExecutionRoleArn,omitempty"`
@@ -50,7 +52,7 @@ type describeEndpointConfigResponse struct {
func (h *Handler) handleCreateEndpointConfig(ctx context.Context, body []byte) ([]byte, error) {
var req createEndpointConfigRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -91,7 +93,7 @@ func (h *Handler) handleCreateEndpointConfig(ctx context.Context, body []byte) (
log := logger.Load(ctx)
log.InfoContext(ctx, "sagemaker: created endpoint config", "name", ec.EndpointConfigName)
- return json.Marshal(map[string]string{"EndpointConfigArn": ec.EndpointConfigARN})
+ return json.Marshal(map[string]string{"EndpointConfigArn": ec.EndpointConfigARN}, jsonx.V1())
}
// describeEndpointConfigRequest is the request body for DescribeEndpointConfig.
@@ -102,7 +104,7 @@ type describeEndpointConfigRequest struct {
func (h *Handler) handleDescribeEndpointConfig(ctx context.Context, body []byte) ([]byte, error) {
var req describeEndpointConfigRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -139,13 +141,17 @@ func (h *Handler) handleDescribeEndpointConfig(ctx context.Context, body []byte)
resp.ShadowProductionVariants = nil
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
-func (h *Handler) handleListEndpointConfigs(ctx context.Context, body []byte) ([]byte, error) {
+//nolint:dupl // structurally similar handlers
+func (h *Handler) handleListEndpointConfigs(
+ ctx context.Context,
+ body []byte,
+) ([]byte, error) {
var req nameTimeListRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -171,7 +177,7 @@ func (h *Handler) handleListEndpointConfigs(ctx context.Context, body []byte) ([
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// deleteEndpointConfigRequest is the request body for DeleteEndpointConfig.
@@ -182,7 +188,7 @@ type deleteEndpointConfigRequest struct {
func (h *Handler) handleDeleteEndpointConfig(ctx context.Context, body []byte) error {
var req deleteEndpointConfigRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_endpoint_configs_test.go b/services/sagemaker/handler_endpoint_configs_test.go
index 38307aa447..749c714761 100644
--- a/services/sagemaker/handler_endpoint_configs_test.go
+++ b/services/sagemaker/handler_endpoint_configs_test.go
@@ -2,7 +2,7 @@ package sagemaker_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -60,7 +61,7 @@ func TestHandler_CreateEndpointConfig(t *testing.T) {
if tt.wantARN {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["EndpointConfigArn"], "arn:aws:sagemaker")
}
})
@@ -111,7 +112,7 @@ func TestHandler_DescribeEndpointConfig(t *testing.T) {
if tt.wantName != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantName, resp["EndpointConfigName"])
}
})
@@ -250,7 +251,7 @@ func TestHandler_ListEndpointConfigs(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
configs, ok := resp["EndpointConfigs"].([]any)
require.True(t, ok)
@@ -281,7 +282,7 @@ func TestHandler_Tags_EndpointConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags, ok := resp["Tags"].([]any)
require.True(t, ok)
@@ -334,7 +335,7 @@ func TestHandler_ListEndpointConfigsPagination(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
configs, configsOK := resp["EndpointConfigs"].([]any)
require.True(t, configsOK)
@@ -355,7 +356,7 @@ func TestHandler_ListEndpointConfigsPagination(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
configs2, configs2OK := resp2["EndpointConfigs"].([]any)
require.True(t, configs2OK)
@@ -444,7 +445,7 @@ func TestCreateEndpointConfig_RequiresProductionVariants(t *testing.T) {
if tt.wantCode == http.StatusBadRequest {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
msg, _ := errResp["message"].(string)
assert.Contains(t, msg, "ProductionVariant",
"error message must mention ProductionVariant")
@@ -525,7 +526,7 @@ func TestHandler_ListEndpointConfigs_FilterSort(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp["EndpointConfigs"].([]any)
require.True(t, ok)
@@ -597,7 +598,7 @@ func TestHandler_ListEndpointConfigs_CreationTimeAfterInclusive(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
configs, ok := resp["EndpointConfigs"].([]any)
require.True(t, ok)
require.Len(t, configs, 1)
diff --git a/services/sagemaker/handler_endpoints.go b/services/sagemaker/handler_endpoints.go
index fde3085066..78b733b463 100644
--- a/services/sagemaker/handler_endpoints.go
+++ b/services/sagemaker/handler_endpoints.go
@@ -2,9 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -14,15 +16,15 @@ import (
// createEndpointInput mirrors CreateEndpointInput (api_op_CreateEndpoint.go:1-49).
type createEndpointInput struct {
- DeploymentConfig json.RawMessage `json:"DeploymentConfig"`
- EndpointName string `json:"EndpointName"`
- EndpointConfigName string `json:"EndpointConfigName"`
- Tags []tagObject `json:"Tags"`
+ DeploymentConfig jsontext.Value `json:"DeploymentConfig"`
+ EndpointName string `json:"EndpointName"`
+ EndpointConfigName string `json:"EndpointConfigName"`
+ Tags []tagObject `json:"Tags"`
}
func (h *Handler) handleCreateEndpointFSM(ctx context.Context, body []byte) ([]byte, error) {
var req createEndpointInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.EndpointName == "" {
@@ -48,24 +50,24 @@ func (h *Handler) handleCreateEndpointFSM(ctx context.Context, body []byte) ([]b
log := logger.Load(ctx)
log.InfoContext(ctx, "sagemaker: created endpoint (FSM)", "name", ep.EndpointName)
- return json.Marshal(map[string]string{keyEndpointArn: ep.EndpointArn})
+ return json.Marshal(map[string]string{keyEndpointArn: ep.EndpointArn}, jsonx.V1())
}
// updateEndpointInput mirrors UpdateEndpointInput (api_op_UpdateEndpoint.go:1-56).
type updateEndpointInput struct {
- DeploymentConfig json.RawMessage `json:"DeploymentConfig"`
- EndpointConfigName string `json:"EndpointConfigName"`
- EndpointName string `json:"EndpointName"`
- ExcludeRetainedVariantProperties []string `json:"ExcludeRetainedVariantProperties"`
- RetainAllVariantProperties bool `json:"RetainAllVariantProperties"`
- RetainDeploymentConfig bool `json:"RetainDeploymentConfig"`
+ DeploymentConfig jsontext.Value `json:"DeploymentConfig"`
+ EndpointConfigName string `json:"EndpointConfigName"`
+ EndpointName string `json:"EndpointName"`
+ ExcludeRetainedVariantProperties []string `json:"ExcludeRetainedVariantProperties"`
+ RetainAllVariantProperties bool `json:"RetainAllVariantProperties"`
+ RetainDeploymentConfig bool `json:"RetainDeploymentConfig"`
}
// handleUpdateEndpointFSM replaces the basic UpdateEndpoint handler with one
// that properly drives Updating → InService via the lifecycle simulator.
func (h *Handler) handleUpdateEndpointFSM(ctx context.Context, body []byte) ([]byte, error) {
var req updateEndpointInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.EndpointName == "" {
@@ -86,7 +88,7 @@ func (h *Handler) handleUpdateEndpointFSM(ctx context.Context, body []byte) ([]b
log := logger.Load(ctx)
log.InfoContext(ctx, "sagemaker: updated endpoint (FSM)", "name", ep.EndpointName)
- return json.Marshal(map[string]string{keyEndpointArn: ep.EndpointArn})
+ return json.Marshal(map[string]string{keyEndpointArn: ep.EndpointArn}, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -105,7 +107,7 @@ func (h *Handler) handleUpdateEndpointWeightsAndCapacitiesFull(
body []byte,
) ([]byte, error) {
var req updateEndpointWeightsAndCapacitiesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.EndpointName == "" {
@@ -129,7 +131,7 @@ func (h *Handler) handleUpdateEndpointWeightsAndCapacitiesFull(
req.EndpointName,
)
- return json.Marshal(map[string]string{keyEndpointArn: ep.EndpointArn})
+ return json.Marshal(map[string]string{keyEndpointArn: ep.EndpointArn}, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -199,7 +201,7 @@ func endpointResponseMap(ep *Endpoint) map[string]any {
func (h *Handler) handleDescribeEndpoint(ctx context.Context, body []byte) ([]byte, error) {
var req describeEndpointInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -212,7 +214,7 @@ func (h *Handler) handleDescribeEndpoint(ctx context.Context, body []byte) ([]by
return nil, err
}
- return json.Marshal(endpointResponseMap(ep))
+ return json.Marshal(endpointResponseMap(ep), jsonx.V1())
}
type endpointSummary struct {
@@ -240,7 +242,7 @@ type listEndpointsInput struct {
func (h *Handler) handleListEndpoints(ctx context.Context, body []byte) ([]byte, error) {
var req listEndpointsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -273,7 +275,7 @@ func (h *Handler) handleListEndpoints(ctx context.Context, body []byte) ([]byte,
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// deleteEndpointInput mirrors DeleteEndpointInput (api_op_DeleteEndpoint.go:29-37).
@@ -284,7 +286,7 @@ type deleteEndpointInput struct {
func (h *Handler) handleDeleteEndpoint(ctx context.Context, body []byte) error {
var req deleteEndpointInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_endpoints_test.go b/services/sagemaker/handler_endpoints_test.go
index 8f3c1622bb..f838980949 100644
--- a/services/sagemaker/handler_endpoints_test.go
+++ b/services/sagemaker/handler_endpoints_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_DescribeEndpoint_FullResponse(t *testing.T) {
@@ -44,7 +46,7 @@ func TestHandler_DescribeEndpoint_FullResponse(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "ep1", descResp["EndpointName"])
assert.NotEmpty(t, descResp["EndpointArn"])
// Status is Creating initially (FSM transitions async)
@@ -94,7 +96,7 @@ func TestHandler_DescribeEndpoint_DataCaptureAndAsyncInferenceConfig(t *testing.
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dcc, ok := resp["DataCaptureConfig"].(map[string]any)
require.True(t, ok, "DataCaptureConfig must be surfaced from the active EndpointConfig")
@@ -137,7 +139,7 @@ func TestHandler_CreateEndpoint_DeploymentConfigEchoedOnDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
lastDeploymentConfig, ok := resp["LastDeploymentConfig"].(map[string]any)
require.True(t, ok, "LastDeploymentConfig must echo the DeploymentConfig sent to Create/UpdateEndpoint")
@@ -186,7 +188,7 @@ func TestHandler_UpdateEndpoint_RetainAllVariantProperties(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeEndpoint", map[string]any{"EndpointName": "ep-retain"})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
variant := resp["ProductionVariants"].([]any)[0].(map[string]any)
assert.InDelta(t, float64(5), variant["DesiredInstanceCount"], 0)
assert.InDelta(t, 9.0, variant["DesiredWeight"], 0.0001)
@@ -207,7 +209,7 @@ func TestHandler_UpdateEndpoint_RetainAllVariantProperties(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeEndpoint", map[string]any{"EndpointName": "ep-retain-true"})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
variant := resp["ProductionVariants"].([]any)[0].(map[string]any)
assert.InDelta(t, float64(1), variant["DesiredInstanceCount"], 0)
assert.InDelta(t, 1.0, variant["DesiredWeight"], 0.0001)
@@ -229,7 +231,7 @@ func TestHandler_UpdateEndpoint_RetainAllVariantProperties(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeEndpoint", map[string]any{"EndpointName": "ep-retain-exclude"})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
variant := resp["ProductionVariants"].([]any)[0].(map[string]any)
// Excluded, so takes the new config's value instead of retaining.
assert.InDelta(t, float64(5), variant["DesiredInstanceCount"], 0)
@@ -298,7 +300,7 @@ func TestHandler_ListEndpoints_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
endpoints, _ := resp["Endpoints"].([]any)
assert.Len(t, endpoints, tc.wantCount)
})
@@ -329,7 +331,7 @@ func TestHandler_ListEndpoints_SortByName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
endpoints, ok := resp["Endpoints"].([]any)
require.True(t, ok)
require.Len(t, endpoints, 2)
@@ -348,7 +350,7 @@ func TestHandler_CreateEndpoint_UnknownEndpointConfig(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ValidationException", errResp["__type"])
rec = doSageMakerRequest(t, h, "DescribeEndpoint", map[string]any{
@@ -389,7 +391,7 @@ func TestHandler_DescribeEndpoint_EventuallyInService(t *testing.T) {
rec := doSageMakerRequest(t, h, "DescribeEndpoint", map[string]any{
"EndpointName": "ep2",
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
})
require.Equal(t, "InService", descResp["EndpointStatus"])
@@ -434,7 +436,7 @@ func TestHandler_EndpointLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recCreate.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut, jsonx.V1()))
assert.NotEmpty(t, createOut["EndpointArn"])
// Describe endpoint.
@@ -451,7 +453,7 @@ func TestHandler_EndpointLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut["Endpoints"].([]any), 1)
// Update endpoint.
diff --git a/services/sagemaker/handler_experiments.go b/services/sagemaker/handler_experiments.go
index b2534a1e6d..c16dab54c0 100644
--- a/services/sagemaker/handler_experiments.go
+++ b/services/sagemaker/handler_experiments.go
@@ -2,9 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -24,7 +25,7 @@ type createExperimentInput struct {
func (h *Handler) handleCreateExperiment(ctx context.Context, body []byte) ([]byte, error) {
var req createExperimentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -41,7 +42,7 @@ func (h *Handler) handleCreateExperiment(ctx context.Context, body []byte) ([]by
logger.Load(ctx).InfoContext(ctx, "sagemaker: created experiment", "name", e.ExperimentName)
- return json.Marshal(map[string]string{keyExperimentArn: e.ExperimentArn})
+ return json.Marshal(map[string]string{keyExperimentArn: e.ExperimentArn}, jsonx.V1())
}
// describeExperimentInput mirrors DescribeExperimentInput
@@ -53,7 +54,7 @@ type describeExperimentInput struct {
func (h *Handler) handleDescribeExperiment(ctx context.Context, body []byte) ([]byte, error) {
var req describeExperimentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -85,7 +86,7 @@ func (h *Handler) handleDescribeExperiment(ctx context.Context, body []byte) ([]
resp["LastModifiedBy"] = e.LastModifiedBy
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type experimentSummary struct {
@@ -110,7 +111,7 @@ type listExperimentsInput struct {
func (h *Handler) handleListExperiments(ctx context.Context, body []byte) ([]byte, error) {
var req listExperimentsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -138,7 +139,7 @@ func (h *Handler) handleListExperiments(ctx context.Context, body []byte) ([]byt
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// deleteExperimentInput mirrors DeleteExperimentInput
@@ -150,7 +151,7 @@ type deleteExperimentInput struct {
func (h *Handler) handleDeleteExperiment(ctx context.Context, body []byte) ([]byte, error) {
var req deleteExperimentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -165,7 +166,7 @@ func (h *Handler) handleDeleteExperiment(ctx context.Context, body []byte) ([]by
logger.Load(ctx).InfoContext(ctx, "sagemaker: deleted experiment", "name", req.ExperimentName)
- return json.Marshal(map[string]string{keyExperimentArn: e.ExperimentArn})
+ return json.Marshal(map[string]string{keyExperimentArn: e.ExperimentArn}, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -186,7 +187,7 @@ type updateExperimentInput struct {
func (h *Handler) handleUpdateExperiment(ctx context.Context, body []byte) ([]byte, error) {
var req updateExperimentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -201,5 +202,5 @@ func (h *Handler) handleUpdateExperiment(ctx context.Context, body []byte) ([]by
logger.Load(ctx).InfoContext(ctx, "sagemaker: updated experiment", "name", e.ExperimentName)
- return json.Marshal(map[string]string{keyExperimentArn: e.ExperimentArn})
+ return json.Marshal(map[string]string{keyExperimentArn: e.ExperimentArn}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_experiments_test.go b/services/sagemaker/handler_experiments_test.go
index 84a75405ce..7981a6f2a5 100644
--- a/services/sagemaker/handler_experiments_test.go
+++ b/services/sagemaker/handler_experiments_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -11,6 +11,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_ExperimentLifecycle(t *testing.T) {
@@ -25,7 +27,7 @@ func TestHandler_ExperimentLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recCreate.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut, jsonx.V1()))
assert.NotEmpty(t, createOut["ExperimentArn"])
// Describe experiment.
@@ -39,7 +41,7 @@ func TestHandler_ExperimentLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut["ExperimentSummaries"].([]any), 1)
// Delete experiment.
@@ -114,7 +116,7 @@ func TestHandler_UpdateExperiment(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.NotEmpty(t, updateResp["ExperimentArn"])
// Describe returns updated fields
@@ -122,7 +124,7 @@ func TestHandler_UpdateExperiment(t *testing.T) {
"ExperimentName": "my-exp",
})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "My Experiment", descResp["DisplayName"])
assert.Equal(t, "A test experiment", descResp["Description"])
}
@@ -152,7 +154,7 @@ func TestHandler_Tags_Experiment(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
expARN := createResp["ExperimentArn"]
require.NotEmpty(t, expARN)
@@ -162,7 +164,7 @@ func TestHandler_Tags_Experiment(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags := tagsResp["Tags"].([]any)
require.Len(t, tags, 1)
assert.Equal(t, "project", tags[0].(map[string]any)["Key"])
@@ -186,7 +188,7 @@ func TestHandler_CreateExperiment_DisplayNameAndDescription(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Friendly Name", descResp["DisplayName"])
assert.Equal(t, "An experiment created with a display name", descResp["Description"])
@@ -194,7 +196,7 @@ func TestHandler_CreateExperiment_DisplayNameAndDescription(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
summaries := listResp["ExperimentSummaries"].([]any)
require.Len(t, summaries, 1)
assert.Equal(t, "Friendly Name", summaries[0].(map[string]any)["DisplayName"])
diff --git a/services/sagemaker/handler_feature_groups.go b/services/sagemaker/handler_feature_groups.go
index dcd850c718..b3008e8acd 100644
--- a/services/sagemaker/handler_feature_groups.go
+++ b/services/sagemaker/handler_feature_groups.go
@@ -2,9 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -33,7 +34,7 @@ type createFeatureGroupInput struct {
func (h *Handler) handleCreateFeatureGroup(ctx context.Context, body []byte) ([]byte, error) {
var req createFeatureGroupInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -72,7 +73,7 @@ func (h *Handler) handleCreateFeatureGroup(ctx context.Context, body []byte) ([]
logger.Load(ctx).
InfoContext(ctx, "sagemaker: created feature group", "name", fg.FeatureGroupName)
- return json.Marshal(map[string]string{keyFeatureGroupArn: fg.FeatureGroupArn})
+ return json.Marshal(map[string]string{keyFeatureGroupArn: fg.FeatureGroupArn}, jsonx.V1())
}
// describeFeatureGroupInput mirrors DescribeFeatureGroupInput
@@ -89,7 +90,7 @@ type describeFeatureGroupInput struct {
func (h *Handler) handleDescribeFeatureGroup(ctx context.Context, body []byte) ([]byte, error) {
var req describeFeatureGroupInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -146,7 +147,7 @@ func (h *Handler) handleDescribeFeatureGroup(ctx context.Context, body []byte) (
resp["LastUpdateStatus"] = fg.LastUpdateStatus
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type featureGroupSummary struct {
@@ -175,7 +176,7 @@ type listFeatureGroupsInput struct {
func (h *Handler) handleListFeatureGroups(ctx context.Context, body []byte) ([]byte, error) {
var req listFeatureGroupsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -206,7 +207,7 @@ func (h *Handler) handleListFeatureGroups(ctx context.Context, body []byte) ([]b
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// deleteFeatureGroupInput mirrors DeleteFeatureGroupInput
@@ -218,7 +219,7 @@ type deleteFeatureGroupInput struct {
func (h *Handler) handleDeleteFeatureGroup(ctx context.Context, body []byte) error {
var req deleteFeatureGroupInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -254,7 +255,7 @@ type updateFeatureGroupInput struct {
func (h *Handler) handleUpdateFeatureGroup(ctx context.Context, body []byte) ([]byte, error) {
var req updateFeatureGroupInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -273,5 +274,5 @@ func (h *Handler) handleUpdateFeatureGroup(ctx context.Context, body []byte) ([]
logger.Load(ctx).InfoContext(ctx, "sagemaker: updated feature group", "name", fg.FeatureGroupName)
- return json.Marshal(map[string]string{keyFeatureGroupArn: fg.FeatureGroupArn})
+ return json.Marshal(map[string]string{keyFeatureGroupArn: fg.FeatureGroupArn}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_feature_groups_stores_test.go b/services/sagemaker/handler_feature_groups_stores_test.go
index 6b3c0f8729..e8945fb515 100644
--- a/services/sagemaker/handler_feature_groups_stores_test.go
+++ b/services/sagemaker/handler_feature_groups_stores_test.go
@@ -1,12 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateFeatureGroup_StoreConfigsRoundTrip(t *testing.T) {
@@ -49,7 +51,7 @@ func TestHandler_CreateFeatureGroup_StoreConfigsRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code, descRec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
onlineCfg, ok := out["OnlineStoreConfig"].(map[string]any)
require.True(t, ok, "OnlineStoreConfig must round-trip")
@@ -96,7 +98,7 @@ func TestHandler_CreateFeatureGroup_NoStoreConfigsOmitsFields(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code, descRec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
_, hasOnline := out["OnlineStoreConfig"]
_, hasOffline := out["OfflineStoreConfig"]
diff --git a/services/sagemaker/handler_feature_groups_test.go b/services/sagemaker/handler_feature_groups_test.go
index 75eae57f28..7e572763bf 100644
--- a/services/sagemaker/handler_feature_groups_test.go
+++ b/services/sagemaker/handler_feature_groups_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_FeatureGroupLifecycle(t *testing.T) {
@@ -34,7 +36,7 @@ func TestHandler_FeatureGroupLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recCreate.Code, recCreate.Body.String())
var createOut map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut, jsonx.V1()))
assert.NotEmpty(t, createOut["FeatureGroupArn"])
// Describe feature group.
@@ -48,7 +50,7 @@ func TestHandler_FeatureGroupLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut["FeatureGroupSummaries"].([]any), 1)
// Delete feature group.
@@ -125,7 +127,7 @@ func TestHandler_UpdateFeatureGroup(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.NotEmpty(t, updateResp["FeatureGroupArn"])
// Describe should show 2 features now
@@ -135,7 +137,7 @@ func TestHandler_UpdateFeatureGroup(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
defs := descResp["FeatureDefinitions"].([]any)
assert.Len(t, defs, 2)
}
@@ -170,7 +172,7 @@ func TestHandler_Tags_FeatureGroup(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
fgARN := createResp["FeatureGroupArn"]
require.NotEmpty(t, fgARN)
@@ -181,7 +183,7 @@ func TestHandler_Tags_FeatureGroup(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags := tagsResp["Tags"].([]any)
require.Len(t, tags, 1)
assert.Equal(t, "env", tags[0].(map[string]any)["Key"])
@@ -210,7 +212,7 @@ func TestHandler_CreateFeatureGroup_RoleArnAndDescription(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "arn:aws:iam::000000000000:role/FeatureStoreRole", descResp["RoleArn"])
assert.Equal(t, "A feature group with a role", descResp["Description"])
}
diff --git a/services/sagemaker/handler_feature_metadata.go b/services/sagemaker/handler_feature_metadata.go
index cffbd96b02..f6ae578c25 100644
--- a/services/sagemaker/handler_feature_metadata.go
+++ b/services/sagemaker/handler_feature_metadata.go
@@ -2,9 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -52,7 +54,7 @@ type describeFeatureMetadataInput struct {
func (h *Handler) handleDescribeFeatureMetadata(ctx context.Context, body []byte) ([]byte, error) {
var req describeFeatureMetadataInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -101,7 +103,7 @@ func (h *Handler) handleDescribeFeatureMetadata(ctx context.Context, body []byte
resp["Parameters"] = featureParametersToWire(meta.Parameters)
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// updateFeatureMetadataInput mirrors UpdateFeatureMetadataInput
@@ -123,7 +125,7 @@ type updateFeatureMetadataInput struct {
func (h *Handler) handleUpdateFeatureMetadata(ctx context.Context, body []byte) ([]byte, error) {
var req updateFeatureMetadataInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -161,7 +163,7 @@ func (h *Handler) handleUpdateFeatureMetadata(ctx context.Context, body []byte)
return nil, updateErr
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
// featureExistsInGroup reports whether featureName is one of fg's defined features.
diff --git a/services/sagemaker/handler_feature_metadata_test.go b/services/sagemaker/handler_feature_metadata_test.go
index 1be34e3fa0..c58c87ef01 100644
--- a/services/sagemaker/handler_feature_metadata_test.go
+++ b/services/sagemaker/handler_feature_metadata_test.go
@@ -2,13 +2,14 @@ package sagemaker_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -45,7 +46,7 @@ func TestHandler_UpdateAndDescribeFeatureMetadata(t *testing.T) {
assert.Equal(t, http.StatusOK, recDescribe.Code)
var describeOut map[string]any
- require.NoError(t, json.Unmarshal(recDescribe.Body.Bytes(), &describeOut))
+ require.NoError(t, json.Unmarshal(recDescribe.Body.Bytes(), &describeOut, jsonx.V1()))
assert.Equal(t, "the record id", describeOut["Description"])
assert.Equal(t, "id", describeOut["FeatureName"])
assert.Equal(t, "Integral", describeOut["FeatureType"])
@@ -90,7 +91,7 @@ func TestHandler_UpdateFeatureMetadata_ParameterRemovals(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
params, ok := out["Parameters"].([]any)
require.True(t, ok)
@@ -124,7 +125,7 @@ func TestHandler_DescribeFeatureMetadata_LastModifiedTimeDefaultsToGroupCreation
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, out["CreationTime"], out["LastModifiedTime"],
"a never-updated feature's LastModifiedTime falls back to the group's CreationTime")
diff --git a/services/sagemaker/handler_flow_definitions.go b/services/sagemaker/handler_flow_definitions.go
index 2210e8b723..ec92feda9a 100644
--- a/services/sagemaker/handler_flow_definitions.go
+++ b/services/sagemaker/handler_flow_definitions.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -48,7 +50,7 @@ type createFlowDefinitionInput struct {
func (h *Handler) handleCreateFlowDefinition(ctx context.Context, body []byte) ([]byte, error) {
var req createFlowDefinitionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -97,7 +99,7 @@ func (h *Handler) handleCreateFlowDefinition(ctx context.Context, body []byte) (
return nil, err
}
- return json.Marshal(map[string]any{keyFlowDefinitionArn: result.FlowDefinitionArn})
+ return json.Marshal(map[string]any{keyFlowDefinitionArn: result.FlowDefinitionArn}, jsonx.V1())
}
// describeFlowDefinitionInput mirrors DescribeFlowDefinitionInput
@@ -109,7 +111,7 @@ type describeFlowDefinitionInput struct {
func (h *Handler) handleDescribeFlowDefinition(ctx context.Context, body []byte) ([]byte, error) {
var req describeFlowDefinitionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -122,7 +124,7 @@ func (h *Handler) handleDescribeFlowDefinition(ctx context.Context, body []byte)
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// deleteFlowDefinitionInput mirrors DeleteFlowDefinitionInput
@@ -134,7 +136,7 @@ type deleteFlowDefinitionInput struct {
func (h *Handler) handleDeleteFlowDefinition(ctx context.Context, body []byte) error {
var req deleteFlowDefinitionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -163,7 +165,7 @@ type listFlowDefinitionsInput struct {
func (h *Handler) handleListFlowDefinitions(ctx context.Context, body []byte) ([]byte, error) {
var req listFlowDefinitionsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_flow_definitions_test.go b/services/sagemaker/handler_flow_definitions_test.go
index 4b57454038..a844285745 100644
--- a/services/sagemaker/handler_flow_definitions_test.go
+++ b/services/sagemaker/handler_flow_definitions_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func createFlowDefinitionRequest(name string) map[string]any {
@@ -29,7 +31,7 @@ func TestHandler_CreateFlowDefinition(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["FlowDefinitionArn"], "my-flow")
}
@@ -77,7 +79,7 @@ func TestHandler_DescribeFlowDefinition(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "flow-1", resp["FlowDefinitionName"])
}
@@ -152,13 +154,13 @@ func TestHandler_ListFlowDefinitions(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["FlowDefinitionSummaries"])
doSageMakerRequest(t, h, "CreateFlowDefinition", createFlowDefinitionRequest("my-flow"))
rec = doSageMakerRequest(t, h, "ListFlowDefinitions", map[string]any{})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
defs := resp["FlowDefinitionSummaries"].([]any)
assert.Len(t, defs, 1)
d := defs[0].(map[string]any)
diff --git a/services/sagemaker/handler_hp_tuning_jobs.go b/services/sagemaker/handler_hp_tuning_jobs.go
index 005d212c7f..6717d846f0 100644
--- a/services/sagemaker/handler_hp_tuning_jobs.go
+++ b/services/sagemaker/handler_hp_tuning_jobs.go
@@ -2,9 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -30,13 +32,13 @@ type hpResourceLimitsRequest struct {
// one Unmarshal pass — encoding/json drops both silently on that ambiguity —
// hence the second decode.)
type createHPTuningJobRequest struct {
- HyperParameterTuningJobName string `json:"HyperParameterTuningJobName"`
- HyperParameterTuningJobConfig json.RawMessage `json:"HyperParameterTuningJobConfig"`
- Autotune json.RawMessage `json:"Autotune"`
- WarmStartConfig json.RawMessage `json:"WarmStartConfig"`
- TrainingJobDefinition json.RawMessage `json:"TrainingJobDefinition"`
- TrainingJobDefinitions json.RawMessage `json:"TrainingJobDefinitions"`
- Tags []tagObject `json:"Tags"`
+ HyperParameterTuningJobName string `json:"HyperParameterTuningJobName"`
+ HyperParameterTuningJobConfig jsontext.Value `json:"HyperParameterTuningJobConfig"`
+ Autotune jsontext.Value `json:"Autotune"`
+ WarmStartConfig jsontext.Value `json:"WarmStartConfig"`
+ TrainingJobDefinition jsontext.Value `json:"TrainingJobDefinition"`
+ TrainingJobDefinitions jsontext.Value `json:"TrainingJobDefinitions"`
+ Tags []tagObject `json:"Tags"`
}
// hpTuningJobConfigStrategyAndLimits mirrors the Strategy/ResourceLimits
@@ -51,7 +53,7 @@ func (h *Handler) handleCreateHyperParameterTuningJob(
body []byte,
) ([]byte, error) {
var req createHPTuningJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -64,7 +66,7 @@ func (h *Handler) handleCreateHyperParameterTuningJob(
}
var config hpTuningJobConfigStrategyAndLimits
- if err := json.Unmarshal(req.HyperParameterTuningJobConfig, &config); err != nil {
+ if err := json.Unmarshal(req.HyperParameterTuningJobConfig, &config, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -93,7 +95,7 @@ func (h *Handler) handleCreateHyperParameterTuningJob(
log.InfoContext(ctx, "sagemaker: created HP tuning job", "name", j.HyperParameterTuningJobName)
return json.Marshal(
- map[string]string{"HyperParameterTuningJobArn": j.HyperParameterTuningJobArn},
+ map[string]string{"HyperParameterTuningJobArn": j.HyperParameterTuningJobArn}, jsonx.V1(),
)
}
@@ -109,7 +111,7 @@ func (h *Handler) handleDescribeHyperParameterTuningJob(
) ([]byte, error) {
var req describeHPTuningJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -153,7 +155,7 @@ func (h *Handler) handleDescribeHyperParameterTuningJob(
resp["HyperParameterTuningEndTime"] = epochSeconds(*j.HyperParameterTuningEndTime)
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type hpTuningJobSummary struct {
@@ -187,7 +189,7 @@ type listHPTuningJobsInput struct {
func (h *Handler) handleListHyperParameterTuningJobs(ctx context.Context, body []byte) ([]byte, error) {
var req listHPTuningJobsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -228,7 +230,7 @@ func (h *Handler) handleListHyperParameterTuningJobs(ctx context.Context, body [
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// stopHPTuningJobInput mirrors StopHyperParameterTuningJobInput
@@ -240,7 +242,7 @@ type stopHPTuningJobInput struct {
func (h *Handler) handleStopHyperParameterTuningJob(ctx context.Context, body []byte) error {
var req stopHPTuningJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -272,7 +274,7 @@ type deleteHPTuningJobInput struct {
func (h *Handler) handleDeleteHyperParameterTuningJob(ctx context.Context, body []byte) error {
var req deleteHPTuningJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -328,7 +330,7 @@ func (h *Handler) handleListTrainingJobsForHyperParameterTuningJob(
) ([]byte, error) {
var req listTrainingJobsForHPTuningJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -371,5 +373,5 @@ func (h *Handler) handleListTrainingJobsForHyperParameterTuningJob(
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/sagemaker/handler_hp_tuning_jobs_test.go b/services/sagemaker/handler_hp_tuning_jobs_test.go
index d84ac73093..441f013ed6 100644
--- a/services/sagemaker/handler_hp_tuning_jobs_test.go
+++ b/services/sagemaker/handler_hp_tuning_jobs_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -93,7 +94,7 @@ func TestHandler_ListTrainingJobsForHyperParameterTuningJob(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["TrainingJobSummaries"].([]any)
require.True(t, ok)
assert.Len(t, summaries, tt.wantSummaries)
@@ -125,7 +126,7 @@ func TestHandler_DescribeHyperParameterTuningJob_WireShape(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// Real AWS nests Strategy/ResourceLimits inside HyperParameterTuningJobConfig
// rather than emitting a flat top-level Strategy field — a client reading
@@ -179,7 +180,7 @@ func TestHandler_ListHyperParameterTuningJobs_WireShape(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["HyperParameterTuningJobSummaries"].([]any)
require.True(t, ok)
@@ -217,7 +218,7 @@ func TestHandler_HyperParameterTuningJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recCreate.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut, jsonx.V1()))
assert.NotEmpty(t, createOut["HyperParameterTuningJobArn"])
// Describe.
@@ -231,7 +232,7 @@ func TestHandler_HyperParameterTuningJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut["HyperParameterTuningJobSummaries"].([]any), 1)
// Stop.
@@ -279,7 +280,7 @@ func TestHandler_StopHyperParameterTuningJob_ReachesStopped(t *testing.T) {
"HyperParameterTuningJobName": "hpt-stop-reaches",
})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Stopping", descResp["HyperParameterTuningJobStatus"])
time.Sleep(time.Second)
@@ -288,7 +289,7 @@ func TestHandler_StopHyperParameterTuningJob_ReachesStopped(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeHyperParameterTuningJob", map[string]any{
"HyperParameterTuningJobName": "hpt-stop-reaches",
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Stopped", descResp["HyperParameterTuningJobStatus"])
})
}
@@ -319,7 +320,7 @@ func TestHandler_CreateHyperParameterTuningJob_ReachesCompleted(t *testing.T) {
"HyperParameterTuningJobName": "hpt-create-reaches-completed",
})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "InProgress", descResp["HyperParameterTuningJobStatus"])
time.Sleep(time.Second)
@@ -328,7 +329,7 @@ func TestHandler_CreateHyperParameterTuningJob_ReachesCompleted(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeHyperParameterTuningJob", map[string]any{
"HyperParameterTuningJobName": "hpt-create-reaches-completed",
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Completed", descResp["HyperParameterTuningJobStatus"])
})
}
diff --git a/services/sagemaker/handler_hub.go b/services/sagemaker/handler_hub.go
index a3e05e6831..d2c73bfb51 100644
--- a/services/sagemaker/handler_hub.go
+++ b/services/sagemaker/handler_hub.go
@@ -2,9 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -155,7 +156,7 @@ type createHubInput struct {
func (h *Handler) handleCreateHub(ctx context.Context, body []byte) ([]byte, error) {
var req createHubInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -182,7 +183,7 @@ func (h *Handler) handleCreateHub(ctx context.Context, body []byte) ([]byte, err
logger.Load(ctx).InfoContext(ctx, "sagemaker: created hub", "name", hub.HubName)
- return json.Marshal(map[string]string{keyHubArn: hub.HubArn})
+ return json.Marshal(map[string]string{keyHubArn: hub.HubArn}, jsonx.V1())
}
func hubToDescribeResponse(hub *Hub) map[string]any {
@@ -226,7 +227,7 @@ type describeHubInput struct {
func (h *Handler) handleDescribeHub(ctx context.Context, body []byte) ([]byte, error) {
var req describeHubInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -239,7 +240,7 @@ func (h *Handler) handleDescribeHub(ctx context.Context, body []byte) ([]byte, e
return nil, err
}
- return json.Marshal(hubToDescribeResponse(hub))
+ return json.Marshal(hubToDescribeResponse(hub), jsonx.V1())
}
type hubInfoSummary struct {
@@ -270,7 +271,7 @@ type listHubsInput struct {
func (h *Handler) handleListHubs(ctx context.Context, body []byte) ([]byte, error) {
var req listHubsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -305,7 +306,7 @@ func (h *Handler) handleListHubs(ctx context.Context, body []byte) ([]byte, erro
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// updateHubInput is the UpdateHub request shape (named, not inline — see
@@ -320,7 +321,7 @@ type updateHubInput struct {
func (h *Handler) handleUpdateHub(ctx context.Context, body []byte) ([]byte, error) {
var req updateHubInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -339,7 +340,7 @@ func (h *Handler) handleUpdateHub(ctx context.Context, body []byte) ([]byte, err
return nil, err
}
- return json.Marshal(map[string]string{keyHubArn: hub.HubArn})
+ return json.Marshal(map[string]string{keyHubArn: hub.HubArn}, jsonx.V1())
}
// deleteHubInput is the DeleteHub request shape (named, not inline — see
@@ -351,7 +352,7 @@ type deleteHubInput struct {
func (h *Handler) handleDeleteHub(ctx context.Context, body []byte) error {
var req deleteHubInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -393,7 +394,7 @@ type importHubContentInput struct {
func (h *Handler) handleImportHubContent(ctx context.Context, body []byte) ([]byte, error) {
var req importHubContentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -423,7 +424,7 @@ func (h *Handler) handleImportHubContent(ctx context.Context, body []byte) ([]by
logger.Load(ctx).InfoContext(ctx, "sagemaker: imported hub content",
"hub", hc.HubName, "name", hc.HubContentName, "version", hc.HubContentVersion)
- return json.Marshal(map[string]string{keyHubArn: hc.HubArn, keyHubContentArn: hc.HubContentArn})
+ return json.Marshal(map[string]string{keyHubArn: hc.HubArn, keyHubContentArn: hc.HubContentArn}, jsonx.V1())
}
// requireImportHubContentFields returns the name of the first missing required
@@ -535,7 +536,7 @@ type describeHubContentInput struct {
func (h *Handler) handleDescribeHubContent(ctx context.Context, body []byte) ([]byte, error) {
var req describeHubContentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -552,7 +553,7 @@ func (h *Handler) handleDescribeHubContent(ctx context.Context, body []byte) ([]
return nil, err
}
- return json.Marshal(hubContentToDescribeResponse(hc))
+ return json.Marshal(hubContentToDescribeResponse(hc), jsonx.V1())
}
type hubContentInfoSummary struct {
@@ -613,7 +614,7 @@ type listHubContentsInput struct {
func (h *Handler) handleListHubContents(ctx context.Context, body []byte) ([]byte, error) {
var req listHubContentsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -642,7 +643,7 @@ func (h *Handler) handleListHubContents(ctx context.Context, body []byte) ([]byt
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// listHubContentVersionsInput is the ListHubContentVersions request shape
@@ -665,7 +666,7 @@ type listHubContentVersionsInput struct {
func (h *Handler) handleListHubContentVersions(ctx context.Context, body []byte) ([]byte, error) {
var req listHubContentVersionsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -698,7 +699,7 @@ func (h *Handler) handleListHubContentVersions(ctx context.Context, body []byte)
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// deleteHubContentInput is the DeleteHubContent request shape (named, not
@@ -714,7 +715,7 @@ type deleteHubContentInput struct {
func (h *Handler) handleDeleteHubContent(ctx context.Context, body []byte) error {
var req deleteHubContentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -750,7 +751,7 @@ type createHubContentReferenceInput struct {
func (h *Handler) handleCreateHubContentReference(ctx context.Context, body []byte) ([]byte, error) {
var req createHubContentReferenceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -768,7 +769,7 @@ func (h *Handler) handleCreateHubContentReference(ctx context.Context, body []by
return nil, err
}
- return json.Marshal(map[string]string{keyHubArn: hc.HubArn, keyHubContentArn: hc.HubContentArn})
+ return json.Marshal(map[string]string{keyHubArn: hc.HubArn, keyHubContentArn: hc.HubContentArn}, jsonx.V1())
}
// deleteHubContentReferenceInput is the DeleteHubContentReference request
@@ -783,7 +784,7 @@ type deleteHubContentReferenceInput struct {
func (h *Handler) handleDeleteHubContentReference(ctx context.Context, body []byte) error {
var req deleteHubContentReferenceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -814,7 +815,7 @@ type updateHubContentInput struct {
func (h *Handler) handleUpdateHubContent(ctx context.Context, body []byte) ([]byte, error) {
var req updateHubContentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -838,7 +839,7 @@ func (h *Handler) handleUpdateHubContent(ctx context.Context, body []byte) ([]by
return nil, err
}
- return json.Marshal(map[string]string{keyHubArn: hc.HubArn, keyHubContentArn: hc.HubContentArn})
+ return json.Marshal(map[string]string{keyHubArn: hc.HubArn, keyHubContentArn: hc.HubContentArn}, jsonx.V1())
}
// updateHubContentReferenceInput is the UpdateHubContentReference request
@@ -854,7 +855,7 @@ type updateHubContentReferenceInput struct {
func (h *Handler) handleUpdateHubContentReference(ctx context.Context, body []byte) ([]byte, error) {
var req updateHubContentReferenceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -871,7 +872,7 @@ func (h *Handler) handleUpdateHubContentReference(ctx context.Context, body []by
return nil, err
}
- return json.Marshal(map[string]string{keyHubArn: hc.HubArn, keyHubContentArn: hc.HubContentArn})
+ return json.Marshal(map[string]string{keyHubArn: hc.HubArn, keyHubContentArn: hc.HubContentArn}, jsonx.V1())
}
// presignedURLAccessConfigJSON is the wire shape of PresignedUrlAccessConfig
@@ -898,7 +899,7 @@ type createHubContentPresignedURLsInput struct {
func (h *Handler) handleCreateHubContentPresignedURLs(ctx context.Context, body []byte) ([]byte, error) {
var req createHubContentPresignedURLsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -938,5 +939,5 @@ func (h *Handler) handleCreateHubContentPresignedURLs(ctx context.Context, body
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/sagemaker/handler_hub_test.go b/services/sagemaker/handler_hub_test.go
index 83b52c0540..f743f6a524 100644
--- a/services/sagemaker/handler_hub_test.go
+++ b/services/sagemaker/handler_hub_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -11,6 +11,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -55,7 +57,7 @@ func TestHandler_CreateHub(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["HubArn"], "hub/my-hub")
}
})
@@ -95,7 +97,7 @@ func TestHandler_HubLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recDesc.Code)
var desc map[string]any
- require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &desc, jsonx.V1()))
assert.Equal(t, "lifecycle-hub", desc["HubName"])
assert.Equal(t, "InService", desc["HubStatus"])
assert.Equal(t, "initial description", desc["HubDescription"])
@@ -117,7 +119,7 @@ func TestHandler_HubLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recDesc2.Code)
var desc2 map[string]any
- require.NoError(t, json.Unmarshal(recDesc2.Body.Bytes(), &desc2))
+ require.NoError(t, json.Unmarshal(recDesc2.Body.Bytes(), &desc2, jsonx.V1()))
assert.Equal(t, "updated description", desc2["HubDescription"])
// Untouched field should be preserved.
assert.Equal(t, "Lifecycle Hub", desc2["HubDisplayName"])
@@ -164,7 +166,7 @@ func TestHandler_ListHubs(t *testing.T) {
var resp struct {
HubSummaries []map[string]any `json:"HubSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.HubSummaries, 3)
// NameContains filter.
@@ -174,7 +176,7 @@ func TestHandler_ListHubs(t *testing.T) {
var filtered struct {
HubSummaries []map[string]any `json:"HubSummaries"`
}
- require.NoError(t, json.Unmarshal(recFiltered.Body.Bytes(), &filtered))
+ require.NoError(t, json.Unmarshal(recFiltered.Body.Bytes(), &filtered, jsonx.V1()))
require.Len(t, filtered.HubSummaries, 1)
assert.Equal(t, "beta-hub", filtered.HubSummaries[0]["HubName"])
}
@@ -363,7 +365,7 @@ func TestHandler_ImportHubContent(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["HubArn"])
assert.Contains(t, resp["HubContentArn"], "hub-content/content-hub/Model/my-model")
}
@@ -428,7 +430,7 @@ func TestHandler_DescribeHubContent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "my-model", resp["HubContentName"])
assert.Equal(t, "2.0.0", resp["HubContentVersion"])
assert.Equal(t, "Model", resp["HubContentType"])
@@ -486,7 +488,7 @@ func TestHandler_HubContent_ListsAndVersions(t *testing.T) {
var listResp struct {
HubContentSummaries []map[string]any `json:"HubContentSummaries"`
}
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp.HubContentSummaries, 2)
// ListHubContentVersions returns every version of one named content.
@@ -498,7 +500,7 @@ func TestHandler_HubContent_ListsAndVersions(t *testing.T) {
var versionsResp struct {
HubContentSummaries []map[string]any `json:"HubContentSummaries"`
}
- require.NoError(t, json.Unmarshal(recVersions.Body.Bytes(), &versionsResp))
+ require.NoError(t, json.Unmarshal(recVersions.Body.Bytes(), &versionsResp, jsonx.V1()))
require.Len(t, versionsResp.HubContentSummaries, 3)
}
@@ -735,7 +737,7 @@ func TestHandler_HubContentReference_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recCreate.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createResp, jsonx.V1()))
assert.NotEmpty(t, createResp["HubArn"])
assert.Contains(t, createResp["HubContentArn"], "hub-content/ref-hub/ModelReference/huggingface-model")
@@ -746,7 +748,7 @@ func TestHandler_HubContentReference_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recDesc.Code)
var desc map[string]any
- require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &desc, jsonx.V1()))
assert.Equal(t, publicArn, desc["SageMakerPublicHubContentArn"])
// Duplicate reference is rejected.
@@ -805,7 +807,7 @@ func TestHandler_CreateHubContentPresignedUrls(t *testing.T) {
var resp struct {
AuthorizedURLConfigs []map[string]string `json:"AuthorizedUrlConfigs"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.AuthorizedURLConfigs, 1)
assert.Contains(t, resp.AuthorizedURLConfigs[0]["Url"], "https://")
assert.NotEmpty(t, resp.AuthorizedURLConfigs[0]["LocalPath"])
@@ -919,7 +921,7 @@ func TestHandler_UpdateHubContent(t *testing.T) {
"HubName": "my-hub2", "HubContentName": "model-a", "HubContentType": "Model",
})
var out map[string]any
- require.NoError(t, json.Unmarshal(recDescribe.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recDescribe.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "new description", out["HubContentDescription"])
}
@@ -942,7 +944,7 @@ func TestHandler_UpdateHubContentReference(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["HubContentArn"])
}
diff --git a/services/sagemaker/handler_human_task_ui.go b/services/sagemaker/handler_human_task_ui.go
index 9d6a3b1b70..7bccf8a7d4 100644
--- a/services/sagemaker/handler_human_task_ui.go
+++ b/services/sagemaker/handler_human_task_ui.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -25,7 +27,7 @@ type createHumanTaskUIInput struct {
func (h *Handler) handleCreateHumanTaskUI(ctx context.Context, body []byte) ([]byte, error) {
var req createHumanTaskUIInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -44,7 +46,7 @@ func (h *Handler) handleCreateHumanTaskUI(ctx context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(map[string]any{keyHumanTaskUIArn: result.HumanTaskUIArn})
+ return json.Marshal(map[string]any{keyHumanTaskUIArn: result.HumanTaskUIArn}, jsonx.V1())
}
// describeHumanTaskUIInput mirrors DescribeHumanTaskUiInput
@@ -56,7 +58,7 @@ type describeHumanTaskUIInput struct {
func (h *Handler) handleDescribeHumanTaskUI(ctx context.Context, body []byte) ([]byte, error) {
var req describeHumanTaskUIInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -69,7 +71,7 @@ func (h *Handler) handleDescribeHumanTaskUI(ctx context.Context, body []byte) ([
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// deleteHumanTaskUIInput mirrors DeleteHumanTaskUiInput
@@ -81,7 +83,7 @@ type deleteHumanTaskUIInput struct {
func (h *Handler) handleDeleteHumanTaskUI(ctx context.Context, body []byte) error {
var req deleteHumanTaskUIInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -107,7 +109,7 @@ type listHumanTaskUIsInput struct {
func (h *Handler) handleListHumanTaskUIs(ctx context.Context, body []byte) ([]byte, error) {
var req listHumanTaskUIsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_human_task_ui_test.go b/services/sagemaker/handler_human_task_ui_test.go
index 61f6e890f1..ad63e71695 100644
--- a/services/sagemaker/handler_human_task_ui_test.go
+++ b/services/sagemaker/handler_human_task_ui_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateHumanTaskUI(t *testing.T) {
@@ -24,7 +26,7 @@ func TestHandler_CreateHumanTaskUI(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["HumanTaskUiArn"], "my-ui")
}
@@ -61,7 +63,7 @@ func TestHandler_DescribeHumanTaskUI(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ui-1", resp["HumanTaskUiName"])
}
@@ -110,7 +112,7 @@ func TestHandler_ListHumanTaskUIs(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["HumanTaskUiSummaries"])
doSageMakerRequest(t, h, "CreateHumanTaskUi", map[string]any{
@@ -119,7 +121,7 @@ func TestHandler_ListHumanTaskUIs(t *testing.T) {
})
rec = doSageMakerRequest(t, h, "ListHumanTaskUis", map[string]any{})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
uis := resp["HumanTaskUiSummaries"].([]any)
assert.Len(t, uis, 1)
u := uis[0].(map[string]any)
diff --git a/services/sagemaker/handler_hyperpod_scheduling.go b/services/sagemaker/handler_hyperpod_scheduling.go
index 289e90b251..e205007dff 100644
--- a/services/sagemaker/handler_hyperpod_scheduling.go
+++ b/services/sagemaker/handler_hyperpod_scheduling.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -21,7 +23,7 @@ type createClusterSchedulerConfigInput struct {
func (h *Handler) handleCreateClusterSchedulerConfig(ctx context.Context, body []byte) ([]byte, error) {
var req createClusterSchedulerConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -39,7 +41,7 @@ func (h *Handler) handleCreateClusterSchedulerConfig(ctx context.Context, body [
return json.Marshal(map[string]any{
keyClusterSchedulerConfigArn: c.ClusterSchedulerConfigArn,
keyClusterSchedulerConfigID: c.ClusterSchedulerConfigID,
- })
+ }, jsonx.V1())
}
type describeClusterSchedulerConfigInput struct {
@@ -50,7 +52,7 @@ type describeClusterSchedulerConfigInput struct {
func (h *Handler) handleDescribeClusterSchedulerConfig(ctx context.Context, body []byte) ([]byte, error) {
var req describeClusterSchedulerConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -61,7 +63,7 @@ func (h *Handler) handleDescribeClusterSchedulerConfig(ctx context.Context, body
return nil, err
}
- return json.Marshal(c)
+ return json.Marshal(c, jsonx.V1())
}
type listClusterSchedulerConfigsInput struct {
@@ -79,7 +81,7 @@ type listClusterSchedulerConfigsInput struct {
func (h *Handler) handleListClusterSchedulerConfigs(ctx context.Context, body []byte) ([]byte, error) {
var req listClusterSchedulerConfigsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -122,7 +124,7 @@ type updateClusterSchedulerConfigInput struct {
func (h *Handler) handleUpdateClusterSchedulerConfig(ctx context.Context, body []byte) ([]byte, error) {
var req updateClusterSchedulerConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -142,7 +144,7 @@ func (h *Handler) handleUpdateClusterSchedulerConfig(ctx context.Context, body [
return json.Marshal(map[string]any{
keyClusterSchedulerConfigArn: c.ClusterSchedulerConfigArn,
keyClusterSchedulerConfigVersion: c.ClusterSchedulerConfigVersion,
- })
+ }, jsonx.V1())
}
type deleteClusterSchedulerConfigInput struct {
@@ -152,7 +154,7 @@ type deleteClusterSchedulerConfigInput struct {
func (h *Handler) handleDeleteClusterSchedulerConfig(ctx context.Context, body []byte) ([]byte, error) {
var req deleteClusterSchedulerConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -160,7 +162,7 @@ func (h *Handler) handleDeleteClusterSchedulerConfig(ctx context.Context, body [
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -180,7 +182,7 @@ type createComputeQuotaInput struct {
func (h *Handler) handleCreateComputeQuota(ctx context.Context, body []byte) ([]byte, error) {
var req createComputeQuotaInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -200,7 +202,7 @@ func (h *Handler) handleCreateComputeQuota(ctx context.Context, body []byte) ([]
return json.Marshal(map[string]any{
keyComputeQuotaArn: q.ComputeQuotaArn,
keyComputeQuotaID: q.ComputeQuotaID,
- })
+ }, jsonx.V1())
}
type describeComputeQuotaInput struct {
@@ -211,7 +213,7 @@ type describeComputeQuotaInput struct {
func (h *Handler) handleDescribeComputeQuota(ctx context.Context, body []byte) ([]byte, error) {
var req describeComputeQuotaInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -220,7 +222,7 @@ func (h *Handler) handleDescribeComputeQuota(ctx context.Context, body []byte) (
return nil, err
}
- return json.Marshal(q)
+ return json.Marshal(q, jsonx.V1())
}
type listComputeQuotasInput struct {
@@ -238,7 +240,7 @@ type listComputeQuotasInput struct {
func (h *Handler) handleListComputeQuotas(ctx context.Context, body []byte) ([]byte, error) {
var req listComputeQuotasInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -286,7 +288,7 @@ type updateComputeQuotaInput struct {
func (h *Handler) handleUpdateComputeQuota(ctx context.Context, body []byte) ([]byte, error) {
var req updateComputeQuotaInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -307,7 +309,7 @@ func (h *Handler) handleUpdateComputeQuota(ctx context.Context, body []byte) ([]
return json.Marshal(map[string]any{
keyComputeQuotaArn: q.ComputeQuotaArn,
keyComputeQuotaVersion: q.ComputeQuotaVersion,
- })
+ }, jsonx.V1())
}
type deleteComputeQuotaInput struct {
@@ -317,7 +319,7 @@ type deleteComputeQuotaInput struct {
func (h *Handler) handleDeleteComputeQuota(ctx context.Context, body []byte) ([]byte, error) {
var req deleteComputeQuotaInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, err
}
@@ -325,5 +327,5 @@ func (h *Handler) handleDeleteComputeQuota(ctx context.Context, body []byte) ([]
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_images.go b/services/sagemaker/handler_images.go
index 87d4012ce5..55618dda53 100644
--- a/services/sagemaker/handler_images.go
+++ b/services/sagemaker/handler_images.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -21,7 +23,7 @@ type createImageInput struct {
func (h *Handler) handleCreateImage(ctx context.Context, body []byte) ([]byte, error) {
var req createImageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -36,7 +38,7 @@ func (h *Handler) handleCreateImage(ctx context.Context, body []byte) ([]byte, e
return nil, err
}
- return json.Marshal(map[string]any{"ImageArn": result.ImageArn})
+ return json.Marshal(map[string]any{"ImageArn": result.ImageArn}, jsonx.V1())
}
type describeImageInput struct {
@@ -46,7 +48,7 @@ type describeImageInput struct {
func (h *Handler) handleDescribeImage(ctx context.Context, body []byte) ([]byte, error) {
var req describeImageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -59,7 +61,7 @@ func (h *Handler) handleDescribeImage(ctx context.Context, body []byte) ([]byte,
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
type deleteImageInput struct {
@@ -69,7 +71,7 @@ type deleteImageInput struct {
func (h *Handler) handleDeleteImage(ctx context.Context, body []byte) error {
var req deleteImageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -95,7 +97,7 @@ type listImagesInput struct {
func (h *Handler) handleListImages(ctx context.Context, body []byte) ([]byte, error) {
var req listImagesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -135,7 +137,7 @@ func (h *Handler) handleListImages(ctx context.Context, body []byte) ([]byte, er
return json.Marshal(map[string]any{
"Images": summaries,
keyNextToken: next,
- })
+ }, jsonx.V1())
}
type updateImageInput struct {
@@ -149,7 +151,7 @@ type updateImageInput struct {
func (h *Handler) handleUpdateImage(ctx context.Context, body []byte) ([]byte, error) {
var req updateImageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -167,7 +169,7 @@ func (h *Handler) handleUpdateImage(ctx context.Context, body []byte) ([]byte, e
return nil, err
}
- return json.Marshal(map[string]any{keyImageArn: result.ImageArn})
+ return json.Marshal(map[string]any{keyImageArn: result.ImageArn}, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -191,7 +193,7 @@ type createImageVersionInput struct {
func (h *Handler) handleCreateImageVersion(ctx context.Context, body []byte) ([]byte, error) {
var req createImageVersionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -214,7 +216,7 @@ func (h *Handler) handleCreateImageVersion(ctx context.Context, body []byte) ([]
return nil, err
}
- return json.Marshal(map[string]any{"ImageVersionArn": result.ImageVersionArn})
+ return json.Marshal(map[string]any{"ImageVersionArn": result.ImageVersionArn}, jsonx.V1())
}
type describeImageVersionInput struct {
@@ -226,7 +228,7 @@ type describeImageVersionInput struct {
func (h *Handler) handleDescribeImageVersion(ctx context.Context, body []byte) ([]byte, error) {
var req describeImageVersionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -241,7 +243,7 @@ func (h *Handler) handleDescribeImageVersion(ctx context.Context, body []byte) (
result.ClientToken = ""
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
type deleteImageVersionInput struct {
@@ -253,7 +255,7 @@ type deleteImageVersionInput struct {
func (h *Handler) handleDeleteImageVersion(ctx context.Context, body []byte) error {
var req deleteImageVersionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -279,7 +281,7 @@ type listImageVersionsInput struct {
func (h *Handler) handleListImageVersions(ctx context.Context, body []byte) ([]byte, error) {
var req listImageVersionsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -313,7 +315,7 @@ func (h *Handler) handleListImageVersions(ctx context.Context, body []byte) ([]b
return json.Marshal(map[string]any{
"ImageVersions": summaries,
keyNextToken: next,
- })
+ }, jsonx.V1())
}
type updateImageVersionInput struct {
@@ -334,7 +336,7 @@ type updateImageVersionInput struct {
func (h *Handler) handleUpdateImageVersion(ctx context.Context, body []byte) ([]byte, error) {
var req updateImageVersionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -358,7 +360,7 @@ func (h *Handler) handleUpdateImageVersion(ctx context.Context, body []byte) ([]
return nil, err
}
- return json.Marshal(map[string]any{keyImageVersionArn: result.ImageVersionArn})
+ return json.Marshal(map[string]any{keyImageVersionArn: result.ImageVersionArn}, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -376,7 +378,7 @@ type listAliasesInput struct {
func (h *Handler) handleListAliases(ctx context.Context, body []byte) ([]byte, error) {
var req listAliasesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -396,5 +398,5 @@ func (h *Handler) handleListAliases(ctx context.Context, body []byte) ([]byte, e
resp[keyNextToken] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/sagemaker/handler_images_test.go b/services/sagemaker/handler_images_test.go
index 0f342f0064..c327c2b132 100644
--- a/services/sagemaker/handler_images_test.go
+++ b/services/sagemaker/handler_images_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -11,6 +11,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateImage(t *testing.T) {
@@ -25,7 +27,7 @@ func TestHandler_CreateImage(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["ImageArn"], "my-image")
}
@@ -40,7 +42,7 @@ func TestHandler_DescribeImage(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "img-1", resp["ImageName"])
assert.Equal(t, "CREATED", resp["ImageStatus"])
}
@@ -70,7 +72,7 @@ func TestHandler_ListImages(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["Images"].([]any)
assert.Len(t, items, 2)
}
@@ -92,7 +94,7 @@ func TestHandler_CreateImageVersion(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["ImageVersionArn"], "img-ver")
}
@@ -110,7 +112,7 @@ func TestHandler_DescribeImageVersion(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.InEpsilon(t, float64(1), resp["Version"], 0.001)
assert.Equal(t, "CREATED", resp["ImageVersionStatus"])
}
@@ -132,7 +134,7 @@ func TestHandler_ListImageVersions(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["ImageVersions"].([]any)
assert.Len(t, items, 2)
}
@@ -208,7 +210,7 @@ func TestHandler_ListAliases(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
aliases, _ := out["SageMakerImageVersionAliases"].([]any)
assert.ElementsMatch(t, []any{"latest", "stable"}, aliases)
}
@@ -249,7 +251,7 @@ func TestHandler_UpdateImage(t *testing.T) {
})
var resp map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "updated", resp["Description"])
assert.Equal(t, "My Image", resp["DisplayName"])
@@ -264,7 +266,7 @@ func TestHandler_UpdateImage(t *testing.T) {
})
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(describeRec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(describeRec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Empty(t, resp2["Description"])
}
@@ -307,7 +309,7 @@ func TestHandler_UpdateImageVersion(t *testing.T) {
})
var resp map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "PyTorch 2.0", resp["MLFramework"])
aliases, ok := resp["Aliases"].([]any)
require.True(t, ok)
@@ -326,7 +328,7 @@ func TestHandler_UpdateImageVersion(t *testing.T) {
})
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(describeRec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(describeRec2.Body.Bytes(), &resp2, jsonx.V1()))
aliases2, ok := resp2["Aliases"].([]any)
require.True(t, ok)
assert.Len(t, aliases2, 1)
diff --git a/services/sagemaker/handler_inference_components.go b/services/sagemaker/handler_inference_components.go
index 2664af7272..15d04a535d 100644
--- a/services/sagemaker/handler_inference_components.go
+++ b/services/sagemaker/handler_inference_components.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -25,7 +28,7 @@ type createInferenceComponentInput struct {
func (h *Handler) handleCreateInferenceComponent(ctx context.Context, body []byte) ([]byte, error) {
var req createInferenceComponentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -57,7 +60,7 @@ func (h *Handler) handleCreateInferenceComponent(ctx context.Context, body []byt
return json.Marshal(map[string]any{
keyInferenceComponentArn: c.InferenceComponentArn,
- })
+ }, jsonx.V1())
}
// inferenceComponentResponseMap builds the DescribeInferenceComponentOutput
@@ -123,7 +126,7 @@ type describeInferenceComponentInput struct {
func (h *Handler) handleDescribeInferenceComponent(ctx context.Context, body []byte) ([]byte, error) {
var req describeInferenceComponentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -132,7 +135,7 @@ func (h *Handler) handleDescribeInferenceComponent(ctx context.Context, body []b
return nil, err
}
- return json.Marshal(inferenceComponentResponseMap(c))
+ return json.Marshal(inferenceComponentResponseMap(c), jsonx.V1())
}
// listInferenceComponentsInput mirrors ListInferenceComponentsInput
@@ -155,7 +158,7 @@ type listInferenceComponentsInput struct {
func (h *Handler) handleListInferenceComponents(ctx context.Context, body []byte) ([]byte, error) {
var req listInferenceComponentsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -200,7 +203,7 @@ func (h *Handler) handleListInferenceComponents(ctx context.Context, body []byte
// (api_op_UpdateInferenceComponent.go:24-53). The real op has no VariantName
// member — a production variant is fixed at Create time.
type updateInferenceComponentInput struct {
- DeploymentConfig json.RawMessage `json:"DeploymentConfig"`
+ DeploymentConfig jsontext.Value `json:"DeploymentConfig"`
RuntimeConfig *InferenceComponentRuntimeConfigInput `json:"RuntimeConfig"`
InferenceComponentName string `json:"InferenceComponentName"`
Specification *InferenceComponentSpecification `json:"Specification"`
@@ -210,7 +213,7 @@ type updateInferenceComponentInput struct {
func (h *Handler) handleUpdateInferenceComponent(ctx context.Context, body []byte) ([]byte, error) {
var req updateInferenceComponentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -234,7 +237,7 @@ func (h *Handler) handleUpdateInferenceComponent(ctx context.Context, body []byt
return json.Marshal(map[string]any{
keyInferenceComponentArn: c.InferenceComponentArn,
- })
+ }, jsonx.V1())
}
// updateInferenceComponentRuntimeConfigInput mirrors
@@ -247,7 +250,7 @@ type updateInferenceComponentRuntimeConfigInput struct {
func (h *Handler) handleUpdateInferenceComponentRuntimeConfig(ctx context.Context, body []byte) ([]byte, error) {
var req updateInferenceComponentRuntimeConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -272,7 +275,7 @@ func (h *Handler) handleUpdateInferenceComponentRuntimeConfig(ctx context.Contex
return json.Marshal(map[string]any{
keyInferenceComponentArn: c.InferenceComponentArn,
- })
+ }, jsonx.V1())
}
// deleteInferenceComponentInput mirrors DeleteInferenceComponentInput
@@ -284,7 +287,7 @@ type deleteInferenceComponentInput struct {
func (h *Handler) handleDeleteInferenceComponent(ctx context.Context, body []byte) ([]byte, error) {
var req deleteInferenceComponentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -292,5 +295,5 @@ func (h *Handler) handleDeleteInferenceComponent(ctx context.Context, body []byt
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_inference_components_test.go b/services/sagemaker/handler_inference_components_test.go
index 9da5a15ba0..9409304470 100644
--- a/services/sagemaker/handler_inference_components_test.go
+++ b/services/sagemaker/handler_inference_components_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_InferenceComponentLifecycle(t *testing.T) {
@@ -32,7 +34,7 @@ func TestHandler_InferenceComponentLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Contains(t, createResp["InferenceComponentArn"], "my-component")
// Describe
@@ -42,7 +44,7 @@ func TestHandler_InferenceComponentLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "my-component", descResp["InferenceComponentName"])
assert.Equal(t, "my-endpoint", descResp["EndpointName"])
assert.Contains(t, descResp["EndpointArn"], "endpoint/my-endpoint")
@@ -62,7 +64,7 @@ func TestHandler_InferenceComponentLifecycle(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeInferenceComponent", map[string]any{
"InferenceComponentName": "my-component",
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "InService", descResp["InferenceComponentStatus"])
runtimeConfig, ok = descResp["RuntimeConfig"].(map[string]any)
@@ -74,7 +76,7 @@ func TestHandler_InferenceComponentLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
components := listResp["InferenceComponents"].([]any)
require.Len(t, components, 1)
@@ -91,7 +93,7 @@ func TestHandler_InferenceComponentLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Contains(t, updateResp["InferenceComponentArn"], "my-component")
// Update component: DeploymentConfig is opaque and echoed back verbatim
@@ -110,7 +112,7 @@ func TestHandler_InferenceComponentLifecycle(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeInferenceComponent", map[string]any{
"InferenceComponentName": "my-component",
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Updating", descResp["InferenceComponentStatus"])
lastDeploymentConfig, ok := descResp["LastDeploymentConfig"].(map[string]any)
@@ -203,7 +205,7 @@ func TestHandler_CreateInferenceComponent_SpecificationContainerImage(t *testing
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
spec, ok := resp["Specification"].(map[string]any)
require.True(t, ok)
@@ -255,7 +257,7 @@ func TestHandler_ListInferenceComponents_Filters(t *testing.T) {
rec := doSageMakerRequest(t, h, "ListInferenceComponents", map[string]any{"StatusEquals": "InService"})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
components, _ := resp["InferenceComponents"].([]any)
require.Len(t, components, 2)
@@ -289,7 +291,7 @@ func TestHandler_ListInferenceComponents_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
components, _ := resp["InferenceComponents"].([]any)
assert.Len(t, components, tc.wantCount)
})
@@ -314,7 +316,7 @@ func TestHandler_ListInferenceComponents_SortByName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
components, ok := resp["InferenceComponents"].([]any)
require.True(t, ok)
require.Len(t, components, 2)
@@ -356,7 +358,7 @@ func TestHandler_ListInferenceComponents_EndpointFilter(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
components := resp["InferenceComponents"].([]any)
assert.Len(t, components, 1)
}
diff --git a/services/sagemaker/handler_inference_experiments.go b/services/sagemaker/handler_inference_experiments.go
index e9df5afd74..c20181158f 100644
--- a/services/sagemaker/handler_inference_experiments.go
+++ b/services/sagemaker/handler_inference_experiments.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -35,7 +37,7 @@ type createInferenceExperimentInput struct {
func (h *Handler) handleCreateInferenceExperiment(ctx context.Context, body []byte) ([]byte, error) {
var req createInferenceExperimentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -69,7 +71,7 @@ func (h *Handler) handleCreateInferenceExperiment(ctx context.Context, body []by
return nil, err
}
- return json.Marshal(map[string]any{keyInferenceExperimentArn: result.Arn})
+ return json.Marshal(map[string]any{keyInferenceExperimentArn: result.Arn}, jsonx.V1())
}
// describeInferenceExperimentInput mirrors DescribeInferenceExperimentInput
@@ -81,7 +83,7 @@ type describeInferenceExperimentInput struct {
func (h *Handler) handleDescribeInferenceExperiment(ctx context.Context, body []byte) ([]byte, error) {
var req describeInferenceExperimentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -94,7 +96,7 @@ func (h *Handler) handleDescribeInferenceExperiment(ctx context.Context, body []
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// stopInferenceExperimentInput mirrors StopInferenceExperimentInput
@@ -115,7 +117,7 @@ type stopInferenceExperimentInput struct {
func (h *Handler) handleStopInferenceExperiment(ctx context.Context, body []byte) ([]byte, error) {
var req stopInferenceExperimentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -133,7 +135,7 @@ func (h *Handler) handleStopInferenceExperiment(ctx context.Context, body []byte
return nil, err
}
- return json.Marshal(map[string]any{keyInferenceExperimentArn: result.Arn})
+ return json.Marshal(map[string]any{keyInferenceExperimentArn: result.Arn}, jsonx.V1())
}
// deleteInferenceExperimentInput mirrors DeleteInferenceExperimentInput
@@ -149,7 +151,7 @@ type deleteInferenceExperimentInput struct {
func (h *Handler) handleDeleteInferenceExperiment(ctx context.Context, body []byte) ([]byte, error) {
var req deleteInferenceExperimentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -162,7 +164,7 @@ func (h *Handler) handleDeleteInferenceExperiment(ctx context.Context, body []by
return nil, err
}
- return json.Marshal(map[string]any{keyInferenceExperimentArn: arn})
+ return json.Marshal(map[string]any{keyInferenceExperimentArn: arn}, jsonx.V1())
}
// startInferenceExperimentInput mirrors StartInferenceExperimentInput
@@ -174,7 +176,7 @@ type startInferenceExperimentInput struct {
func (h *Handler) handleStartInferenceExperiment(ctx context.Context, body []byte) ([]byte, error) {
var req startInferenceExperimentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -187,7 +189,7 @@ func (h *Handler) handleStartInferenceExperiment(ctx context.Context, body []byt
return nil, err
}
- return json.Marshal(map[string]any{keyInferenceExperimentArn: result.Arn})
+ return json.Marshal(map[string]any{keyInferenceExperimentArn: result.Arn}, jsonx.V1())
}
// updateInferenceExperimentInput mirrors UpdateInferenceExperimentInput
@@ -205,7 +207,7 @@ type updateInferenceExperimentInput struct {
func (h *Handler) handleUpdateInferenceExperiment(ctx context.Context, body []byte) ([]byte, error) {
var req updateInferenceExperimentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -224,7 +226,7 @@ func (h *Handler) handleUpdateInferenceExperiment(ctx context.Context, body []by
return nil, err
}
- return json.Marshal(map[string]any{keyInferenceExperimentArn: result.Arn})
+ return json.Marshal(map[string]any{keyInferenceExperimentArn: result.Arn}, jsonx.V1())
}
// listInferenceExperimentsInput mirrors ListInferenceExperimentsInput
@@ -249,7 +251,7 @@ type listInferenceExperimentsInput struct {
func (h *Handler) handleListInferenceExperiments(ctx context.Context, body []byte) ([]byte, error) {
var req listInferenceExperimentsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_inference_experiments_test.go b/services/sagemaker/handler_inference_experiments_test.go
index 9a5a9979dd..be75919bc8 100644
--- a/services/sagemaker/handler_inference_experiments_test.go
+++ b/services/sagemaker/handler_inference_experiments_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func minimalInferenceExperimentBody(name string) map[string]any {
@@ -35,7 +37,7 @@ func TestHandler_CreateInferenceExperiment(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["InferenceExperimentArn"], "my-exp")
}
@@ -80,7 +82,7 @@ func TestHandler_DescribeInferenceExperiment(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "exp-1", resp["Name"])
assert.Contains(t, resp, "EndpointMetadata")
assert.Contains(t, resp, "ModelVariants")
@@ -99,7 +101,7 @@ func TestHandler_StopInferenceExperiment(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["InferenceExperimentArn"], "exp-stop")
}
@@ -113,7 +115,7 @@ func TestHandler_DeleteInferenceExperiment(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["InferenceExperimentArn"], "exp-del")
rec = doSageMakerRequest(t, h, "DescribeInferenceExperiment", map[string]any{"Name": "exp-del"})
@@ -141,7 +143,7 @@ func TestHandler_InferenceExperiment_StartAndUpdate(t *testing.T) {
recDescribe := doSageMakerRequest(t, h, "DescribeInferenceExperiment", map[string]any{"Name": "inf-exp"})
var out map[string]any
- require.NoError(t, json.Unmarshal(recDescribe.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recDescribe.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "Running", out["Status"])
assert.Equal(t, "updated", out["Description"])
}
@@ -174,7 +176,7 @@ func TestHandler_ListInferenceExperiments(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["InferenceExperiments"])
body := minimalInferenceExperimentBody("my-exp")
@@ -182,7 +184,7 @@ func TestHandler_ListInferenceExperiments(t *testing.T) {
doSageMakerRequest(t, h, "CreateInferenceExperiment", body)
rec = doSageMakerRequest(t, h, "ListInferenceExperiments", map[string]any{})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
exps := resp["InferenceExperiments"].([]any)
assert.Len(t, exps, 1)
e := exps[0].(map[string]any)
diff --git a/services/sagemaker/handler_inference_recommendations_jobs.go b/services/sagemaker/handler_inference_recommendations_jobs.go
index 04de314f37..b5ebcd9343 100644
--- a/services/sagemaker/handler_inference_recommendations_jobs.go
+++ b/services/sagemaker/handler_inference_recommendations_jobs.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -13,20 +16,20 @@ import (
// createInferenceRecommendationsJobInput mirrors
// CreateInferenceRecommendationsJobInput (api_op_CreateInferenceRecommendationsJob.go:1-58).
type createInferenceRecommendationsJobInput struct {
- JobName string `json:"JobName"`
- JobType string `json:"JobType,omitempty"`
- JobDescription string `json:"JobDescription,omitempty"`
- RoleArn string `json:"RoleArn,omitempty"`
- InputConfig json.RawMessage `json:"InputConfig,omitempty"`
- OutputConfig json.RawMessage `json:"OutputConfig,omitempty"`
- StoppingConditions json.RawMessage `json:"StoppingConditions,omitempty"`
- Tags []tagObject `json:"Tags,omitempty"`
+ JobName string `json:"JobName"`
+ JobType string `json:"JobType,omitempty"`
+ JobDescription string `json:"JobDescription,omitempty"`
+ RoleArn string `json:"RoleArn,omitempty"`
+ InputConfig jsontext.Value `json:"InputConfig,omitempty"`
+ OutputConfig jsontext.Value `json:"OutputConfig,omitempty"`
+ StoppingConditions jsontext.Value `json:"StoppingConditions,omitempty"`
+ Tags []tagObject `json:"Tags,omitempty"`
}
func (h *Handler) handleCreateInferenceRecommendationsJob(ctx context.Context, body []byte) ([]byte, error) {
var req createInferenceRecommendationsJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -52,7 +55,7 @@ func (h *Handler) handleCreateInferenceRecommendationsJob(ctx context.Context, b
return nil, err
}
- return json.Marshal(map[string]string{keyJobArn: j.JobArn})
+ return json.Marshal(map[string]string{keyJobArn: j.JobArn}, jsonx.V1())
}
// describeInferenceRecommendationsJobInput mirrors
@@ -64,7 +67,7 @@ type describeInferenceRecommendationsJobInput struct {
func (h *Handler) handleDescribeInferenceRecommendationsJob(ctx context.Context, body []byte) ([]byte, error) {
var req describeInferenceRecommendationsJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -103,7 +106,7 @@ func (h *Handler) handleDescribeInferenceRecommendationsJob(ctx context.Context,
resp["StoppingConditions"] = j.StoppingConditions
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// stopInferenceRecommendationsJobInput mirrors
@@ -115,7 +118,7 @@ type stopInferenceRecommendationsJobInput struct {
func (h *Handler) handleStopInferenceRecommendationsJob(ctx context.Context, body []byte) error {
var req stopInferenceRecommendationsJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -157,7 +160,7 @@ type listInferenceRecommendationsJobsInput struct {
func (h *Handler) handleListInferenceRecommendationsJobs(ctx context.Context, body []byte) ([]byte, error) {
var req listInferenceRecommendationsJobsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -198,7 +201,7 @@ func (h *Handler) handleListInferenceRecommendationsJobs(ctx context.Context, bo
resp[keyNextToken] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// listInferenceRecommendationsJobStepsInput mirrors
@@ -218,7 +221,7 @@ type listInferenceRecommendationsJobStepsInput struct {
func (h *Handler) handleListInferenceRecommendationsJobSteps(ctx context.Context, body []byte) ([]byte, error) {
var req listInferenceRecommendationsJobStepsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -230,5 +233,5 @@ func (h *Handler) handleListInferenceRecommendationsJobSteps(ctx context.Context
return nil, err
}
- return json.Marshal(map[string]any{"Steps": []any{}})
+ return json.Marshal(map[string]any{"Steps": []any{}}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_inference_recommendations_jobs_test.go b/services/sagemaker/handler_inference_recommendations_jobs_test.go
index d9498ca705..e282418017 100644
--- a/services/sagemaker/handler_inference_recommendations_jobs_test.go
+++ b/services/sagemaker/handler_inference_recommendations_jobs_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -12,6 +12,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_InferenceRecommendationsJobLifecycle(t *testing.T) {
@@ -34,7 +36,7 @@ func TestHandler_InferenceRecommendationsJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Contains(t, createResp["JobArn"], "my-rec-job")
// Describe
@@ -44,7 +46,7 @@ func TestHandler_InferenceRecommendationsJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "my-rec-job", descResp["JobName"])
assert.Equal(t, "IN_PROGRESS", descResp["Status"])
assert.Equal(t, "Default", descResp["JobType"])
@@ -56,7 +58,7 @@ func TestHandler_InferenceRecommendationsJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
summaries := listResp["InferenceRecommendationsJobs"].([]any)
assert.Len(t, summaries, 1)
@@ -67,7 +69,7 @@ func TestHandler_InferenceRecommendationsJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var stepsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stepsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stepsResp, jsonx.V1()))
steps := stepsResp["Steps"].([]any)
assert.Empty(t, steps)
@@ -80,7 +82,7 @@ func TestHandler_InferenceRecommendationsJobLifecycle(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeInferenceRecommendationsJob", map[string]any{
"JobName": "my-rec-job",
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "STOPPING", descResp["Status"])
// Correct immediately after Stop, but an assertion that only checks this
@@ -92,7 +94,7 @@ func TestHandler_InferenceRecommendationsJobLifecycle(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeInferenceRecommendationsJob", map[string]any{
"JobName": "my-rec-job",
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "STOPPED", descResp["Status"])
})
}
@@ -121,7 +123,7 @@ func TestHandler_InferenceRecommendationsJob_ReachesCompleted(t *testing.T) {
"JobName": "rec-job-completes",
})
var out map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "COMPLETED", out["Status"])
})
}
@@ -150,7 +152,7 @@ func TestHandler_CreateInferenceRecommendationsJob_InputConfigRoundTrip(t *testi
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
inputConfig, ok := descResp["InputConfig"].(map[string]any)
require.True(t, ok, "DescribeInferenceRecommendationsJob must return the accepted InputConfig")
@@ -211,7 +213,7 @@ func TestHandler_CreateInferenceRecommendationsJob_JobTypeDefault(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "Default", resp["JobType"])
}
@@ -353,7 +355,7 @@ func TestHandler_AIRecommendationJobLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Contains(t, createResp["AIRecommendationJobArn"], "rec-2")
rec = doSageMakerRequest(t, h, "DescribeAIRecommendationJob", map[string]any{
@@ -362,7 +364,7 @@ func TestHandler_AIRecommendationJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "InProgress", descResp["AIRecommendationJobStatus"])
assert.Empty(t, descResp["Recommendations"])
},
@@ -385,7 +387,7 @@ func TestHandler_AIRecommendationJobLifecycle(t *testing.T) {
"AIRecommendationJobName": "rec-stop",
})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Stopping", descResp["AIRecommendationJobStatus"])
},
},
@@ -422,7 +424,7 @@ func TestHandler_AIRecommendationJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["AIRecommendationJobs"].([]any)
require.Len(t, items, 1)
assert.Equal(t, "rec-list", items[0].(map[string]any)["AIRecommendationJobName"])
diff --git a/services/sagemaker/handler_jobs.go b/services/sagemaker/handler_jobs.go
index a32e9cae97..4c70ce6de4 100644
--- a/services/sagemaker/handler_jobs.go
+++ b/services/sagemaker/handler_jobs.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// aiAndGenericJobOpsSupported aggregates the operation lists for the four
@@ -87,7 +89,7 @@ type createJobInput struct {
func (h *Handler) handleCreateJob(ctx context.Context, body []byte) ([]byte, error) {
var req createJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -103,7 +105,7 @@ func (h *Handler) handleCreateJob(ctx context.Context, body []byte) ([]byte, err
return nil, err
}
- return json.Marshal(map[string]string{keyJobArn: j.JobArn})
+ return json.Marshal(map[string]string{keyJobArn: j.JobArn}, jsonx.V1())
}
// describeJobInput mirrors DescribeJobInput (api_op_DescribeJob.go:27-38):
@@ -116,7 +118,7 @@ type describeJobInput struct {
func (h *Handler) handleDescribeJob(ctx context.Context, body []byte) ([]byte, error) {
var req describeJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -139,13 +141,13 @@ func (h *Handler) handleDescribeJob(ctx context.Context, body []byte) ([]byte, e
// had any tags at all. Round-trips through Job.MarshalJSON (for its
// epoch-seconds time handling) then substitutes the Tags shape.
func marshalJobResponse(j *Job) ([]byte, error) {
- raw, err := json.Marshal(j)
+ raw, err := json.Marshal(j, jsonx.V1())
if err != nil {
return nil, err
}
var resp map[string]any
- if unmarshalErr := json.Unmarshal(raw, &resp); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(raw, &resp, jsonx.V1()); unmarshalErr != nil {
return nil, unmarshalErr
}
@@ -155,7 +157,7 @@ func marshalJobResponse(j *Job) ([]byte, error) {
delete(resp, "Tags")
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// deleteJobInput mirrors DeleteJobInput (api_op_DeleteJob.go:27-38): both
@@ -168,7 +170,7 @@ type deleteJobInput struct {
func (h *Handler) handleDeleteJob(ctx context.Context, body []byte) error {
var req deleteJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -189,7 +191,7 @@ type stopJobInput struct {
func (h *Handler) handleStopJob(ctx context.Context, body []byte) error {
var req stopJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -222,7 +224,7 @@ type listJobsInput struct {
func (h *Handler) handleListJobs(ctx context.Context, body []byte) ([]byte, error) {
var req listJobsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -278,7 +280,7 @@ type describeJobSchemaVersionInput struct {
func (h *Handler) handleDescribeJobSchemaVersion(ctx context.Context, body []byte) ([]byte, error) {
var req describeJobSchemaVersionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -295,7 +297,7 @@ func (h *Handler) handleDescribeJobSchemaVersion(ctx context.Context, body []byt
"JobCategory": req.JobCategory,
"JobConfigSchema": info.JobConfigSchema,
"JobConfigSchemaVersion": info.JobConfigSchemaVersion,
- })
+ }, jsonx.V1())
}
// listJobSchemaVersionsInput mirrors ListJobSchemaVersionsInput
@@ -310,7 +312,7 @@ type listJobSchemaVersionsInput struct {
func (h *Handler) handleListJobSchemaVersions(ctx context.Context, body []byte) ([]byte, error) {
var req listJobSchemaVersionsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_labeling.go b/services/sagemaker/handler_labeling.go
index 2dc833e00d..fd56a14517 100644
--- a/services/sagemaker/handler_labeling.go
+++ b/services/sagemaker/handler_labeling.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// labelingOpsSupported returns the labeling-job / workteam-subscription
@@ -93,7 +95,7 @@ type createLabelingJobRequest struct {
func (h *Handler) handleCreateLabelingJob(ctx context.Context, body []byte) ([]byte, error) {
var req createLabelingJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -137,7 +139,7 @@ func (h *Handler) handleCreateLabelingJob(ctx context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(map[string]any{keyLabelingJobArn: result.LabelingJobArn})
+ return json.Marshal(map[string]any{keyLabelingJobArn: result.LabelingJobArn}, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -201,7 +203,7 @@ type describeLabelingJobInput struct {
func (h *Handler) handleDescribeLabelingJob(ctx context.Context, body []byte) ([]byte, error) {
var req describeLabelingJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -214,7 +216,7 @@ func (h *Handler) handleDescribeLabelingJob(ctx context.Context, body []byte) ([
return nil, err
}
- return json.Marshal(labelingJobResponseMap(result))
+ return json.Marshal(labelingJobResponseMap(result), jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -229,7 +231,7 @@ type stopLabelingJobInput struct {
func (h *Handler) handleStopLabelingJob(ctx context.Context, body []byte) ([]byte, error) {
var req stopLabelingJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -241,7 +243,7 @@ func (h *Handler) handleStopLabelingJob(ctx context.Context, body []byte) ([]byt
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -280,7 +282,7 @@ type listLabelingJobsInput struct {
func (h *Handler) handleListLabelingJobs(ctx context.Context, body []byte) ([]byte, error) {
var req listLabelingJobsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -319,7 +321,7 @@ func (h *Handler) handleListLabelingJobs(ctx context.Context, body []byte) ([]by
resp[keyNextToken] = next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -353,7 +355,7 @@ type listLabelingJobsForWorkteamInput struct {
func (h *Handler) handleListLabelingJobsForWorkteam(ctx context.Context, body []byte) ([]byte, error) {
var req listLabelingJobsForWorkteamInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -395,7 +397,7 @@ func (h *Handler) handleListLabelingJobsForWorkteam(ctx context.Context, body []
resp[keyNextToken] = next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -422,11 +424,11 @@ type listSubscribedWorkteamsInput struct {
func (h *Handler) handleListSubscribedWorkteams(_ context.Context, body []byte) ([]byte, error) {
var req listSubscribedWorkteamsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
- return json.Marshal(map[string]any{"SubscribedWorkteams": []any{}})
+ return json.Marshal(map[string]any{"SubscribedWorkteams": []any{}}, jsonx.V1())
}
// describeSubscribedWorkteamInput mirrors DescribeSubscribedWorkteamInput
@@ -438,7 +440,7 @@ type describeSubscribedWorkteamInput struct {
func (h *Handler) handleDescribeSubscribedWorkteam(_ context.Context, body []byte) error {
var req describeSubscribedWorkteamInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_labeling_test.go b/services/sagemaker/handler_labeling_test.go
index 9c0ec7027d..92bc73eea0 100644
--- a/services/sagemaker/handler_labeling_test.go
+++ b/services/sagemaker/handler_labeling_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -52,7 +54,7 @@ func TestHandler_CreateLabelingJob(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["LabelingJobArn"], "labeling-job/job-1")
}
@@ -73,7 +75,7 @@ func TestHandler_CreateLabelingJob_TagsRoundTripThroughDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags, ok := resp["Tags"].([]any)
require.True(t, ok, "DescribeLabelingJob must return Tags")
require.Len(t, tags, 1)
@@ -135,7 +137,7 @@ func TestHandler_DescribeLabelingJob_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "job-lifecycle", resp["LabelingJobName"])
assert.Equal(t, "Initializing", resp["LabelingJobStatus"])
assert.NotEmpty(t, resp["JobReferenceCode"])
@@ -148,7 +150,7 @@ func TestHandler_DescribeLabelingJob_Lifecycle(t *testing.T) {
synctest.Wait()
rec = doSageMakerRequest(t, h, "DescribeLabelingJob", map[string]any{"LabelingJobName": "job-lifecycle"})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "Completed", resp["LabelingJobStatus"])
})
}
@@ -162,7 +164,7 @@ func TestHandler_DescribeLabelingJob_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
@@ -186,14 +188,14 @@ func TestHandler_StopLabelingJob(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeLabelingJob", map[string]any{"LabelingJobName": "job-stop"})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, []any{"Stopping", "Stopped"}, resp["LabelingJobStatus"])
time.Sleep(time.Second)
synctest.Wait()
rec = doSageMakerRequest(t, h, "DescribeLabelingJob", map[string]any{"LabelingJobName": "job-stop"})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "Stopped", resp["LabelingJobStatus"])
})
}
@@ -229,7 +231,7 @@ func TestHandler_ListLabelingJobs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["LabelingJobSummaryList"].([]any)
require.True(t, ok)
assert.Len(t, items, 2)
@@ -240,7 +242,7 @@ func TestHandler_ListLabelingJobs(t *testing.T) {
// NameContains filter.
rec = doSageMakerRequest(t, h, "ListLabelingJobs", map[string]any{"NameContains": "list-job-a"})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ = resp["LabelingJobSummaryList"].([]any)
assert.Len(t, items, 1)
}
@@ -269,7 +271,7 @@ func TestHandler_ListLabelingJobsForWorkteam(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["LabelingJobSummaryList"].([]any)
require.True(t, ok)
require.Len(t, items, 1)
@@ -308,7 +310,7 @@ func TestHandler_ListLabelingJobs_ResponseIncludesInputConfigAndOutput(t *testin
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["LabelingJobSummaryList"].([]any)
require.True(t, ok)
require.Len(t, items, 1)
@@ -322,7 +324,7 @@ func TestHandler_ListLabelingJobs_ResponseIncludesInputConfigAndOutput(t *testin
synctest.Wait()
rec = doSageMakerRequest(t, h, "ListLabelingJobs", map[string]any{})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok = resp["LabelingJobSummaryList"].([]any)
require.True(t, ok)
require.Len(t, items, 1)
@@ -378,7 +380,7 @@ func TestHandler_ListLabelingJobs_TimeFilters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["LabelingJobSummaryList"].([]any)
assert.Len(t, items, tc.wantCount)
})
@@ -429,7 +431,7 @@ func TestHandler_ListLabelingJobs_SortBy(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["LabelingJobSummaryList"].([]any)
require.True(t, ok)
require.Len(t, items, 2)
@@ -454,7 +456,7 @@ func TestHandler_ListLabelingJobsForWorkteam_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, describeRec.Code)
var describeResp map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp, jsonx.V1()))
jobReferenceCode, ok := describeResp["JobReferenceCode"].(string)
require.True(t, ok)
require.NotEmpty(t, jobReferenceCode)
@@ -490,7 +492,7 @@ func TestHandler_ListLabelingJobsForWorkteam_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["LabelingJobSummaryList"].([]any)
assert.Len(t, items, tc.wantCount)
})
@@ -512,7 +514,7 @@ func TestHandler_ListWorkforces(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["Workforces"].([]any)
require.True(t, ok)
require.Len(t, items, 1)
@@ -563,7 +565,7 @@ func TestHandler_DeleteWorkforce_ResourceInUse(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceInUse", resp["__type"])
}
@@ -591,7 +593,7 @@ func TestHandler_UpdateWorkforce_WithSourceIPConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wf, ok := resp["Workforce"].(map[string]any)
require.True(t, ok)
@@ -623,7 +625,7 @@ func TestHandler_CreateWorkteam_WithWorkforceName(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeWorkteam", map[string]any{"WorkteamName": "team-link"})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wt, ok := resp["Workteam"].(map[string]any)
require.True(t, ok)
assert.Contains(t, wt["WorkforceArn"], "workforce/wf-link")
@@ -662,7 +664,7 @@ func TestHandler_UpdateWorkteam(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wt, ok := resp["Workteam"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "after", wt["Description"])
@@ -689,7 +691,7 @@ func TestHandler_DeleteWorkteam_ReturnsSuccess(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, true, resp["Success"])
}
@@ -706,7 +708,7 @@ func TestHandler_ListSubscribedWorkteams_AlwaysEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["SubscribedWorkteams"].([]any)
require.True(t, ok)
assert.Empty(t, items)
@@ -724,7 +726,7 @@ func TestHandler_DescribeSubscribedWorkteam_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
@@ -782,7 +784,7 @@ func TestLabelingFamily_SnapshotRestore(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var wtResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &wtResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &wtResp, jsonx.V1()))
wt, ok := wtResp["Workteam"].(map[string]any)
require.True(t, ok)
assert.Contains(t, wt["WorkforceArn"], "workforce/persist-wf")
@@ -791,6 +793,6 @@ func TestLabelingFamily_SnapshotRestore(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var jobResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &jobResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &jobResp, jsonx.V1()))
assert.Equal(t, "persist-job", jobResp["LabelingJobName"])
}
diff --git a/services/sagemaker/handler_lineage.go b/services/sagemaker/handler_lineage.go
index 8127595339..9a1076831a 100644
--- a/services/sagemaker/handler_lineage.go
+++ b/services/sagemaker/handler_lineage.go
@@ -2,9 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -174,7 +175,7 @@ type createArtifactInput struct {
func (h *Handler) handleCreateArtifact(ctx context.Context, body []byte) ([]byte, error) {
var req createArtifactInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -201,7 +202,7 @@ func (h *Handler) handleCreateArtifact(ctx context.Context, body []byte) ([]byte
logger.Load(ctx).InfoContext(ctx, "sagemaker: created artifact", "arn", ar.ArtifactArn)
- return json.Marshal(map[string]string{keyArtifactArn: ar.ArtifactArn})
+ return json.Marshal(map[string]string{keyArtifactArn: ar.ArtifactArn}, jsonx.V1())
}
// describeArtifactInput is the DescribeArtifact request shape (named, not
@@ -213,7 +214,7 @@ type describeArtifactInput struct {
func (h *Handler) handleDescribeArtifact(ctx context.Context, body []byte) ([]byte, error) {
var req describeArtifactInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -245,7 +246,7 @@ func (h *Handler) handleDescribeArtifact(ctx context.Context, body []byte) ([]by
resp["MetadataProperties"] = toMetadataProperties(ar.MetadataProperties)
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// updateArtifactInput is the UpdateArtifact request shape (named, not
@@ -260,7 +261,7 @@ type updateArtifactInput struct {
func (h *Handler) handleUpdateArtifact(ctx context.Context, body []byte) ([]byte, error) {
var req updateArtifactInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -275,7 +276,7 @@ func (h *Handler) handleUpdateArtifact(ctx context.Context, body []byte) ([]byte
logger.Load(ctx).InfoContext(ctx, "sagemaker: updated artifact", "arn", ar.ArtifactArn)
- return json.Marshal(map[string]string{keyArtifactArn: ar.ArtifactArn})
+ return json.Marshal(map[string]string{keyArtifactArn: ar.ArtifactArn}, jsonx.V1())
}
// deleteArtifactInput is the DeleteArtifact request shape (named, not
@@ -290,7 +291,7 @@ type deleteArtifactInput struct {
func (h *Handler) handleDeleteArtifact(ctx context.Context, body []byte) ([]byte, error) {
var req deleteArtifactInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -307,7 +308,7 @@ func (h *Handler) handleDeleteArtifact(ctx context.Context, body []byte) ([]byte
logger.Load(ctx).InfoContext(ctx, "sagemaker: deleted artifact", "arn", ar.ArtifactArn)
- return json.Marshal(map[string]string{keyArtifactArn: ar.ArtifactArn})
+ return json.Marshal(map[string]string{keyArtifactArn: ar.ArtifactArn}, jsonx.V1())
}
type artifactSummary struct {
@@ -332,7 +333,7 @@ func marshalSummaryPage[T, S any](key, nextToken string, items []*T, conv func(*
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// listArtifactsInput is the ListArtifacts request shape (named, not inline —
@@ -351,7 +352,7 @@ type listArtifactsInput struct {
func (h *Handler) handleListArtifacts(ctx context.Context, body []byte) ([]byte, error) {
var req listArtifactsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -412,7 +413,7 @@ type createContextInput struct {
func (h *Handler) handleCreateContext(ctx context.Context, body []byte) ([]byte, error) {
var req createContextInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -443,7 +444,7 @@ func (h *Handler) handleCreateContext(ctx context.Context, body []byte) ([]byte,
logger.Load(ctx).InfoContext(ctx, "sagemaker: created context", "name", c.ContextName)
- return json.Marshal(map[string]string{keyContextArn: c.ContextArn})
+ return json.Marshal(map[string]string{keyContextArn: c.ContextArn}, jsonx.V1())
}
// describeContextInput is the DescribeContext request shape (named, not
@@ -455,7 +456,7 @@ type describeContextInput struct {
func (h *Handler) handleDescribeContext(ctx context.Context, body []byte) ([]byte, error) {
var req describeContextInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -484,7 +485,7 @@ func (h *Handler) handleDescribeContext(ctx context.Context, body []byte) ([]byt
resp["Properties"] = c.Properties
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// updateContextInput is the UpdateContext request shape (named, not
@@ -499,7 +500,7 @@ type updateContextInput struct {
func (h *Handler) handleUpdateContext(ctx context.Context, body []byte) ([]byte, error) {
var req updateContextInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -514,7 +515,7 @@ func (h *Handler) handleUpdateContext(ctx context.Context, body []byte) ([]byte,
logger.Load(ctx).InfoContext(ctx, "sagemaker: updated context", "name", c.ContextName)
- return json.Marshal(map[string]string{keyContextArn: c.ContextArn})
+ return json.Marshal(map[string]string{keyContextArn: c.ContextArn}, jsonx.V1())
}
// deleteContextInput is the DeleteContext request shape (named, not
@@ -526,7 +527,7 @@ type deleteContextInput struct {
func (h *Handler) handleDeleteContext(ctx context.Context, body []byte) ([]byte, error) {
var req deleteContextInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -541,7 +542,7 @@ func (h *Handler) handleDeleteContext(ctx context.Context, body []byte) ([]byte,
logger.Load(ctx).InfoContext(ctx, "sagemaker: deleted context", "name", req.ContextName)
- return json.Marshal(map[string]string{keyContextArn: c.ContextArn})
+ return json.Marshal(map[string]string{keyContextArn: c.ContextArn}, jsonx.V1())
}
type contextSummary struct {
@@ -569,7 +570,7 @@ type listContextsInput struct {
func (h *Handler) handleListContexts(ctx context.Context, body []byte) ([]byte, error) {
var req listContextsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -611,7 +612,7 @@ type describeActionInput struct {
func (h *Handler) handleDescribeAction(ctx context.Context, body []byte) ([]byte, error) {
var req describeActionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -651,7 +652,7 @@ func (h *Handler) handleDescribeAction(ctx context.Context, body []byte) ([]byte
resp["MetadataProperties"] = toMetadataProperties(a.MetadataProperties)
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// updateActionInput is the UpdateAction request shape (named, not
@@ -667,7 +668,7 @@ type updateActionInput struct {
func (h *Handler) handleUpdateAction(ctx context.Context, body []byte) ([]byte, error) {
var req updateActionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -684,7 +685,7 @@ func (h *Handler) handleUpdateAction(ctx context.Context, body []byte) ([]byte,
logger.Load(ctx).InfoContext(ctx, "sagemaker: updated action", "name", a.ActionName)
- return json.Marshal(map[string]string{keyActionArn: a.ActionArn})
+ return json.Marshal(map[string]string{keyActionArn: a.ActionArn}, jsonx.V1())
}
// deleteActionInput is the DeleteAction request shape (named, not
@@ -696,7 +697,7 @@ type deleteActionInput struct {
func (h *Handler) handleDeleteAction(ctx context.Context, body []byte) ([]byte, error) {
var req deleteActionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -711,7 +712,7 @@ func (h *Handler) handleDeleteAction(ctx context.Context, body []byte) ([]byte,
logger.Load(ctx).InfoContext(ctx, "sagemaker: deleted action", "name", req.ActionName)
- return json.Marshal(map[string]string{keyActionArn: a.ActionArn})
+ return json.Marshal(map[string]string{keyActionArn: a.ActionArn}, jsonx.V1())
}
type actionSummary struct {
@@ -740,7 +741,7 @@ type listActionsInput struct {
func (h *Handler) handleListActions(ctx context.Context, body []byte) ([]byte, error) {
var req listActionsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -784,7 +785,7 @@ type deleteAssociationInput struct {
func (h *Handler) handleDeleteAssociation(ctx context.Context, body []byte) ([]byte, error) {
var req deleteAssociationInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -803,7 +804,7 @@ func (h *Handler) handleDeleteAssociation(ctx context.Context, body []byte) ([]b
logger.Load(ctx).InfoContext(ctx, "sagemaker: deleted association",
"source", req.SourceArn, "destination", req.DestinationArn)
- return json.Marshal(map[string]string{"SourceArn": req.SourceArn, "DestinationArn": req.DestinationArn})
+ return json.Marshal(map[string]string{"SourceArn": req.SourceArn, "DestinationArn": req.DestinationArn}, jsonx.V1())
}
// associationSummary mirrors types.AssociationSummary (types.go:157-188).
@@ -839,7 +840,7 @@ type listAssociationsInput struct {
func (h *Handler) handleListAssociations(ctx context.Context, body []byte) ([]byte, error) {
var req listAssociationsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -888,7 +889,7 @@ type describeLineageGroupInput struct {
func (h *Handler) handleDescribeLineageGroup(ctx context.Context, body []byte) ([]byte, error) {
var req describeLineageGroupInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -906,7 +907,7 @@ func (h *Handler) handleDescribeLineageGroup(ctx context.Context, body []byte) (
"LineageGroupName": req.LineageGroupName,
keyCreationTime: epochSeconds(createdAt),
keyLastModifiedTime: epochSeconds(createdAt),
- })
+ }, jsonx.V1())
}
// listLineageGroupsInput is the ListLineageGroups request shape (named, not
@@ -923,7 +924,7 @@ type listLineageGroupsInput struct {
func (h *Handler) handleListLineageGroups(ctx context.Context, body []byte) ([]byte, error) {
var req listLineageGroupsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -951,7 +952,7 @@ func (h *Handler) handleListLineageGroups(ctx context.Context, body []byte) ([]b
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// getLineageGroupPolicyInput is the GetLineageGroupPolicy request shape
@@ -963,7 +964,7 @@ type getLineageGroupPolicyInput struct {
func (h *Handler) handleGetLineageGroupPolicy(ctx context.Context, body []byte) ([]byte, error) {
var req getLineageGroupPolicyInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -1007,7 +1008,7 @@ type queryLineageInput struct {
func (h *Handler) handleQueryLineage(ctx context.Context, body []byte) ([]byte, error) {
var req queryLineageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -1056,7 +1057,7 @@ func (h *Handler) handleQueryLineage(ctx context.Context, body []byte) ([]byte,
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// fromQueryFilters converts the wire Filters object to backend QueryLineageFilters.
@@ -1087,7 +1088,7 @@ type addAssociationRequest struct {
func (h *Handler) handleAddAssociation(ctx context.Context, body []byte) ([]byte, error) {
var req addAssociationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -1118,7 +1119,7 @@ func (h *Handler) handleAddAssociation(ctx context.Context, body []byte) ([]byte
return json.Marshal(map[string]string{
"SourceArn": req.SourceArn,
"DestinationArn": req.DestinationArn,
- })
+ }, jsonx.V1())
}
// associateTrialComponentRequest is the request body for AssociateTrialComponent.
@@ -1129,7 +1130,7 @@ type associateTrialComponentRequest struct {
func (h *Handler) handleAssociateTrialComponent(ctx context.Context, body []byte) ([]byte, error) {
var req associateTrialComponentRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -1153,7 +1154,7 @@ func (h *Handler) handleAssociateTrialComponent(ctx context.Context, body []byte
return json.Marshal(map[string]string{
"TrialArn": assoc.TrialArn,
"TrialComponentArn": assoc.TrialComponentArn,
- })
+ }, jsonx.V1())
}
// actionSourceRequest is the source for a CreateAction request.
@@ -1180,7 +1181,7 @@ type createActionRequest struct {
func (h *Handler) handleCreateAction(ctx context.Context, body []byte) ([]byte, error) {
var req createActionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -1212,5 +1213,5 @@ func (h *Handler) handleCreateAction(ctx context.Context, body []byte) ([]byte,
log := logger.Load(ctx)
log.InfoContext(ctx, "sagemaker: created action", "name", a.ActionName)
- return json.Marshal(map[string]string{"ActionArn": a.ActionArn})
+ return json.Marshal(map[string]string{"ActionArn": a.ActionArn}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_lineage_test.go b/services/sagemaker/handler_lineage_test.go
index e2875ef159..123c2e8a57 100644
--- a/services/sagemaker/handler_lineage_test.go
+++ b/services/sagemaker/handler_lineage_test.go
@@ -2,7 +2,7 @@ package sagemaker_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -34,7 +35,7 @@ func TestHandler_ArtifactLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recCreate.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut, jsonx.V1()))
artifactArn, _ := createOut["ArtifactArn"].(string)
require.NotEmpty(t, artifactArn)
assert.Contains(t, artifactArn, "artifact/")
@@ -44,7 +45,7 @@ func TestHandler_ArtifactLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recDesc.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, "my-artifact", descOut["ArtifactName"])
assert.Equal(t, "DataSet", descOut["ArtifactType"])
assert.NotZero(t, descOut["CreationTime"])
@@ -59,7 +60,7 @@ func TestHandler_ArtifactLifecycle(t *testing.T) {
recDesc2 := doSageMakerRequest(t, h, "DescribeArtifact", map[string]any{"ArtifactArn": artifactArn})
var descOut2 map[string]any
- require.NoError(t, json.Unmarshal(recDesc2.Body.Bytes(), &descOut2))
+ require.NoError(t, json.Unmarshal(recDesc2.Body.Bytes(), &descOut2, jsonx.V1()))
assert.Equal(t, "renamed-artifact", descOut2["ArtifactName"])
props, _ := descOut2["Properties"].(map[string]any)
assert.Equal(t, "final", props["stage"])
@@ -70,7 +71,7 @@ func TestHandler_ArtifactLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut["ArtifactSummaries"].([]any), 1)
// Delete.
@@ -79,7 +80,7 @@ func TestHandler_ArtifactLifecycle(t *testing.T) {
recList2 := doSageMakerRequest(t, h, "ListArtifacts", map[string]any{})
var listOut2 map[string]any
- require.NoError(t, json.Unmarshal(recList2.Body.Bytes(), &listOut2))
+ require.NoError(t, json.Unmarshal(recList2.Body.Bytes(), &listOut2, jsonx.V1()))
assert.Empty(t, listOut2["ArtifactSummaries"].([]any))
}
@@ -140,7 +141,7 @@ func TestHandler_ListArtifacts_FilterByType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries := out["ArtifactSummaries"].([]any)
require.Len(t, summaries, 1)
assert.Equal(t, "a2", summaries[0].(map[string]any)["ArtifactName"])
@@ -300,7 +301,7 @@ func TestHandler_ContextLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recCreate.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut, jsonx.V1()))
contextArn, _ := createOut["ContextArn"].(string)
require.NotEmpty(t, contextArn)
assert.Contains(t, contextArn, "context/my-context")
@@ -309,7 +310,7 @@ func TestHandler_ContextLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recDesc.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, "Endpoint", descOut["ContextType"])
recUpdate := doSageMakerRequest(t, h, "UpdateContext", map[string]any{
@@ -320,12 +321,12 @@ func TestHandler_ContextLifecycle(t *testing.T) {
recDesc2 := doSageMakerRequest(t, h, "DescribeContext", map[string]any{"ContextName": "my-context"})
var descOut2 map[string]any
- require.NoError(t, json.Unmarshal(recDesc2.Body.Bytes(), &descOut2))
+ require.NoError(t, json.Unmarshal(recDesc2.Body.Bytes(), &descOut2, jsonx.V1()))
assert.Equal(t, "updated", descOut2["Description"])
recList := doSageMakerRequest(t, h, "ListContexts", map[string]any{})
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut["ContextSummaries"].([]any), 1)
recDelete := doSageMakerRequest(t, h, "DeleteContext", map[string]any{"ContextName": "my-context"})
@@ -428,7 +429,7 @@ func TestHandler_ActionLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recDesc.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, "ModelDeployment", descOut["ActionType"])
assert.Equal(t, "InProgress", descOut["Status"])
@@ -440,12 +441,12 @@ func TestHandler_ActionLifecycle(t *testing.T) {
recDesc2 := doSageMakerRequest(t, h, "DescribeAction", map[string]any{"ActionName": "my-action"})
var descOut2 map[string]any
- require.NoError(t, json.Unmarshal(recDesc2.Body.Bytes(), &descOut2))
+ require.NoError(t, json.Unmarshal(recDesc2.Body.Bytes(), &descOut2, jsonx.V1()))
assert.Equal(t, "Completed", descOut2["Status"])
recList := doSageMakerRequest(t, h, "ListActions", map[string]any{})
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut["ActionSummaries"].([]any), 1)
recDelete := doSageMakerRequest(t, h, "DeleteAction", map[string]any{"ActionName": "my-action"})
@@ -552,7 +553,7 @@ func TestHandler_AssociationLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
summaries := listOut["AssociationSummaries"].([]any)
require.Len(t, summaries, 1)
assoc := summaries[0].(map[string]any)
@@ -568,7 +569,7 @@ func TestHandler_AssociationLifecycle(t *testing.T) {
recList2 := doSageMakerRequest(t, h, "ListAssociations", map[string]any{})
var listOut2 map[string]any
- require.NoError(t, json.Unmarshal(recList2.Body.Bytes(), &listOut2))
+ require.NoError(t, json.Unmarshal(recList2.Body.Bytes(), &listOut2, jsonx.V1()))
assert.Empty(t, listOut2["AssociationSummaries"].([]any))
}
@@ -595,7 +596,7 @@ func TestHandler_ListAssociations_ResolvesEntityNames(t *testing.T) {
"Source": map[string]any{"SourceUri": "arn:aws:sagemaker:us-east-1:0:endpoint/e"},
})
var actionOut map[string]any
- require.NoError(t, json.Unmarshal(recAction.Body.Bytes(), &actionOut))
+ require.NoError(t, json.Unmarshal(recAction.Body.Bytes(), &actionOut, jsonx.V1()))
actionArn := actionOut["ActionArn"].(string)
recArtifact := doSageMakerRequest(t, h, "CreateArtifact", map[string]any{
@@ -604,7 +605,7 @@ func TestHandler_ListAssociations_ResolvesEntityNames(t *testing.T) {
"Source": map[string]any{"SourceUri": "s3://bucket/data"},
})
var artifactOut map[string]any
- require.NoError(t, json.Unmarshal(recArtifact.Body.Bytes(), &artifactOut))
+ require.NoError(t, json.Unmarshal(recArtifact.Body.Bytes(), &artifactOut, jsonx.V1()))
artifactArn := artifactOut["ArtifactArn"].(string)
doSageMakerRequest(t, h, "AddAssociation", map[string]any{
@@ -615,7 +616,7 @@ func TestHandler_ListAssociations_ResolvesEntityNames(t *testing.T) {
rec := doSageMakerRequest(t, h, "ListAssociations", map[string]any{})
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assoc := out["AssociationSummaries"].([]any)[0].(map[string]any)
assert.Equal(t, "src-action", assoc["SourceName"])
assert.Equal(t, "ModelDeployment", assoc["SourceType"])
@@ -656,7 +657,7 @@ func listAssociationSourceArns(t *testing.T, body []byte) []string {
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
summaries, _ := out["AssociationSummaries"].([]any)
arns := make([]string, 0, len(summaries))
@@ -787,7 +788,7 @@ func TestHandler_ListAssociations_MaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries, _ := out["AssociationSummaries"].([]any)
require.Len(t, summaries, 1, "MaxResults=1 should return exactly one association out of two")
@@ -802,7 +803,7 @@ func TestHandler_ListAssociations_MaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
summaries2, _ := out2["AssociationSummaries"].([]any)
require.Len(t, summaries2, 1, "second page should return the remaining association")
@@ -844,7 +845,7 @@ func TestHandler_QueryLineage_Traversal(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
vertices := out["Vertices"].([]any)
arns := make([]string, 0, len(vertices))
@@ -884,7 +885,7 @@ func TestHandler_QueryLineage_MaxDepth(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
vertices := out["Vertices"].([]any)
arns := make([]string, 0, len(vertices))
@@ -917,7 +918,7 @@ func TestHandler_QueryLineage_UnknownArn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
vertices := out["Vertices"].([]any)
require.Len(t, vertices, 1)
assert.Empty(t, vertices[0].(map[string]any)["Type"])
@@ -1042,7 +1043,7 @@ func TestHandler_DescribeLineageGroup(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out["LineageGroupArn"], "lineage-group/sagemaker-default-lineage-group")
}
@@ -1064,7 +1065,7 @@ func TestHandler_ListLineageGroups(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
summaries := out["LineageGroupSummaries"].([]any)
require.Len(t, summaries, 1)
assert.Equal(t, "sagemaker-default-lineage-group", summaries[0].(map[string]any)["LineageGroupName"])
@@ -1154,7 +1155,7 @@ func createTestContext(t *testing.T, h *sagemaker.Handler, name string) string {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["ContextArn"].(string)
}
@@ -1170,7 +1171,7 @@ func createTestAction(t *testing.T, h *sagemaker.Handler, name string) string {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["ActionArn"].(string)
}
@@ -1186,7 +1187,7 @@ func createTestArtifact(t *testing.T, h *sagemaker.Handler, name string) string
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out["ArtifactArn"].(string)
}
@@ -1256,7 +1257,7 @@ func TestCreateAction_TagsPresent(t *testing.T) {
if tt.wantTags {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arnStr := resp["ActionArn"]
require.NotEmpty(t, arnStr)
@@ -1265,7 +1266,7 @@ func TestCreateAction_TagsPresent(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagsResp, jsonx.V1()))
tags := tagsResp["Tags"].([]any)
require.Len(t, tags, 1)
@@ -1339,7 +1340,7 @@ func TestHandler_AddAssociation(t *testing.T) {
if tt.wantARN {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["SourceArn"], "arn:aws:sagemaker")
assert.Contains(t, resp["DestinationArn"], "arn:aws:sagemaker")
}
@@ -1439,7 +1440,7 @@ func TestHandler_AssociateTrialComponent(t *testing.T) {
if tt.wantArns {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["TrialArn"], "arn:aws:sagemaker")
assert.Contains(t, resp["TrialComponentArn"], "arn:aws:sagemaker")
}
@@ -1527,7 +1528,7 @@ func TestHandler_CreateAction(t *testing.T) {
if tt.wantARN {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["ActionArn"], "arn:aws:sagemaker")
assert.Contains(t, resp["ActionArn"], "action/")
}
diff --git a/services/sagemaker/handler_list_notebook_filters_test.go b/services/sagemaker/handler_list_notebook_filters_test.go
index 70f37bfafe..9f36e748cb 100644
--- a/services/sagemaker/handler_list_notebook_filters_test.go
+++ b/services/sagemaker/handler_list_notebook_filters_test.go
@@ -1,12 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestListNotebookInstances_Filters verifies that ListNotebookInstances honors
@@ -65,7 +67,7 @@ func TestListNotebookInstances_Filters(t *testing.T) {
} `json:"NotebookInstances"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
gotNames := make([]string, 0, len(resp.NotebookInstances))
for _, nb := range resp.NotebookInstances {
diff --git a/services/sagemaker/handler_mlflow.go b/services/sagemaker/handler_mlflow.go
index b69c44da27..262d363dd2 100644
--- a/services/sagemaker/handler_mlflow.go
+++ b/services/sagemaker/handler_mlflow.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -28,7 +30,7 @@ type createMlflowTrackingServerInput struct {
func (h *Handler) handleCreateMlflowTrackingServer(ctx context.Context, body []byte) ([]byte, error) {
var req createMlflowTrackingServerInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -67,7 +69,7 @@ func (h *Handler) handleCreateMlflowTrackingServer(ctx context.Context, body []b
return nil, err
}
- return json.Marshal(map[string]any{keyTrackingServerArn: result.TrackingServerArn})
+ return json.Marshal(map[string]any{keyTrackingServerArn: result.TrackingServerArn}, jsonx.V1())
}
// describeMlflowTrackingServerInput is DescribeMlflowTrackingServer's request
@@ -104,7 +106,7 @@ type describeMlflowTrackingServerResponse struct {
func (h *Handler) handleDescribeMlflowTrackingServer(ctx context.Context, body []byte) ([]byte, error) {
var req describeMlflowTrackingServerInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -135,7 +137,7 @@ func (h *Handler) handleDescribeMlflowTrackingServer(ctx context.Context, body [
S3BucketOwnerVerification: result.S3BucketOwnerVerification,
CreationTime: epochSeconds(result.CreationTime),
LastModifiedTime: epochSeconds(result.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// deleteMlflowTrackingServerInput is DeleteMlflowTrackingServer's request
@@ -147,7 +149,7 @@ type deleteMlflowTrackingServerInput struct {
func (h *Handler) handleDeleteMlflowTrackingServer(ctx context.Context, body []byte) ([]byte, error) {
var req deleteMlflowTrackingServerInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -160,7 +162,7 @@ func (h *Handler) handleDeleteMlflowTrackingServer(ctx context.Context, body []b
return nil, err
}
- return json.Marshal(map[string]any{keyTrackingServerArn: arnStr})
+ return json.Marshal(map[string]any{keyTrackingServerArn: arnStr}, jsonx.V1())
}
// startMlflowTrackingServerInput is StartMlflowTrackingServer's request shape
@@ -172,7 +174,7 @@ type startMlflowTrackingServerInput struct {
func (h *Handler) handleStartMlflowTrackingServer(ctx context.Context, body []byte) ([]byte, error) {
var req startMlflowTrackingServerInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -185,7 +187,7 @@ func (h *Handler) handleStartMlflowTrackingServer(ctx context.Context, body []by
return nil, err
}
- return json.Marshal(map[string]any{keyTrackingServerArn: arnStr})
+ return json.Marshal(map[string]any{keyTrackingServerArn: arnStr}, jsonx.V1())
}
// stopMlflowTrackingServerInput is StopMlflowTrackingServer's request shape
@@ -197,7 +199,7 @@ type stopMlflowTrackingServerInput struct {
func (h *Handler) handleStopMlflowTrackingServer(ctx context.Context, body []byte) ([]byte, error) {
var req stopMlflowTrackingServerInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -210,7 +212,7 @@ func (h *Handler) handleStopMlflowTrackingServer(ctx context.Context, body []byt
return nil, err
}
- return json.Marshal(map[string]any{keyTrackingServerArn: arnStr})
+ return json.Marshal(map[string]any{keyTrackingServerArn: arnStr}, jsonx.V1())
}
// createPresignedMlflowTrackingServerURLInput is
@@ -226,7 +228,7 @@ type createPresignedMlflowTrackingServerURLInput struct {
func (h *Handler) handleCreatePresignedMlflowTrackingServerURL(ctx context.Context, body []byte) ([]byte, error) {
var req createPresignedMlflowTrackingServerURLInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -243,7 +245,7 @@ func (h *Handler) handleCreatePresignedMlflowTrackingServerURL(ctx context.Conte
return nil, err
}
- return json.Marshal(map[string]string{keyAuthorizedURL: url})
+ return json.Marshal(map[string]string{keyAuthorizedURL: url}, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -266,7 +268,7 @@ type createMlflowAppInput struct {
func (h *Handler) handleCreateMlflowApp(ctx context.Context, body []byte) ([]byte, error) {
var req createMlflowAppInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -288,7 +290,7 @@ func (h *Handler) handleCreateMlflowApp(ctx context.Context, body []byte) ([]byt
return nil, err
}
- return json.Marshal(map[string]any{keyGenericArn: result.Arn})
+ return json.Marshal(map[string]any{keyGenericArn: result.Arn}, jsonx.V1())
}
// describeMlflowAppResponse is the response body for DescribeMlflowApp.
@@ -319,7 +321,7 @@ type describeMlflowAppInput struct {
func (h *Handler) handleDescribeMlflowApp(ctx context.Context, body []byte) ([]byte, error) {
var req describeMlflowAppInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -345,7 +347,7 @@ func (h *Handler) handleDescribeMlflowApp(ctx context.Context, body []byte) ([]b
DefaultDomainIDList: result.DefaultDomainIDList,
CreationTime: epochSeconds(result.CreationTime),
LastModifiedTime: epochSeconds(result.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// deleteMlflowAppInput is DeleteMlflowApp's request shape
@@ -357,7 +359,7 @@ type deleteMlflowAppInput struct {
func (h *Handler) handleDeleteMlflowApp(ctx context.Context, body []byte) ([]byte, error) {
var req deleteMlflowAppInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -369,7 +371,7 @@ func (h *Handler) handleDeleteMlflowApp(ctx context.Context, body []byte) ([]byt
return nil, err
}
- return json.Marshal(map[string]any{keyGenericArn: req.Arn})
+ return json.Marshal(map[string]any{keyGenericArn: req.Arn}, jsonx.V1())
}
// updateMlflowAppInput is UpdateMlflowApp's request shape
@@ -389,7 +391,7 @@ type updateMlflowAppInput struct {
func (h *Handler) handleUpdateMlflowApp(ctx context.Context, body []byte) ([]byte, error) {
var req updateMlflowAppInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -409,7 +411,7 @@ func (h *Handler) handleUpdateMlflowApp(ctx context.Context, body []byte) ([]byt
return nil, err
}
- return json.Marshal(map[string]any{keyGenericArn: result.Arn})
+ return json.Marshal(map[string]any{keyGenericArn: result.Arn}, jsonx.V1())
}
// listMlflowAppsInput is ListMlflowApps' request shape
@@ -430,7 +432,7 @@ type listMlflowAppsInput struct {
func (h *Handler) handleListMlflowApps(ctx context.Context, body []byte) ([]byte, error) {
var req listMlflowAppsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -474,7 +476,7 @@ type createPresignedMlflowAppURLInput struct {
func (h *Handler) handleCreatePresignedMlflowAppURL(ctx context.Context, body []byte) ([]byte, error) {
var req createPresignedMlflowAppURLInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -491,7 +493,7 @@ func (h *Handler) handleCreatePresignedMlflowAppURL(ctx context.Context, body []
return nil, err
}
- return json.Marshal(map[string]string{keyAuthorizedURL: url})
+ return json.Marshal(map[string]string{keyAuthorizedURL: url}, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -514,7 +516,7 @@ type listMlflowTrackingServersInput struct {
func (h *Handler) handleListMlflowTrackingServers(ctx context.Context, body []byte) ([]byte, error) {
var req listMlflowTrackingServersInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -566,7 +568,7 @@ type updateMlflowTrackingServerInput struct {
func (h *Handler) handleUpdateMlflowTrackingServer(ctx context.Context, body []byte) ([]byte, error) {
var req updateMlflowTrackingServerInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -579,5 +581,5 @@ func (h *Handler) handleUpdateMlflowTrackingServer(ctx context.Context, body []b
return nil, err
}
- return json.Marshal(map[string]string{keyTrackingServerArn: s.TrackingServerArn})
+ return json.Marshal(map[string]string{keyTrackingServerArn: s.TrackingServerArn}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_mlflow_test.go b/services/sagemaker/handler_mlflow_test.go
index dedecc3731..0bde3ad443 100644
--- a/services/sagemaker/handler_mlflow_test.go
+++ b/services/sagemaker/handler_mlflow_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -11,6 +11,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateMlflowTrackingServer(t *testing.T) {
@@ -24,7 +26,7 @@ func TestHandler_CreateMlflowTrackingServer(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["TrackingServerArn"], "my-server")
}
@@ -38,7 +40,7 @@ func TestHandler_DescribeMlflowTrackingServer(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ts-1", resp["TrackingServerName"])
}
@@ -53,14 +55,14 @@ func TestHandler_StartStopMlflowTrackingServer(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
assert.Contains(t, startResp["TrackingServerArn"], "ts-ss")
rec = doSageMakerRequest(t, h, "StopMlflowTrackingServer", map[string]any{"TrackingServerName": "ts-ss"})
assert.Equal(t, http.StatusOK, rec.Code)
var stopResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stopResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &stopResp, jsonx.V1()))
assert.Contains(t, stopResp["TrackingServerArn"], "ts-ss")
}
@@ -120,7 +122,7 @@ func TestHandler_DeleteMlflowTrackingServer(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var deleteResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &deleteResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &deleteResp, jsonx.V1()))
assert.Contains(t, deleteResp["TrackingServerArn"], "ts-del")
rec = doSageMakerRequest(t, h, "DescribeMlflowTrackingServer", map[string]any{"TrackingServerName": "ts-del"})
@@ -313,7 +315,7 @@ func TestHandler_CreatePresignedMlflowTrackingServerUrl(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["AuthorizedUrl"], "ts-1")
}
@@ -345,7 +347,7 @@ func TestHandler_MlflowApp_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
appARN := createResp["Arn"]
require.Contains(t, appARN, "mlflow-app/app-1")
@@ -353,7 +355,7 @@ func TestHandler_MlflowApp_Lifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, describeRec.Code)
var describeResp map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp, jsonx.V1()))
assert.Equal(t, "app-1", describeResp["Name"])
assert.Equal(t, "Created", describeResp["Status"])
assert.Equal(t, "s3://bucket/path", describeResp["ArtifactStoreUri"])
@@ -365,21 +367,21 @@ func TestHandler_MlflowApp_Lifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, updateRec.Code)
describeRec = doSageMakerRequest(t, h, "DescribeMlflowApp", map[string]any{"Arn": appARN})
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp, jsonx.V1()))
assert.Equal(t, "s3://bucket/new-path", describeResp["ArtifactStoreUri"])
presignRec := doSageMakerRequest(t, h, "CreatePresignedMlflowAppUrl", map[string]any{"Arn": appARN})
assert.Equal(t, http.StatusOK, presignRec.Code)
var presignResp map[string]string
- require.NoError(t, json.Unmarshal(presignRec.Body.Bytes(), &presignResp))
+ require.NoError(t, json.Unmarshal(presignRec.Body.Bytes(), &presignResp, jsonx.V1()))
assert.Contains(t, presignResp["AuthorizedUrl"], "app-1")
listRec := doSageMakerRequest(t, h, "ListMlflowApps", map[string]any{})
assert.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
summaries, ok := listResp["Summaries"].([]any)
require.True(t, ok)
assert.Len(t, summaries, 1)
@@ -388,7 +390,7 @@ func TestHandler_MlflowApp_Lifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, deleteRec.Code)
var deleteResp map[string]string
- require.NoError(t, json.Unmarshal(deleteRec.Body.Bytes(), &deleteResp))
+ require.NoError(t, json.Unmarshal(deleteRec.Body.Bytes(), &deleteResp, jsonx.V1()))
assert.Equal(t, appARN, deleteResp["Arn"])
describeRec = doSageMakerRequest(t, h, "DescribeMlflowApp", map[string]any{"Arn": appARN})
@@ -586,7 +588,7 @@ func TestHandler_ListMlflowTrackingServers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["TrackingServerSummaries"])
// Create one
@@ -600,7 +602,7 @@ func TestHandler_ListMlflowTrackingServers(t *testing.T) {
rec = doSageMakerRequest(t, h, "ListMlflowTrackingServers", map[string]any{})
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["TrackingServerSummaries"].([]any)
assert.Len(t, summaries, 1)
@@ -631,7 +633,7 @@ func TestHandler_UpdateMlflowTrackingServer(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["TrackingServerArn"])
}
@@ -663,7 +665,7 @@ func TestHandler_UpdateMlflowTrackingServer_MlflowVersionNotOnWire(t *testing.T)
"TrackingServerName": "my-server",
})
var resp map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(
t, "2.0.0", resp["MlflowVersion"],
"MlflowVersion is not a real UpdateMlflowTrackingServerInput field and must not change",
diff --git a/services/sagemaker/handler_model_cards.go b/services/sagemaker/handler_model_cards.go
index ea0d85e3b0..10dddc50af 100644
--- a/services/sagemaker/handler_model_cards.go
+++ b/services/sagemaker/handler_model_cards.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -24,7 +26,7 @@ type createModelCardInput struct {
func (h *Handler) handleCreateModelCard(ctx context.Context, body []byte) ([]byte, error) {
var req createModelCardInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -43,7 +45,7 @@ func (h *Handler) handleCreateModelCard(ctx context.Context, body []byte) ([]byt
return nil, err
}
- return json.Marshal(map[string]any{keyModelCardArn: result.ModelCardArn})
+ return json.Marshal(map[string]any{keyModelCardArn: result.ModelCardArn}, jsonx.V1())
}
// describeModelCardInput mirrors DescribeModelCardInput
@@ -58,7 +60,7 @@ type describeModelCardInput struct {
func (h *Handler) handleDescribeModelCard(ctx context.Context, body []byte) ([]byte, error) {
var req describeModelCardInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -71,7 +73,7 @@ func (h *Handler) handleDescribeModelCard(ctx context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// updateModelCardInput mirrors UpdateModelCardInput (api_op_UpdateModelCard.go:31-59):
@@ -86,7 +88,7 @@ type updateModelCardInput struct {
func (h *Handler) handleUpdateModelCard(ctx context.Context, body []byte) ([]byte, error) {
var req updateModelCardInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -99,7 +101,7 @@ func (h *Handler) handleUpdateModelCard(ctx context.Context, body []byte) ([]byt
return nil, err
}
- return json.Marshal(map[string]any{keyModelCardArn: result.ModelCardArn})
+ return json.Marshal(map[string]any{keyModelCardArn: result.ModelCardArn}, jsonx.V1())
}
// deleteModelCardInput mirrors DeleteModelCardInput (api_op_DeleteModelCard.go:27-33):
@@ -111,7 +113,7 @@ type deleteModelCardInput struct {
func (h *Handler) handleDeleteModelCard(ctx context.Context, body []byte) error {
var req deleteModelCardInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -142,7 +144,7 @@ type listModelCardsInput struct {
func (h *Handler) handleListModelCards(ctx context.Context, body []byte) ([]byte, error) {
var req listModelCardsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -199,7 +201,7 @@ type listModelCardVersionsInput struct {
func (h *Handler) handleListModelCardVersions(ctx context.Context, body []byte) ([]byte, error) {
var req listModelCardVersionsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -224,7 +226,7 @@ func (h *Handler) handleListModelCardVersions(ctx context.Context, body []byte)
})
}
- return json.Marshal(map[string]any{"ModelCardVersionSummaryList": summaries})
+ return json.Marshal(map[string]any{"ModelCardVersionSummaryList": summaries}, jsonx.V1())
}
func modelCardVersionMatchesFilter(card *ModelCard, req listModelCardVersionsInput) bool {
@@ -262,7 +264,7 @@ type listModelCardExportJobsInput struct {
func (h *Handler) handleListModelCardExportJobs(ctx context.Context, body []byte) ([]byte, error) {
var req listModelCardExportJobsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_model_cards_test.go b/services/sagemaker/handler_model_cards_test.go
index 2c5f12287f..be1de9e354 100644
--- a/services/sagemaker/handler_model_cards_test.go
+++ b/services/sagemaker/handler_model_cards_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateModelCard(t *testing.T) {
@@ -25,7 +27,7 @@ func TestHandler_CreateModelCard(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["ModelCardArn"], "my-card")
}
@@ -51,7 +53,7 @@ func TestHandler_DescribeModelCard(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "card-1", resp["ModelCardName"])
}
@@ -70,7 +72,7 @@ func TestHandler_UpdateModelCard(t *testing.T) {
// Verify version incremented
rec = doSageMakerRequest(t, h, "DescribeModelCard", map[string]any{"ModelCardName": "card-upd"})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.InDelta(t, float64(2), resp["ModelCardVersion"], 0)
}
@@ -101,7 +103,7 @@ func TestHandler_ListModelCards(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["ModelCardSummaries"])
// Create one
@@ -113,7 +115,7 @@ func TestHandler_ListModelCards(t *testing.T) {
rec = doSageMakerRequest(t, h, "ListModelCards", map[string]any{})
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["ModelCardSummaries"].([]any)
assert.Len(t, summaries, 1)
@@ -142,7 +144,7 @@ func TestHandler_ListModelCardVersions(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions := resp["ModelCardVersionSummaryList"].([]any)
assert.Len(t, versions, 1)
@@ -178,7 +180,7 @@ func TestHandler_ListModelCardExportJobs(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
jobs := resp["ModelCardExportJobSummaries"].([]any)
assert.Empty(t, jobs)
}
diff --git a/services/sagemaker/handler_model_packages.go b/services/sagemaker/handler_model_packages.go
index ded5c4aa85..d508211da6 100644
--- a/services/sagemaker/handler_model_packages.go
+++ b/services/sagemaker/handler_model_packages.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -27,23 +30,23 @@ type createModelPackageInput struct {
ModelApprovalStatus string `json:"ModelApprovalStatus"`
ModelPackageGroupName string `json:"ModelPackageGroupName"`
ModelPackageName string `json:"ModelPackageName"`
- InferenceSpecification json.RawMessage `json:"InferenceSpecification"`
- ValidationSpecification json.RawMessage `json:"ValidationSpecification"`
- DriftCheckBaselines json.RawMessage `json:"DriftCheckBaselines"`
- ModelMetrics json.RawMessage `json:"ModelMetrics"`
- ModelCard json.RawMessage `json:"ModelCard"`
- ModelLifeCycle json.RawMessage `json:"ModelLifeCycle"`
- MetadataProperties json.RawMessage `json:"MetadataProperties"`
- SecurityConfig json.RawMessage `json:"SecurityConfig"`
- AdditionalInferenceSpecifications json.RawMessage `json:"AdditionalInferenceSpecifications"`
+ InferenceSpecification jsontext.Value `json:"InferenceSpecification"`
+ ValidationSpecification jsontext.Value `json:"ValidationSpecification"`
+ DriftCheckBaselines jsontext.Value `json:"DriftCheckBaselines"`
+ ModelMetrics jsontext.Value `json:"ModelMetrics"`
+ ModelCard jsontext.Value `json:"ModelCard"`
+ ModelLifeCycle jsontext.Value `json:"ModelLifeCycle"`
+ MetadataProperties jsontext.Value `json:"MetadataProperties"`
+ SecurityConfig jsontext.Value `json:"SecurityConfig"`
+ AdditionalInferenceSpecifications jsontext.Value `json:"AdditionalInferenceSpecifications"`
Tags []tagObject `json:"Tags"`
- SourceAlgorithmSpecification json.RawMessage `json:"SourceAlgorithmSpecification"`
+ SourceAlgorithmSpecification jsontext.Value `json:"SourceAlgorithmSpecification"`
}
func (h *Handler) handleCreateModelPackage(ctx context.Context, body []byte) ([]byte, error) {
var req createModelPackageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -92,7 +95,7 @@ func (h *Handler) handleCreateModelPackage(ctx context.Context, body []byte) ([]
return nil, err
}
- return json.Marshal(map[string]any{keyModelPackageArn: result.ModelPackageArn})
+ return json.Marshal(map[string]any{keyModelPackageArn: result.ModelPackageArn}, jsonx.V1())
}
// describeModelPackageInput is the wire request body for
@@ -106,7 +109,7 @@ type describeModelPackageInput struct {
func (h *Handler) handleDescribeModelPackage(ctx context.Context, body []byte) ([]byte, error) {
var req describeModelPackageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -119,7 +122,7 @@ func (h *Handler) handleDescribeModelPackage(ctx context.Context, body []byte) (
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// deleteModelPackageInput is the wire request body for DeleteModelPackage
@@ -131,7 +134,7 @@ type deleteModelPackageInput struct {
func (h *Handler) handleDeleteModelPackage(ctx context.Context, body []byte) error {
var req deleteModelPackageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -160,7 +163,7 @@ type listModelPackagesInput struct {
func (h *Handler) handleListModelPackages(ctx context.Context, body []byte) ([]byte, error) {
var req listModelPackagesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -205,7 +208,7 @@ func (h *Handler) handleListModelPackages(ctx context.Context, body []byte) ([]b
return json.Marshal(map[string]any{
"ModelPackageSummaryList": summaries,
keyNextToken: next,
- })
+ }, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -225,7 +228,7 @@ type createModelPackageGroupInput struct {
func (h *Handler) handleCreateModelPackageGroup(ctx context.Context, body []byte) ([]byte, error) {
var req createModelPackageGroupInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -247,7 +250,7 @@ func (h *Handler) handleCreateModelPackageGroup(ctx context.Context, body []byte
return nil, err
}
- return json.Marshal(map[string]any{keyModelPackageGroupArn: result.ModelPackageGroupArn})
+ return json.Marshal(map[string]any{keyModelPackageGroupArn: result.ModelPackageGroupArn}, jsonx.V1())
}
// describeModelPackageGroupInput is the wire request body for
@@ -260,7 +263,7 @@ type describeModelPackageGroupInput struct {
func (h *Handler) handleDescribeModelPackageGroup(ctx context.Context, body []byte) ([]byte, error) {
var req describeModelPackageGroupInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -273,7 +276,7 @@ func (h *Handler) handleDescribeModelPackageGroup(ctx context.Context, body []by
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// deleteModelPackageGroupInput is the wire request body for
@@ -286,7 +289,7 @@ type deleteModelPackageGroupInput struct {
func (h *Handler) handleDeleteModelPackageGroup(ctx context.Context, body []byte) error {
var req deleteModelPackageGroupInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -314,7 +317,7 @@ type listModelPackageGroupsInput struct {
func (h *Handler) handleListModelPackageGroups(ctx context.Context, body []byte) ([]byte, error) {
var req listModelPackageGroupsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -353,7 +356,7 @@ func (h *Handler) handleListModelPackageGroups(ctx context.Context, body []byte)
return json.Marshal(map[string]any{
"ModelPackageGroupSummaryList": summaries,
keyNextToken: next,
- })
+ }, jsonx.V1())
}
// getModelPackageGroupPolicyInput is the wire request body for
@@ -366,7 +369,7 @@ type getModelPackageGroupPolicyInput struct {
func (h *Handler) handleGetModelPackageGroupPolicy(ctx context.Context, body []byte) ([]byte, error) {
var req getModelPackageGroupPolicyInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -379,7 +382,7 @@ func (h *Handler) handleGetModelPackageGroupPolicy(ctx context.Context, body []b
return nil, err
}
- return json.Marshal(map[string]string{"ResourcePolicy": policy})
+ return json.Marshal(map[string]string{"ResourcePolicy": policy}, jsonx.V1())
}
// putModelPackageGroupPolicyInput is the wire request body for
@@ -393,7 +396,7 @@ type putModelPackageGroupPolicyInput struct {
func (h *Handler) handlePutModelPackageGroupPolicy(ctx context.Context, body []byte) ([]byte, error) {
var req putModelPackageGroupPolicyInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -410,7 +413,7 @@ func (h *Handler) handlePutModelPackageGroupPolicy(ctx context.Context, body []b
return nil, err
}
- return json.Marshal(map[string]string{keyModelPackageGroupArn: g.ModelPackageGroupArn})
+ return json.Marshal(map[string]string{keyModelPackageGroupArn: g.ModelPackageGroupArn}, jsonx.V1())
}
// deleteModelPackageGroupPolicyInput is the wire request body for
@@ -423,7 +426,7 @@ type deleteModelPackageGroupPolicyInput struct {
func (h *Handler) handleDeleteModelPackageGroupPolicy(ctx context.Context, body []byte) error {
var req deleteModelPackageGroupPolicyInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -452,16 +455,16 @@ type updateModelPackageInput struct {
SourceURI string `json:"SourceUri"`
CustomerMetadataProperties map[string]string `json:"CustomerMetadataProperties"`
CustomerMetadataPropertiesToRemove []string `json:"CustomerMetadataPropertiesToRemove"`
- InferenceSpecification json.RawMessage `json:"InferenceSpecification"`
- ModelCard json.RawMessage `json:"ModelCard"`
- ModelLifeCycle json.RawMessage `json:"ModelLifeCycle"`
- AdditionalInferenceSpecificationsToAdd json.RawMessage `json:"AdditionalInferenceSpecificationsToAdd"`
+ InferenceSpecification jsontext.Value `json:"InferenceSpecification"`
+ ModelCard jsontext.Value `json:"ModelCard"`
+ ModelLifeCycle jsontext.Value `json:"ModelLifeCycle"`
+ AdditionalInferenceSpecificationsToAdd jsontext.Value `json:"AdditionalInferenceSpecificationsToAdd"`
}
func (h *Handler) handleUpdateModelPackage(ctx context.Context, body []byte) ([]byte, error) {
var req updateModelPackageInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -489,7 +492,7 @@ func (h *Handler) handleUpdateModelPackage(ctx context.Context, body []byte) ([]
return nil, err
}
- return json.Marshal(map[string]string{keyModelPackageArn: mp.ModelPackageArn})
+ return json.Marshal(map[string]string{keyModelPackageArn: mp.ModelPackageArn}, jsonx.V1())
}
// batchDescribeModelPackageRequest is the request body for BatchDescribeModelPackage.
@@ -519,7 +522,7 @@ type batchDescribeModelPackageError struct {
func (h *Handler) handleBatchDescribeModelPackage(ctx context.Context, body []byte) ([]byte, error) {
var req batchDescribeModelPackageRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -551,5 +554,5 @@ func (h *Handler) handleBatchDescribeModelPackage(ctx context.Context, body []by
return json.Marshal(map[string]any{
"ModelPackageSummaries": modelPackageMap,
"BatchDescribeModelPackageErrorMap": errorsMap,
- })
+ }, jsonx.V1())
}
diff --git a/services/sagemaker/handler_model_packages_test.go b/services/sagemaker/handler_model_packages_test.go
index 649850fbae..bfea04fb51 100644
--- a/services/sagemaker/handler_model_packages_test.go
+++ b/services/sagemaker/handler_model_packages_test.go
@@ -2,7 +2,7 @@ package sagemaker_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -27,7 +28,7 @@ func TestHandler_CreateModelPackage(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["ModelPackageArn"], "my-pkg")
}
@@ -42,7 +43,7 @@ func TestHandler_DescribeModelPackage(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "pkg-1", resp["ModelPackageName"])
}
@@ -71,7 +72,7 @@ func TestHandler_ListModelPackages(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["ModelPackageSummaryList"].([]any)
assert.Len(t, items, 2)
}
@@ -91,7 +92,7 @@ func TestHandler_CreateModelPackageGroup(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["ModelPackageGroupArn"], "my-group")
}
@@ -106,7 +107,7 @@ func TestHandler_DescribeModelPackageGroup(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "grp-1", resp["ModelPackageGroupName"])
assert.Equal(t, "Completed", resp["ModelPackageGroupStatus"])
}
@@ -136,7 +137,7 @@ func TestHandler_ListModelPackageGroups(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["ModelPackageGroupSummaryList"].([]any)
assert.Len(t, items, 2)
}
@@ -294,7 +295,7 @@ func TestAddTags_ModelPackage(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
tags := resp["Tags"].([]any)
require.Len(t, tags, 1)
@@ -382,7 +383,7 @@ func TestHandler_ModelPackageGroupPolicy_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recPut.Code)
var putOut map[string]any
- require.NoError(t, json.Unmarshal(recPut.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(recPut.Body.Bytes(), &putOut, jsonx.V1()))
assert.NotEmpty(t, putOut["ModelPackageGroupArn"])
recGet2 := doSageMakerRequest(t, h, "GetModelPackageGroupPolicy", map[string]any{
@@ -391,7 +392,7 @@ func TestHandler_ModelPackageGroupPolicy_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recGet2.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(recGet2.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(recGet2.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, policy, getOut["ResourcePolicy"])
recDelete := doSageMakerRequest(t, h, "DeleteModelPackageGroupPolicy", map[string]any{
@@ -479,7 +480,7 @@ func TestHandler_UpdateModelPackage(t *testing.T) {
})
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
pkgArn := createResp["ModelPackageArn"]
require.NotEmpty(t, pkgArn)
@@ -490,7 +491,7 @@ func TestHandler_UpdateModelPackage(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, pkgArn, resp["ModelPackageArn"])
// Describe and verify approval status
@@ -498,7 +499,7 @@ func TestHandler_UpdateModelPackage(t *testing.T) {
"ModelPackageName": "my-pkg",
})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Approved", descResp["ModelApprovalStatus"])
}
@@ -593,7 +594,7 @@ func TestHandler_BatchDescribeModelPackage(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "ModelPackageSummaries")
assert.Contains(t, resp, "BatchDescribeModelPackageErrorMap")
}
@@ -624,7 +625,7 @@ func TestHandler_BatchDescribeModelPackage_ExistingAndMissing(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["ModelPackageSummaries"].(map[string]any)
require.True(t, ok)
diff --git a/services/sagemaker/handler_modelcard_export.go b/services/sagemaker/handler_modelcard_export.go
index cf31e66fa7..0279690320 100644
--- a/services/sagemaker/handler_modelcard_export.go
+++ b/services/sagemaker/handler_modelcard_export.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// modelCardExportOpsSupported returns the real stateful operations
@@ -53,7 +55,7 @@ type createModelCardExportJobInput struct {
func (h *Handler) handleCreateModelCardExportJob(ctx context.Context, body []byte) ([]byte, error) {
var req createModelCardExportJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -68,7 +70,7 @@ func (h *Handler) handleCreateModelCardExportJob(ctx context.Context, body []byt
return nil, err
}
- return json.Marshal(map[string]any{keyModelCardExportJobArn: job.ModelCardExportJobArn})
+ return json.Marshal(map[string]any{keyModelCardExportJobArn: job.ModelCardExportJobArn}, jsonx.V1())
}
// describeModelCardExportJobInput mirrors DescribeModelCardExportJobInput
@@ -81,7 +83,7 @@ type describeModelCardExportJobInput struct {
func (h *Handler) handleDescribeModelCardExportJob(ctx context.Context, body []byte) ([]byte, error) {
var req describeModelCardExportJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -104,5 +106,5 @@ func (h *Handler) handleDescribeModelCardExportJob(ctx context.Context, body []b
keyStatus: job.Status,
keyOutputConfig: map[string]any{"S3OutputPath": job.S3OutputPath},
"ExportArtifacts": map[string]any{"S3ExportArtifacts": job.S3ExportArtifacts},
- })
+ }, jsonx.V1())
}
diff --git a/services/sagemaker/handler_modelcard_export_test.go b/services/sagemaker/handler_modelcard_export_test.go
index 30857b0415..47f462f683 100644
--- a/services/sagemaker/handler_modelcard_export_test.go
+++ b/services/sagemaker/handler_modelcard_export_test.go
@@ -1,12 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_ModelCardExportJob_Lifecycle(t *testing.T) {
@@ -27,7 +29,7 @@ func TestHandler_ModelCardExportJob_Lifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
jobArn, _ := createResp["ModelCardExportJobArn"].(string)
require.NotEmpty(t, jobArn)
@@ -37,7 +39,7 @@ func TestHandler_ModelCardExportJob_Lifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, describeRec.Code)
var describeResp map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp, jsonx.V1()))
assert.Equal(t, "Completed", describeResp["Status"])
assert.Equal(t, "export-1", describeResp["ModelCardExportJobName"])
assert.Equal(t, "my-card", describeResp["ModelCardName"])
@@ -48,7 +50,7 @@ func TestHandler_ModelCardExportJob_Lifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
summaries, ok := listResp["ModelCardExportJobSummaries"].([]any)
require.True(t, ok)
assert.Len(t, summaries, 1)
diff --git a/services/sagemaker/handler_modelmonitor_test.go b/services/sagemaker/handler_modelmonitor_test.go
index 3574c9322d..c7dc988e4f 100644
--- a/services/sagemaker/handler_modelmonitor_test.go
+++ b/services/sagemaker/handler_modelmonitor_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -46,7 +47,7 @@ func TestHandler_ListDataQualityJobDefinitions(t *testing.T) {
var resp struct {
JobDefinitionSummaries []map[string]any `json:"JobDefinitionSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.JobDefinitionSummaries, 3)
}
@@ -67,7 +68,7 @@ func TestHandler_ListDataQualityJobDefinitions_FilterByEndpointName(t *testing.T
var resp struct {
JobDefinitionSummaries []map[string]any `json:"JobDefinitionSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.JobDefinitionSummaries, 2)
for _, s := range resp.JobDefinitionSummaries {
@@ -94,7 +95,7 @@ func TestHandler_ListDataQualityJobDefinitions_NameContainsAndSort(t *testing.T)
var resp struct {
JobDefinitionSummaries []map[string]any `json:"JobDefinitionSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.JobDefinitionSummaries, 2)
assert.Equal(t, "alpha-job", resp.JobDefinitionSummaries[0]["MonitoringJobDefinitionName"])
assert.Equal(t, "alpha-other", resp.JobDefinitionSummaries[1]["MonitoringJobDefinitionName"])
@@ -120,7 +121,7 @@ func TestHandler_ListDataQualityJobDefinitions_Pagination(t *testing.T) {
NextToken string `json:"NextToken"`
JobDefinitionSummaries []map[string]any `json:"JobDefinitionSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page1, jsonx.V1()))
require.Len(t, page1.JobDefinitionSummaries, 2)
require.NotEmpty(t, page1.NextToken)
@@ -134,7 +135,7 @@ func TestHandler_ListDataQualityJobDefinitions_Pagination(t *testing.T) {
var page2 struct {
JobDefinitionSummaries []map[string]any `json:"JobDefinitionSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &page2, jsonx.V1()))
assert.Len(t, page2.JobDefinitionSummaries, 1)
}
@@ -149,7 +150,7 @@ func TestHandler_ListModelBiasJobDefinitions_Empty(t *testing.T) {
var resp struct {
JobDefinitionSummaries []map[string]any `json:"JobDefinitionSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp.JobDefinitionSummaries)
}
@@ -168,7 +169,7 @@ func TestHandler_ListModelQualityJobDefinitions_ReturnsCreated(t *testing.T) {
var resp struct {
JobDefinitionSummaries []map[string]any `json:"JobDefinitionSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.JobDefinitionSummaries, 1)
assert.Equal(t, "mq-list-1", resp.JobDefinitionSummaries[0]["MonitoringJobDefinitionName"])
}
@@ -188,7 +189,7 @@ func TestHandler_ListModelExplainabilityJobDefinitions_ReturnsCreated(t *testing
var resp struct {
JobDefinitionSummaries []map[string]any `json:"JobDefinitionSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.JobDefinitionSummaries, 1)
assert.Equal(t, "me-list-1", resp.JobDefinitionSummaries[0]["MonitoringJobDefinitionName"])
}
@@ -230,7 +231,7 @@ func TestHandler_UpdateMonitoringAlert_CreatesThenUpdates(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["MonitoringScheduleArn"], "mm-sched")
assert.Equal(t, "alert-1", resp["MonitoringAlertName"])
@@ -251,7 +252,7 @@ func TestHandler_UpdateMonitoringAlert_CreatesThenUpdates(t *testing.T) {
var listResp struct {
MonitoringAlertSummaries []map[string]any `json:"MonitoringAlertSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.MonitoringAlertSummaries, 1)
assert.InEpsilon(t, float64(5), listResp.MonitoringAlertSummaries[0]["DatapointsToAlert"], 0)
assert.InEpsilon(t, float64(6), listResp.MonitoringAlertSummaries[0]["EvaluationPeriod"], 0)
@@ -335,7 +336,7 @@ func TestHandler_ListMonitoringAlerts_MaxResults(t *testing.T) {
NextToken string `json:"NextToken"`
MonitoringAlertSummaries []map[string]any `json:"MonitoringAlertSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.MonitoringAlertSummaries, 2)
assert.NotEmpty(t, resp.NextToken)
}
@@ -369,7 +370,7 @@ func TestHandler_ListMonitoringAlerts_EmptyForFreshSchedule(t *testing.T) {
var resp struct {
MonitoringAlertSummaries []map[string]any `json:"MonitoringAlertSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp.MonitoringAlertSummaries)
}
@@ -403,7 +404,7 @@ func TestHandler_ListMonitoringAlertHistory_FiltersByScheduleAndStatus(t *testin
var resp struct {
MonitoringAlertHistory []map[string]any `json:"MonitoringAlertHistory"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.MonitoringAlertHistory, 1)
assert.Equal(t, "sched-1", resp.MonitoringAlertHistory[0]["MonitoringScheduleName"])
assert.Equal(t, "InAlert", resp.MonitoringAlertHistory[0]["AlertStatus"])
@@ -446,7 +447,7 @@ func TestHandler_ListMonitoringAlertHistory_SortByStatus(t *testing.T) {
var defaultResp struct {
MonitoringAlertHistory []map[string]any `json:"MonitoringAlertHistory"`
}
- require.NoError(t, json.Unmarshal(defaultRec.Body.Bytes(), &defaultResp))
+ require.NoError(t, json.Unmarshal(defaultRec.Body.Bytes(), &defaultResp, jsonx.V1()))
require.Len(t, defaultResp.MonitoringAlertHistory, 2)
assert.Equal(t, "InAlert", defaultResp.MonitoringAlertHistory[0]["AlertStatus"])
@@ -462,7 +463,7 @@ func TestHandler_ListMonitoringAlertHistory_SortByStatus(t *testing.T) {
var statusResp struct {
MonitoringAlertHistory []map[string]any `json:"MonitoringAlertHistory"`
}
- require.NoError(t, json.Unmarshal(statusRec.Body.Bytes(), &statusResp))
+ require.NoError(t, json.Unmarshal(statusRec.Body.Bytes(), &statusResp, jsonx.V1()))
require.Len(t, statusResp.MonitoringAlertHistory, 2)
assert.Equal(t, "OK", statusResp.MonitoringAlertHistory[0]["AlertStatus"], "SortBy=Status must be honored")
}
@@ -501,7 +502,7 @@ func TestHandler_ListMonitoringExecutions_FiltersByScheduleAndStatus(t *testing.
var resp struct {
MonitoringExecutionSummaries []map[string]any `json:"MonitoringExecutionSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.MonitoringExecutionSummaries, 1)
assert.Equal(t, "sched-a", resp.MonitoringExecutionSummaries[0]["MonitoringScheduleName"])
assert.Equal(t, "Completed", resp.MonitoringExecutionSummaries[0]["MonitoringExecutionStatus"])
@@ -518,6 +519,6 @@ func TestHandler_ListMonitoringExecutions_EmptyWhenNoneSeeded(t *testing.T) {
var resp struct {
MonitoringExecutionSummaries []map[string]any `json:"MonitoringExecutionSummaries"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp.MonitoringExecutionSummaries)
}
diff --git a/services/sagemaker/handler_models.go b/services/sagemaker/handler_models.go
index 688b77a9be..9ad05cc9be 100644
--- a/services/sagemaker/handler_models.go
+++ b/services/sagemaker/handler_models.go
@@ -2,9 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -42,7 +43,7 @@ type describeModelResponse struct {
func (h *Handler) handleCreateModel(ctx context.Context, body []byte) ([]byte, error) {
var req createModelRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -96,7 +97,7 @@ func (h *Handler) handleCreateModel(ctx context.Context, body []byte) ([]byte, e
log := logger.Load(ctx)
log.InfoContext(ctx, "sagemaker: created model", "name", m.ModelName)
- return json.Marshal(map[string]string{"ModelArn": m.ModelARN})
+ return json.Marshal(map[string]string{"ModelArn": m.ModelARN}, jsonx.V1())
}
// describeModelRequest is the request body for DescribeModel.
@@ -107,7 +108,7 @@ type describeModelRequest struct {
func (h *Handler) handleDescribeModel(ctx context.Context, body []byte) ([]byte, error) {
var req describeModelRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -136,13 +137,17 @@ func (h *Handler) handleDescribeModel(ctx context.Context, body []byte) ([]byte,
resp.Containers = nil
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
-func (h *Handler) handleListModels(ctx context.Context, body []byte) ([]byte, error) {
+//nolint:dupl // structurally similar handlers
+func (h *Handler) handleListModels(
+ ctx context.Context,
+ body []byte,
+) ([]byte, error) {
var req nameTimeListRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -168,7 +173,7 @@ func (h *Handler) handleListModels(ctx context.Context, body []byte) ([]byte, er
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// deleteModelRequest is the request body for DeleteModel.
@@ -179,7 +184,7 @@ type deleteModelRequest struct {
func (h *Handler) handleDeleteModel(ctx context.Context, body []byte) error {
var req deleteModelRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_models_test.go b/services/sagemaker/handler_models_test.go
index 063a02233a..460e378bf8 100644
--- a/services/sagemaker/handler_models_test.go
+++ b/services/sagemaker/handler_models_test.go
@@ -3,7 +3,7 @@ package sagemaker_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -62,7 +63,7 @@ func TestHandler_CreateModel(t *testing.T) {
if tt.body != nil {
var err error
- bodyBytes, err = json.Marshal(tt.body)
+ bodyBytes, err = json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("not-json")
@@ -82,7 +83,7 @@ func TestHandler_CreateModel(t *testing.T) {
if tt.wantARN {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["ModelArn"], "arn:aws:sagemaker")
assert.Contains(t, resp["ModelArn"], "model/my-model")
}
@@ -140,7 +141,7 @@ func TestHandler_DescribeModel(t *testing.T) {
if tt.wantName != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantName, resp["ModelName"])
}
})
@@ -176,7 +177,7 @@ func TestHandler_ListModels(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
models, ok := resp["Models"].([]any)
require.True(t, ok)
@@ -277,7 +278,7 @@ func TestHandler_ListModelsPagination(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
models, modelsOK := resp["Models"].([]any)
require.True(t, modelsOK)
@@ -298,7 +299,7 @@ func TestHandler_ListModelsPagination(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
models2, models2OK := resp2["Models"].([]any)
require.True(t, models2OK)
@@ -432,7 +433,7 @@ func TestHandler_ListModels_FilterSort(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
list, ok := resp["Models"].([]any)
require.True(t, ok)
@@ -497,7 +498,7 @@ func TestHandler_ListModels_CreationTimeAfterInclusive(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
models, ok := resp["Models"].([]any)
require.True(t, ok)
require.Len(t, models, 1)
diff --git a/services/sagemaker/handler_monitoring.go b/services/sagemaker/handler_monitoring.go
index 79d4201acb..f6096905bc 100644
--- a/services/sagemaker/handler_monitoring.go
+++ b/services/sagemaker/handler_monitoring.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -21,7 +23,7 @@ type updateMonitoringAlertRequest struct {
func (h *Handler) handleUpdateMonitoringAlert(ctx context.Context, body []byte) ([]byte, error) {
var req updateMonitoringAlertRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -51,7 +53,7 @@ func (h *Handler) handleUpdateMonitoringAlert(ctx context.Context, body []byte)
return json.Marshal(map[string]any{
keyMonitoringScheduleArn: scheduleArn,
keyMonitoringAlertName: alert.MonitoringAlertName,
- })
+ }, jsonx.V1())
}
// listMonitoringAlertsRequest is the request body for ListMonitoringAlerts.
@@ -64,7 +66,7 @@ type listMonitoringAlertsRequest struct {
func (h *Handler) handleListMonitoringAlerts(ctx context.Context, body []byte) ([]byte, error) {
var req listMonitoringAlertsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -97,7 +99,7 @@ func (h *Handler) handleListMonitoringAlerts(ctx context.Context, body []byte) (
resp[keyNextToken] = next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// listMonitoringAlertHistoryRequest is the request body for ListMonitoringAlertHistory.
@@ -116,7 +118,7 @@ type listMonitoringAlertHistoryRequest struct {
func (h *Handler) handleListMonitoringAlertHistory(ctx context.Context, body []byte) ([]byte, error) {
var req listMonitoringAlertHistoryRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -148,7 +150,7 @@ func (h *Handler) handleListMonitoringAlertHistory(ctx context.Context, body []b
resp[keyNextToken] = next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// listMonitoringExecutionsRequest is the parsed representation of a
@@ -192,13 +194,13 @@ func (r listMonitoringExecutionsRequest) toFilter() MonitoringExecutionFilter {
func (h *Handler) handleListMonitoringExecutions(ctx context.Context, body []byte) ([]byte, error) {
var req listMonitoringExecutionsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
items, next := h.Backend.ListMonitoringExecutions(ctx, req.NextToken, req.toFilter())
- return json.Marshal(buildMonitoringExecutionListResponse(items, next))
+ return json.Marshal(buildMonitoringExecutionListResponse(items, next), jsonx.V1())
}
func buildMonitoringExecutionListResponse(items []*MonitoringExecution, next string) map[string]any {
diff --git a/services/sagemaker/handler_monitoring_job_definitions.go b/services/sagemaker/handler_monitoring_job_definitions.go
index d655702a7d..0ea8372743 100644
--- a/services/sagemaker/handler_monitoring_job_definitions.go
+++ b/services/sagemaker/handler_monitoring_job_definitions.go
@@ -2,10 +2,13 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -27,7 +30,7 @@ import (
// JobInput/JobOutputConfig blocks plus the shared JobResources/NetworkConfig/
// StoppingCondition/BaselineConfig) is kept verbatim in Config.
type jobDefRequest struct {
- Config map[string]json.RawMessage
+ Config map[string]jsontext.Value
Tags map[string]string
JobDefinitionName string
RoleArn string
@@ -38,26 +41,26 @@ type jobDefRequest struct {
// wire field name of the type's JobInput block (e.g. "DataQualityJobInput"),
// used to derive EndpointName for List filtering/summaries.
func parseJobDefRequest(body []byte, jobInputKey string) (jobDefRequest, error) {
- var raw map[string]json.RawMessage
- if err := json.Unmarshal(body, &raw); err != nil {
+ var raw map[string]jsontext.Value
+ if err := json.Unmarshal(body, &raw, jsonx.V1()); err != nil {
return jobDefRequest{}, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
- req := jobDefRequest{Config: make(map[string]json.RawMessage, len(raw))}
+ req := jobDefRequest{Config: make(map[string]jsontext.Value, len(raw))}
for k, v := range raw {
switch k {
case keyJobDefinitionName:
- if err := json.Unmarshal(v, &req.JobDefinitionName); err != nil {
+ if err := json.Unmarshal(v, &req.JobDefinitionName, jsonx.V1()); err != nil {
return jobDefRequest{}, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
case "RoleArn":
- if err := json.Unmarshal(v, &req.RoleArn); err != nil {
+ if err := json.Unmarshal(v, &req.RoleArn, jsonx.V1()); err != nil {
return jobDefRequest{}, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
case keyTagsField:
var tags []tagObject
- if err := json.Unmarshal(v, &tags); err != nil {
+ if err := json.Unmarshal(v, &tags, jsonx.V1()); err != nil {
return jobDefRequest{}, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -83,7 +86,7 @@ func parseJobDefRequest(body []byte, jobInputKey string) (jobDefRequest, error)
// AppSpecification/JobInput/JobOutputConfig, whose wire names share the
// type's name prefix derived from jobInputKey (e.g. "DataQualityJobInput" ->
// "DataQuality"). Returns the decoded JobInput block on success.
-func validateJobDefRequest(req jobDefRequest, jobInputKey string) (json.RawMessage, error) {
+func validateJobDefRequest(req jobDefRequest, jobInputKey string) (jsontext.Value, error) {
if req.JobDefinitionName == "" {
return nil, fmt.Errorf("%w: JobDefinitionName is required", errInvalidRequest)
}
@@ -117,14 +120,14 @@ func validateJobDefRequest(req jobDefRequest, jobInputKey string) (json.RawMessa
// extractEndpointName pulls EndpointInput.EndpointName out of a job input
// block (DataQualityJobInput / ModelBiasJobInput / ModelQualityJobInput /
// ModelExplainabilityJobInput all share this shape).
-func extractEndpointName(jobInput json.RawMessage) string {
+func extractEndpointName(jobInput jsontext.Value) string {
var in struct {
EndpointInput *struct {
EndpointName string `json:"EndpointName"`
} `json:"EndpointInput"`
}
- if err := json.Unmarshal(jobInput, &in); err != nil || in.EndpointInput == nil {
+ if err := json.Unmarshal(jobInput, &in, jsonx.V1()); err != nil || in.EndpointInput == nil {
return ""
}
@@ -144,7 +147,7 @@ type jobDefinitionNameInput struct {
func parseJobDefinitionName(body []byte) (string, error) {
var req jobDefinitionNameInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return "", fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -201,7 +204,7 @@ type jobDefinitionListInput struct {
func parseJobDefinitionListRequest(body []byte) (jobDefListRequest, error) {
var req jobDefinitionListInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return jobDefListRequest{}, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -268,7 +271,7 @@ func (h *Handler) handleCreateDataQualityJobDefinition(ctx context.Context, body
return nil, err
}
- return json.Marshal(map[string]any{keyJobDefinitionArn: result.JobDefinitionArn})
+ return json.Marshal(map[string]any{keyJobDefinitionArn: result.JobDefinitionArn}, jsonx.V1())
}
func (h *Handler) handleDescribeDataQualityJobDefinition(ctx context.Context, body []byte) ([]byte, error) {
@@ -282,7 +285,7 @@ func (h *Handler) handleDescribeDataQualityJobDefinition(ctx context.Context, bo
return nil, err
}
- return json.Marshal(buildJobDefinitionResponse(result))
+ return json.Marshal(buildJobDefinitionResponse(result), jsonx.V1())
}
func (h *Handler) handleDeleteDataQualityJobDefinition(ctx context.Context, body []byte) error {
@@ -302,7 +305,7 @@ func (h *Handler) handleListDataQualityJobDefinitions(ctx context.Context, body
items, next := h.Backend.ListDataQualityJobDefinitions(ctx, req.NextToken, req.Filter)
- return json.Marshal(buildJobDefinitionListResponse(items, next))
+ return json.Marshal(buildJobDefinitionListResponse(items, next), jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -322,7 +325,7 @@ func (h *Handler) handleCreateModelBiasJobDefinition(ctx context.Context, body [
return nil, err
}
- return json.Marshal(map[string]any{keyJobDefinitionArn: result.JobDefinitionArn})
+ return json.Marshal(map[string]any{keyJobDefinitionArn: result.JobDefinitionArn}, jsonx.V1())
}
func (h *Handler) handleDescribeModelBiasJobDefinition(ctx context.Context, body []byte) ([]byte, error) {
@@ -336,7 +339,7 @@ func (h *Handler) handleDescribeModelBiasJobDefinition(ctx context.Context, body
return nil, err
}
- return json.Marshal(buildJobDefinitionResponse(result))
+ return json.Marshal(buildJobDefinitionResponse(result), jsonx.V1())
}
func (h *Handler) handleDeleteModelBiasJobDefinition(ctx context.Context, body []byte) error {
@@ -356,7 +359,7 @@ func (h *Handler) handleListModelBiasJobDefinitions(ctx context.Context, body []
items, next := h.Backend.ListModelBiasJobDefinitions(ctx, req.NextToken, req.Filter)
- return json.Marshal(buildJobDefinitionListResponse(items, next))
+ return json.Marshal(buildJobDefinitionListResponse(items, next), jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -376,7 +379,7 @@ func (h *Handler) handleCreateModelQualityJobDefinition(ctx context.Context, bod
return nil, err
}
- return json.Marshal(map[string]any{keyJobDefinitionArn: result.JobDefinitionArn})
+ return json.Marshal(map[string]any{keyJobDefinitionArn: result.JobDefinitionArn}, jsonx.V1())
}
func (h *Handler) handleDescribeModelQualityJobDefinition(ctx context.Context, body []byte) ([]byte, error) {
@@ -390,7 +393,7 @@ func (h *Handler) handleDescribeModelQualityJobDefinition(ctx context.Context, b
return nil, err
}
- return json.Marshal(buildJobDefinitionResponse(result))
+ return json.Marshal(buildJobDefinitionResponse(result), jsonx.V1())
}
func (h *Handler) handleDeleteModelQualityJobDefinition(ctx context.Context, body []byte) error {
@@ -410,7 +413,7 @@ func (h *Handler) handleListModelQualityJobDefinitions(ctx context.Context, body
items, next := h.Backend.ListModelQualityJobDefinitions(ctx, req.NextToken, req.Filter)
- return json.Marshal(buildJobDefinitionListResponse(items, next))
+ return json.Marshal(buildJobDefinitionListResponse(items, next), jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -430,7 +433,7 @@ func (h *Handler) handleCreateModelExplainabilityJobDefinition(ctx context.Conte
return nil, err
}
- return json.Marshal(map[string]any{keyJobDefinitionArn: result.JobDefinitionArn})
+ return json.Marshal(map[string]any{keyJobDefinitionArn: result.JobDefinitionArn}, jsonx.V1())
}
func (h *Handler) handleDescribeModelExplainabilityJobDefinition(ctx context.Context, body []byte) ([]byte, error) {
@@ -444,7 +447,7 @@ func (h *Handler) handleDescribeModelExplainabilityJobDefinition(ctx context.Con
return nil, err
}
- return json.Marshal(buildJobDefinitionResponse(result))
+ return json.Marshal(buildJobDefinitionResponse(result), jsonx.V1())
}
func (h *Handler) handleDeleteModelExplainabilityJobDefinition(ctx context.Context, body []byte) error {
@@ -464,5 +467,5 @@ func (h *Handler) handleListModelExplainabilityJobDefinitions(ctx context.Contex
items, next := h.Backend.ListModelExplainabilityJobDefinitions(ctx, req.NextToken, req.Filter)
- return json.Marshal(buildJobDefinitionListResponse(items, next))
+ return json.Marshal(buildJobDefinitionListResponse(items, next), jsonx.V1())
}
diff --git a/services/sagemaker/handler_monitoring_job_definitions_test.go b/services/sagemaker/handler_monitoring_job_definitions_test.go
index dadb14c3d1..b1b559ed71 100644
--- a/services/sagemaker/handler_monitoring_job_definitions_test.go
+++ b/services/sagemaker/handler_monitoring_job_definitions_test.go
@@ -1,12 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// minimalJobDefinitionFixture builds a structurally valid Create*JobDefinition
@@ -41,7 +43,7 @@ func TestHandler_CreateDataQualityJobDefinition(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["JobDefinitionArn"], "dq-def-1")
}
@@ -59,7 +61,7 @@ func TestHandler_DescribeDataQualityJobDefinition(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "dq-def-2", resp["JobDefinitionName"])
assert.Equal(t, "arn:aws:iam::000000000000:role/monitor", resp["RoleArn"])
assert.NotNil(t, resp["DataQualityAppSpecification"])
@@ -99,7 +101,7 @@ func TestHandler_CreateDataQualityJobDefinition_DuplicateReturnsResourceInUse(t
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceInUse", resp["__type"])
}
@@ -117,7 +119,7 @@ func TestHandler_CreateModelBiasJobDefinition(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["JobDefinitionArn"], "mb-def-1")
}
@@ -153,7 +155,7 @@ func TestHandler_CreateModelQualityJobDefinition(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["JobDefinitionArn"], "mq-def-1")
}
@@ -184,7 +186,7 @@ func TestHandler_CreateModelExplainabilityJobDefinition(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["JobDefinitionArn"], "me-def-1")
}
diff --git a/services/sagemaker/handler_monitoring_schedules.go b/services/sagemaker/handler_monitoring_schedules.go
index 90b5f2f887..a15144978f 100644
--- a/services/sagemaker/handler_monitoring_schedules.go
+++ b/services/sagemaker/handler_monitoring_schedules.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -22,7 +24,7 @@ type createMonitoringScheduleInput struct {
func (h *Handler) handleCreateMonitoringSchedule(ctx context.Context, body []byte) ([]byte, error) {
var req createMonitoringScheduleInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -37,7 +39,7 @@ func (h *Handler) handleCreateMonitoringSchedule(ctx context.Context, body []byt
return nil, err
}
- return json.Marshal(map[string]any{keyMonitoringScheduleArn: result.MonitoringScheduleArn})
+ return json.Marshal(map[string]any{keyMonitoringScheduleArn: result.MonitoringScheduleArn}, jsonx.V1())
}
// describeMonitoringScheduleInput mirrors DescribeMonitoringScheduleInput
@@ -49,7 +51,7 @@ type describeMonitoringScheduleInput struct {
func (h *Handler) handleDescribeMonitoringSchedule(ctx context.Context, body []byte) ([]byte, error) {
var req describeMonitoringScheduleInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -62,7 +64,7 @@ func (h *Handler) handleDescribeMonitoringSchedule(ctx context.Context, body []b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// deleteMonitoringScheduleInput mirrors DeleteMonitoringScheduleInput
@@ -74,7 +76,7 @@ type deleteMonitoringScheduleInput struct {
func (h *Handler) handleDeleteMonitoringSchedule(ctx context.Context, body []byte) error {
var req deleteMonitoringScheduleInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -94,7 +96,7 @@ type stopMonitoringScheduleInput struct {
func (h *Handler) handleStopMonitoringSchedule(ctx context.Context, body []byte) error {
var req stopMonitoringScheduleInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -114,7 +116,7 @@ type startMonitoringScheduleInput struct {
func (h *Handler) handleStartMonitoringSchedule(ctx context.Context, body []byte) error {
var req startMonitoringScheduleInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -135,7 +137,7 @@ type updateMonitoringScheduleInput struct {
func (h *Handler) handleUpdateMonitoringSchedule(ctx context.Context, body []byte) ([]byte, error) {
var req updateMonitoringScheduleInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -148,7 +150,7 @@ func (h *Handler) handleUpdateMonitoringSchedule(ctx context.Context, body []byt
return nil, err
}
- return json.Marshal(map[string]any{keyMonitoringScheduleArn: result.MonitoringScheduleArn})
+ return json.Marshal(map[string]any{keyMonitoringScheduleArn: result.MonitoringScheduleArn}, jsonx.V1())
}
// listMonitoringSchedulesInput mirrors ListMonitoringSchedulesInput
@@ -175,7 +177,7 @@ type listMonitoringSchedulesInput struct {
func (h *Handler) handleListMonitoringSchedules(ctx context.Context, body []byte) ([]byte, error) {
var req listMonitoringSchedulesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -225,5 +227,5 @@ func (h *Handler) handleListMonitoringSchedules(ctx context.Context, body []byte
return json.Marshal(map[string]any{
"MonitoringScheduleSummaries": summaries,
keyNextToken: next,
- })
+ }, jsonx.V1())
}
diff --git a/services/sagemaker/handler_monitoring_schedules_test.go b/services/sagemaker/handler_monitoring_schedules_test.go
index fac766d234..143be00437 100644
--- a/services/sagemaker/handler_monitoring_schedules_test.go
+++ b/services/sagemaker/handler_monitoring_schedules_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateMonitoringSchedule(t *testing.T) {
@@ -24,7 +26,7 @@ func TestHandler_CreateMonitoringSchedule(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["MonitoringScheduleArn"], "my-schedule")
}
@@ -53,7 +55,7 @@ func TestHandler_DescribeMonitoringSchedule(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "sched-1", resp["MonitoringScheduleName"])
assert.Equal(t, "Scheduled", resp["MonitoringScheduleStatus"])
assert.Contains(t, resp, "MonitoringScheduleConfig")
@@ -73,7 +75,7 @@ func TestHandler_StopMonitoringSchedule(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeMonitoringSchedule", map[string]any{"MonitoringScheduleName": "sched-stop"})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "Stopped", resp["MonitoringScheduleStatus"])
}
@@ -97,7 +99,7 @@ func TestHandler_StartMonitoringSchedule(t *testing.T) {
map[string]any{"MonitoringScheduleName": "sched-start"},
)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "Scheduled", resp["MonitoringScheduleStatus"])
}
@@ -135,7 +137,7 @@ func TestHandler_ListMonitoringSchedules(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["MonitoringScheduleSummaries"].([]any)
assert.Len(t, items, 2)
}
diff --git a/services/sagemaker/handler_notebook_instances.go b/services/sagemaker/handler_notebook_instances.go
index 61ae26c482..7c0a8ae54c 100644
--- a/services/sagemaker/handler_notebook_instances.go
+++ b/services/sagemaker/handler_notebook_instances.go
@@ -2,9 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -28,7 +29,7 @@ func (h *Handler) handleCreateNotebookInstanceLifecycleConfig(
body []byte,
) ([]byte, error) {
var req createNotebookLifecycleConfigRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -62,7 +63,7 @@ func (h *Handler) handleCreateNotebookInstanceLifecycleConfig(
log := logger.Load(ctx)
log.InfoContext(ctx, "sagemaker: created notebook lifecycle config", "name", lc.Name)
- return json.Marshal(map[string]string{keyNotebookLifecycleConfigArn: lc.ARN})
+ return json.Marshal(map[string]string{keyNotebookLifecycleConfigArn: lc.ARN}, jsonx.V1())
}
// describeNotebookInstanceLifecycleConfigInput is
@@ -77,7 +78,7 @@ func (h *Handler) handleDescribeNotebookInstanceLifecycleConfig(
body []byte,
) ([]byte, error) {
var req describeNotebookInstanceLifecycleConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.NotebookInstanceLifecycleConfigName == "" {
@@ -111,7 +112,7 @@ func (h *Handler) handleDescribeNotebookInstanceLifecycleConfig(
"OnStart": onStart,
keyCreationTime: epochSeconds(lc.CreationTime),
keyLastModifiedTime: epochSeconds(lc.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// updateNotebookInstanceLifecycleConfigInput is
@@ -128,7 +129,7 @@ func (h *Handler) handleUpdateNotebookInstanceLifecycleConfig(
body []byte,
) ([]byte, error) {
var req updateNotebookInstanceLifecycleConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.NotebookInstanceLifecycleConfigName == "" {
@@ -163,7 +164,7 @@ func (h *Handler) handleUpdateNotebookInstanceLifecycleConfig(
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
// deleteNotebookInstanceLifecycleConfigInput is
@@ -178,7 +179,7 @@ func (h *Handler) handleDeleteNotebookInstanceLifecycleConfig(
body []byte,
) error {
var req deleteNotebookInstanceLifecycleConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.NotebookInstanceLifecycleConfigName == "" {
@@ -227,7 +228,7 @@ type listNotebookInstanceLifecycleConfigsInput struct {
func (h *Handler) handleListNotebookInstanceLifecycleConfigs(ctx context.Context, body []byte) ([]byte, error) {
var req listNotebookInstanceLifecycleConfigsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -258,7 +259,7 @@ func (h *Handler) handleListNotebookInstanceLifecycleConfigs(ctx context.Context
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -302,7 +303,7 @@ func (h *Handler) handleCreateNotebookInstanceFull(
body []byte,
) ([]byte, error) {
var req createNotebookInstanceFullRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -352,7 +353,7 @@ func (h *Handler) handleCreateNotebookInstanceFull(
log := logger.Load(ctx)
log.InfoContext(ctx, "sagemaker: created notebook instance (full)", "name", nb.NotebookInstanceName)
- return json.Marshal(map[string]string{keyNotebookInstanceArn: nb.NotebookInstanceArn})
+ return json.Marshal(map[string]string{keyNotebookInstanceArn: nb.NotebookInstanceArn}, jsonx.V1())
}
// describeNotebookInstanceInput is DescribeNotebookInstance's request shape
@@ -367,7 +368,7 @@ func (h *Handler) handleDescribeNotebookInstanceFull(
body []byte,
) ([]byte, error) {
var req describeNotebookInstanceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.NotebookInstanceName == "" {
@@ -390,7 +391,7 @@ func (h *Handler) handleDescribeNotebookInstanceFull(
}
addNotebookOptionalFields(resp, nb)
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// addNotebookOptionalFields adds every describable optional field.
@@ -477,7 +478,7 @@ type updateNotebookInstanceInput struct {
// handleUpdateNotebookInstanceFull supports all mutable fields (#1 update coverage).
func (h *Handler) handleUpdateNotebookInstanceFull(ctx context.Context, body []byte) error {
var req updateNotebookInstanceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.NotebookInstanceName == "" {
@@ -558,7 +559,7 @@ type listNotebookInstancesInput struct {
func (h *Handler) handleListNotebookInstances(ctx context.Context, body []byte) ([]byte, error) {
var req listNotebookInstancesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -599,7 +600,7 @@ func (h *Handler) handleListNotebookInstances(ctx context.Context, body []byte)
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// deleteNotebookInstanceInput is DeleteNotebookInstance's request shape
@@ -611,7 +612,7 @@ type deleteNotebookInstanceInput struct {
func (h *Handler) handleDeleteNotebookInstance(ctx context.Context, body []byte) error {
var req deleteNotebookInstanceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -638,7 +639,7 @@ type startNotebookInstanceInput struct {
func (h *Handler) handleStartNotebookInstance(ctx context.Context, body []byte) error {
var req startNotebookInstanceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -665,7 +666,7 @@ type stopNotebookInstanceInput struct {
func (h *Handler) handleStopNotebookInstance(ctx context.Context, body []byte) error {
var req stopNotebookInstanceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -698,7 +699,7 @@ func (h *Handler) handleCreatePresignedNotebookInstanceURL(
) ([]byte, error) {
var req createPresignedNotebookInstanceURLInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -715,5 +716,5 @@ func (h *Handler) handleCreatePresignedNotebookInstanceURL(
return nil, err
}
- return json.Marshal(map[string]string{"AuthorizedUrl": url})
+ return json.Marshal(map[string]string{"AuthorizedUrl": url}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_notebook_instances_test.go b/services/sagemaker/handler_notebook_instances_test.go
index 1ea6b4e57f..12bfaee9ac 100644
--- a/services/sagemaker/handler_notebook_instances_test.go
+++ b/services/sagemaker/handler_notebook_instances_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_NotebookInstanceLifecycle(t *testing.T) {
@@ -25,7 +27,7 @@ func TestHandler_NotebookInstanceLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recCreate.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut, jsonx.V1()))
assert.NotEmpty(t, createOut["NotebookInstanceArn"])
// Describe.
@@ -39,7 +41,7 @@ func TestHandler_NotebookInstanceLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut["NotebookInstances"].([]any), 1)
// Start.
@@ -129,7 +131,7 @@ func TestHandler_NotebookInstance_EventuallyInService(t *testing.T) {
assert.Equal(t, http.StatusOK, recDesc.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(recDesc.Body.Bytes(), &descOut, jsonx.V1()))
assert.NotEmpty(t, descOut["NotebookInstanceStatus"])
})
}
diff --git a/services/sagemaker/handler_optimization_jobs.go b/services/sagemaker/handler_optimization_jobs.go
index 5be37defb9..3ace41657d 100644
--- a/services/sagemaker/handler_optimization_jobs.go
+++ b/services/sagemaker/handler_optimization_jobs.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -15,10 +18,10 @@ import (
// OutputConfig/VpcConfig are decoded as json.RawMessage — see
// OptimizationJob's doc comment in optimization_jobs.go for why.
type createOptimizationJobInput struct {
- ModelSource json.RawMessage `json:"ModelSource"`
- OptimizationConfigs json.RawMessage `json:"OptimizationConfigs"`
- OutputConfig json.RawMessage `json:"OutputConfig"`
- VpcConfig json.RawMessage `json:"VpcConfig"`
+ ModelSource jsontext.Value `json:"ModelSource"`
+ OptimizationConfigs jsontext.Value `json:"OptimizationConfigs"`
+ OutputConfig jsontext.Value `json:"OutputConfig"`
+ VpcConfig jsontext.Value `json:"VpcConfig"`
StoppingCondition *StoppingCondition `json:"StoppingCondition"`
OptimizationEnvironment map[string]string `json:"OptimizationEnvironment"`
OptimizationJobName string `json:"OptimizationJobName"`
@@ -32,7 +35,7 @@ type createOptimizationJobInput struct {
func (h *Handler) handleCreateOptimizationJob(ctx context.Context, body []byte) ([]byte, error) {
var req createOptimizationJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -58,7 +61,7 @@ func (h *Handler) handleCreateOptimizationJob(ctx context.Context, body []byte)
return nil, err
}
- return json.Marshal(map[string]any{keyOptimizationJobArn: result.OptimizationJobArn})
+ return json.Marshal(map[string]any{keyOptimizationJobArn: result.OptimizationJobArn}, jsonx.V1())
}
// describeOptimizationJobInput mirrors DescribeOptimizationJobInput
@@ -126,7 +129,7 @@ func optimizationJobResponseMap(j *OptimizationJob) map[string]any {
func (h *Handler) handleDescribeOptimizationJob(ctx context.Context, body []byte) ([]byte, error) {
var req describeOptimizationJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -139,7 +142,7 @@ func (h *Handler) handleDescribeOptimizationJob(ctx context.Context, body []byte
return nil, err
}
- return json.Marshal(optimizationJobResponseMap(result))
+ return json.Marshal(optimizationJobResponseMap(result), jsonx.V1())
}
// deleteOptimizationJobInput mirrors DeleteOptimizationJobInput
@@ -151,7 +154,7 @@ type deleteOptimizationJobInput struct {
func (h *Handler) handleDeleteOptimizationJob(ctx context.Context, body []byte) error {
var req deleteOptimizationJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -171,7 +174,7 @@ type stopOptimizationJobInput struct {
func (h *Handler) handleStopOptimizationJob(ctx context.Context, body []byte) error {
var req stopOptimizationJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -201,7 +204,7 @@ type listOptimizationJobsInput struct {
func (h *Handler) handleListOptimizationJobs(ctx context.Context, body []byte) ([]byte, error) {
var req listOptimizationJobsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_optimization_jobs_test.go b/services/sagemaker/handler_optimization_jobs_test.go
index 4a15775518..13086f0c4e 100644
--- a/services/sagemaker/handler_optimization_jobs_test.go
+++ b/services/sagemaker/handler_optimization_jobs_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -11,6 +11,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// validOptimizationJobBody returns a CreateOptimizationJob request carrying
@@ -50,7 +52,7 @@ func TestHandler_CreateOptimizationJob(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["OptimizationJobArn"], "my-opt-job")
}
@@ -96,7 +98,7 @@ func TestHandler_DescribeOptimizationJob(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "opt-1", resp["OptimizationJobName"])
assert.Equal(t, "ml.g5.2xlarge", resp["DeploymentInstanceType"])
}
@@ -177,14 +179,14 @@ func TestHandler_ListOptimizationJobs(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["OptimizationJobSummaries"])
// Create and list
doSageMakerRequest(t, h, "CreateOptimizationJob", validOptimizationJobBody("my-opt-job"))
rec = doSageMakerRequest(t, h, "ListOptimizationJobs", map[string]any{})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["OptimizationJobSummaries"].([]any)
assert.Len(t, summaries, 1)
s := summaries[0].(map[string]any)
@@ -296,7 +298,7 @@ func TestHandler_AIWorkloadConfigLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Contains(t, createResp["AIWorkloadConfigArn"], "cfg-1")
rec = doSageMakerRequest(t, h, "DescribeAIWorkloadConfig", map[string]any{
@@ -305,7 +307,7 @@ func TestHandler_AIWorkloadConfigLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "cfg-1", descResp["AIWorkloadConfigName"])
assert.NotEmpty(t, descResp["CreationTime"])
},
@@ -336,7 +338,7 @@ func TestHandler_AIWorkloadConfigLifecycle(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "ResourceNotFound", out["__type"])
},
},
@@ -371,7 +373,7 @@ func TestHandler_AIWorkloadConfigLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["AIWorkloadConfigs"].([]any)
assert.Len(t, items, 1)
assert.Equal(t, "cfg-list", items[0].(map[string]any)["AIWorkloadConfigName"])
diff --git a/services/sagemaker/handler_partner_apps.go b/services/sagemaker/handler_partner_apps.go
index 50fcdc7823..c47f961a6c 100644
--- a/services/sagemaker/handler_partner_apps.go
+++ b/services/sagemaker/handler_partner_apps.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -23,7 +26,7 @@ type createPartnerAppInput struct {
Tier string `json:"Tier,omitempty"`
KmsKeyID string `json:"KmsKeyId,omitempty"`
Tags []tagObject `json:"Tags"`
- ApplicationConfig json.RawMessage `json:"ApplicationConfig,omitempty"`
+ ApplicationConfig jsontext.Value `json:"ApplicationConfig,omitempty"`
EnableAutoMinorVersionUpgrade bool `json:"EnableAutoMinorVersionUpgrade,omitempty"`
EnableIamSessionBasedIdentity bool `json:"EnableIamSessionBasedIdentity,omitempty"`
}
@@ -31,7 +34,7 @@ type createPartnerAppInput struct {
func (h *Handler) handleCreatePartnerApp(ctx context.Context, body []byte) ([]byte, error) {
var req createPartnerAppInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -72,7 +75,7 @@ func (h *Handler) handleCreatePartnerApp(ctx context.Context, body []byte) ([]by
return nil, err
}
- return json.Marshal(map[string]any{keyGenericArn: result.Arn})
+ return json.Marshal(map[string]any{keyGenericArn: result.Arn}, jsonx.V1())
}
// describePartnerAppResponse is the response body for DescribePartnerApp
@@ -94,7 +97,7 @@ type describePartnerAppResponse struct {
KmsKeyID string `json:"KmsKeyId,omitempty"`
Status string `json:"Status"`
Name string `json:"Name"`
- ApplicationConfig json.RawMessage `json:"ApplicationConfig,omitempty"`
+ ApplicationConfig jsontext.Value `json:"ApplicationConfig,omitempty"`
CreationTime float64 `json:"CreationTime"`
LastModifiedTime float64 `json:"LastModifiedTime"`
EnableAutoMinorVersionUpgrade bool `json:"EnableAutoMinorVersionUpgrade,omitempty"`
@@ -114,7 +117,7 @@ type describePartnerAppInput struct {
func (h *Handler) handleDescribePartnerApp(ctx context.Context, body []byte) ([]byte, error) {
var req describePartnerAppInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -143,7 +146,7 @@ func (h *Handler) handleDescribePartnerApp(ctx context.Context, body []byte) ([]
EnableIamSessionBasedIdentity: result.EnableIamSessionBasedIdentity,
CreationTime: epochSeconds(result.CreationTime),
LastModifiedTime: epochSeconds(result.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// deletePartnerAppInput mirrors DeletePartnerAppInput
@@ -156,7 +159,7 @@ type deletePartnerAppInput struct {
func (h *Handler) handleDeletePartnerApp(ctx context.Context, body []byte) ([]byte, error) {
var req deletePartnerAppInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -168,7 +171,7 @@ func (h *Handler) handleDeletePartnerApp(ctx context.Context, body []byte) ([]by
return nil, err
}
- return json.Marshal(map[string]string{keyGenericArn: req.Arn})
+ return json.Marshal(map[string]string{keyGenericArn: req.Arn}, jsonx.V1())
}
// updatePartnerAppInput mirrors UpdatePartnerAppInput
@@ -184,13 +187,13 @@ type updatePartnerAppInput struct {
AppVersion string `json:"AppVersion,omitempty"`
Tier string `json:"Tier,omitempty"`
Tags []tagObject `json:"Tags"`
- ApplicationConfig json.RawMessage `json:"ApplicationConfig,omitempty"`
+ ApplicationConfig jsontext.Value `json:"ApplicationConfig,omitempty"`
}
func (h *Handler) handleUpdatePartnerApp(ctx context.Context, body []byte) ([]byte, error) {
var req updatePartnerAppInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -211,7 +214,7 @@ func (h *Handler) handleUpdatePartnerApp(ctx context.Context, body []byte) ([]by
return nil, err
}
- return json.Marshal(map[string]any{keyGenericArn: result.Arn})
+ return json.Marshal(map[string]any{keyGenericArn: result.Arn}, jsonx.V1())
}
// listPartnerAppsInput mirrors ListPartnerAppsInput
@@ -224,7 +227,7 @@ type listPartnerAppsInput struct {
func (h *Handler) handleListPartnerApps(ctx context.Context, body []byte) ([]byte, error) {
var req listPartnerAppsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -257,7 +260,7 @@ type createPartnerAppPresignedURLInput struct {
func (h *Handler) handleCreatePartnerAppPresignedURL(ctx context.Context, body []byte) ([]byte, error) {
var req createPartnerAppPresignedURLInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -274,5 +277,5 @@ func (h *Handler) handleCreatePartnerAppPresignedURL(ctx context.Context, body [
return nil, err
}
- return json.Marshal(map[string]string{keyURL: url})
+ return json.Marshal(map[string]string{keyURL: url}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_partner_apps_test.go b/services/sagemaker/handler_partner_apps_test.go
index d041e8d295..d8383af0f8 100644
--- a/services/sagemaker/handler_partner_apps_test.go
+++ b/services/sagemaker/handler_partner_apps_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreatePartnerApp(t *testing.T) {
@@ -27,7 +29,7 @@ func TestHandler_CreatePartnerApp(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["Arn"], "my-app")
}
@@ -44,13 +46,13 @@ func TestHandler_DescribePartnerApp(t *testing.T) {
"Tier": "small",
})
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
rec := doSageMakerRequest(t, h, "DescribePartnerApp", map[string]any{"Arn": createResp["Arn"]})
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "app-1", resp["Name"])
}
@@ -67,7 +69,7 @@ func TestHandler_DeletePartnerApp(t *testing.T) {
"Tier": "small",
})
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
rec := doSageMakerRequest(t, h, "DeletePartnerApp", map[string]any{"Arn": createResp["Arn"]})
assert.Equal(t, http.StatusOK, rec.Code)
@@ -95,14 +97,14 @@ func TestHandler_PartnerApp_ExtendedLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
appARN := createResp["Arn"]
describeRec := doSageMakerRequest(t, h, "DescribePartnerApp", map[string]any{"Arn": appARN})
assert.Equal(t, http.StatusOK, describeRec.Code)
var describeResp map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp, jsonx.V1()))
assert.Equal(t, "papp-1", describeResp["Name"])
assert.Equal(t, "comet", describeResp["Type"])
assert.Equal(t, "arn:aws:iam::000000000000:role/partner", describeResp["ExecutionRoleArn"])
@@ -115,14 +117,14 @@ func TestHandler_PartnerApp_ExtendedLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, updateRec.Code)
describeRec = doSageMakerRequest(t, h, "DescribePartnerApp", map[string]any{"Arn": appARN})
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp, jsonx.V1()))
assert.Equal(t, "large", describeResp["Tier"])
listRec := doSageMakerRequest(t, h, "ListPartnerApps", map[string]any{})
assert.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
summaries, ok := listResp["Summaries"].([]any)
require.True(t, ok)
assert.Len(t, summaries, 1)
@@ -131,7 +133,7 @@ func TestHandler_PartnerApp_ExtendedLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, presignRec.Code)
var presignResp map[string]string
- require.NoError(t, json.Unmarshal(presignRec.Body.Bytes(), &presignResp))
+ require.NoError(t, json.Unmarshal(presignRec.Body.Bytes(), &presignResp, jsonx.V1()))
assert.Contains(t, presignResp["Url"], "papp-1")
}
@@ -176,7 +178,7 @@ func TestHandler_ListPartnerApps_Empty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["Summaries"])
}
@@ -193,13 +195,13 @@ func TestHandler_DeletePartnerApp_ReturnsArn(t *testing.T) {
"Tier": "small",
})
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
rec := doSageMakerRequest(t, h, "DeletePartnerApp", map[string]any{"Arn": createResp["Arn"]})
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, createResp["Arn"], resp["Arn"])
}
diff --git a/services/sagemaker/handler_pipeline_versions.go b/services/sagemaker/handler_pipeline_versions.go
index a19504f69d..d80a3413bb 100644
--- a/services/sagemaker/handler_pipeline_versions.go
+++ b/services/sagemaker/handler_pipeline_versions.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -49,7 +51,7 @@ type listPipelineVersionsRequest struct {
func (h *Handler) handleListPipelineVersions(ctx context.Context, body []byte) ([]byte, error) {
var req listPipelineVersionsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -103,7 +105,7 @@ type updatePipelineVersionRequest struct {
func (h *Handler) handleUpdatePipelineVersion(ctx context.Context, body []byte) ([]byte, error) {
var req updatePipelineVersionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -125,7 +127,7 @@ func (h *Handler) handleUpdatePipelineVersion(ctx context.Context, body []byte)
return json.Marshal(map[string]any{
keyPipelineArn: v.PipelineArn,
"PipelineVersionId": v.PipelineVersionID,
- })
+ }, jsonx.V1())
}
type describePipelineDefinitionForExecutionRequest struct {
@@ -135,7 +137,7 @@ type describePipelineDefinitionForExecutionRequest struct {
func (h *Handler) handleDescribePipelineDefinitionForExecution(ctx context.Context, body []byte) ([]byte, error) {
var req describePipelineDefinitionForExecutionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -151,7 +153,7 @@ func (h *Handler) handleDescribePipelineDefinitionForExecution(ctx context.Conte
return json.Marshal(map[string]any{
keyPipelineDefinition: definition,
keyCreationTime: epochSeconds(creationTime),
- })
+ }, jsonx.V1())
}
type updatePipelineExecutionRequest struct {
@@ -164,7 +166,7 @@ type updatePipelineExecutionRequest struct {
func (h *Handler) handleUpdatePipelineExecution(ctx context.Context, body []byte) ([]byte, error) {
var req updatePipelineExecutionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -183,5 +185,5 @@ func (h *Handler) handleUpdatePipelineExecution(ctx context.Context, body []byte
return nil, err
}
- return json.Marshal(map[string]string{keyPipelineExecutionArn: pe.PipelineExecutionArn})
+ return json.Marshal(map[string]string{keyPipelineExecutionArn: pe.PipelineExecutionArn}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_pipeline_versions_test.go b/services/sagemaker/handler_pipeline_versions_test.go
index e556fa2792..182a012376 100644
--- a/services/sagemaker/handler_pipeline_versions_test.go
+++ b/services/sagemaker/handler_pipeline_versions_test.go
@@ -1,13 +1,15 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_PipelineVersions_Lifecycle(t *testing.T) {
@@ -23,7 +25,7 @@ func TestHandler_PipelineVersions_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
versions, _ := listOut["PipelineVersionSummaries"].([]any)
require.Len(t, versions, 1)
assert.InDelta(t, float64(1), versions[0].(map[string]any)["PipelineVersionId"], 0)
@@ -36,7 +38,7 @@ func TestHandler_PipelineVersions_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recList2.Code)
var listOut2 map[string]any
- require.NoError(t, json.Unmarshal(recList2.Body.Bytes(), &listOut2))
+ require.NoError(t, json.Unmarshal(recList2.Body.Bytes(), &listOut2, jsonx.V1()))
versions2, _ := listOut2["PipelineVersionSummaries"].([]any)
require.Len(t, versions2, 2)
// Newest first.
@@ -49,7 +51,7 @@ func TestHandler_PipelineVersions_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recAsc.Code)
var ascOut map[string]any
- require.NoError(t, json.Unmarshal(recAsc.Body.Bytes(), &ascOut))
+ require.NoError(t, json.Unmarshal(recAsc.Body.Bytes(), &ascOut, jsonx.V1()))
ascVersions, _ := ascOut["PipelineVersionSummaries"].([]any)
require.Len(t, ascVersions, 2)
assert.InDelta(t, float64(1), ascVersions[0].(map[string]any)["PipelineVersionId"], 0)
@@ -63,7 +65,7 @@ func TestHandler_PipelineVersions_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recFuture.Code)
var futureOut map[string]any
- require.NoError(t, json.Unmarshal(recFuture.Body.Bytes(), &futureOut))
+ require.NoError(t, json.Unmarshal(recFuture.Body.Bytes(), &futureOut, jsonx.V1()))
futureVersions, _ := futureOut["PipelineVersionSummaries"].([]any)
assert.Empty(t, futureVersions, "CreatedAfter in the future must exclude every version")
@@ -74,13 +76,13 @@ func TestHandler_PipelineVersions_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recPast.Code)
var pastOut map[string]any
- require.NoError(t, json.Unmarshal(recPast.Body.Bytes(), &pastOut))
+ require.NoError(t, json.Unmarshal(recPast.Body.Bytes(), &pastOut, jsonx.V1()))
pastVersions, _ := pastOut["PipelineVersionSummaries"].([]any)
assert.Empty(t, pastVersions, "CreatedBefore in the past must exclude every version")
recDescribe := doSageMakerRequest(t, h, "DescribePipeline", map[string]any{"PipelineName": "ver-pipeline"})
var pipelineOut map[string]any
- require.NoError(t, json.Unmarshal(recDescribe.Body.Bytes(), &pipelineOut))
+ require.NoError(t, json.Unmarshal(recDescribe.Body.Bytes(), &pipelineOut, jsonx.V1()))
pipelineArn, _ := pipelineOut["PipelineArn"].(string)
require.NotEmpty(t, pipelineArn)
@@ -93,7 +95,7 @@ func TestHandler_PipelineVersions_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recStart.Code)
var startOut map[string]any
- require.NoError(t, json.Unmarshal(recStart.Body.Bytes(), &startOut))
+ require.NoError(t, json.Unmarshal(recStart.Body.Bytes(), &startOut, jsonx.V1()))
execArn, _ := startOut["PipelineExecutionArn"].(string)
require.NotEmpty(t, execArn)
@@ -103,7 +105,7 @@ func TestHandler_PipelineVersions_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recDefinition.Code)
var defOut map[string]any
- require.NoError(t, json.Unmarshal(recDefinition.Body.Bytes(), &defOut))
+ require.NoError(t, json.Unmarshal(recDefinition.Body.Bytes(), &defOut, jsonx.V1()))
assert.JSONEq(t, `{"Version":"v2"}`, defOut["PipelineDefinition"].(string))
recUpdateExec := doSageMakerRequest(t, h, "UpdatePipelineExecution", map[string]any{
@@ -112,7 +114,7 @@ func TestHandler_PipelineVersions_Lifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recUpdateExec.Code)
var updateExecOut map[string]any
- require.NoError(t, json.Unmarshal(recUpdateExec.Body.Bytes(), &updateExecOut))
+ require.NoError(t, json.Unmarshal(recUpdateExec.Body.Bytes(), &updateExecOut, jsonx.V1()))
assert.Equal(t, execArn, updateExecOut["PipelineExecutionArn"])
}
diff --git a/services/sagemaker/handler_pipelines.go b/services/sagemaker/handler_pipelines.go
index 183984216d..74ab158598 100644
--- a/services/sagemaker/handler_pipelines.go
+++ b/services/sagemaker/handler_pipelines.go
@@ -2,9 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -36,7 +37,7 @@ type retryPipelineExecutionInput struct {
func (h *Handler) handleRetryPipelineExecution(ctx context.Context, body []byte) ([]byte, error) {
var req retryPipelineExecutionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.PipelineExecutionArn == "" {
@@ -51,7 +52,7 @@ func (h *Handler) handleRetryPipelineExecution(ctx context.Context, body []byte)
log := logger.Load(ctx)
log.InfoContext(ctx, "sagemaker: retried pipeline execution", "arn", exec.PipelineExecutionArn)
- return json.Marshal(map[string]string{keyPipelineExecutionArn: exec.PipelineExecutionArn})
+ return json.Marshal(map[string]string{keyPipelineExecutionArn: exec.PipelineExecutionArn}, jsonx.V1())
}
// stopPipelineExecutionInput mirrors StopPipelineExecutionInput
@@ -63,7 +64,7 @@ type stopPipelineExecutionInput struct {
func (h *Handler) handleStopPipelineExecution(ctx context.Context, body []byte) ([]byte, error) {
var req stopPipelineExecutionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.PipelineExecutionArn == "" {
@@ -78,7 +79,7 @@ func (h *Handler) handleStopPipelineExecution(ctx context.Context, body []byte)
log := logger.Load(ctx)
log.InfoContext(ctx, "sagemaker: stopped pipeline execution", "arn", exec.PipelineExecutionArn)
- return json.Marshal(map[string]string{keyPipelineExecutionArn: exec.PipelineExecutionArn})
+ return json.Marshal(map[string]string{keyPipelineExecutionArn: exec.PipelineExecutionArn}, jsonx.V1())
}
// sendPipelineExecutionStepSuccessInput mirrors SendPipelineExecutionStepSuccessInput
@@ -98,7 +99,7 @@ func (h *Handler) handleSendPipelineExecutionStepSuccess(
body []byte,
) ([]byte, error) {
var req sendPipelineExecutionStepSuccessInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.CallbackToken == "" {
@@ -116,7 +117,7 @@ func (h *Handler) handleSendPipelineExecutionStepSuccess(
// backend's execution-ARN convention, so it doubles as the real
// response's PipelineExecutionArn (SendPipelineExecutionStepSuccessOutput,
// api_op_SendPipelineExecutionStepSuccess.go:48-50).
- return json.Marshal(map[string]string{keyPipelineExecutionArn: req.CallbackToken})
+ return json.Marshal(map[string]string{keyPipelineExecutionArn: req.CallbackToken}, jsonx.V1())
}
// sendPipelineExecutionStepFailureInput mirrors SendPipelineExecutionStepFailureInput
@@ -134,7 +135,7 @@ func (h *Handler) handleSendPipelineExecutionStepFailure(
body []byte,
) ([]byte, error) {
var req sendPipelineExecutionStepFailureInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.CallbackToken == "" {
@@ -148,7 +149,7 @@ func (h *Handler) handleSendPipelineExecutionStepFailure(
log := logger.Load(ctx)
log.InfoContext(ctx, "sagemaker: sent pipeline step failure", "token", req.CallbackToken)
- return json.Marshal(map[string]string{keyPipelineExecutionArn: req.CallbackToken})
+ return json.Marshal(map[string]string{keyPipelineExecutionArn: req.CallbackToken}, jsonx.V1())
}
// pipelineStepCallbackMetadata mirrors types.CallbackStepMetadata
@@ -191,7 +192,7 @@ type listPipelineExecutionStepsInput struct {
func (h *Handler) handleListPipelineExecutionSteps(ctx context.Context, body []byte) ([]byte, error) {
var req listPipelineExecutionStepsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.PipelineExecutionArn == "" {
@@ -234,7 +235,7 @@ func (h *Handler) handleListPipelineExecutionSteps(ctx context.Context, body []b
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -258,7 +259,7 @@ type createPipelineInput struct {
func (h *Handler) handleCreatePipelineFull(ctx context.Context, body []byte) ([]byte, error) {
var req createPipelineInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -293,7 +294,7 @@ func (h *Handler) handleCreatePipelineFull(ctx context.Context, body []byte) ([]
logger.Load(ctx).InfoContext(ctx, "sagemaker: created pipeline (full)", "name", p.PipelineName)
- return json.Marshal(map[string]string{keyPipelineArn: p.PipelineArn})
+ return json.Marshal(map[string]string{keyPipelineArn: p.PipelineArn}, jsonx.V1())
}
// updatePipelineInput mirrors UpdatePipelineInput (api_op_UpdatePipeline.go:
@@ -311,7 +312,7 @@ type updatePipelineInput struct {
func (h *Handler) handleUpdatePipelineFull(ctx context.Context, body []byte) ([]byte, error) {
var req updatePipelineInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -345,7 +346,7 @@ func (h *Handler) handleUpdatePipelineFull(ctx context.Context, body []byte) ([]
logger.Load(ctx).InfoContext(ctx, "sagemaker: updated pipeline (full)", "name", p.PipelineName)
- return json.Marshal(map[string]string{keyPipelineArn: p.PipelineArn})
+ return json.Marshal(map[string]string{keyPipelineArn: p.PipelineArn}, jsonx.V1())
}
// startPipelineExecutionInput mirrors StartPipelineExecutionInput
@@ -371,7 +372,7 @@ func (h *Handler) handleStartPipelineExecutionFull(
) ([]byte, error) {
var req startPipelineExecutionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -391,7 +392,7 @@ func (h *Handler) handleStartPipelineExecutionFull(
pe.PipelineExecutionArn,
)
- return json.Marshal(map[string]string{keyPipelineExecutionArn: pe.PipelineExecutionArn})
+ return json.Marshal(map[string]string{keyPipelineExecutionArn: pe.PipelineExecutionArn}, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -413,7 +414,7 @@ func (h *Handler) handleListPipelineParametersForExecution(
) ([]byte, error) {
var req listPipelineParametersForExecutionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -438,7 +439,7 @@ func (h *Handler) handleListPipelineParametersForExecution(
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -455,7 +456,7 @@ type describePipelineInput struct {
func (h *Handler) handleDescribePipeline(ctx context.Context, body []byte) ([]byte, error) {
var req describePipelineInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -506,7 +507,7 @@ func (h *Handler) handleDescribePipeline(ctx context.Context, body []byte) ([]by
}
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// pipelineSummary mirrors PipelineSummary (types.go:17593-17620,
@@ -539,7 +540,7 @@ type listPipelinesInput struct {
func (h *Handler) handleListPipelines(ctx context.Context, body []byte) ([]byte, error) {
var req listPipelinesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -577,7 +578,7 @@ func (h *Handler) handleListPipelines(ctx context.Context, body []byte) ([]byte,
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// deletePipelineInput mirrors DeletePipelineInput (api_op_DeletePipeline.go:
@@ -590,7 +591,7 @@ type deletePipelineInput struct {
func (h *Handler) handleDeletePipeline(ctx context.Context, body []byte) ([]byte, error) {
var req deletePipelineInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -605,7 +606,7 @@ func (h *Handler) handleDeletePipeline(ctx context.Context, body []byte) ([]byte
logger.Load(ctx).InfoContext(ctx, "sagemaker: deleted pipeline", "name", req.PipelineName)
- return json.Marshal(map[string]string{keyPipelineArn: p.PipelineArn})
+ return json.Marshal(map[string]string{keyPipelineArn: p.PipelineArn}, jsonx.V1())
}
// describePipelineExecutionInput mirrors DescribePipelineExecutionInput
@@ -617,7 +618,7 @@ type describePipelineExecutionInput struct {
func (h *Handler) handleDescribePipelineExecution(ctx context.Context, body []byte) ([]byte, error) {
var req describePipelineExecutionInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -661,7 +662,7 @@ func (h *Handler) handleDescribePipelineExecution(ctx context.Context, body []by
resp["MLflowConfig"] = map[string]string{"MlflowExperimentName": pe.MlflowExperimentName}
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type pipelineExecutionSummary struct {
@@ -688,7 +689,7 @@ type listPipelineExecutionsInput struct {
func (h *Handler) handleListPipelineExecutions(ctx context.Context, body []byte) ([]byte, error) {
var req listPipelineExecutionsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -723,5 +724,5 @@ func (h *Handler) handleListPipelineExecutions(ctx context.Context, body []byte)
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/sagemaker/handler_pipelines_test.go b/services/sagemaker/handler_pipelines_test.go
index cd351a471e..c6b666db63 100644
--- a/services/sagemaker/handler_pipelines_test.go
+++ b/services/sagemaker/handler_pipelines_test.go
@@ -3,7 +3,7 @@ package sagemaker_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -18,6 +18,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -68,7 +69,7 @@ func TestHandler_CreatePipeline_FullFields(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "My Pipeline", descResp["PipelineDisplayName"])
assert.Equal(t, "A test pipeline", descResp["PipelineDescription"])
assert.NotNil(t, descResp["ParallelismConfiguration"])
@@ -95,7 +96,7 @@ func TestHandler_UpdatePipeline_FullFields(t *testing.T) {
"PipelineName": "my-pipeline",
})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Updated Display", descResp["PipelineDisplayName"])
assert.Equal(t, "Updated desc", descResp["PipelineDescription"])
}
@@ -128,7 +129,7 @@ func TestHandler_CreatePipeline_S3Location_UnreadableRejected(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
rec = doSageMakerRequest(t, h, "DescribePipeline", map[string]any{"PipelineName": "s3-def-pipeline"})
@@ -155,7 +156,7 @@ func TestHandler_UpdatePipeline_S3Location_UnreadableRejected(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
@@ -270,7 +271,7 @@ func TestHandler_StartPipelineExecution_WithParams(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
execArn := startResp["PipelineExecutionArn"]
assert.NotEmpty(t, execArn)
@@ -281,7 +282,7 @@ func TestHandler_StartPipelineExecution_WithParams(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Run 1", descResp["PipelineExecutionDisplayName"])
assert.Equal(t, "First run", descResp["PipelineExecutionDescription"])
params := descResp["PipelineParameters"].([]any)
@@ -314,7 +315,7 @@ func TestHandler_StartPipelineExecution_ParallelismAndSelectiveExecution(t *test
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
execArn := startResp["PipelineExecutionArn"]
require.NotEmpty(t, execArn)
@@ -324,7 +325,7 @@ func TestHandler_StartPipelineExecution_ParallelismAndSelectiveExecution(t *test
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
parallelism, ok := descResp["ParallelismConfiguration"].(map[string]any)
require.True(t, ok, "ParallelismConfiguration must be present in DescribePipelineExecution")
@@ -367,7 +368,7 @@ func TestHandler_ListPipelineParametersForExecution(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
execArn := startResp["PipelineExecutionArn"]
require.NotEmpty(t, execArn)
@@ -383,7 +384,7 @@ func TestHandler_ListPipelineParametersForExecution(t *testing.T) {
Value string `json:"Value"`
} `json:"PipelineParameters"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp.PipelineParameters, 2)
names := make(map[string]string)
@@ -409,7 +410,7 @@ func TestHandler_ListPipelineParametersForExecution_Empty(t *testing.T) {
"PipelineName": "empty-param-pipe",
})
var startResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
rec2 := doSageMakerRequest(t, h, "ListPipelineParametersForExecution", map[string]any{
"PipelineExecutionArn": startResp["PipelineExecutionArn"],
@@ -419,7 +420,7 @@ func TestHandler_ListPipelineParametersForExecution_Empty(t *testing.T) {
var listResp struct {
PipelineParameters []any `json:"PipelineParameters"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listResp, jsonx.V1()))
assert.NotNil(t, listResp.PipelineParameters)
assert.Empty(t, listResp.PipelineParameters)
}
@@ -449,7 +450,7 @@ func TestHandler_PipelineLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recCreate.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut, jsonx.V1()))
assert.NotEmpty(t, createOut["PipelineArn"])
// Describe pipeline.
@@ -466,7 +467,7 @@ func TestHandler_PipelineLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut["PipelineSummaries"].([]any), 1)
// Update pipeline.
@@ -483,7 +484,7 @@ func TestHandler_PipelineLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recExec.Code)
var execOut map[string]any
- require.NoError(t, json.Unmarshal(recExec.Body.Bytes(), &execOut))
+ require.NoError(t, json.Unmarshal(recExec.Body.Bytes(), &execOut, jsonx.V1()))
execArn := execOut["PipelineExecutionArn"].(string)
assert.NotEmpty(t, execArn)
@@ -553,7 +554,7 @@ func TestHandler_DeletePipeline_RunningExecution(t *testing.T) {
require.Equal(t, http.StatusBadRequest, recDelete.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(recDelete.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(recDelete.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ConflictException", errResp["__type"])
// Once the execution reaches Succeeded, the delete is allowed.
@@ -640,7 +641,7 @@ func TestHandler_PipelineExecutionSteps(t *testing.T) {
require.Equal(t, http.StatusOK, recExec.Code)
var execOut map[string]any
- require.NoError(t, json.Unmarshal(recExec.Body.Bytes(), &execOut))
+ require.NoError(t, json.Unmarshal(recExec.Body.Bytes(), &execOut, jsonx.V1()))
execArn := execOut["PipelineExecutionArn"].(string)
// ListPipelineExecutionSteps.
diff --git a/services/sagemaker/handler_pipelines_versionid_test.go b/services/sagemaker/handler_pipelines_versionid_test.go
index 99c8837356..3816453bf2 100644
--- a/services/sagemaker/handler_pipelines_versionid_test.go
+++ b/services/sagemaker/handler_pipelines_versionid_test.go
@@ -1,12 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_DescribePipeline_PipelineVersionId(t *testing.T) {
@@ -59,7 +61,7 @@ func TestHandler_DescribePipeline_PipelineVersionId(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantDef, out["PipelineDefinition"])
})
}
@@ -81,7 +83,7 @@ func TestHandler_DescribePipeline_LastRunTime(t *testing.T) {
require.Equal(t, http.StatusOK, beforeRec.Code, beforeRec.Body.String())
var beforeOut map[string]any
- require.NoError(t, json.Unmarshal(beforeRec.Body.Bytes(), &beforeOut))
+ require.NoError(t, json.Unmarshal(beforeRec.Body.Bytes(), &beforeOut, jsonx.V1()))
_, hasLastRunTime := beforeOut["LastRunTime"]
assert.False(t, hasLastRunTime, "a pipeline that has never run must not emit LastRunTime")
@@ -96,7 +98,7 @@ func TestHandler_DescribePipeline_LastRunTime(t *testing.T) {
require.Equal(t, http.StatusOK, afterRec.Code, afterRec.Body.String())
var afterOut map[string]any
- require.NoError(t, json.Unmarshal(afterRec.Body.Bytes(), &afterOut))
+ require.NoError(t, json.Unmarshal(afterRec.Body.Bytes(), &afterOut, jsonx.V1()))
lastRunTime, ok := afterOut["LastRunTime"].(float64)
require.True(t, ok, "a pipeline that has run must emit LastRunTime")
diff --git a/services/sagemaker/handler_presigned_session.go b/services/sagemaker/handler_presigned_session.go
index 4b06eb5631..f64a4781df 100644
--- a/services/sagemaker/handler_presigned_session.go
+++ b/services/sagemaker/handler_presigned_session.go
@@ -2,11 +2,13 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -108,7 +110,7 @@ type createPresignedDomainURLRequest struct {
func (h *Handler) handleCreatePresignedDomainURL(ctx context.Context, body []byte) ([]byte, error) {
var req createPresignedDomainURLRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -133,7 +135,7 @@ func (h *Handler) handleCreatePresignedDomainURL(ctx context.Context, body []byt
return nil, err
}
- return json.Marshal(map[string]string{keyAuthorizedURL: url})
+ return json.Marshal(map[string]string{keyAuthorizedURL: url}, jsonx.V1())
}
// templateVarPattern matches simple Liquid-style "{{ task.input. }}"
@@ -162,7 +164,7 @@ type renderUITemplateRequest struct {
func (h *Handler) handleRenderUITemplate(ctx context.Context, body []byte) ([]byte, error) {
var req renderUITemplateRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -195,7 +197,7 @@ func (h *Handler) handleRenderUITemplate(ctx context.Context, body []byte) ([]by
return json.Marshal(map[string]any{
"RenderedContent": rendered,
"Errors": []any{},
- })
+ }, jsonx.V1())
}
// renderUITemplateContent substitutes "{{ task.input. }}" variables in
@@ -206,7 +208,7 @@ func renderUITemplateContent(content, taskInput string) string {
}
var fields map[string]any
- if err := json.Unmarshal([]byte(taskInput), &fields); err != nil {
+ if err := json.Unmarshal([]byte(taskInput), &fields, jsonx.V1()); err != nil {
return content
}
@@ -233,7 +235,7 @@ type startSessionRequest struct {
func (h *Handler) handleStartSession(ctx context.Context, body []byte) ([]byte, error) {
var req startSessionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -250,5 +252,5 @@ func (h *Handler) handleStartSession(ctx context.Context, body []byte) ([]byte,
"SessionId": result.SessionID,
"StreamUrl": result.StreamURL,
"TokenValue": result.TokenValue,
- })
+ }, jsonx.V1())
}
diff --git a/services/sagemaker/handler_presigned_session_test.go b/services/sagemaker/handler_presigned_session_test.go
index aacc381360..8a58cdfa28 100644
--- a/services/sagemaker/handler_presigned_session_test.go
+++ b/services/sagemaker/handler_presigned_session_test.go
@@ -1,13 +1,15 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_RenderUiTemplate(t *testing.T) {
@@ -25,7 +27,7 @@ func TestHandler_RenderUiTemplate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "hello world
", out["RenderedContent"])
assert.Empty(t, out["Errors"])
}
@@ -70,7 +72,7 @@ func TestHandler_StartSession(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["SessionId"])
assert.NotEmpty(t, out["StreamUrl"])
assert.NotEmpty(t, out["TokenValue"])
@@ -95,7 +97,7 @@ func TestHandler_CreatePresignedDomainUrl(t *testing.T) {
"DefaultUserSettings": map[string]any{},
})
var domainOut map[string]any
- require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut))
+ require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut, jsonx.V1()))
domainID, _ := domainOut["DomainId"].(string)
require.NotEmpty(t, domainID)
@@ -109,7 +111,7 @@ func TestHandler_CreatePresignedDomainUrl(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["AuthorizedUrl"])
}
diff --git a/services/sagemaker/handler_processing_jobs.go b/services/sagemaker/handler_processing_jobs.go
index 4cbc569ae0..63fd7023e1 100644
--- a/services/sagemaker/handler_processing_jobs.go
+++ b/services/sagemaker/handler_processing_jobs.go
@@ -2,9 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -248,7 +249,7 @@ type createProcessingJobRequest struct {
func (h *Handler) handleCreateProcessingJob(ctx context.Context, body []byte) ([]byte, error) {
var req createProcessingJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.ProcessingJobName == "" {
@@ -312,7 +313,7 @@ func (h *Handler) handleCreateProcessingJob(ctx context.Context, body []byte) ([
log := logger.Load(ctx)
log.InfoContext(ctx, "sagemaker: created processing job", "name", pj.ProcessingJobName)
- return json.Marshal(map[string]string{keyProcessingJobArn: pj.ProcessingJobArn})
+ return json.Marshal(map[string]string{keyProcessingJobArn: pj.ProcessingJobArn}, jsonx.V1())
}
type describeProcessingJobRequest struct {
@@ -321,7 +322,7 @@ type describeProcessingJobRequest struct {
func (h *Handler) handleDescribeProcessingJob(ctx context.Context, body []byte) ([]byte, error) {
var req describeProcessingJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.ProcessingJobName == "" {
@@ -367,7 +368,7 @@ func (h *Handler) handleDescribeProcessingJob(ctx context.Context, body []byte)
resp["Environment"] = pj.Environment
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type stopProcessingJobRequest struct {
@@ -376,7 +377,7 @@ type stopProcessingJobRequest struct {
func (h *Handler) handleStopProcessingJob(ctx context.Context, body []byte) error {
var req stopProcessingJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.ProcessingJobName == "" {
@@ -399,7 +400,7 @@ type deleteProcessingJobRequest struct {
func (h *Handler) handleDeleteProcessingJob(ctx context.Context, body []byte) error {
var req deleteProcessingJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.ProcessingJobName == "" {
@@ -440,7 +441,7 @@ type listProcessingJobsRequest struct {
func (h *Handler) handleListProcessingJobs(ctx context.Context, body []byte) ([]byte, error) {
var req listProcessingJobsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -477,5 +478,5 @@ func (h *Handler) handleListProcessingJobs(ctx context.Context, body []byte) ([]
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/sagemaker/handler_processing_jobs_test.go b/services/sagemaker/handler_processing_jobs_test.go
index 65da75075a..06a085de42 100644
--- a/services/sagemaker/handler_processing_jobs_test.go
+++ b/services/sagemaker/handler_processing_jobs_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -12,6 +12,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_DeleteProcessingJob(t *testing.T) {
@@ -233,7 +235,7 @@ func TestHandler_ListProcessingJobs_FilterSort(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["ProcessingJobSummaries"].([]any)
require.Len(t, summaries, 1)
assert.Equal(t, "pj-beta", summaries[0].(map[string]any)["ProcessingJobName"])
@@ -243,7 +245,7 @@ func TestHandler_ListProcessingJobs_FilterSort(t *testing.T) {
"SortOrder": "Descending",
})
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries = resp["ProcessingJobSummaries"].([]any)
require.Len(t, summaries, 2)
assert.Equal(t, "pj-beta", summaries[0].(map[string]any)["ProcessingJobName"])
diff --git a/services/sagemaker/handler_projects.go b/services/sagemaker/handler_projects.go
index 7bf40101d9..0b6e777c06 100644
--- a/services/sagemaker/handler_projects.go
+++ b/services/sagemaker/handler_projects.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -12,17 +15,17 @@ import (
// createProjectInput mirrors CreateProjectInput (api_op_CreateProject.go:30-59).
type createProjectInput struct {
- ServiceCatalogProvisioningDetails json.RawMessage `json:"ServiceCatalogProvisioningDetails"`
- TemplateProviders json.RawMessage `json:"TemplateProviders"`
- ProjectName string `json:"ProjectName"`
- ProjectDescription string `json:"ProjectDescription"`
- Tags []tagObject `json:"Tags"`
+ ServiceCatalogProvisioningDetails jsontext.Value `json:"ServiceCatalogProvisioningDetails"`
+ TemplateProviders jsontext.Value `json:"TemplateProviders"`
+ ProjectName string `json:"ProjectName"`
+ ProjectDescription string `json:"ProjectDescription"`
+ Tags []tagObject `json:"Tags"`
}
func (h *Handler) handleCreateProject(ctx context.Context, body []byte) ([]byte, error) {
var req createProjectInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -44,7 +47,7 @@ func (h *Handler) handleCreateProject(ctx context.Context, body []byte) ([]byte,
return json.Marshal(map[string]any{
keyProjectArn: result.ProjectArn,
keyProjectID: result.ProjectID,
- })
+ }, jsonx.V1())
}
// describeProjectInput mirrors DescribeProjectInput (api_op_DescribeProject.go:29-34).
@@ -100,7 +103,7 @@ func projectResponseMap(p *Project) map[string]any {
func (h *Handler) handleDescribeProject(ctx context.Context, body []byte) ([]byte, error) {
var req describeProjectInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -113,7 +116,7 @@ func (h *Handler) handleDescribeProject(ctx context.Context, body []byte) ([]byt
return nil, err
}
- return json.Marshal(projectResponseMap(result))
+ return json.Marshal(projectResponseMap(result), jsonx.V1())
}
// deleteProjectInput mirrors DeleteProjectInput (api_op_DeleteProject.go:27-32).
@@ -124,7 +127,7 @@ type deleteProjectInput struct {
func (h *Handler) handleDeleteProject(ctx context.Context, body []byte) error {
var req deleteProjectInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -149,7 +152,7 @@ type listProjectsInput struct {
func (h *Handler) handleListProjects(ctx context.Context, body []byte) ([]byte, error) {
var req listProjectsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -183,7 +186,7 @@ func (h *Handler) handleListProjects(ctx context.Context, body []byte) ([]byte,
resp[keyNextToken] = next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -202,7 +205,7 @@ type updateProjectInput struct {
func (h *Handler) handleUpdateProject(ctx context.Context, body []byte) ([]byte, error) {
var req updateProjectInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -228,5 +231,5 @@ func (h *Handler) handleUpdateProject(ctx context.Context, body []byte) ([]byte,
return nil, err
}
- return json.Marshal(map[string]string{keyProjectArn: p.ProjectArn})
+ return json.Marshal(map[string]string{keyProjectArn: p.ProjectArn}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_projects_test.go b/services/sagemaker/handler_projects_test.go
index 17fb110c3c..734d171a12 100644
--- a/services/sagemaker/handler_projects_test.go
+++ b/services/sagemaker/handler_projects_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateProject(t *testing.T) {
@@ -23,7 +25,7 @@ func TestHandler_CreateProject(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["ProjectArn"], "my-project")
assert.NotEmpty(t, resp["ProjectId"])
}
@@ -39,7 +41,7 @@ func TestHandler_DescribeProject(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "proj-1", resp["ProjectName"])
assert.Equal(t, "CreateCompleted", resp["ProjectStatus"])
}
@@ -69,7 +71,7 @@ func TestHandler_ListProjects(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["ProjectSummaryList"].([]any)
assert.Len(t, items, 2)
}
@@ -104,7 +106,7 @@ func TestHandler_ListProjects_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["ProjectSummaryList"].([]any)
require.Len(t, items, len(tc.wantNames))
@@ -135,7 +137,7 @@ func TestHandler_DescribeProject_NoTagsLeak(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotContains(t, resp, "Tags")
assert.NotZero(t, resp["LastModifiedTime"])
}
@@ -185,7 +187,7 @@ func TestHandler_UpdateProject(t *testing.T) {
require.Equal(t, http.StatusOK, recDescribe.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(recDescribe.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(recDescribe.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "updated description", out["ProjectDescription"])
}
diff --git a/services/sagemaker/handler_servicecatalog.go b/services/sagemaker/handler_servicecatalog.go
index c72befbb23..ecbae1207a 100644
--- a/services/sagemaker/handler_servicecatalog.go
+++ b/services/sagemaker/handler_servicecatalog.go
@@ -2,7 +2,9 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -35,11 +37,11 @@ func (h *Handler) dispatchServicecatalogOps(
return []byte("{}"), true, nil
case "GetSagemakerServicecatalogPortfolioStatus":
status := h.Backend.GetSagemakerServicecatalogPortfolioStatus(ctx)
- r, err := json.Marshal(map[string]string{keyStatus: status})
+ r, err := json.Marshal(map[string]string{keyStatus: status}, jsonx.V1())
return r, true, err
case "ListResourceCatalogs":
- r, err := json.Marshal(map[string]any{"ResourceCatalogs": h.Backend.ListResourceCatalogs()})
+ r, err := json.Marshal(map[string]any{"ResourceCatalogs": h.Backend.ListResourceCatalogs()}, jsonx.V1())
return r, true, err
}
diff --git a/services/sagemaker/handler_servicecatalog_test.go b/services/sagemaker/handler_servicecatalog_test.go
index b8c341f10b..bacff5951f 100644
--- a/services/sagemaker/handler_servicecatalog_test.go
+++ b/services/sagemaker/handler_servicecatalog_test.go
@@ -1,12 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_ServicecatalogPortfolio_Toggle(t *testing.T) {
@@ -19,7 +21,7 @@ func TestHandler_ServicecatalogPortfolio_Toggle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, want, out["Status"])
}
@@ -47,7 +49,7 @@ func TestHandler_ListResourceCatalogs_Empty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out["ResourceCatalogs"])
}
diff --git a/services/sagemaker/handler_spaces.go b/services/sagemaker/handler_spaces.go
index 2982a9e202..9bdc4f1d00 100644
--- a/services/sagemaker/handler_spaces.go
+++ b/services/sagemaker/handler_spaces.go
@@ -2,8 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -16,19 +19,19 @@ import (
// carried as opaque json.RawMessage passthrough per this file's established
// convention.
type createSpaceInput struct {
- DomainID string `json:"DomainId"`
- SpaceName string `json:"SpaceName"`
- SpaceDisplayName string `json:"SpaceDisplayName"`
- OwnershipSettings json.RawMessage `json:"OwnershipSettings"`
- SpaceSettings json.RawMessage `json:"SpaceSettings"`
- SpaceSharingSettings json.RawMessage `json:"SpaceSharingSettings"`
- Tags []tagObject `json:"Tags"`
+ DomainID string `json:"DomainId"`
+ SpaceName string `json:"SpaceName"`
+ SpaceDisplayName string `json:"SpaceDisplayName"`
+ OwnershipSettings jsontext.Value `json:"OwnershipSettings"`
+ SpaceSettings jsontext.Value `json:"SpaceSettings"`
+ SpaceSharingSettings jsontext.Value `json:"SpaceSharingSettings"`
+ Tags []tagObject `json:"Tags"`
}
func (h *Handler) handleCreateSpace(ctx context.Context, body []byte) ([]byte, error) {
var req createSpaceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -54,7 +57,7 @@ func (h *Handler) handleCreateSpace(ctx context.Context, body []byte) ([]byte, e
return nil, err
}
- return json.Marshal(map[string]any{"SpaceArn": result.SpaceArn})
+ return json.Marshal(map[string]any{"SpaceArn": result.SpaceArn}, jsonx.V1())
}
type describeSpaceInput struct {
@@ -65,7 +68,7 @@ type describeSpaceInput struct {
func (h *Handler) handleDescribeSpace(ctx context.Context, body []byte) ([]byte, error) {
var req describeSpaceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -82,7 +85,7 @@ func (h *Handler) handleDescribeSpace(ctx context.Context, body []byte) ([]byte,
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
type deleteSpaceInput struct {
@@ -93,7 +96,7 @@ type deleteSpaceInput struct {
func (h *Handler) handleDeleteSpace(ctx context.Context, body []byte) error {
var req deleteSpaceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -120,7 +123,7 @@ type listSpacesInput struct {
func (h *Handler) handleListSpaces(ctx context.Context, body []byte) ([]byte, error) {
var req listSpacesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -147,21 +150,21 @@ func (h *Handler) handleListSpaces(ctx context.Context, body []byte) ([]byte, er
return json.Marshal(map[string]any{
"Spaces": summaries,
keyNextToken: next,
- })
+ }, jsonx.V1())
}
// updateSpaceInput mirrors UpdateSpaceInput (api_op_UpdateSpace.go:27-42).
type updateSpaceInput struct {
- DomainID string `json:"DomainId"`
- SpaceName string `json:"SpaceName"`
- SpaceDisplayName string `json:"SpaceDisplayName"`
- SpaceSettings json.RawMessage `json:"SpaceSettings"`
+ DomainID string `json:"DomainId"`
+ SpaceName string `json:"SpaceName"`
+ SpaceDisplayName string `json:"SpaceDisplayName"`
+ SpaceSettings jsontext.Value `json:"SpaceSettings"`
}
func (h *Handler) handleUpdateSpace(ctx context.Context, body []byte) ([]byte, error) {
var req updateSpaceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -185,5 +188,5 @@ func (h *Handler) handleUpdateSpace(ctx context.Context, body []byte) ([]byte, e
return nil, err
}
- return json.Marshal(map[string]string{keySpaceArn: s.SpaceArn})
+ return json.Marshal(map[string]string{keySpaceArn: s.SpaceArn}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_spaces_test.go b/services/sagemaker/handler_spaces_test.go
index 579dcc1a2e..be593eb580 100644
--- a/services/sagemaker/handler_spaces_test.go
+++ b/services/sagemaker/handler_spaces_test.go
@@ -1,12 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateSpace(t *testing.T) {
@@ -21,7 +23,7 @@ func TestHandler_CreateSpace(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["SpaceArn"], "my-space")
}
@@ -36,7 +38,7 @@ func TestHandler_DescribeSpace(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "space-1", resp["SpaceName"])
assert.Equal(t, "InService", resp["Status"])
}
@@ -67,7 +69,7 @@ func TestHandler_ListSpaces(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["Spaces"].([]any)
assert.Len(t, items, 2)
}
@@ -91,7 +93,7 @@ func TestHandler_ListSpaces_NameContainsAndMaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["Spaces"].([]any), 2)
})
@@ -105,7 +107,7 @@ func TestHandler_ListSpaces_NameContainsAndMaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["Spaces"].([]any), 1, "MaxResults must cap the page, not just be parsed and ignored")
assert.NotEmpty(t, resp["NextToken"])
})
@@ -127,7 +129,7 @@ func TestHandler_CreateSpace_DisplayNameRoundTrips(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "My Display Name", resp["SpaceDisplayName"])
}
@@ -148,7 +150,7 @@ func TestHandler_UpdateSpace(t *testing.T) {
var domainResp map[string]any
rec := doSageMakerRequest(t, h, "ListDomains", map[string]any{})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &domainResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &domainResp, jsonx.V1()))
domains := domainResp["Domains"].([]any)
require.Len(t, domains, 1)
domainID := domains[0].(map[string]any)["DomainId"].(string)
@@ -165,7 +167,7 @@ func TestHandler_UpdateSpace(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["SpaceArn"])
}
diff --git a/services/sagemaker/handler_studio_lifecycle_configs.go b/services/sagemaker/handler_studio_lifecycle_configs.go
index dba90be8fe..67040238b1 100644
--- a/services/sagemaker/handler_studio_lifecycle_configs.go
+++ b/services/sagemaker/handler_studio_lifecycle_configs.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -22,7 +24,7 @@ type createStudioLifecycleConfigInput struct {
func (h *Handler) handleCreateStudioLifecycleConfig(ctx context.Context, body []byte) ([]byte, error) {
var req createStudioLifecycleConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -42,7 +44,7 @@ func (h *Handler) handleCreateStudioLifecycleConfig(ctx context.Context, body []
return nil, err
}
- return json.Marshal(map[string]any{keyStudioLifecycleConfigArn: result.StudioLifecycleConfigArn})
+ return json.Marshal(map[string]any{keyStudioLifecycleConfigArn: result.StudioLifecycleConfigArn}, jsonx.V1())
}
// describeStudioLifecycleConfigInput mirrors DescribeStudioLifecycleConfigInput
@@ -54,7 +56,7 @@ type describeStudioLifecycleConfigInput struct {
func (h *Handler) handleDescribeStudioLifecycleConfig(ctx context.Context, body []byte) ([]byte, error) {
var req describeStudioLifecycleConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -67,7 +69,7 @@ func (h *Handler) handleDescribeStudioLifecycleConfig(ctx context.Context, body
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// deleteStudioLifecycleConfigInput mirrors DeleteStudioLifecycleConfigInput
@@ -79,7 +81,7 @@ type deleteStudioLifecycleConfigInput struct {
func (h *Handler) handleDeleteStudioLifecycleConfig(ctx context.Context, body []byte) error {
var req deleteStudioLifecycleConfigInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -110,7 +112,7 @@ type listStudioLifecycleConfigsInput struct {
func (h *Handler) handleListStudioLifecycleConfigs(ctx context.Context, body []byte) ([]byte, error) {
var req listStudioLifecycleConfigsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_studio_lifecycle_configs_test.go b/services/sagemaker/handler_studio_lifecycle_configs_test.go
index e24af8cc23..2c512db2f2 100644
--- a/services/sagemaker/handler_studio_lifecycle_configs_test.go
+++ b/services/sagemaker/handler_studio_lifecycle_configs_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateStudioLifecycleConfig(t *testing.T) {
@@ -25,7 +27,7 @@ func TestHandler_CreateStudioLifecycleConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["StudioLifecycleConfigArn"], "my-lc")
}
@@ -45,7 +47,7 @@ func TestHandler_DescribeStudioLifecycleConfig(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "lc-1", resp["StudioLifecycleConfigName"])
}
@@ -67,7 +69,7 @@ func TestHandler_CreateStudioLifecycleConfig_Content(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "IyEvYmluL2Jhc2g=", resp["StudioLifecycleConfigContent"])
}
@@ -134,7 +136,7 @@ func TestHandler_ListStudioLifecycleConfigs(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["StudioLifecycleConfigs"])
doSageMakerRequest(t, h, "CreateStudioLifecycleConfig", map[string]any{
@@ -144,7 +146,7 @@ func TestHandler_ListStudioLifecycleConfigs(t *testing.T) {
})
rec = doSageMakerRequest(t, h, "ListStudioLifecycleConfigs", map[string]any{})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
configs := resp["StudioLifecycleConfigs"].([]any)
assert.Len(t, configs, 1)
c := configs[0].(map[string]any)
diff --git a/services/sagemaker/handler_tags.go b/services/sagemaker/handler_tags.go
index e925d9cce8..2d366166e5 100644
--- a/services/sagemaker/handler_tags.go
+++ b/services/sagemaker/handler_tags.go
@@ -2,9 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -29,7 +30,7 @@ type deleteTagsRequest struct {
func (h *Handler) handleAddTags(ctx context.Context, body []byte) ([]byte, error) {
var req addTagsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -55,12 +56,12 @@ func (h *Handler) handleAddTags(ctx context.Context, body []byte) ([]byte, error
return nil, err
}
- return json.Marshal(map[string]any{"Tags": toTagObjects(allTags)})
+ return json.Marshal(map[string]any{"Tags": toTagObjects(allTags)}, jsonx.V1())
}
func (h *Handler) handleListTags(ctx context.Context, body []byte) ([]byte, error) {
var req listTagsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -80,12 +81,12 @@ func (h *Handler) handleListTags(ctx context.Context, body []byte) ([]byte, erro
resp["NextToken"] = outToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
func (h *Handler) handleDeleteTags(ctx context.Context, body []byte) error {
var req deleteTagsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/sagemaker/handler_tags_test.go b/services/sagemaker/handler_tags_test.go
index 4bdbeb828c..8999f8f465 100644
--- a/services/sagemaker/handler_tags_test.go
+++ b/services/sagemaker/handler_tags_test.go
@@ -2,12 +2,14 @@ package sagemaker_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_Tags(t *testing.T) {
@@ -34,7 +36,7 @@ func TestHandler_Tags(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var addResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &addResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &addResp, jsonx.V1()))
addTags, ok := addResp["Tags"].([]any)
require.True(t, ok, "AddTags response body: %s", rec.Body.String())
@@ -47,7 +49,7 @@ func TestHandler_Tags(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags, ok := resp["Tags"].([]any)
require.True(t, ok)
@@ -64,7 +66,7 @@ func TestHandler_Tags(t *testing.T) {
rec = doSageMakerRequest(t, h, "ListTags", map[string]any{
"ResourceArn": m.ModelARN,
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags, ok = resp["Tags"].([]any)
require.True(t, ok)
@@ -203,7 +205,7 @@ func TestHandler_ListTags_MaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
page, ok := resp["Tags"].([]any)
require.True(t, ok)
diff --git a/services/sagemaker/handler_test.go b/services/sagemaker/handler_test.go
index f627737373..2089a2d263 100644
--- a/services/sagemaker/handler_test.go
+++ b/services/sagemaker/handler_test.go
@@ -2,7 +2,7 @@ package sagemaker_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -32,7 +33,7 @@ func doSageMakerRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -246,7 +247,7 @@ func TestHandler_SageMakerReset(t *testing.T) {
require.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
require.NotEmpty(t, listOut["Models"])
// Reset.
@@ -257,7 +258,7 @@ func TestHandler_SageMakerReset(t *testing.T) {
require.Equal(t, http.StatusOK, recList2.Code)
var listOut2 map[string]any
- require.NoError(t, json.Unmarshal(recList2.Body.Bytes(), &listOut2))
+ require.NoError(t, json.Unmarshal(recList2.Body.Bytes(), &listOut2, jsonx.V1()))
assert.Empty(t, listOut2["Models"])
// Feature groups gone.
@@ -265,7 +266,7 @@ func TestHandler_SageMakerReset(t *testing.T) {
require.Equal(t, http.StatusOK, recFG.Code)
var fgOut map[string]any
- require.NoError(t, json.Unmarshal(recFG.Body.Bytes(), &fgOut))
+ require.NoError(t, json.Unmarshal(recFG.Body.Bytes(), &fgOut, jsonx.V1()))
assert.Empty(t, fgOut["FeatureGroupSummaries"])
}
diff --git a/services/sagemaker/handler_training_jobs.go b/services/sagemaker/handler_training_jobs.go
index 2c2f37a5ad..f5eda7cac6 100644
--- a/services/sagemaker/handler_training_jobs.go
+++ b/services/sagemaker/handler_training_jobs.go
@@ -2,9 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -43,7 +45,7 @@ type createTrainingJobFullRequest struct {
func (h *Handler) handleCreateTrainingJobFull(ctx context.Context, body []byte) ([]byte, error) {
var req createTrainingJobFullRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.TrainingJobName == "" {
@@ -110,7 +112,7 @@ func (h *Handler) handleCreateTrainingJobFull(ctx context.Context, body []byte)
log := logger.Load(ctx)
log.InfoContext(ctx, "sagemaker: created training job (full)", "name", tj.TrainingJobName)
- return json.Marshal(map[string]string{keyTrainingJobArn: tj.TrainingJobArn})
+ return json.Marshal(map[string]string{keyTrainingJobArn: tj.TrainingJobArn}, jsonx.V1())
}
// describeTrainingJobInput mirrors DescribeTrainingJobInput (api_op_DescribeTrainingJob.go:29-36).
@@ -127,18 +129,18 @@ func trainingJobPassthroughKeys() []string {
}
}
-func trainingJobPassthroughConfigs(body []byte) (map[string]json.RawMessage, error) {
- var all map[string]json.RawMessage
- if err := json.Unmarshal(body, &all); err != nil {
+func trainingJobPassthroughConfigs(body []byte) (map[string]jsontext.Value, error) {
+ var all map[string]jsontext.Value
+ if err := json.Unmarshal(body, &all, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
- var out map[string]json.RawMessage
+ var out map[string]jsontext.Value
for _, k := range trainingJobPassthroughKeys() {
if v, ok := all[k]; ok && string(v) != "null" {
if out == nil {
- out = make(map[string]json.RawMessage)
+ out = make(map[string]jsontext.Value)
}
out[k] = v
@@ -150,7 +152,7 @@ func trainingJobPassthroughConfigs(body []byte) (map[string]json.RawMessage, err
func (h *Handler) handleDescribeTrainingJobFull(ctx context.Context, body []byte) ([]byte, error) {
var req describeTrainingJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.TrainingJobName == "" {
@@ -181,7 +183,7 @@ func (h *Handler) handleDescribeTrainingJobFull(ctx context.Context, body []byte
}
addTrainingJobOptionalFields(resp, tj)
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
func addTrainingJobOptionalFields(resp map[string]any, tj *TrainingJob) {
@@ -252,7 +254,7 @@ type stopTrainingJobInput struct {
func (h *Handler) handleStopTrainingJobFSM(ctx context.Context, body []byte) error {
var req stopTrainingJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
if req.TrainingJobName == "" {
@@ -287,7 +289,7 @@ type listTrainingJobsInput struct {
func (h *Handler) handleListTrainingJobsFiltered(ctx context.Context, body []byte) ([]byte, error) {
var req listTrainingJobsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -328,7 +330,7 @@ func (h *Handler) handleListTrainingJobsFiltered(ctx context.Context, body []byt
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -356,7 +358,7 @@ type deleteTrainingJobInput struct {
func (h *Handler) handleDeleteTrainingJob(ctx context.Context, body []byte) error {
var req deleteTrainingJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -392,15 +394,15 @@ type resourceConfigForUpdate struct {
type updateTrainingJobInput struct {
ResourceConfig *resourceConfigForUpdate `json:"ResourceConfig"`
TrainingJobName string `json:"TrainingJobName"`
- ProfilerConfig json.RawMessage `json:"ProfilerConfig"`
- ProfilerRuleConfigurations json.RawMessage `json:"ProfilerRuleConfigurations"`
- RemoteDebugConfig json.RawMessage `json:"RemoteDebugConfig"`
+ ProfilerConfig jsontext.Value `json:"ProfilerConfig"`
+ ProfilerRuleConfigurations jsontext.Value `json:"ProfilerRuleConfigurations"`
+ RemoteDebugConfig jsontext.Value `json:"RemoteDebugConfig"`
}
func (h *Handler) handleUpdateTrainingJob(ctx context.Context, body []byte) ([]byte, error) {
var req updateTrainingJobInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -421,5 +423,5 @@ func (h *Handler) handleUpdateTrainingJob(ctx context.Context, body []byte) ([]b
log := logger.Load(ctx)
log.InfoContext(ctx, "sagemaker: updated training job", "name", req.TrainingJobName)
- return json.Marshal(map[string]string{keyTrainingJobArn: tj.TrainingJobArn})
+ return json.Marshal(map[string]string{keyTrainingJobArn: tj.TrainingJobArn}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_training_jobs_test.go b/services/sagemaker/handler_training_jobs_test.go
index 7efd37761f..f326328281 100644
--- a/services/sagemaker/handler_training_jobs_test.go
+++ b/services/sagemaker/handler_training_jobs_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -12,6 +12,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_TrainingJobLifecycle(t *testing.T) {
@@ -43,7 +45,7 @@ func testHandlerTrainingJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recCreate.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(recCreate.Body.Bytes(), &createOut, jsonx.V1()))
assert.NotEmpty(t, createOut["TrainingJobArn"])
// Describe training job.
@@ -58,7 +60,7 @@ func testHandlerTrainingJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut["TrainingJobSummaries"].([]any), 1)
// StopTrainingJob.
diff --git a/services/sagemaker/handler_training_plan.go b/services/sagemaker/handler_training_plan.go
index fc79577bf0..72986bda32 100644
--- a/services/sagemaker/handler_training_plan.go
+++ b/services/sagemaker/handler_training_plan.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// trainingPlanExtOpsSupported returns the real stateful operations implemented
@@ -150,7 +152,7 @@ type listTrainingPlansInput struct {
func (h *Handler) handleListTrainingPlans(ctx context.Context, body []byte) ([]byte, error) {
var req listTrainingPlansInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -207,7 +209,7 @@ type searchTrainingPlanOfferingsInput struct {
func (h *Handler) handleSearchTrainingPlanOfferings(ctx context.Context, body []byte) ([]byte, error) {
var req searchTrainingPlanOfferingsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -241,7 +243,7 @@ func (h *Handler) handleSearchTrainingPlanOfferings(ctx context.Context, body []
return json.Marshal(map[string]any{
"TrainingPlanOfferings": items,
"TrainingPlanExtensionOfferings": extOfferings,
- })
+ }, jsonx.V1())
}
// extendTrainingPlanInput mirrors ExtendTrainingPlanInput
@@ -254,7 +256,7 @@ type extendTrainingPlanInput struct {
func (h *Handler) handleExtendTrainingPlan(ctx context.Context, body []byte) ([]byte, error) {
var req extendTrainingPlanInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -267,7 +269,7 @@ func (h *Handler) handleExtendTrainingPlan(ctx context.Context, body []byte) ([]
return nil, err
}
- return json.Marshal(map[string]any{"TrainingPlanExtensions": extensions})
+ return json.Marshal(map[string]any{"TrainingPlanExtensions": extensions}, jsonx.V1())
}
// describeTrainingPlanExtensionHistoryInput mirrors
@@ -283,7 +285,7 @@ type describeTrainingPlanExtensionHistoryInput struct {
func (h *Handler) handleDescribeTrainingPlanExtensionHistory(ctx context.Context, body []byte) ([]byte, error) {
var req describeTrainingPlanExtensionHistoryInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -298,7 +300,7 @@ func (h *Handler) handleDescribeTrainingPlanExtensionHistory(ctx context.Context
return nil, err
}
- return json.Marshal(map[string]any{"TrainingPlanExtensions": extensions, keyNextToken: next})
+ return json.Marshal(map[string]any{"TrainingPlanExtensions": extensions, keyNextToken: next}, jsonx.V1())
}
// describeReservedCapacityInput mirrors DescribeReservedCapacityInput
@@ -311,7 +313,7 @@ type describeReservedCapacityInput struct {
func (h *Handler) handleDescribeReservedCapacity(ctx context.Context, body []byte) ([]byte, error) {
var req describeReservedCapacityInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -324,7 +326,7 @@ func (h *Handler) handleDescribeReservedCapacity(ctx context.Context, body []byt
return nil, err
}
- return json.Marshal(rc)
+ return json.Marshal(rc, jsonx.V1())
}
// listUltraServersByReservedCapacityInput mirrors
@@ -340,7 +342,7 @@ type listUltraServersByReservedCapacityInput struct {
func (h *Handler) handleListUltraServersByReservedCapacity(ctx context.Context, body []byte) ([]byte, error) {
var req listUltraServersByReservedCapacityInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -355,5 +357,5 @@ func (h *Handler) handleListUltraServersByReservedCapacity(ctx context.Context,
return nil, err
}
- return json.Marshal(map[string]any{"UltraServers": servers, keyNextToken: next})
+ return json.Marshal(map[string]any{"UltraServers": servers, keyNextToken: next}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_training_plan_test.go b/services/sagemaker/handler_training_plan_test.go
index 8454478e5e..2660952f2d 100644
--- a/services/sagemaker/handler_training_plan_test.go
+++ b/services/sagemaker/handler_training_plan_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -11,6 +11,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_TrainingPlan_OfferingLifecycle(t *testing.T) {
@@ -24,7 +26,7 @@ func TestHandler_TrainingPlan_OfferingLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, searchRec.Code)
var searchResp map[string]any
- require.NoError(t, json.Unmarshal(searchRec.Body.Bytes(), &searchResp))
+ require.NoError(t, json.Unmarshal(searchRec.Body.Bytes(), &searchResp, jsonx.V1()))
offerings, ok := searchResp["TrainingPlanOfferings"].([]any)
require.True(t, ok)
require.NotEmpty(t, offerings)
@@ -46,7 +48,7 @@ func TestHandler_TrainingPlan_OfferingLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, describeRec.Code)
var describeResp map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp, jsonx.V1()))
assert.Equal(t, "Scheduled", describeResp["Status"])
assert.InEpsilon(t, float64(720), describeResp["DurationHours"], 0.001)
@@ -68,7 +70,7 @@ func TestHandler_TrainingPlan_OfferingLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rcRec.Code)
var rcResp map[string]any
- require.NoError(t, json.Unmarshal(rcRec.Body.Bytes(), &rcResp))
+ require.NoError(t, json.Unmarshal(rcRec.Body.Bytes(), &rcResp, jsonx.V1()))
assert.Equal(t, "ml.p5.48xlarge", rcResp["InstanceType"])
assert.InEpsilon(t, float64(8), rcResp["TotalInstanceCount"], 0.001)
@@ -77,7 +79,7 @@ func TestHandler_TrainingPlan_OfferingLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
summaries, ok := listResp["TrainingPlanSummaries"].([]any)
require.True(t, ok)
assert.Len(t, summaries, 1)
@@ -89,7 +91,7 @@ func TestHandler_TrainingPlan_OfferingLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, extSearchRec.Code)
var extSearchResp map[string]any
- require.NoError(t, json.Unmarshal(extSearchRec.Body.Bytes(), &extSearchResp))
+ require.NoError(t, json.Unmarshal(extSearchRec.Body.Bytes(), &extSearchResp, jsonx.V1()))
extOfferings, ok := extSearchResp["TrainingPlanExtensionOfferings"].([]any)
require.True(t, ok)
require.NotEmpty(t, extOfferings)
@@ -105,7 +107,7 @@ func TestHandler_TrainingPlan_OfferingLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, extendRec.Code)
var extendResp map[string]any
- require.NoError(t, json.Unmarshal(extendRec.Body.Bytes(), &extendResp))
+ require.NoError(t, json.Unmarshal(extendRec.Body.Bytes(), &extendResp, jsonx.V1()))
extensions, ok := extendResp["TrainingPlanExtensions"].([]any)
require.True(t, ok)
assert.Len(t, extensions, 1)
@@ -116,7 +118,7 @@ func TestHandler_TrainingPlan_OfferingLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, historyRec.Code)
var historyResp map[string]any
- require.NoError(t, json.Unmarshal(historyRec.Body.Bytes(), &historyResp))
+ require.NoError(t, json.Unmarshal(historyRec.Body.Bytes(), &historyResp, jsonx.V1()))
historyExtensions, ok := historyResp["TrainingPlanExtensions"].([]any)
require.True(t, ok)
assert.Len(t, historyExtensions, 1)
@@ -127,7 +129,7 @@ func TestHandler_TrainingPlan_OfferingLifecycle(t *testing.T) {
})
var describeAfterResp map[string]any
- require.NoError(t, json.Unmarshal(describeAfterRec.Body.Bytes(), &describeAfterResp))
+ require.NoError(t, json.Unmarshal(describeAfterRec.Body.Bytes(), &describeAfterResp, jsonx.V1()))
assert.Greater(t, describeAfterResp["DurationHours"], describeResp["DurationHours"])
}
@@ -141,7 +143,7 @@ func TestHandler_TrainingPlan_UltraServerOffering(t *testing.T) {
})
var searchResp map[string]any
- require.NoError(t, json.Unmarshal(searchRec.Body.Bytes(), &searchResp))
+ require.NoError(t, json.Unmarshal(searchRec.Body.Bytes(), &searchResp, jsonx.V1()))
offerings, ok := searchResp["TrainingPlanOfferings"].([]any)
require.True(t, ok)
require.NotEmpty(t, offerings)
@@ -160,7 +162,7 @@ func TestHandler_TrainingPlan_UltraServerOffering(t *testing.T) {
})
var describeResp map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &describeResp, jsonx.V1()))
rcSummaries, ok := describeResp["ReservedCapacitySummaries"].([]any)
require.True(t, ok)
require.Len(t, rcSummaries, 1)
@@ -176,7 +178,7 @@ func TestHandler_TrainingPlan_UltraServerOffering(t *testing.T) {
assert.Equal(t, http.StatusOK, ultraRec.Code)
var ultraResp map[string]any
- require.NoError(t, json.Unmarshal(ultraRec.Body.Bytes(), &ultraResp))
+ require.NoError(t, json.Unmarshal(ultraRec.Body.Bytes(), &ultraResp, jsonx.V1()))
servers, ok := ultraResp["UltraServers"].([]any)
require.True(t, ok)
assert.Len(t, servers, 1)
diff --git a/services/sagemaker/handler_training_plans.go b/services/sagemaker/handler_training_plans.go
index 27a815b67d..735943b5ac 100644
--- a/services/sagemaker/handler_training_plans.go
+++ b/services/sagemaker/handler_training_plans.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -25,7 +27,7 @@ type createTrainingPlanInput struct {
func (h *Handler) handleCreateTrainingPlan(ctx context.Context, body []byte) ([]byte, error) {
var req createTrainingPlanInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -45,7 +47,7 @@ func (h *Handler) handleCreateTrainingPlan(ctx context.Context, body []byte) ([]
return nil, err
}
- return json.Marshal(map[string]any{keyTrainingPlanArn: result.TrainingPlanArn})
+ return json.Marshal(map[string]any{keyTrainingPlanArn: result.TrainingPlanArn}, jsonx.V1())
}
// describeTrainingPlanInput mirrors DescribeTrainingPlanInput
@@ -58,7 +60,7 @@ type describeTrainingPlanInput struct {
func (h *Handler) handleDescribeTrainingPlan(ctx context.Context, body []byte) ([]byte, error) {
var req describeTrainingPlanInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -71,5 +73,5 @@ func (h *Handler) handleDescribeTrainingPlan(ctx context.Context, body []byte) (
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
diff --git a/services/sagemaker/handler_training_plans_test.go b/services/sagemaker/handler_training_plans_test.go
index 1ea544a645..0968dd25c5 100644
--- a/services/sagemaker/handler_training_plans_test.go
+++ b/services/sagemaker/handler_training_plans_test.go
@@ -1,12 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateTrainingPlan(t *testing.T) {
@@ -21,7 +23,7 @@ func TestHandler_CreateTrainingPlan(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["TrainingPlanArn"], "my-plan")
}
@@ -38,7 +40,7 @@ func TestHandler_DescribeTrainingPlan(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "plan-1", resp["TrainingPlanName"])
// TargetResources/TotalInstanceCount/UpfrontFee were previously tagged
diff --git a/services/sagemaker/handler_transform_jobs.go b/services/sagemaker/handler_transform_jobs.go
index 62026d07e6..3f986c4f67 100644
--- a/services/sagemaker/handler_transform_jobs.go
+++ b/services/sagemaker/handler_transform_jobs.go
@@ -2,9 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -32,7 +33,7 @@ type createTransformJobRequest struct {
func (h *Handler) handleCreateTransformJob(ctx context.Context, body []byte) ([]byte, error) {
var req createTransformJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -94,7 +95,7 @@ func (h *Handler) handleCreateTransformJob(ctx context.Context, body []byte) ([]
logger.Load(ctx).InfoContext(ctx, "sagemaker: created transform job", "name", tj.TransformJobName)
- return json.Marshal(map[string]string{"TransformJobArn": tj.TransformJobArn})
+ return json.Marshal(map[string]string{"TransformJobArn": tj.TransformJobArn}, jsonx.V1())
}
type describeTransformJobRequest struct {
@@ -104,7 +105,7 @@ type describeTransformJobRequest struct {
func (h *Handler) handleDescribeTransformJob(ctx context.Context, body []byte) ([]byte, error) {
var req describeTransformJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -156,7 +157,7 @@ func (h *Handler) handleDescribeTransformJob(ctx context.Context, body []byte) (
resp["ModelClientConfig"] = tj.ModelClientConfig
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type stopTransformJobRequest struct {
@@ -166,7 +167,7 @@ type stopTransformJobRequest struct {
func (h *Handler) handleStopTransformJob(ctx context.Context, body []byte) error {
var req stopTransformJobRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -212,7 +213,7 @@ type listTransformJobsRequest struct {
func (h *Handler) handleListTransformJobs(ctx context.Context, body []byte) ([]byte, error) {
var req listTransformJobsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -252,5 +253,5 @@ func (h *Handler) handleListTransformJobs(ctx context.Context, body []byte) ([]b
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/sagemaker/handler_transform_jobs_test.go b/services/sagemaker/handler_transform_jobs_test.go
index edf1955d24..3e93533214 100644
--- a/services/sagemaker/handler_transform_jobs_test.go
+++ b/services/sagemaker/handler_transform_jobs_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -50,7 +51,7 @@ func TestHandler_TransformJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.NotEmpty(t, createResp["TransformJobArn"])
// Describe — InProgress initially
@@ -60,7 +61,7 @@ func TestHandler_TransformJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "my-transform", descResp["TransformJobName"])
assert.Equal(t, "my-model", descResp["ModelName"])
assert.Equal(t, "InProgress", descResp["TransformJobStatus"])
@@ -71,7 +72,7 @@ func TestHandler_TransformJobLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
summaries := listResp["TransformJobSummaries"].([]any)
assert.Len(t, summaries, 1)
@@ -81,7 +82,7 @@ func TestHandler_TransformJobLifecycle(t *testing.T) {
rec = doSageMakerRequest(t, h, "DescribeTransformJob", map[string]any{
"TransformJobName": "my-transform",
})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Completed", descResp["TransformJobStatus"])
})
}
@@ -147,7 +148,7 @@ func TestHandler_CreateTransformJob_ModelNotFound(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFound", errResp["__type"])
rec = doSageMakerRequest(t, h, "DescribeTransformJob", map[string]any{
@@ -185,7 +186,7 @@ func TestHandler_StopTransformJob(t *testing.T) {
"TransformJobName": "stop-me",
})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Stopping", descResp["TransformJobStatus"])
}
@@ -219,7 +220,7 @@ func TestHandler_ListTransformJobs_StatusFilter(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
summaries := listResp["TransformJobSummaries"].([]any)
assert.Len(t, summaries, 1)
assert.Equal(t, "tj-2", summaries[0].(map[string]any)["TransformJobName"])
@@ -253,7 +254,7 @@ func TestHandler_ListTransformJobs_SortByName(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["TransformJobSummaries"].([]any)
require.Len(t, summaries, 2)
assert.Equal(t, "tj-alpha", summaries[0].(map[string]any)["TransformJobName"])
@@ -341,7 +342,7 @@ func TestHandler_CreateTransformJob_RoleArnNotPartOfWireShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
_, present := descResp["RoleArn"]
assert.False(t, present, "RoleArn is not a real CreateTransformJobInput/DescribeTransformJobOutput field")
}
@@ -482,7 +483,7 @@ func TestHandler_Tags_TransformJob(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
jobARN := createResp["TransformJobArn"]
require.NotEmpty(t, jobARN)
@@ -492,7 +493,7 @@ func TestHandler_Tags_TransformJob(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags := tagsResp["Tags"].([]any)
require.Len(t, tags, 1)
assert.Equal(t, "owner", tags[0].(map[string]any)["Key"])
diff --git a/services/sagemaker/handler_trial_components.go b/services/sagemaker/handler_trial_components.go
index 6f5f5b0bd6..723ba00523 100644
--- a/services/sagemaker/handler_trial_components.go
+++ b/services/sagemaker/handler_trial_components.go
@@ -2,9 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -31,7 +32,7 @@ type createTrialComponentInput struct {
func (h *Handler) handleCreateTrialComponent(ctx context.Context, body []byte) ([]byte, error) {
var req createTrialComponentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -58,7 +59,7 @@ func (h *Handler) handleCreateTrialComponent(ctx context.Context, body []byte) (
logger.Load(ctx).
InfoContext(ctx, "sagemaker: created trial component", "name", tc.TrialComponentName)
- return json.Marshal(map[string]string{keyTrialComponentArn: tc.TrialComponentArn})
+ return json.Marshal(map[string]string{keyTrialComponentArn: tc.TrialComponentArn}, jsonx.V1())
}
// describeTrialComponentInput mirrors DescribeTrialComponentInput
@@ -78,7 +79,7 @@ type describeTrialComponentInput struct {
func (h *Handler) handleDescribeTrialComponent(ctx context.Context, body []byte) ([]byte, error) {
var req describeTrialComponentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -129,7 +130,7 @@ func (h *Handler) handleDescribeTrialComponent(ctx context.Context, body []byte)
resp["LastModifiedBy"] = tc.LastModifiedBy
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// deleteTrialComponentInput mirrors DeleteTrialComponentInput
@@ -142,7 +143,7 @@ type deleteTrialComponentInput struct {
func (h *Handler) handleDeleteTrialComponent(ctx context.Context, body []byte) ([]byte, error) {
var req deleteTrialComponentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -158,7 +159,7 @@ func (h *Handler) handleDeleteTrialComponent(ctx context.Context, body []byte) (
logger.Load(ctx).
InfoContext(ctx, "sagemaker: deleted trial component", "name", req.TrialComponentName)
- return json.Marshal(map[string]string{keyTrialComponentArn: tc.TrialComponentArn})
+ return json.Marshal(map[string]string{keyTrialComponentArn: tc.TrialComponentArn}, jsonx.V1())
}
// ---------------------------------------------------------------------------
@@ -176,7 +177,7 @@ type disassociateTrialComponentInput struct {
func (h *Handler) handleDisassociateTrialComponent(ctx context.Context, body []byte) ([]byte, error) {
var req disassociateTrialComponentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -198,7 +199,7 @@ func (h *Handler) handleDisassociateTrialComponent(ctx context.Context, body []b
return json.Marshal(map[string]string{
keyTrialArn: trialArn,
keyTrialComponentArn: trialComponentArn,
- })
+ }, jsonx.V1())
}
// listTrialComponentsInput mirrors ListTrialComponentsInput
@@ -220,7 +221,7 @@ type listTrialComponentsInput struct {
func (h *Handler) handleListTrialComponents(ctx context.Context, body []byte) ([]byte, error) {
var req listTrialComponentsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -283,7 +284,7 @@ type updateTrialComponentInput struct {
func (h *Handler) handleUpdateTrialComponent(ctx context.Context, body []byte) ([]byte, error) {
var req updateTrialComponentInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -316,5 +317,5 @@ func (h *Handler) handleUpdateTrialComponent(ctx context.Context, body []byte) (
tc.TrialComponentName,
)
- return json.Marshal(map[string]string{keyTrialComponentArn: tc.TrialComponentArn})
+ return json.Marshal(map[string]string{keyTrialComponentArn: tc.TrialComponentArn}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_trial_components_test.go b/services/sagemaker/handler_trial_components_test.go
index 8f753730e7..cc7c647a12 100644
--- a/services/sagemaker/handler_trial_components_test.go
+++ b/services/sagemaker/handler_trial_components_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -11,6 +11,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_ListAndDisassociateTrialComponents(t *testing.T) {
@@ -31,7 +33,7 @@ func TestHandler_ListAndDisassociateTrialComponents(t *testing.T) {
require.Equal(t, http.StatusOK, recByTrial.Code)
var byTrial map[string]any
- require.NoError(t, json.Unmarshal(recByTrial.Body.Bytes(), &byTrial))
+ require.NoError(t, json.Unmarshal(recByTrial.Body.Bytes(), &byTrial, jsonx.V1()))
summaries, _ := byTrial["TrialComponentSummaries"].([]any)
require.Len(t, summaries, 1)
assert.Equal(t, "tc-a", summaries[0].(map[string]any)["TrialComponentName"])
@@ -41,7 +43,7 @@ func TestHandler_ListAndDisassociateTrialComponents(t *testing.T) {
require.Equal(t, http.StatusOK, recByExp.Code)
var byExp map[string]any
- require.NoError(t, json.Unmarshal(recByExp.Body.Bytes(), &byExp))
+ require.NoError(t, json.Unmarshal(recByExp.Body.Bytes(), &byExp, jsonx.V1()))
expSummaries, _ := byExp["TrialComponentSummaries"].([]any)
require.Len(t, expSummaries, 1)
@@ -50,7 +52,7 @@ func TestHandler_ListAndDisassociateTrialComponents(t *testing.T) {
require.Equal(t, http.StatusOK, recAll.Code)
var all map[string]any
- require.NoError(t, json.Unmarshal(recAll.Body.Bytes(), &all))
+ require.NoError(t, json.Unmarshal(recAll.Body.Bytes(), &all, jsonx.V1()))
allSummaries, _ := all["TrialComponentSummaries"].([]any)
assert.Len(t, allSummaries, 2)
@@ -61,7 +63,7 @@ func TestHandler_ListAndDisassociateTrialComponents(t *testing.T) {
require.Equal(t, http.StatusOK, recDisassoc.Code)
var disassocOut map[string]any
- require.NoError(t, json.Unmarshal(recDisassoc.Body.Bytes(), &disassocOut))
+ require.NoError(t, json.Unmarshal(recDisassoc.Body.Bytes(), &disassocOut, jsonx.V1()))
assert.NotEmpty(t, disassocOut["TrialArn"])
assert.NotEmpty(t, disassocOut["TrialComponentArn"])
@@ -69,7 +71,7 @@ func TestHandler_ListAndDisassociateTrialComponents(t *testing.T) {
require.Equal(t, http.StatusOK, recByTrial2.Code)
var byTrial2 map[string]any
- require.NoError(t, json.Unmarshal(recByTrial2.Body.Bytes(), &byTrial2))
+ require.NoError(t, json.Unmarshal(recByTrial2.Body.Bytes(), &byTrial2, jsonx.V1()))
assert.Empty(t, byTrial2["TrialComponentSummaries"])
}
@@ -87,7 +89,7 @@ func TestHandler_DisassociateTrialComponent_Idempotent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out["TrialArn"], "never-existed")
}
@@ -173,7 +175,7 @@ func TestHandler_UpdateTrialComponent(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.NotEmpty(t, updateResp["TrialComponentArn"])
// Describe returns updated fields
@@ -181,7 +183,7 @@ func TestHandler_UpdateTrialComponent(t *testing.T) {
"TrialComponentName": "my-tc",
})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "TC Display", descResp["DisplayName"])
assert.Equal(t, "InProgress", descResp["Status"].(map[string]any)["PrimaryStatus"])
assert.NotNil(t, descResp["Parameters"])
@@ -218,7 +220,7 @@ func TestHandler_CreateTrialComponent_FullFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Training Run 1", descResp["DisplayName"])
assert.InEpsilon(t, float64(1700000000), descResp["StartTime"], 0)
assert.InEpsilon(t, float64(1700003600), descResp["EndTime"], 0)
diff --git a/services/sagemaker/handler_trials.go b/services/sagemaker/handler_trials.go
index 8f605e9eff..4ff655bacc 100644
--- a/services/sagemaker/handler_trials.go
+++ b/services/sagemaker/handler_trials.go
@@ -2,9 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -24,7 +25,7 @@ type createTrialInput struct {
func (h *Handler) handleCreateTrial(ctx context.Context, body []byte) ([]byte, error) {
var req createTrialInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -41,7 +42,7 @@ func (h *Handler) handleCreateTrial(ctx context.Context, body []byte) ([]byte, e
logger.Load(ctx).InfoContext(ctx, "sagemaker: created trial", "name", t.TrialName)
- return json.Marshal(map[string]string{keyTrialArn: t.TrialArn})
+ return json.Marshal(map[string]string{keyTrialArn: t.TrialArn}, jsonx.V1())
}
// describeTrialInput mirrors DescribeTrialInput (api_op_DescribeTrial.go:29-36).
@@ -52,7 +53,7 @@ type describeTrialInput struct {
func (h *Handler) handleDescribeTrial(ctx context.Context, body []byte) ([]byte, error) {
var req describeTrialInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -86,7 +87,7 @@ func (h *Handler) handleDescribeTrial(ctx context.Context, body []byte) ([]byte,
resp["LastModifiedBy"] = t.LastModifiedBy
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type trialSummary struct {
@@ -112,7 +113,7 @@ type listTrialsInput struct {
func (h *Handler) handleListTrials(ctx context.Context, body []byte) ([]byte, error) {
var req listTrialsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -142,7 +143,7 @@ func (h *Handler) handleListTrials(ctx context.Context, body []byte) ([]byte, er
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// deleteTrialInput mirrors DeleteTrialInput (api_op_DeleteTrial.go:30-37).
@@ -153,7 +154,7 @@ type deleteTrialInput struct {
func (h *Handler) handleDeleteTrial(ctx context.Context, body []byte) ([]byte, error) {
var req deleteTrialInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -168,7 +169,7 @@ func (h *Handler) handleDeleteTrial(ctx context.Context, body []byte) ([]byte, e
logger.Load(ctx).InfoContext(ctx, "sagemaker: deleted trial", "name", req.TrialName)
- return json.Marshal(map[string]string{keyTrialArn: t.TrialArn})
+ return json.Marshal(map[string]string{keyTrialArn: t.TrialArn}, jsonx.V1())
}
// updateTrialInput mirrors UpdateTrialInput (api_op_UpdateTrial.go:27-39).
@@ -180,7 +181,7 @@ type updateTrialInput struct {
func (h *Handler) handleUpdateTrial(ctx context.Context, body []byte) ([]byte, error) {
var req updateTrialInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -195,5 +196,5 @@ func (h *Handler) handleUpdateTrial(ctx context.Context, body []byte) ([]byte, e
logger.Load(ctx).InfoContext(ctx, "sagemaker: updated trial", "name", t.TrialName)
- return json.Marshal(map[string]string{keyTrialArn: t.TrialArn})
+ return json.Marshal(map[string]string{keyTrialArn: t.TrialArn}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_trials_test.go b/services/sagemaker/handler_trials_test.go
index acbede9677..267027f82f 100644
--- a/services/sagemaker/handler_trials_test.go
+++ b/services/sagemaker/handler_trials_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -11,6 +11,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_TrialLifecycle(t *testing.T) {
@@ -44,7 +46,7 @@ func TestHandler_TrialLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut["TrialSummaries"].([]any), 1)
// Delete trial.
@@ -70,13 +72,13 @@ func TestHandler_UpdateTrial(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.NotEmpty(t, updateResp["TrialArn"])
// Describe returns updated DisplayName
rec = doSageMakerRequest(t, h, "DescribeTrial", map[string]any{"TrialName": "my-trial"})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "Trial Display", descResp["DisplayName"])
}
@@ -98,14 +100,14 @@ func TestHandler_CreateTrial_DisplayName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, "My Trial", descResp["DisplayName"])
rec = doSageMakerRequest(t, h, "ListTrials", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
summaries := listResp["TrialSummaries"].([]any)
require.Len(t, summaries, 1)
assert.Equal(t, "My Trial", summaries[0].(map[string]any)["DisplayName"])
@@ -264,7 +266,7 @@ func TestHandler_ListTrials_TrialComponentName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["TrialSummaries"].([]any)
require.Len(t, summaries, 1)
assert.Equal(t, "trial-with-tc", summaries[0].(map[string]any)["TrialName"])
diff --git a/services/sagemaker/handler_user_profiles.go b/services/sagemaker/handler_user_profiles.go
index d59665daca..43d576b06a 100644
--- a/services/sagemaker/handler_user_profiles.go
+++ b/services/sagemaker/handler_user_profiles.go
@@ -2,9 +2,11 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -17,18 +19,18 @@ import (
// it — see gopherstack-oc9v). UserSettings is carried as opaque
// json.RawMessage passthrough per this file's established convention.
type createUserProfileInput struct {
- DomainID string `json:"DomainId"`
- UserProfileName string `json:"UserProfileName"`
- SingleSignOnUserIdentifier string `json:"SingleSignOnUserIdentifier"`
- SingleSignOnUserValue string `json:"SingleSignOnUserValue"`
- UserSettings json.RawMessage `json:"UserSettings"`
- Tags []tagObject `json:"Tags"`
+ DomainID string `json:"DomainId"`
+ UserProfileName string `json:"UserProfileName"`
+ SingleSignOnUserIdentifier string `json:"SingleSignOnUserIdentifier"`
+ SingleSignOnUserValue string `json:"SingleSignOnUserValue"`
+ UserSettings jsontext.Value `json:"UserSettings"`
+ Tags []tagObject `json:"Tags"`
}
func (h *Handler) handleCreateUserProfile(ctx context.Context, body []byte) ([]byte, error) {
var req createUserProfileInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -61,7 +63,7 @@ func (h *Handler) handleCreateUserProfile(ctx context.Context, body []byte) ([]b
logger.Load(ctx).InfoContext(ctx, "sagemaker: created user profile", "name", up.UserProfileName)
- return json.Marshal(map[string]string{keyUserProfileArn: up.UserProfileArn})
+ return json.Marshal(map[string]string{keyUserProfileArn: up.UserProfileArn}, jsonx.V1())
}
type describeUserProfileInput struct {
@@ -72,7 +74,7 @@ type describeUserProfileInput struct {
func (h *Handler) handleDescribeUserProfile(ctx context.Context, body []byte) ([]byte, error) {
var req describeUserProfileInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -110,7 +112,7 @@ func (h *Handler) handleDescribeUserProfile(ctx context.Context, body []byte) ([
resp["UserSettings"] = up.UserSettings
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type userProfileSummary struct {
@@ -133,7 +135,7 @@ type listUserProfilesInput struct {
func (h *Handler) handleListUserProfiles(ctx context.Context, body []byte) ([]byte, error) {
var req listUserProfilesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -155,7 +157,7 @@ func (h *Handler) handleListUserProfiles(ctx context.Context, body []byte) ([]by
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type deleteUserProfileInput struct {
@@ -166,7 +168,7 @@ type deleteUserProfileInput struct {
func (h *Handler) handleDeleteUserProfile(ctx context.Context, body []byte) error {
var req deleteUserProfileInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -191,15 +193,15 @@ func (h *Handler) handleDeleteUserProfile(ctx context.Context, body []byte) erro
// updateUserProfileInput mirrors UpdateUserProfileInput
// (api_op_UpdateUserProfile.go:26-38).
type updateUserProfileInput struct {
- DomainID string `json:"DomainId"`
- UserProfileName string `json:"UserProfileName"`
- UserSettings json.RawMessage `json:"UserSettings"`
+ DomainID string `json:"DomainId"`
+ UserProfileName string `json:"UserProfileName"`
+ UserSettings jsontext.Value `json:"UserSettings"`
}
func (h *Handler) handleUpdateUserProfile(ctx context.Context, body []byte) ([]byte, error) {
var req updateUserProfileInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -222,5 +224,5 @@ func (h *Handler) handleUpdateUserProfile(ctx context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(map[string]string{keyUserProfileArn: up.UserProfileArn})
+ return json.Marshal(map[string]string{keyUserProfileArn: up.UserProfileArn}, jsonx.V1())
}
diff --git a/services/sagemaker/handler_user_profiles_test.go b/services/sagemaker/handler_user_profiles_test.go
index bae48b909e..5006a94570 100644
--- a/services/sagemaker/handler_user_profiles_test.go
+++ b/services/sagemaker/handler_user_profiles_test.go
@@ -1,12 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_UserProfileLifecycle(t *testing.T) {
@@ -24,7 +26,7 @@ func TestHandler_UserProfileLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, recDomain.Code)
var domainOut map[string]any
- require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut))
+ require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut, jsonx.V1()))
domainID := domainOut["DomainId"].(string)
// Create user profile.
@@ -51,7 +53,7 @@ func TestHandler_UserProfileLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, recList.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(recList.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut["UserProfiles"].([]any), 1)
// Delete user profile.
@@ -75,7 +77,7 @@ func TestHandler_UserProfile_NotFound(t *testing.T) {
require.Equal(t, http.StatusOK, recDomain.Code)
var domainOut map[string]any
- require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut))
+ require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut, jsonx.V1()))
domainID := domainOut["DomainId"].(string)
for _, op := range []string{"DescribeUserProfile", "DeleteUserProfile"} {
@@ -104,7 +106,7 @@ func TestHandler_UpdateUserProfile(t *testing.T) {
var domainResp map[string]any
rec := doSageMakerRequest(t, h, "ListDomains", map[string]any{})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &domainResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &domainResp, jsonx.V1()))
domains := domainResp["Domains"].([]any)
require.Len(t, domains, 1)
domainID := domains[0].(map[string]any)["DomainId"].(string)
@@ -121,7 +123,7 @@ func TestHandler_UpdateUserProfile(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["UserProfileArn"])
}
@@ -137,7 +139,7 @@ func TestHandler_ListUserProfiles_NameContainsAndMaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, recDomain.Code)
var domainOut map[string]any
- require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut))
+ require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut, jsonx.V1()))
domainID := domainOut["DomainId"].(string)
for _, name := range []string{"alpha-user", "beta-user", "alpha-other"} {
@@ -158,7 +160,7 @@ func TestHandler_ListUserProfiles_NameContainsAndMaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out["UserProfiles"].([]any), 2)
})
@@ -172,7 +174,7 @@ func TestHandler_ListUserProfiles_NameContainsAndMaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out["UserProfiles"].([]any), 1, "MaxResults must cap the page, not just be parsed and ignored")
assert.NotEmpty(t, out["NextToken"])
})
@@ -191,7 +193,7 @@ func TestHandler_CreateUserProfile_SSOFieldsRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, recDomain.Code)
var domainOut map[string]any
- require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut))
+ require.NoError(t, json.Unmarshal(recDomain.Body.Bytes(), &domainOut, jsonx.V1()))
domainID := domainOut["DomainId"].(string)
rec := doSageMakerRequest(t, h, "CreateUserProfile", map[string]any{
@@ -209,7 +211,7 @@ func TestHandler_CreateUserProfile_SSOFieldsRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "UserName", out["SingleSignOnUserIdentifier"])
assert.Equal(t, "jdoe", out["SingleSignOnUserValue"])
}
diff --git a/services/sagemaker/handler_workforces.go b/services/sagemaker/handler_workforces.go
index a807595820..92632138c0 100644
--- a/services/sagemaker/handler_workforces.go
+++ b/services/sagemaker/handler_workforces.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -96,7 +98,7 @@ type createWorkforceInput struct {
func (h *Handler) handleCreateWorkforce(ctx context.Context, body []byte) ([]byte, error) {
var req createWorkforceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -117,7 +119,7 @@ func (h *Handler) handleCreateWorkforce(ctx context.Context, body []byte) ([]byt
return nil, err
}
- return json.Marshal(map[string]any{keyWorkforceArn: result.WorkforceArn})
+ return json.Marshal(map[string]any{keyWorkforceArn: result.WorkforceArn}, jsonx.V1())
}
// describeWorkforceInput mirrors DescribeWorkforceInput (api_op_DescribeWorkforce.go:34-42).
@@ -128,7 +130,7 @@ type describeWorkforceInput struct {
func (h *Handler) handleDescribeWorkforce(ctx context.Context, body []byte) ([]byte, error) {
var req describeWorkforceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -141,7 +143,7 @@ func (h *Handler) handleDescribeWorkforce(ctx context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(map[string]any{"Workforce": workforceResponseMap(result)})
+ return json.Marshal(map[string]any{"Workforce": workforceResponseMap(result)}, jsonx.V1())
}
// updateWorkforceInput mirrors UpdateWorkforceInput (api_op_UpdateWorkforce.go:65-93).
@@ -156,7 +158,7 @@ type updateWorkforceInput struct {
func (h *Handler) handleUpdateWorkforce(ctx context.Context, body []byte) ([]byte, error) {
var req updateWorkforceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -175,7 +177,7 @@ func (h *Handler) handleUpdateWorkforce(ctx context.Context, body []byte) ([]byt
return nil, err
}
- return json.Marshal(map[string]any{"Workforce": workforceResponseMap(result)})
+ return json.Marshal(map[string]any{"Workforce": workforceResponseMap(result)}, jsonx.V1())
}
// deleteWorkforceInput mirrors DeleteWorkforceInput (api_op_DeleteWorkforce.go:39-44).
@@ -186,7 +188,7 @@ type deleteWorkforceInput struct {
func (h *Handler) handleDeleteWorkforce(ctx context.Context, body []byte) ([]byte, error) {
var req deleteWorkforceInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -198,7 +200,7 @@ func (h *Handler) handleDeleteWorkforce(ctx context.Context, body []byte) ([]byt
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
// listWorkforcesInput mirrors ListWorkforcesInput (api_op_ListWorkforces.go:31-48).
@@ -213,7 +215,7 @@ type listWorkforcesInput struct {
func (h *Handler) handleListWorkforces(ctx context.Context, body []byte) ([]byte, error) {
var req listWorkforcesInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -234,5 +236,5 @@ func (h *Handler) handleListWorkforces(ctx context.Context, body []byte) ([]byte
resp[keyNextToken] = next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/sagemaker/handler_workforces_test.go b/services/sagemaker/handler_workforces_test.go
index b5c903db27..dccc49422d 100644
--- a/services/sagemaker/handler_workforces_test.go
+++ b/services/sagemaker/handler_workforces_test.go
@@ -1,12 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateWorkforce(t *testing.T) {
@@ -20,7 +22,7 @@ func TestHandler_CreateWorkforce(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["WorkforceArn"], "my-workforce")
}
@@ -34,7 +36,7 @@ func TestHandler_DescribeWorkforce(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Workforce"])
}
@@ -48,7 +50,7 @@ func TestHandler_UpdateWorkforce(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Workforce"])
}
@@ -66,7 +68,7 @@ func TestHandler_CreateWorkforce_IPAddressType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wf := resp["Workforce"].(map[string]any)
assert.Equal(t, "dualstack", wf["IpAddressType"])
}
@@ -88,7 +90,7 @@ func TestHandler_UpdateWorkforce_IPAddressType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wf := resp["Workforce"].(map[string]any)
assert.Equal(t, "dualstack", wf["IpAddressType"])
}
@@ -118,7 +120,7 @@ func TestHandler_ListWorkforces_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["Workforces"].([]any)
assert.Len(t, items, tc.wantCount)
})
diff --git a/services/sagemaker/handler_workteams.go b/services/sagemaker/handler_workteams.go
index bee8920421..f8bb19d0a7 100644
--- a/services/sagemaker/handler_workteams.go
+++ b/services/sagemaker/handler_workteams.go
@@ -2,8 +2,10 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -59,7 +61,7 @@ type createWorkteamInput struct {
func (h *Handler) handleCreateWorkteam(ctx context.Context, body []byte) ([]byte, error) {
var req createWorkteamInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -91,7 +93,7 @@ func (h *Handler) handleCreateWorkteam(ctx context.Context, body []byte) ([]byte
return nil, err
}
- return json.Marshal(map[string]any{"WorkteamArn": result.WorkteamArn})
+ return json.Marshal(map[string]any{"WorkteamArn": result.WorkteamArn}, jsonx.V1())
}
// describeWorkteamInput mirrors DescribeWorkteamInput (api_op_DescribeWorkteam.go:31-36).
@@ -102,7 +104,7 @@ type describeWorkteamInput struct {
func (h *Handler) handleDescribeWorkteam(ctx context.Context, body []byte) ([]byte, error) {
var req describeWorkteamInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -115,7 +117,7 @@ func (h *Handler) handleDescribeWorkteam(ctx context.Context, body []byte) ([]by
return nil, err
}
- return json.Marshal(map[string]any{"Workteam": workteamResponseMap(result)})
+ return json.Marshal(map[string]any{"Workteam": workteamResponseMap(result)}, jsonx.V1())
}
// updateWorkteamInput mirrors UpdateWorkteamInput (api_op_UpdateWorkteam.go:28-70).
@@ -130,7 +132,7 @@ type updateWorkteamInput struct {
func (h *Handler) handleUpdateWorkteam(ctx context.Context, body []byte) ([]byte, error) {
var req updateWorkteamInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -149,7 +151,7 @@ func (h *Handler) handleUpdateWorkteam(ctx context.Context, body []byte) ([]byte
return nil, err
}
- return json.Marshal(map[string]any{"Workteam": workteamResponseMap(result)})
+ return json.Marshal(map[string]any{"Workteam": workteamResponseMap(result)}, jsonx.V1())
}
// deleteWorkteamInput mirrors DeleteWorkteamInput (api_op_DeleteWorkteam.go:27-32).
@@ -160,7 +162,7 @@ type deleteWorkteamInput struct {
func (h *Handler) handleDeleteWorkteam(ctx context.Context, body []byte) ([]byte, error) {
var req deleteWorkteamInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -172,7 +174,7 @@ func (h *Handler) handleDeleteWorkteam(ctx context.Context, body []byte) ([]byte
return nil, err
}
- return json.Marshal(map[string]any{"Success": true})
+ return json.Marshal(map[string]any{"Success": true}, jsonx.V1())
}
// listWorkteamsInput mirrors ListWorkteamsInput (api_op_ListWorkteams.go:31-51).
@@ -187,7 +189,7 @@ type listWorkteamsInput struct {
func (h *Handler) handleListWorkteams(ctx context.Context, body []byte) ([]byte, error) {
var req listWorkteamsInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -208,5 +210,5 @@ func (h *Handler) handleListWorkteams(ctx context.Context, body []byte) ([]byte,
resp[keyNextToken] = next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/sagemaker/handler_workteams_test.go b/services/sagemaker/handler_workteams_test.go
index 233de8cd5a..545f838d48 100644
--- a/services/sagemaker/handler_workteams_test.go
+++ b/services/sagemaker/handler_workteams_test.go
@@ -1,12 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// defaultMemberDefinitions returns a minimal, real-shaped MemberDefinitions
@@ -33,7 +35,7 @@ func TestHandler_CreateWorkteam(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["WorkteamArn"], "my-team")
}
@@ -50,7 +52,7 @@ func TestHandler_DescribeWorkteam(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wt := resp["Workteam"].(map[string]any)
assert.Equal(t, "team-1", wt["WorkteamName"])
}
@@ -86,7 +88,7 @@ func TestHandler_ListWorkteams(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["Workteams"].([]any)
assert.Len(t, items, 2)
}
@@ -130,7 +132,7 @@ func TestHandler_ListWorkteams_Filters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["Workteams"].([]any)
require.Len(t, items, len(tc.wantNames))
@@ -166,7 +168,7 @@ func TestHandler_CreateWorkteam_NotificationAndWorkerAccessConfig(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wt := resp["Workteam"].(map[string]any)
nc := wt["NotificationConfiguration"].(map[string]any)
@@ -196,7 +198,7 @@ func TestHandler_UpdateWorkteam_NotificationConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wt := resp["Workteam"].(map[string]any)
nc := wt["NotificationConfiguration"].(map[string]any)
assert.Equal(t, "arn:aws:sns:us-east-1:123456789012:updated", nc["NotificationTopicArn"])
diff --git a/services/sagemaker/hp_tuning_jobs.go b/services/sagemaker/hp_tuning_jobs.go
index 79432ecd40..6e3c972480 100644
--- a/services/sagemaker/hp_tuning_jobs.go
+++ b/services/sagemaker/hp_tuning_jobs.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"sort"
@@ -68,11 +68,11 @@ type HyperParameterTuningJob struct {
Strategy string `json:"Strategy,omitempty"`
HyperParameterTuningJobStatus string `json:"HyperParameterTuningJobStatus"`
HyperParameterTuningJobArn string `json:"HyperParameterTuningJobArn"`
- HyperParameterTuningJobConfig json.RawMessage `json:"HyperParameterTuningJobConfig,omitempty"`
- TrainingJobDefinitions json.RawMessage `json:"TrainingJobDefinitions,omitempty"`
- TrainingJobDefinition json.RawMessage `json:"TrainingJobDefinition,omitempty"`
- WarmStartConfig json.RawMessage `json:"WarmStartConfig,omitempty"`
- Autotune json.RawMessage `json:"Autotune,omitempty"`
+ HyperParameterTuningJobConfig jsontext.Value `json:"HyperParameterTuningJobConfig,omitempty"`
+ TrainingJobDefinitions jsontext.Value `json:"TrainingJobDefinitions,omitempty"`
+ TrainingJobDefinition jsontext.Value `json:"TrainingJobDefinition,omitempty"`
+ WarmStartConfig jsontext.Value `json:"WarmStartConfig,omitempty"`
+ Autotune jsontext.Value `json:"Autotune,omitempty"`
TrainingJobStatusCounters HPTrainingJobStatusCounters `json:"TrainingJobStatusCounters"`
ResourceLimits HPResourceLimits `json:"ResourceLimits"`
ObjectiveStatusCounters HPObjectiveStatusCounters `json:"ObjectiveStatusCounters"`
@@ -82,11 +82,11 @@ type HyperParameterTuningJob struct {
func cloneHPTuningJob(j *HyperParameterTuningJob) *HyperParameterTuningJob {
cp := *j
cp.Tags = maps.Clone(j.Tags)
- cp.HyperParameterTuningJobConfig = append(json.RawMessage(nil), j.HyperParameterTuningJobConfig...)
- cp.Autotune = append(json.RawMessage(nil), j.Autotune...)
- cp.WarmStartConfig = append(json.RawMessage(nil), j.WarmStartConfig...)
- cp.TrainingJobDefinition = append(json.RawMessage(nil), j.TrainingJobDefinition...)
- cp.TrainingJobDefinitions = append(json.RawMessage(nil), j.TrainingJobDefinitions...)
+ cp.HyperParameterTuningJobConfig = append(jsontext.Value(nil), j.HyperParameterTuningJobConfig...)
+ cp.Autotune = append(jsontext.Value(nil), j.Autotune...)
+ cp.WarmStartConfig = append(jsontext.Value(nil), j.WarmStartConfig...)
+ cp.TrainingJobDefinition = append(jsontext.Value(nil), j.TrainingJobDefinition...)
+ cp.TrainingJobDefinitions = append(jsontext.Value(nil), j.TrainingJobDefinitions...)
return &cp
}
@@ -101,11 +101,11 @@ type CreateHyperParameterTuningJobOptions struct {
Tags map[string]string
Name string
Strategy string
- HyperParameterTuningJobConfig json.RawMessage
- Autotune json.RawMessage
- WarmStartConfig json.RawMessage
- TrainingJobDefinition json.RawMessage
- TrainingJobDefinitions json.RawMessage
+ HyperParameterTuningJobConfig jsontext.Value
+ Autotune jsontext.Value
+ WarmStartConfig jsontext.Value
+ TrainingJobDefinition jsontext.Value
+ TrainingJobDefinitions jsontext.Value
Limits HPResourceLimits
}
diff --git a/services/sagemaker/human_task_ui.go b/services/sagemaker/human_task_ui.go
index 04e396bdc9..001bddf387 100644
--- a/services/sagemaker/human_task_ui.go
+++ b/services/sagemaker/human_task_ui.go
@@ -4,13 +4,14 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrHumanTaskUINotFound is returned when a human task UI does not exist.
@@ -63,7 +64,7 @@ func (h *HumanTaskUI) MarshalJSON() ([]byte, error) {
alias: (*alias)(h),
CreationTime: epochSeconds(h.CreationTime),
UITemplate: uiTemplateInfo{ContentSha256: h.UITemplateContentSha256},
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [HumanTaskUI.MarshalJSON], read by
@@ -77,7 +78,7 @@ func (h *HumanTaskUI) UnmarshalJSON(data []byte) error {
CreationTime float64 `json:"CreationTime"`
}{alias: (*alias)(h)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/sagemaker/hyperpod_scheduling.go b/services/sagemaker/hyperpod_scheduling.go
index cf9eec7b5f..ee76f13e4f 100644
--- a/services/sagemaker/hyperpod_scheduling.go
+++ b/services/sagemaker/hyperpod_scheduling.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -10,6 +10,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/store"
)
@@ -105,7 +106,7 @@ func (c *ClusterSchedulerConfig) MarshalJSON() ([]byte, error) {
alias: (*alias)(c),
CreationTime: epochSeconds(c.CreationTime),
LastModifiedTime: epochSeconds(c.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [ClusterSchedulerConfig.MarshalJSON], read
@@ -119,7 +120,7 @@ func (c *ClusterSchedulerConfig) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(c)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -595,7 +596,7 @@ func (q *ComputeQuota) MarshalJSON() ([]byte, error) {
alias: (*alias)(q),
CreationTime: epochSeconds(q.CreationTime),
LastModifiedTime: epochSeconds(q.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [ComputeQuota.MarshalJSON], read by
@@ -609,7 +610,7 @@ func (q *ComputeQuota) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(q)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/sagemaker/images.go b/services/sagemaker/images.go
index c344e8000e..45108cc057 100644
--- a/services/sagemaker/images.go
+++ b/services/sagemaker/images.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -66,7 +67,7 @@ func (img *SMImage) MarshalJSON() ([]byte, error) {
alias: (*alias)(img),
CreationTime: epochSeconds(img.CreationTime),
LastModifiedTime: epochSeconds(img.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [SMImage.MarshalJSON], read by
@@ -80,7 +81,7 @@ func (img *SMImage) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(img)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -358,7 +359,7 @@ func (v *ImageVersion) MarshalJSON() ([]byte, error) {
alias: (*alias)(v),
CreationTime: epochSeconds(v.CreationTime),
LastModifiedTime: epochSeconds(v.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [ImageVersion.MarshalJSON], read by
@@ -372,7 +373,7 @@ func (v *ImageVersion) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(v)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/sagemaker/inference_components.go b/services/sagemaker/inference_components.go
index acc20b2c20..8edb7fffed 100644
--- a/services/sagemaker/inference_components.go
+++ b/services/sagemaker/inference_components.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"sort"
@@ -35,7 +35,7 @@ type InferenceComponentContainerSpec struct {
Environment map[string]string `json:"Environment,omitempty"`
ArtifactURL string `json:"ArtifactUrl,omitempty"`
Image string `json:"Image,omitempty"`
- ContainerMetricsConfig json.RawMessage `json:"ContainerMetricsConfig,omitempty"`
+ ContainerMetricsConfig jsontext.Value `json:"ContainerMetricsConfig,omitempty"`
}
// InferenceComponentSpecification mirrors types.InferenceComponentSpecification
@@ -52,10 +52,10 @@ type InferenceComponentSpecification struct {
BaseInferenceComponentName string `json:"BaseInferenceComponentName,omitempty"`
InstanceType string `json:"InstanceType,omitempty"`
ModelName string `json:"ModelName,omitempty"`
- ComputeResourceRequirements json.RawMessage `json:"ComputeResourceRequirements,omitempty"`
- DataCacheConfig json.RawMessage `json:"DataCacheConfig,omitempty"`
- SchedulingConfig json.RawMessage `json:"SchedulingConfig,omitempty"`
- StartupParameters json.RawMessage `json:"StartupParameters,omitempty"`
+ ComputeResourceRequirements jsontext.Value `json:"ComputeResourceRequirements,omitempty"`
+ DataCacheConfig jsontext.Value `json:"DataCacheConfig,omitempty"`
+ SchedulingConfig jsontext.Value `json:"SchedulingConfig,omitempty"`
+ StartupParameters jsontext.Value `json:"StartupParameters,omitempty"`
}
// inferenceComponentSpecificationSummary builds the InferenceComponentSpecificationSummary
@@ -129,7 +129,7 @@ type InferenceComponent struct {
VariantName string
InferenceComponentStatus string
FailureReason string
- DeploymentConfig json.RawMessage
+ DeploymentConfig jsontext.Value
Specifications []InferenceComponentSpecification
CopyCount int32
CurrentCopyCount int32
@@ -369,7 +369,7 @@ func (b *InMemoryBackend) ListInferenceComponents(
// VariantName member on the real input — a production variant is fixed at
// Create time and cannot be moved via Update.
type UpdateInferenceComponentOptions struct {
- DeploymentConfig json.RawMessage
+ DeploymentConfig jsontext.Value
RuntimeConfig *InferenceComponentRuntimeConfigInput
Specification *InferenceComponentSpecification
Specifications []InferenceComponentSpecification
diff --git a/services/sagemaker/inference_experiments.go b/services/sagemaker/inference_experiments.go
index 60eb9095a8..03a3bdba31 100644
--- a/services/sagemaker/inference_experiments.go
+++ b/services/sagemaker/inference_experiments.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -10,6 +10,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrInferenceExperimentNotFound is returned when an inference experiment does not exist.
@@ -206,7 +207,7 @@ func (e *InferenceExperiment) MarshalJSON() ([]byte, error) {
CreationTime: epochSeconds(e.CreationTime),
LastModifiedTime: epochSeconds(e.LastModifiedTime),
ModelVariants: modelVariantConfigSummaries(e.ModelVariants),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [InferenceExperiment.MarshalJSON], read by
@@ -221,7 +222,7 @@ func (e *InferenceExperiment) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(e)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/sagemaker/inference_recommendations_jobs.go b/services/sagemaker/inference_recommendations_jobs.go
index 6bfe9a2d16..7c28918440 100644
--- a/services/sagemaker/inference_recommendations_jobs.go
+++ b/services/sagemaker/inference_recommendations_jobs.go
@@ -2,7 +2,8 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -11,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -50,17 +52,17 @@ type InferenceRecommendationsJob struct {
JobDescription string `json:"JobDescription,omitempty"`
Status string `json:"Status"`
RoleArn string `json:"RoleArn,omitempty"`
- InputConfig json.RawMessage `json:"InputConfig,omitempty"`
- OutputConfig json.RawMessage `json:"OutputConfig,omitempty"`
- StoppingConditions json.RawMessage `json:"StoppingConditions,omitempty"`
+ InputConfig jsontext.Value `json:"InputConfig,omitempty"`
+ OutputConfig jsontext.Value `json:"OutputConfig,omitempty"`
+ StoppingConditions jsontext.Value `json:"StoppingConditions,omitempty"`
}
func cloneInferenceRecommendationsJob(j *InferenceRecommendationsJob) *InferenceRecommendationsJob {
cp := *j
cp.Tags = maps.Clone(j.Tags)
- cp.InputConfig = append(json.RawMessage(nil), j.InputConfig...)
- cp.OutputConfig = append(json.RawMessage(nil), j.OutputConfig...)
- cp.StoppingConditions = append(json.RawMessage(nil), j.StoppingConditions...)
+ cp.InputConfig = append(jsontext.Value(nil), j.InputConfig...)
+ cp.OutputConfig = append(jsontext.Value(nil), j.OutputConfig...)
+ cp.StoppingConditions = append(jsontext.Value(nil), j.StoppingConditions...)
return &cp
}
@@ -74,10 +76,10 @@ type inputConfigModelIdentity struct {
ModelPackageVersionArn string `json:"ModelPackageVersionArn"`
}
-func decodeInputConfigModelIdentity(raw json.RawMessage) inputConfigModelIdentity {
+func decodeInputConfigModelIdentity(raw jsontext.Value) inputConfigModelIdentity {
var id inputConfigModelIdentity
- _ = json.Unmarshal(raw, &id)
+ _ = json.Unmarshal(raw, &id, jsonx.V1())
return id
}
@@ -89,9 +91,9 @@ type CreateInferenceRecommendationsJobOptions struct {
JobType string
JobDescription string
RoleArn string
- InputConfig json.RawMessage
- OutputConfig json.RawMessage
- StoppingConditions json.RawMessage
+ InputConfig jsontext.Value
+ OutputConfig jsontext.Value
+ StoppingConditions jsontext.Value
}
// CreateInferenceRecommendationsJob creates an inference recommendations job.
diff --git a/services/sagemaker/interfaces.go b/services/sagemaker/interfaces.go
index 6b51700497..6863c90e7a 100644
--- a/services/sagemaker/interfaces.go
+++ b/services/sagemaker/interfaces.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
)
// StorageBackend defines the interface for SageMaker backend implementations.
@@ -47,7 +47,7 @@ type StorageBackend interface {
kmsKeyID string,
shadowProductionVariants []ProductionVariant,
enableNetworkIsolation bool,
- explainerConfig, metricsConfig json.RawMessage,
+ explainerConfig, metricsConfig jsontext.Value,
) error
AddTags(ctx context.Context, resourceARN string, tags map[string]string) error
diff --git a/services/sagemaker/jobs.go b/services/sagemaker/jobs.go
index 8f5a37de82..69bb4270be 100644
--- a/services/sagemaker/jobs.go
+++ b/services/sagemaker/jobs.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/store"
)
@@ -74,7 +75,7 @@ func (t JobSecondaryStatusTransition) MarshalJSON() ([]byte, error) {
alias: (*alias)(&t),
StartTime: epochSeconds(t.StartTime),
EndTime: epochSecondsPtr(t.EndTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [JobSecondaryStatusTransition.MarshalJSON],
@@ -88,7 +89,7 @@ func (t *JobSecondaryStatusTransition) UnmarshalJSON(data []byte) error {
StartTime float64 `json:"StartTime"`
}{alias: (*alias)(t)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -135,7 +136,7 @@ func (j *Job) MarshalJSON() ([]byte, error) {
CreationTime: epochSeconds(j.CreationTime),
LastModifiedTime: epochSeconds(j.LastModifiedTime),
EndTime: epochSecondsPtr(j.EndTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [Job.MarshalJSON], used by persistence.go's
@@ -150,7 +151,7 @@ func (j *Job) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(j)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/sagemaker/list_summary_shapes_test.go b/services/sagemaker/list_summary_shapes_test.go
index d0111aa6bf..2a6d061ccc 100644
--- a/services/sagemaker/list_summary_shapes_test.go
+++ b/services/sagemaker/list_summary_shapes_test.go
@@ -1,13 +1,15 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
sagemakersdk "github.com/aws/aws-sdk-go-v2/service/sagemaker"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestListSummaries_MemberRoundTrips drives each op's Create then List
@@ -199,7 +201,7 @@ func TestListInferenceExperiments_SummaryOmitsArn(t *testing.T) {
var resp struct {
InferenceExperiments []map[string]any `json:"InferenceExperiments"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.InferenceExperiments, 1)
_, hasArn := resp.InferenceExperiments[0]["Arn"]
@@ -244,7 +246,7 @@ func TestListTransformJobs_SummaryOmitsModelName(t *testing.T) {
var resp struct {
TransformJobSummaries []map[string]any `json:"TransformJobSummaries"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.TransformJobSummaries, 1)
_, hasModelName := resp.TransformJobSummaries[0]["ModelName"]
diff --git a/services/sagemaker/mlflow.go b/services/sagemaker/mlflow.go
index 3789cf7aea..79a17e9ae2 100644
--- a/services/sagemaker/mlflow.go
+++ b/services/sagemaker/mlflow.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -64,7 +65,7 @@ func (s *MlflowTrackingServer) MarshalJSON() ([]byte, error) {
alias: (*alias)(s),
CreationTime: epochSeconds(s.CreationTime),
LastModifiedTime: epochSeconds(s.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [MlflowTrackingServer.MarshalJSON], read by
@@ -78,7 +79,7 @@ func (s *MlflowTrackingServer) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(s)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/sagemaker/model_cards.go b/services/sagemaker/model_cards.go
index ac0203308d..9075ffa174 100644
--- a/services/sagemaker/model_cards.go
+++ b/services/sagemaker/model_cards.go
@@ -2,7 +2,8 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
@@ -12,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrModelCardNotFound is returned when a model card does not exist.
@@ -83,7 +85,7 @@ func (c *ModelCard) MarshalJSON() ([]byte, error) {
alias: (*alias)(c),
CreationTime: epochSeconds(c.CreationTime),
LastModifiedTime: epochSeconds(c.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [ModelCard.MarshalJSON], read by
@@ -97,7 +99,7 @@ func (c *ModelCard) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(c)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -222,22 +224,22 @@ var modelCardMetadataOnlyPaths = [][2]string{
// object sanitizes to "{}", since a non-object document trivially has none
// of the preserved paths.
func sanitizeModelCardContent(content string) string {
- var doc map[string]json.RawMessage
+ var doc map[string]jsontext.Value
- out := map[string]map[string]json.RawMessage{}
+ out := map[string]map[string]jsontext.Value{}
- if unmarshalErr := json.Unmarshal([]byte(content), &doc); unmarshalErr == nil {
+ if unmarshalErr := json.Unmarshal([]byte(content), &doc, jsonx.V1()); unmarshalErr == nil {
for _, path := range modelCardMetadataOnlyPaths {
section, value := path[0], path[1]
- var fields map[string]json.RawMessage
- if json.Unmarshal(doc[section], &fields) != nil {
+ var fields map[string]jsontext.Value
+ if json.Unmarshal(doc[section], &fields, jsonx.V1()) != nil {
continue
}
if v, ok := fields[value]; ok {
if out[section] == nil {
- out[section] = map[string]json.RawMessage{}
+ out[section] = map[string]jsontext.Value{}
}
out[section][value] = v
@@ -245,7 +247,7 @@ func sanitizeModelCardContent(content string) string {
}
}
- sanitized, err := json.Marshal(out)
+ sanitized, err := json.Marshal(out, jsonx.V1())
if err != nil {
return "{}"
}
diff --git a/services/sagemaker/model_packages.go b/services/sagemaker/model_packages.go
index 964609449e..a2be19ab5c 100644
--- a/services/sagemaker/model_packages.go
+++ b/services/sagemaker/model_packages.go
@@ -2,7 +2,8 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -12,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -128,7 +130,7 @@ func (g *ModelPackageGroup) MarshalJSON() ([]byte, error) {
}{
alias: (*alias)(g),
CreationTime: epochSeconds(g.CreationTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [ModelPackageGroup.MarshalJSON], read by
@@ -141,7 +143,7 @@ func (g *ModelPackageGroup) UnmarshalJSON(data []byte) error {
CreationTime float64 `json:"CreationTime"`
}{alias: (*alias)(g)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -405,16 +407,16 @@ type CreateModelPackageOptions struct {
ApprovalStatus string
Tags map[string]string
CustomerMetadataProperties map[string]string
- InferenceSpecification json.RawMessage
- SourceAlgorithmSpecification json.RawMessage
- ValidationSpecification json.RawMessage
- DriftCheckBaselines json.RawMessage
- ModelMetrics json.RawMessage
- ModelCard json.RawMessage
- ModelLifeCycle json.RawMessage
- MetadataProperties json.RawMessage
- SecurityConfig json.RawMessage
- AdditionalInferenceSpecifications json.RawMessage
+ InferenceSpecification jsontext.Value
+ SourceAlgorithmSpecification jsontext.Value
+ ValidationSpecification jsontext.Value
+ DriftCheckBaselines jsontext.Value
+ ModelMetrics jsontext.Value
+ ModelCard jsontext.Value
+ ModelLifeCycle jsontext.Value
+ MetadataProperties jsontext.Value
+ SecurityConfig jsontext.Value
+ AdditionalInferenceSpecifications jsontext.Value
CertifyForMarketplace bool
}
@@ -698,10 +700,10 @@ type UpdateModelPackageOptions struct {
SourceURI string
CustomerMetadataProperties map[string]string
CustomerMetadataPropertiesToRemove []string
- InferenceSpecification json.RawMessage
- ModelCard json.RawMessage
- ModelLifeCycle json.RawMessage
- AdditionalInferenceSpecificationsToAdd json.RawMessage
+ InferenceSpecification jsontext.Value
+ ModelCard jsontext.Value
+ ModelLifeCycle jsontext.Value
+ AdditionalInferenceSpecificationsToAdd jsontext.Value
}
// UpdateModelPackage updates a model package identified by its ARN (the sole
@@ -776,22 +778,22 @@ func (b *InMemoryBackend) UpdateModelPackage(
// the existing array". Both are opaque json.RawMessage passthrough (see
// ModelPackage's own doc comment), so this manipulates them as raw JSON
// arrays rather than unmarshaling into a typed element.
-func appendInferenceSpecifications(existing, toAdd json.RawMessage) json.RawMessage {
- var newItems []json.RawMessage
- if err := json.Unmarshal(toAdd, &newItems); err != nil {
+func appendInferenceSpecifications(existing, toAdd jsontext.Value) jsontext.Value {
+ var newItems []jsontext.Value
+ if err := json.Unmarshal(toAdd, &newItems, jsonx.V1()); err != nil {
return existing
}
- existingItems := make([]json.RawMessage, 0, len(newItems))
+ existingItems := make([]jsontext.Value, 0, len(newItems))
if len(existing) > 0 {
- if err := json.Unmarshal(existing, &existingItems); err != nil {
+ if err := json.Unmarshal(existing, &existingItems, jsonx.V1()); err != nil {
return existing
}
}
existingItems = append(existingItems, newItems...)
- out, err := json.Marshal(existingItems)
+ out, err := json.Marshal(existingItems, jsonx.V1())
if err != nil {
return existing
}
diff --git a/services/sagemaker/models.go b/services/sagemaker/models.go
index 041486e2e7..80be4bee14 100644
--- a/services/sagemaker/models.go
+++ b/services/sagemaker/models.go
@@ -1,10 +1,13 @@
package sagemaker
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"slices"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ImageConfig specifies where SageMaker should pull the container image from.
@@ -139,7 +142,7 @@ func (pv *ProductionVariant) UnmarshalJSON(data []byte) error {
type plain ProductionVariant
aux := plain{InitialVariantWeight: defaultVariantWeight}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -157,7 +160,7 @@ func (pv ProductionVariant) MarshalJSON() ([]byte, error) {
plain
InitialVariantWeight float64 `json:"InitialVariantWeight"`
- }{plain: plain(pv), InitialVariantWeight: pv.InitialVariantWeight})
+ }{plain: plain(pv), InitialVariantWeight: pv.InitialVariantWeight}, jsonx.V1())
}
// DataCaptureConfig specifies real-time data capture for an endpoint config.
@@ -219,8 +222,8 @@ type EndpointConfig struct {
VpcConfig *VpcConfig `json:"VpcConfig,omitempty"`
DataCaptureConfig *DataCaptureConfig `json:"DataCaptureConfig,omitempty"`
AsyncInferenceConfig *AsyncInferenceConfig `json:"AsyncInferenceConfig,omitempty"`
- ExplainerConfig json.RawMessage `json:"ExplainerConfig,omitempty"`
- MetricsConfig json.RawMessage `json:"MetricsConfig,omitempty"`
+ ExplainerConfig jsontext.Value `json:"ExplainerConfig,omitempty"`
+ MetricsConfig jsontext.Value `json:"MetricsConfig,omitempty"`
EndpointConfigName string `json:"EndpointConfigName"`
EndpointConfigARN string `json:"EndpointConfigARN"`
ExecutionRoleArn string `json:"ExecutionRoleArn,omitempty"`
@@ -275,8 +278,8 @@ func cloneEndpointConfig(ec *EndpointConfig) *EndpointConfig {
aic := *ec.AsyncInferenceConfig
cp.AsyncInferenceConfig = &aic
}
- cp.ExplainerConfig = append(json.RawMessage(nil), ec.ExplainerConfig...)
- cp.MetricsConfig = append(json.RawMessage(nil), ec.MetricsConfig...)
+ cp.ExplainerConfig = append(jsontext.Value(nil), ec.ExplainerConfig...)
+ cp.MetricsConfig = append(jsontext.Value(nil), ec.MetricsConfig...)
return &cp
}
@@ -360,9 +363,9 @@ type Algorithm struct {
AlgorithmStatus string `json:"AlgorithmStatus"`
ProductID string `json:"ProductId,omitempty"`
AlgorithmStatusDetails AlgorithmStatusDetails `json:"AlgorithmStatusDetails"`
- TrainingSpecification json.RawMessage `json:"TrainingSpecification,omitempty"`
- InferenceSpecification json.RawMessage `json:"InferenceSpecification,omitempty"`
- ValidationSpecification json.RawMessage `json:"ValidationSpecification,omitempty"`
+ TrainingSpecification jsontext.Value `json:"TrainingSpecification,omitempty"`
+ InferenceSpecification jsontext.Value `json:"InferenceSpecification,omitempty"`
+ ValidationSpecification jsontext.Value `json:"ValidationSpecification,omitempty"`
CertifyForMarketplace bool `json:"CertifyForMarketplace,omitempty"`
}
@@ -370,9 +373,9 @@ type Algorithm struct {
func cloneAlgorithm(al *Algorithm) *Algorithm {
cp := *al
cp.Tags = maps.Clone(al.Tags)
- cp.TrainingSpecification = append(json.RawMessage(nil), al.TrainingSpecification...)
- cp.InferenceSpecification = append(json.RawMessage(nil), al.InferenceSpecification...)
- cp.ValidationSpecification = append(json.RawMessage(nil), al.ValidationSpecification...)
+ cp.TrainingSpecification = append(jsontext.Value(nil), al.TrainingSpecification...)
+ cp.InferenceSpecification = append(jsontext.Value(nil), al.InferenceSpecification...)
+ cp.ValidationSpecification = append(jsontext.Value(nil), al.ValidationSpecification...)
cp.AlgorithmStatusDetails.ImageScanStatuses = append(
[]AlgorithmStatusItem(nil), al.AlgorithmStatusDetails.ImageScanStatuses...,
)
@@ -847,18 +850,18 @@ type ModelPackage struct {
LastModifiedTime time.Time `json:"LastModifiedTime,omitzero"`
Tags map[string]string `json:"Tags,omitempty"`
CustomerMetadataProperties map[string]string `json:"CustomerMetadataProperties,omitempty"`
- InferenceSpecification json.RawMessage `json:"InferenceSpecification,omitempty"`
- SourceAlgorithmSpecification json.RawMessage `json:"SourceAlgorithmSpecification,omitempty"`
- ValidationSpecification json.RawMessage `json:"ValidationSpecification,omitempty"`
- DriftCheckBaselines json.RawMessage `json:"DriftCheckBaselines,omitempty"`
- ModelMetrics json.RawMessage `json:"ModelMetrics,omitempty"`
- ModelCard json.RawMessage `json:"ModelCard,omitempty"`
- ModelLifeCycle json.RawMessage `json:"ModelLifeCycle,omitempty"`
+ InferenceSpecification jsontext.Value `json:"InferenceSpecification,omitempty"`
+ SourceAlgorithmSpecification jsontext.Value `json:"SourceAlgorithmSpecification,omitempty"`
+ ValidationSpecification jsontext.Value `json:"ValidationSpecification,omitempty"`
+ DriftCheckBaselines jsontext.Value `json:"DriftCheckBaselines,omitempty"`
+ ModelMetrics jsontext.Value `json:"ModelMetrics,omitempty"`
+ ModelCard jsontext.Value `json:"ModelCard,omitempty"`
+ ModelLifeCycle jsontext.Value `json:"ModelLifeCycle,omitempty"`
CreatedBy *UserContext `json:"CreatedBy,omitempty"`
LastModifiedBy *UserContext `json:"LastModifiedBy,omitempty"`
- MetadataProperties json.RawMessage `json:"MetadataProperties,omitempty"`
- SecurityConfig json.RawMessage `json:"SecurityConfig,omitempty"`
- AdditionalInferenceSpecifications json.RawMessage `json:"AdditionalInferenceSpecifications,omitempty"`
+ MetadataProperties jsontext.Value `json:"MetadataProperties,omitempty"`
+ SecurityConfig jsontext.Value `json:"SecurityConfig,omitempty"`
+ AdditionalInferenceSpecifications jsontext.Value `json:"AdditionalInferenceSpecifications,omitempty"`
ModelPackageName string `json:"ModelPackageName"`
ModelPackageArn string `json:"ModelPackageArn"`
ModelPackageGroupName string `json:"ModelPackageGroupName,omitempty"`
@@ -891,16 +894,16 @@ func cloneModelPackage(mp *ModelPackage) *ModelPackage {
cp.ModelPackageStatusDetails.ImageScanStatuses = append(
[]ModelPackageStatusItem{}, mp.ModelPackageStatusDetails.ImageScanStatuses...,
)
- cp.InferenceSpecification = append(json.RawMessage(nil), mp.InferenceSpecification...)
- cp.SourceAlgorithmSpecification = append(json.RawMessage(nil), mp.SourceAlgorithmSpecification...)
- cp.ValidationSpecification = append(json.RawMessage(nil), mp.ValidationSpecification...)
- cp.DriftCheckBaselines = append(json.RawMessage(nil), mp.DriftCheckBaselines...)
- cp.ModelMetrics = append(json.RawMessage(nil), mp.ModelMetrics...)
- cp.ModelCard = append(json.RawMessage(nil), mp.ModelCard...)
- cp.ModelLifeCycle = append(json.RawMessage(nil), mp.ModelLifeCycle...)
- cp.MetadataProperties = append(json.RawMessage(nil), mp.MetadataProperties...)
- cp.SecurityConfig = append(json.RawMessage(nil), mp.SecurityConfig...)
- cp.AdditionalInferenceSpecifications = append(json.RawMessage(nil), mp.AdditionalInferenceSpecifications...)
+ cp.InferenceSpecification = append(jsontext.Value(nil), mp.InferenceSpecification...)
+ cp.SourceAlgorithmSpecification = append(jsontext.Value(nil), mp.SourceAlgorithmSpecification...)
+ cp.ValidationSpecification = append(jsontext.Value(nil), mp.ValidationSpecification...)
+ cp.DriftCheckBaselines = append(jsontext.Value(nil), mp.DriftCheckBaselines...)
+ cp.ModelMetrics = append(jsontext.Value(nil), mp.ModelMetrics...)
+ cp.ModelCard = append(jsontext.Value(nil), mp.ModelCard...)
+ cp.ModelLifeCycle = append(jsontext.Value(nil), mp.ModelLifeCycle...)
+ cp.MetadataProperties = append(jsontext.Value(nil), mp.MetadataProperties...)
+ cp.SecurityConfig = append(jsontext.Value(nil), mp.SecurityConfig...)
+ cp.AdditionalInferenceSpecifications = append(jsontext.Value(nil), mp.AdditionalInferenceSpecifications...)
return &cp
}
@@ -923,7 +926,7 @@ func (mp *ModelPackage) MarshalJSON() ([]byte, error) {
aux.LastModifiedTime = epochSeconds(mp.LastModifiedTime)
}
- return json.Marshal(aux)
+ return json.Marshal(aux, jsonx.V1())
}
// UnmarshalJSON is the inverse of [ModelPackage.MarshalJSON], read by
@@ -937,7 +940,7 @@ func (mp *ModelPackage) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(mp)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/sagemaker/monitoring_job_definitions.go b/services/sagemaker/monitoring_job_definitions.go
index 5374f4ccc3..327b496bf0 100644
--- a/services/sagemaker/monitoring_job_definitions.go
+++ b/services/sagemaker/monitoring_job_definitions.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"sort"
@@ -46,14 +46,14 @@ var (
// StoppingCondition/BaselineConfig blocks, so Describe echoes back exactly
// what Create received.
type JobDefinition struct {
- CreationTime time.Time `json:"CreationTime"`
- Tags map[string]string `json:"Tags,omitempty"`
- Config map[string]json.RawMessage `json:"Config,omitempty"`
- JobDefinitionName string `json:"JobDefinitionName"`
- JobDefinitionArn string `json:"JobDefinitionArn"`
- JobDefinitionType string `json:"JobDefinitionType"`
- RoleArn string `json:"RoleArn,omitempty"`
- EndpointName string `json:"EndpointName,omitempty"`
+ CreationTime time.Time `json:"CreationTime"`
+ Tags map[string]string `json:"Tags,omitempty"`
+ Config map[string]jsontext.Value `json:"Config,omitempty"`
+ JobDefinitionName string `json:"JobDefinitionName"`
+ JobDefinitionArn string `json:"JobDefinitionArn"`
+ JobDefinitionType string `json:"JobDefinitionType"`
+ RoleArn string `json:"RoleArn,omitempty"`
+ EndpointName string `json:"EndpointName,omitempty"`
}
func cloneJobDefinition(j *JobDefinition) *JobDefinition {
@@ -71,7 +71,7 @@ func (b *InMemoryBackend) createJobDefinition(
ctx context.Context,
storeFn func(string) *store.Table[JobDefinition],
defType, name, roleArn, endpointName string,
- config map[string]json.RawMessage,
+ config map[string]jsontext.Value,
tags map[string]string,
resourceType string,
alreadyExists error,
@@ -227,7 +227,7 @@ func (b *InMemoryBackend) listJobDefinitions(
func (b *InMemoryBackend) CreateDataQualityJobDefinition(
ctx context.Context,
name, roleArn, endpointName string,
- config map[string]json.RawMessage,
+ config map[string]jsontext.Value,
tags map[string]string,
) (*JobDefinition, error) {
return b.createJobDefinition(
@@ -266,7 +266,7 @@ func (b *InMemoryBackend) ListDataQualityJobDefinitions(
func (b *InMemoryBackend) CreateModelBiasJobDefinition(
ctx context.Context,
name, roleArn, endpointName string,
- config map[string]json.RawMessage,
+ config map[string]jsontext.Value,
tags map[string]string,
) (*JobDefinition, error) {
return b.createJobDefinition(
@@ -305,7 +305,7 @@ func (b *InMemoryBackend) ListModelBiasJobDefinitions(
func (b *InMemoryBackend) CreateModelQualityJobDefinition(
ctx context.Context,
name, roleArn, endpointName string,
- config map[string]json.RawMessage,
+ config map[string]jsontext.Value,
tags map[string]string,
) (*JobDefinition, error) {
return b.createJobDefinition(
@@ -344,7 +344,7 @@ func (b *InMemoryBackend) ListModelQualityJobDefinitions(
func (b *InMemoryBackend) CreateModelExplainabilityJobDefinition(
ctx context.Context,
name, roleArn, endpointName string,
- config map[string]json.RawMessage,
+ config map[string]jsontext.Value,
tags map[string]string,
) (*JobDefinition, error) {
return b.createJobDefinition(
diff --git a/services/sagemaker/monitoring_schedules.go b/services/sagemaker/monitoring_schedules.go
index 10e0acb1e6..0ef320fc15 100644
--- a/services/sagemaker/monitoring_schedules.go
+++ b/services/sagemaker/monitoring_schedules.go
@@ -2,7 +2,8 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -11,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -45,7 +47,7 @@ type MonitoringScheduleConfig struct {
ScheduleConfig *MonitoringScheduleSchedule `json:"ScheduleConfig,omitempty"`
MonitoringJobDefinitionName string `json:"MonitoringJobDefinitionName,omitempty"`
MonitoringType string `json:"MonitoringType,omitempty"`
- MonitoringJobDefinition json.RawMessage `json:"MonitoringJobDefinition,omitempty"`
+ MonitoringJobDefinition jsontext.Value `json:"MonitoringJobDefinition,omitempty"`
}
// MonitoringSchedule represents a SageMaker monitoring schedule.
@@ -67,7 +69,7 @@ func cloneMonitoringSchedule(ms *MonitoringSchedule) *MonitoringSchedule {
if ms.MonitoringScheduleConfig != nil {
cfg := *ms.MonitoringScheduleConfig
cfg.MonitoringJobDefinition = append(
- json.RawMessage(nil),
+ jsontext.Value(nil),
ms.MonitoringScheduleConfig.MonitoringJobDefinition...)
if ms.MonitoringScheduleConfig.ScheduleConfig != nil {
@@ -106,7 +108,7 @@ func (ms *MonitoringSchedule) MarshalJSON() ([]byte, error) {
CreationTime: epochSeconds(ms.CreationTime),
LastModifiedTime: epochSeconds(ms.LastModifiedTime),
MonitoringType: monitoringType,
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [MonitoringSchedule.MarshalJSON], read by
@@ -120,7 +122,7 @@ func (ms *MonitoringSchedule) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(ms)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -141,7 +143,7 @@ func (ms *MonitoringSchedule) UnmarshalJSON(data []byte) error {
// raw is empty, unparseable, or has no endpoint input (e.g. a batch
// transform monitoring job, or a schedule referencing a job definition by
// name only).
-func monitoringEndpointNameFromJobDefinition(raw json.RawMessage) string {
+func monitoringEndpointNameFromJobDefinition(raw jsontext.Value) string {
var doc struct {
MonitoringInputs []struct {
EndpointInput *struct {
@@ -150,7 +152,7 @@ func monitoringEndpointNameFromJobDefinition(raw json.RawMessage) string {
} `json:"MonitoringInputs"`
}
- if len(raw) == 0 || json.Unmarshal(raw, &doc) != nil {
+ if len(raw) == 0 || json.Unmarshal(raw, &doc, jsonx.V1()) != nil {
return ""
}
diff --git a/services/sagemaker/notebook_url_test.go b/services/sagemaker/notebook_url_test.go
index 9bc0279bf0..c0f774b0cf 100644
--- a/services/sagemaker/notebook_url_test.go
+++ b/services/sagemaker/notebook_url_test.go
@@ -1,13 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -42,7 +43,7 @@ func TestNotebookInstance_UrlHost(t *testing.T) {
var out struct {
URL string `json:"Url"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.want, out.URL)
})
}
diff --git a/services/sagemaker/optimization_jobs.go b/services/sagemaker/optimization_jobs.go
index 11f89df379..aa681ae3b6 100644
--- a/services/sagemaker/optimization_jobs.go
+++ b/services/sagemaker/optimization_jobs.go
@@ -2,7 +2,8 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -11,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrOptimizationJobNotFound is returned when an optimization job does not exist.
@@ -45,7 +47,7 @@ func optimizationConfigTypeName(key string) (string, bool) {
// optimizationTypesOf derives OptimizationJobSummary's required
// OptimizationTypes from the raw OptimizationConfigs the client sent, by
// checking which union member keys appear in the JSON.
-func optimizationTypesOf(rawConfigs json.RawMessage) []string {
+func optimizationTypesOf(rawConfigs jsontext.Value) []string {
// Every OptimizationConfig union member's wire key, in the same order
// optimizationConfigTypeName recognizes them.
wireKeys := [...]string{
@@ -84,11 +86,11 @@ type OptimizationJob struct {
OptimizationJobArn string `json:"OptimizationJobArn"`
RoleArn string `json:"RoleArn,omitempty"`
DeploymentInstanceType string `json:"DeploymentInstanceType,omitempty"`
- OutputConfig json.RawMessage `json:"OutputConfig,omitempty"`
- VpcConfig json.RawMessage `json:"VpcConfig,omitempty"`
- OptimizationConfigs json.RawMessage `json:"OptimizationConfigs,omitempty"`
+ OutputConfig jsontext.Value `json:"OutputConfig,omitempty"`
+ VpcConfig jsontext.Value `json:"VpcConfig,omitempty"`
+ OptimizationConfigs jsontext.Value `json:"OptimizationConfigs,omitempty"`
TrainingPlanArns []string `json:"TrainingPlanArns,omitempty"`
- ModelSource json.RawMessage `json:"ModelSource,omitempty"`
+ ModelSource jsontext.Value `json:"ModelSource,omitempty"`
MaxInstanceCount int32 `json:"MaxInstanceCount,omitempty"`
}
@@ -96,10 +98,10 @@ func cloneOptimizationJob(j *OptimizationJob) *OptimizationJob {
cp := *j
cp.Tags = maps.Clone(j.Tags)
cp.OptimizationEnvironment = maps.Clone(j.OptimizationEnvironment)
- cp.ModelSource = append(json.RawMessage(nil), j.ModelSource...)
- cp.OptimizationConfigs = append(json.RawMessage(nil), j.OptimizationConfigs...)
- cp.OutputConfig = append(json.RawMessage(nil), j.OutputConfig...)
- cp.VpcConfig = append(json.RawMessage(nil), j.VpcConfig...)
+ cp.ModelSource = append(jsontext.Value(nil), j.ModelSource...)
+ cp.OptimizationConfigs = append(jsontext.Value(nil), j.OptimizationConfigs...)
+ cp.OutputConfig = append(jsontext.Value(nil), j.OutputConfig...)
+ cp.VpcConfig = append(jsontext.Value(nil), j.VpcConfig...)
cp.TrainingPlanArns = append([]string(nil), j.TrainingPlanArns...)
if j.StoppingCondition != nil {
@@ -128,7 +130,7 @@ func (j *OptimizationJob) MarshalJSON() ([]byte, error) {
LastModifiedTime: epochSeconds(j.LastModifiedTime),
OptimizationStartTime: epochSecondsPtr(j.OptimizationStartTime),
OptimizationEndTime: epochSecondsPtr(j.OptimizationEndTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [OptimizationJob.MarshalJSON], read by
@@ -144,7 +146,7 @@ func (j *OptimizationJob) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(j)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -165,10 +167,10 @@ type CreateOptimizationJobOptions struct {
Name string
RoleArn string
DeploymentInstanceType string
- ModelSource json.RawMessage
- OptimizationConfigs json.RawMessage
- OutputConfig json.RawMessage
- VpcConfig json.RawMessage
+ ModelSource jsontext.Value
+ OptimizationConfigs jsontext.Value
+ OutputConfig jsontext.Value
+ VpcConfig jsontext.Value
TrainingPlanArns []string
MaxInstanceCount int32
}
diff --git a/services/sagemaker/partner_apps.go b/services/sagemaker/partner_apps.go
index b7e5eb976a..797570fafa 100644
--- a/services/sagemaker/partner_apps.go
+++ b/services/sagemaker/partner_apps.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"time"
@@ -40,7 +40,7 @@ type PartnerApp struct {
Tier string `json:"Tier,omitempty"`
KmsKeyID string `json:"KmsKeyId,omitempty"`
BaseURL string `json:"-"`
- ApplicationConfig json.RawMessage `json:"ApplicationConfig,omitempty"`
+ ApplicationConfig jsontext.Value `json:"ApplicationConfig,omitempty"`
EnableAutoMinorVersionUpgrade bool `json:"EnableAutoMinorVersionUpgrade,omitempty"`
EnableIamSessionBasedIdentity bool `json:"EnableIamSessionBasedIdentity,omitempty"`
}
@@ -48,7 +48,7 @@ type PartnerApp struct {
func clonePartnerApp(p *PartnerApp) *PartnerApp {
cp := *p
cp.Tags = maps.Clone(p.Tags)
- cp.ApplicationConfig = append(json.RawMessage(nil), p.ApplicationConfig...)
+ cp.ApplicationConfig = append(jsontext.Value(nil), p.ApplicationConfig...)
if p.MaintenanceConfig != nil {
mc := *p.MaintenanceConfig
@@ -81,7 +81,7 @@ type CreatePartnerAppOptions struct {
AuthType string
Tier string
KmsKeyID string
- ApplicationConfig json.RawMessage
+ ApplicationConfig jsontext.Value
EnableAutoMinorVersionUpgrade bool
EnableIamSessionBasedIdentity bool
}
@@ -178,7 +178,7 @@ type UpdatePartnerAppOptions struct {
EnableIamSessionBasedIdentity *bool
Arn string
Tier string
- ApplicationConfig json.RawMessage
+ ApplicationConfig jsontext.Value
}
// UpdatePartnerApp updates a partner app's mutable fields.
diff --git a/services/sagemaker/persistence.go b/services/sagemaker/persistence.go
index d583803c30..c43d0ce8db 100644
--- a/services/sagemaker/persistence.go
+++ b/services/sagemaker/persistence.go
@@ -2,11 +2,13 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -158,7 +160,7 @@ func hubContentKeyString(k hubContentKey) string {
// each one doesn't fit the Table[V] keyed-by-string-derived-from-V model —
// so they are persisted directly here instead of via Tables.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ImageVersions map[string]map[string]map[int]*ImageVersion `json:"imageVersions,omitempty"`
ImageVersionCounts map[string]map[string]int `json:"imageVersionCounts,omitempty"`
MonitoringAlertHistory map[string][]*MonitoringAlertHistoryEntry `json:"monitoringAlertHistory,omitempty"`
@@ -193,7 +195,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
dtos[i] = toPersistedCluster(c)
}
- data, marshalErr := json.Marshal(dtos)
+ data, marshalErr := json.Marshal(dtos, jsonx.V1())
if marshalErr != nil {
logger.Load(ctx).WarnContext(ctx,
"sagemaker: snapshot cluster table marshal failed", "region", region, "error", marshalErr)
@@ -256,7 +258,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// b.clusters here; RestoreAll's "reset tables absent from data" behavior
// (since we've stripped every "clusters:*" key from snap.Tables) clears
// any such region's table if it was already registered.
- clusterTables := make(map[string]json.RawMessage)
+ clusterTables := make(map[string]jsontext.Value)
for name, raw := range snap.Tables {
if region, ok := strings.CutPrefix(name, clusterTablePrefix); ok {
@@ -281,7 +283,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
for region, raw := range clusterTables {
var dtos []*persistedCluster
- if err := json.Unmarshal(raw, &dtos); err != nil {
+ if err := json.Unmarshal(raw, &dtos, jsonx.V1()); err != nil {
return fmt.Errorf("sagemaker: restore cluster table %q: %w", region, err)
}
@@ -543,7 +545,7 @@ var tableRegistrars = map[string]func(*InMemoryBackend, string){
// key present in raw — see tableRegistrars — so the subsequent
// registry.RestoreAll call actually receives every table the snapshot
// contains, not just whichever ones happen to already be registered.
-func (b *InMemoryBackend) ensureTablesRegistered(raw map[string]json.RawMessage) {
+func (b *InMemoryBackend) ensureTablesRegistered(raw map[string]jsontext.Value) {
for name := range raw {
field, region, cut := strings.Cut(name, ":")
if !cut {
diff --git a/services/sagemaker/persistence_test.go b/services/sagemaker/persistence_test.go
index be88ff4fd6..62705bb02c 100644
--- a/services/sagemaker/persistence_test.go
+++ b/services/sagemaker/persistence_test.go
@@ -2,7 +2,7 @@ package sagemaker_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -104,19 +105,19 @@ func TestPersistenceRoundtrip_PolicyCatalogAndPipelineVersions(t *testing.T) {
require.Equal(t, http.StatusOK, recPolicy.Code)
var policyOut map[string]any
- require.NoError(t, json.Unmarshal(recPolicy.Body.Bytes(), &policyOut))
+ require.NoError(t, json.Unmarshal(recPolicy.Body.Bytes(), &policyOut, jsonx.V1()))
assert.JSONEq(t, `{"Version":"2012-10-17"}`, policyOut["ResourcePolicy"].(string))
recStatus := doSageMakerRequest(t, h2, "GetSagemakerServicecatalogPortfolioStatus", map[string]any{})
var statusOut map[string]any
- require.NoError(t, json.Unmarshal(recStatus.Body.Bytes(), &statusOut))
+ require.NoError(t, json.Unmarshal(recStatus.Body.Bytes(), &statusOut, jsonx.V1()))
assert.Equal(t, "Enabled", statusOut["Status"])
recVersions := doSageMakerRequest(t, h2, "ListPipelineVersions", map[string]any{"PipelineName": "persist-pipeline"})
require.Equal(t, http.StatusOK, recVersions.Code)
var versionsOut map[string]any
- require.NoError(t, json.Unmarshal(recVersions.Body.Bytes(), &versionsOut))
+ require.NoError(t, json.Unmarshal(recVersions.Body.Bytes(), &versionsOut, jsonx.V1()))
versions, _ := versionsOut["PipelineVersionSummaries"].([]any)
assert.Len(t, versions, 2)
}
@@ -201,7 +202,7 @@ func TestPersistenceRoundtrip_ClusterFullFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "arn:aws:iam::000000000000:role/HyperPodClusterRole", resp["ClusterRole"])
@@ -297,7 +298,7 @@ func TestPersistenceRoundtrip_PipelineDefinitionFromS3(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.JSONEq(t, `{"Version":"2020-12-01","Steps":[{"Name":"Step1"}]}`, resp["PipelineDefinition"].(string))
}
@@ -434,7 +435,7 @@ func TestPersistenceRoundtrip_AIAndGenericJobFamilies(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code, descRec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantValue, out[tt.wantField])
})
}
@@ -458,7 +459,7 @@ func TestPersistenceRoundtrip_Domain(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code, createRec.Body.String())
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
domainID := createOut["DomainId"].(string)
snap := h1.Snapshot(t.Context())
@@ -471,7 +472,7 @@ func TestPersistenceRoundtrip_Domain(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code, descRec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "VpcOnly", out["AppNetworkAccessType"])
assert.ElementsMatch(t, []any{"subnet-1"}, out["SubnetIds"])
assert.NotEmpty(t, out["DefaultUserSettings"])
diff --git a/services/sagemaker/pipeline_executions.go b/services/sagemaker/pipeline_executions.go
index 8dd1fa1ef0..b673c27ca4 100644
--- a/services/sagemaker/pipeline_executions.go
+++ b/services/sagemaker/pipeline_executions.go
@@ -2,11 +2,13 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -285,7 +287,7 @@ func pipelineDefinitionSteps(definition string) []definedPipelineStep {
Steps []definedPipelineStep `json:"Steps"`
}
- if json.Unmarshal([]byte(definition), &doc) != nil {
+ if json.Unmarshal([]byte(definition), &doc, jsonx.V1()) != nil {
return nil
}
diff --git a/services/sagemaker/projects.go b/services/sagemaker/projects.go
index 8bc3b60086..29547d98c5 100644
--- a/services/sagemaker/projects.go
+++ b/services/sagemaker/projects.go
@@ -2,7 +2,8 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -11,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrProjectNotFound is returned when a project does not exist.
@@ -37,8 +39,8 @@ type Project struct {
ProjectStatus string `json:"ProjectStatus"`
ProjectDescription string `json:"ProjectDescription,omitempty"`
ProvisionedProductID string `json:"ProvisionedProductId,omitempty"`
- ServiceCatalogProvisioningDetails json.RawMessage `json:"ServiceCatalogProvisioningDetails,omitempty"`
- TemplateProviders json.RawMessage `json:"TemplateProviders,omitempty"`
+ ServiceCatalogProvisioningDetails jsontext.Value `json:"ServiceCatalogProvisioningDetails,omitempty"`
+ TemplateProviders jsontext.Value `json:"TemplateProviders,omitempty"`
}
func cloneProject(p *Project) *Project {
@@ -46,8 +48,8 @@ func cloneProject(p *Project) *Project {
cp.Tags = maps.Clone(p.Tags)
cp.CreatedBy = cloneUserContext(p.CreatedBy)
cp.LastModifiedBy = cloneUserContext(p.LastModifiedBy)
- cp.ServiceCatalogProvisioningDetails = append(json.RawMessage(nil), p.ServiceCatalogProvisioningDetails...)
- cp.TemplateProviders = append(json.RawMessage(nil), p.TemplateProviders...)
+ cp.ServiceCatalogProvisioningDetails = append(jsontext.Value(nil), p.ServiceCatalogProvisioningDetails...)
+ cp.TemplateProviders = append(jsontext.Value(nil), p.TemplateProviders...)
return &cp
}
@@ -68,7 +70,7 @@ func (p *Project) MarshalJSON() ([]byte, error) {
alias: (*alias)(p),
CreationTime: epochSeconds(p.CreationTime),
LastModifiedTime: epochSeconds(p.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [Project.MarshalJSON], read by
@@ -82,7 +84,7 @@ func (p *Project) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(p)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -97,8 +99,8 @@ type CreateProjectOptions struct {
Tags map[string]string
Name string
Description string
- ServiceCatalogProvisioningDetails json.RawMessage
- TemplateProviders json.RawMessage
+ ServiceCatalogProvisioningDetails jsontext.Value
+ TemplateProviders jsontext.Value
}
// CreateProject creates a SageMaker project.
diff --git a/services/sagemaker/projects_update.go b/services/sagemaker/projects_update.go
index 085ce2e530..0811944eed 100644
--- a/services/sagemaker/projects_update.go
+++ b/services/sagemaker/projects_update.go
@@ -1,8 +1,11 @@
package sagemaker
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// UpdateProjectOptions holds the parameters for UpdateProject.
@@ -35,7 +38,7 @@ type updateTemplateProviderInput struct {
// applyProvisioningUpdate merges ServiceCatalogProvisioningUpdateDetails
// (ProvisioningArtifactId, ProvisioningParameters) into the stored details.
-func applyProvisioningUpdate(stored json.RawMessage, upd *serviceCatalogUpdateDetails) (json.RawMessage, error) {
+func applyProvisioningUpdate(stored jsontext.Value, upd *serviceCatalogUpdateDetails) (jsontext.Value, error) {
if upd == nil {
return stored, nil
}
@@ -47,7 +50,7 @@ func applyProvisioningUpdate(stored json.RawMessage, upd *serviceCatalogUpdateDe
}
var details map[string]any
- if err := json.Unmarshal(stored, &details); err != nil {
+ if err := json.Unmarshal(stored, &details, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: stored provisioning details: %w", ErrValidation, err)
}
@@ -59,22 +62,22 @@ func applyProvisioningUpdate(stored json.RawMessage, upd *serviceCatalogUpdateDe
details["ProvisioningParameters"] = upd.ProvisioningParameters
}
- return json.Marshal(details)
+ return json.Marshal(details, jsonx.V1())
}
// applyTemplateProviderUpdates replaces TemplateURL/Parameters of each stored
// CfnTemplateProvider matched by TemplateName.
func applyTemplateProviderUpdates(
- stored json.RawMessage,
+ stored jsontext.Value,
updates []cfnUpdateTemplateProvider,
-) (json.RawMessage, error) {
+) (jsontext.Value, error) {
if len(updates) == 0 {
return stored, nil
}
var providers []map[string]map[string]any
if len(stored) > 0 {
- if err := json.Unmarshal(stored, &providers); err != nil {
+ if err := json.Unmarshal(stored, &providers, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: stored template providers: %w", ErrValidation, err)
}
}
@@ -85,7 +88,7 @@ func applyTemplateProviderUpdates(
}
}
- return json.Marshal(providers)
+ return json.Marshal(providers, jsonx.V1())
}
func updateOneTemplateProvider(providers []map[string]map[string]any, u cfnUpdateTemplateProvider) bool {
@@ -108,9 +111,9 @@ func updateOneTemplateProvider(providers []map[string]map[string]any, u cfnUpdat
// templateProviderDetails converts stored CreateTemplateProvider entries to the
// TemplateProviderDetail wire shape (CfnTemplateProviderDetail key).
-func templateProviderDetails(stored json.RawMessage) []map[string]any {
+func templateProviderDetails(stored jsontext.Value) []map[string]any {
var providers []map[string]map[string]any
- if err := json.Unmarshal(stored, &providers); err != nil {
+ if err := json.Unmarshal(stored, &providers, jsonx.V1()); err != nil {
return nil
}
diff --git a/services/sagemaker/request_realism_test.go b/services/sagemaker/request_realism_test.go
index ac36d79e39..47017fde73 100644
--- a/services/sagemaker/request_realism_test.go
+++ b/services/sagemaker/request_realism_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const realismRole = "arn:aws:iam::000000000000:role/r"
@@ -37,7 +39,7 @@ func errorOf(t *testing.T, body []byte) (string, string) {
t.Helper()
var out map[string]string
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
return out["__type"], out["message"]
}
@@ -188,7 +190,7 @@ func TestRequestRealism_TrainingSecondaryStatusProgression(t *testing.T) {
r := doSageMakerRequest(t, h, "DescribeTrainingJob", map[string]any{"TrainingJobName": "progress"})
got.SecondaryStatusTransitions = nil
- return json.Unmarshal(r.Body.Bytes(), &got) == nil && got.TrainingJobStatus == "Completed"
+ return json.Unmarshal(r.Body.Bytes(), &got, jsonx.V1()) == nil && got.TrainingJobStatus == "Completed"
}, 5*time.Second, 5*time.Millisecond)
statuses := make([]string, 0, len(got.SecondaryStatusTransitions))
@@ -217,7 +219,7 @@ func TestRequestRealism_StopCompletedTrainingJobIsNoop(t *testing.T) {
}
r := doSageMakerRequest(t, h, "DescribeTrainingJob", map[string]any{"TrainingJobName": "done"})
- return json.Unmarshal(r.Body.Bytes(), &out) == nil && out.TrainingJobStatus == "Completed"
+ return json.Unmarshal(r.Body.Bytes(), &out, jsonx.V1()) == nil && out.TrainingJobStatus == "Completed"
}, 5*time.Second, 5*time.Millisecond)
require.Equal(t, http.StatusOK,
@@ -227,7 +229,7 @@ func TestRequestRealism_StopCompletedTrainingJobIsNoop(t *testing.T) {
TrainingJobStatus string `json:"TrainingJobStatus"`
}
r := doSageMakerRequest(t, h, "DescribeTrainingJob", map[string]any{"TrainingJobName": "done"})
- require.NoError(t, json.Unmarshal(r.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(r.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "Completed", out.TrainingJobStatus)
})
}
@@ -254,7 +256,7 @@ func TestRequestRealism_StopFinishedProcessingJobKeepsStatus(t *testing.T) {
ProcessingJobStatus string `json:"ProcessingJobStatus"`
}
r := doSageMakerRequest(t, h, "DescribeProcessingJob", map[string]any{"ProcessingJobName": "pj"})
- _ = json.Unmarshal(r.Body.Bytes(), &out)
+ _ = json.Unmarshal(r.Body.Bytes(), &out, jsonx.V1())
return out.ProcessingJobStatus
}
diff --git a/services/sagemaker/request_validation.go b/services/sagemaker/request_validation.go
index 60d2d10076..f6c428b0f5 100644
--- a/services/sagemaker/request_validation.go
+++ b/services/sagemaker/request_validation.go
@@ -1,13 +1,14 @@
package sagemaker
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -152,7 +153,7 @@ func validateListPaging(op string, body []byte) error {
NextToken string `json:"NextToken"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if m := req.MaxResults; m != nil && (*m < minListMaxResults || *m > maxListMaxResults) {
return fmt.Errorf(
diff --git a/services/sagemaker/shape_validation.go b/services/sagemaker/shape_validation.go
index 0d0483532f..51efcee9af 100644
--- a/services/sagemaker/shape_validation.go
+++ b/services/sagemaker/shape_validation.go
@@ -3,12 +3,15 @@ package sagemaker
import (
"bytes"
_ "embed" // shape_table.json
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"reflect"
"strings"
"sync"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errShapeInvalid = errors.New("invalid document")
@@ -34,7 +37,7 @@ type shapeTableDoc struct {
//nolint:gochecknoglobals // lazily parsed read-only schema
var loadShapeTable = sync.OnceValue(func() shapeTableDoc {
var doc shapeTableDoc
- if err := json.Unmarshal(shapeTableJSON, &doc); err != nil {
+ if err := json.Unmarshal(shapeTableJSON, &doc, jsonx.V1()); err != nil {
panic(fmt.Sprintf("sagemaker: invalid shape_table.json: %v", err))
}
@@ -49,7 +52,7 @@ func validateRequestShapes(op string, req any) error {
for i := range v.NumField() {
f := v.Type().Field(i)
- if f.Type != reflect.TypeFor[json.RawMessage]() {
+ if f.Type != reflect.TypeFor[jsontext.Value]() {
continue
}
@@ -60,7 +63,7 @@ func validateRequestShapes(op string, req any) error {
continue
}
- raw, _ := reflect.TypeAssert[json.RawMessage](v.Field(i))
+ raw, _ := reflect.TypeAssert[jsontext.Value](v.Field(i))
if isAbsentJSON(raw) {
continue
}
@@ -73,13 +76,13 @@ func validateRequestShapes(op string, req any) error {
return nil
}
-func isAbsentJSON(raw json.RawMessage) bool {
+func isAbsentJSON(raw jsontext.Value) bool {
t := bytes.TrimSpace(raw)
return len(t) == 0 || bytes.Equal(t, []byte("null"))
}
-func validateShape(table shapeTableDoc, raw json.RawMessage, shape, path string) error {
+func validateShape(table shapeTableDoc, raw jsontext.Value, shape, path string) error {
rule, ok := table.Shapes[shape]
if !ok {
return nil
@@ -89,8 +92,8 @@ func validateShape(table shapeTableDoc, raw json.RawMessage, shape, path string)
return validateShapeList(table, raw, rule.List, path)
}
- var obj map[string]json.RawMessage
- if err := json.Unmarshal(raw, &obj); err != nil {
+ var obj map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &obj, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %s must be an object", errShapeInvalid, path)
}
@@ -115,7 +118,7 @@ func validateShape(table shapeTableDoc, raw json.RawMessage, shape, path string)
return nil
}
-func validateShapeMember(table shapeTableDoc, raw json.RawMessage, shape, path string) error {
+func validateShapeMember(table shapeTableDoc, raw jsontext.Value, shape, path string) error {
if isAbsentJSON(raw) {
return nil
}
@@ -123,9 +126,9 @@ func validateShapeMember(table shapeTableDoc, raw json.RawMessage, shape, path s
return validateShape(table, raw, shape, path)
}
-func validateShapeList(table shapeTableDoc, raw json.RawMessage, elem, path string) error {
- var items []json.RawMessage
- if err := json.Unmarshal(raw, &items); err != nil {
+func validateShapeList(table shapeTableDoc, raw jsontext.Value, elem, path string) error {
+ var items []jsontext.Value
+ if err := json.Unmarshal(raw, &items, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %s must be a list", errShapeInvalid, path)
}
diff --git a/services/sagemaker/spaces.go b/services/sagemaker/spaces.go
index 1053928181..08aebe306e 100644
--- a/services/sagemaker/spaces.go
+++ b/services/sagemaker/spaces.go
@@ -2,7 +2,8 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -11,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrSpaceNotFound is returned when a space does not exist.
@@ -34,17 +36,17 @@ type Space struct {
DomainID string `json:"DomainId"`
SpaceStatus string `json:"Status"`
SpaceDisplayName string `json:"SpaceDisplayName,omitempty"`
- OwnershipSettings json.RawMessage `json:"OwnershipSettings,omitempty"`
- SpaceSettings json.RawMessage `json:"SpaceSettings,omitempty"`
- SpaceSharingSettings json.RawMessage `json:"SpaceSharingSettings,omitempty"`
+ OwnershipSettings jsontext.Value `json:"OwnershipSettings,omitempty"`
+ SpaceSettings jsontext.Value `json:"SpaceSettings,omitempty"`
+ SpaceSharingSettings jsontext.Value `json:"SpaceSharingSettings,omitempty"`
}
func cloneSpace(s *Space) *Space {
cp := *s
cp.Tags = maps.Clone(s.Tags)
- cp.OwnershipSettings = append(json.RawMessage(nil), s.OwnershipSettings...)
- cp.SpaceSettings = append(json.RawMessage(nil), s.SpaceSettings...)
- cp.SpaceSharingSettings = append(json.RawMessage(nil), s.SpaceSharingSettings...)
+ cp.OwnershipSettings = append(jsontext.Value(nil), s.OwnershipSettings...)
+ cp.SpaceSettings = append(jsontext.Value(nil), s.SpaceSettings...)
+ cp.SpaceSharingSettings = append(jsontext.Value(nil), s.SpaceSharingSettings...)
return &cp
}
@@ -63,7 +65,7 @@ func (s *Space) MarshalJSON() ([]byte, error) {
alias: (*alias)(s),
CreationTime: epochSeconds(s.CreationTime),
LastModifiedTime: epochSeconds(s.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [Space.MarshalJSON], read by
@@ -77,7 +79,7 @@ func (s *Space) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(s)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -94,9 +96,9 @@ func spaceKey(domainID, spaceName string) string {
// CreateSpaceOptions bundles CreateSpace's optional fields.
type CreateSpaceOptions struct {
SpaceDisplayName string
- OwnershipSettings json.RawMessage
- SpaceSettings json.RawMessage
- SpaceSharingSettings json.RawMessage
+ OwnershipSettings jsontext.Value
+ SpaceSettings jsontext.Value
+ SpaceSharingSettings jsontext.Value
}
// CreateSpace creates a SageMaker Studio space.
@@ -249,7 +251,7 @@ func (b *InMemoryBackend) ListSpaces(ctx context.Context, params ListSpacesParam
// unset, so only a non-empty value is applied.
type UpdateSpaceOptions struct {
SpaceDisplayName string
- SpaceSettings json.RawMessage
+ SpaceSettings jsontext.Value
}
// UpdateSpace updates a space in a domain. Returns the updated space.
diff --git a/services/sagemaker/studio_lifecycle_configs.go b/services/sagemaker/studio_lifecycle_configs.go
index 8897a80c12..b5da90e117 100644
--- a/services/sagemaker/studio_lifecycle_configs.go
+++ b/services/sagemaker/studio_lifecycle_configs.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -11,6 +11,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrStudioLifecycleConfigNotFound is returned when a studio lifecycle config does not exist.
@@ -52,7 +53,7 @@ func (s *StudioLifecycleConfig) MarshalJSON() ([]byte, error) {
alias: (*alias)(s),
CreationTime: epochSeconds(s.CreationTime),
LastModifiedTime: epochSeconds(s.LastModifiedTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [StudioLifecycleConfig.MarshalJSON], read
@@ -66,7 +67,7 @@ func (s *StudioLifecycleConfig) UnmarshalJSON(data []byte) error {
LastModifiedTime float64 `json:"LastModifiedTime"`
}{alias: (*alias)(s)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/sagemaker/training_jobs.go b/services/sagemaker/training_jobs.go
index 088367887e..5313610b3a 100644
--- a/services/sagemaker/training_jobs.go
+++ b/services/sagemaker/training_jobs.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"sort"
@@ -21,7 +21,7 @@ var (
)
// rawConfigMap holds CreateTrainingJob members stored verbatim.
-type rawConfigMap = map[string]json.RawMessage
+type rawConfigMap = map[string]jsontext.Value
type TrainingJob struct {
PassthroughConfigs rawConfigMap `json:"PassthroughConfigs,omitempty"`
diff --git a/services/sagemaker/training_plan.go b/services/sagemaker/training_plan.go
index ef9480ac76..758063d4a9 100644
--- a/services/sagemaker/training_plan.go
+++ b/services/sagemaker/training_plan.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"sort"
@@ -10,6 +10,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// trainingPlanStatusScheduled mirrors AWS's TrainingPlanStatus "Scheduled"
@@ -126,7 +127,7 @@ func (rc *ReservedCapacity) MarshalJSON() ([]byte, error) {
StartTime: epochSeconds(rc.StartTime),
EndTime: epochSeconds(rc.EndTime),
UltraServerSummary: rc.ultraServerSummary(),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [ReservedCapacity.MarshalJSON], read by
@@ -140,7 +141,7 @@ func (rc *ReservedCapacity) UnmarshalJSON(data []byte) error {
EndTime float64 `json:"EndTime"`
}{alias: (*alias)(rc)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -478,7 +479,7 @@ func (o TrainingPlanExtensionOffering) MarshalJSON() ([]byte, error) {
alias: alias(o),
StartDate: epochSeconds(o.StartDate),
EndDate: epochSeconds(o.EndDate),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [TrainingPlanExtensionOffering.MarshalJSON].
@@ -491,7 +492,7 @@ func (o *TrainingPlanExtensionOffering) UnmarshalJSON(data []byte) error {
EndDate float64 `json:"EndDate"`
}{alias: (*alias)(o)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/sagemaker/training_plans.go b/services/sagemaker/training_plans.go
index 54c17dcc4f..c043dc6333 100644
--- a/services/sagemaker/training_plans.go
+++ b/services/sagemaker/training_plans.go
@@ -2,13 +2,14 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrTrainingPlanNotFound is returned when a training plan does not exist.
@@ -48,7 +49,7 @@ func (r *ReservedCapacitySummary) MarshalJSON() ([]byte, error) {
alias: (*alias)(r),
StartTime: epochSecondsPtr(r.StartTime),
EndTime: epochSecondsPtr(r.EndTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [ReservedCapacitySummary.MarshalJSON],
@@ -62,7 +63,7 @@ func (r *ReservedCapacitySummary) UnmarshalJSON(data []byte) error {
EndTime *float64 `json:"EndTime,omitempty"`
}{alias: (*alias)(r)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -142,7 +143,7 @@ func (e TrainingPlanExtension) MarshalJSON() ([]byte, error) {
ExtendedAt: epochSeconds(e.ExtendedAt),
StartDate: epochSeconds(e.StartDate),
EndDate: epochSeconds(e.EndDate),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [TrainingPlanExtension.MarshalJSON], read
@@ -157,7 +158,7 @@ func (e *TrainingPlanExtension) UnmarshalJSON(data []byte) error {
EndDate float64 `json:"EndDate"`
}{alias: (*alias)(e)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
@@ -184,7 +185,7 @@ func (t *TrainingPlan) MarshalJSON() ([]byte, error) {
CreationTime: epochSeconds(t.CreationTime),
StartTime: epochSecondsPtr(t.StartTime),
EndTime: epochSecondsPtr(t.EndTime),
- })
+ }, jsonx.V1())
}
// UnmarshalJSON is the inverse of [TrainingPlan.MarshalJSON], read by
@@ -199,7 +200,7 @@ func (t *TrainingPlan) UnmarshalJSON(data []byte) error {
CreationTime float64 `json:"CreationTime"`
}{alias: (*alias)(t)}
- if err := json.Unmarshal(data, &aux); err != nil {
+ if err := json.Unmarshal(data, &aux, jsonx.V1()); err != nil {
return err
}
diff --git a/services/sagemaker/user_profiles.go b/services/sagemaker/user_profiles.go
index c9b472ff08..14755b725d 100644
--- a/services/sagemaker/user_profiles.go
+++ b/services/sagemaker/user_profiles.go
@@ -2,7 +2,7 @@ package sagemaker
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"sort"
@@ -45,13 +45,13 @@ type UserProfile struct {
Status string `json:"Status"`
SingleSignOnUserIdentifier string `json:"SingleSignOnUserIdentifier,omitempty"`
SingleSignOnUserValue string `json:"SingleSignOnUserValue,omitempty"`
- UserSettings json.RawMessage `json:"UserSettings,omitempty"`
+ UserSettings jsontext.Value `json:"UserSettings,omitempty"`
}
func cloneUserProfile(up *UserProfile) *UserProfile {
cp := *up
cp.Tags = maps.Clone(up.Tags)
- cp.UserSettings = append(json.RawMessage(nil), up.UserSettings...)
+ cp.UserSettings = append(jsontext.Value(nil), up.UserSettings...)
return &cp
}
@@ -60,7 +60,7 @@ func cloneUserProfile(up *UserProfile) *UserProfile {
type CreateUserProfileOptions struct {
SingleSignOnUserIdentifier string
SingleSignOnUserValue string
- UserSettings json.RawMessage
+ UserSettings jsontext.Value
}
// CreateUserProfile creates a new user profile in a domain.
@@ -226,7 +226,7 @@ func (b *InMemoryBackend) DeleteUserProfile(ctx context.Context, domainID, name
// (api_op_UpdateUserProfile.go:26-38). UserSettings is omitted from the
// request when unset, so only a non-empty value is applied.
type UpdateUserProfileOptions struct {
- UserSettings json.RawMessage
+ UserSettings jsontext.Value
}
// UpdateUserProfile updates a user profile in a domain. Returns the updated profile.
diff --git a/services/sagemaker/variant_weight_default_test.go b/services/sagemaker/variant_weight_default_test.go
index 89758544d6..5e94114547 100644
--- a/services/sagemaker/variant_weight_default_test.go
+++ b/services/sagemaker/variant_weight_default_test.go
@@ -1,13 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
)
@@ -73,7 +74,7 @@ func TestEndpointConfig_VariantWeightDefaultsToOne(t *testing.T) {
DesiredWeight float64 `json:"DesiredWeight"`
} `json:"ProductionVariants"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &ep))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &ep, jsonx.V1()))
require.Len(t, ep.ProductionVariants, 1)
assert.InDelta(t, tt.want, ep.ProductionVariants[0].DesiredWeight, 0.0001)
@@ -85,7 +86,7 @@ func TestEndpointConfig_VariantWeightDefaultsToOne(t *testing.T) {
InitialVariantWeight float64 `json:"InitialVariantWeight"`
} `json:"ProductionVariants"`
}
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &cfg))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &cfg, jsonx.V1()))
require.Len(t, cfg.ProductionVariants, 1)
assert.InDelta(t, tt.want, cfg.ProductionVariants[0].InitialVariantWeight, 0.0001)
})
diff --git a/services/sagemaker/wire_field_fixes_respsweep2_test.go b/services/sagemaker/wire_field_fixes_respsweep2_test.go
index 019f58c0b6..b7d4a1cf11 100644
--- a/services/sagemaker/wire_field_fixes_respsweep2_test.go
+++ b/services/sagemaker/wire_field_fixes_respsweep2_test.go
@@ -1,7 +1,7 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -11,6 +11,8 @@ import (
smtypes "github.com/aws/aws-sdk-go-v2/service/sagemaker/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestCreateEndpointConfig_DataCaptureConfig proves
@@ -83,7 +85,7 @@ func TestCreateEndpointConfig_VpcConfigSurvivesEmptyArrays(t *testing.T) {
require.Equal(t, http.StatusOK, rr.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
vpc, ok := resp["VpcConfig"].(map[string]any)
require.True(t, ok, "VpcConfig key must be present")
@@ -128,7 +130,7 @@ func TestCreateTrainingJob_ModelArtifactsRequiredWhileInProgress(t *testing.T) {
require.Equal(t, http.StatusOK, rr.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
require.Equal(t, "InProgress", resp["TrainingJobStatus"])
artifacts, ok := resp["ModelArtifacts"].(map[string]any)
@@ -267,7 +269,7 @@ func TestCreatePipeline_ParallelismConfigurationSurvivesZero(t *testing.T) {
require.Equal(t, http.StatusOK, rr.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
pc, ok := resp["ParallelismConfiguration"].(map[string]any)
require.True(t, ok, "ParallelismConfiguration key must be present")
@@ -301,7 +303,7 @@ func TestCreateProcessingJob_OutputsSurvivesEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rr.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
poc, ok := resp["ProcessingOutputConfig"].(map[string]any)
require.True(t, ok)
@@ -337,7 +339,7 @@ func TestDescribeAutoMLJob_RequiredFieldsSurviveEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rr.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
outCfg, ok := resp["OutputDataConfig"].(map[string]any)
require.True(t, ok)
@@ -421,7 +423,7 @@ func TestCreateCompilationJob_InputConfigOrModelPackageVersionArnRequired(t *tes
require.Equal(t, http.StatusOK, rr.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
inCfg, ok := resp["InputConfig"].(map[string]any)
require.True(t, ok)
s3uri, ok := inCfg["S3Uri"]
@@ -472,7 +474,7 @@ func TestListTrainingJobsForHPTuningJob_TunedHyperParametersSurvivesEmpty(t *tes
require.Equal(t, http.StatusOK, rr.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["TrainingJobSummaries"].([]any)
require.True(t, ok)
require.Len(t, summaries, 1)
diff --git a/services/sagemaker/wire_field_fixes_respsweep_test.go b/services/sagemaker/wire_field_fixes_respsweep_test.go
index 22773e74bb..49a99186a8 100644
--- a/services/sagemaker/wire_field_fixes_respsweep_test.go
+++ b/services/sagemaker/wire_field_fixes_respsweep_test.go
@@ -1,12 +1,14 @@
package sagemaker_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestCompilationJob_RequiredOutputMembersSurviveInProgress drives
@@ -47,7 +49,7 @@ func TestCompilationJob_RequiredOutputMembersSurviveInProgress(t *testing.T) {
require.Equal(t, http.StatusOK, rr.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
require.Equal(
t, "INPROGRESS", resp["CompilationJobStatus"],
@@ -86,7 +88,7 @@ func TestModelCard_ContentSurvivesEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rr.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp, jsonx.V1()))
content, ok := resp["Content"]
assert.True(t, ok, "Content key must be present even when empty")
diff --git a/services/sagemakerruntime/endpoint_validation_test.go b/services/sagemakerruntime/endpoint_validation_test.go
index 002af55518..d6eb33d23a 100644
--- a/services/sagemakerruntime/endpoint_validation_test.go
+++ b/services/sagemakerruntime/endpoint_validation_test.go
@@ -2,7 +2,7 @@ package sagemakerruntime_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
"github.com/blackbirdworks/gopherstack/services/sagemakerruntime"
@@ -88,7 +89,7 @@ func TestHandler_EndpointValidation(t *testing.T) {
if want == http.StatusBadRequest {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationError", body["__type"])
assert.Contains(t, body["message"], tt.endpointName)
}
diff --git a/services/sagemakerruntime/handler.go b/services/sagemakerruntime/handler.go
index 70301fd784..98e75a3e34 100644
--- a/services/sagemakerruntime/handler.go
+++ b/services/sagemakerruntime/handler.go
@@ -3,7 +3,7 @@ package sagemakerruntime
import (
"context"
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"hash/crc32"
"math"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -266,7 +267,7 @@ func (h *Handler) handleInvokeEndpointAsync(
acc.FailureLocation,
)
- out, err := json.Marshal(map[string]string{"InferenceId": acc.InferenceID})
+ out, err := json.Marshal(map[string]string{"InferenceId": acc.InferenceID}, jsonx.V1())
if err != nil {
return err
}
diff --git a/services/sagemakerruntime/handler_test.go b/services/sagemakerruntime/handler_test.go
index e160516882..2cbd724645 100644
--- a/services/sagemakerruntime/handler_test.go
+++ b/services/sagemakerruntime/handler_test.go
@@ -2,7 +2,7 @@ package sagemakerruntime_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/sdktest"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/sagemakerruntime"
@@ -54,7 +55,7 @@ func doRequestWithHeaders(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -294,7 +295,7 @@ func TestHandler_ErrorCodes(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, tt.wantType, body["__type"])
})
}
diff --git a/services/sagemakerruntime/invoke_endpoint_async_test.go b/services/sagemakerruntime/invoke_endpoint_async_test.go
index cbb85c7940..c4a95f990c 100644
--- a/services/sagemakerruntime/invoke_endpoint_async_test.go
+++ b/services/sagemakerruntime/invoke_endpoint_async_test.go
@@ -1,7 +1,7 @@
package sagemakerruntime_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -10,6 +10,8 @@ import (
sagemakerruntimesdk "github.com/aws/aws-sdk-go-v2/service/sagemakerruntime"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// --- InvokeEndpointAsync tests ---
@@ -41,7 +43,7 @@ func TestHandler_InvokeEndpointAsync(t *testing.T) {
assert.Equal(t, http.StatusAccepted, rec.Code)
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["InferenceId"])
assert.NotContains(t, out, "OutputLocation")
assert.NotContains(t, out, "FailureLocation")
@@ -351,7 +353,7 @@ func TestAsyncInvocation_BodyInputLocationMutualExclusion(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "ValidationError", out["__type"])
assert.Contains(t, out["message"], "mutually exclusive")
}
diff --git a/services/sagemakerruntime/invoke_errors_test.go b/services/sagemakerruntime/invoke_errors_test.go
index 0925f80743..bbabac3562 100644
--- a/services/sagemakerruntime/invoke_errors_test.go
+++ b/services/sagemakerruntime/invoke_errors_test.go
@@ -1,7 +1,7 @@
package sagemakerruntime_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sagemakerruntime"
)
@@ -36,7 +37,7 @@ func TestInvoke_NoContainerBackend_NeverFabricatesOutput(t *testing.T) {
require.Equal(t, http.StatusServiceUnavailable, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ServiceUnavailable", body["__type"])
assert.NotContains(t, rec.Body.String(), "mock")
assert.Empty(t, h.Backend.ListInvocations(), "a failed invoke must not be recorded")
@@ -95,7 +96,7 @@ func TestInvoke_ErrorMapping(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, tt.wantType, body["__type"])
})
}
diff --git a/services/sagemakerruntime/multi_region_test.go b/services/sagemakerruntime/multi_region_test.go
index ae59a72123..08f71637ea 100644
--- a/services/sagemakerruntime/multi_region_test.go
+++ b/services/sagemakerruntime/multi_region_test.go
@@ -2,7 +2,8 @@ package sagemakerruntime_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/sagemaker"
"github.com/blackbirdworks/gopherstack/services/sagemakerruntime"
@@ -152,8 +154,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/sagemakerruntime/persistence.go b/services/sagemakerruntime/persistence.go
index 1a80643607..e7dc4c3f60 100644
--- a/services/sagemakerruntime/persistence.go
+++ b/services/sagemakerruntime/persistence.go
@@ -2,9 +2,11 @@ package sagemakerruntime
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -32,10 +34,10 @@ const sagemakerruntimeSnapshotVersion = 1
// incompatible (older or newer) build of this backend as though it were the
// current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Invocations []*Invocation `json:"invocations"`
- NextID uint64 `json:"nextId"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Invocations []*Invocation `json:"invocations"`
+ NextID uint64 `json:"nextId"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
@@ -64,7 +66,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
NextID: b.nextID,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "sagemakerruntime: failed to marshal snapshot", "error", err)
diff --git a/services/scheduler/handler.go b/services/scheduler/handler.go
index 61753a3150..6f7fcddab8 100644
--- a/services/scheduler/handler.go
+++ b/services/scheduler/handler.go
@@ -3,7 +3,8 @@ package scheduler
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -16,6 +17,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -266,7 +268,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
return ""
}
var req scheduleNameInput
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return req.Name
}
@@ -443,13 +445,13 @@ func (h *Handler) enrichRESTBody(action, name string, body []byte, q url.Values)
enrichTagsPath(m, name, action, q)
}
- result, _ := json.Marshal(m)
+ result, _ := json.Marshal(m, jsonx.V1())
return result
}
// enrichScheduleByPath injects Name from the URL path and GroupName from the query string.
-func enrichScheduleByPath(m map[string]json.RawMessage, name string, q url.Values) {
+func enrichScheduleByPath(m map[string]jsontext.Value, name string, q url.Values) {
if name != "" {
setJSONString(m, "Name", name)
}
@@ -460,7 +462,7 @@ func enrichScheduleByPath(m map[string]json.RawMessage, name string, q url.Value
}
// enrichListSchedulesQuery injects ListSchedules query parameters into the JSON map.
-func enrichListSchedulesQuery(m map[string]json.RawMessage, q url.Values) {
+func enrichListSchedulesQuery(m map[string]jsontext.Value, q url.Values) {
if sg := q.Get("ScheduleGroup"); sg != "" {
setJSONString(m, "GroupName", sg)
}
@@ -478,7 +480,7 @@ func enrichListSchedulesQuery(m map[string]json.RawMessage, q url.Values) {
}
// enrichListScheduleGroupsQuery injects ListScheduleGroups query parameters into the JSON map.
-func enrichListScheduleGroupsQuery(m map[string]json.RawMessage, q url.Values) {
+func enrichListScheduleGroupsQuery(m map[string]jsontext.Value, q url.Values) {
for _, qk := range []struct{ query, json string }{
{keyNamePrefix, keyNamePrefix},
{keyNextToken, keyNextToken},
@@ -494,7 +496,7 @@ func enrichListScheduleGroupsQuery(m map[string]json.RawMessage, q url.Values) {
// string. UntagResource sends TagKeys as a repeated query parameter
// (?TagKeys=a&TagKeys=b, see awsRestjson1_serializeOpHttpBindingsUntagResourceInput's
// encoder.AddQuery("TagKeys", ...)), not a single comma-separated value.
-func enrichTagsPath(m map[string]json.RawMessage, name, action string, q url.Values) {
+func enrichTagsPath(m map[string]jsontext.Value, name, action string, q url.Values) {
if name != "" {
setJSONString(m, "ResourceArn", name)
}
@@ -504,34 +506,34 @@ func enrichTagsPath(m map[string]json.RawMessage, name, action string, q url.Val
}
if keys := q["TagKeys"]; len(keys) > 0 {
- keysJSON, _ := json.Marshal(keys)
- m["TagKeys"] = json.RawMessage(keysJSON)
+ keysJSON, _ := json.Marshal(keys, jsonx.V1())
+ m["TagKeys"] = jsontext.Value(keysJSON)
}
}
// unmarshalOrEmpty parses body as a JSON object, returning an empty map on failure.
-func unmarshalOrEmpty(body []byte) map[string]json.RawMessage {
- var m map[string]json.RawMessage
+func unmarshalOrEmpty(body []byte) map[string]jsontext.Value {
+ var m map[string]jsontext.Value
if len(body) > 0 {
- if err := json.Unmarshal(body, &m); err != nil {
- return make(map[string]json.RawMessage)
+ if err := json.Unmarshal(body, &m, jsonx.V1()); err != nil {
+ return make(map[string]jsontext.Value)
}
return m
}
- return make(map[string]json.RawMessage)
+ return make(map[string]jsontext.Value)
}
// setJSONString encodes value as a JSON string and sets it on key in the map,
// unless key is already present.
-func setJSONString(m map[string]json.RawMessage, key, value string) {
+func setJSONString(m map[string]jsontext.Value, key, value string) {
if _, exists := m[key]; exists {
return
}
- quoted, _ := json.Marshal(value)
- m[key] = json.RawMessage(quoted)
+ quoted, _ := json.Marshal(value, jsonx.V1())
+ m[key] = jsontext.Value(quoted)
}
// buildOps constructs the dispatch map once at handler creation time.
@@ -563,33 +565,30 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, ErrNotFound):
payload, _ := json.Marshal(service.JSONErrorResponse{
Type: "ResourceNotFoundException",
Message: errMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusNotFound, payload)
case errors.Is(err, ErrAlreadyExists):
payload, _ := json.Marshal(service.JSONErrorResponse{
Type: "ConflictException",
Message: errMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusConflict, payload)
case errors.Is(err, ErrValidation):
payload, _ := json.Marshal(service.JSONErrorResponse{
Type: errCodeValidation,
Message: errMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
// scheduler@v1.20.4 deserializers.go models ValidationException and
@@ -597,18 +596,18 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
// scanning every awsRestjson1_deserializeOpError* switch), so both are safe
// blanket fallbacks regardless of which operation reached this path.
case errors.Is(err, errInvalidRequest), errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
payload, _ := json.Marshal(service.JSONErrorResponse{
Type: errCodeValidation,
Message: errMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
default:
payload, _ := json.Marshal(service.JSONErrorResponse{
Type: "InternalServerException",
Message: errMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusInternalServerError, payload)
}
diff --git a/services/scheduler/handler_error_type_internal_test.go b/services/scheduler/handler_error_type_internal_test.go
index ae609beac6..b2049e1c32 100644
--- a/services/scheduler/handler_error_type_internal_test.go
+++ b/services/scheduler/handler_error_type_internal_test.go
@@ -2,7 +2,7 @@ package scheduler
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -42,6 +43,6 @@ func TestHandleError_GenericErrorSurfacesInternalServerException(t *testing.T) {
assert.Equal(t, http.StatusInternalServerError, rec.Code)
var payload service.JSONErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &payload, jsonx.V1()))
assert.Equal(t, "InternalServerException", payload.Type)
}
diff --git a/services/scheduler/handler_test.go b/services/scheduler/handler_test.go
index 4742ce7448..8740822c23 100644
--- a/services/scheduler/handler_test.go
+++ b/services/scheduler/handler_test.go
@@ -2,7 +2,7 @@ package scheduler_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/scheduler"
)
@@ -27,7 +28,7 @@ func doSchedulerRequest(t *testing.T, h *scheduler.Handler, action string, body
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -78,7 +79,7 @@ func doRESTRequest(
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
diff --git a/services/scheduler/idempotency_test.go b/services/scheduler/idempotency_test.go
index 1cdf337e9a..7e78e6cf66 100644
--- a/services/scheduler/idempotency_test.go
+++ b/services/scheduler/idempotency_test.go
@@ -1,13 +1,15 @@
package scheduler_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestCreateSchedule_ClientToken_ReplaysOnRetry verifies that retrying a
@@ -32,13 +34,13 @@ func TestCreateSchedule_ClientToken_ReplaysOnRetry(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]string
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
rec2 := doSchedulerRequest(t, h, "CreateSchedule", req)
require.Equal(t, http.StatusOK, rec2.Code, "retry with the same ClientToken must replay, not 409")
var out2 map[string]string
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
assert.Equal(t, out1["ScheduleArn"], out2["ScheduleArn"])
@@ -49,7 +51,7 @@ func TestCreateSchedule_ClientToken_ReplaysOnRetry(t *testing.T) {
var list struct {
Schedules []map[string]any `json:"Schedules"`
}
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &list))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &list, jsonx.V1()))
assert.Len(t, list.Schedules, 1)
}
@@ -120,13 +122,13 @@ func TestCreateScheduleGroup_ClientToken_ReplaysOnRetry(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]string
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
rec2 := doSchedulerRequest(t, h, "CreateScheduleGroup", req)
require.Equal(t, http.StatusOK, rec2.Code, "retry with the same ClientToken must replay, not 409")
var out2 map[string]string
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
assert.Equal(t, out1["ScheduleGroupArn"], out2["ScheduleGroupArn"])
}
diff --git a/services/scheduler/persistence.go b/services/scheduler/persistence.go
index 72bf0df938..6bb035af08 100644
--- a/services/scheduler/persistence.go
+++ b/services/scheduler/persistence.go
@@ -2,7 +2,7 @@ package scheduler
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"time"
@@ -82,10 +82,10 @@ func persistedScheduleGroupKeyFn(v *persistedScheduleGroup) string { return v.AR
// decoding a snapshot from an incompatible (older or newer) build of this
// backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// buildPersistenceDTORegistry constructs the ephemeral DTO registry used by both
@@ -260,7 +260,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreResourceTables rebuilds the schedules and scheduleGroups store.Table
// values from snap's per-table JSON, factored out of Restore to keep Restore's
// own cognitive complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
dtoReg, scheduleDTOs, groupDTOs := buildPersistenceDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
diff --git a/services/scheduler/runner.go b/services/scheduler/runner.go
index df181480ea..74bf9b7636 100644
--- a/services/scheduler/runner.go
+++ b/services/scheduler/runner.go
@@ -2,12 +2,13 @@ package scheduler
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"strings"
"sync"
"time"
arnpkg "github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/roleauth"
@@ -598,7 +599,7 @@ func buildSchedulerPayload(s *Schedule) []byte {
ScheduleName: s.Name,
}
- b, _ := json.Marshal(p)
+ b, _ := json.Marshal(p, jsonx.V1())
return b
}
diff --git a/services/scheduler/schedule_groups_test.go b/services/scheduler/schedule_groups_test.go
index 83b15936c4..194b48261c 100644
--- a/services/scheduler/schedule_groups_test.go
+++ b/services/scheduler/schedule_groups_test.go
@@ -2,7 +2,8 @@ package scheduler_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/scheduler"
)
@@ -60,7 +62,7 @@ func TestSchedulerHandler_CreateScheduleGroup(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
if tt.wantARN {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["ScheduleGroupArn"], "arn:aws:scheduler:")
assert.Contains(t, resp["ScheduleGroupArn"], "schedule-group/my-group")
}
@@ -125,7 +127,7 @@ func TestCreateScheduleGroup_DescriptionNotAccepted(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
_, hasDescription := resp["Description"]
assert.False(t, hasDescription, "GetScheduleGroupOutput has no Description member on real AWS")
}
@@ -172,7 +174,7 @@ func TestSchedulerHandler_GetScheduleGroup(t *testing.T) {
if tt.wantName != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantName, resp["Name"])
assert.Contains(t, resp["Arn"], "schedule-group/"+tt.wantName)
assert.Equal(t, "ACTIVE", resp["State"])
@@ -192,7 +194,7 @@ func TestGetScheduleGroup_ReturnsCreationDate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["CreationDate"])
assert.NotEmpty(t, resp["LastModificationDate"])
}
@@ -206,11 +208,11 @@ func TestGetScheduleGroup_TimestampsAreEpochSeconds(t *testing.T) {
rec := doSchedulerRequest(t, h, "GetScheduleGroup", map[string]any{"Name": "default"})
require.Equal(t, http.StatusOK, rec.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
var cd float64
- require.NoError(t, json.Unmarshal(out["CreationDate"], &cd), "CreationDate must be a JSON number")
+ require.NoError(t, json.Unmarshal(out["CreationDate"], &cd, jsonx.V1()), "CreationDate must be a JSON number")
assert.Greater(t, cd, float64(0))
}
@@ -232,19 +234,19 @@ func TestGetScheduleGroup_OmitsTags(t *testing.T) {
rec2 := doSchedulerRequest(t, h, "GetScheduleGroup", map[string]any{"Name": "grp-with-tags"})
require.Equal(t, http.StatusOK, rec2.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
_, hasTags := out["Tags"]
assert.False(t, hasTags, "GetScheduleGroup must not include a Tags field (not in real GetScheduleGroupOutput)")
var groupARN string
- require.NoError(t, json.Unmarshal(out["Arn"], &groupARN))
+ require.NoError(t, json.Unmarshal(out["Arn"], &groupARN, jsonx.V1()))
listRec := doSchedulerRequest(t, h, "ListTagsForResource", map[string]any{"ResourceArn": groupARN})
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tagsMap := wireTagsToMap(t, listResp["Tags"])
assert.Equal(t, "test", tagsMap["env"])
}
@@ -371,7 +373,7 @@ func TestSchedulerHandler_ListScheduleGroups(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "ScheduleGroups")
groups, ok := resp["ScheduleGroups"].([]any)
require.True(t, ok)
@@ -395,7 +397,7 @@ func TestListScheduleGroups_FilterByNamePrefix(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups := resp["ScheduleGroups"].([]any)
require.Len(t, groups, 1)
@@ -417,7 +419,7 @@ func TestListScheduleGroups_Sorted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups := resp["ScheduleGroups"].([]any)
names := make([]string, len(groups))
@@ -441,13 +443,13 @@ func TestListScheduleGroups_TimestampsAreEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out struct {
- ScheduleGroups []map[string]json.RawMessage `json:"ScheduleGroups"`
+ ScheduleGroups []map[string]jsontext.Value `json:"ScheduleGroups"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.ScheduleGroups)
var cd float64
- require.NoError(t, json.Unmarshal(out.ScheduleGroups[0]["CreationDate"], &cd))
+ require.NoError(t, json.Unmarshal(out.ScheduleGroups[0]["CreationDate"], &cd, jsonx.V1()))
assert.Greater(t, cd, float64(0))
}
@@ -469,13 +471,13 @@ func TestListScheduleGroups_OmitsTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out struct {
- ScheduleGroups []map[string]json.RawMessage `json:"ScheduleGroups"`
+ ScheduleGroups []map[string]jsontext.Value `json:"ScheduleGroups"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
for _, g := range out.ScheduleGroups {
var name string
- require.NoError(t, json.Unmarshal(g["Name"], &name))
+ require.NoError(t, json.Unmarshal(g["Name"], &name, jsonx.V1()))
if name == "list-grp-tags" {
_, hasTags := g["Tags"]
@@ -503,15 +505,15 @@ func TestListScheduleGroups_MaxResultsPagination(t *testing.T) {
rec := doSchedulerRequest(t, h, "ListScheduleGroups", map[string]any{"MaxResults": "2"})
require.Equal(t, http.StatusOK, rec.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
- var groups []json.RawMessage
- require.NoError(t, json.Unmarshal(out["ScheduleGroups"], &groups))
+ var groups []jsontext.Value
+ require.NoError(t, json.Unmarshal(out["ScheduleGroups"], &groups, jsonx.V1()))
assert.Len(t, groups, 2)
var token string
- require.NoError(t, json.Unmarshal(out["NextToken"], &token))
+ require.NoError(t, json.Unmarshal(out["NextToken"], &token, jsonx.V1()))
assert.NotEmpty(t, token)
}
@@ -527,7 +529,7 @@ func TestRESTListScheduleGroups(t *testing.T) {
var out struct {
ScheduleGroups []map[string]any `json:"ScheduleGroups"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.ScheduleGroups, "default group should always be present")
}
@@ -551,7 +553,7 @@ func TestSchedulerHandler_ScheduleGroup_RESTRouting(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, http.StatusOK, rec2.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "my-group", getResp["Name"])
req3 := httptest.NewRequest(http.MethodGet, "/schedule-groups", nil)
diff --git a/services/scheduler/schedules_list_test.go b/services/scheduler/schedules_list_test.go
index 3d48eac0d7..c13ad8093f 100644
--- a/services/scheduler/schedules_list_test.go
+++ b/services/scheduler/schedules_list_test.go
@@ -2,13 +2,15 @@ package scheduler_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/scheduler"
)
@@ -39,7 +41,7 @@ func TestSchedulerHandler_ListSchedules(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "Schedules")
schedules, ok := resp["Schedules"].([]any)
require.True(t, ok)
@@ -64,7 +66,7 @@ func TestListSchedules_FilterByGroupName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
schedules := resp["Schedules"].([]any)
require.Len(t, schedules, 1)
@@ -84,7 +86,7 @@ func TestListSchedules_FilterByNamePrefix(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
schedules := resp["Schedules"].([]any)
assert.Len(t, schedules, 2)
@@ -108,7 +110,7 @@ func TestListSchedules_FilterByState(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
schedules := resp["Schedules"].([]any)
require.Len(t, schedules, 1)
@@ -128,7 +130,7 @@ func TestListSchedules_Sorted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
schedules := resp["Schedules"].([]any)
require.Len(t, schedules, 3)
@@ -152,7 +154,7 @@ func TestListSchedules_IncludesGroupNameAndDates(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
schedules := resp["Schedules"].([]any)
require.Len(t, schedules, 1)
@@ -174,13 +176,13 @@ func TestListSchedules_TimestampsAreEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out struct {
- Schedules []map[string]json.RawMessage `json:"Schedules"`
+ Schedules []map[string]jsontext.Value `json:"Schedules"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.Schedules)
var cd float64
- require.NoError(t, json.Unmarshal(out.Schedules[0]["CreationDate"], &cd))
+ require.NoError(t, json.Unmarshal(out.Schedules[0]["CreationDate"], &cd, jsonx.V1()))
assert.Greater(t, cd, float64(0))
}
@@ -198,7 +200,7 @@ func TestListSchedules_NextTokenOmittedWhenComplete(t *testing.T) {
NextToken string `json:"NextToken"`
Schedules []any `json:"Schedules"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// NextToken should be empty when all results fit.
assert.Empty(t, out.NextToken)
}
@@ -217,26 +219,26 @@ func TestListSchedules_MaxResultsPagination(t *testing.T) {
rec1 := doSchedulerRequest(t, h, "ListSchedules", map[string]any{"MaxResults": "2"})
require.Equal(t, http.StatusOK, rec1.Code)
- var page1 map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1))
+ var page1 map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &page1, jsonx.V1()))
- var schedules1 []json.RawMessage
- require.NoError(t, json.Unmarshal(page1["Schedules"], &schedules1))
+ var schedules1 []jsontext.Value
+ require.NoError(t, json.Unmarshal(page1["Schedules"], &schedules1, jsonx.V1()))
require.Len(t, schedules1, 2)
var nextToken string
- require.NoError(t, json.Unmarshal(page1["NextToken"], &nextToken))
+ require.NoError(t, json.Unmarshal(page1["NextToken"], &nextToken, jsonx.V1()))
assert.NotEmpty(t, nextToken)
// Get second page using the token.
rec2 := doSchedulerRequest(t, h, "ListSchedules", map[string]any{"MaxResults": "2", "NextToken": nextToken})
require.Equal(t, http.StatusOK, rec2.Code)
- var page2 map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2))
+ var page2 map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &page2, jsonx.V1()))
- var schedules2 []json.RawMessage
- require.NoError(t, json.Unmarshal(page2["Schedules"], &schedules2))
+ var schedules2 []jsontext.Value
+ require.NoError(t, json.Unmarshal(page2["Schedules"], &schedules2, jsonx.V1()))
assert.Len(t, schedules2, 2)
}
@@ -254,11 +256,11 @@ func TestListSchedules_NoTokenReturnsAll(t *testing.T) {
rec := doSchedulerRequest(t, h, "ListSchedules", map[string]any{})
require.Equal(t, http.StatusOK, rec.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
- var schedules []json.RawMessage
- require.NoError(t, json.Unmarshal(out["Schedules"], &schedules))
+ var schedules []jsontext.Value
+ require.NoError(t, json.Unmarshal(out["Schedules"], &schedules, jsonx.V1()))
assert.Len(t, schedules, 3)
assert.Nil(t, out["NextToken"])
}
@@ -286,9 +288,9 @@ func TestListSchedules_IncludesTargetSummary(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out struct {
- Schedules []map[string]json.RawMessage `json:"Schedules"`
+ Schedules []map[string]jsontext.Value `json:"Schedules"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Schedules, 1)
item := out.Schedules[0]
@@ -298,13 +300,13 @@ func TestListSchedules_IncludesTargetSummary(t *testing.T) {
var target struct {
Arn string `json:"Arn"`
}
- require.NoError(t, json.Unmarshal(targetRaw, &target))
+ require.NoError(t, json.Unmarshal(targetRaw, &target, jsonx.V1()))
assert.Equal(t, "arn:aws:lambda:us-east-1:123:function:fn", target.Arn)
// Real AWS's TargetSummary has no RoleArn field; assert gopherstack doesn't
// invent one on the wire.
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(targetRaw, &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(targetRaw, &raw, jsonx.V1()))
_, hasRoleArn := raw["RoleArn"]
assert.False(t, hasRoleArn, "ListSchedules Target summary must not include RoleArn (not in real TargetSummary)")
}
@@ -338,7 +340,7 @@ func TestListSchedules_TargetMatchesGetSchedule(t *testing.T) {
} `json:"Target"`
} `json:"Schedules"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.Schedules, 1)
assert.Equal(t, targetArn, listOut.Schedules[0].Target.Arn)
}
diff --git a/services/scheduler/schedules_rest_test.go b/services/scheduler/schedules_rest_test.go
index 728eb6d98f..321d0528e0 100644
--- a/services/scheduler/schedules_rest_test.go
+++ b/services/scheduler/schedules_rest_test.go
@@ -1,12 +1,15 @@
package scheduler_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestRESTGetScheduleByPath verifies GET /schedules/{name} works with REST routing.
@@ -20,7 +23,7 @@ func TestRESTGetScheduleByPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "rest-sched", out["Name"])
}
@@ -37,7 +40,7 @@ func TestRESTGetScheduleWithGroupName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "grp-sched", out["Name"])
assert.Equal(t, "default", out["GroupName"])
}
@@ -99,7 +102,7 @@ func TestRESTUpdateSchedule(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "rate(10 minutes)", out["ScheduleExpression"])
}
@@ -124,7 +127,7 @@ func TestRESTListSchedulesWithGroupNameFilter(t *testing.T) {
var out struct {
Schedules []map[string]any `json:"Schedules"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Schedules, 1)
assert.Equal(t, "s-grp", out.Schedules[0]["Name"])
}
@@ -143,7 +146,7 @@ func TestRESTCreateScheduleByPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["ScheduleArn"])
// Verify it can be retrieved.
@@ -166,11 +169,11 @@ func TestRESTListSchedules_MaxResultsQueryParam(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
- var schedules []json.RawMessage
- require.NoError(t, json.Unmarshal(out["Schedules"], &schedules))
+ var schedules []jsontext.Value
+ require.NoError(t, json.Unmarshal(out["Schedules"], &schedules, jsonx.V1()))
assert.Len(t, schedules, 2)
}
diff --git a/services/scheduler/schedules_target_test.go b/services/scheduler/schedules_target_test.go
index c185a8c45c..137037e999 100644
--- a/services/scheduler/schedules_target_test.go
+++ b/services/scheduler/schedules_target_test.go
@@ -1,12 +1,15 @@
package scheduler_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestCreateSchedule_RetryPolicyRoundTrip verifies RetryPolicy fields round-trip.
@@ -33,14 +36,14 @@ func TestCreateSchedule_RetryPolicyRoundTrip(t *testing.T) {
get := doSchedulerRequest(t, h, "GetSchedule", map[string]any{"Name": "sched-retry"})
require.Equal(t, http.StatusOK, get.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out, jsonx.V1()))
- var target map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(out["Target"], &target))
+ var target map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(out["Target"], &target, jsonx.V1()))
var rp map[string]int
- require.NoError(t, json.Unmarshal(target["RetryPolicy"], &rp))
+ require.NoError(t, json.Unmarshal(target["RetryPolicy"], &rp, jsonx.V1()))
assert.Equal(t, 3600, rp["MaximumEventAgeInSeconds"])
assert.Equal(t, 10, rp["MaximumRetryAttempts"])
}
@@ -131,14 +134,14 @@ func TestUpdateSchedule_RetryPolicyPersisted(t *testing.T) {
get := doSchedulerRequest(t, h, "GetSchedule", map[string]any{"Name": "retry-update"})
require.Equal(t, http.StatusOK, get.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out, jsonx.V1()))
- var target map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(out["Target"], &target))
+ var target map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(out["Target"], &target, jsonx.V1()))
var rp map[string]int
- require.NoError(t, json.Unmarshal(target["RetryPolicy"], &rp))
+ require.NoError(t, json.Unmarshal(target["RetryPolicy"], &rp, jsonx.V1()))
assert.Equal(t, 5, rp["MaximumRetryAttempts"])
}
@@ -167,14 +170,14 @@ func TestCreateSchedule_DeadLetterConfigRoundTrip(t *testing.T) {
get := doSchedulerRequest(t, h, "GetSchedule", map[string]any{"Name": "sched-dlq"})
require.Equal(t, http.StatusOK, get.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out, jsonx.V1()))
- var target map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(out["Target"], &target))
+ var target map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(out["Target"], &target, jsonx.V1()))
var dlc map[string]string
- require.NoError(t, json.Unmarshal(target["DeadLetterConfig"], &dlc))
+ require.NoError(t, json.Unmarshal(target["DeadLetterConfig"], &dlc, jsonx.V1()))
assert.Equal(t, dlqARN, dlc["Arn"])
}
@@ -224,14 +227,14 @@ func TestCreateSchedule_EventBridgeParametersRoundTrip(t *testing.T) {
get := doSchedulerRequest(t, h, "GetSchedule", map[string]any{"Name": "sched-eb"})
require.Equal(t, http.StatusOK, get.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out, jsonx.V1()))
- var target map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(out["Target"], &target))
+ var target map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(out["Target"], &target, jsonx.V1()))
var ebParams map[string]string
- require.NoError(t, json.Unmarshal(target["EventBridgeParameters"], &ebParams))
+ require.NoError(t, json.Unmarshal(target["EventBridgeParameters"], &ebParams, jsonx.V1()))
assert.Equal(t, "MyDetailType", ebParams["DetailType"])
assert.Equal(t, "com.example.app", ebParams["Source"])
}
@@ -299,14 +302,14 @@ func TestCreateSchedule_KinesisParametersRoundTrip(t *testing.T) {
get := doSchedulerRequest(t, h, "GetSchedule", map[string]any{"Name": "sched-kinesis"})
require.Equal(t, http.StatusOK, get.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out, jsonx.V1()))
- var target map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(out["Target"], &target))
+ var target map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(out["Target"], &target, jsonx.V1()))
var kp map[string]string
- require.NoError(t, json.Unmarshal(target["KinesisParameters"], &kp))
+ require.NoError(t, json.Unmarshal(target["KinesisParameters"], &kp, jsonx.V1()))
assert.Equal(t, "my-partition-key", kp["PartitionKey"])
}
@@ -357,14 +360,14 @@ func TestCreateSchedule_SqsParametersMessageGroupIdRoundTrip(t *testing.T) {
get := doSchedulerRequest(t, h, "GetSchedule", map[string]any{"Name": "sched-fifo"})
require.Equal(t, http.StatusOK, get.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out, jsonx.V1()))
- var target map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(out["Target"], &target))
+ var target map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(out["Target"], &target, jsonx.V1()))
var sqsP map[string]string
- require.NoError(t, json.Unmarshal(target["SqsParameters"], &sqsP))
+ require.NoError(t, json.Unmarshal(target["SqsParameters"], &sqsP, jsonx.V1()))
assert.Equal(t, "grp-1", sqsP["MessageGroupId"])
}
@@ -395,17 +398,17 @@ func TestCreateSchedule_SageMakerPipelineParametersRoundTrip(t *testing.T) {
get := doSchedulerRequest(t, h, "GetSchedule", map[string]any{"Name": "sched-sm"})
require.Equal(t, http.StatusOK, get.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out, jsonx.V1()))
- var target map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(out["Target"], &target))
+ var target map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(out["Target"], &target, jsonx.V1()))
- var smParams map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(target["SageMakerPipelineParameters"], &smParams))
+ var smParams map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(target["SageMakerPipelineParameters"], &smParams, jsonx.V1()))
var paramList []map[string]string
- require.NoError(t, json.Unmarshal(smParams["PipelineParameterList"], ¶mList))
+ require.NoError(t, json.Unmarshal(smParams["PipelineParameterList"], ¶mList, jsonx.V1()))
require.Len(t, paramList, 2)
assert.Equal(t, "param1", paramList[0]["Name"])
assert.Equal(t, "val1", paramList[0]["Value"])
@@ -436,21 +439,21 @@ func TestCreateSchedule_EcsParametersRoundTrip(t *testing.T) {
get := doSchedulerRequest(t, h, "GetSchedule", map[string]any{"Name": "sched-ecs"})
require.Equal(t, http.StatusOK, get.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out, jsonx.V1()))
- var target map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(out["Target"], &target))
+ var target map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(out["Target"], &target, jsonx.V1()))
- var ecsP map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(target["EcsParameters"], &ecsP))
+ var ecsP map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(target["EcsParameters"], &ecsP, jsonx.V1()))
var tdArn string
- require.NoError(t, json.Unmarshal(ecsP["TaskDefinitionArn"], &tdArn))
+ require.NoError(t, json.Unmarshal(ecsP["TaskDefinitionArn"], &tdArn, jsonx.V1()))
assert.Equal(t, "arn:aws:ecs:us-east-1:000000000000:task-definition/my-td:1", tdArn)
var launchType string
- require.NoError(t, json.Unmarshal(ecsP["LaunchType"], &launchType))
+ require.NoError(t, json.Unmarshal(ecsP["LaunchType"], &launchType, jsonx.V1()))
assert.Equal(t, "FARGATE", launchType)
}
@@ -519,7 +522,7 @@ func TestCreateSchedule_EcsParametersNetworkConfigurationRoundtrip(t *testing.T)
} `json:"EcsParameters"`
} `json:"Target"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
cfg := out.Target.EcsParameters.NetworkConfiguration.AwsvpcConfiguration
assert.Equal(t, []string{"subnet-aaa", "subnet-bbb"}, cfg.Subnets)
@@ -564,7 +567,7 @@ func TestCreateSchedule_EcsParametersCapacityProviderStrategyRoundtrip(t *testin
} `json:"EcsParameters"`
} `json:"Target"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
strategy := out.Target.EcsParameters.CapacityProviderStrategy
require.Len(t, strategy, 2)
@@ -616,7 +619,7 @@ func TestCreateSchedule_EcsParametersPlacementConstraintsRoundtrip(t *testing.T)
} `json:"EcsParameters"`
} `json:"Target"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
constraints := out.Target.EcsParameters.PlacementConstraints
require.Len(t, constraints, 1)
@@ -666,7 +669,7 @@ func TestCreateSchedule_EcsParametersTaskTagsRoundtrip(t *testing.T) {
} `json:"EcsParameters"`
} `json:"Target"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
ecsTags := out.Target.EcsParameters.Tags
require.Len(t, ecsTags, 2)
@@ -704,11 +707,11 @@ func TestCreateSchedule_InputTransformerNotEchoed(t *testing.T) {
get := doSchedulerRequest(t, h, "GetSchedule", map[string]any{"Name": "sched-transformer"})
require.Equal(t, http.StatusOK, get.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out, jsonx.V1()))
- var target map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(out["Target"], &target))
+ var target map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(out["Target"], &target, jsonx.V1()))
_, present := target["InputTransformer"]
assert.False(t, present, "InputTransformer is not a real Scheduler Target member and must not be echoed back")
@@ -753,13 +756,13 @@ func TestFullTarget_UpdateRoundTrip(t *testing.T) {
get := doSchedulerRequest(t, h, "GetSchedule", map[string]any{"Name": "full-target"})
require.Equal(t, http.StatusOK, get.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(get.Body.Bytes(), &out, jsonx.V1()))
- var target map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(out["Target"], &target))
+ var target map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(out["Target"], &target, jsonx.V1()))
var kp map[string]string
- require.NoError(t, json.Unmarshal(target["KinesisParameters"], &kp))
+ require.NoError(t, json.Unmarshal(target["KinesisParameters"], &kp, jsonx.V1()))
assert.Equal(t, "updated-key", kp["PartitionKey"])
}
diff --git a/services/scheduler/schedules_test.go b/services/scheduler/schedules_test.go
index 6bc19fb18d..da5443a977 100644
--- a/services/scheduler/schedules_test.go
+++ b/services/scheduler/schedules_test.go
@@ -2,7 +2,8 @@ package scheduler_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"net/http"
"strings"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/scheduler"
)
@@ -73,7 +75,7 @@ func TestSchedulerHandler_CreateSchedule(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["ScheduleArn"], "arn:aws:scheduler:")
}
@@ -114,7 +116,7 @@ func TestSchedulerHandler_CreateScheduleDefaultState(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ENABLED", resp["State"])
}
@@ -139,7 +141,7 @@ func TestCreateSchedule_DefaultStateEnabled(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ENABLED", resp["State"])
}
@@ -404,7 +406,7 @@ func TestCreateSchedule_ScheduleExpression_Validation(t *testing.T) {
if tt.wantType != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
}
})
@@ -805,7 +807,7 @@ func TestCreateSchedule_FlexibleModeRequiresMaximumWindowInMinutes(t *testing.T)
if tt.wantCode == http.StatusBadRequest {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ValidationException", errResp["__type"])
}
})
@@ -835,7 +837,7 @@ func TestCreateSchedule_FlexibleWindowRoundtrip(t *testing.T) {
MaximumWindowInMinutes int `json:"MaximumWindowInMinutes"`
} `json:"FlexibleTimeWindow"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "FLEXIBLE", out.FlexibleTimeWindow.Mode)
assert.Equal(t, 20, out.FlexibleTimeWindow.MaximumWindowInMinutes)
}
@@ -864,7 +866,7 @@ func TestCreateSchedule_FlexibleTimeWindowMaximumWindowInMinutes(t *testing.T) {
MaximumWindowInMinutes int `json:"MaximumWindowInMinutes"`
} `json:"FlexibleTimeWindow"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "FLEXIBLE", out.FlexibleTimeWindow.Mode)
assert.Equal(t, 15, out.FlexibleTimeWindow.MaximumWindowInMinutes)
}
@@ -884,7 +886,7 @@ func TestSchedulerHandler_GetSchedule(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "my-schedule", resp["Name"])
assert.Equal(t, "rate(5 minutes)", resp["ScheduleExpression"])
assert.Contains(t, resp, "Target")
@@ -913,7 +915,7 @@ func TestGetSchedule_ReturnsGroupNameAndDates(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "mygroup", resp["GroupName"])
assert.Equal(t, "test description", resp["Description"])
@@ -938,7 +940,7 @@ func TestGetSchedule_ReturnsARNWithGroupName(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
assert.Contains(t, createResp["ScheduleArn"], "schedule/my-group/grouped-sched")
}
@@ -968,18 +970,18 @@ func TestGetSchedule_TimestampsAreEpochSeconds(t *testing.T) {
rec2 := doSchedulerRequest(t, h, "GetSchedule", map[string]any{"Name": "ts-schedule"})
require.Equal(t, http.StatusOK, rec2.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
// CreationDate must be a JSON number (float64), not a string.
var creationDate float64
- require.NoError(t, json.Unmarshal(out["CreationDate"], &creationDate), "CreationDate must be a number")
+ require.NoError(t, json.Unmarshal(out["CreationDate"], &creationDate, jsonx.V1()), "CreationDate must be a number")
assert.Greater(t, creationDate, float64(0), "CreationDate must be a positive epoch value")
var lastModDate float64
require.NoError(
t,
- json.Unmarshal(out["LastModificationDate"], &lastModDate),
+ json.Unmarshal(out["LastModificationDate"], &lastModDate, jsonx.V1()),
"LastModificationDate must be a number",
)
assert.GreaterOrEqual(t, lastModDate, creationDate)
@@ -1006,11 +1008,11 @@ func TestCreateSchedule_EpochSecondsConvertBack(t *testing.T) {
rec2 := doSchedulerRequest(t, h, "GetSchedule", map[string]any{"Name": "epoch-test"})
require.Equal(t, http.StatusOK, rec2.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
var startDate float64
- require.NoError(t, json.Unmarshal(out["StartDate"], &startDate))
+ require.NoError(t, json.Unmarshal(out["StartDate"], &startDate, jsonx.V1()))
// The stored epoch should round-trip back to the same second.
assert.InDelta(t, epoch, startDate, 1.0)
}
@@ -1037,7 +1039,7 @@ func TestCreateSchedule_ActionAfterCompletion(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "DELETE", out["ActionAfterCompletion"])
}
@@ -1061,7 +1063,7 @@ func TestCreateSchedule_KmsKeyArn(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, kmsARN, out["KmsKeyArn"])
}
@@ -1086,8 +1088,8 @@ func TestCreateSchedule_StartDateEndDate(t *testing.T) {
rec2 := doSchedulerRequest(t, h, "GetSchedule", map[string]any{"Name": "dated-sched"})
require.Equal(t, http.StatusOK, rec2.Code)
- var out map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ var out map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
_, hasStart := out["StartDate"]
_, hasEnd := out["EndDate"]
@@ -1113,7 +1115,7 @@ func TestCreateSchedule_ScheduleExpressionTimezone(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "America/New_York", resp["ScheduleExpressionTimezone"])
}
@@ -1152,7 +1154,7 @@ func TestCreateSchedule_ScheduleExpressionTimezone_Validation(t *testing.T) {
if tt.wantCode != http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
})
@@ -1183,7 +1185,7 @@ func TestUpdateSchedule_ScheduleExpressionTimezone_Validation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
@@ -1261,13 +1263,13 @@ func TestSchedulerHandler_UpdateSchedule(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["ScheduleArn"], "arn:aws:scheduler:")
// Verify the update
getRec := doSchedulerRequest(t, h, "GetSchedule", map[string]any{"Name": "my-schedule"})
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "rate(10 minutes)", getResp["ScheduleExpression"])
assert.Equal(t, "DISABLED", getResp["State"])
}
@@ -1355,7 +1357,7 @@ func TestUpdateSchedule_WithActionAfterCompletion(t *testing.T) {
rec2 := doSchedulerRequest(t, h, "GetSchedule", map[string]any{"Name": "upd-aac"})
var out map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "NONE", out["ActionAfterCompletion"])
}
@@ -1440,7 +1442,7 @@ func TestUpdateSchedule_RequiredFieldValidation(t *testing.T) {
if tt.wantCode == http.StatusBadRequest {
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "ValidationException", errResp["__type"])
}
})
@@ -1490,7 +1492,7 @@ func TestUpdateSchedule_ScheduleExpression_SemanticValidation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &errResp, jsonx.V1()))
assert.Equal(t, "ValidationException", errResp["__type"])
})
}
@@ -1523,7 +1525,7 @@ func TestUpdateSchedule_DoesNotBlankFields(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &out, jsonx.V1()))
assert.Equal(t, "rate(10 minutes)", out["ScheduleExpression"])
@@ -1582,7 +1584,7 @@ func TestUpdateSchedule_ClearsOmittedOptionalFields(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.NewDecoder(getRec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(getRec.Body), &out, jsonx.V1()))
assert.NotContains(t, out, tc.field, "%s should be cleared after an update that omits it", tc.field)
})
diff --git a/services/scheduler/tags_test.go b/services/scheduler/tags_test.go
index 21f0569cef..0acd8ce1e7 100644
--- a/services/scheduler/tags_test.go
+++ b/services/scheduler/tags_test.go
@@ -2,7 +2,7 @@ package scheduler_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/scheduler"
)
@@ -30,7 +31,7 @@ func TestSchedulerHandler_TagResource(t *testing.T) {
"FlexibleTimeWindow": map[string]any{"Mode": "OFF"},
})
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
arn := createResp["ScheduleArn"]
rec := doSchedulerRequest(t, h, "TagResource", map[string]any{
@@ -56,7 +57,7 @@ func TestSchedulerHandler_ListTagsForResource(t *testing.T) {
"FlexibleTimeWindow": map[string]any{"Mode": "OFF"},
})
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
arn := createResp["ScheduleArn"]
doSchedulerRequest(t, h, "TagResource", map[string]any{
@@ -68,7 +69,7 @@ func TestSchedulerHandler_ListTagsForResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "Tags")
tags := wireTagsToMap(t, resp["Tags"])
assert.Equal(t, "prod", tags["env"])
@@ -88,7 +89,7 @@ func TestTagResource_AndListTags(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
schedARN := createResp["ScheduleArn"]
tagRec := doSchedulerRequest(t, h, "TagResource", map[string]any{
@@ -101,7 +102,7 @@ func TestTagResource_AndListTags(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags := wireTagsToMap(t, tagsResp["Tags"])
assert.Equal(t, "prod", tags["env"])
assert.Equal(t, "platform", tags["team"])
@@ -178,7 +179,7 @@ func TestSchedulerHandler_UntagResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
resourceARN = createResp["ScheduleArn"]
if len(tt.setupTags) > 0 {
@@ -201,7 +202,7 @@ func TestSchedulerHandler_UntagResource(t *testing.T) {
listRec := doSchedulerRequest(t, h, "ListTagsForResource", map[string]any{"ResourceArn": resourceARN})
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
remaining := wireTagsToMap(t, listResp["Tags"])
for _, key := range tt.wantRemoved {
assert.NotContains(t, remaining, key)
@@ -233,7 +234,7 @@ func TestUntagResource(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags := wireTagsToMap(t, tagsResp["Tags"])
assert.NotContains(t, tags, "k1")
assert.Contains(t, tags, "k2")
@@ -252,7 +253,7 @@ func TestSchedulerHandler_UntagResource_ScheduleGroup(t *testing.T) {
getRec := doSchedulerRequest(t, h, "GetScheduleGroup", map[string]any{"Name": "tagged-group"})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
groupARN, _ := getResp["Arn"].(string)
require.NotEmpty(t, groupARN)
@@ -265,7 +266,7 @@ func TestSchedulerHandler_UntagResource_ScheduleGroup(t *testing.T) {
listRec := doSchedulerRequest(t, h, "ListTagsForResource", map[string]any{"ResourceArn": groupARN})
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
remaining := wireTagsToMap(t, listResp["Tags"])
assert.NotContains(t, remaining, "env")
assert.Contains(t, remaining, "team")
@@ -279,7 +280,7 @@ func TestSchedulerHandler_TagResource_ScheduleGroup(t *testing.T) {
doSchedulerRequest(t, h, "CreateScheduleGroup", map[string]any{"Name": "tag-group"})
getRec := doSchedulerRequest(t, h, "GetScheduleGroup", map[string]any{"Name": "tag-group"})
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
groupARN, _ := getResp["Arn"].(string)
rec := doSchedulerRequest(t, h, "TagResource", map[string]any{
@@ -291,7 +292,7 @@ func TestSchedulerHandler_TagResource_ScheduleGroup(t *testing.T) {
listRec := doSchedulerRequest(t, h, "ListTagsForResource", map[string]any{"ResourceArn": groupARN})
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tagsMap := wireTagsToMap(t, listResp["Tags"])
assert.Equal(t, "team-a", tagsMap["owner"])
}
@@ -312,7 +313,7 @@ func TestRESTTagResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
schedARN := createOut["ScheduleArn"]
require.NotEmpty(t, schedARN)
@@ -327,7 +328,7 @@ func TestRESTTagResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var tagsOut map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &tagsOut, jsonx.V1()))
tags := wireTagsToMap(t, tagsOut["Tags"])
assert.Equal(t, "prod", tags["env"])
assert.Equal(t, "platform", tags["team"])
@@ -349,7 +350,7 @@ func TestRESTListTagsForResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
schedARN := createOut["ScheduleArn"]
// Tag the resource first via the standard operation.
@@ -362,7 +363,7 @@ func TestRESTListTagsForResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var tagsOut map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &tagsOut, jsonx.V1()))
tags := wireTagsToMap(t, tagsOut["Tags"])
assert.Equal(t, "val1", tags["key1"])
}
@@ -382,7 +383,7 @@ func TestRESTUntagResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
schedARN := createOut["ScheduleArn"]
_ = doSchedulerRequest(t, h, "TagResource", map[string]any{
@@ -397,7 +398,7 @@ func TestRESTUntagResource(t *testing.T) {
rec3 := doSchedulerRequest(t, h, "ListTagsForResource", map[string]any{"ResourceArn": schedARN})
var tagsOut map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &tagsOut, jsonx.V1()))
tags := wireTagsToMap(t, tagsOut["Tags"])
assert.NotContains(t, tags, "key1")
assert.Equal(t, "val2", tags["key2"])
@@ -422,7 +423,7 @@ func TestRESTUntagResourceMultipleTagKeys(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
schedARN := createOut["ScheduleArn"]
_ = doSchedulerRequest(t, h, "TagResource", map[string]any{
@@ -440,7 +441,7 @@ func TestRESTUntagResourceMultipleTagKeys(t *testing.T) {
rec3 := doSchedulerRequest(t, h, "ListTagsForResource", map[string]any{"ResourceArn": schedARN})
var tagsOut map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &tagsOut, jsonx.V1()))
tags := wireTagsToMap(t, tagsOut["Tags"])
assert.NotContains(t, tags, "key1")
assert.NotContains(t, tags, "key2")
diff --git a/services/scheduler/whitebox_test.go b/services/scheduler/whitebox_test.go
index 07263154cb..e85f7045fe 100644
--- a/services/scheduler/whitebox_test.go
+++ b/services/scheduler/whitebox_test.go
@@ -3,7 +3,7 @@ package scheduler
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,12 +12,14 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func doWhiteboxRequest(t *testing.T, h *Handler, action string, body any) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
diff --git a/services/secretsmanager/batchgetsecretvalue_test.go b/services/secretsmanager/batchgetsecretvalue_test.go
index 65768f0536..369f892d18 100644
--- a/services/secretsmanager/batchgetsecretvalue_test.go
+++ b/services/secretsmanager/batchgetsecretvalue_test.go
@@ -2,7 +2,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -334,7 +335,7 @@ func TestBatchGetSecretValue_SecretIDListTooLong_HTTP(t *testing.T) {
if tt.wantType != "" {
var errResp secretsmanager.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp.Type)
}
})
@@ -358,7 +359,7 @@ func TestBatchGetSecretValue_SecretIdListAndFiltersRejected(t *testing.T) {
"BatchGetSecretValue with both SecretIdList and Filters must return 400; body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", resp["__type"])
}
@@ -386,7 +387,7 @@ func TestBatchGetSecretValue_FilterUsesPrefix(t *testing.T) {
var out struct {
SecretValues []map[string]any `json:"SecretValues"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.SecretValues, 2,
"real AWS: BatchGetSecretValue filter uses prefix matching; body: %s", rec.Body.String())
}
@@ -426,7 +427,7 @@ func TestBatchGetSecretValue_HTTP(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.BatchGetSecretValueOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.SecretValues, 2)
assert.Empty(t, out.Errors)
},
@@ -446,7 +447,7 @@ func TestBatchGetSecretValue_HTTP(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.BatchGetSecretValueOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.SecretValues, 1)
assert.Len(t, out.Errors, 1)
assert.Equal(t, "ResourceNotFoundException", out.Errors[0].ErrorCode)
@@ -473,7 +474,7 @@ func TestBatchGetSecretValue_HTTP(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.BatchGetSecretValueOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.SecretValues, 2)
},
},
diff --git a/services/secretsmanager/cancelrotatesecret_test.go b/services/secretsmanager/cancelrotatesecret_test.go
index dcd207842b..4c37034866 100644
--- a/services/secretsmanager/cancelrotatesecret_test.go
+++ b/services/secretsmanager/cancelrotatesecret_test.go
@@ -2,7 +2,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -128,7 +129,7 @@ func TestCancelRotateSecret_HTTP(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.CancelRotateSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "cancel-rot", out.Name)
assert.NotEmpty(t, out.ARN)
},
diff --git a/services/secretsmanager/createsecret_test.go b/services/secretsmanager/createsecret_test.go
index f038db032a..acc1ad7c4c 100644
--- a/services/secretsmanager/createsecret_test.go
+++ b/services/secretsmanager/createsecret_test.go
@@ -6,7 +6,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -100,7 +101,7 @@ func TestCreateSecret_NameAWSPrefixHTTP(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp secretsmanager.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp.Type)
}
@@ -227,7 +228,7 @@ func TestCreateSecret_DuplicateHTTPStatus(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp secretsmanager.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceExistsException", errResp.Type)
}
@@ -459,7 +460,7 @@ func TestCreateSecret_ReplicaRegionsAddedIsReadableInReplicaRegion(t *testing.T)
require.Equal(t, http.StatusOK, getRec.Code)
var out secretsmanager.GetSecretValueOutput
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "secret-value", out.SecretString)
}
@@ -513,12 +514,12 @@ func TestCreateSecret_ReplicaRegionsAddedHTTP(t *testing.T) {
"AddReplicaRegions": []map[string]string{
{"Region": "eu-central-1"},
},
- })
+ }, jsonx.V1())
rec := doR1Request(t, h, "secretsmanager.CreateSecret", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var out secretsmanager.CreateSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.ReplicationStatus, 1)
assert.Equal(t, "eu-central-1", out.ReplicationStatus[0].Region)
}
@@ -540,7 +541,7 @@ func TestCreateSecret_ReplicaRegionsAddedMultipleHTTP(t *testing.T) {
{"Region": "eu-north-1"},
{"Region": "ap-east-1"},
},
- })
+ }, jsonx.V1())
rec := doR1Request(t, h, "secretsmanager.CreateSecret", string(body))
require.Equal(t, http.StatusOK, rec.Code)
@@ -548,7 +549,7 @@ func TestCreateSecret_ReplicaRegionsAddedMultipleHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var desc secretsmanager.DescribeSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc, jsonx.V1()))
assert.Len(t, desc.ReplicationStatus, 3)
}
@@ -801,7 +802,7 @@ func TestCreateSecret_DeletedNameCollision_HTTP(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var errResp secretsmanager.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, errResp.Type)
})
}
@@ -1007,7 +1008,7 @@ func TestCreateSecret_NameValidation(t *testing.T) {
t.Cleanup(b.StopRotationScheduler)
h := secretsmanager.NewHandler(b)
- body, _ := json.Marshal(map[string]string{"Name": tt.secretName, "SecretString": "v"})
+ body, _ := json.Marshal(map[string]string{"Name": tt.secretName, "SecretString": "v"}, jsonx.V1())
rec := doR1Request(t, h, "secretsmanager.CreateSecret", string(body))
assert.Equal(t, tt.wantStatus, rec.Code)
})
@@ -1033,7 +1034,7 @@ func TestCreateSecret_CreateDescribeRoundtrip(t *testing.T) {
},
}
- body, _ := json.Marshal(createBody)
+ body, _ := json.Marshal(createBody, jsonx.V1())
rec := doR1Request(t, h, "secretsmanager.CreateSecret", string(body))
require.Equal(t, http.StatusOK, rec.Code)
@@ -1041,7 +1042,7 @@ func TestCreateSecret_CreateDescribeRoundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var desc secretsmanager.DescribeSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc, jsonx.V1()))
assert.Equal(t, "roundtrip/secret", desc.Name)
assert.Equal(t, "roundtrip test", desc.Description)
assert.Equal(t, "alias/roundtrip", desc.KmsKeyID)
diff --git a/services/secretsmanager/deletesecret_test.go b/services/secretsmanager/deletesecret_test.go
index 8e2144fb1b..f3f9436d5f 100644
--- a/services/secretsmanager/deletesecret_test.go
+++ b/services/secretsmanager/deletesecret_test.go
@@ -6,7 +6,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -283,7 +284,7 @@ func TestDeleteSecret_ForceAndRecoveryWindowHTTPErrorType(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp secretsmanager.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp.Type)
assert.Equal(t, "InvalidParameterException", rec.Header().Get("X-Amzn-Errortype"),
"error response must echo the error code in X-Amzn-Errortype")
@@ -501,7 +502,7 @@ func TestDeleteSecret_RecoveryWindowInDaysHTTP(t *testing.T) {
// Create the secret(s) needed for the test.
for _, name := range []string{"recover-test", "recover-test2"} {
- body, _ := json.Marshal(map[string]string{"Name": name, "SecretString": "v"})
+ body, _ := json.Marshal(map[string]string{"Name": name, "SecretString": "v"}, jsonx.V1())
rec := doR1Request(t, h, "secretsmanager.CreateSecret", string(body))
require.Equal(t, http.StatusOK, rec.Code)
}
@@ -511,7 +512,7 @@ func TestDeleteSecret_RecoveryWindowInDaysHTTP(t *testing.T) {
if tt.wantStatus == http.StatusOK && tt.checkForward != nil {
var out secretsmanager.DeleteSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tt.checkForward(t, out.DeletionDate)
}
})
diff --git a/services/secretsmanager/describesecret_test.go b/services/secretsmanager/describesecret_test.go
index c64a0540cf..fc1369a959 100644
--- a/services/secretsmanager/describesecret_test.go
+++ b/services/secretsmanager/describesecret_test.go
@@ -6,7 +6,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -251,7 +252,7 @@ func TestDescribeSecret_HTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out secretsmanager.DescribeSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "alias/http-key", out.KmsKeyID)
}
@@ -282,7 +283,7 @@ func TestDescribeSecret_HasAllFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var desc secretsmanager.DescribeSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc, jsonx.V1()))
assert.Equal(t, "full-meta", desc.Name)
assert.Equal(t, "full metadata", desc.Description)
assert.Equal(t, "alias/mykey", desc.KmsKeyID)
@@ -306,7 +307,7 @@ func TestDescribeSecret_HasCreatedDate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var desc secretsmanager.DescribeSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc, jsonx.V1()))
assert.NotNil(t, desc.CreatedDate)
assert.Greater(t, *desc.CreatedDate, float64(0))
}
diff --git a/services/secretsmanager/explicit_empty.go b/services/secretsmanager/explicit_empty.go
index 706cfb0b1f..40843d9c2e 100644
--- a/services/secretsmanager/explicit_empty.go
+++ b/services/secretsmanager/explicit_empty.go
@@ -1,15 +1,18 @@
package secretsmanager
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// rejectExplicitEmpty fails when any named member is present as "": the SDK
// models them as *string with a min length, so omitted keeps and "" is invalid.
func rejectExplicitEmpty(data []byte, op string, members ...string) error {
- var probe map[string]json.RawMessage
- if err := json.Unmarshal(data, &probe); err != nil {
+ var probe map[string]jsontext.Value
+ if err := json.Unmarshal(data, &probe, jsonx.V1()); err != nil {
return err
}
@@ -32,7 +35,7 @@ func (in *PutSecretValueInput) UnmarshalJSON(data []byte) error {
return err
}
- return json.Unmarshal(data, (*putSecretValueInputAlias)(in))
+ return json.Unmarshal(data, (*putSecretValueInputAlias)(in), jsonx.V1())
}
type updateSecretInputAlias UpdateSecretInput
@@ -43,7 +46,7 @@ func (in *UpdateSecretInput) UnmarshalJSON(data []byte) error {
return err
}
- return json.Unmarshal(data, (*updateSecretInputAlias)(in))
+ return json.Unmarshal(data, (*updateSecretInputAlias)(in), jsonx.V1())
}
type updateSecretVersionStageInputAlias UpdateSecretVersionStageInput
@@ -56,5 +59,5 @@ func (in *UpdateSecretVersionStageInput) UnmarshalJSON(data []byte) error {
return err
}
- return json.Unmarshal(data, (*updateSecretVersionStageInputAlias)(in))
+ return json.Unmarshal(data, (*updateSecretVersionStageInputAlias)(in), jsonx.V1())
}
diff --git a/services/secretsmanager/getrandompassword_test.go b/services/secretsmanager/getrandompassword_test.go
index 079e84e981..cb26545e4c 100644
--- a/services/secretsmanager/getrandompassword_test.go
+++ b/services/secretsmanager/getrandompassword_test.go
@@ -1,7 +1,7 @@
package secretsmanager_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -381,7 +382,7 @@ func TestGetRandomPassword_Handler(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.GetRandomPasswordOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, []rune(out.RandomPassword), 32)
},
},
@@ -392,7 +393,7 @@ func TestGetRandomPassword_Handler(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.GetRandomPasswordOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, []rune(out.RandomPassword), 20)
},
},
@@ -403,7 +404,7 @@ func TestGetRandomPassword_Handler(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var errResp secretsmanager.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp.Type)
},
},
@@ -414,7 +415,7 @@ func TestGetRandomPassword_Handler(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.GetRandomPasswordOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, []rune(out.RandomPassword), 50)
for _, ch := range out.RandomPassword {
assert.NotContains(t, "0123456789", string(ch))
@@ -497,7 +498,7 @@ func TestGetRandomPassword_RandomRunesOptimized(t *testing.T) {
RandomPassword string `json:"RandomPassword"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.GreaterOrEqual(t, len(out.RandomPassword), tt.minLen)
})
}
diff --git a/services/secretsmanager/getsecretvalue_test.go b/services/secretsmanager/getsecretvalue_test.go
index 9437c72ec4..bc191863a1 100644
--- a/services/secretsmanager/getsecretvalue_test.go
+++ b/services/secretsmanager/getsecretvalue_test.go
@@ -2,7 +2,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -38,7 +39,7 @@ func TestGetSecretValue_NotFoundHTTP(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp secretsmanager.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp.Type)
}
@@ -74,7 +75,7 @@ func TestGetSecretValue_DeletedHTTPStatus(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp secretsmanager.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", errResp.Type)
}
@@ -230,12 +231,12 @@ func TestGetSecretValue_VersionMismatchHTTPErrorType(t *testing.T) {
"SecretId": "mismatch-http",
"VersionId": "v1",
"VersionStage": secretsmanager.StagingLabelPrevious,
- })
+ }, jsonx.V1())
rec = doR1Request(t, h, "secretsmanager.GetSecretValue", string(body))
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp secretsmanager.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp.Type,
"HTTP error body must carry ResourceNotFoundException")
}
@@ -371,7 +372,7 @@ func TestGetSecretValue_LastAccessedDateTracking(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var desc1 secretsmanager.DescribeSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc1, jsonx.V1()))
assert.Nil(t, desc1.LastAccessedDate)
// GetSecretValue should update LastAccessedDate.
@@ -383,7 +384,7 @@ func TestGetSecretValue_LastAccessedDateTracking(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var desc2 secretsmanager.DescribeSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc2, jsonx.V1()))
require.NotNil(t, desc2.LastAccessedDate)
// The LastAccessedDate should be today (day-granularity as AWS does).
accessTime := time.Unix(int64(*desc2.LastAccessedDate), 0).UTC()
diff --git a/services/secretsmanager/handler.go b/services/secretsmanager/handler.go
index babd9c8b0e..c14933502b 100644
--- a/services/secretsmanager/handler.go
+++ b/services/secretsmanager/handler.go
@@ -2,7 +2,7 @@ package secretsmanager
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/worker"
@@ -188,7 +189,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var data map[string]any
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
@@ -226,7 +227,7 @@ type smActionFn func(ctx context.Context, region string, body []byte) (any, erro
func decodeAction[I any](fn func(ctx context.Context, input *I) (any, error)) smActionFn {
return func(ctx context.Context, _ string, body []byte) (any, error) {
var input I
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -250,7 +251,7 @@ func (h *Handler) dispatch(ctx context.Context, r *http.Request, action string,
return nil, err
}
- return json.Marshal(response)
+ return json.Marshal(response, jsonx.V1())
}
// handleError writes a structured error response for a Secrets Manager operation failure.
@@ -307,7 +308,7 @@ func (h *Handler) handleError(ctx context.Context, c *echo.Context, action strin
payload, _ := json.Marshal(ErrorResponse{
Type: errorType,
Message: awserr.StripCode(errorType, reqErr.Error()),
- })
+ }, jsonx.V1())
return c.JSONBlob(statusCode, payload)
}
diff --git a/services/secretsmanager/handler_dispatch_test.go b/services/secretsmanager/handler_dispatch_test.go
index 4f4a0ce3e9..bf93794c67 100644
--- a/services/secretsmanager/handler_dispatch_test.go
+++ b/services/secretsmanager/handler_dispatch_test.go
@@ -7,7 +7,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -47,7 +48,7 @@ func TestHandler_Dispatch(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.CreateSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "test-secret", out.Name)
},
},
@@ -66,7 +67,7 @@ func TestHandler_Dispatch(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.GetSecretValueOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "the-value", out.SecretString)
},
},
@@ -78,7 +79,7 @@ func TestHandler_Dispatch(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var errResp secretsmanager.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "UnknownOperationException", errResp.Type)
},
},
@@ -96,7 +97,7 @@ func TestHandler_Dispatch(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var ops []string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops, jsonx.V1()))
assert.Contains(t, ops, "CreateSecret")
},
},
@@ -188,7 +189,7 @@ func TestHandler_FullCycle(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec.Code)
var listOut secretsmanager.ListSecretsOutput
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
assert.Len(t, listOut.SecretList, 1)
// UpdateSecret
@@ -402,7 +403,7 @@ func TestHandler_ErrorCases(t *testing.T) {
assert.Equal(t, tt.expectedStatus, rec.Code)
var errResp secretsmanager.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.expectedErrTyp, errResp.Type)
})
}
@@ -762,7 +763,7 @@ func TestHandler_ErrorResponseAmznErrortypeHeader(t *testing.T) {
require.GreaterOrEqual(t, rec.Code, http.StatusBadRequest)
var errResp secretsmanager.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp.Type, "body __type")
assert.Equal(t, tt.wantType, rec.Header().Get("X-Amzn-Errortype"),
"X-Amzn-Errortype header must equal body __type")
diff --git a/services/secretsmanager/handler_random_password.go b/services/secretsmanager/handler_random_password.go
index 95a71454e0..dd0c4d1559 100644
--- a/services/secretsmanager/handler_random_password.go
+++ b/services/secretsmanager/handler_random_password.go
@@ -2,14 +2,16 @@ package secretsmanager
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) smRandomPasswordActions() map[string]smActionFn {
return map[string]smActionFn{
"GetRandomPassword": func(_ context.Context, _ string, b []byte) (any, error) {
var input GetRandomPasswordInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/secretsmanager/handler_rotation.go b/services/secretsmanager/handler_rotation.go
index f4f2522862..46c6b0fe3a 100644
--- a/services/secretsmanager/handler_rotation.go
+++ b/services/secretsmanager/handler_rotation.go
@@ -2,10 +2,11 @@ package secretsmanager
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -13,7 +14,7 @@ func (h *Handler) smRotationActions() map[string]smActionFn {
return map[string]smActionFn{
"RotateSecret": func(ctx context.Context, region string, b []byte) (any, error) {
var input RotateSecretInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -21,7 +22,7 @@ func (h *Handler) smRotationActions() map[string]smActionFn {
},
"CancelRotateSecret": func(ctx context.Context, _ string, b []byte) (any, error) {
var input CancelRotateSecretInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -57,7 +58,7 @@ func buildRotationStepEvent(secretID, token, step string) ([]byte, error) {
rotationEventSecretIDKey: secretID,
rotationEventTokenKey: token,
rotationEventStepKey: step,
- })
+ }, jsonx.V1())
}
// extractFunctionNameFromARN extracts the bare function name from a Lambda ARN.
diff --git a/services/secretsmanager/handler_secrets.go b/services/secretsmanager/handler_secrets.go
index 668708a041..158521b6c2 100644
--- a/services/secretsmanager/handler_secrets.go
+++ b/services/secretsmanager/handler_secrets.go
@@ -2,8 +2,9 @@ package secretsmanager
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -11,7 +12,7 @@ func (h *Handler) smSecretsActions() map[string]smActionFn {
return map[string]smActionFn{
"CreateSecret": func(ctx context.Context, region string, b []byte) (any, error) {
var input CreateSecretInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
input.Region = region
@@ -23,7 +24,7 @@ func (h *Handler) smSecretsActions() map[string]smActionFn {
},
"GetSecretValue": func(ctx context.Context, _ string, b []byte) (any, error) {
var input GetSecretValueInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -31,7 +32,7 @@ func (h *Handler) smSecretsActions() map[string]smActionFn {
},
"PutSecretValue": func(ctx context.Context, _ string, b []byte) (any, error) {
var input PutSecretValueInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -39,7 +40,7 @@ func (h *Handler) smSecretsActions() map[string]smActionFn {
},
"DeleteSecret": func(ctx context.Context, _ string, b []byte) (any, error) {
var input DeleteSecretInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -47,7 +48,7 @@ func (h *Handler) smSecretsActions() map[string]smActionFn {
},
opListSecrets: func(ctx context.Context, _ string, b []byte) (any, error) {
var input ListSecretsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -55,7 +56,7 @@ func (h *Handler) smSecretsActions() map[string]smActionFn {
},
opDescribeSecret: func(ctx context.Context, _ string, b []byte) (any, error) {
var input DescribeSecretInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -63,7 +64,7 @@ func (h *Handler) smSecretsActions() map[string]smActionFn {
},
"UpdateSecret": func(ctx context.Context, _ string, b []byte) (any, error) {
var input UpdateSecretInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -71,7 +72,7 @@ func (h *Handler) smSecretsActions() map[string]smActionFn {
},
"RestoreSecret": func(ctx context.Context, _ string, b []byte) (any, error) {
var input RestoreSecretInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/secretsmanager/handler_tags.go b/services/secretsmanager/handler_tags.go
index 7de93cc5d5..bb264275ad 100644
--- a/services/secretsmanager/handler_tags.go
+++ b/services/secretsmanager/handler_tags.go
@@ -2,9 +2,10 @@ package secretsmanager
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -12,7 +13,7 @@ func (h *Handler) smTagActions() map[string]smActionFn {
return map[string]smActionFn{
"TagResource": func(ctx context.Context, _ string, b []byte) (any, error) {
var input TagResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -26,7 +27,7 @@ func (h *Handler) smTagActions() map[string]smActionFn {
},
"UntagResource": func(ctx context.Context, _ string, b []byte) (any, error) {
var input UntagResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/secretsmanager/listsecrets_test.go b/services/secretsmanager/listsecrets_test.go
index 46554b236f..638ec99d30 100644
--- a/services/secretsmanager/listsecrets_test.go
+++ b/services/secretsmanager/listsecrets_test.go
@@ -5,7 +5,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -658,7 +659,7 @@ func TestListSecrets_OwningServiceHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out secretsmanager.ListSecretsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.SecretList, "owning-service=rds must match neither om-1 nor om-2")
}
@@ -924,7 +925,7 @@ func TestListSecrets_FilterByNameHTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out secretsmanager.ListSecretsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.SecretList, 2)
}
@@ -969,7 +970,7 @@ func TestListSecrets_SortOrder(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out secretsmanager.ListSecretsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.SecretList, 3)
assert.Equal(t, "gamma", out.SecretList[0].Name)
assert.Equal(t, "alpha", out.SecretList[2].Name)
@@ -987,7 +988,7 @@ func TestListSecrets_FilterAll(t *testing.T) {
"Name": "findme/secret",
"Description": "unique-token",
"SecretString": "v",
- })
+ }, jsonx.V1())
rec := doR1Request(t, h, "secretsmanager.CreateSecret", string(body))
require.Equal(t, http.StatusOK, rec.Code)
@@ -1001,7 +1002,7 @@ func TestListSecrets_FilterAll(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out secretsmanager.ListSecretsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.SecretList, 1)
assert.Equal(t, "findme/secret", out.SecretList[0].Name)
}
@@ -1025,7 +1026,7 @@ func TestListSecrets_EntryHasRotationFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out secretsmanager.ListSecretsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.SecretList, 1)
entry := out.SecretList[0]
@@ -1056,7 +1057,7 @@ func TestListSecrets_EntryHasCreatedAndRotatedDate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out secretsmanager.ListSecretsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.SecretList, 1)
assert.NotNil(t, out.SecretList[0].CreatedDate)
assert.NotNil(t, out.SecretList[0].LastRotatedDate)
diff --git a/services/secretsmanager/listsecretversionids_test.go b/services/secretsmanager/listsecretversionids_test.go
index c959855911..b6c3357415 100644
--- a/services/secretsmanager/listsecretversionids_test.go
+++ b/services/secretsmanager/listsecretversionids_test.go
@@ -2,7 +2,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -280,7 +281,7 @@ func TestListSecretVersionIds_HTTPDispatchTable(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.ListSecretVersionIDsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "lsvi-secret", out.Name)
assert.NotEmpty(t, out.Versions)
},
@@ -481,7 +482,7 @@ func TestListSecretVersionIds_ViaHandler(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out secretsmanager.ListSecretVersionIDsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "handler-lsv", out.Name)
assert.NotEmpty(t, out.Versions)
}
diff --git a/services/secretsmanager/persistence.go b/services/secretsmanager/persistence.go
index a4b8acbd20..dfcaf33998 100644
--- a/services/secretsmanager/persistence.go
+++ b/services/secretsmanager/persistence.go
@@ -2,7 +2,7 @@ package secretsmanager
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -67,7 +67,7 @@ func secretSnapshotKeyFn(ss *secretSnapshot) string {
// from an incompatible (older or newer) build of this backend as though it
// were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ResourcePolicies map[string]map[string]string `json:"resourcePolicies,omitempty"`
ReplicationConfigs map[string]map[string][]ReplicationStatusType `json:"replicationConfigs,omitempty"`
AccountID string `json:"accountID"`
diff --git a/services/secretsmanager/putsecretvalue_test.go b/services/secretsmanager/putsecretvalue_test.go
index 7df8364aab..6718342bb5 100644
--- a/services/secretsmanager/putsecretvalue_test.go
+++ b/services/secretsmanager/putsecretvalue_test.go
@@ -2,7 +2,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -486,7 +487,7 @@ func TestPutSecretValue_Idempotency(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out1 secretsmanager.PutSecretValueOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out1, jsonx.V1()))
assert.Equal(t, token, out1.VersionID)
// Second put with same token + content: should return the existing version.
@@ -494,7 +495,7 @@ func TestPutSecretValue_Idempotency(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out2 secretsmanager.PutSecretValueOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out2, jsonx.V1()))
assert.Equal(t, token, out2.VersionID)
assert.Equal(t, out1.VersionID, out2.VersionID)
}
diff --git a/services/secretsmanager/replica_write_guard_test.go b/services/secretsmanager/replica_write_guard_test.go
index 71b97b2dbf..9690c4026a 100644
--- a/services/secretsmanager/replica_write_guard_test.go
+++ b/services/secretsmanager/replica_write_guard_test.go
@@ -13,13 +13,14 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -97,7 +98,7 @@ func TestReplicaWriteGuard_BlockedOnReplica(t *testing.T) {
var out struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", out.Type)
})
}
diff --git a/services/secretsmanager/replication_test.go b/services/secretsmanager/replication_test.go
index 1f9fc790f4..657b649076 100644
--- a/services/secretsmanager/replication_test.go
+++ b/services/secretsmanager/replication_test.go
@@ -6,7 +6,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -180,7 +181,7 @@ func TestReplication_ReplicaSecretReadableInReplicaRegion(t *testing.T) {
"replica region must serve the replicated secret, got: %s", getRec.Body.String())
var getOut secretsmanager.GetSecretValueOutput
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
assert.Equal(t, "replicated-value", getOut.SecretString)
descRec := doSMRequestInRegion(t, h, "us-west-2", "secretsmanager.DescribeSecret",
@@ -188,7 +189,7 @@ func TestReplication_ReplicaSecretReadableInReplicaRegion(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut secretsmanager.DescribeSecretOutput
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, secretsmanager.MockRegion, descOut.PrimaryRegion)
assert.Contains(t, descOut.ARN, "us-west-2")
}
@@ -251,7 +252,7 @@ func TestReplication_Operations(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.ReplicateSecretToRegionsOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.ARN)
require.Len(t, out.ReplicationStatus, 1)
assert.Equal(t, "us-west-2", out.ReplicationStatus[0].Region)
@@ -279,7 +280,7 @@ func TestReplication_Operations(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.RemoveRegionsFromReplicationOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.ReplicationStatus, 1)
assert.Equal(t, "ap-east-1", out.ReplicationStatus[0].Region)
},
@@ -305,7 +306,7 @@ func TestReplication_Operations(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.StopReplicationToReplicaOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.ARN)
},
},
@@ -474,7 +475,7 @@ func TestReplicateSecretToRegions_ExistingRegionErrorType_WireCode(t *testing.T)
var body struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(second.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(second.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", body.Type)
assert.NotEqual(t, "ResourceExistsException", body.Type)
}
diff --git a/services/secretsmanager/resource_policy.go b/services/secretsmanager/resource_policy.go
index 728d6a941a..3d3d75e57e 100644
--- a/services/secretsmanager/resource_policy.go
+++ b/services/secretsmanager/resource_policy.go
@@ -2,9 +2,12 @@ package secretsmanager
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"slices"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// GetResourcePolicy retrieves the resource-based policy for a secret.
@@ -142,8 +145,8 @@ func (b *InMemoryBackend) ValidateResourcePolicy(
}
func validateResourcePolicyDocument(policyStr string) []PolicyValidationException {
- var policy map[string]json.RawMessage
- if err := json.Unmarshal([]byte(policyStr), &policy); err != nil {
+ var policy map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(policyStr), &policy, jsonx.V1()); err != nil {
return []PolicyValidationException{
{
CheckName: "SyntaxCheck",
@@ -172,25 +175,25 @@ func validateResourcePolicyDocument(policyStr string) []PolicyValidationExceptio
}
type policyStatement struct {
- Effect string `json:"Effect"`
- Principal json.RawMessage `json:"Principal"`
- Condition json.RawMessage `json:"Condition"`
+ Effect string `json:"Effect"`
+ Principal jsontext.Value `json:"Principal"`
+ Condition jsontext.Value `json:"Condition"`
}
// policyAllowsPublicAccess reports whether an unconditioned Allow statement
// grants a wildcard principal.
func policyAllowsPublicAccess(policyStr string) bool {
var doc struct {
- Statement json.RawMessage `json:"Statement"`
+ Statement jsontext.Value `json:"Statement"`
}
- if json.Unmarshal([]byte(policyStr), &doc) != nil {
+ if json.Unmarshal([]byte(policyStr), &doc, jsonx.V1()) != nil {
return false
}
var stmts []policyStatement
- if json.Unmarshal(doc.Statement, &stmts) != nil {
+ if json.Unmarshal(doc.Statement, &stmts, jsonx.V1()) != nil {
var one policyStatement
- if json.Unmarshal(doc.Statement, &one) != nil {
+ if json.Unmarshal(doc.Statement, &one, jsonx.V1()) != nil {
return false
}
@@ -206,14 +209,14 @@ func policyAllowsPublicAccess(policyStr string) bool {
return false
}
-func principalIsWildcard(raw json.RawMessage) bool {
+func principalIsWildcard(raw jsontext.Value) bool {
var s string
- if json.Unmarshal(raw, &s) == nil {
+ if json.Unmarshal(raw, &s, jsonx.V1()) == nil {
return s == "*"
}
- var m map[string]json.RawMessage
- if json.Unmarshal(raw, &m) != nil {
+ var m map[string]jsontext.Value
+ if json.Unmarshal(raw, &m, jsonx.V1()) != nil {
return false
}
@@ -222,11 +225,11 @@ func principalIsWildcard(raw json.RawMessage) bool {
return false
}
- if json.Unmarshal(aws, &s) == nil {
+ if json.Unmarshal(aws, &s, jsonx.V1()) == nil {
return s == "*"
}
var list []string
- return json.Unmarshal(aws, &list) == nil && slices.Contains(list, "*")
+ return json.Unmarshal(aws, &list, jsonx.V1()) == nil && slices.Contains(list, "*")
}
diff --git a/services/secretsmanager/resource_policy_test.go b/services/secretsmanager/resource_policy_test.go
index 0c47c2156b..1e64e3a7ca 100644
--- a/services/secretsmanager/resource_policy_test.go
+++ b/services/secretsmanager/resource_policy_test.go
@@ -6,7 +6,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -360,7 +361,7 @@ func TestGetResourcePolicy_DeletedSecret_HTTP(t *testing.T) {
assert.Equal(t, tt.expectedStatus, rec.Code)
var errResp secretsmanager.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.expectedType, errResp.Type)
})
}
@@ -398,7 +399,7 @@ func TestResourcePolicy_Cycle(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.PutResourcePolicyOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "policy-secret", out.Name)
assert.NotEmpty(t, out.ARN)
},
@@ -424,7 +425,7 @@ func TestResourcePolicy_Cycle(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.GetResourcePolicyOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "get-policy", out.Name)
assert.Contains(t, out.ResourcePolicy, "2012-10-17")
},
@@ -450,7 +451,7 @@ func TestResourcePolicy_Cycle(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.DeleteResourcePolicyOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "del-policy", out.Name)
},
},
@@ -690,7 +691,7 @@ func TestValidateResourcePolicy_HTTP(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out secretsmanager.ValidateResourcePolicyOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantPassed, out.PolicyValidationPassed)
if tt.wantErrCount > 0 {
@@ -720,12 +721,12 @@ func TestValidateResourcePolicy_HTTP_SecretExists(t *testing.T) {
body, _ := json.Marshal(map[string]string{
"SecretId": "validate-test",
"ResourcePolicy": validPolicy,
- })
+ }, jsonx.V1())
rec = doR1Request(t, h, "secretsmanager.ValidateResourcePolicy", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var out secretsmanager.ValidateResourcePolicyOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.True(t, out.PolicyValidationPassed)
}
diff --git a/services/secretsmanager/rotatesecret_test.go b/services/secretsmanager/rotatesecret_test.go
index c12929cdf6..83d59c2a9c 100644
--- a/services/secretsmanager/rotatesecret_test.go
+++ b/services/secretsmanager/rotatesecret_test.go
@@ -2,7 +2,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -280,7 +281,7 @@ func TestRotateSecret_Backend(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out secretsmanager.RotateSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "rotate-secret", out.Name)
assert.NotEmpty(t, out.VersionID)
@@ -335,7 +336,7 @@ func TestRotateSecret_WithLambda(t *testing.T) {
assert.Equal(t, "my-rotator", call.name)
assert.Equal(t, "RequestResponse", call.invocationType)
var event map[string]string
- require.NoError(t, json.Unmarshal(call.payload, &event))
+ require.NoError(t, json.Unmarshal(call.payload, &event, jsonx.V1()))
assert.Equal(t, "lambda-rotate-secret", event["SecretId"])
assert.Equal(t, steps[i], event["Step"])
assert.NotEmpty(t, event["ClientRequestToken"])
@@ -394,7 +395,7 @@ func TestRotateSecret_OmittedARNUsesStoredLambda(t *testing.T) {
assert.Equal(t, "my-rotator", call.name)
assert.Equal(t, steps[i], func() string {
var event map[string]string
- require.NoError(t, json.Unmarshal(call.payload, &event))
+ require.NoError(t, json.Unmarshal(call.payload, &event, jsonx.V1()))
return event["Step"]
}())
@@ -448,7 +449,7 @@ func TestRotateSecret_RotateImmediatelyFalseWithLambdaRunsTestSecretProbe(t *tes
assert.Equal(t, http.StatusOK, rec.Code)
var out secretsmanager.RotateSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out.VersionID, "no permanent version is created by a test probe")
// Exactly one Lambda invocation: the testSecret step.
@@ -456,7 +457,7 @@ func TestRotateSecret_RotateImmediatelyFalseWithLambdaRunsTestSecretProbe(t *tes
assert.Equal(t, "probe-rotator", mock.calls[0].name)
var event map[string]string
- require.NoError(t, json.Unmarshal(mock.calls[0].payload, &event))
+ require.NoError(t, json.Unmarshal(mock.calls[0].payload, &event, jsonx.V1()))
assert.Equal(t, "probe-secret", event["SecretId"])
assert.Equal(t, "testSecret", event["Step"])
@@ -546,7 +547,7 @@ func TestRotateSecret_NoLambdaInvoker(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out secretsmanager.RotateSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.VersionID)
}
@@ -668,7 +669,7 @@ func TestRotateSecret_InvalidDays(t *testing.T) {
rotBody, _ := json.Marshal(map[string]any{
"SecretId": "days-validation",
"RotationRules": map[string]any{"AutomaticallyAfterDays": tt.days},
- })
+ }, jsonx.V1())
rotRec := doR1Request(t, h, "secretsmanager.RotateSecret", string(rotBody))
assert.Equal(t, http.StatusBadRequest, rotRec.Code)
})
diff --git a/services/secretsmanager/secret_versions_test.go b/services/secretsmanager/secret_versions_test.go
index b6172cadee..0dd30d7d57 100644
--- a/services/secretsmanager/secret_versions_test.go
+++ b/services/secretsmanager/secret_versions_test.go
@@ -2,7 +2,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -302,7 +303,7 @@ func TestSecretSizeValidation_Handler(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp secretsmanager.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp.Type)
}
@@ -388,7 +389,7 @@ func TestPutSecretValueLabelRotation(t *testing.T) {
putBody, _ := json.Marshal(map[string]string{
"SecretId": "rotate-test",
"SecretString": "v2",
- })
+ }, jsonx.V1())
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(putBody)))
req.Header.Set("X-Amz-Target", "secretsmanager.PutSecretValue")
rec := httptest.NewRecorder()
@@ -396,7 +397,7 @@ func TestPutSecretValueLabelRotation(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var putOut secretsmanager.PutSecretValueOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putOut, jsonx.V1()))
assert.Contains(t, putOut.VersionStages, secretsmanager.StagingLabelCurrent)
// Current should be v2
diff --git a/services/secretsmanager/updatesecret_test.go b/services/secretsmanager/updatesecret_test.go
index 37653fd01e..b9d3a08ee5 100644
--- a/services/secretsmanager/updatesecret_test.go
+++ b/services/secretsmanager/updatesecret_test.go
@@ -6,7 +6,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -235,7 +236,7 @@ func TestUpdateSecret_KmsKeyID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var desc secretsmanager.DescribeSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &desc, jsonx.V1()))
assert.Equal(t, "arn:aws:kms:us-east-1:123456789012:key/test-key-id", desc.KmsKeyID)
}
@@ -257,17 +258,17 @@ func TestUpdateSecret_ClientRequestTokenIdempotency(t *testing.T) {
"SecretId": "update-idempotent",
"SecretString": "updated-value",
"ClientRequestToken": token,
- })
+ }, jsonx.V1())
rec = doR1Request(t, h, "secretsmanager.UpdateSecret", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var out1 secretsmanager.UpdateSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out1, jsonx.V1()))
rec = doR1Request(t, h, "secretsmanager.UpdateSecret", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var out2 secretsmanager.UpdateSecretOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out2, jsonx.V1()))
assert.Equal(t, out1.VersionID, out2.VersionID)
}
diff --git a/services/secretsmanager/updatesecretversionstage_test.go b/services/secretsmanager/updatesecretversionstage_test.go
index 5614330ad7..0f9422bd9f 100644
--- a/services/secretsmanager/updatesecretversionstage_test.go
+++ b/services/secretsmanager/updatesecretversionstage_test.go
@@ -2,7 +2,7 @@ package secretsmanager_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/secretsmanager"
)
@@ -515,7 +516,7 @@ func TestUpdateSecretVersionStage_CannotRemoveAWSCURRENT_HTTP(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var errResp secretsmanager.ErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, errResp.Type)
})
}
@@ -560,7 +561,7 @@ func TestUpdateSecretVersionStage_HTTP(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.UpdateSecretVersionStageOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "stage-secret", out.Name)
assert.NotEmpty(t, out.ARN)
},
@@ -592,7 +593,7 @@ func TestUpdateSecretVersionStage_HTTP(t *testing.T) {
checkFn: func(t *testing.T, rec *httptest.ResponseRecorder) {
t.Helper()
var out secretsmanager.UpdateSecretVersionStageOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "stage-remove", out.Name)
},
},
diff --git a/services/securityhub/action_targets_test.go b/services/securityhub/action_targets_test.go
index 007002f241..8df6e34370 100644
--- a/services/securityhub/action_targets_test.go
+++ b/services/securityhub/action_targets_test.go
@@ -1,13 +1,14 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -27,7 +28,7 @@ func TestCreateActionTargetPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ := resp["ActionTargetArn"].(string)
assert.NotEmpty(t, arn)
@@ -51,7 +52,7 @@ func TestDescribeActionTargetsIsPOSTActionTargetsGet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
targets, _ := resp["ActionTargets"].([]any)
assert.Len(t, targets, 1)
@@ -74,7 +75,7 @@ func TestDeleteActionTargetIsDELETEActionTargetsArn(t *testing.T) {
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
arn, _ := createResp["ActionTargetArn"].(string)
@@ -82,7 +83,7 @@ func TestDeleteActionTargetIsDELETEActionTargetsArn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
deletedArn, _ := resp["ActionTargetArn"].(string)
assert.Equal(t, arn, deletedArn, "DeleteActionTarget must return the deleted ActionTargetArn")
diff --git a/services/securityhub/aggregators_v2_test.go b/services/securityhub/aggregators_v2_test.go
index 0a150de588..3439bc1b9a 100644
--- a/services/securityhub/aggregators_v2_test.go
+++ b/services/securityhub/aggregators_v2_test.go
@@ -1,12 +1,14 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestAggregatorV2(t *testing.T) {
@@ -90,7 +92,7 @@ func TestAggregatorV2(t *testing.T) {
rec := doRequest(t, h, s.method, s.path, s.body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
s.check(t, rec.Code, resp)
}
})
@@ -111,7 +113,7 @@ func TestHandler_UpdateAggregatorV2(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp["AggregatorV2Arn"].(string)
rec2 := doRequest(t, h, http.MethodPatch, "/aggregatorv2/update/"+arn, map[string]any{
@@ -120,7 +122,7 @@ func TestHandler_UpdateAggregatorV2(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
require.Equal(t, "SPECIFIED_REGIONS", resp2["RegionLinkingMode"])
})
@@ -135,7 +137,7 @@ func TestHandler_UpdateAggregatorV2(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp["AggregatorV2Arn"].(string)
rec2 := doRequest(t, h, http.MethodDelete, "/aggregatorv2/delete/"+arn, nil)
@@ -164,7 +166,7 @@ func TestCreateAggregatorV2_LinkedRegionsAndTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ := resp["AggregatorV2Arn"].(string)
require.NotEmpty(t, arn)
@@ -175,7 +177,7 @@ func TestCreateAggregatorV2_LinkedRegionsAndTags(t *testing.T) {
require.Equal(t, http.StatusOK, tagRec.Code)
var tagResp map[string]any
- require.NoError(t, json.Unmarshal(tagRec.Body.Bytes(), &tagResp))
+ require.NoError(t, json.Unmarshal(tagRec.Body.Bytes(), &tagResp, jsonx.V1()))
tags, _ := tagResp["Tags"].(map[string]any)
assert.Equal(t, "prod", tags["env"])
}
diff --git a/services/securityhub/analytics_filters_test.go b/services/securityhub/analytics_filters_test.go
index 27d323170b..b7a9319912 100644
--- a/services/securityhub/analytics_filters_test.go
+++ b/services/securityhub/analytics_filters_test.go
@@ -1,13 +1,14 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -48,7 +49,7 @@ func decodeMap(t *testing.T, body []byte) map[string]any {
t.Helper()
var out map[string]any
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
return out
}
diff --git a/services/securityhub/automation_rules_test.go b/services/securityhub/automation_rules_test.go
index 94afdf48cf..22ed7faee3 100644
--- a/services/securityhub/automation_rules_test.go
+++ b/services/securityhub/automation_rules_test.go
@@ -1,13 +1,14 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -29,7 +30,7 @@ func TestCreateAutomationRulePath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ruleArn, _ := resp["RuleArn"].(string)
assert.NotEmpty(t, ruleArn)
@@ -55,7 +56,7 @@ func TestListAutomationRulesIsGETAutomationrulesList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rules, _ := resp["AutomationRulesMetadata"].([]any)
assert.Len(t, rules, 1)
@@ -81,7 +82,7 @@ func TestBatchGetAutomationRulesPath(t *testing.T) {
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
ruleArn, _ := createResp["RuleArn"].(string)
rec := doRequest(t, h, http.MethodPost, "/automationrules/get", map[string]any{
@@ -91,7 +92,7 @@ func TestBatchGetAutomationRulesPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rules, _ := resp["Rules"].([]any)
assert.Len(t, rules, 1)
@@ -117,7 +118,7 @@ func TestBatchDeleteAutomationRulesPath(t *testing.T) {
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
ruleArn, _ := createResp["RuleArn"].(string)
rec := doRequest(t, h, http.MethodPost, "/automationrules/delete", map[string]any{
@@ -127,7 +128,7 @@ func TestBatchDeleteAutomationRulesPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "ProcessedAutomationRules")
assert.Contains(t, resp, "UnprocessedAutomationRules")
@@ -156,7 +157,7 @@ func TestCreateAutomationRule_ResponseFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["RuleArn"], "RuleArn must be present")
assert.NotEmpty(t, resp["CreatedAt"], "CreatedAt must be present")
assert.Equal(t, "my-rule", resp["RuleName"])
@@ -184,7 +185,7 @@ func TestCreateAutomationRule_TagsReachTagStore(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ruleArn, _ := resp["RuleArn"].(string)
require.NotEmpty(t, ruleArn)
@@ -192,7 +193,7 @@ func TestCreateAutomationRule_TagsReachTagStore(t *testing.T) {
require.Equal(t, http.StatusOK, tagRec.Code)
var tagResp map[string]any
- require.NoError(t, json.Unmarshal(tagRec.Body.Bytes(), &tagResp))
+ require.NoError(t, json.Unmarshal(tagRec.Body.Bytes(), &tagResp, jsonx.V1()))
tags, _ := tagResp["Tags"].(map[string]any)
assert.Equal(t, "sec", tags["team"])
}
@@ -215,7 +216,7 @@ func TestBatchUpdateAutomationRules_CriteriaAndActions(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
ruleArn := createResp["RuleArn"].(string)
// Update its Criteria and Actions
@@ -240,7 +241,7 @@ func TestBatchUpdateAutomationRules_CriteriaAndActions(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
rules, _ := getResp["Rules"].([]any)
require.Len(t, rules, 1)
@@ -272,7 +273,7 @@ func TestUpdateAutomationRuleV2_ActionsApplied(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
identifier, _ := createResp["RuleId"].(string)
require.NotEmpty(t, identifier)
@@ -289,7 +290,7 @@ func TestUpdateAutomationRuleV2_ActionsApplied(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
actions, _ := updateResp["Actions"].([]any)
require.Len(t, actions, 1, "Actions update must be applied, not silently dropped")
@@ -378,7 +379,7 @@ func TestHandler_BatchUpdateAutomationRules(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
ruleArn, _ := createResp["RuleArn"].(string)
require.NotEmpty(t, ruleArn)
@@ -581,7 +582,7 @@ func TestAutomationRulesV2(t *testing.T) {
rec := doRequest(t, h, s.method, s.path, s.body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
s.check(t, rec.Code, resp)
}
})
@@ -654,7 +655,7 @@ func TestBatchImportFindings_AutomationRuleFires(t *testing.T) {
var resp struct {
Findings []map[string]any `json:"Findings"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Findings, 1)
severity, _ := resp.Findings[0]["Severity"].(map[string]any)
diff --git a/services/securityhub/configuration_policies_test.go b/services/securityhub/configuration_policies_test.go
index ea58727798..a7b36ca85c 100644
--- a/services/securityhub/configuration_policies_test.go
+++ b/services/securityhub/configuration_policies_test.go
@@ -1,13 +1,14 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -422,7 +423,7 @@ func TestConfigurationPolicy(t *testing.T) {
rec := doRequest(t, h, s.method, s.path, substitutePolicyID(s.body, policyID))
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if id := s.check(t, rec.Code, resp); id != "" {
policyID = id
}
@@ -478,7 +479,7 @@ func TestConfigurationPolicyAssociation_TargetTypeDerived(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
policyID, _ := createResp["Id"].(string)
require.NotEmpty(t, policyID)
@@ -495,7 +496,7 @@ func TestConfigurationPolicyAssociation_TargetTypeDerived(t *testing.T) {
require.Equal(t, http.StatusOK, assocRec.Code)
var assocResp map[string]any
- require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp))
+ require.NoError(t, json.Unmarshal(assocRec.Body.Bytes(), &assocResp, jsonx.V1()))
assert.Equal(
t,
tc.wantTargetType,
@@ -517,7 +518,7 @@ func TestConfigurationPolicyAssociation_TargetTypeDerived(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, tc.wantTargetType, getResp["TargetType"])
})
}
@@ -541,7 +542,7 @@ func TestCreateConfigurationPolicy_TagsReachTagStore(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policyArn, _ := resp["Arn"].(string)
require.NotEmpty(t, policyArn)
@@ -549,7 +550,7 @@ func TestCreateConfigurationPolicy_TagsReachTagStore(t *testing.T) {
require.Equal(t, http.StatusOK, tagRec.Code)
var tagResp map[string]any
- require.NoError(t, json.Unmarshal(tagRec.Body.Bytes(), &tagResp))
+ require.NoError(t, json.Unmarshal(tagRec.Body.Bytes(), &tagResp, jsonx.V1()))
tags, _ := tagResp["Tags"].(map[string]any)
assert.Equal(t, "sec", tags["team"])
}
diff --git a/services/securityhub/connectors_v2_test.go b/services/securityhub/connectors_v2_test.go
index 109b44ebed..d591b2b36a 100644
--- a/services/securityhub/connectors_v2_test.go
+++ b/services/securityhub/connectors_v2_test.go
@@ -1,7 +1,7 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -148,7 +149,7 @@ func TestConnectorsV2(t *testing.T) {
rec := doRequest(t, h, s.method, s.path, s.body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
s.check(t, rec.Code, resp)
}
})
@@ -217,7 +218,7 @@ func TestTicketV2(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
connectorID, _ := created["ConnectorId"].(string)
require.NotEmpty(t, connectorID)
@@ -584,7 +585,7 @@ func TestConnectors(t *testing.T) {
rec := doRequest(t, h, s.method, s.path, s.body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
s.check(t, rec.Code, resp)
}
})
diff --git a/services/securityhub/controls_test.go b/services/securityhub/controls_test.go
index 0233babd92..c6cc0ba356 100644
--- a/services/securityhub/controls_test.go
+++ b/services/securityhub/controls_test.go
@@ -1,7 +1,7 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -23,7 +24,7 @@ func TestListSecurityControlDefinitionsPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
defs, _ := resp["SecurityControlDefinitions"].([]any)
assert.NotEmpty(t, defs)
@@ -51,7 +52,7 @@ func TestGetSecurityControlDefinitionPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
def, _ := resp["SecurityControlDefinition"].(map[string]any)
assert.Equal(t, "CloudTrail.1", def["SecurityControlId"])
@@ -70,7 +71,7 @@ func TestBatchGetSecurityControlsPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
controls, _ := resp["SecurityControls"].([]any)
assert.Len(t, controls, 2)
diff --git a/services/securityhub/exportjobs_v2.go b/services/securityhub/exportjobs_v2.go
index 13549c7258..184b5383c7 100644
--- a/services/securityhub/exportjobs_v2.go
+++ b/services/securityhub/exportjobs_v2.go
@@ -4,7 +4,8 @@ import (
"bytes"
"context"
"encoding/csv"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"path"
@@ -20,6 +21,7 @@ import (
shtypes "github.com/aws/aws-sdk-go-v2/service/securityhub/types"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -195,9 +197,9 @@ func (b *InMemoryBackend) renderExportLocked(job *ExportJobV2) ([]byte, string,
if job.Format == exportFormatOCSFJSON {
var buf bytes.Buffer
- enc := json.NewEncoder(&buf)
+ enc := jsontext.NewEncoder(&buf, jsonx.V1())
for _, f := range findings {
- if err := enc.Encode(f); err != nil {
+ if err := json.MarshalEncode(enc, f); err != nil {
return nil, "", fmt.Errorf("encode finding: %w", err)
}
}
diff --git a/services/securityhub/finding_aggregators_test.go b/services/securityhub/finding_aggregators_test.go
index a2c3c6bd54..657e5f0753 100644
--- a/services/securityhub/finding_aggregators_test.go
+++ b/services/securityhub/finding_aggregators_test.go
@@ -1,13 +1,14 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -157,7 +158,7 @@ func TestFindingAggregator(t *testing.T) {
rec := doRequest(t, h, s.method, s.path, s.body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
s.check(t, rec.Code, resp)
}
})
diff --git a/services/securityhub/findings.go b/services/securityhub/findings.go
index beee5a999b..46307e7f9d 100644
--- a/services/securityhub/findings.go
+++ b/services/securityhub/findings.go
@@ -1,7 +1,7 @@
package securityhub
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -9,6 +9,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// findingCustomerManagedFields lists ASFF fields that AWS documents as
@@ -304,7 +305,7 @@ func findingSortValue(f map[string]any, field string) string {
return s
}
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return fmt.Sprintf("%v", v)
}
@@ -649,7 +650,7 @@ func historyValueString(v any) *string {
return &s
}
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
s := fmt.Sprintf("%v", v)
diff --git a/services/securityhub/findings_test.go b/services/securityhub/findings_test.go
index 37438817d5..4710a20ea7 100644
--- a/services/securityhub/findings_test.go
+++ b/services/securityhub/findings_test.go
@@ -1,7 +1,7 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -25,7 +26,7 @@ func TestGetFindingsIsPOSTFindings(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasFindings := resp["Findings"]
assert.True(t, hasFindings, "GetFindings must return 'Findings' key")
@@ -59,7 +60,7 @@ func TestBatchImportFindingsPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "SuccessCount")
assert.Contains(t, resp, "FailedCount")
@@ -108,7 +109,7 @@ func TestBatchUpdateFindingsPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "ProcessedFindings")
assert.Contains(t, resp, "UnprocessedFindings")
@@ -145,7 +146,7 @@ func TestGetFindingHistoryPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "Records")
}
@@ -274,7 +275,7 @@ func TestBatchImportFindings_RequiredFields(t *testing.T) {
SuccessCount int `json:"SuccessCount"`
FailedCount int `json:"FailedCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantSuccess, resp.SuccessCount, "SuccessCount")
assert.Equal(t, tc.wantFailed, resp.FailedCount, "FailedCount")
@@ -308,7 +309,7 @@ func TestBatchImportFindings_MixedValidity(t *testing.T) {
SuccessCount int `json:"SuccessCount"`
FailedCount int `json:"FailedCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, 2, resp.SuccessCount)
assert.Equal(t, 1, resp.FailedCount)
assert.Len(t, resp.FailedFindings, 1)
@@ -329,7 +330,7 @@ func TestBatchImportFindings_EmptyFindingsList(t *testing.T) {
SuccessCount int `json:"SuccessCount"`
FailedCount int `json:"FailedCount"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, 0, resp.SuccessCount)
assert.Equal(t, 0, resp.FailedCount)
}
@@ -452,7 +453,7 @@ func TestGetFindings_FiltersApplied(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
findings, _ := resp["Findings"].([]any)
assert.Len(t, findings, tt.wantCount)
})
@@ -497,7 +498,7 @@ func TestGetFindings_MultipleFilterCombinations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
findings, _ := resp["Findings"].([]any)
assert.Len(t, findings, 1)
@@ -546,7 +547,7 @@ func TestGetFindings_MultiValueSameFieldCombination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
findings, _ := resp["Findings"].([]any)
gotIDs := make([]string, 0, len(findings))
@@ -589,7 +590,7 @@ func TestGetFindings_MultiValueSameFieldCombination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
findings, _ := resp["Findings"].([]any)
gotIDs := make([]string, 0, len(findings))
@@ -875,7 +876,7 @@ func TestGetFindingHistory_RecordsChanges(t *testing.T) {
var resp struct {
Records []map[string]any `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Records, 3, "creation + 2 field-change events")
@@ -914,6 +915,6 @@ func TestGetFindingHistory_UnknownFinding(t *testing.T) {
var resp struct {
Records []map[string]any `json:"Records"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp.Records)
}
diff --git a/services/securityhub/findings_v2_test.go b/services/securityhub/findings_v2_test.go
index 3db52a1e3d..d714f3a33b 100644
--- a/services/securityhub/findings_v2_test.go
+++ b/services/securityhub/findings_v2_test.go
@@ -1,7 +1,7 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -90,7 +91,7 @@ func TestGetFindingsV2_ReturnsSameStoreAsV1(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
findings, _ := resp["Findings"].([]any)
assert.NotEmpty(t, findings)
}
@@ -213,7 +214,7 @@ func TestGetFindingsV2_CompositeFilters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
findings, _ := resp["Findings"].([]any)
assert.Len(t, findings, tc.wantCount)
})
@@ -573,7 +574,7 @@ func TestGetFindingsV2_CompositeFilters_DateMapIPBooleanNested(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
findings, _ := resp["Findings"].([]any)
assert.Len(t, findings, tc.wantCount)
@@ -652,7 +653,7 @@ func TestBatchUpdateFindingsV2_WireShape(t *testing.T) {
ProcessedFindings []map[string]any `json:"ProcessedFindings"`
UnprocessedFindings []map[string]any `json:"UnprocessedFindings"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.ProcessedFindings, 1, "matching identifier must process")
assert.Empty(t, resp.UnprocessedFindings)
@@ -674,7 +675,7 @@ func TestBatchUpdateFindingsV2_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
findings, _ := getResp["Findings"].([]any)
require.Len(t, findings, 1)
@@ -718,7 +719,7 @@ func TestBatchUpdateFindingsV2_UnmatchedIdentifiers(t *testing.T) {
ProcessedFindings []map[string]any `json:"ProcessedFindings"`
UnprocessedFindings []map[string]any `json:"UnprocessedFindings"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp.ProcessedFindings)
require.Len(t, resp.UnprocessedFindings, 2)
@@ -749,7 +750,7 @@ func TestBatchUpdateFindingsV2_MetadataUids(t *testing.T) {
} `json:"metadata"`
} `json:"Findings"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &got, jsonx.V1()))
require.Len(t, got.Findings, 1)
uid := got.Findings[0].Metadata.UID
require.NotEmpty(t, uid)
@@ -764,7 +765,7 @@ func TestBatchUpdateFindingsV2_MetadataUids(t *testing.T) {
ProcessedFindings []map[string]any `json:"ProcessedFindings"`
UnprocessedFindings []map[string]any `json:"UnprocessedFindings"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.ProcessedFindings, 1)
assert.Equal(t, uid, resp.ProcessedFindings[0]["MetadataUid"])
require.Len(t, resp.UnprocessedFindings, 1)
diff --git a/services/securityhub/handler.go b/services/securityhub/handler.go
index 6af5cce26e..cb17f85531 100644
--- a/services/securityhub/handler.go
+++ b/services/securityhub/handler.go
@@ -1,7 +1,8 @@
package securityhub
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"strconv"
@@ -12,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -569,7 +571,12 @@ func decodeJSONBody(c *echo.Context) (map[string]any, error) {
var body map[string]any
if c.Request().ContentLength != 0 {
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil && err.Error() != "EOF" {
+ if err := json.UnmarshalDecode(
+ jsontext.NewDecoder(c.Request().Body),
+ &body,
+ jsonx.V1(),
+ ); err != nil &&
+ err.Error() != "EOF" {
return nil, errInvalidJSONBody
}
}
diff --git a/services/securityhub/handler_malformed_body_test.go b/services/securityhub/handler_malformed_body_test.go
index 0c9726e12b..6bfe66ac54 100644
--- a/services/securityhub/handler_malformed_body_test.go
+++ b/services/securityhub/handler_malformed_body_test.go
@@ -2,7 +2,7 @@ package securityhub_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -77,6 +78,6 @@ func TestMalformedJSONBody_DoesNotDoubleWrite(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]any
- err := json.Unmarshal(rec.Body.Bytes(), &body)
+ err := json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1())
require.NoError(t, err, "a single write must produce one well-formed JSON body, got: %s", rec.Body.String())
}
diff --git a/services/securityhub/handler_test.go b/services/securityhub/handler_test.go
index 16e8d9fc57..ca62e3017d 100644
--- a/services/securityhub/handler_test.go
+++ b/services/securityhub/handler_test.go
@@ -2,7 +2,7 @@ package securityhub_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -29,7 +30,7 @@ func doRequest(t *testing.T, h *securityhub.Handler, method, path string, body a
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
diff --git a/services/securityhub/hub_test.go b/services/securityhub/hub_test.go
index edce2bedfd..a3548a8b9e 100644
--- a/services/securityhub/hub_test.go
+++ b/services/securityhub/hub_test.go
@@ -1,7 +1,7 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"sync"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -24,7 +25,7 @@ func TestEnableSecurityHubPath(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// EnableSecurityHub returns an empty object, not a hub object
assert.NotContains(t, resp, "HubArn", "EnableSecurityHub must return empty body")
}
@@ -41,7 +42,7 @@ func TestDescribeHubIsGETAccounts(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "HubArn")
assert.Contains(t, resp, "SubscribedAt")
@@ -97,7 +98,7 @@ func TestUpdateSecurityHubConfigurationIsPATCHAccounts(t *testing.T) {
// Verify the change
rec2 := doRequest(t, h, http.MethodGet, "/accounts", nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, false, resp["AutoEnableControls"])
assert.Equal(t, "NONE", resp["AutoEnableStandards"])
@@ -115,7 +116,7 @@ func TestHubArnFormat(t *testing.T) {
rec := doRequest(t, h, http.MethodGet, "/accounts", nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
hubArn, _ := resp["HubArn"].(string)
assert.Equal(t, "arn:aws:securityhub:eu-west-1:123456789012:hub/default", hubArn)
@@ -214,7 +215,7 @@ func TestHubV2(t *testing.T) {
rec := doRequest(t, h, s.method, s.path, s.body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
s.check(t, rec.Code, resp)
}
})
@@ -404,7 +405,7 @@ func TestHubV2Features(t *testing.T) {
rec := doRequest(t, h, s.method, s.path, s.body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
s.check(t, rec.Code, resp)
}
})
diff --git a/services/securityhub/insights_test.go b/services/securityhub/insights_test.go
index 89e6962324..a1097c96d1 100644
--- a/services/securityhub/insights_test.go
+++ b/services/securityhub/insights_test.go
@@ -1,13 +1,14 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -27,7 +28,7 @@ func TestCreateInsightPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ := resp["InsightArn"].(string)
assert.NotEmpty(t, arn)
@@ -47,7 +48,7 @@ func TestGetInsightsIsPOSTInsightsGet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "Insights")
}
@@ -67,7 +68,7 @@ func TestGetInsightResultsIsGETInsightsResults(t *testing.T) {
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
insightArn, _ := createResp["InsightArn"].(string)
require.NotEmpty(t, insightArn)
@@ -76,7 +77,7 @@ func TestGetInsightResultsIsGETInsightsResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// Response is wrapped in InsightResults key
insightResults, hasWrapper := resp["InsightResults"]
@@ -103,7 +104,7 @@ func TestUpdateInsightIsPATCHInsightsArn(t *testing.T) {
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
insightArn, _ := createResp["InsightArn"].(string)
require.NotEmpty(t, insightArn)
@@ -129,7 +130,7 @@ func TestDeleteInsightIsDELETEInsightsArn(t *testing.T) {
})
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
insightArn, _ := createResp["InsightArn"].(string)
require.NotEmpty(t, insightArn)
@@ -138,7 +139,7 @@ func TestDeleteInsightIsDELETEInsightsArn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
deletedArn, _ := resp["InsightArn"].(string)
assert.Equal(t, insightArn, deletedArn, "DeleteInsight must return the deleted InsightArn")
diff --git a/services/securityhub/invitations_test.go b/services/securityhub/invitations_test.go
index 40c945ec34..1f6fa9dcb3 100644
--- a/services/securityhub/invitations_test.go
+++ b/services/securityhub/invitations_test.go
@@ -1,13 +1,14 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -176,7 +177,7 @@ func TestInvitations(t *testing.T) {
rec := doRequest(t, h, s.method, s.path, s.body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
s.check(t, rec.Code, resp)
}
})
@@ -375,7 +376,7 @@ func TestHandler_DeclineDeleteInvitations(t *testing.T) {
rec := doRequest(t, h, s.method, s.path, s.body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
s.check(t, rec.Code, resp)
}
})
diff --git a/services/securityhub/members_only_associated_test.go b/services/securityhub/members_only_associated_test.go
index 91858b60d5..f770b4c89a 100644
--- a/services/securityhub/members_only_associated_test.go
+++ b/services/securityhub/members_only_associated_test.go
@@ -1,12 +1,14 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestListMembers_OnlyAssociated(t *testing.T) {
@@ -43,7 +45,7 @@ func TestListMembers_OnlyAssociated(t *testing.T) {
InvitationID string `json:"InvitationId"`
} `json:"Invitations"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &inv))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &inv, jsonx.V1()))
require.Len(t, inv.Invitations, 1)
require.Equal(t, http.StatusOK, doRequest(t, h, http.MethodPost, "/administrator", map[string]any{
@@ -59,7 +61,7 @@ func TestListMembers_OnlyAssociated(t *testing.T) {
MemberStatus string `json:"MemberStatus"`
} `json:"Members"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Members, tt.wantCount)
if tt.accept {
diff --git a/services/securityhub/members_test.go b/services/securityhub/members_test.go
index 6302e69ced..dcc3716295 100644
--- a/services/securityhub/members_test.go
+++ b/services/securityhub/members_test.go
@@ -1,12 +1,14 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestMembers(t *testing.T) {
@@ -196,7 +198,7 @@ func TestMembers(t *testing.T) {
rec := doRequest(t, h, s.method, s.path, s.body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
s.check(t, rec.Code, resp)
}
})
@@ -242,7 +244,7 @@ func TestInviteMembers_UnknownAccountUnprocessed(t *testing.T) {
require.Equal(t, http.StatusOK, inviteRec.Code)
var inviteResp map[string]any
- require.NoError(t, json.Unmarshal(inviteRec.Body.Bytes(), &inviteResp))
+ require.NoError(t, json.Unmarshal(inviteRec.Body.Bytes(), &inviteResp, jsonx.V1()))
unprocessed, _ := inviteResp["UnprocessedAccounts"].([]any)
assert.Len(t, unprocessed, tc.wantUnproc)
@@ -252,7 +254,7 @@ func TestInviteMembers_UnknownAccountUnprocessed(t *testing.T) {
require.Equal(t, http.StatusOK, membersRec.Code)
var membersResp map[string]any
- require.NoError(t, json.Unmarshal(membersRec.Body.Bytes(), &membersResp))
+ require.NoError(t, json.Unmarshal(membersRec.Body.Bytes(), &membersResp, jsonx.V1()))
members, _ := membersResp["Members"].([]any)
require.Len(t, members, 1)
diff --git a/services/securityhub/multi_region_test.go b/services/securityhub/multi_region_test.go
index 07a6fd9aa7..d1c4f3b66c 100644
--- a/services/securityhub/multi_region_test.go
+++ b/services/securityhub/multi_region_test.go
@@ -2,7 +2,7 @@ package securityhub_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -41,7 +42,7 @@ func decode(t *testing.T, body string) map[string]any {
out := map[string]any{}
if body != "" {
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
}
return out
diff --git a/services/securityhub/organizations_test.go b/services/securityhub/organizations_test.go
index c9c92ddbdf..71cb1c983b 100644
--- a/services/securityhub/organizations_test.go
+++ b/services/securityhub/organizations_test.go
@@ -1,12 +1,14 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestOrganization(t *testing.T) {
@@ -133,7 +135,7 @@ func TestOrganization(t *testing.T) {
rec := doRequest(t, h, s.method, s.path, s.body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
s.check(t, rec.Code, resp)
}
})
diff --git a/services/securityhub/persistence_test.go b/services/securityhub/persistence_test.go
index 933d687449..1b35e47c82 100644
--- a/services/securityhub/persistence_test.go
+++ b/services/securityhub/persistence_test.go
@@ -1,12 +1,14 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -285,12 +287,12 @@ func TestInMemoryBackend_SnapshotRestore_FullState(t *testing.T) {
assert.Equal(t, "finding-metadata-uid-1", ticketV2.FindingMetadataUid)
var snapOut struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
}
- require.NoError(t, json.Unmarshal(snap, &snapOut))
+ require.NoError(t, json.Unmarshal(snap, &snapOut, jsonx.V1()))
var restoredTickets []securityhub.TicketV2
- require.NoError(t, json.Unmarshal(snapOut.Tables["ticketsV2"], &restoredTickets))
+ require.NoError(t, json.Unmarshal(snapOut.Tables["ticketsV2"], &restoredTickets, jsonx.V1()))
require.Len(t, restoredTickets, 1)
assert.Equal(t, ticketV2.TicketId, restoredTickets[0].TicketId)
assert.Equal(t, connV2.ConnectorId, restoredTickets[0].ConnectorId)
diff --git a/services/securityhub/products_test.go b/services/securityhub/products_test.go
index c16355686f..fe99496c0d 100644
--- a/services/securityhub/products_test.go
+++ b/services/securityhub/products_test.go
@@ -1,7 +1,7 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -24,7 +25,7 @@ func TestDescribeProductsIsGETProducts(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
products, _ := resp["Products"].([]any)
assert.NotEmpty(t, products)
@@ -49,7 +50,7 @@ func TestEnableImportFindingsForProductPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
subArn, _ := resp["ProductSubscriptionArn"].(string)
assert.NotEmpty(t, subArn)
@@ -69,7 +70,7 @@ func TestListEnabledProductsForImportIsGETProductSubscriptions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
subs, _ := resp["ProductSubscriptions"].([]any)
assert.Len(t, subs, 1)
@@ -170,7 +171,7 @@ func TestHandler_DisableImportFindingsForProduct(t *testing.T) {
require.Equal(t, http.StatusOK, enableRec.Code)
var enableResp map[string]any
- require.NoError(t, json.Unmarshal(enableRec.Body.Bytes(), &enableResp))
+ require.NoError(t, json.Unmarshal(enableRec.Body.Bytes(), &enableResp, jsonx.V1()))
subArn, _ := enableResp["ProductSubscriptionArn"].(string)
require.NotEmpty(t, subArn)
@@ -308,7 +309,7 @@ func TestRecommendedPolicyV2(t *testing.T) {
rec := doRequest(t, h, s.method, s.path, s.body)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
s.check(t, rec.Code, resp)
}
})
diff --git a/services/securityhub/resources_v2_test.go b/services/securityhub/resources_v2_test.go
index 365c7caeeb..a9376cd268 100644
--- a/services/securityhub/resources_v2_test.go
+++ b/services/securityhub/resources_v2_test.go
@@ -1,7 +1,7 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -23,7 +24,7 @@ func TestGetResourcesV2_Empty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
resources, _ := resp["Resources"].([]any)
assert.Empty(t, resources)
}
diff --git a/services/securityhub/standards_test.go b/services/securityhub/standards_test.go
index b5a83bbb6e..1c9a8f12e1 100644
--- a/services/securityhub/standards_test.go
+++ b/services/securityhub/standards_test.go
@@ -1,7 +1,7 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/securityhub"
)
@@ -30,7 +31,7 @@ func TestBatchEnableStandardsPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
subs, _ := resp["StandardsSubscriptions"].([]any)
assert.Len(t, subs, 1)
@@ -55,7 +56,7 @@ func TestBatchEnableStandardsPath(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
gotSubs, _ := getResp["StandardsSubscriptions"].([]any)
require.Len(t, gotSubs, 1)
@@ -76,7 +77,7 @@ func TestBatchDisableStandardsPath(t *testing.T) {
})
var enableResp map[string]any
- require.NoError(t, json.Unmarshal(enableRec.Body.Bytes(), &enableResp))
+ require.NoError(t, json.Unmarshal(enableRec.Body.Bytes(), &enableResp, jsonx.V1()))
subs, _ := enableResp["StandardsSubscriptions"].([]any)
require.Len(t, subs, 1)
@@ -89,7 +90,7 @@ func TestBatchDisableStandardsPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
disabledSubs, _ := resp["StandardsSubscriptions"].([]any)
assert.Len(t, disabledSubs, 1)
@@ -113,7 +114,7 @@ func TestGetEnabledStandardsIsPOSTStandardsGet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "StandardsSubscriptions")
}
@@ -128,7 +129,7 @@ func TestDescribeStandardsIsGETStandards(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
standards, _ := resp["Standards"].([]any)
assert.NotEmpty(t, standards, "DescribeStandards must return built-in standards")
@@ -157,7 +158,7 @@ func TestDescribeStandardsControlsPath(t *testing.T) {
})
var enableResp map[string]any
- require.NoError(t, json.Unmarshal(enableRec.Body.Bytes(), &enableResp))
+ require.NoError(t, json.Unmarshal(enableRec.Body.Bytes(), &enableResp, jsonx.V1()))
subs, _ := enableResp["StandardsSubscriptions"].([]any)
subArn, _ := subs[0].(map[string]any)["StandardsSubscriptionArn"].(string)
@@ -166,7 +167,7 @@ func TestDescribeStandardsControlsPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
controls, _ := resp["Controls"].([]any)
assert.NotEmpty(t, controls)
@@ -192,7 +193,7 @@ func TestUpdateStandardsControlPath(t *testing.T) {
})
var enableResp map[string]any
- require.NoError(t, json.Unmarshal(enableRec.Body.Bytes(), &enableResp))
+ require.NoError(t, json.Unmarshal(enableRec.Body.Bytes(), &enableResp, jsonx.V1()))
subs, _ := enableResp["StandardsSubscriptions"].([]any)
subArn, _ := subs[0].(map[string]any)["StandardsSubscriptionArn"].(string)
@@ -253,7 +254,7 @@ func TestListStandardsControlAssociationsIsGETAssociations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assocs, _ := resp["StandardsControlAssociationSummaries"].([]any)
assert.NotEmpty(t, assocs)
@@ -282,7 +283,7 @@ func TestBatchGetStdCtlAssociationsPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "StandardsControlAssociationDetails")
assert.Contains(t, resp, "UnprocessedAssociations")
@@ -308,7 +309,7 @@ func TestBatchUpdateStdCtlAssociationsPath(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "UnprocessedAssociationUpdates")
}
@@ -332,7 +333,7 @@ func TestBatchDisableStandards_StatusIsDeleting(t *testing.T) {
require.Equal(t, http.StatusOK, enRec.Code)
var enResp map[string]any
- require.NoError(t, json.Unmarshal(enRec.Body.Bytes(), &enResp))
+ require.NoError(t, json.Unmarshal(enRec.Body.Bytes(), &enResp, jsonx.V1()))
subs, _ := enResp["StandardsSubscriptions"].([]any)
require.NotEmpty(t, subs)
@@ -346,7 +347,7 @@ func TestBatchDisableStandards_StatusIsDeleting(t *testing.T) {
require.Equal(t, http.StatusOK, disRec.Code)
var disResp map[string]any
- require.NoError(t, json.Unmarshal(disRec.Body.Bytes(), &disResp))
+ require.NoError(t, json.Unmarshal(disRec.Body.Bytes(), &disResp, jsonx.V1()))
disabledSubs, _ := disResp["StandardsSubscriptions"].([]any)
require.NotEmpty(t, disabledSubs)
@@ -375,7 +376,7 @@ func TestUpdateStandardsControl_StatusValidation(t *testing.T) {
require.Equal(t, http.StatusOK, enRec.Code)
var enResp map[string]any
- require.NoError(t, json.Unmarshal(enRec.Body.Bytes(), &enResp))
+ require.NoError(t, json.Unmarshal(enRec.Body.Bytes(), &enResp, jsonx.V1()))
subs, _ := enResp["StandardsSubscriptions"].([]any)
require.NotEmpty(t, subs)
@@ -454,7 +455,7 @@ func TestBatchUpdateStdCtlAssociations_Persisted(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
unprocessed, _ := updateResp["UnprocessedAssociationUpdates"].([]any)
assert.Empty(t, unprocessed, "update must succeed without unprocessed items")
@@ -470,7 +471,7 @@ func TestBatchUpdateStdCtlAssociations_Persisted(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
details, _ := getResp["StandardsControlAssociationDetails"].([]any)
require.Len(t, details, 1)
@@ -503,7 +504,7 @@ func TestStandardsSubscriptions_IncludesStandardsStatusReason(t *testing.T) {
require.Equal(t, http.StatusOK, enRec.Code)
var enResp map[string]any
- require.NoError(t, json.Unmarshal(enRec.Body.Bytes(), &enResp))
+ require.NoError(t, json.Unmarshal(enRec.Body.Bytes(), &enResp, jsonx.V1()))
subs, _ := enResp["StandardsSubscriptions"].([]any)
require.NotEmpty(t, subs)
@@ -516,7 +517,7 @@ func TestStandardsSubscriptions_IncludesStandardsStatusReason(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
getSubs, _ := getResp["StandardsSubscriptions"].([]any)
require.NotEmpty(t, getSubs)
_, hasStatusReasonGet := getSubs[0].(map[string]any)["StandardsStatusReason"]
@@ -544,7 +545,7 @@ func TestBatchGetStdCtlAssociations_MissingFields(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
details, _ := resp["StandardsControlAssociationDetails"].([]any)
require.Len(t, details, 1)
diff --git a/services/securityhub/store.go b/services/securityhub/store.go
index 91f8855b2e..6c78a2da4a 100644
--- a/services/securityhub/store.go
+++ b/services/securityhub/store.go
@@ -2,7 +2,7 @@ package securityhub
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"strconv"
@@ -229,9 +229,9 @@ type snapshot struct {
OrgAdminAccounts map[string]string `json:"orgAdminAccounts"`
OrgAdminAccountFeatures map[string]string `json:"orgAdminAccountFeatures,omitempty"`
// V2
- HubV2 *HubV2 `json:"hubV2"`
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ HubV2 *HubV2 `json:"hubV2"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
StandardsSeq int `json:"standardsSeq"`
ActionTargetSeq int `json:"actionTargetSeq"`
diff --git a/services/securityhub/tags_test.go b/services/securityhub/tags_test.go
index 2a03bc6b18..e1775d28e1 100644
--- a/services/securityhub/tags_test.go
+++ b/services/securityhub/tags_test.go
@@ -1,7 +1,7 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -9,6 +9,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Batch-1 accuracy gap: TagResource is POST /tags/{ResourceArn}.
@@ -40,7 +42,7 @@ func TestListTagsForResourceIsGETTagsArn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags, _ := resp["Tags"].(map[string]any)
assert.Equal(t, "prod", tags["env"])
@@ -71,7 +73,7 @@ func TestUntagResourceIsDELETETagsArn(t *testing.T) {
// Verify tag removed
getRec := doRequest(t, h, http.MethodGet, "/tags/"+testArn, nil)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
tags, _ := resp["Tags"].(map[string]any)
_, hasEnv := tags["env"]
diff --git a/services/securityhub/ticket_v2_client_token_test.go b/services/securityhub/ticket_v2_client_token_test.go
index e025a37104..16f83719c2 100644
--- a/services/securityhub/ticket_v2_client_token_test.go
+++ b/services/securityhub/ticket_v2_client_token_test.go
@@ -1,13 +1,15 @@
package securityhub_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCreateTicketV2_ClientToken(t *testing.T) {
@@ -43,7 +45,7 @@ func TestCreateTicketV2_ClientToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var conn map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &conn))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &conn, jsonx.V1()))
create := func(token, uid string) *httptest.ResponseRecorder {
body := map[string]any{"ConnectorId": conn["ConnectorId"], "FindingMetadataUid": uid}
@@ -64,8 +66,8 @@ func TestCreateTicketV2_ClientToken(t *testing.T) {
}
var a, b map[string]any
- require.NoError(t, json.Unmarshal(first.Body.Bytes(), &a))
- require.NoError(t, json.Unmarshal(second.Body.Bytes(), &b))
+ require.NoError(t, json.Unmarshal(first.Body.Bytes(), &a, jsonx.V1()))
+ require.NoError(t, json.Unmarshal(second.Body.Bytes(), &b, jsonx.V1()))
assert.Equal(t, tt.wantSame, a["TicketId"] == b["TicketId"])
})
}
diff --git a/services/serverlessrepo/handler.go b/services/serverlessrepo/handler.go
index db8f90cc83..1ddefb91c4 100644
--- a/services/serverlessrepo/handler.go
+++ b/services/serverlessrepo/handler.go
@@ -3,7 +3,7 @@ package serverlessrepo
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -18,6 +18,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -412,34 +413,31 @@ func (h *Handler) dispatchPolicyAndMiscOps(
}
func (h *Handler) handleError(c *echo.Context, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, awserr.ErrNotFound):
// json.Marshal on map[string]string never returns an error; _ is intentional.
payload, _ := json.Marshal(map[string]string{
keyTypeField: "NotFoundException",
keyMessageField: wireMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusNotFound, payload)
case errors.Is(err, awserr.ErrConflict):
payload, _ := json.Marshal(map[string]string{
keyTypeField: "ConflictException",
keyMessageField: wireMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusConflict, payload)
case errors.Is(err, awserr.ErrInvalidParameter),
errors.Is(err, errInvalidRequest),
errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr),
- errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err),
+ jsonx.IsTypeError(err):
payload, _ := json.Marshal(map[string]string{
keyTypeField: "BadRequestException",
keyMessageField: wireMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
default:
@@ -452,7 +450,7 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
payload, _ := json.Marshal(map[string]string{
keyTypeField: "InternalServerErrorException",
keyMessageField: wireMessage(err),
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusInternalServerError, payload)
}
diff --git a/services/serverlessrepo/handler_application_dependencies.go b/services/serverlessrepo/handler_application_dependencies.go
index 6b5ee859fe..ff10b8b17a 100644
--- a/services/serverlessrepo/handler_application_dependencies.go
+++ b/services/serverlessrepo/handler_application_dependencies.go
@@ -1,8 +1,10 @@
package serverlessrepo
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleListApplicationDependencies(req *http.Request) ([]byte, error) {
@@ -60,5 +62,5 @@ func (h *Handler) handleListApplicationDependencies(req *http.Request) ([]byte,
resp["nextToken"] = encodePageToken(deps[end-1].ApplicationID)
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/serverlessrepo/handler_application_dependencies_test.go b/services/serverlessrepo/handler_application_dependencies_test.go
index ef4312c455..e1de9bc828 100644
--- a/services/serverlessrepo/handler_application_dependencies_test.go
+++ b/services/serverlessrepo/handler_application_dependencies_test.go
@@ -1,13 +1,14 @@
package serverlessrepo_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/serverlessrepo"
)
@@ -44,7 +45,7 @@ func TestHandler_ListApplicationDependencies(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
deps, ok := resp["dependencies"].([]any)
require.True(t, ok)
assert.Empty(t, deps)
@@ -64,7 +65,7 @@ func TestListApplicationDependencies_EmptyList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
deps, ok := resp["dependencies"].([]any)
require.True(t, ok, "dependencies must be an array (not nil)")
assert.Empty(t, deps)
@@ -91,7 +92,7 @@ func TestListApplicationDependencies_NoSemanticVersion_ReturnsEmpty(t *testing.T
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
deps := resp["dependencies"].([]any)
assert.Empty(t, deps)
}
@@ -139,7 +140,7 @@ func TestListApplicationDependencies_PersistedState(t *testing.T) {
var response struct {
Dependencies []serverlessrepo.ApplicationDependency `json:"dependencies"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &response))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &response, jsonx.V1()))
assert.ElementsMatch(t, tests, response.Dependencies)
}
@@ -171,7 +172,7 @@ func TestListApplicationDependencies_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp1, jsonx.V1()))
page1, ok := resp1["dependencies"].([]any)
require.True(t, ok)
assert.Len(t, page1, 2)
@@ -186,7 +187,7 @@ func TestListApplicationDependencies_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
page2, ok := resp2["dependencies"].([]any)
require.True(t, ok)
assert.Len(t, page2, 1)
@@ -214,7 +215,7 @@ func TestListApplicationDependencies_MaxItemsDefault(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
page, ok := resp["dependencies"].([]any)
require.True(t, ok)
assert.Len(t, page, 3)
@@ -243,7 +244,7 @@ func TestListApplicationDependencies_DeterministicOrder(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
page, ok := resp["dependencies"].([]any)
require.True(t, ok)
require.Len(t, page, 3)
diff --git a/services/serverlessrepo/handler_application_policy.go b/services/serverlessrepo/handler_application_policy.go
index 92b55284d7..14867354f6 100644
--- a/services/serverlessrepo/handler_application_policy.go
+++ b/services/serverlessrepo/handler_application_policy.go
@@ -2,10 +2,11 @@ package serverlessrepo
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -33,7 +34,7 @@ func (h *Handler) handleGetApplicationPolicy(req *http.Request) ([]byte, error)
return nil, backendErr
}
- return json.Marshal(map[string]any{"statements": toPolicyStatementsResponse(stmts)})
+ return json.Marshal(map[string]any{"statements": toPolicyStatementsResponse(stmts)}, jsonx.V1())
}
func (h *Handler) handlePutApplicationPolicy(ctx context.Context, req *http.Request, body []byte) ([]byte, error) {
@@ -43,7 +44,7 @@ func (h *Handler) handlePutApplicationPolicy(ctx context.Context, req *http.Requ
}
var putReq putApplicationPolicyRequest
- if jsonErr := json.Unmarshal(body, &putReq); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &putReq, jsonx.V1()); jsonErr != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, jsonErr)
}
@@ -65,7 +66,7 @@ func (h *Handler) handlePutApplicationPolicy(ctx context.Context, req *http.Requ
log := logger.Load(ctx)
log.InfoContext(ctx, "serverlessrepo: updated application policy", "app", appName)
- return json.Marshal(map[string]any{"statements": toPolicyStatementsResponse(result)})
+ return json.Marshal(map[string]any{"statements": toPolicyStatementsResponse(result)}, jsonx.V1())
}
func toPolicyStatementsResponse(stmts []*ApplicationPolicyStatement) []map[string]any {
diff --git a/services/serverlessrepo/handler_application_policy_test.go b/services/serverlessrepo/handler_application_policy_test.go
index 56d4e38805..9eff5ad563 100644
--- a/services/serverlessrepo/handler_application_policy_test.go
+++ b/services/serverlessrepo/handler_application_policy_test.go
@@ -1,13 +1,14 @@
package serverlessrepo_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/serverlessrepo"
)
@@ -62,7 +63,7 @@ func TestHandler_GetApplicationPolicy(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts, ok := resp["statements"].([]any)
require.True(t, ok)
assert.Len(t, stmts, tt.wantLen)
@@ -82,7 +83,7 @@ func TestGetApplicationPolicy_EmptyForNewApp(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts, ok := resp["statements"].([]any)
require.True(t, ok, "statements must be an array")
assert.Empty(t, stmts, "new app should have empty policy statements")
@@ -99,7 +100,7 @@ func TestGetApplicationPolicy_EmptyPolicy_ReturnsEmptyStatements(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts := resp["statements"].([]any)
assert.Empty(t, stmts)
}
@@ -125,7 +126,7 @@ func TestGetApplicationPolicy_ARNForm_Routing(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts, ok := resp["statements"].([]any)
require.True(t, ok)
assert.Len(t, stmts, 1)
@@ -198,7 +199,7 @@ func TestHandler_PutApplicationPolicy(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts, ok := resp["statements"].([]any)
require.True(t, ok)
assert.Len(t, stmts, tt.wantLen)
@@ -334,7 +335,7 @@ func TestErrValidation_MapsTo400(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "BadRequestException", resp["__type"])
}
@@ -357,7 +358,7 @@ func TestPutApplicationPolicy_AutoGeneratesStatementID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts, ok := resp["statements"].([]any)
require.True(t, ok)
require.Len(t, stmts, 1)
@@ -382,7 +383,7 @@ func TestPolicyResponse_NonNilSlices(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts, ok := resp["statements"].([]any)
require.True(t, ok)
@@ -419,7 +420,7 @@ func TestPutApplicationPolicy_PrincipalOrgIDs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
stmts := putResp["statements"].([]any)
stmt := stmts[0].(map[string]any)
orgIDs := stmt["principalOrgIDs"].([]any)
@@ -431,7 +432,7 @@ func TestPutApplicationPolicy_PrincipalOrgIDs(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp, jsonx.V1()))
stmts2 := getResp["statements"].([]any)
stmt2 := stmts2[0].(map[string]any)
orgIDs2 := stmt2["principalOrgIDs"].([]any)
@@ -466,7 +467,7 @@ func TestPutApplicationPolicy_ReplacesExistingStatements(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stmts := resp["statements"].([]any)
require.Len(t, stmts, 1, "policy should have exactly 1 statement after replacement")
assert.Equal(t, "s3", stmts[0].(map[string]any)["statementId"])
diff --git a/services/serverlessrepo/handler_application_versions.go b/services/serverlessrepo/handler_application_versions.go
index 9ac76253e0..cb0019dc31 100644
--- a/services/serverlessrepo/handler_application_versions.go
+++ b/services/serverlessrepo/handler_application_versions.go
@@ -2,10 +2,11 @@ package serverlessrepo
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -32,7 +33,7 @@ func (h *Handler) handleCreateApplicationVersion(ctx context.Context, req *http.
}
var createReq createApplicationVersionRequest
- if jsonErr := json.Unmarshal(body, &createReq); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &createReq, jsonx.V1()); jsonErr != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, jsonErr)
}
@@ -66,7 +67,7 @@ func (h *Handler) handleCreateApplicationVersion(ctx context.Context, req *http.
log.InfoContext(ctx, "serverlessrepo: created application version",
"app", appName, "version", v.SemanticVersion)
- b, marshalErr := json.Marshal(toVersionResponse(v))
+ b, marshalErr := json.Marshal(toVersionResponse(v), jsonx.V1())
if marshalErr != nil {
return nil, marshalErr
}
@@ -154,7 +155,7 @@ func (h *Handler) handleListApplicationVersions(req *http.Request) ([]byte, erro
resp["nextToken"] = encodePageToken(versions[end-1].SemanticVersion)
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// toVersionResponse converts an ApplicationVersion to a map matching the AWS SAR Version shape.
diff --git a/services/serverlessrepo/handler_application_versions_test.go b/services/serverlessrepo/handler_application_versions_test.go
index be50a10f44..8fb701489d 100644
--- a/services/serverlessrepo/handler_application_versions_test.go
+++ b/services/serverlessrepo/handler_application_versions_test.go
@@ -1,13 +1,14 @@
package serverlessrepo_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/serverlessrepo"
)
@@ -69,7 +70,7 @@ func TestHandler_CreateApplicationVersion(t *testing.T) {
if tt.wantCode == http.StatusCreated {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "1.0.0", resp["semanticVersion"])
assert.NotNil(t, resp["parameterDefinitions"])
assert.NotNil(t, resp["requiredCapabilities"])
@@ -104,7 +105,7 @@ func TestCreateApplicationVersion_HasRequiredFields(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["parameterDefinitions"], "parameterDefinitions must be present")
assert.NotNil(t, resp["requiredCapabilities"], "requiredCapabilities must be present")
assert.Equal(t, true, resp["resourcesSupported"])
@@ -157,7 +158,7 @@ func TestCreateApplicationVersion_ArchiveURL(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var response map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &response))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &response, jsonx.V1()))
assert.Equal(t, "s3://bucket/archive.zip", response["sourceCodeArchiveUrl"])
assert.NotEmpty(t, response["templateUrl"])
})
@@ -177,7 +178,7 @@ func TestCreateApplicationVersion_TemplateURLOnly(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "s3://bucket/tmpl.yaml", resp["templateUrl"])
}
@@ -227,7 +228,7 @@ func TestCreateApplicationVersion_TemplateBody(t *testing.T) {
if tt.wantCode == http.StatusCreated {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["templateUrl"])
}
})
@@ -251,7 +252,7 @@ func TestCreateApplicationVersion_DuplicateReturns409(t *testing.T) {
assert.Equal(t, http.StatusConflict, rec2.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ConflictException", errResp["__type"])
}
@@ -297,7 +298,7 @@ func TestCreateApplicationVersion_GeneratesTemplateURL(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["templateUrl"], "templateUrl should be auto-generated from sourceCodeUrl")
}
@@ -321,7 +322,7 @@ func TestCreateApplicationVersion_UpdatesLatestVersion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
version, ok := resp["version"].(map[string]any)
require.True(t, ok, "version field must be present")
assert.Equal(t, "2.0.0", version["semanticVersion"], "should return latest created version")
@@ -341,7 +342,7 @@ func TestCreateApplicationVersion_FullVersionDataInGetApplication(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
version, ok := resp["version"].(map[string]any)
require.True(t, ok, "version must be embedded in GetApplication response")
assert.Equal(t, "3.1.4", version["semanticVersion"])
@@ -400,7 +401,7 @@ func TestHandler_ListApplicationVersions(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions, ok := resp["versions"].([]any)
require.True(t, ok)
assert.Len(t, versions, tt.wantLen)
@@ -425,7 +426,7 @@ func TestListApplicationVersions_SemanticVersionFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions, ok := resp["versions"].([]any)
require.True(t, ok)
assert.Len(t, versions, 1, "filter should return only 1 matching version")
@@ -448,7 +449,7 @@ func TestListApplicationVersions_MaxItems(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions, ok := resp["versions"].([]any)
require.True(t, ok)
assert.Len(t, versions, 2)
@@ -472,7 +473,7 @@ func TestListApplicationVersions_PaginationNextToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r1, jsonx.V1()))
v1 := r1["versions"].([]any)
assert.Len(t, v1, 2)
nt, ok := r1["nextToken"].(string)
@@ -487,7 +488,7 @@ func TestListApplicationVersions_PaginationNextToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
v2 := r2["versions"].([]any)
assert.Len(t, v2, 2)
assert.Nil(t, r2["nextToken"])
@@ -513,7 +514,7 @@ func TestListApplicationVersions_SummaryShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions, ok := resp["versions"].([]any)
require.True(t, ok)
require.Len(t, versions, 1)
@@ -543,7 +544,7 @@ func TestListApplicationVersions_ARNForm(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions, ok := resp["versions"].([]any)
require.True(t, ok)
assert.Len(t, versions, 1)
diff --git a/services/serverlessrepo/handler_applications.go b/services/serverlessrepo/handler_applications.go
index f57d1d61d2..8c9d10a22b 100644
--- a/services/serverlessrepo/handler_applications.go
+++ b/services/serverlessrepo/handler_applications.go
@@ -2,10 +2,11 @@ package serverlessrepo
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -153,7 +154,7 @@ func toEmbeddedVersionResponse(v *ApplicationVersion) *versionResponse {
func (h *Handler) handleCreateApplication(ctx context.Context, body []byte) ([]byte, error) {
var req createApplicationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -205,7 +206,7 @@ func (h *Handler) handleCreateApplication(ctx context.Context, body []byte) ([]b
resp.Version = toEmbeddedVersionResponse(v)
}
- b, marshalErr := json.Marshal(resp)
+ b, marshalErr := json.Marshal(resp, jsonx.V1())
if marshalErr != nil {
return nil, marshalErr
}
@@ -244,7 +245,7 @@ func (h *Handler) handleGetApplication(req *http.Request) ([]byte, error) {
}
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
func (h *Handler) handleListApplications(req *http.Request) ([]byte, error) {
@@ -300,7 +301,7 @@ func (h *Handler) handleListApplications(req *http.Request) ([]byte, error) {
resp["nextToken"] = encodePageToken(apps[end-1].Name)
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// updateApplicationRequest is the request body for UpdateApplication.
@@ -320,7 +321,7 @@ func (h *Handler) handleUpdateApplication(ctx context.Context, req *http.Request
}
var updateReq updateApplicationRequest
- if err := json.Unmarshal(body, &updateReq); err != nil {
+ if err := json.Unmarshal(body, &updateReq, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -365,7 +366,7 @@ func (h *Handler) handleUpdateApplication(ctx context.Context, req *http.Request
}
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
func (h *Handler) handleDeleteApplication(ctx context.Context, req *http.Request) error {
@@ -396,7 +397,7 @@ func (h *Handler) handleUnshareApplication(ctx context.Context, req *http.Reques
}
var unshareReq unshareApplicationRequest
- if jsonErr := json.Unmarshal(body, &unshareReq); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &unshareReq, jsonx.V1()); jsonErr != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, jsonErr)
}
diff --git a/services/serverlessrepo/handler_applications_list_test.go b/services/serverlessrepo/handler_applications_list_test.go
index 5a7004cad4..44427f46b2 100644
--- a/services/serverlessrepo/handler_applications_list_test.go
+++ b/services/serverlessrepo/handler_applications_list_test.go
@@ -1,13 +1,14 @@
package serverlessrepo_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/serverlessrepo"
)
@@ -50,7 +51,7 @@ func TestHandler_ListApplications(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
apps, ok := resp["applications"].([]any)
require.True(t, ok)
@@ -70,7 +71,7 @@ func TestListApplications_IncludesLabels(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
apps, ok := resp["applications"].([]any)
require.True(t, ok)
require.Len(t, apps, 1)
@@ -96,7 +97,7 @@ func TestListApplications_PageBoundaryExact(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r1, jsonx.V1()))
apps1 := r1["applications"].([]any)
assert.Len(t, apps1, 2)
nt := r1["nextToken"].(string)
@@ -106,7 +107,7 @@ func TestListApplications_PageBoundaryExact(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r2, jsonx.V1()))
apps2 := r2["applications"].([]any)
assert.Len(t, apps2, 2)
assert.Nil(t, r2["nextToken"], "no more pages when exactly on boundary")
@@ -126,7 +127,7 @@ func TestListApplications_Pagination_MaxItems(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
apps, ok := resp["applications"].([]any)
require.True(t, ok)
assert.Len(t, apps, 2, "maxItems=2 should return 2 items")
@@ -148,7 +149,7 @@ func TestListApplications_Pagination_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
nextToken, ok := resp1["nextToken"].(string)
require.True(t, ok, "nextToken should be a string")
@@ -157,7 +158,7 @@ func TestListApplications_Pagination_NextToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
apps, ok := resp2["applications"].([]any)
require.True(t, ok)
assert.Len(t, apps, 1, "second page should have 1 remaining item")
diff --git a/services/serverlessrepo/handler_applications_test.go b/services/serverlessrepo/handler_applications_test.go
index cb1bd90980..59a4f36e2d 100644
--- a/services/serverlessrepo/handler_applications_test.go
+++ b/services/serverlessrepo/handler_applications_test.go
@@ -1,13 +1,14 @@
package serverlessrepo_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/serverlessrepo"
)
@@ -73,7 +74,7 @@ func TestHandler_CreateApplication(t *testing.T) {
if tt.wantARN {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["applicationId"])
assert.Equal(t, tt.body["name"], resp["name"])
}
@@ -104,7 +105,7 @@ func TestCreateApplication_MissingAuthor(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["message"], "Author is required")
}
@@ -119,7 +120,7 @@ func TestCreateApplication_MissingDescription(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["message"], "Description is required")
}
@@ -136,7 +137,7 @@ func TestCreateApplication_WithLabels(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
labels, ok := resp["labels"].([]any)
require.True(t, ok)
@@ -156,7 +157,7 @@ func TestCreateApplication_WithHomePageURL(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "https://github.com/example/my-app", resp["homePageUrl"])
}
@@ -193,7 +194,7 @@ func TestApplicationResponse_LabelsField(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// SAR uses "labels" ([]string), not "tags" (map[string]string)
labels, ok := resp["labels"].([]any)
@@ -214,7 +215,7 @@ func TestApplication_ARNFormat(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
appID := resp["applicationId"].(string)
assert.Contains(t, appID, "arn:aws:serverlessrepo:")
assert.Contains(t, appID, ":applications/arn-check-app")
@@ -234,7 +235,7 @@ func TestCreateApplication_TemplateURLOnly_CreatesVersion(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
version := resp["version"].(map[string]any)
assert.Equal(t, "s3://bucket/template.yaml", version["templateUrl"])
assert.Equal(t, "1.0.0", version["semanticVersion"])
@@ -256,7 +257,7 @@ func TestCreateApplication_InlineVersion_FullResponseFields(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "MIT", resp["spdxLicenseId"])
version, ok := resp["version"].(map[string]any)
@@ -339,7 +340,7 @@ func TestApplicationMetadata(t *testing.T) {
assert.Contains(t, []int{http.StatusCreated, http.StatusOK}, rec.Code)
var response map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &response))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &response, jsonx.V1()))
tt.check(t, response)
})
}
@@ -393,7 +394,7 @@ func TestHandler_GetApplication(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "existing-app", resp["name"])
}
})
@@ -429,7 +430,7 @@ func TestGetApplication_WithSemanticVersion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versionObj, ok := resp["version"].(map[string]any)
require.True(t, ok, "version field must be present when semanticVersion is requested")
assert.Equal(t, "1.2.3", versionObj["semanticVersion"])
@@ -464,7 +465,7 @@ func TestGetApplication_VersionEmbedFromStore_IncludesTemplateURL(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
version := resp["version"].(map[string]any)
assert.Equal(t, "1.5.0", version["semanticVersion"])
assert.Equal(t, "s3://my-bucket/tmpl.yaml", version["templateUrl"])
@@ -491,7 +492,7 @@ func TestGetApplication_ResponseFieldCompleteness(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "full-resp-app", resp["name"])
assert.Equal(t, "A full response app", resp["description"])
assert.Equal(t, "test-author", resp["author"])
@@ -576,7 +577,7 @@ func TestUpdateApplication_EmbedCurrentVersion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
version, ok := resp["version"].(map[string]any)
require.True(t, ok, "version must be embedded in UpdateApplication response when current version exists")
@@ -597,7 +598,7 @@ func TestUpdateApplication_NoVersionWhenNoneCreated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Nil(t, resp["version"], "version should be absent when no version has been created")
}
@@ -768,7 +769,7 @@ func TestApplication_ARNFormRouting(t *testing.T) {
if tt.check != nil {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tt.check(t, resp)
}
})
@@ -867,7 +868,7 @@ func TestCreateApplication_BodyVariants(t *testing.T) {
if tt.check != nil {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tt.check(t, resp)
}
})
@@ -910,7 +911,7 @@ func TestUpdateApplication_ReadmeBody(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["readmeUrl"])
}
})
diff --git a/services/serverlessrepo/handler_applications_validation_test.go b/services/serverlessrepo/handler_applications_validation_test.go
index cdc958b9dd..122c4fc8bc 100644
--- a/services/serverlessrepo/handler_applications_validation_test.go
+++ b/services/serverlessrepo/handler_applications_validation_test.go
@@ -1,13 +1,15 @@
package serverlessrepo_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestCreateApplication_NameLength(t *testing.T) {
@@ -36,7 +38,7 @@ func TestCreateApplication_NameLength(t *testing.T) {
if tt.wantCode == http.StatusBadRequest {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["message"], "Name must be at most")
}
})
@@ -108,7 +110,7 @@ func TestCreateApplication_AuthorLength(t *testing.T) {
if tt.wantCode == http.StatusBadRequest {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["message"], "Author must be at most")
}
})
@@ -141,7 +143,7 @@ func TestCreateApplication_DescriptionLength(t *testing.T) {
if tt.wantCode == http.StatusBadRequest {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["message"], "Description must be at most")
}
})
@@ -266,7 +268,7 @@ func TestCreateApplication_LabelsValidation(t *testing.T) {
if tt.wantMessage != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["message"], tt.wantMessage)
}
})
diff --git a/services/serverlessrepo/handler_cloud_formation.go b/services/serverlessrepo/handler_cloud_formation.go
index a39f505d81..c32d30fd17 100644
--- a/services/serverlessrepo/handler_cloud_formation.go
+++ b/services/serverlessrepo/handler_cloud_formation.go
@@ -2,11 +2,12 @@ package serverlessrepo
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -26,7 +27,7 @@ func (h *Handler) handleCreateCloudFormationTemplate(
}
var createReq createCFTemplateRequest
- if jsonErr := json.Unmarshal(body, &createReq); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &createReq, jsonx.V1()); jsonErr != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, jsonErr)
}
@@ -51,7 +52,7 @@ func (h *Handler) handleCreateCloudFormationTemplate(
resp[keySemanticVersion] = t.SemanticVersion
}
- b, marshalErr := json.Marshal(resp)
+ b, marshalErr := json.Marshal(resp, jsonx.V1())
if marshalErr != nil {
return nil, marshalErr
}
@@ -96,7 +97,7 @@ func (h *Handler) handleGetCloudFormationTemplate(req *http.Request) ([]byte, er
resp[keySemanticVersion] = t.SemanticVersion
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// createCFChangeSetRequest is the request body for CreateCloudFormationChangeSet.
@@ -120,7 +121,7 @@ func (h *Handler) handleCreateCloudFormationChangeSet(
}
var createReq createCFChangeSetRequest
- if jsonErr := json.Unmarshal(body, &createReq); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &createReq, jsonx.V1()); jsonErr != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, jsonErr)
}
@@ -162,7 +163,7 @@ func (h *Handler) handleCreateCloudFormationChangeSet(
csResp[keySemanticVersion] = cs.SemanticVersion
}
- b, marshalErr := json.Marshal(csResp)
+ b, marshalErr := json.Marshal(csResp, jsonx.V1())
if marshalErr != nil {
return nil, marshalErr
}
diff --git a/services/serverlessrepo/handler_cloud_formation_test.go b/services/serverlessrepo/handler_cloud_formation_test.go
index 4fe44e7257..c2645d97a8 100644
--- a/services/serverlessrepo/handler_cloud_formation_test.go
+++ b/services/serverlessrepo/handler_cloud_formation_test.go
@@ -1,13 +1,14 @@
package serverlessrepo_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/serverlessrepo"
)
@@ -52,7 +53,7 @@ func TestHandler_CreateCloudFormationTemplate(t *testing.T) {
if tt.wantCode == http.StatusCreated {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["templateId"])
assert.Equal(t, "ACTIVE", resp["status"])
}
@@ -86,7 +87,7 @@ func TestCreateCloudFormationTemplate_HasTemplateURL(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["templateUrl"])
assert.NotEmpty(t, resp["expirationTime"])
}
@@ -103,7 +104,7 @@ func TestCreateCloudFormationTemplate_OmitsSemanticVersionWhenEmpty(t *testing.T
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasSV := resp["semanticVersion"]
assert.False(t, hasSV, "semanticVersion must be absent from response when not provided")
assert.NotEmpty(t, resp["templateId"])
@@ -124,7 +125,7 @@ func TestCreateCloudFormationTemplate_IncludesSemanticVersionWhenProvided(t *tes
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "2.0.0", resp["semanticVersion"])
}
@@ -186,7 +187,7 @@ func TestHandler_GetCloudFormationTemplate(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["templateId"])
assert.Equal(t, "ACTIVE", resp["status"])
}
@@ -205,7 +206,7 @@ func TestGetCloudFormationTemplate_ActiveStatus(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
templateID := createResp["templateId"].(string)
rec2 := doServerlessRepoRequest(t, h, http.MethodGet,
@@ -213,7 +214,7 @@ func TestGetCloudFormationTemplate_ActiveStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "ACTIVE", getResp["status"])
}
@@ -229,7 +230,7 @@ func TestGetCloudFormationTemplate_OmitsSemanticVersionWhenEmpty(t *testing.T) {
require.Equal(t, http.StatusCreated, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
templateID := createResp["templateId"].(string)
getRec := doServerlessRepoRequest(t, h, http.MethodGet,
@@ -237,7 +238,7 @@ func TestGetCloudFormationTemplate_OmitsSemanticVersionWhenEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
_, hasSV := getResp["semanticVersion"]
assert.False(t, hasSV, "semanticVersion must be absent from GetCloudFormationTemplate when not set")
}
@@ -258,7 +259,7 @@ func TestGetCloudFormationTemplate_ExpiredStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "EXPIRED", resp["status"])
assert.Equal(t, "1.0.0", resp["semanticVersion"])
}
@@ -278,7 +279,7 @@ func TestGetCloudFormationTemplate_Fields(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
templateID := createResp["templateId"].(string)
rec2 := doServerlessRepoRequest(t, h, http.MethodGet,
@@ -286,7 +287,7 @@ func TestGetCloudFormationTemplate_Fields(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "ACTIVE", getResp["status"])
assert.Equal(t, "1.0.0", getResp["semanticVersion"])
assert.NotEmpty(t, getResp["templateUrl"])
@@ -357,7 +358,7 @@ func TestHandler_CreateCloudFormationChangeSet(t *testing.T) {
if tt.wantCode == http.StatusCreated {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["changeSetId"])
assert.NotEmpty(t, resp["stackId"])
}
@@ -392,7 +393,7 @@ func TestCreateCloudFormationChangeSet_CustomName(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["changeSetId"].(string), "my-custom-changeset")
}
@@ -477,7 +478,7 @@ func TestCreateCloudFormationChangeSet_TemplateID(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "BadRequestException", resp["__type"])
})
@@ -514,7 +515,7 @@ func TestCreateCloudFormationChangeSet_OmitsSemanticVersionWhenEmpty(t *testing.
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasSV := resp["semanticVersion"]
assert.False(t, hasSV, "semanticVersion must be absent from response when not provided")
assert.NotEmpty(t, resp["changeSetId"])
@@ -535,7 +536,7 @@ func TestCreateCloudFormationChangeSet_IncludesSemanticVersionWhenProvided(t *te
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "3.1.4", resp["semanticVersion"])
}
@@ -557,7 +558,7 @@ func TestCreateCloudFormationChangeSet_ResponseFields(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["changeSetId"])
assert.NotEmpty(t, resp["stackId"])
assert.Equal(t, "1.0.0", resp["semanticVersion"])
@@ -579,7 +580,7 @@ func TestCreateCloudFormationChangeSet_ARNForm(t *testing.T) {
require.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["changeSetId"].(string), "cloudformation", "changeSetId should be a CloudFormation ARN")
assert.Contains(t, resp["stackId"].(string), "cloudformation", "stackId should be a CloudFormation ARN")
}
diff --git a/services/serverlessrepo/handler_pagination_restart_test.go b/services/serverlessrepo/handler_pagination_restart_test.go
index e2a6942467..2e6fb60569 100644
--- a/services/serverlessrepo/handler_pagination_restart_test.go
+++ b/services/serverlessrepo/handler_pagination_restart_test.go
@@ -2,13 +2,14 @@ package serverlessrepo_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/serverlessrepo"
)
@@ -30,7 +31,7 @@ func TestListApplications_Pagination_DeletedMidPage(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r1, jsonx.V1()))
nextToken, ok := r1["nextToken"].(string)
require.True(t, ok)
require.NotEmpty(t, nextToken)
@@ -43,7 +44,7 @@ func TestListApplications_Pagination_DeletedMidPage(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r2, jsonx.V1()))
apps2, _ := r2["applications"].([]any)
restarted := false
@@ -78,7 +79,7 @@ func TestListApplicationVersions_Pagination_StaleTokenDoesNotRestart(t *testing.
require.Equal(t, http.StatusOK, rec.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r1, jsonx.V1()))
v1, _ := r1["versions"].([]any)
require.Len(t, v1, 2)
@@ -96,7 +97,7 @@ func TestListApplicationVersions_Pagination_StaleTokenDoesNotRestart(t *testing.
require.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
v2, _ := r2["versions"].([]any)
for _, item := range v2 {
@@ -137,7 +138,7 @@ func TestListApplicationDependencies_Pagination_StaleTokenDoesNotRestart(t *test
require.Equal(t, http.StatusOK, rec.Code)
var r1 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r1))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &r1, jsonx.V1()))
page1, _ := r1["dependencies"].([]any)
require.Len(t, page1, 2)
@@ -154,7 +155,7 @@ func TestListApplicationDependencies_Pagination_StaleTokenDoesNotRestart(t *test
require.Equal(t, http.StatusOK, rec2.Code)
var r2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
page2, _ := r2["dependencies"].([]any)
for _, item := range page2 {
diff --git a/services/serverlessrepo/handler_test.go b/services/serverlessrepo/handler_test.go
index 5cf76f6bd8..b087f2c8e9 100644
--- a/services/serverlessrepo/handler_test.go
+++ b/services/serverlessrepo/handler_test.go
@@ -2,7 +2,7 @@ package serverlessrepo_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/serverlessrepo"
)
@@ -38,7 +39,7 @@ func doServerlessRepoRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -77,7 +78,7 @@ func doServerlessRepoRequestEncoded(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -275,7 +276,7 @@ func TestHandler_UnexpectedError_ReturnsInternalServerErrorException(t *testing.
assert.Equal(t, http.StatusInternalServerError, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// The aws-sdk-go-v2 restjson1 error deserializer only recognizes the exact string
// "InternalServerErrorException" (case-insensitively) in the __type field to build a
// typed types.InternalServerErrorException; any other spelling (e.g.
@@ -298,7 +299,7 @@ func TestErrorShape_NotFound(t *testing.T) {
require.Equal(t, http.StatusNotFound, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "NotFoundException", resp["__type"])
assert.NotEmpty(t, resp["message"])
}
@@ -316,7 +317,7 @@ func TestErrorShape_Conflict(t *testing.T) {
require.Equal(t, http.StatusConflict, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ConflictException", resp["__type"])
}
@@ -332,6 +333,6 @@ func TestErrorShape_BadRequest(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "BadRequestException", resp["__type"])
}
diff --git a/services/serverlessrepo/multi_region_test.go b/services/serverlessrepo/multi_region_test.go
index c6101e08f1..e540de25cd 100644
--- a/services/serverlessrepo/multi_region_test.go
+++ b/services/serverlessrepo/multi_region_test.go
@@ -2,7 +2,8 @@ package serverlessrepo_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/serverlessrepo"
)
@@ -176,8 +178,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/serverlessrepo/persistence.go b/services/serverlessrepo/persistence.go
index 7687858168..5a86458e0b 100644
--- a/services/serverlessrepo/persistence.go
+++ b/services/serverlessrepo/persistence.go
@@ -2,10 +2,12 @@ package serverlessrepo
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -80,7 +82,7 @@ func newDirtyDTORegistry() (
// snapshot from an incompatible (older or newer) build of this backend as
// though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
AppPolicies map[string][]*ApplicationPolicyStatement `json:"appPolicies"`
AppDependencies map[string]map[string][]*ApplicationDependency `json:"appDependencies"`
AccountID string `json:"accountID"`
@@ -157,7 +159,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.region,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "serverlessrepo: failed to marshal snapshot", "error", err)
@@ -225,7 +227,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// cfTemplates, cfChangeSets) from tables via the ephemeral DTO registry,
// converting each DTO back to its live type. The caller MUST hold b.mu for
// writing.
-func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]jsontext.Value) error {
dtoReg, vDTOs, tDTOs, csDTOs := newDirtyDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
diff --git a/services/serverlessrepo/wire_sdk_roundtrip_test.go b/services/serverlessrepo/wire_sdk_roundtrip_test.go
index fc2ac4f7af..afad95c3c3 100644
--- a/services/serverlessrepo/wire_sdk_roundtrip_test.go
+++ b/services/serverlessrepo/wire_sdk_roundtrip_test.go
@@ -1,7 +1,7 @@
package serverlessrepo_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/serverlessrepo"
@@ -96,7 +97,7 @@ func TestCreateApplication_NoTopLevelSourceCodeURL_SDKRoundTrip(t *testing.T) {
require.NoError(t, err)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rawBody, &raw))
+ require.NoError(t, json.Unmarshal(rawBody, &raw, jsonx.V1()))
_, hasTopLevelSourceCodeURL := raw["sourceCodeUrl"]
require.False(t, hasTopLevelSourceCodeURL,
diff --git a/services/servicecatalog/constraints.go b/services/servicecatalog/constraints.go
index e1bce0dbdc..7ba042002b 100644
--- a/services/servicecatalog/constraints.go
+++ b/services/servicecatalog/constraints.go
@@ -3,9 +3,11 @@ package servicecatalog
import (
"cmp"
"context"
- "encoding/json"
+ "encoding/json/v2"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const maxNotificationARNs = 5
@@ -28,7 +30,7 @@ func constraintDetail(c *Constraint) *ConstraintDetail {
// validateConstraintParameters checks the JSON a constraint type requires.
func validateConstraintParameters(typ, params string) error {
var doc map[string]any
- if err := json.Unmarshal([]byte(params), &doc); err != nil {
+ if err := json.Unmarshal([]byte(params), &doc, jsonx.V1()); err != nil {
return invalid("Parameters is not a valid JSON object")
}
diff --git a/services/servicecatalog/handler.go b/services/servicecatalog/handler.go
index 3dfee978b3..0ffa3f8522 100644
--- a/services/servicecatalog/handler.go
+++ b/services/servicecatalog/handler.go
@@ -3,7 +3,7 @@ package servicecatalog
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"slices"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -35,7 +36,7 @@ func op[I, O any](fn func(context.Context, *I) (*O, error)) opFunc {
var in I
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, invalid("malformed request body: %v", err)
}
}
diff --git a/services/servicecatalog/helpers_test.go b/services/servicecatalog/helpers_test.go
index 3d7ac34ef0..0ebae9de99 100644
--- a/services/servicecatalog/helpers_test.go
+++ b/services/servicecatalog/helpers_test.go
@@ -2,7 +2,7 @@ package servicecatalog_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"strings"
@@ -20,6 +20,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/sdktest"
"github.com/blackbirdworks/gopherstack/services/servicecatalog"
)
@@ -103,7 +104,7 @@ type fakeTemplate struct {
func (f *fakeEngine) InspectTemplate(_ context.Context, body string) (servicecatalog.TemplateInfo, error) {
var t fakeTemplate
- if err := json.Unmarshal([]byte(body), &t); err != nil {
+ if err := json.Unmarshal([]byte(body), &t, jsonx.V1()); err != nil {
return servicecatalog.TemplateInfo{}, err
}
@@ -145,7 +146,7 @@ func sortedMapKeys[V any](m map[string]V) []string {
func (f *fakeEngine) outputsOf(template string) []servicecatalog.Output {
var t fakeTemplate
- _ = json.Unmarshal([]byte(template), &t)
+ _ = json.Unmarshal([]byte(template), &t, jsonx.V1())
out := make([]servicecatalog.Output, 0, len(t.Outputs))
for _, k := range sortedMapKeys(t.Outputs) {
diff --git a/services/servicecatalog/persistence.go b/services/servicecatalog/persistence.go
index d6d742ac43..f7d03d5b8b 100644
--- a/services/servicecatalog/persistence.go
+++ b/services/servicecatalog/persistence.go
@@ -2,7 +2,7 @@ package servicecatalog
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,8 +12,8 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/servicecatalog/rules.go b/services/servicecatalog/rules.go
index b82f6557f3..2b55490aa6 100644
--- a/services/servicecatalog/rules.go
+++ b/services/servicecatalog/rules.go
@@ -2,9 +2,11 @@ package servicecatalog
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const pairArgs = 2
@@ -58,13 +60,13 @@ func isRuleFunc(fn string) bool {
// validateTemplateRules rejects a Rules document CloudFormation would not accept.
func validateTemplateRules(rules map[string]any) error {
- raw, err := json.Marshal(map[string]any{"Rules": rules})
+ raw, err := json.Marshal(map[string]any{"Rules": rules}, jsonx.V1())
if err != nil {
return invalid("Rules is not valid JSON")
}
var doc ruleDoc
- if err = json.Unmarshal(raw, &doc); err != nil {
+ if err = json.Unmarshal(raw, &doc, jsonx.V1()); err != nil {
return invalid("Rules is malformed: %v", err)
}
@@ -131,7 +133,7 @@ func validateRuleExpr(rule string, expr any) error {
func checkTemplateRules(env *ruleEnv, constraintParams []string) error {
for _, raw := range constraintParams {
var doc ruleDoc
- if err := json.Unmarshal([]byte(raw), &doc); err != nil {
+ if err := json.Unmarshal([]byte(raw), &doc, jsonx.V1()); err != nil {
continue
}
@@ -435,7 +437,7 @@ func toStr(v any) string {
return "false"
case float64:
- b, _ := json.Marshal(s)
+ b, _ := json.Marshal(s, jsonx.V1())
return string(b)
}
diff --git a/services/servicecatalog/serviceactions.go b/services/servicecatalog/serviceactions.go
index a5da1f4eb3..d3b4fa6923 100644
--- a/services/servicecatalog/serviceactions.go
+++ b/services/servicecatalog/serviceactions.go
@@ -3,8 +3,10 @@ package servicecatalog
import (
"cmp"
"context"
- "encoding/json"
+ "encoding/json/v2"
"slices"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -47,7 +49,7 @@ func parseActionParams(def map[string]string) ([]actionParam, error) {
}
var ps []actionParam
- if err := json.Unmarshal([]byte(raw), &ps); err != nil {
+ if err := json.Unmarshal([]byte(raw), &ps, jsonx.V1()); err != nil {
return nil, invalid("Definition Parameters is not a valid JSON list of {Name, Type}")
}
diff --git a/services/servicecatalog/util.go b/services/servicecatalog/util.go
index 36a18ca856..d0df67925a 100644
--- a/services/servicecatalog/util.go
+++ b/services/servicecatalog/util.go
@@ -2,7 +2,7 @@ package servicecatalog
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"maps"
"slices"
@@ -10,6 +10,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func sortedKeys[V any](m map[string]V) []string {
@@ -91,6 +92,6 @@ func idemReplay[T any](b *InMemoryBackend, opName, token string, req any, do fun
return res, err
}
-func jsonUnmarshal(s string, v any) error { return json.Unmarshal([]byte(s), v) }
+func jsonUnmarshal(s string, v any) error { return json.Unmarshal([]byte(s), v, jsonx.V1()) }
func awsmetaCaller(ctx context.Context) string { return awsmeta.CallerArn(ctx) }
diff --git a/services/servicediscovery/discovery_test.go b/services/servicediscovery/discovery_test.go
index 9a167497fb..1c1b617675 100644
--- a/services/servicediscovery/discovery_test.go
+++ b/services/servicediscovery/discovery_test.go
@@ -1,13 +1,14 @@
package servicediscovery_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/servicediscovery"
)
@@ -20,14 +21,14 @@ func TestHandler_DiscoverInstances(t *testing.T) {
require.Equal(t, http.StatusOK, nsRec.Code)
var nsResp map[string]any
- require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp))
+ require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp, jsonx.V1()))
opID := nsResp["OperationId"].(string)
opRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
require.Equal(t, http.StatusOK, opRec.Code)
var opResp map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp, jsonx.V1()))
operation := opResp["Operation"].(map[string]any)
targets := operation["Targets"].(map[string]any)
nsID := targets["NAMESPACE"].(string)
@@ -39,7 +40,7 @@ func TestHandler_DiscoverInstances(t *testing.T) {
require.Equal(t, http.StatusOK, svcRec.Code)
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcData := svcResp["Service"].(map[string]any)
svcID := svcData["Id"].(string)
@@ -174,11 +175,11 @@ func TestHandler_DiscoverInstancesHealthStatusFilter(t *testing.T) {
nsRec := doSDRequest(t, h, "CreateHttpNamespace", map[string]any{"Name": "ns-discover-health"})
var nsResp map[string]any
- require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp))
+ require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp, jsonx.V1()))
nsOpID := nsResp["OperationId"].(string)
nsOpRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": nsOpID})
var nsOpResp map[string]any
- require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp))
+ require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp, jsonx.V1()))
nsID := nsOpResp["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
svcRec := doSDRequest(t, h, "CreateService", map[string]any{
@@ -187,7 +188,7 @@ func TestHandler_DiscoverInstancesHealthStatusFilter(t *testing.T) {
"HealthCheckCustomConfig": map[string]any{"FailureThreshold": 1},
})
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcID := svcResp["Service"].(map[string]any)["Id"].(string)
doSDRequest(t, h, "RegisterInstance", map[string]any{
@@ -212,7 +213,7 @@ func TestHandler_DiscoverInstancesHealthStatusFilter(t *testing.T) {
require.Equal(t, 200, recHealthy.Code)
var respH map[string]any
- require.NoError(t, json.Unmarshal(recHealthy.Body.Bytes(), &respH))
+ require.NoError(t, json.Unmarshal(recHealthy.Body.Bytes(), &respH, jsonx.V1()))
assert.Len(t, respH["Instances"].([]any), 1)
assert.Equal(t, "healthy-i", respH["Instances"].([]any)[0].(map[string]any)["InstanceId"])
@@ -223,7 +224,7 @@ func TestHandler_DiscoverInstancesHealthStatusFilter(t *testing.T) {
"HealthStatus": "ALL",
})
var respAll map[string]any
- require.NoError(t, json.Unmarshal(recAll.Body.Bytes(), &respAll))
+ require.NoError(t, json.Unmarshal(recAll.Body.Bytes(), &respAll, jsonx.V1()))
assert.Len(t, respAll["Instances"].([]any), 2)
}
@@ -235,16 +236,16 @@ func TestHandler_DiscoverInstancesQueryParameters(t *testing.T) {
nsRec := doSDRequest(t, h, "CreateHttpNamespace", map[string]any{"Name": "ns-qp"})
var nsResp map[string]any
- require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp))
+ require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp, jsonx.V1()))
nsOpID := nsResp["OperationId"].(string)
nsOpRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": nsOpID})
var nsOpResp map[string]any
- require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp))
+ require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp, jsonx.V1()))
nsID := nsOpResp["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
svcRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "svc-qp", "NamespaceId": nsID})
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcID := svcResp["Service"].(map[string]any)["Id"].(string)
doSDRequest(t, h, "RegisterInstance", map[string]any{
@@ -264,7 +265,7 @@ func TestHandler_DiscoverInstancesQueryParameters(t *testing.T) {
require.Equal(t, 200, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
insts := resp["Instances"].([]any)
assert.Len(t, insts, 1)
assert.Equal(t, "i-web", insts[0].(map[string]any)["InstanceId"])
@@ -287,16 +288,16 @@ func TestHandler_DiscoverInstancesOptionalParameters(t *testing.T) {
nsRec := doSDRequest(t, h, "CreateHttpNamespace", map[string]any{"Name": "ns-optparams"})
var nsResp map[string]any
- require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp))
+ require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp, jsonx.V1()))
nsOpID := nsResp["OperationId"].(string)
nsOpRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": nsOpID})
var nsOpResp map[string]any
- require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp))
+ require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp, jsonx.V1()))
nsID := nsOpResp["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
svcRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "svc-optparams", "NamespaceId": nsID})
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcID := svcResp["Service"].(map[string]any)["Id"].(string)
doSDRequest(t, h, "RegisterInstance", map[string]any{
@@ -325,7 +326,7 @@ func TestHandler_DiscoverInstancesOptionalParameters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
insts := resp["Instances"].([]any)
require.Len(t, insts, 1, "only the instance matching both QueryParameters and OptionalParameters")
assert.Equal(t, "i-blue-web", insts[0].(map[string]any)["InstanceId"])
@@ -345,7 +346,7 @@ func TestHandler_DiscoverInstancesOptionalParameters(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
insts := resp["Instances"].([]any)
assert.Len(t, insts, 2, "no instance matches OptionalParameters, so it's ignored")
})
@@ -364,7 +365,7 @@ func TestHandler_DiscoverInstancesUnhealthyFilter(t *testing.T) {
"HealthCheckCustomConfig": map[string]any{"FailureThreshold": 1},
})
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcID := svcResp["Service"].(map[string]any)["Id"].(string)
doSDRequest(t, h, "RegisterInstance", map[string]any{
@@ -383,7 +384,7 @@ func TestHandler_DiscoverInstancesUnhealthyFilter(t *testing.T) {
require.Equal(t, 200, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["Instances"].([]any), 1)
}
@@ -405,7 +406,7 @@ func TestHandler_DiscoverInstancesHealthStatusIgnoredWithoutHealthCheck(t *testi
"NamespaceId": nsID,
})
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcID := svcResp["Service"].(map[string]any)["Id"].(string)
doSDRequest(t, h, "RegisterInstance", map[string]any{
@@ -420,7 +421,7 @@ func TestHandler_DiscoverInstancesHealthStatusIgnoredWithoutHealthCheck(t *testi
require.Equal(t, 200, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["Instances"].([]any), 1, "HealthStatus must be ignored when the service has no health check")
}
@@ -437,7 +438,7 @@ func TestHandler_DiscoverInstancesEmptyWhenNamespaceNotFound(t *testing.T) {
require.Equal(t, 200, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["Instances"].([]any))
}
@@ -471,11 +472,11 @@ func TestHandler_DiscoverInstancesRevision(t *testing.T) {
createNsRec := doSDRequest(t, h, "CreateHttpNamespace", map[string]any{"Name": "rev-ns"})
require.Equal(t, http.StatusOK, createNsRec.Code)
var nsOut map[string]any
- require.NoError(t, json.Unmarshal(createNsRec.Body.Bytes(), &nsOut))
+ require.NoError(t, json.Unmarshal(createNsRec.Body.Bytes(), &nsOut, jsonx.V1()))
opID := nsOut["OperationId"].(string)
opRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
var opOut map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opOut))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opOut, jsonx.V1()))
nsID := opOut["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
createSvcRec := doSDRequest(t, h, "CreateService", map[string]any{
@@ -510,13 +511,13 @@ func TestHandler_DiscoverInstancesRevision(t *testing.T) {
_ = nsOut
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
rev1 := out1["InstancesRevision"].(float64)
// Register an instance to bump revision
createSvcRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "bump-svc"})
var svcOut map[string]any
- require.NoError(t, json.Unmarshal(createSvcRec.Body.Bytes(), &svcOut))
+ require.NoError(t, json.Unmarshal(createSvcRec.Body.Bytes(), &svcOut, jsonx.V1()))
svcID := svcOut["Service"].(map[string]any)["Id"].(string)
doSDRequest(t, h, "RegisterInstance", map[string]any{
@@ -532,7 +533,7 @@ func TestHandler_DiscoverInstancesRevision(t *testing.T) {
})
assert.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
rev2 := out2["InstancesRevision"].(float64)
assert.Greater(t, rev2, rev1)
diff --git a/services/servicediscovery/dns_registrar_test.go b/services/servicediscovery/dns_registrar_test.go
index 0b36407694..23ddf5549d 100644
--- a/services/servicediscovery/dns_registrar_test.go
+++ b/services/servicediscovery/dns_registrar_test.go
@@ -1,7 +1,7 @@
package servicediscovery_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -12,6 +12,7 @@ import (
gopherDNS "github.com/blackbirdworks/gopherstack/pkgs/dns"
"github.com/blackbirdworks/gopherstack/pkgs/dns/dnstest"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/servicediscovery"
)
@@ -58,7 +59,7 @@ func createPublicDNSNamespace(t *testing.T, h *servicediscovery.Handler, name st
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
opID, ok := resp["OperationId"].(string)
require.True(t, ok)
@@ -66,7 +67,7 @@ func createPublicDNSNamespace(t *testing.T, h *servicediscovery.Handler, name st
require.Equal(t, http.StatusOK, opRec.Code, opRec.Body.String())
var opResp map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp, jsonx.V1()))
targets := opResp["Operation"].(map[string]any)["Targets"].(map[string]any)
@@ -100,7 +101,7 @@ func TestRegisterInstance_DNS_RealResolution(t *testing.T) {
require.Equal(t, http.StatusOK, svcRec.Code, svcRec.Body.String())
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcID := svcResp["Service"].(map[string]any)["Id"].(string)
hostname := "svc-dns-resolve.ns-dns-resolve"
@@ -146,19 +147,19 @@ func TestRegisterInstance_HTTPNamespace_NoDNSRegistration(t *testing.T) {
require.Equal(t, http.StatusOK, nsRec.Code, nsRec.Body.String())
var nsResp map[string]any
- require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp))
+ require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp, jsonx.V1()))
opID := nsResp["OperationId"].(string)
opRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
var opResp map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp, jsonx.V1()))
nsID := opResp["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
svcRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "svc-http", "NamespaceId": nsID})
require.Equal(t, http.StatusOK, svcRec.Code, svcRec.Body.String())
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcID := svcResp["Service"].(map[string]any)["Id"].(string)
regRec := doSDRequest(t, h, "RegisterInstance", map[string]any{
@@ -198,7 +199,7 @@ func TestDeregisterInstance_DNS_ResyncKeepsSibling(t *testing.T) {
require.Equal(t, http.StatusOK, svcRec.Code, svcRec.Body.String())
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcID := svcResp["Service"].(map[string]any)["Id"].(string)
hostname := "svc-multi.ns-multi."
diff --git a/services/servicediscovery/handler.go b/services/servicediscovery/handler.go
index 7ed2277ec7..3a31e66094 100644
--- a/services/servicediscovery/handler.go
+++ b/services/servicediscovery/handler.go
@@ -3,7 +3,7 @@ package servicediscovery
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -17,6 +17,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -90,7 +91,7 @@ func marshalPagedResponse(key string, items []map[string]any, nextToken string)
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// Handler is the HTTP handler for the AWS Cloud Map service discovery API.
@@ -197,7 +198,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var data map[string]any
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
@@ -434,10 +435,7 @@ var sentinelErrorCodes = sync.OnceValue(func() []struct {
// just like errInvalidRequest but isn't itself a sentinel in
// sentinelErrorCodes.
func isMalformedRequest(err error) bool {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
- return errors.Is(err, errInvalidRequest) || errors.As(err, &syntaxErr) || errors.As(err, &typeErr)
+ return errors.Is(err, errInvalidRequest) || jsonx.IsSyntaxError(err) || jsonx.IsTypeError(err)
}
func (h *Handler) handleError(c *echo.Context, err error) error {
@@ -468,7 +466,7 @@ func (h *Handler) errorResponse(c *echo.Context, errType string, err error) erro
payload, _ := json.Marshal(map[string]string{
keyTypeField: errType,
keyMessageField: msg,
- })
+ }, jsonx.V1())
return c.JSONBlob(http.StatusBadRequest, payload)
}
diff --git a/services/servicediscovery/handler_discovery.go b/services/servicediscovery/handler_discovery.go
index baa7b789f3..5a0338de5d 100644
--- a/services/servicediscovery/handler_discovery.go
+++ b/services/servicediscovery/handler_discovery.go
@@ -2,8 +2,10 @@ package servicediscovery
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type discoverInstancesRequest struct {
@@ -17,7 +19,7 @@ type discoverInstancesRequest struct {
func (h *Handler) handleDiscoverInstances(_ context.Context, body []byte) ([]byte, error) {
var req discoverInstancesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -68,7 +70,7 @@ func (h *Handler) handleDiscoverInstances(_ context.Context, body []byte) ([]byt
return json.Marshal(map[string]any{
"Instances": items,
"InstancesRevision": revision,
- })
+ }, jsonx.V1())
}
type discoverInstancesRevisionRequest struct {
@@ -78,7 +80,7 @@ type discoverInstancesRevisionRequest struct {
func (h *Handler) handleDiscoverInstancesRevision(_ context.Context, body []byte) ([]byte, error) {
var req discoverInstancesRevisionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -97,5 +99,5 @@ func (h *Handler) handleDiscoverInstancesRevision(_ context.Context, body []byte
return json.Marshal(map[string]any{
"InstancesRevision": revision,
- })
+ }, jsonx.V1())
}
diff --git a/services/servicediscovery/handler_instances.go b/services/servicediscovery/handler_instances.go
index 758891660e..5d57a7268f 100644
--- a/services/servicediscovery/handler_instances.go
+++ b/services/servicediscovery/handler_instances.go
@@ -2,10 +2,11 @@ package servicediscovery
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/collections"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type registerInstanceRequest struct {
@@ -17,7 +18,7 @@ type registerInstanceRequest struct {
func (h *Handler) handleRegisterInstance(_ context.Context, body []byte) ([]byte, error) {
var req registerInstanceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -46,7 +47,7 @@ func (h *Handler) handleRegisterInstance(_ context.Context, body []byte) ([]byte
return nil, err
}
- return json.Marshal(map[string]string{keyOperationID: opID})
+ return json.Marshal(map[string]string{keyOperationID: opID}, jsonx.V1())
}
type deregisterInstanceRequest struct {
@@ -56,7 +57,7 @@ type deregisterInstanceRequest struct {
func (h *Handler) handleDeregisterInstance(_ context.Context, body []byte) ([]byte, error) {
var req deregisterInstanceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -73,7 +74,7 @@ func (h *Handler) handleDeregisterInstance(_ context.Context, body []byte) ([]by
return nil, err
}
- return json.Marshal(map[string]string{keyOperationID: opID})
+ return json.Marshal(map[string]string{keyOperationID: opID}, jsonx.V1())
}
type getInstanceRequest struct {
@@ -83,7 +84,7 @@ type getInstanceRequest struct {
func (h *Handler) handleGetInstance(_ context.Context, body []byte) ([]byte, error) {
var req getInstanceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -105,7 +106,7 @@ func (h *Handler) handleGetInstance(_ context.Context, body []byte) ([]byte, err
"Id": inst.ID,
keyAttributes: inst.Attributes,
},
- })
+ }, jsonx.V1())
}
type listInstancesRequest struct {
@@ -116,7 +117,7 @@ type listInstancesRequest struct {
func (h *Handler) handleListInstances(_ context.Context, body []byte) ([]byte, error) {
var req listInstancesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -156,7 +157,7 @@ func (h *Handler) handleListInstances(_ context.Context, body []byte) ([]byte, e
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type getInstancesHealthStatusRequest struct {
@@ -170,7 +171,7 @@ type getInstancesHealthStatusRequest struct {
// Instances without a recorded custom status default to HEALTHY.
func (h *Handler) handleGetInstancesHealthStatus(_ context.Context, body []byte) ([]byte, error) {
var req getInstancesHealthStatusRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -206,7 +207,7 @@ func (h *Handler) handleGetInstancesHealthStatus(_ context.Context, body []byte)
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type updateInstanceCustomHealthStatusRequest struct {
@@ -217,7 +218,7 @@ type updateInstanceCustomHealthStatusRequest struct {
func (h *Handler) handleUpdateInstanceCustomHealthStatus(_ context.Context, body []byte) error {
var req updateInstanceCustomHealthStatusRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/servicediscovery/handler_list_summary_test.go b/services/servicediscovery/handler_list_summary_test.go
index e0f5b05fff..ebe02a3689 100644
--- a/services/servicediscovery/handler_list_summary_test.go
+++ b/services/servicediscovery/handler_list_summary_test.go
@@ -1,11 +1,13 @@
package servicediscovery_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_ListServices_SummaryShape locks that ListServices emits the
@@ -27,7 +29,7 @@ func TestHandler_ListServices_SummaryShape(t *testing.T) {
require.Equal(t, 200, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["Services"].([]any)
require.True(t, ok)
require.Len(t, items, 1)
diff --git a/services/servicediscovery/handler_namespaces.go b/services/servicediscovery/handler_namespaces.go
index 717c37c05c..e4a54f1736 100644
--- a/services/servicediscovery/handler_namespaces.go
+++ b/services/servicediscovery/handler_namespaces.go
@@ -2,10 +2,11 @@ package servicediscovery
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type dnsPropertiesRequest struct {
@@ -23,7 +24,7 @@ type createHTTPNamespaceRequest struct {
func (h *Handler) handleCreateHTTPNamespace(_ context.Context, body []byte) ([]byte, error) {
var req createHTTPNamespaceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -44,7 +45,7 @@ func (h *Handler) handleCreateHTTPNamespace(_ context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(map[string]string{keyOperationID: opID})
+ return json.Marshal(map[string]string{keyOperationID: opID}, jsonx.V1())
}
type createPrivateDNSNamespaceRequest struct {
@@ -60,7 +61,7 @@ type createPrivateDNSNamespaceRequest struct {
func (h *Handler) handleCreatePrivateDNSNamespace(_ context.Context, body []byte) ([]byte, error) {
var req createPrivateDNSNamespaceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -86,7 +87,7 @@ func (h *Handler) handleCreatePrivateDNSNamespace(_ context.Context, body []byte
return nil, err
}
- return json.Marshal(map[string]string{keyOperationID: opID})
+ return json.Marshal(map[string]string{keyOperationID: opID}, jsonx.V1())
}
type createPublicDNSNamespaceRequest struct {
@@ -101,7 +102,7 @@ type createPublicDNSNamespaceRequest struct {
func (h *Handler) handleCreatePublicDNSNamespace(_ context.Context, body []byte) ([]byte, error) {
var req createPublicDNSNamespaceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -127,7 +128,7 @@ func (h *Handler) handleCreatePublicDNSNamespace(_ context.Context, body []byte)
return nil, err
}
- return json.Marshal(map[string]string{keyOperationID: opID})
+ return json.Marshal(map[string]string{keyOperationID: opID}, jsonx.V1())
}
type deleteNamespaceRequest struct {
@@ -136,7 +137,7 @@ type deleteNamespaceRequest struct {
func (h *Handler) handleDeleteNamespace(_ context.Context, body []byte) ([]byte, error) {
var req deleteNamespaceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -149,7 +150,7 @@ func (h *Handler) handleDeleteNamespace(_ context.Context, body []byte) ([]byte,
return nil, err
}
- return json.Marshal(map[string]string{keyOperationID: opID})
+ return json.Marshal(map[string]string{keyOperationID: opID}, jsonx.V1())
}
type getNamespaceRequest struct {
@@ -158,7 +159,7 @@ type getNamespaceRequest struct {
func (h *Handler) handleGetNamespace(_ context.Context, body []byte) ([]byte, error) {
var req getNamespaceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -173,7 +174,7 @@ func (h *Handler) handleGetNamespace(_ context.Context, body []byte) ([]byte, er
return json.Marshal(map[string]any{
"Namespace": namespaceToMap(ns, h.Backend.AccountID()),
- })
+ }, jsonx.V1())
}
type namespaceFilter struct {
@@ -218,7 +219,7 @@ func buildNamespacesFilter(filters []namespaceFilter) ListNamespacesFilter {
func (h *Handler) handleListNamespaces(_ context.Context, body []byte) ([]byte, error) {
var req listNamespacesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -306,7 +307,7 @@ type updateHTTPNamespaceRequest struct {
func (h *Handler) handleUpdateHTTPNamespace(_ context.Context, body []byte) ([]byte, error) {
var req updateHTTPNamespaceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -319,7 +320,7 @@ func (h *Handler) handleUpdateHTTPNamespace(_ context.Context, body []byte) ([]b
return nil, err
}
- return json.Marshal(map[string]string{keyOperationID: opID})
+ return json.Marshal(map[string]string{keyOperationID: opID}, jsonx.V1())
}
// dnsNamespacePropertiesChangeRequest mirrors types.PrivateDnsNamespacePropertiesChange
@@ -355,7 +356,7 @@ type updatePrivateDNSNamespaceRequest struct {
func (h *Handler) handleUpdatePrivateDNSNamespace(_ context.Context, body []byte) ([]byte, error) {
var req updatePrivateDNSNamespaceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -370,7 +371,7 @@ func (h *Handler) handleUpdatePrivateDNSNamespace(_ context.Context, body []byte
return nil, err
}
- return json.Marshal(map[string]string{keyOperationID: opID})
+ return json.Marshal(map[string]string{keyOperationID: opID}, jsonx.V1())
}
type updatePublicDNSNamespaceRequest struct {
@@ -381,7 +382,7 @@ type updatePublicDNSNamespaceRequest struct {
func (h *Handler) handleUpdatePublicDNSNamespace(_ context.Context, body []byte) ([]byte, error) {
var req updatePublicDNSNamespaceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -396,7 +397,7 @@ func (h *Handler) handleUpdatePublicDNSNamespace(_ context.Context, body []byte)
return nil, err
}
- return json.Marshal(map[string]string{keyOperationID: opID})
+ return json.Marshal(map[string]string{keyOperationID: opID}, jsonx.V1())
}
func applyPaginationNamespaces(items []Namespace, nextToken string, maxResults int) ([]Namespace, string) {
diff --git a/services/servicediscovery/handler_operations.go b/services/servicediscovery/handler_operations.go
index 444bffb216..2aeff5699d 100644
--- a/services/servicediscovery/handler_operations.go
+++ b/services/servicediscovery/handler_operations.go
@@ -2,12 +2,13 @@ package servicediscovery
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strconv"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type getOperationRequest struct {
@@ -16,7 +17,7 @@ type getOperationRequest struct {
func (h *Handler) handleGetOperation(_ context.Context, body []byte) ([]byte, error) {
var req getOperationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -31,7 +32,7 @@ func (h *Handler) handleGetOperation(_ context.Context, body []byte) ([]byte, er
return json.Marshal(map[string]any{
"Operation": operationToMap(op),
- })
+ }, jsonx.V1())
}
type operationFilter struct {
@@ -106,7 +107,7 @@ func buildOperationsFilter(filters []operationFilter) ListOperationsFilter {
func (h *Handler) handleListOperations(_ context.Context, body []byte) ([]byte, error) {
var req listOperationsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/servicediscovery/handler_services.go b/services/servicediscovery/handler_services.go
index 7c9ccaa5e1..a1489a772d 100644
--- a/services/servicediscovery/handler_services.go
+++ b/services/servicediscovery/handler_services.go
@@ -2,10 +2,11 @@ package servicediscovery
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type dnsRecordRequest struct {
@@ -124,7 +125,7 @@ func validateHealthCheckConfigEnum(hc *HealthCheckConfig) error {
func (h *Handler) handleCreateService(_ context.Context, body []byte) ([]byte, error) {
var req createServiceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -173,7 +174,7 @@ func (h *Handler) handleCreateService(_ context.Context, body []byte) ([]byte, e
return json.Marshal(map[string]any{
keyService: serviceToMap(svc, h.Backend.AccountID()),
- })
+ }, jsonx.V1())
}
type deleteServiceRequest struct {
@@ -187,7 +188,7 @@ type deleteServiceRequest struct {
// enforces this (returns ErrResourceInUse).
func (h *Handler) handleDeleteService(_ context.Context, body []byte) error {
var req deleteServiceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -204,7 +205,7 @@ type getServiceRequest struct {
func (h *Handler) handleGetService(_ context.Context, body []byte) ([]byte, error) {
var req getServiceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -219,7 +220,7 @@ func (h *Handler) handleGetService(_ context.Context, body []byte) ([]byte, erro
return json.Marshal(map[string]any{
keyService: serviceToMap(svc, h.Backend.AccountID()),
- })
+ }, jsonx.V1())
}
type serviceFilter struct {
@@ -260,7 +261,7 @@ func buildServicesFilter(filters []serviceFilter) ListServicesFilter {
func (h *Handler) handleListServices(_ context.Context, body []byte) ([]byte, error) {
var req listServicesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -288,7 +289,7 @@ func (h *Handler) handleListServices(_ context.Context, body []byte) ([]byte, er
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// serviceToMap converts a Service to a JSON-serialisable map including DNS and
@@ -373,7 +374,7 @@ type updateServiceRequest struct {
func (h *Handler) handleUpdateService(_ context.Context, body []byte) ([]byte, error) {
var req updateServiceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -401,7 +402,7 @@ func (h *Handler) handleUpdateService(_ context.Context, body []byte) ([]byte, e
return nil, err
}
- return json.Marshal(map[string]string{keyOperationID: opID})
+ return json.Marshal(map[string]string{keyOperationID: opID}, jsonx.V1())
}
type getServiceAttributesRequest struct {
@@ -410,7 +411,7 @@ type getServiceAttributesRequest struct {
func (h *Handler) handleGetServiceAttributes(_ context.Context, body []byte) ([]byte, error) {
var req getServiceAttributesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -431,7 +432,7 @@ func (h *Handler) handleGetServiceAttributes(_ context.Context, body []byte) ([]
"ServiceArn": arn,
keyAttributes: attrs,
},
- })
+ }, jsonx.V1())
}
type updateServiceAttributesRequest struct {
@@ -441,7 +442,7 @@ type updateServiceAttributesRequest struct {
func (h *Handler) handleUpdateServiceAttributes(_ context.Context, body []byte) error {
var req updateServiceAttributesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -463,7 +464,7 @@ type deleteServiceAttributesRequest struct {
func (h *Handler) handleDeleteServiceAttributes(_ context.Context, body []byte) error {
var req deleteServiceAttributesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/servicediscovery/handler_tags.go b/services/servicediscovery/handler_tags.go
index 9628901781..a0e45695ff 100644
--- a/services/servicediscovery/handler_tags.go
+++ b/services/servicediscovery/handler_tags.go
@@ -2,10 +2,11 @@ package servicediscovery
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/collections"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type listTagsForResourceRequest struct {
@@ -14,7 +15,7 @@ type listTagsForResourceRequest struct {
func (h *Handler) handleListTagsForResource(_ context.Context, body []byte) ([]byte, error) {
var req listTagsForResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -31,7 +32,7 @@ func (h *Handler) handleListTagsForResource(_ context.Context, body []byte) ([]b
return json.Marshal(map[string]any{
keyTags: tagList,
- })
+ }, jsonx.V1())
}
type tagResourceRequest struct {
@@ -41,7 +42,7 @@ type tagResourceRequest struct {
func (h *Handler) handleTagResource(_ context.Context, body []byte) ([]byte, error) {
var req tagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -67,7 +68,7 @@ type untagResourceRequest struct {
func (h *Handler) handleUntagResource(_ context.Context, body []byte) ([]byte, error) {
var req untagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/servicediscovery/handler_test.go b/services/servicediscovery/handler_test.go
index 19dc5708d9..ab894e6d16 100644
--- a/services/servicediscovery/handler_test.go
+++ b/services/servicediscovery/handler_test.go
@@ -2,7 +2,7 @@ package servicediscovery_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/servicediscovery"
)
@@ -29,7 +30,7 @@ func doSDRequest(t *testing.T, h *servicediscovery.Handler, op string, body any)
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -215,7 +216,7 @@ func TestHandler_ExtractResource(t *testing.T) {
h := newTestHandler(t)
e := echo.New()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
@@ -288,7 +289,7 @@ func TestServiceDiscovery_Handler_Reset(t *testing.T) {
var out struct {
Namespaces []any `json:"Namespaces"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Namespaces, tt.wantAfter)
})
}
@@ -358,13 +359,13 @@ func createNamespaceHelper(t *testing.T, h *servicediscovery.Handler, name strin
require.Equal(t, 200, nsRec.Code)
var nsResp map[string]any
- require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp))
+ require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp, jsonx.V1()))
opID := nsResp["OperationId"].(string)
nsOpRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
var nsOpResp map[string]any
- require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp))
+ require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp, jsonx.V1()))
return nsOpResp["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
}
diff --git a/services/servicediscovery/input_realism_test.go b/services/servicediscovery/input_realism_test.go
index dfc81aafa9..841f1b1793 100644
--- a/services/servicediscovery/input_realism_test.go
+++ b/services/servicediscovery/input_realism_test.go
@@ -1,13 +1,15 @@
package servicediscovery_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_NameAndAddressValidation(t *testing.T) {
@@ -110,7 +112,7 @@ func extractFirstID(t *testing.T, body string) string {
ID string `json:"Id"`
} `json:"Namespaces"`
}
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
require.NotEmpty(t, out.Namespaces)
return out.Namespaces[0].ID
@@ -124,7 +126,7 @@ func extractServiceID(t *testing.T, body string) string {
ID string `json:"Id"`
} `json:"Service"`
}
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
return out.Service.ID
}
diff --git a/services/servicediscovery/instances_test.go b/services/servicediscovery/instances_test.go
index 6dbfb840a7..da227f6df5 100644
--- a/services/servicediscovery/instances_test.go
+++ b/services/servicediscovery/instances_test.go
@@ -1,7 +1,7 @@
package servicediscovery_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/servicediscovery"
)
@@ -64,7 +65,7 @@ func TestHandler_RegisterInstance(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
svcData := createResp["Service"].(map[string]any)
svcID := svcData["Id"].(string)
@@ -160,7 +161,7 @@ func TestHandler_RegisterInstance_AttributeQuota(t *testing.T) {
createRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "attr-quota-svc"})
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
svcID := createResp["Service"].(map[string]any)["Id"].(string)
rec := doSDRequest(t, h, "RegisterInstance", map[string]any{
@@ -228,7 +229,7 @@ func TestHandler_RegisterInstance_InitHealthStatus(t *testing.T) {
createRec := doSDRequest(t, h, "CreateService", body)
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
svcID := createResp["Service"].(map[string]any)["Id"].(string)
regRec := doSDRequest(t, h, "RegisterInstance", map[string]any{
@@ -239,7 +240,7 @@ func TestHandler_RegisterInstance_InitHealthStatus(t *testing.T) {
statusRec := doSDRequest(t, h, "GetInstancesHealthStatus", map[string]any{"ServiceId": svcID})
require.Equal(t, http.StatusOK, statusRec.Code)
var statusResp map[string]any
- require.NoError(t, json.Unmarshal(statusRec.Body.Bytes(), &statusResp))
+ require.NoError(t, json.Unmarshal(statusRec.Body.Bytes(), &statusResp, jsonx.V1()))
statuses := statusResp["Status"].(map[string]any)
assert.Equal(t, tt.wantStatus, statuses["i-init-health"])
})
@@ -255,7 +256,7 @@ func TestHandler_GetInstance(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
svcData := createResp["Service"].(map[string]any)
svcID := svcData["Id"].(string)
@@ -317,7 +318,7 @@ func TestHandler_ListInstances(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
svcData := createResp["Service"].(map[string]any)
svcID := svcData["Id"].(string)
@@ -371,7 +372,7 @@ func TestHandler_DeregisterInstance(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
svcData := createResp["Service"].(map[string]any)
svcID := svcData["Id"].(string)
@@ -445,7 +446,7 @@ func TestHandler_GetInstancesHealthStatus(t *testing.T) {
require.Equal(t, http.StatusOK, svcRec.Code)
var svcOut map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcOut))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcOut, jsonx.V1()))
svc := svcOut["Service"].(map[string]any)
return svc["Id"].(string)
@@ -481,7 +482,7 @@ func TestHandler_GetInstancesHealthStatus(t *testing.T) {
if len(tt.wantFields) > 0 {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
for _, field := range tt.wantFields {
assert.Contains(t, out, field)
}
@@ -550,16 +551,16 @@ func TestHandler_RegisterInstanceRealOperationID(t *testing.T) {
// Create namespace and service first.
nsRec := doSDRequest(t, h, "CreateHttpNamespace", map[string]any{"Name": "ns-reg"})
var nsResp map[string]any
- require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp))
+ require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp, jsonx.V1()))
nsOpID := nsResp["OperationId"].(string)
nsOpRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": nsOpID})
var nsOpResp map[string]any
- require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp))
+ require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp, jsonx.V1()))
nsID := nsOpResp["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
svcRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "svc-reg", "NamespaceId": nsID})
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcID := svcResp["Service"].(map[string]any)["Id"].(string)
// Register instance.
@@ -571,7 +572,7 @@ func TestHandler_RegisterInstanceRealOperationID(t *testing.T) {
require.Equal(t, 200, regRec.Code)
var regResp map[string]any
- require.NoError(t, json.Unmarshal(regRec.Body.Bytes(), ®Resp))
+ require.NoError(t, json.Unmarshal(regRec.Body.Bytes(), ®Resp, jsonx.V1()))
opID := regResp["OperationId"].(string)
assert.NotEqual(t, "op-register", opID, "must not be hardcoded op ID")
@@ -582,7 +583,7 @@ func TestHandler_RegisterInstanceRealOperationID(t *testing.T) {
require.Equal(t, 200, opRec.Code)
var opResp map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp, jsonx.V1()))
assert.Equal(t, "REGISTER_INSTANCE", opResp["Operation"].(map[string]any)["Type"])
}
@@ -596,16 +597,16 @@ func TestHandler_DeregisterInstanceRealOperationID(t *testing.T) {
// Setup.
nsRec := doSDRequest(t, h, "CreateHttpNamespace", map[string]any{"Name": "ns-dereg"})
var nsResp map[string]any
- require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp))
+ require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp, jsonx.V1()))
nsOpID := nsResp["OperationId"].(string)
nsOpRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": nsOpID})
var nsOpResp map[string]any
- require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp))
+ require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp, jsonx.V1()))
nsID := nsOpResp["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
svcRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "svc-dereg", "NamespaceId": nsID})
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcID := svcResp["Service"].(map[string]any)["Id"].(string)
doSDRequest(t, h, "RegisterInstance", map[string]any{
@@ -622,7 +623,7 @@ func TestHandler_DeregisterInstanceRealOperationID(t *testing.T) {
require.Equal(t, 200, deregRec.Code)
var deregResp map[string]any
- require.NoError(t, json.Unmarshal(deregRec.Body.Bytes(), &deregResp))
+ require.NoError(t, json.Unmarshal(deregRec.Body.Bytes(), &deregResp, jsonx.V1()))
opID := deregResp["OperationId"].(string)
assert.NotEqual(t, "op-deregister", opID)
@@ -632,7 +633,7 @@ func TestHandler_DeregisterInstanceRealOperationID(t *testing.T) {
require.Equal(t, 200, opRec.Code)
var opResp map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp, jsonx.V1()))
assert.Equal(t, "DEREGISTER_INSTANCE", opResp["Operation"].(map[string]any)["Type"])
}
@@ -646,11 +647,11 @@ func TestHandler_GetInstancesHealthStatusStoredValue(t *testing.T) {
// Setup service and instances.
nsRec := doSDRequest(t, h, "CreateHttpNamespace", map[string]any{"Name": "ns-health"})
var nsResp map[string]any
- require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp))
+ require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp, jsonx.V1()))
nsOpID := nsResp["OperationId"].(string)
nsOpRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": nsOpID})
var nsOpResp map[string]any
- require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp))
+ require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp, jsonx.V1()))
nsID := nsOpResp["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
svcRec := doSDRequest(t, h, "CreateService", map[string]any{
@@ -659,7 +660,7 @@ func TestHandler_GetInstancesHealthStatusStoredValue(t *testing.T) {
"HealthCheckCustomConfig": map[string]any{"FailureThreshold": 1},
})
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcID := svcResp["Service"].(map[string]any)["Id"].(string)
doSDRequest(t, h, "RegisterInstance", map[string]any{
@@ -683,7 +684,7 @@ func TestHandler_GetInstancesHealthStatusStoredValue(t *testing.T) {
require.Equal(t, 200, statusRec.Code)
var statusResp map[string]any
- require.NoError(t, json.Unmarshal(statusRec.Body.Bytes(), &statusResp))
+ require.NoError(t, json.Unmarshal(statusRec.Body.Bytes(), &statusResp, jsonx.V1()))
statuses := statusResp["Status"].(map[string]any)
assert.Equal(t, "HEALTHY", statuses["inst-healthy"])
@@ -698,16 +699,16 @@ func TestHandler_GetInstancesHealthStatusFilterByIDs(t *testing.T) {
nsRec := doSDRequest(t, h, "CreateHttpNamespace", map[string]any{"Name": "ns-filter-health"})
var nsResp map[string]any
- require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp))
+ require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp, jsonx.V1()))
nsOpID := nsResp["OperationId"].(string)
nsOpRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": nsOpID})
var nsOpResp map[string]any
- require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp))
+ require.NoError(t, json.Unmarshal(nsOpRec.Body.Bytes(), &nsOpResp, jsonx.V1()))
nsID := nsOpResp["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
svcRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "svc-filter", "NamespaceId": nsID})
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcID := svcResp["Service"].(map[string]any)["Id"].(string)
for _, iid := range []string{"a", "b", "c"} {
@@ -724,7 +725,7 @@ func TestHandler_GetInstancesHealthStatusFilterByIDs(t *testing.T) {
require.Equal(t, 200, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
statuses := resp["Status"].(map[string]any)
assert.Len(t, statuses, 2)
@@ -777,7 +778,7 @@ func TestHandler_GetInstancesHealthStatusUnknownInstanceID(t *testing.T) {
svcRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "svc-unknown-inst"})
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcID := svcResp["Service"].(map[string]any)["Id"].(string)
registerRec := doSDRequest(t, h, "RegisterInstance", map[string]any{
@@ -793,7 +794,7 @@ func TestHandler_GetInstancesHealthStatusUnknownInstanceID(t *testing.T) {
if tt.wantCode == http.StatusBadRequest {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InstanceNotFound", errResp["__type"])
}
})
@@ -809,7 +810,7 @@ func TestListInstances_Pagination(t *testing.T) {
createSvcRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "svc-inst-page"})
require.Equal(t, http.StatusOK, createSvcRec.Code)
var svcOut map[string]any
- require.NoError(t, json.Unmarshal(createSvcRec.Body.Bytes(), &svcOut))
+ require.NoError(t, json.Unmarshal(createSvcRec.Body.Bytes(), &svcOut, jsonx.V1()))
svcID := svcOut["Service"].(map[string]any)["Id"].(string)
for i := range 4 {
@@ -849,7 +850,7 @@ func TestListInstances_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
insts := out["Instances"].([]any)
assert.Len(t, insts, tt.wantLen)
if tt.wantNextToken {
@@ -896,7 +897,7 @@ func TestHandler_UpdateInstanceCustomHealthStatus(t *testing.T) {
})
require.Equal(t, http.StatusOK, createRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out, jsonx.V1()))
svcID := out["Service"].(map[string]any)["Id"].(string)
regRec := doSDRequest(t, h, "RegisterInstance", map[string]any{
"ServiceId": svcID,
diff --git a/services/servicediscovery/multi_region_test.go b/services/servicediscovery/multi_region_test.go
index 276dc0bae1..59441204fc 100644
--- a/services/servicediscovery/multi_region_test.go
+++ b/services/servicediscovery/multi_region_test.go
@@ -1,7 +1,7 @@
package servicediscovery_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/servicediscovery"
)
@@ -39,7 +40,7 @@ func regionCall(t *testing.T, h *servicediscovery.Handler, region, op, body stri
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
out := map[string]any{}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/servicediscovery/namespaces_test.go b/services/servicediscovery/namespaces_test.go
index 4aa28383d7..3b859cda19 100644
--- a/services/servicediscovery/namespaces_test.go
+++ b/services/servicediscovery/namespaces_test.go
@@ -1,7 +1,7 @@
package servicediscovery_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/servicediscovery"
)
@@ -207,7 +208,7 @@ func TestHandler_GetNamespace(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
opID := createResp["OperationId"].(string)
@@ -215,7 +216,7 @@ func TestHandler_GetNamespace(t *testing.T) {
require.Equal(t, http.StatusOK, opRec.Code)
var opResp map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp, jsonx.V1()))
operation := opResp["Operation"].(map[string]any)
targets := operation["Targets"].(map[string]any)
@@ -277,14 +278,14 @@ func TestHandler_DeleteNamespace(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
opID := createResp["OperationId"].(string)
opRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
require.Equal(t, http.StatusOK, opRec.Code)
var opResp map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp, jsonx.V1()))
operation := opResp["Operation"].(map[string]any)
targets := operation["Targets"].(map[string]any)
nsID := targets["NAMESPACE"].(string)
@@ -336,13 +337,13 @@ func TestHandler_NamespaceTagsViaListTagsForResource(t *testing.T) {
require.Equal(t, 200, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
opID := createResp["OperationId"].(string)
opRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
var opResp map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp, jsonx.V1()))
nsID := opResp["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
// GetNamespace has CreateDate but never a Tags field.
@@ -350,7 +351,7 @@ func TestHandler_NamespaceTagsViaListTagsForResource(t *testing.T) {
require.Equal(t, 200, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
ns := getResp["Namespace"].(map[string]any)
assert.NotZero(t, ns["CreateDate"], "CreateDate must be present in GetNamespace response")
@@ -361,7 +362,7 @@ func TestHandler_NamespaceTagsViaListTagsForResource(t *testing.T) {
require.Equal(t, 200, tagsRec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags := tagsResp["Tags"].([]any)
assert.Len(t, tags, 2, "expected 2 tags")
@@ -386,7 +387,7 @@ func TestHandler_ListNamespacesOmitsTags(t *testing.T) {
require.Equal(t, 200, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
nsList := resp["Namespaces"].([]any)
require.Len(t, nsList, 1)
@@ -431,13 +432,13 @@ func TestNamespace_ServiceCount(t *testing.T) {
createNsRec := doSDRequest(t, h, "CreateHttpNamespace", map[string]any{"Name": "ns-parity"})
require.Equal(t, http.StatusOK, createNsRec.Code)
var nsOp map[string]string
- require.NoError(t, json.Unmarshal(createNsRec.Body.Bytes(), &nsOp))
+ require.NoError(t, json.Unmarshal(createNsRec.Body.Bytes(), &nsOp, jsonx.V1()))
opID := nsOp["OperationId"]
opRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
require.Equal(t, http.StatusOK, opRec.Code)
var opOut map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opOut))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opOut, jsonx.V1()))
nsID := opOut["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
for i := range tt.servicesBefore {
@@ -452,7 +453,7 @@ func TestNamespace_ServiceCount(t *testing.T) {
getRec := doSDRequest(t, h, "GetNamespace", map[string]any{"Id": nsID})
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
ns := getOut["Namespace"].(map[string]any)
assert.Equal(t, tt.wantCount, int(ns["ServiceCount"].(float64)), "GetNamespace ServiceCount")
@@ -460,7 +461,7 @@ func TestNamespace_ServiceCount(t *testing.T) {
listRec := doSDRequest(t, h, "ListNamespaces", map[string]any{})
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
nsList := listOut["Namespaces"].([]any)
require.Len(t, nsList, 1)
gotListCount := int(nsList[0].(map[string]any)["ServiceCount"].(float64))
@@ -512,7 +513,7 @@ func TestListNamespaces_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
nsList := out["Namespaces"].([]any)
assert.Len(t, nsList, tt.wantLen)
if tt.wantNextToken {
@@ -559,7 +560,7 @@ func TestListNamespaces_TypeFilter(t *testing.T) {
rec := doSDRequest(t, h, "ListNamespaces", req)
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
nsList := out["Namespaces"].([]any)
assert.Len(t, nsList, tt.wantLen, "filterType=%s", tt.filterType)
})
@@ -600,7 +601,7 @@ func TestListNamespaces_HTTPNameFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out["Namespaces"].([]any), tt.wantLen)
})
}
@@ -638,7 +639,7 @@ func TestListNamespaces_ResourceOwnerFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out["Namespaces"].([]any), tt.wantLen)
})
}
@@ -663,7 +664,7 @@ func TestListNamespaces_NameBeginsWithFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out["Namespaces"].([]any), 2)
}
@@ -694,11 +695,11 @@ func TestHandler_UpdateHttpNamespace(t *testing.T) {
createRec := doSDRequest(t, h, "CreateHttpNamespace", map[string]any{"Name": "http-ns"})
require.Equal(t, http.StatusOK, createRec.Code)
var nsOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &nsOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &nsOut, jsonx.V1()))
opID := nsOut["OperationId"].(string)
opRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
var opOut map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opOut))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opOut, jsonx.V1()))
nsID := opOut["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
rec := doSDRequest(t, h, "UpdateHttpNamespace", map[string]any{
@@ -726,11 +727,11 @@ func TestHandler_UpdateHttpNamespace(t *testing.T) {
createRec := doSDRequest(t, h, "CreatePrivateDnsNamespace", map[string]any{"Name": "private-ns"})
require.Equal(t, http.StatusOK, createRec.Code)
var nsOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &nsOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &nsOut, jsonx.V1()))
opID := nsOut["OperationId"].(string)
opRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
var opOut map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opOut))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opOut, jsonx.V1()))
privateNsID := opOut["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
rec := doSDRequest(t, h, "UpdateHttpNamespace", map[string]any{
@@ -771,11 +772,11 @@ func TestHandler_UpdatePrivateDnsNamespace(t *testing.T) {
case "success":
createRec := doSDRequest(t, h, "CreatePrivateDnsNamespace", map[string]any{"Name": "private-ns"})
var out map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out, jsonx.V1()))
opID := out["OperationId"].(string)
opRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
var opOut map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opOut))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opOut, jsonx.V1()))
nsID := opOut["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
rec := doSDRequest(t, h, "UpdatePrivateDnsNamespace", map[string]any{
"Id": nsID,
@@ -825,11 +826,11 @@ func TestHandler_UpdatePublicDnsNamespace(t *testing.T) {
case "success":
createRec := doSDRequest(t, h, "CreatePublicDnsNamespace", map[string]any{"Name": "public-ns"})
var out map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out, jsonx.V1()))
opID := out["OperationId"].(string)
opRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
var opOut map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opOut))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opOut, jsonx.V1()))
nsID := opOut["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
rec := doSDRequest(t, h, "UpdatePublicDnsNamespace", map[string]any{
"Id": nsID,
diff --git a/services/servicediscovery/operations_test.go b/services/servicediscovery/operations_test.go
index a5c8082937..84a3755a92 100644
--- a/services/servicediscovery/operations_test.go
+++ b/services/servicediscovery/operations_test.go
@@ -1,7 +1,7 @@
package servicediscovery_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/servicediscovery"
)
@@ -60,7 +61,7 @@ func TestHandler_GetOperation(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
opID := createResp["OperationId"].(string)
rec = doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
@@ -100,7 +101,7 @@ func TestOperationCountIncrementsOnRegisterDeregister(t *testing.T) {
nsID := createNamespaceHelper(t, h, "ns-opcount")
svcRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "svc-opcount", "NamespaceId": nsID})
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcID := svcResp["Service"].(map[string]any)["Id"].(string)
opsBefore := servicediscovery.OperationCount(b)
@@ -158,7 +159,7 @@ func TestListOperations_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
ops := out["Operations"].([]any)
assert.Len(t, ops, tt.wantLen)
if tt.wantNextToken {
@@ -187,7 +188,7 @@ func TestListOperations_SummaryShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
ops := out["Operations"].([]any)
require.Len(t, ops, 1)
@@ -219,7 +220,7 @@ func TestListOperations_NamespaceAndServiceIDFilters(t *testing.T) {
svcRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "svc-op-filter", "NamespaceId": nsID})
var svcResp map[string]any
- require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp))
+ require.NoError(t, json.Unmarshal(svcRec.Body.Bytes(), &svcResp, jsonx.V1()))
svcID := svcResp["Service"].(map[string]any)["Id"].(string)
doSDRequest(t, h, "RegisterInstance", map[string]any{
@@ -232,7 +233,7 @@ func TestListOperations_NamespaceAndServiceIDFilters(t *testing.T) {
require.Equal(t, http.StatusOK, nsFilterRec.Code)
var nsOut map[string]any
- require.NoError(t, json.Unmarshal(nsFilterRec.Body.Bytes(), &nsOut))
+ require.NoError(t, json.Unmarshal(nsFilterRec.Body.Bytes(), &nsOut, jsonx.V1()))
assert.Len(t, nsOut["Operations"].([]any), 1, "only CREATE_NAMESPACE targets this namespace")
svcFilterRec := doSDRequest(t, h, "ListOperations", map[string]any{
@@ -241,7 +242,7 @@ func TestListOperations_NamespaceAndServiceIDFilters(t *testing.T) {
require.Equal(t, http.StatusOK, svcFilterRec.Code)
var svcOut map[string]any
- require.NoError(t, json.Unmarshal(svcFilterRec.Body.Bytes(), &svcOut))
+ require.NoError(t, json.Unmarshal(svcFilterRec.Body.Bytes(), &svcOut, jsonx.V1()))
assert.Len(t, svcOut["Operations"].([]any), 1, "only REGISTER_INSTANCE targets this service")
}
@@ -263,7 +264,7 @@ func TestListOperations_TypeFilterIN(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out["Operations"].([]any), 2)
}
@@ -291,7 +292,7 @@ func TestListOperations_UpdateDateBetweenFilter(t *testing.T) {
require.Equal(t, http.StatusOK, inRangeRec.Code)
var inRangeOut map[string]any
- require.NoError(t, json.Unmarshal(inRangeRec.Body.Bytes(), &inRangeOut))
+ require.NoError(t, json.Unmarshal(inRangeRec.Body.Bytes(), &inRangeOut, jsonx.V1()))
assert.Len(t, inRangeOut["Operations"].([]any), 1)
outOfRangeRec := doSDRequest(t, h, "ListOperations", map[string]any{
@@ -309,7 +310,7 @@ func TestListOperations_UpdateDateBetweenFilter(t *testing.T) {
require.Equal(t, http.StatusOK, outOfRangeRec.Code)
var outOfRangeOut map[string]any
- require.NoError(t, json.Unmarshal(outOfRangeRec.Body.Bytes(), &outOfRangeOut))
+ require.NoError(t, json.Unmarshal(outOfRangeRec.Body.Bytes(), &outOfRangeOut, jsonx.V1()))
assert.Empty(t, outOfRangeOut["Operations"].([]any))
}
@@ -345,7 +346,7 @@ func TestListOperations_StatusFilter(t *testing.T) {
rec := doSDRequest(t, h, "ListOperations", req)
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
ops := out["Operations"].([]any)
assert.Len(t, ops, tt.wantLen, "filterStatus=%s", tt.filterStatus)
})
diff --git a/services/servicediscovery/persistence.go b/services/servicediscovery/persistence.go
index 9ca25c121d..6850445b95 100644
--- a/services/servicediscovery/persistence.go
+++ b/services/servicediscovery/persistence.go
@@ -2,9 +2,11 @@ package servicediscovery
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -32,7 +34,7 @@ const servicediscoverySnapshotVersion = 1
// snapshot from an incompatible (older or newer) build of this backend as
// though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ServiceAttributes map[string]map[string]string `json:"serviceAttributes"`
InstanceHealthStatuses map[string]string `json:"instanceHealthStatuses"`
AccountID string `json:"accountID"`
@@ -74,7 +76,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
OpCounter: b.opCounter,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "servicediscovery: snapshot marshal failed", "error", err)
diff --git a/services/servicediscovery/persistence_test.go b/services/servicediscovery/persistence_test.go
index 52ecf60c82..d02c4938e4 100644
--- a/services/servicediscovery/persistence_test.go
+++ b/services/servicediscovery/persistence_test.go
@@ -1,7 +1,7 @@
package servicediscovery_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/servicediscovery"
)
@@ -322,7 +323,7 @@ func TestHandler_SnapshotRestore_ServiceAttributesAndHealth(t *testing.T) {
})
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
svc := createOut["Service"].(map[string]any)
svcID := svc["Id"].(string)
svcARN := svc["Arn"].(string)
@@ -359,7 +360,7 @@ func TestHandler_SnapshotRestore_ServiceAttributesAndHealth(t *testing.T) {
getRec := doSDRequest(t, h2, "GetServiceAttributes", map[string]any{"ServiceId": svcID})
assert.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
attrs := getOut["ServiceAttributes"].(map[string]any)["Attributes"].(map[string]any)
assert.Equal(t, "test", attrs["stage"])
assert.Equal(t, svcARN, getOut["ServiceAttributes"].(map[string]any)["ServiceArn"])
diff --git a/services/servicediscovery/services_test.go b/services/servicediscovery/services_test.go
index 5f3d9fbf68..1d172c2d20 100644
--- a/services/servicediscovery/services_test.go
+++ b/services/servicediscovery/services_test.go
@@ -1,7 +1,7 @@
package servicediscovery_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/servicediscovery"
)
@@ -105,7 +106,7 @@ func TestHandler_GetService(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
svcData := createResp["Service"].(map[string]any)
svcID := svcData["Id"].(string)
@@ -161,7 +162,7 @@ func TestHandler_DeleteService(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
svcData := createResp["Service"].(map[string]any)
svcID := svcData["Id"].(string)
@@ -270,7 +271,7 @@ func TestHandler_ServiceTagsViaListTagsForResource(t *testing.T) {
require.Equal(t, 200, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
svc := createResp["Service"].(map[string]any)
assert.NotContains(t, svc, "Tags", "real CreateService never returns a Tags field")
@@ -282,7 +283,7 @@ func TestHandler_ServiceTagsViaListTagsForResource(t *testing.T) {
require.Equal(t, 200, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
svcGet := getResp["Service"].(map[string]any)
assert.NotContains(t, svcGet, "Tags", "real GetService never returns a Tags field")
@@ -291,7 +292,7 @@ func TestHandler_ServiceTagsViaListTagsForResource(t *testing.T) {
require.Equal(t, 200, tagsRec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags := tagsResp["Tags"].([]any)
assert.Len(t, tags, 1)
@@ -316,7 +317,7 @@ func TestHandler_ListServicesNamespaceFilter(t *testing.T) {
// List without filter - should return all 3.
recAll := doSDRequest(t, h, "ListServices", map[string]any{})
var respAll map[string]any
- require.NoError(t, json.Unmarshal(recAll.Body.Bytes(), &respAll))
+ require.NoError(t, json.Unmarshal(recAll.Body.Bytes(), &respAll, jsonx.V1()))
assert.Len(t, respAll["Services"].([]any), 3)
// List with NAMESPACE_ID filter - should return only 2.
@@ -328,7 +329,7 @@ func TestHandler_ListServicesNamespaceFilter(t *testing.T) {
require.Equal(t, 200, recFiltered.Code)
var respFiltered map[string]any
- require.NoError(t, json.Unmarshal(recFiltered.Body.Bytes(), &respFiltered))
+ require.NoError(t, json.Unmarshal(recFiltered.Body.Bytes(), &respFiltered, jsonx.V1()))
assert.Len(t, respFiltered["Services"].([]any), 2)
}
@@ -450,7 +451,7 @@ func TestHandler_ListServicesResourceOwnerFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out["Services"].([]any), tt.wantLen)
})
}
@@ -464,13 +465,13 @@ func createPrivateDNSNamespaceHelper(t *testing.T, h *servicediscovery.Handler,
require.Equal(t, http.StatusOK, nsRec.Code)
var nsResp map[string]any
- require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp))
+ require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp, jsonx.V1()))
opID := nsResp["OperationId"].(string)
opRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
var opResp map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp, jsonx.V1()))
return opResp["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
}
@@ -555,7 +556,7 @@ func TestUpdateService_ReturnsOperationId(t *testing.T) {
createRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "svc-parity"})
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
svcID := created["Service"].(map[string]any)["Id"].(string)
rec := doSDRequest(t, h, "UpdateService", map[string]any{
@@ -565,7 +566,7 @@ func TestUpdateService_ReturnsOperationId(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tt.wantOpID {
assert.NotEmpty(t, out["OperationId"], "should contain OperationId")
}
@@ -606,7 +607,7 @@ func TestService_InstanceCount(t *testing.T) {
createSvcRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "svc-count"})
require.Equal(t, http.StatusOK, createSvcRec.Code)
var svcOut map[string]any
- require.NoError(t, json.Unmarshal(createSvcRec.Body.Bytes(), &svcOut))
+ require.NoError(t, json.Unmarshal(createSvcRec.Body.Bytes(), &svcOut, jsonx.V1()))
svcID := svcOut["Service"].(map[string]any)["Id"].(string)
for i := range tt.instancesBefore {
@@ -622,7 +623,7 @@ func TestService_InstanceCount(t *testing.T) {
getRec := doSDRequest(t, h, "GetService", map[string]any{"Id": svcID})
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
svc := getOut["Service"].(map[string]any)
assert.Equal(t, tt.wantCount, int(svc["InstanceCount"].(float64)), "GetService InstanceCount")
@@ -630,7 +631,7 @@ func TestService_InstanceCount(t *testing.T) {
listRec := doSDRequest(t, h, "ListServices", map[string]any{})
require.Equal(t, http.StatusOK, listRec.Code)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
svcs := listOut["Services"].([]any)
require.Len(t, svcs, 1)
gotListCount := int(svcs[0].(map[string]any)["InstanceCount"].(float64))
@@ -682,7 +683,7 @@ func TestListServices_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
svcs := out["Services"].([]any)
assert.Len(t, svcs, tt.wantLen)
if tt.wantNextToken {
@@ -703,7 +704,7 @@ func TestUpdateService_CreatesOperation(t *testing.T) {
createRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "svc-op-check"})
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
svcID := created["Service"].(map[string]any)["Id"].(string)
updateRec := doSDRequest(t, h, "UpdateService", map[string]any{
@@ -713,14 +714,14 @@ func TestUpdateService_CreatesOperation(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateOut map[string]string
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateOut, jsonx.V1()))
opID := updateOut["OperationId"]
require.NotEmpty(t, opID)
getOpRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
require.Equal(t, http.StatusOK, getOpRec.Code)
var opOut map[string]any
- require.NoError(t, json.Unmarshal(getOpRec.Body.Bytes(), &opOut))
+ require.NoError(t, json.Unmarshal(getOpRec.Body.Bytes(), &opOut, jsonx.V1()))
op := opOut["Operation"].(map[string]any)
assert.Equal(t, "UPDATE_SERVICE", op["Type"])
assert.Equal(t, "SUCCESS", op["Status"])
@@ -774,7 +775,7 @@ func TestUpdateService_OmittedConfigDeletesIt(t *testing.T) {
})
require.Equal(t, http.StatusOK, createRec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &created, jsonx.V1()))
svcID := created["Service"].(map[string]any)["Id"].(string)
updateRec := doSDRequest(t, h, "UpdateService", map[string]any{
@@ -786,7 +787,7 @@ func TestUpdateService_OmittedConfigDeletesIt(t *testing.T) {
getRec := doSDRequest(t, h, "GetService", map[string]any{"Id": svcID})
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
svc := out["Service"].(map[string]any)
if tt.wantDNS {
@@ -835,7 +836,7 @@ func TestHandler_UpdateService(t *testing.T) {
case "success":
createRec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "my-svc"})
var out map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &out, jsonx.V1()))
svcID := out["Service"].(map[string]any)["Id"].(string)
rec := doSDRequest(t, h, "UpdateService", map[string]any{
"Id": svcID,
@@ -898,7 +899,7 @@ func TestHandler_ServiceAttributes(t *testing.T) {
rec := doSDRequest(t, h, "CreateService", map[string]any{"Name": "attrs-svc"})
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
svc := out["Service"].(map[string]any)
return svc["Id"].(string), svc["Arn"].(string)
@@ -917,7 +918,7 @@ func TestHandler_ServiceAttributes(t *testing.T) {
assert.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
sa := getOut["ServiceAttributes"].(map[string]any)
attrs := sa["Attributes"].(map[string]any)
assert.Equal(t, "prod", attrs["env"])
@@ -973,7 +974,7 @@ func TestHandler_ServiceAttributes(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
attrs := getOut["ServiceAttributes"].(map[string]any)["Attributes"].(map[string]any)
assert.NotContains(t, attrs, "env")
assert.Equal(t, "yes", attrs["keep"])
@@ -1003,7 +1004,7 @@ func TestHandler_ServiceAttributes(t *testing.T) {
assert.Equal(t, tt.wantCode, getRec.Code, "body: %s", getRec.Body.String())
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
attrs := getOut["ServiceAttributes"].(map[string]any)["Attributes"]
assert.Empty(t, attrs)
@@ -1026,7 +1027,7 @@ func TestHandler_ServiceAttributes(t *testing.T) {
assert.Equal(t, tt.wantCode, getRec.Code, "body: %s", getRec.Body.String())
var getOut map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getOut, jsonx.V1()))
attrs := getOut["ServiceAttributes"].(map[string]any)["Attributes"]
assert.Empty(t, attrs)
}
@@ -1096,7 +1097,7 @@ func TestHandler_UpdateServiceAttributesQuota(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
svcID := createResp["Service"].(map[string]any)["Id"].(string)
if tt.presetTo > 0 {
@@ -1120,7 +1121,7 @@ func TestHandler_UpdateServiceAttributesQuota(t *testing.T) {
if tt.wantType != "" {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
}
})
diff --git a/services/servicediscovery/tags_test.go b/services/servicediscovery/tags_test.go
index 8efed4427f..8687fa2550 100644
--- a/services/servicediscovery/tags_test.go
+++ b/services/servicediscovery/tags_test.go
@@ -1,13 +1,15 @@
package servicediscovery_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_TagsLifecycle(t *testing.T) {
@@ -22,14 +24,14 @@ func TestHandler_TagsLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
opID := createResp["OperationId"].(string)
opRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
require.Equal(t, http.StatusOK, opRec.Code)
var opResp map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp, jsonx.V1()))
operation := opResp["Operation"].(map[string]any)
targets := operation["Targets"].(map[string]any)
nsID := targets["NAMESPACE"].(string)
@@ -38,7 +40,7 @@ func TestHandler_TagsLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, nsRec.Code)
var nsResp map[string]any
- require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp))
+ require.NoError(t, json.Unmarshal(nsRec.Body.Bytes(), &nsResp, jsonx.V1()))
arn := nsResp["Namespace"].(map[string]any)["Arn"].(string)
rec := doSDRequest(t, h, "ListTagsForResource", map[string]any{"ResourceARN": arn})
@@ -142,21 +144,21 @@ func TestHandler_TagResource_TooManyTags_Merged(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
opID := createResp["OperationId"].(string)
opRec := doSDRequest(t, h, "GetOperation", map[string]any{"OperationId": opID})
require.Equal(t, http.StatusOK, opRec.Code)
var opResp map[string]any
- require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp))
+ require.NoError(t, json.Unmarshal(opRec.Body.Bytes(), &opResp, jsonx.V1()))
nsID := opResp["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
getRec := doSDRequest(t, h, "GetNamespace", map[string]any{"Id": nsID})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
arn := getResp["Namespace"].(map[string]any)["Arn"].(string)
firstBatch := make([]map[string]string, 0, 40)
@@ -197,14 +199,14 @@ func TestMapToTagEntriesSorted(t *testing.T) {
require.Equal(t, 200, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
svcARN := createResp["Service"].(map[string]any)["Arn"].(string)
tagsRec := doSDRequest(t, h, "ListTagsForResource", map[string]any{"ResourceARN": svcARN})
require.Equal(t, 200, tagsRec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp, jsonx.V1()))
tags := tagsResp["Tags"].([]any)
require.Len(t, tags, 3)
@@ -226,7 +228,7 @@ func TestHandler_ErrResourceNotFoundTagging(t *testing.T) {
assert.Equal(t, 400, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
}
diff --git a/services/servicequotas/autonotify.go b/services/servicequotas/autonotify.go
index edc4064cc4..5086969443 100644
--- a/services/servicequotas/autonotify.go
+++ b/services/servicequotas/autonotify.go
@@ -2,11 +2,12 @@ package servicequotas
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -151,7 +152,7 @@ func (b *InMemoryBackend) noticeFor(
body, _ := json.Marshal(map[string]any{
"account": am.Account, "region": am.Region, "serviceCode": serviceCode, "quotaCode": def.Code,
"quotaName": def.Name, "utilization": util, "threshold": level, "optInType": am.OptInType,
- })
+ }, jsonx.V1())
return pendingNotice{
topicArn: am.NotificationArn,
diff --git a/services/servicequotas/catalog.go b/services/servicequotas/catalog.go
index 88d1222800..ede079c8a6 100644
--- a/services/servicequotas/catalog.go
+++ b/services/servicequotas/catalog.go
@@ -4,11 +4,13 @@ import (
"bytes"
"compress/gzip"
_ "embed" // quota catalog
- "encoding/json"
+ "encoding/json/v2"
"io"
"slices"
"strings"
"sync"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
//go:embed catalog.json
@@ -62,7 +64,7 @@ type Catalog struct {
var defaultCatalog = sync.OnceValue(func() *Catalog { //nolint:gochecknoglobals // immutable embedded data
var svcs []ServiceDef
- if err := json.Unmarshal(catalogJSON, &svcs); err != nil {
+ if err := json.Unmarshal(catalogJSON, &svcs, jsonx.V1()); err != nil {
panic("servicequotas: embedded catalog is invalid: " + err.Error())
}
@@ -72,7 +74,7 @@ var defaultCatalog = sync.OnceValue(func() *Catalog { //nolint:gochecknoglobals
}
var docs []ServiceDef
- if err = json.Unmarshal(raw, &docs); err != nil {
+ if err = json.Unmarshal(raw, &docs, jsonx.V1()); err != nil {
panic("servicequotas: embedded docs catalog is invalid: " + err.Error())
}
diff --git a/services/servicequotas/docs_catalog_test.go b/services/servicequotas/docs_catalog_test.go
index d2cf99ee43..177bfb5495 100644
--- a/services/servicequotas/docs_catalog_test.go
+++ b/services/servicequotas/docs_catalog_test.go
@@ -4,7 +4,8 @@ import (
"bytes"
"compress/gzip"
_ "embed"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"regexp"
"testing"
@@ -14,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/servicequotas"
)
@@ -30,7 +32,7 @@ func TestDocsCatalog_Provenance(t *testing.T) {
require.NoError(t, err)
var svcs []servicequotas.ServiceDef
- require.NoError(t, json.Unmarshal(docsQuotas, &svcs))
+ require.NoError(t, json.Unmarshal(docsQuotas, &svcs, jsonx.V1()))
codeRe := regexp.MustCompile(`^L-[0-9A-F]{8}$`)
seen := map[string]bool{}
@@ -55,7 +57,7 @@ func TestDocsCatalog_Provenance(t *testing.T) {
seen[s.Code] = true
}
- canon, err := json.MarshalIndent(svcs, "", " ")
+ canon, err := json.Marshal(svcs, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
require.NoError(t, err)
assert.True(t, bytes.Equal(append(canon, '\n'), docsQuotas), "docs_quotas.json.gz is not in generator output form")
}
diff --git a/services/servicequotas/handler.go b/services/servicequotas/handler.go
index 2a4e9bbd23..89a245723e 100644
--- a/services/servicequotas/handler.go
+++ b/services/servicequotas/handler.go
@@ -2,7 +2,7 @@ package servicequotas
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"slices"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/worker"
@@ -39,7 +40,7 @@ func bind[I, O any](fn func(*Handler, context.Context, *I) (*O, error)) opFunc {
var in I
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, fail(ErrIllegalArgument, "malformed request body: %v", err)
}
}
diff --git a/services/servicequotas/persistence.go b/services/servicequotas/persistence.go
index 4e17ce5c2a..3a18c22313 100644
--- a/services/servicequotas/persistence.go
+++ b/services/servicequotas/persistence.go
@@ -2,7 +2,7 @@ package servicequotas
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,11 +12,11 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Seq int64 `json:"seq"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Seq int64 `json:"seq"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/ses/deep_flows_sdk_test.go b/services/ses/deep_flows_sdk_test.go
index ad57b0be1c..d6a77d33e7 100644
--- a/services/ses/deep_flows_sdk_test.go
+++ b/services/ses/deep_flows_sdk_test.go
@@ -1,7 +1,7 @@
package ses_test
import (
- "encoding/json"
+ "encoding/json/v2"
"sync"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ses"
)
@@ -199,8 +200,8 @@ func TestEventDestination_SendAndDeliveryEvents_SDK(t *testing.T) {
require.Len(t, pub.messages, 2)
var send, delivery map[string]any
- require.NoError(t, json.Unmarshal([]byte(pub.messages[0]), &send))
- require.NoError(t, json.Unmarshal([]byte(pub.messages[1]), &delivery))
+ require.NoError(t, json.Unmarshal([]byte(pub.messages[0]), &send, jsonx.V1()))
+ require.NoError(t, json.Unmarshal([]byte(pub.messages[1]), &delivery, jsonx.V1()))
assert.Equal(t, "Send", send["eventType"])
assert.Contains(t, send, "send")
assert.Equal(t, "Delivery", delivery["eventType"])
diff --git a/services/ses/email_sending.go b/services/ses/email_sending.go
index 82b4653142..6f0f6f0fa7 100644
--- a/services/ses/email_sending.go
+++ b/services/ses/email_sending.go
@@ -1,13 +1,15 @@
package ses
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strings"
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// maxRecipientsPerMessage is the AWS SES limit on the combined number of
@@ -637,7 +639,7 @@ func (b *InMemoryBackend) SendBulkTemplatedEmail(in SendBulkTemplatedEmailInput)
return nil, err
}
- mergedJSON, err := json.Marshal(merged)
+ mergedJSON, err := json.Marshal(merged, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("%w: failed to encode template data", ErrInvalidParameter)
}
diff --git a/services/ses/identities.go b/services/ses/identities.go
index 6bb2813d70..3b3fa11a10 100644
--- a/services/ses/identities.go
+++ b/services/ses/identities.go
@@ -1,12 +1,13 @@
package ses
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"net/mail"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/collections"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -226,7 +227,7 @@ func (b *InMemoryBackend) PutIdentityPolicy(identity, policyName, policy string)
return fmt.Errorf("%w: Policy is required", ErrInvalidParameter)
}
- if !json.Valid([]byte(policy)) {
+ if !jsontext.Value([]byte(policy)).IsValid(jsonx.V1()) {
return fmt.Errorf("%w: Policy must be a valid JSON policy document", ErrInvalidPolicy)
}
diff --git a/services/ses/inbound_actions.go b/services/ses/inbound_actions.go
index bc8f5d44b4..837d4dad23 100644
--- a/services/ses/inbound_actions.go
+++ b/services/ses/inbound_actions.go
@@ -3,7 +3,7 @@ package ses
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/mail"
@@ -12,6 +12,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -337,7 +338,7 @@ func (r *inboundRun) lambdaAction(ctx context.Context, a ReceiptAction) (ruleFlo
payload, err := json.Marshal(lambdaEvent{Records: []lambdaRecord{{
EventSource: "aws:ses", EventVersion: "1.0", SES: lambdaSES{Receipt: n.Receipt, Mail: n.Mail},
- }}})
+ }}}, jsonx.V1())
if err != nil {
return flowContinue, fmt.Errorf("marshal lambda event: %w", err)
}
@@ -356,7 +357,7 @@ func (r *inboundRun) lambdaAction(ctx context.Context, a ReceiptAction) (ruleFlo
Disposition string `json:"disposition"`
}
- _ = json.Unmarshal(out, &resp)
+ _ = json.Unmarshal(out, &resp, jsonx.V1())
switch strings.ToUpper(resp.Disposition) {
case "STOP_RULE":
@@ -428,7 +429,7 @@ func (r *inboundRun) notify(topicARN string, n receiptNotification) error {
return errInboundNoSNS
}
- body, err := json.Marshal(n)
+ body, err := json.Marshal(n, jsonx.V1())
if err != nil {
return fmt.Errorf("marshal notification: %w", err)
}
diff --git a/services/ses/inbound_auth_test.go b/services/ses/inbound_auth_test.go
index 6d1f12710c..82d6206d5a 100644
--- a/services/ses/inbound_auth_test.go
+++ b/services/ses/inbound_auth_test.go
@@ -9,7 +9,7 @@ import (
"crypto/sha256"
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/textproto"
@@ -19,6 +19,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errNXDomain = errors.New("nxdomain")
@@ -249,7 +251,7 @@ func TestInboundAuthVerdicts(t *testing.T) {
} `json:"receipt"`
}
- require.NoError(t, json.Unmarshal([]byte(snsF.msgs["arn:t"][0]), &n))
+ require.NoError(t, json.Unmarshal([]byte(snsF.msgs["arn:t"][0]), &n, jsonx.V1()))
assert.Equal(t, tc.wantSPF, n.Receipt.SPF.Status)
assert.Equal(t, tc.wantDKIM, n.Receipt.DKIM.Status)
assert.Equal(t, tc.wantDMARC, n.Receipt.DMARC.Status)
diff --git a/services/ses/inbound_dsn_kms_test.go b/services/ses/inbound_dsn_kms_test.go
index 25720f4b99..b5a3a86c11 100644
--- a/services/ses/inbound_dsn_kms_test.go
+++ b/services/ses/inbound_dsn_kms_test.go
@@ -5,7 +5,7 @@ import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"mime"
"mime/multipart"
@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ses"
)
@@ -173,7 +174,7 @@ func TestInboundS3KMS(t *testing.T) {
assert.NotContains(t, string(ct), "body line")
var mat map[string]string
- require.NoError(t, json.Unmarshal([]byte(meta["x-amz-matdesc"]), &mat))
+ require.NoError(t, json.Unmarshal([]byte(meta["x-amz-matdesc"]), &mat, jsonx.V1()))
assert.Equal(t, "r", mat["aws:ses:rule-name"])
assert.Equal(t, log[0].MessageID, mat["aws:ses:message-id"])
diff --git a/services/ses/inbound_kms.go b/services/ses/inbound_kms.go
index 389c5c2b82..ceaabe14d6 100644
--- a/services/ses/inbound_kms.go
+++ b/services/ses/inbound_kms.go
@@ -6,9 +6,11 @@ import (
"crypto/cipher"
"crypto/rand"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -61,7 +63,7 @@ func sealEnvelope(
return nil, nil, fmt.Errorf("iv: %w", err)
}
- matdesc, err := json.Marshal(encCtx)
+ matdesc, err := json.Marshal(encCtx, jsonx.V1())
if err != nil {
return nil, nil, fmt.Errorf("matdesc: %w", err)
}
diff --git a/services/ses/inbound_spf_test.go b/services/ses/inbound_spf_test.go
index 93bdfc4bc3..6ea079e483 100644
--- a/services/ses/inbound_spf_test.go
+++ b/services/ses/inbound_spf_test.go
@@ -2,7 +2,7 @@ package ses_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/netip"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ses"
)
@@ -94,7 +95,7 @@ func deliver(t *testing.T, dns ses.InboundTXTResolver, mailFrom, msg string) ver
} `json:"receipt"`
}
- require.NoError(t, json.Unmarshal([]byte(snsF.msgs["arn:t"][0]), &n))
+ require.NoError(t, json.Unmarshal([]byte(snsF.msgs["arn:t"][0]), &n, jsonx.V1()))
return verdictSet{spf: n.Receipt.SPF.Status, dmarc: n.Receipt.DMARC.Status}
}
diff --git a/services/ses/inbound_test.go b/services/ses/inbound_test.go
index 4a51fc3ec1..fd1f649857 100644
--- a/services/ses/inbound_test.go
+++ b/services/ses/inbound_test.go
@@ -5,7 +5,7 @@ import (
"context"
"crypto/tls"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/smtp"
"net/textproto"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ses"
)
@@ -364,7 +365,7 @@ func TestInboundNotificationShape(t *testing.T) {
} `json:"receipt"`
}
- require.NoError(t, json.Unmarshal([]byte(snsF.msgs["arn:t"][0]), &n))
+ require.NoError(t, json.Unmarshal([]byte(snsF.msgs["arn:t"][0]), &n, jsonx.V1()))
assert.Equal(t, "Received", n.NotificationType)
assert.Equal(t, "sender@sender.test", n.Mail.Source)
assert.Equal(t, []string{"user@example.com"}, n.Mail.Destination)
@@ -400,7 +401,7 @@ func TestInboundNotificationShape(t *testing.T) {
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(lam.payloads[0], &ev))
+ require.NoError(t, json.Unmarshal(lam.payloads[0], &ev, jsonx.V1()))
require.Len(t, ev.Records, 1)
assert.Equal(t, "aws:ses", ev.Records[0].EventSource)
assert.Equal(t, "Lambda", ev.Records[0].SES.Receipt.Action.Type)
diff --git a/services/ses/multi_region_test.go b/services/ses/multi_region_test.go
index a766d038cd..91479af8b0 100644
--- a/services/ses/multi_region_test.go
+++ b/services/ses/multi_region_test.go
@@ -1,7 +1,8 @@
package ses_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ses"
)
@@ -161,9 +163,9 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(t.Context())
- var doc map[string]json.RawMessage
+ var doc map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(snap, &doc))
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
_, hasRegions := doc["regions"]
assert.Equal(t, tc.wantRegions, hasRegions)
diff --git a/services/ses/notifications.go b/services/ses/notifications.go
index 24d3b7c752..f73baacd34 100644
--- a/services/ses/notifications.go
+++ b/services/ses/notifications.go
@@ -1,10 +1,12 @@
package ses
import (
- "encoding/json"
+ "encoding/json/v2"
"slices"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// sesNotificationMail is the "mail" object of an SES SNS notification.
@@ -100,7 +102,7 @@ func buildSESNotificationPayload(
}
// map[string]any marshaling of these concrete field types cannot fail.
- bs, _ := json.Marshal(data)
+ bs, _ := json.Marshal(data, jsonx.V1())
return bs
}
diff --git a/services/ses/persistence.go b/services/ses/persistence.go
index 537f698a19..74e3c159da 100644
--- a/services/ses/persistence.go
+++ b/services/ses/persistence.go
@@ -2,11 +2,13 @@ package ses
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -88,7 +90,7 @@ func newDirtyDTORegistry() (
// snapshot from an incompatible (older or newer) build of this backend as
// though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Policies map[string]map[string]string `json:"policies,omitempty"`
ActiveRuleSet string `json:"activeRuleSet,omitempty"`
Region string `json:"region,omitempty"`
@@ -159,7 +161,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
AccountID: b.accountID,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "ses: failed to marshal snapshot", "error", err)
@@ -234,7 +236,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// configSets, trackingOptions, eventDestinations) from tables via the
// ephemeral DTO registry, converting each DTO back to its live type. The
// caller MUST hold b.mu for writing.
-func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]jsontext.Value) error {
dtoReg, idDTOs, csDTOs, toDTOs, edDTOs := newDirtyDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
diff --git a/services/ses/templates.go b/services/ses/templates.go
index f283d2072e..41d793596b 100644
--- a/services/ses/templates.go
+++ b/services/ses/templates.go
@@ -1,12 +1,13 @@
package ses
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"strings"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -117,7 +118,7 @@ func parseTemplateData(templateData string) (map[string]string, error) {
}
raw := map[string]any{}
- if err := json.Unmarshal([]byte(templateData), &raw); err != nil {
+ if err := json.Unmarshal([]byte(templateData), &raw, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: TemplateData must be valid JSON", ErrInvalidParameter)
}
diff --git a/services/sesv2/account_test.go b/services/sesv2/account_test.go
index e9a58c31bf..91d0da0095 100644
--- a/services/sesv2/account_test.go
+++ b/services/sesv2/account_test.go
@@ -1,7 +1,7 @@
package sesv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,8 @@ import (
sesv2types "github.com/aws/aws-sdk-go-v2/service/sesv2/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestGetAccount tests the GetAccount operation.
@@ -114,7 +116,7 @@ func TestGetBlacklistReports(t *testing.T) {
var out struct {
BlacklistReport map[string][]string `json:"BlacklistReport"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out.BlacklistReport, "1.2.3.4")
assert.Contains(t, out.BlacklistReport, "5.6.7.8")
assert.Empty(t, out.BlacklistReport["1.2.3.4"])
diff --git a/services/sesv2/configuration_sets_test.go b/services/sesv2/configuration_sets_test.go
index 7b816d8ab9..91d9fa8fb1 100644
--- a/services/sesv2/configuration_sets_test.go
+++ b/services/sesv2/configuration_sets_test.go
@@ -1,13 +1,14 @@
package sesv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sesv2"
)
@@ -33,7 +34,7 @@ func TestConfigSetTrackingOptions(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tracking, ok := out["TrackingOptions"].(map[string]any)
require.True(t, ok, "TrackingOptions should be present in GetConfigurationSet response")
@@ -74,7 +75,7 @@ func TestConfigSetDeliveryOptions(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
delivery, ok := out["DeliveryOptions"].(map[string]any)
require.True(t, ok, "DeliveryOptions should be present")
@@ -113,7 +114,7 @@ func TestConfigSetSendingOptions(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
sending, ok := out["SendingOptions"].(map[string]any)
require.True(t, ok, "SendingOptions should be present")
@@ -150,7 +151,7 @@ func TestConfigSetReputationOptions(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
rep, ok := out["ReputationOptions"].(map[string]any)
require.True(t, ok, "ReputationOptions should be present")
@@ -187,7 +188,7 @@ func TestConfigSetSuppressionOptions(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
supp, ok := out["SuppressionOptions"].(map[string]any)
require.True(t, ok, "SuppressionOptions should be present")
@@ -234,7 +235,7 @@ func TestGetConfigurationSetDefaultSendingEnabled(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
sending, ok := out["SendingOptions"].(map[string]any)
require.True(t, ok, "SendingOptions always present in GetConfigurationSet")
@@ -265,7 +266,7 @@ func TestConfigSetMultipleOptions(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "multi-cs", out["ConfigurationSetName"])
assert.NotNil(t, out["TrackingOptions"])
@@ -635,7 +636,7 @@ func TestGetConfigurationSet(t *testing.T) {
if tt.wantCode == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.csName, out["ConfigurationSetName"])
}
})
@@ -668,7 +669,7 @@ func TestListConfigurationSets(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
sets, ok := out["ConfigurationSets"].([]any)
require.True(t, ok)
diff --git a/services/sesv2/deep_flows_sdk_test.go b/services/sesv2/deep_flows_sdk_test.go
index 322781aacf..c8c7f0fdb6 100644
--- a/services/sesv2/deep_flows_sdk_test.go
+++ b/services/sesv2/deep_flows_sdk_test.go
@@ -1,7 +1,7 @@
package sesv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"sync"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sesv2"
)
@@ -125,8 +126,8 @@ func TestSendEmail_SNSEventDestination_SDK(t *testing.T) {
assert.Equal(t, []string{topic, topic}, pub.topics)
var send, delivery map[string]any
- require.NoError(t, json.Unmarshal([]byte(pub.messages[0]), &send))
- require.NoError(t, json.Unmarshal([]byte(pub.messages[1]), &delivery))
+ require.NoError(t, json.Unmarshal([]byte(pub.messages[0]), &send, jsonx.V1()))
+ require.NoError(t, json.Unmarshal([]byte(pub.messages[1]), &delivery, jsonx.V1()))
assert.Equal(t, "Send", send["eventType"])
assert.Equal(t, "Delivery", delivery["eventType"])
diff --git a/services/sesv2/deliverability_test.go b/services/sesv2/deliverability_test.go
index c313bc92ff..503ab7b08f 100644
--- a/services/sesv2/deliverability_test.go
+++ b/services/sesv2/deliverability_test.go
@@ -1,7 +1,7 @@
package sesv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sesv2"
)
@@ -95,7 +96,7 @@ func TestPutDeliverabilityDashboardOption(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, true, out["DashboardEnabled"])
assert.Equal(t, "ACTIVE", out["AccountStatus"])
@@ -138,7 +139,7 @@ func TestGetDeliverabilityTestReport(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
reportID := createResp["ReportId"].(string)
rec := doReq(t, h, http.MethodGet, "/v2/email/deliverability-dashboard/test-reports/"+reportID, nil)
@@ -188,7 +189,7 @@ func TestGetDomainDeliverabilityCampaign(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
campaign, ok := out["DomainDeliverabilityCampaign"].(map[string]any)
require.True(t, ok, "response missing DomainDeliverabilityCampaign wrapper: %s", rec.Body)
@@ -267,7 +268,7 @@ func TestGetEmailAddressInsights(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
validation, ok := out["MailboxValidation"].(map[string]any)
require.True(t, ok, "response missing MailboxValidation: %s", rec.Body)
diff --git a/services/sesv2/email_identities_test.go b/services/sesv2/email_identities_test.go
index a7a48b5bb0..0eeea5880e 100644
--- a/services/sesv2/email_identities_test.go
+++ b/services/sesv2/email_identities_test.go
@@ -1,7 +1,7 @@
package sesv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sesv2"
)
@@ -32,7 +33,7 @@ func TestPutEmailIdentityConfigurationSetAttributes(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "my-cs", out["ConfigurationSetName"])
}
@@ -67,7 +68,7 @@ func TestPutEmailIdentityDkimAttributes(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
dkim, ok := out["DkimAttributes"].(map[string]any)
require.True(t, ok, "DkimAttributes should be present")
@@ -145,7 +146,7 @@ func TestPutEmailIdentityDkimSigningAttributes(t *testing.T) {
DkimStatus string `json:"DkimStatus"`
DkimTokens []string `json:"DkimTokens"`
}
- err := json.Unmarshal(rec.Body.Bytes(), &resp)
+ err := json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1())
require.NoError(t, err)
assert.Equal(t, tt.wantDkimStat, resp.DkimStatus)
if tt.wantTokens {
@@ -177,7 +178,7 @@ func TestPutEmailIdentityFeedbackAttributes(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, false, out["FeedbackForwardingStatus"])
}
@@ -214,7 +215,7 @@ func TestPutEmailIdentityMailFromAttributes(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
mailFrom, ok := out["MailFromAttributes"].(map[string]any)
require.True(t, ok, "MailFromAttributes should be present after setting mail-from domain")
@@ -256,7 +257,7 @@ func TestPutEmailIdentityMailFromClear(t *testing.T) {
rec := doReq(t, h, http.MethodGet, "/v2/email/identities/mfclear%40example.com", nil)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// MailFromAttributes should be absent when domain is empty.
assert.Nil(t, out["MailFromAttributes"])
@@ -277,7 +278,7 @@ func TestGetEmailIdentityDefaults(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotContains(t, out, "EmailIdentity")
assert.Equal(t, "EMAIL_ADDRESS", out["IdentityType"])
@@ -304,7 +305,7 @@ func TestGetEmailIdentityDomainHasDkimTokens(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "DOMAIN", out["IdentityType"])
@@ -335,7 +336,7 @@ func TestGetEmailIdentityEmailAddressNoDkimTokens(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
dkim, ok := out["DkimAttributes"].(map[string]any)
require.True(t, ok)
@@ -357,7 +358,7 @@ func TestCreateEmailIdentityWithConfigSet(t *testing.T) {
rec = doReq(t, h, http.MethodGet, "/v2/email/identities/tagged%40example.com", nil)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "my-cs", out["ConfigurationSetName"])
}
@@ -378,7 +379,7 @@ func TestCreateEmailIdentityWithTags(t *testing.T) {
rec = doReq(t, h, http.MethodGet, "/v2/email/identities/tagged2%40example.com", nil)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tags, ok := out["Tags"].([]any)
require.True(t, ok, "Tags should be returned")
@@ -397,7 +398,7 @@ func TestCreateDomainIdentityReturnsDkimAttributes(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// Domain identities should return DkimAttributes with tokens in create response.
dkim, ok := out["DkimAttributes"].(map[string]any)
@@ -420,7 +421,7 @@ func TestCreateEmailAddressDoesNotReturnDkimAttributes(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// Email addresses should not return DkimAttributes in create response.
assert.Nil(t, out["DkimAttributes"], "Email address CreateEmailIdentity should omit DkimAttributes")
@@ -442,7 +443,7 @@ func TestGetEmailIdentityPoliciesIncludedInGet(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
policies, ok := out["Policies"].(map[string]any)
require.True(t, ok, "Policies should be returned in GetEmailIdentity")
@@ -943,7 +944,7 @@ func TestCreateEmailIdentity(t *testing.T) {
if tt.wantType != "" {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantType, out["IdentityType"])
assert.Equal(t, true, out["VerifiedForSendingStatus"])
}
@@ -994,7 +995,7 @@ func TestGetEmailIdentity(t *testing.T) {
if tt.wantCode == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotContains(t, out, "EmailIdentity")
}
})
@@ -1027,7 +1028,7 @@ func TestListEmailIdentities(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
identities, ok := out["EmailIdentities"].([]any)
require.True(t, ok)
diff --git a/services/sesv2/email_templates.go b/services/sesv2/email_templates.go
index 828f141b12..947dce07dc 100644
--- a/services/sesv2/email_templates.go
+++ b/services/sesv2/email_templates.go
@@ -1,7 +1,7 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -9,6 +9,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -187,7 +188,7 @@ func parseTemplateVars(data string) (map[string]string, error) {
}
raw := map[string]any{}
- if err := json.Unmarshal([]byte(data), &raw); err != nil {
+ if err := json.Unmarshal([]byte(data), &raw, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: TemplateData must be valid JSON", ErrInvalidInput)
}
diff --git a/services/sesv2/email_templates_test.go b/services/sesv2/email_templates_test.go
index 964c99b6fe..09d89eb958 100644
--- a/services/sesv2/email_templates_test.go
+++ b/services/sesv2/email_templates_test.go
@@ -1,13 +1,14 @@
package sesv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sesv2"
)
@@ -141,7 +142,7 @@ func TestGetEmailTemplate_RendersTags(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Tags, 1)
assert.Equal(t, "team", resp.Tags[0].Key)
assert.Equal(t, "sec", resp.Tags[0].Value)
diff --git a/services/sesv2/event_publishing.go b/services/sesv2/event_publishing.go
index dfd17277df..1d40b9f1f7 100644
--- a/services/sesv2/event_publishing.go
+++ b/services/sesv2/event_publishing.go
@@ -1,10 +1,12 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/v2"
"net/mail"
"slices"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type eventMail struct {
@@ -63,7 +65,7 @@ func (b *InMemoryBackend) publishSendEvents(email Email) {
}
if slices.Contains(d.MatchingEventTypes, "SEND") {
- payload, _ := json.Marshal(map[string]any{"eventType": "Send", "mail": m, "send": struct{}{}})
+ payload, _ := json.Marshal(map[string]any{"eventType": "Send", "mail": m, "send": struct{}{}}, jsonx.V1())
_ = pub.PublishToTopic(d.SnsDestination.TopicArn, string(payload))
}
@@ -71,7 +73,7 @@ func (b *InMemoryBackend) publishSendEvents(email Email) {
payload, _ := json.Marshal(map[string]any{
"eventType": "Delivery", "mail": m,
"delivery": eventDelivery{Timestamp: ts, Recipients: email.To},
- })
+ }, jsonx.V1())
_ = pub.PublishToTopic(d.SnsDestination.TopicArn, string(payload))
}
}
diff --git a/services/sesv2/export_jobs_test.go b/services/sesv2/export_jobs_test.go
index e82ea88f07..b9e24e9f08 100644
--- a/services/sesv2/export_jobs_test.go
+++ b/services/sesv2/export_jobs_test.go
@@ -1,13 +1,14 @@
package sesv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sesv2"
)
@@ -127,7 +128,7 @@ func TestGetExportJob(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
jobID := createResp["JobId"].(string)
rec := doRequest(t, h, http.MethodGet, "/v2/email/export-jobs/"+jobID, nil)
@@ -249,7 +250,7 @@ func TestCreateExportJobWireShape(t *testing.T) {
}
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
jobID, ok := createResp["JobId"].(string)
require.True(t, ok)
@@ -257,7 +258,7 @@ func TestCreateExportJobWireShape(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, tt.wantSource, getResp["ExportSourceType"])
})
}
@@ -285,7 +286,7 @@ func TestCancelExportJobAfterCreate(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
jobID := createResp["JobId"].(string)
rec := doRequest(t, h, http.MethodPut, "/v2/email/export-jobs/"+jobID+"/cancel", nil)
diff --git a/services/sesv2/handler_account.go b/services/sesv2/handler_account.go
index 6994f49515..1548c71be3 100644
--- a/services/sesv2/handler_account.go
+++ b/services/sesv2/handler_account.go
@@ -1,10 +1,13 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// account handlers
@@ -45,7 +48,7 @@ func (h *Handler) handlePutAccountDedicatedIPWarmupAttributes(c *echo.Context) (
AutoWarmupEnabled bool `json:"AutoWarmupEnabled"`
}
- _ = json.NewDecoder(c.Request().Body).Decode(&in)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1())
if err := h.Backend.PutAccountDedicatedIPWarmupAttributes(in.AutoWarmupEnabled); err != nil {
return nil, err
@@ -67,7 +70,7 @@ type putAccountDetailsInput struct {
func (h *Handler) handlePutAccountDetails(c *echo.Context) (any, error) {
var in putAccountDetailsInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -90,7 +93,7 @@ type putAccountSendingInput struct {
func (h *Handler) handlePutAccountSendingAttributes(c *echo.Context) (any, error) {
var in putAccountSendingInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -107,7 +110,7 @@ func (h *Handler) handlePutAccountPricingAttributes(c *echo.Context) (any, error
Plan string `json:"Plan"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -123,7 +126,7 @@ func (h *Handler) handlePutAccountSuppressionAttributes(c *echo.Context) (any, e
SuppressedReasons []string `json:"SuppressedReasons"`
}
- _ = json.NewDecoder(c.Request().Body).Decode(&in)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1())
if err := h.Backend.PutAccountSuppressionAttributes(in.SuppressedReasons); err != nil {
return nil, err
diff --git a/services/sesv2/handler_configuration_sets.go b/services/sesv2/handler_configuration_sets.go
index 35e426ea45..8acac37d63 100644
--- a/services/sesv2/handler_configuration_sets.go
+++ b/services/sesv2/handler_configuration_sets.go
@@ -1,10 +1,13 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createConfigurationSetInput struct {
@@ -203,7 +206,7 @@ type listConfigurationSetsOutput struct {
func (h *Handler) handleCreateConfigurationSet(c *echo.Context) (any, error) {
var in createConfigurationSetInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidParameter, err.Error())
}
@@ -313,7 +316,7 @@ func (h *Handler) handlePutConfigurationSetArchivingOptions(
ArchiveARN string `json:"ArchiveArn"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -332,7 +335,7 @@ func (h *Handler) handlePutConfigurationSetDeliveryOptions(
TLSPolicy string `json:"TlsPolicy"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -351,7 +354,7 @@ func (h *Handler) handlePutConfigurationSetReputationOptions(
ReputationMetricsEnabled bool `json:"ReputationMetricsEnabled"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -369,7 +372,7 @@ func (h *Handler) handlePutConfigurationSetSendingOptions(
SendingEnabled bool `json:"SendingEnabled"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -382,7 +385,7 @@ func (h *Handler) handlePutConfigurationSetSuppressionOptions(
) (any, error) {
var in suppressionOptionsInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -403,7 +406,7 @@ func (h *Handler) handlePutConfigurationSetTrackingOptions(
HTTPSPolicy string `json:"HttpsPolicy"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_contact_lists.go b/services/sesv2/handler_contact_lists.go
index 3e81a74f9c..9c746c9a00 100644
--- a/services/sesv2/handler_contact_lists.go
+++ b/services/sesv2/handler_contact_lists.go
@@ -1,10 +1,13 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// topicInput mirrors types.Topic.
@@ -38,7 +41,7 @@ type createContactListInput struct {
func (h *Handler) handleCreateContactList(c *echo.Context) (any, error) {
var in createContactListInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -78,7 +81,7 @@ type updateContactListInput struct {
func (h *Handler) handleUpdateContactList(c *echo.Context, name string) (any, error) {
var in updateContactListInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_contacts.go b/services/sesv2/handler_contacts.go
index 0e4c7ee5cd..7515241c9e 100644
--- a/services/sesv2/handler_contacts.go
+++ b/services/sesv2/handler_contacts.go
@@ -1,12 +1,15 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/url"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createContactInput struct {
@@ -19,7 +22,7 @@ type createContactInput struct {
func (h *Handler) handleCreateContact(c *echo.Context, contactListName string) (any, error) {
var in createContactInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -106,7 +109,7 @@ func (h *Handler) handleUpdateContact(c *echo.Context, contactListName string) (
var in updateContactInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -136,7 +139,7 @@ type listContactsInput struct {
func (h *Handler) handleListContacts(c *echo.Context, contactListName string) (any, error) {
var in listContactsInput
- _ = json.NewDecoder(c.Request().Body).Decode(&in)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1())
var filter ContactFilter
if in.Filter != nil {
diff --git a/services/sesv2/handler_custom_verification_email_templates.go b/services/sesv2/handler_custom_verification_email_templates.go
index c11aad56fb..c879df6040 100644
--- a/services/sesv2/handler_custom_verification_email_templates.go
+++ b/services/sesv2/handler_custom_verification_email_templates.go
@@ -1,10 +1,13 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createCustomVerificationEmailTemplateInput struct {
@@ -19,7 +22,7 @@ type createCustomVerificationEmailTemplateInput struct {
func (h *Handler) handleCreateCustomVerificationEmailTemplate(c *echo.Context) (any, error) {
var in createCustomVerificationEmailTemplateInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -72,7 +75,7 @@ func (h *Handler) handleUpdateCustomVerificationEmailTemplate(
) (any, error) {
var in updateCustomVerificationEmailTemplateInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_dedicated_ip_pools.go b/services/sesv2/handler_dedicated_ip_pools.go
index 17dad99596..5bea4486f8 100644
--- a/services/sesv2/handler_dedicated_ip_pools.go
+++ b/services/sesv2/handler_dedicated_ip_pools.go
@@ -1,11 +1,14 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strconv"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createDedicatedIPPoolInput struct {
@@ -17,7 +20,7 @@ type createDedicatedIPPoolInput struct {
func (h *Handler) handleCreateDedicatedIPPool(c *echo.Context) (any, error) {
var in createDedicatedIPPoolInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -73,7 +76,7 @@ func (h *Handler) handlePutDedicatedIPPoolScalingAttributes(
) (any, error) {
var in putDedicatedIPPoolScalingInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_dedicated_ips.go b/services/sesv2/handler_dedicated_ips.go
index a2b91fdff0..f57365ccb8 100644
--- a/services/sesv2/handler_dedicated_ips.go
+++ b/services/sesv2/handler_dedicated_ips.go
@@ -1,11 +1,14 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strconv"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleGetDedicatedIP(ip string) (any, error) {
@@ -41,7 +44,7 @@ type putDedicatedIPInPoolInput struct {
func (h *Handler) handlePutDedicatedIPInPool(c *echo.Context, ip string) (any, error) {
var in putDedicatedIPInPoolInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -59,7 +62,7 @@ type putDedicatedIPWarmupInput struct {
func (h *Handler) handlePutDedicatedIPWarmupAttributes(c *echo.Context, ip string) (any, error) {
var in putDedicatedIPWarmupInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_deliverability.go b/services/sesv2/handler_deliverability.go
index e46169d814..8f71204485 100644
--- a/services/sesv2/handler_deliverability.go
+++ b/services/sesv2/handler_deliverability.go
@@ -1,10 +1,13 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createDeliverabilityTestReportInput struct {
@@ -20,7 +23,7 @@ type createDeliverabilityTestReportOutput struct {
func (h *Handler) handleCreateDeliverabilityTestReport(c *echo.Context) (any, error) {
var in createDeliverabilityTestReportInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -245,7 +248,7 @@ func (h *Handler) handleUpdateReputationEntityCustomerManagedStatus(
) (any, error) {
var in updateReputationEntityCustomerManagedStatusInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -270,7 +273,7 @@ func (h *Handler) handleUpdateReputationEntityPolicy(
) (any, error) {
var in updateReputationEntityPolicyInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_email_identities.go b/services/sesv2/handler_email_identities.go
index 98bf818707..4d07ec0275 100644
--- a/services/sesv2/handler_email_identities.go
+++ b/services/sesv2/handler_email_identities.go
@@ -1,12 +1,15 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/url"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- request types ----
@@ -71,7 +74,7 @@ type listEmailIdentitiesOutput struct {
func (h *Handler) handleCreateEmailIdentity(c *echo.Context) (any, error) {
var in createEmailIdentityInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidParameter, err.Error())
}
@@ -164,7 +167,11 @@ func (h *Handler) handleDeleteEmailIdentity(identity string) (any, error) {
return &emptyDeleteOutput{}, nil
}
-func (h *Handler) handleCreateEmailIdentityPolicy(c *echo.Context, identity string) (any, error) {
+//nolint:dupl // structurally similar handlers
+func (h *Handler) handleCreateEmailIdentityPolicy(
+ c *echo.Context,
+ identity string,
+) (any, error) {
// Extract policyName from the URL: .../identities/{identity}/policies/{policyName}
segments := strings.Split(strings.TrimPrefix(c.Request().URL.Path, sesv2PathPrefix), "/")
if len(segments) < policyPathMinSegments {
@@ -178,7 +185,7 @@ func (h *Handler) handleCreateEmailIdentityPolicy(c *echo.Context, identity stri
var in createEmailIdentityPolicyInput
- if decErr := json.NewDecoder(c.Request().Body).Decode(&in); decErr != nil {
+ if decErr := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); decErr != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, decErr.Error())
}
@@ -219,7 +226,11 @@ func (h *Handler) handleDeleteEmailIdentityPolicy(c *echo.Context, identity stri
return &emptyDeleteOutput{}, nil
}
-func (h *Handler) handleUpdateEmailIdentityPolicy(c *echo.Context, identity string) (any, error) {
+//nolint:dupl // structurally similar handlers
+func (h *Handler) handleUpdateEmailIdentityPolicy(
+ c *echo.Context,
+ identity string,
+) (any, error) {
segments := strings.Split(strings.TrimPrefix(c.Request().URL.Path, sesv2PathPrefix), "/")
if len(segments) < policyPathMinSegments {
return nil, fmt.Errorf("%w: invalid policy path", ErrInvalidInput)
@@ -233,7 +244,7 @@ func (h *Handler) handleUpdateEmailIdentityPolicy(c *echo.Context, identity stri
var in createEmailIdentityPolicyInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -254,7 +265,7 @@ func (h *Handler) handlePutEmailIdentityConfigurationSetAttributes(
ConfigurationSetName string `json:"ConfigurationSetName"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -272,7 +283,7 @@ func (h *Handler) handlePutEmailIdentityDkimAttributes(
SigningEnabled bool `json:"SigningEnabled"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -307,7 +318,7 @@ func (h *Handler) handlePutEmailIdentityFeedbackAttributes(
EmailForwardingEnabled bool `json:"EmailForwardingEnabled"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -326,7 +337,7 @@ func (h *Handler) handlePutEmailIdentityMailFromAttributes(
BehaviorOnMxFailure string `json:"BehaviorOnMxFailure"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_email_templates.go b/services/sesv2/handler_email_templates.go
index 25bae5f9cc..60df1ca005 100644
--- a/services/sesv2/handler_email_templates.go
+++ b/services/sesv2/handler_email_templates.go
@@ -1,10 +1,13 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createEmailTemplateInput struct {
@@ -16,7 +19,7 @@ type createEmailTemplateInput struct {
func (h *Handler) handleCreateEmailTemplate(c *echo.Context) (any, error) {
var in createEmailTemplateInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -54,7 +57,7 @@ type updateEmailTemplateInput struct {
func (h *Handler) handleUpdateEmailTemplate(c *echo.Context, name string) (any, error) {
var in updateEmailTemplateInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -87,7 +90,7 @@ type testRenderEmailTemplateInput struct {
func (h *Handler) handleTestRenderEmailTemplate(c *echo.Context, name string) (any, error) {
var in testRenderEmailTemplateInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_event_destinations.go b/services/sesv2/handler_event_destinations.go
index 607045887c..c5628bedb2 100644
--- a/services/sesv2/handler_event_destinations.go
+++ b/services/sesv2/handler_event_destinations.go
@@ -1,12 +1,15 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/url"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// eventDestinationDefinitionInput mirrors types.EventDestinationDefinition.
@@ -87,7 +90,7 @@ func (h *Handler) handleCreateConfigurationSetEventDestination(
) (any, error) {
var in createConfigurationSetEventDestinationInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -156,7 +159,7 @@ func (h *Handler) handleUpdateConfigurationSetEventDestination(
var in updateConfigurationSetEventDestinationInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_export_jobs.go b/services/sesv2/handler_export_jobs.go
index 0c13805e75..dc7092473a 100644
--- a/services/sesv2/handler_export_jobs.go
+++ b/services/sesv2/handler_export_jobs.go
@@ -1,10 +1,13 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCancelExportJob(jobID string) (any, error) {
@@ -25,8 +28,8 @@ func (h *Handler) handleCancelExportJob(jobID string) (any, error) {
// accepted opaquely via json.RawMessage rather than decoded into a typed
// shape -- only which branch was set is used, to derive ExportSourceType.
type exportDataSourceInput struct {
- MetricsDataSource json.RawMessage `json:"MetricsDataSource,omitempty"`
- MessageInsightsDataSource json.RawMessage `json:"MessageInsightsDataSource,omitempty"`
+ MetricsDataSource jsontext.Value `json:"MetricsDataSource,omitempty"`
+ MessageInsightsDataSource jsontext.Value `json:"MessageInsightsDataSource,omitempty"`
}
// sourceType validates that exactly one branch is set (real SES v2 requires
@@ -65,7 +68,7 @@ type createExportJobInput struct {
func (h *Handler) handleCreateExportJob(c *echo.Context) (any, error) {
var in createExportJobInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_identity_certificates.go b/services/sesv2/handler_identity_certificates.go
index 3a04ad1f3e..1e7a64261f 100644
--- a/services/sesv2/handler_identity_certificates.go
+++ b/services/sesv2/handler_identity_certificates.go
@@ -1,10 +1,13 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type identityCertificateOutput struct {
@@ -19,7 +22,7 @@ type listEmailIdentityCertificatesOutput struct {
}
func decodeBody(c *echo.Context, in any) error {
- if err := json.NewDecoder(c.Request().Body).Decode(in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), in, jsonx.V1()); err != nil {
return fmt.Errorf("%w: invalid request body: %s", ErrInvalidParameter, err.Error())
}
diff --git a/services/sesv2/handler_import_jobs.go b/services/sesv2/handler_import_jobs.go
index c5fa97bcfe..3aa9e24806 100644
--- a/services/sesv2/handler_import_jobs.go
+++ b/services/sesv2/handler_import_jobs.go
@@ -1,10 +1,13 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// importDataSourceInput mirrors types.ImportDataSource. Both members are
@@ -85,7 +88,7 @@ type createImportJobInput struct {
func (h *Handler) handleCreateImportJob(c *echo.Context) (any, error) {
var in createImportJobInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_message_insights.go b/services/sesv2/handler_message_insights.go
index 250dbb23dd..df874d0538 100644
--- a/services/sesv2/handler_message_insights.go
+++ b/services/sesv2/handler_message_insights.go
@@ -1,13 +1,15 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"time"
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type batchGetMetricDataInput struct {
@@ -49,7 +51,7 @@ func epochSecondsToTime(f float64) time.Time {
func (h *Handler) handleBatchGetMetricData(c *echo.Context) (any, error) {
var in batchGetMetricDataInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_multi_region_endpoints.go b/services/sesv2/handler_multi_region_endpoints.go
index d823ca2a58..2a4473dcbd 100644
--- a/services/sesv2/handler_multi_region_endpoints.go
+++ b/services/sesv2/handler_multi_region_endpoints.go
@@ -1,10 +1,13 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// multi-region endpoint handlers
@@ -22,7 +25,7 @@ type createMultiRegionEndpointInput struct {
func (h *Handler) handleCreateMultiRegionEndpoint(c *echo.Context) (any, error) {
var in createMultiRegionEndpointInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_page_size.go b/services/sesv2/handler_page_size.go
index f6b141a8c5..e7b3d82bcc 100644
--- a/services/sesv2/handler_page_size.go
+++ b/services/sesv2/handler_page_size.go
@@ -1,11 +1,14 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strconv"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func queryPageSize(c *echo.Context) int {
@@ -36,7 +39,7 @@ func listRequestParams(c *echo.Context) listParams {
PageSize int `json:"PageSize"`
}
- _ = json.NewDecoder(c.Request().Body).Decode(&in)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1())
return listParams{Filter: in.Filter, NextToken: in.NextToken, PageSize: max(in.PageSize, 0)}
}
diff --git a/services/sesv2/handler_send_email.go b/services/sesv2/handler_send_email.go
index 3cb7ae87e4..4627213428 100644
--- a/services/sesv2/handler_send_email.go
+++ b/services/sesv2/handler_send_email.go
@@ -1,10 +1,13 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type sendEmailInput struct {
@@ -69,7 +72,7 @@ type sendEmailOutput struct {
func (h *Handler) handleSendEmail(c *echo.Context) (any, error) {
var in sendEmailInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidParameter, err.Error())
}
@@ -137,7 +140,7 @@ type sendBulkEmailInput struct {
func (h *Handler) handleSendBulkEmail(c *echo.Context) (any, error) {
var in sendBulkEmailInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
@@ -168,7 +171,7 @@ type sendCustomVerificationEmailInput struct {
func (h *Handler) handleSendCustomVerificationEmail(c *echo.Context) (any, error) {
var in sendCustomVerificationEmailInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_suppression.go b/services/sesv2/handler_suppression.go
index c07f037d17..65c097e61c 100644
--- a/services/sesv2/handler_suppression.go
+++ b/services/sesv2/handler_suppression.go
@@ -1,12 +1,15 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strconv"
"time"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// suppressed destination handlers
@@ -20,7 +23,7 @@ type putSuppressedDestinationInput struct {
func (h *Handler) handlePutSuppressedDestination(c *echo.Context) (any, error) {
var in putSuppressedDestinationInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_tags.go b/services/sesv2/handler_tags.go
index df8a19dd0b..e12af4d07d 100644
--- a/services/sesv2/handler_tags.go
+++ b/services/sesv2/handler_tags.go
@@ -1,11 +1,14 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"sort"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type listTagsOutput struct {
@@ -38,7 +41,7 @@ type tagResourceInput struct {
func (h *Handler) handleTagResource(c *echo.Context) (any, error) {
var in tagResourceInput
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid body", ErrInvalidInput)
}
diff --git a/services/sesv2/handler_tenants.go b/services/sesv2/handler_tenants.go
index 7640713565..78e80d42b4 100644
--- a/services/sesv2/handler_tenants.go
+++ b/services/sesv2/handler_tenants.go
@@ -1,10 +1,13 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Tenant handlers.
@@ -18,7 +21,7 @@ import (
// no {Placeholder} segments for this family at all).
func decodeSESv2Body(c *echo.Context, v any) error {
- if err := json.NewDecoder(c.Request().Body).Decode(v); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), v, jsonx.V1()); err != nil {
return fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/handler_test.go b/services/sesv2/handler_test.go
index f40cc54d34..d2d8b6cbe5 100644
--- a/services/sesv2/handler_test.go
+++ b/services/sesv2/handler_test.go
@@ -2,7 +2,7 @@ package sesv2_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/sesv2"
)
@@ -34,7 +35,7 @@ func doRequest(
var bodyReader *bytes.Reader
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
bodyReader = bytes.NewReader(b)
@@ -366,7 +367,7 @@ func TestSESv2Handler_ErrorResponseIncludesType(t *testing.T) {
assert.Equal(t, http.StatusNotFound, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(
t,
"NotFoundException",
@@ -399,7 +400,7 @@ func TestSESv2Handler_URLEncodedIdentity(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotContains(t, out, "EmailIdentity")
// DELETE using percent-encoded path.
diff --git a/services/sesv2/handler_update_configuration_set.go b/services/sesv2/handler_update_configuration_set.go
index 2d33455ed8..a81d484a9a 100644
--- a/services/sesv2/handler_update_configuration_set.go
+++ b/services/sesv2/handler_update_configuration_set.go
@@ -1,10 +1,13 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// messageSecurityOptions mirrors types.MessageSecurityOptions: SigningScheme is
@@ -55,7 +58,7 @@ func (h *Handler) handleUpdateConfigurationSet(c *echo.Context) (any, error) {
ConfigurationSetName string `json:"ConfigurationSetName"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidParameter, err.Error())
}
diff --git a/services/sesv2/handler_vdm.go b/services/sesv2/handler_vdm.go
index a669266bfe..f9fa3979e3 100644
--- a/services/sesv2/handler_vdm.go
+++ b/services/sesv2/handler_vdm.go
@@ -1,10 +1,13 @@
package sesv2
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handlePutAccountVdmAttributes(c *echo.Context) (any, error) {
@@ -12,7 +15,7 @@ func (h *Handler) handlePutAccountVdmAttributes(c *echo.Context) (any, error) {
VdmAttributes map[string]any `json:"VdmAttributes"`
}
- _ = json.NewDecoder(c.Request().Body).Decode(&in)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1())
if err := h.Backend.PutAccountVdmAttributes(in.VdmAttributes); err != nil {
return nil, err
@@ -32,7 +35,7 @@ func (h *Handler) handlePutConfigurationSetVdmOptions(
} `json:"VdmOptions"`
}
- if err := json.NewDecoder(c.Request().Body).Decode(&in); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &in, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid request body: %s", ErrInvalidInput, err.Error())
}
diff --git a/services/sesv2/import_jobs_test.go b/services/sesv2/import_jobs_test.go
index e27e72f29a..8d948989c2 100644
--- a/services/sesv2/import_jobs_test.go
+++ b/services/sesv2/import_jobs_test.go
@@ -1,12 +1,14 @@
package sesv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestCreateImportJob tests the CreateImportJob operation.
@@ -48,7 +50,7 @@ func TestGetImportJob(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
jobID := createResp["JobId"].(string)
rec := doRequest(t, h, http.MethodGet, "/v2/email/import-jobs/"+jobID, nil)
@@ -175,7 +177,7 @@ func TestCreateImportJobWireShape(t *testing.T) {
}
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
jobID, ok := createResp["JobId"].(string)
require.True(t, ok)
@@ -183,7 +185,7 @@ func TestCreateImportJobWireShape(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
dest, ok := getResp["ImportDestination"].(map[string]any)
require.True(t, ok)
diff --git a/services/sesv2/message_insights_test.go b/services/sesv2/message_insights_test.go
index 61db9bebbd..6fc0528984 100644
--- a/services/sesv2/message_insights_test.go
+++ b/services/sesv2/message_insights_test.go
@@ -2,7 +2,7 @@ package sesv2_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sesv2"
)
@@ -123,7 +124,7 @@ func TestGetMessageInsights(t *testing.T) {
require.Equal(t, http.StatusOK, sendRec.Code)
var sendResp map[string]any
- require.NoError(t, json.Unmarshal(sendRec.Body.Bytes(), &sendResp))
+ require.NoError(t, json.Unmarshal(sendRec.Body.Bytes(), &sendResp, jsonx.V1()))
messageID, _ := sendResp["MessageId"].(string)
require.NotEmpty(t, messageID)
@@ -131,7 +132,7 @@ func TestGetMessageInsights(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, messageID, out["MessageId"])
assert.Equal(t, "sender@example.com", out["FromEmailAddress"])
diff --git a/services/sesv2/multi_region_test.go b/services/sesv2/multi_region_test.go
index 67b72d4a12..21a1f1ce1c 100644
--- a/services/sesv2/multi_region_test.go
+++ b/services/sesv2/multi_region_test.go
@@ -2,7 +2,7 @@ package sesv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sesv2"
)
@@ -38,7 +39,7 @@ func regionCall(t *testing.T, h *sesv2.Handler, region, method, path, body strin
out := map[string]any{}
if rec.Body.Len() > 0 {
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
}
return out
diff --git a/services/sesv2/persistence.go b/services/sesv2/persistence.go
index ee03c7a1a0..9f5118d926 100644
--- a/services/sesv2/persistence.go
+++ b/services/sesv2/persistence.go
@@ -2,9 +2,11 @@ package sesv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -29,7 +31,7 @@ const sesv2SnapshotVersion = 1
// decoding a snapshot from an incompatible (older or newer) build of this
// backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
EmailIdentityPolicies map[string]map[string]string `json:"emailIdentityPolicies"`
ResourceTags map[string]map[string]string `json:"resourceTags"`
MultiRegionEndpoints map[string]map[string]any `json:"multiRegionEndpoints"`
@@ -101,7 +103,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
DeliverabilityDashboardDomains: b.deliverabilityDashboardDomains,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "sesv2: failed to marshal snapshot", "error", err)
diff --git a/services/sesv2/realism_test.go b/services/sesv2/realism_test.go
index 6c46e6a502..0334b717f4 100644
--- a/services/sesv2/realism_test.go
+++ b/services/sesv2/realism_test.go
@@ -1,13 +1,15 @@
package sesv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func errBody(t *testing.T, body []byte) (string, string) {
@@ -17,7 +19,7 @@ func errBody(t *testing.T, body []byte) (string, string) {
Type string `json:"__type"`
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
return out.Type, out.Message
}
diff --git a/services/sesv2/send_email_test.go b/services/sesv2/send_email_test.go
index b9fe48c4c1..c8a8670c9f 100644
--- a/services/sesv2/send_email_test.go
+++ b/services/sesv2/send_email_test.go
@@ -1,7 +1,7 @@
package sesv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sesv2"
)
@@ -408,7 +409,7 @@ func TestSendEmail(t *testing.T) {
if tt.wantCode == http.StatusOK {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["MessageId"])
emails := h.Backend.ListEmails()
diff --git a/services/sesv2/smtp_relay_test.go b/services/sesv2/smtp_relay_test.go
index cebb0f6820..409ee3562c 100644
--- a/services/sesv2/smtp_relay_test.go
+++ b/services/sesv2/smtp_relay_test.go
@@ -3,7 +3,7 @@ package sesv2_test
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"mime"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/smtprelay/smtptest"
"github.com/blackbirdworks/gopherstack/services/sesv2"
@@ -147,7 +148,7 @@ func TestSMTPRelayDelivery(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
sess := nextSession(t, srv)
msg, err := mail.ReadMessage(strings.NewReader(sess.Data))
@@ -195,7 +196,7 @@ func TestSMTPRelayRespectsRejectionAndFailure(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["MessageId"])
_ = nextSession(t, srv)
diff --git a/services/sesv2/store_test.go b/services/sesv2/store_test.go
index dbcca5bfec..748064e0d0 100644
--- a/services/sesv2/store_test.go
+++ b/services/sesv2/store_test.go
@@ -2,7 +2,8 @@ package sesv2_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http/httptest"
"testing"
@@ -14,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/sesv2"
)
@@ -79,7 +81,7 @@ func doReq(
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -110,7 +112,7 @@ func doReqQuery(
var buf bytes.Buffer
if body != nil {
- require.NoError(t, json.NewEncoder(&buf).Encode(body))
+ require.NoError(t, json.MarshalEncode(jsontext.NewEncoder(&buf, jsonx.V1()), body))
}
req := httptest.NewRequest(method, path, &buf)
@@ -140,7 +142,7 @@ func decodeJSON(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&m))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &m, jsonx.V1()))
return m
}
diff --git a/services/sesv2/whitebox_test.go b/services/sesv2/whitebox_test.go
index 20a9888ce3..2a1dd59e84 100644
--- a/services/sesv2/whitebox_test.go
+++ b/services/sesv2/whitebox_test.go
@@ -2,7 +2,7 @@ package sesv2
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"reflect"
@@ -12,6 +12,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// wantCreationTimeTagValue is the tag value newTaggableTenant/
@@ -82,7 +84,7 @@ func TestCreateOpsWithTags_CreationTimeTagsRoundTrip(t *testing.T) {
func doWhiteboxRequest(t *testing.T, h *Handler, method, path string, body any) *httptest.ResponseRecorder {
t.Helper()
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(method, path, bytes.NewReader(b))
diff --git a/services/shield/errors_test.go b/services/shield/errors_test.go
index bb798b64f5..96b7c5c17f 100644
--- a/services/shield/errors_test.go
+++ b/services/shield/errors_test.go
@@ -1,7 +1,7 @@
package shield_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/shield"
)
@@ -20,7 +21,7 @@ func wireErrorType(t *testing.T, body []byte) string {
var env struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(body, &env))
+ require.NoError(t, json.Unmarshal(body, &env, jsonx.V1()))
return env.Type
}
diff --git a/services/shield/handler.go b/services/shield/handler.go
index d95464abcb..a5fdd10c27 100644
--- a/services/shield/handler.go
+++ b/services/shield/handler.go
@@ -3,7 +3,7 @@ package shield
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -394,10 +395,7 @@ func shieldErrorRules() []shieldErrorRule {
// treated the same as InvalidParameterException since they never reach a specific handler's own
// validation. Unrecognized errors fall back to InternalErrorException/500.
func classifyShieldError(err error) (string, int) {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
- if errors.As(err, &syntaxErr) || errors.As(err, &typeErr) {
+ if jsonx.IsSyntaxError(err) || jsonx.IsTypeError(err) {
return codeInvalidParameterException, http.StatusBadRequest
}
@@ -415,7 +413,7 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
payload, _ := json.Marshal(map[string]string{
keyTypeField: code,
keyMessageField: messageWithoutCode(code, err),
- })
+ }, jsonx.V1())
return c.JSONBlob(status, payload)
}
@@ -452,7 +450,7 @@ func decodeOffsetToken(token string) (int, error) {
}
var t offsetToken
- if unmarshalErr := json.Unmarshal(raw, &t); unmarshalErr != nil || t.O < 0 {
+ if unmarshalErr := json.Unmarshal(raw, &t, jsonx.V1()); unmarshalErr != nil || t.O < 0 {
return 0, errInvalidPaginationToken
}
@@ -461,7 +459,7 @@ func decodeOffsetToken(token string) (int, error) {
// encodeOffsetToken encodes an integer cursor as an opaque base64url(JSON) string.
func encodeOffsetToken(offset int) string {
- data, _ := json.Marshal(offsetToken{O: offset})
+ data, _ := json.Marshal(offsetToken{O: offset}, jsonx.V1())
return base64.RawURLEncoding.EncodeToString(data)
}
diff --git a/services/shield/handler_application_layer_automatic_response.go b/services/shield/handler_application_layer_automatic_response.go
index 7803e2a560..8046c95013 100644
--- a/services/shield/handler_application_layer_automatic_response.go
+++ b/services/shield/handler_application_layer_automatic_response.go
@@ -1,8 +1,10 @@
package shield
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// alarAction is the nested action object in ALAR requests.
@@ -31,7 +33,7 @@ func alarActionString(a alarAction) (string, error) {
func (h *Handler) handleEnableApplicationLayerAutomaticResponse(body []byte) error {
var req alarRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -54,7 +56,7 @@ type disableALARRequest struct {
func (h *Handler) handleDisableApplicationLayerAutomaticResponse(body []byte) error {
var req disableALARRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -67,7 +69,7 @@ func (h *Handler) handleDisableApplicationLayerAutomaticResponse(body []byte) er
func (h *Handler) handleUpdateApplicationLayerAutomaticResponse(body []byte) error {
var req alarRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/shield/handler_attacks.go b/services/shield/handler_attacks.go
index 816fb1f272..c364887bf5 100644
--- a/services/shield/handler_attacks.go
+++ b/services/shield/handler_attacks.go
@@ -1,8 +1,10 @@
package shield
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// listAttacksTimeRange mirrors the AWS Shield TimeRange wire format for ListAttacks.
@@ -24,7 +26,7 @@ type listAttacksRequest struct {
func (h *Handler) handleListAttacks(body []byte) ([]byte, error) {
var req listAttacksRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -89,7 +91,7 @@ func (h *Handler) handleListAttacks(body []byte) ([]byte, error) {
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// simulateAttackRequest is the request body for the __SimulateAttack endpoint (gap 25).
@@ -101,7 +103,7 @@ type simulateAttackRequest struct {
// handleSimulateAttack creates a synthetic attack record via the chaos endpoint (gap 25/10).
func (h *Handler) handleSimulateAttack(body []byte) ([]byte, error) {
var req simulateAttackRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -119,7 +121,7 @@ func (h *Handler) handleSimulateAttack(body []byte) ([]byte, error) {
keyResourceArn: attack.ResourceARN,
keyStartTime: floatSeconds(attack.StartTime),
keyEndTime: floatSeconds(attack.EndTime),
- })
+ }, jsonx.V1())
}
// describeAttackRequest is the request body for DescribeAttack.
@@ -129,7 +131,7 @@ type describeAttackRequest struct {
func (h *Handler) handleDescribeAttack(body []byte) ([]byte, error) {
var req describeAttackRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -174,7 +176,7 @@ func (h *Handler) handleDescribeAttack(body []byte) ([]byte, error) {
"AttackCounters": counters,
"Mitigations": mitigations,
},
- })
+ }, jsonx.V1())
}
func (h *Handler) handleDescribeAttackStatistics() ([]byte, error) {
@@ -187,5 +189,5 @@ func (h *Handler) handleDescribeAttackStatistics() ([]byte, error) {
"ToExclusive": float64(stats.TimeRange.ToExclusive),
},
"DataItems": stats.DataItems,
- })
+ }, jsonx.V1())
}
diff --git a/services/shield/handler_attacks_test.go b/services/shield/handler_attacks_test.go
index 1d9da2956c..ba648aa825 100644
--- a/services/shield/handler_attacks_test.go
+++ b/services/shield/handler_attacks_test.go
@@ -1,7 +1,7 @@
package shield_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/shield"
)
@@ -25,7 +26,7 @@ func TestHandler_DescribeAttackStatisticsFlatResponse(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasWrapper := resp["AttackStatistics"]
assert.False(t, hasWrapper, "response must NOT be wrapped in 'AttackStatistics'")
@@ -48,7 +49,7 @@ func TestHandler_DescribeAttackStatisticsTimeRangeIsFloat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tr := resp["TimeRange"].(map[string]any)
@@ -83,7 +84,7 @@ func TestHandler_ListAttacksStartTimeRangeObject(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["AttackSummaries"].([]any)
assert.Len(t, summaries, 1, "StartTime TimeRange object must be accepted")
@@ -107,7 +108,7 @@ func TestHandler_ListAttacksEndTimeRangeObject(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["AttackSummaries"].([]any)
assert.Len(t, summaries, 1, "EndTime TimeRange object must be accepted")
@@ -131,7 +132,7 @@ func TestHandler_ListAttacksEndTimeRangeFiltersOut(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["AttackSummaries"].([]any)
assert.Empty(t, summaries, "attacks after ToExclusive must be excluded")
@@ -157,7 +158,7 @@ func TestHandler_ListAttacksEndTimeExclusiveBoundary(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["AttackSummaries"].([]any)
assert.Empty(t, summaries, "an attack starting exactly at ToExclusive must be excluded, not included")
@@ -181,7 +182,7 @@ func TestHandler_ListAttacksDefaultMaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["AttackSummaries"].([]any)
assert.Len(t, summaries, 20, "omitted MaxResults must default to 20 per the documented default")
@@ -209,7 +210,7 @@ func TestHandler_ListAttacksOpaquePageToken(t *testing.T) {
NextToken string `json:"NextToken"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.NextToken)
offset := opaqueOffset(t, out.NextToken)
@@ -274,7 +275,7 @@ func TestHandler_DescribeAttack(t *testing.T) {
if tt.wantFields {
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
attack, ok := result["Attack"].(map[string]any)
require.True(t, ok)
assert.Equal(t, id, attack["AttackId"])
@@ -323,7 +324,7 @@ func TestHandler_DescribeAttackStatistics(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
items, ok := result["DataItems"].([]any)
require.True(t, ok)
@@ -350,7 +351,7 @@ func TestHandler_AttackRichFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// No "AttackVectors" key: types.AttackDetail (shield@v1.37.4 types/types.go)
// has no such member -- that's AttackSummary's field (ListAttacks), see
@@ -379,7 +380,7 @@ func TestHandler_ListAttacksMultipleARNs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["AttackSummaries"].([]any)
assert.Len(t, summaries, 2)
@@ -404,7 +405,7 @@ func TestHandler_ListAttacksPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["AttackSummaries"].([]any)
assert.Len(t, summaries, 2)
@@ -427,7 +428,7 @@ func TestHandler_DescribeAttackStatisticsWithVolume(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["TimeRange"])
assert.NotNil(t, resp["DataItems"])
@@ -447,7 +448,7 @@ func TestHandler_DescribeAttackStatisticsEmptyWhenNoAttacks(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["DataItems"].([]any)
assert.Len(t, items, 1)
@@ -467,7 +468,7 @@ func TestHandler_AttackTimestampIsFloat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
atk := raw["Attack"].(map[string]any)
st, ok := atk["StartTime"].(float64)
@@ -494,7 +495,7 @@ func TestHandler_SimulateAttackHTTPEndpoint(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["AttackId"])
assert.Equal(t, 1, shield.AttackCount(b))
@@ -533,7 +534,7 @@ func TestHandler_AttackSimulationEndToEnd(t *testing.T) {
require.Equal(t, http.StatusOK, simRec.Code)
var simResp map[string]any
- require.NoError(t, json.Unmarshal(simRec.Body.Bytes(), &simResp))
+ require.NoError(t, json.Unmarshal(simRec.Body.Bytes(), &simResp, jsonx.V1()))
attackID := simResp["AttackId"].(string)
// ListAttacks must show it.
@@ -541,7 +542,7 @@ func TestHandler_AttackSimulationEndToEnd(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
summaries := listResp["AttackSummaries"].([]any)
require.Len(t, summaries, 1)
@@ -550,7 +551,7 @@ func TestHandler_AttackSimulationEndToEnd(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
atk := descResp["Attack"].(map[string]any)
_, hasVectors := atk["AttackVectors"]
assert.False(t, hasVectors, "types.AttackDetail has no AttackVectors member")
@@ -562,7 +563,7 @@ func TestHandler_AttackSimulationEndToEnd(t *testing.T) {
require.Equal(t, http.StatusOK, statsRec.Code)
var statsResp map[string]any
- require.NoError(t, json.Unmarshal(statsRec.Body.Bytes(), &statsResp))
+ require.NoError(t, json.Unmarshal(statsRec.Body.Bytes(), &statsResp, jsonx.V1()))
items := statsResp["DataItems"].([]any)
require.NotEmpty(t, items)
@@ -585,7 +586,7 @@ func TestHandler_ListAttacksIncludesVectors(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["AttackSummaries"].([]any)
require.Len(t, summaries, 1)
diff --git a/services/shield/handler_drt.go b/services/shield/handler_drt.go
index 9483764cc8..5fd8dd55a6 100644
--- a/services/shield/handler_drt.go
+++ b/services/shield/handler_drt.go
@@ -1,8 +1,10 @@
package shield
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// associateDRTLogBucketRequest is the request body for AssociateDRTLogBucket.
@@ -12,7 +14,7 @@ type associateDRTLogBucketRequest struct {
func (h *Handler) handleAssociateDRTLogBucket(body []byte) error {
var req associateDRTLogBucketRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -34,7 +36,7 @@ type disassociateDRTLogBucketRequest struct {
func (h *Handler) handleDisassociateDRTLogBucket(body []byte) error {
var req disassociateDRTLogBucketRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -52,7 +54,7 @@ type associateDRTRoleRequest struct {
func (h *Handler) handleAssociateDRTRole(body []byte) error {
var req associateDRTRoleRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -83,5 +85,5 @@ func (h *Handler) handleDescribeDRTAccess() ([]byte, error) {
resp["RoleArn"] = access.RoleArn
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/shield/handler_drt_test.go b/services/shield/handler_drt_test.go
index b813bc4507..d6abb5cee4 100644
--- a/services/shield/handler_drt_test.go
+++ b/services/shield/handler_drt_test.go
@@ -1,13 +1,14 @@
package shield_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/shield"
)
@@ -150,7 +151,7 @@ func TestHandler_DescribeDRTAccess(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
if tt.wantRoleArn != "" {
assert.Equal(t, tt.wantRoleArn, result["RoleArn"])
diff --git a/services/shield/handler_emergency_contacts.go b/services/shield/handler_emergency_contacts.go
index a6fcb098ae..07b707d3dc 100644
--- a/services/shield/handler_emergency_contacts.go
+++ b/services/shield/handler_emergency_contacts.go
@@ -1,8 +1,10 @@
package shield
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// emergencyContactItem represents a single emergency contact in the API request/response.
@@ -19,7 +21,7 @@ type associateProactiveEngagementRequest struct {
func (h *Handler) handleAssociateProactiveEngagementDetails(body []byte) error {
var req associateProactiveEngagementRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -57,7 +59,7 @@ type updateEmergencyContactSettingsRequest struct {
func (h *Handler) handleUpdateEmergencyContactSettings(body []byte) error {
var req updateEmergencyContactSettingsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -80,5 +82,5 @@ func (h *Handler) handleDescribeEmergencyContactSettings() ([]byte, error) {
return json.Marshal(map[string]any{
"EmergencyContactList": items,
- })
+ }, jsonx.V1())
}
diff --git a/services/shield/handler_health_checks.go b/services/shield/handler_health_checks.go
index 30b61d4f09..5a84873fda 100644
--- a/services/shield/handler_health_checks.go
+++ b/services/shield/handler_health_checks.go
@@ -1,9 +1,11 @@
package shield
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// validateHealthCheckARN checks that the ARN is a Route 53 health check ARN.
@@ -27,7 +29,7 @@ type associateHealthCheckRequest struct {
func (h *Handler) handleAssociateHealthCheck(body []byte) error {
var req associateHealthCheckRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -54,7 +56,7 @@ type disassociateHealthCheckRequest struct {
func (h *Handler) handleDisassociateHealthCheck(body []byte) error {
var req disassociateHealthCheckRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/shield/handler_protection_groups.go b/services/shield/handler_protection_groups.go
index 98bf2ae4c3..24e820c1ee 100644
--- a/services/shield/handler_protection_groups.go
+++ b/services/shield/handler_protection_groups.go
@@ -1,9 +1,10 @@
package shield
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -19,7 +20,7 @@ type createProtectionGroupRequest struct {
func (h *Handler) handleCreateProtectionGroup(body []byte) error {
var req createProtectionGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -50,7 +51,7 @@ type describeProtectionGroupRequest struct {
func (h *Handler) handleDescribeProtectionGroup(body []byte) ([]byte, error) {
var req describeProtectionGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -65,7 +66,7 @@ func (h *Handler) handleDescribeProtectionGroup(body []byte) ([]byte, error) {
return json.Marshal(map[string]any{
"ProtectionGroup": protectionGroupToMap(pg),
- })
+ }, jsonx.V1())
}
// listProtectionGroupsRequest is the request body for ListProtectionGroups.
@@ -83,7 +84,7 @@ type listProtectionGroupsRequest struct {
func (h *Handler) handleListProtectionGroups(body []byte) ([]byte, error) {
var req listProtectionGroupsRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -108,7 +109,7 @@ func (h *Handler) handleListProtectionGroups(body []byte) ([]byte, error) {
}
if start >= len(groups) {
- return json.Marshal(map[string]any{"ProtectionGroups": []map[string]any{}})
+ return json.Marshal(map[string]any{"ProtectionGroups": []map[string]any{}}, jsonx.V1())
}
end := start + maxResults
@@ -134,7 +135,7 @@ func (h *Handler) handleListProtectionGroups(body []byte) ([]byte, error) {
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// protectionGroupMatchesFilters returns true if pg passes all filter sets.
@@ -200,7 +201,7 @@ type updateProtectionGroupRequest struct {
func (h *Handler) handleUpdateProtectionGroup(body []byte) error {
var req updateProtectionGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -246,7 +247,7 @@ type deleteProtectionGroupRequest struct {
func (h *Handler) handleDeleteProtectionGroup(body []byte) error {
var req deleteProtectionGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -266,7 +267,7 @@ type listResourcesInProtectionGroupRequest struct {
func (h *Handler) handleListResourcesInProtectionGroup(body []byte) ([]byte, error) {
var req listResourcesInProtectionGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -287,7 +288,7 @@ func (h *Handler) handleListResourcesInProtectionGroup(body []byte) ([]byte, err
}
if start >= len(arns) {
- return json.Marshal(map[string]any{"ResourceArns": []string{}})
+ return json.Marshal(map[string]any{"ResourceArns": []string{}}, jsonx.V1())
}
end := start + maxResults
@@ -306,5 +307,5 @@ func (h *Handler) handleListResourcesInProtectionGroup(body []byte) ([]byte, err
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/shield/handler_protection_groups_test.go b/services/shield/handler_protection_groups_test.go
index ed5475512b..5dcd3e3a21 100644
--- a/services/shield/handler_protection_groups_test.go
+++ b/services/shield/handler_protection_groups_test.go
@@ -1,7 +1,7 @@
package shield_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/shield"
)
@@ -181,7 +182,7 @@ func TestHandler_ListProtectionGroupsOpaquePageToken(t *testing.T) {
NextToken string `json:"NextToken"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.NextToken)
offset := opaqueOffset(t, out.NextToken)
@@ -213,7 +214,7 @@ func TestHandler_ListProtectionGroupsPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups := resp["ProtectionGroups"].([]any)
assert.Len(t, groups, 2)
@@ -247,7 +248,7 @@ func TestHandler_ListProtectionGroupsDefaultMaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups := resp["ProtectionGroups"].([]any)
assert.Len(t, groups, 20, "omitted MaxResults must default to 20 per the documented default")
@@ -281,7 +282,7 @@ func TestHandler_ListProtectionGroupsInclusionFilterByPattern(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups := resp["ProtectionGroups"].([]any)
assert.Len(t, groups, 1)
@@ -311,7 +312,7 @@ func TestHandler_ListProtectionGroupsInclusionFilterByAggregation(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups := resp["ProtectionGroups"].([]any)
assert.Len(t, groups, 1)
@@ -346,7 +347,7 @@ func TestHandler_ListResourcesInProtectionGroupPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arns := resp["ResourceArns"].([]any)
assert.Len(t, arns, 20, "omitted MaxResults must default to 20 per the documented default")
@@ -363,7 +364,7 @@ func TestHandler_ListResourcesInProtectionGroupPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp2, jsonx.V1()))
assert.Len(t, resp2["ResourceArns"].([]any), 5, "the remaining 5 of 25 members must be on the next page")
assert.NotContains(t, resp2, "NextToken", "no further page should be signaled once all members are returned")
}
diff --git a/services/shield/handler_protections.go b/services/shield/handler_protections.go
index ee5f27a0e1..022985f656 100644
--- a/services/shield/handler_protections.go
+++ b/services/shield/handler_protections.go
@@ -2,10 +2,11 @@ package shield
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -57,7 +58,7 @@ type createProtectionRequest struct {
func (h *Handler) handleCreateProtection(ctx context.Context, body []byte) ([]byte, error) {
var req createProtectionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -89,7 +90,7 @@ func (h *Handler) handleCreateProtection(ctx context.Context, body []byte) ([]by
return json.Marshal(map[string]string{
"ProtectionId": p.ID,
- })
+ }, jsonx.V1())
}
// describeProtectionRequest is the request body for DescribeProtection.
@@ -100,7 +101,7 @@ type describeProtectionRequest struct {
func (h *Handler) handleDescribeProtection(body []byte) ([]byte, error) {
var req describeProtectionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -117,7 +118,7 @@ func (h *Handler) handleDescribeProtection(body []byte) ([]byte, error) {
return json.Marshal(map[string]any{
"Protection": protectionToMap(p, alarCfg),
- })
+ }, jsonx.V1())
}
// deleteProtectionRequest is the request body for DeleteProtection.
@@ -127,7 +128,7 @@ type deleteProtectionRequest struct {
func (h *Handler) handleDeleteProtection(ctx context.Context, body []byte) error {
var req deleteProtectionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -159,7 +160,7 @@ type listProtectionsRequest struct {
func (h *Handler) handleListProtections(body []byte) ([]byte, error) {
var req listProtectionsRequest
if len(body) > 0 {
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
}
@@ -183,7 +184,7 @@ func (h *Handler) handleListProtections(body []byte) ([]byte, error) {
}
if start >= len(protections) {
- return json.Marshal(map[string]any{"Protections": []map[string]any{}})
+ return json.Marshal(map[string]any{"Protections": []map[string]any{}}, jsonx.V1())
}
end := start + maxResults
@@ -210,7 +211,7 @@ func (h *Handler) handleListProtections(body []byte) ([]byte, error) {
resp["NextToken"] = nextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// protectionMatchesFilters returns true if p passes all inclusion filter sets.
diff --git a/services/shield/handler_protections_test.go b/services/shield/handler_protections_test.go
index 1ac0442782..eac841da60 100644
--- a/services/shield/handler_protections_test.go
+++ b/services/shield/handler_protections_test.go
@@ -2,7 +2,7 @@ package shield_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/shield"
)
@@ -78,7 +79,7 @@ func TestHandler_CreateProtection(t *testing.T) {
if tt.wantID {
var result map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
assert.NotEmpty(t, result["ProtectionId"])
}
})
@@ -156,7 +157,7 @@ func TestHandler_DescribeProtection(t *testing.T) {
if tt.wantName != "" {
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
prot, ok := result["Protection"].(map[string]any)
require.True(t, ok)
assert.Equal(t, tt.wantName, prot["Name"])
@@ -256,7 +257,7 @@ func TestHandler_ListProtections(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
protections, ok := result["Protections"].([]any)
require.True(t, ok)
@@ -285,7 +286,7 @@ func TestHandler_ListProtectionsOpaquePageToken(t *testing.T) {
NextToken string `json:"NextToken"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.NotEmpty(t, out.NextToken)
offset := opaqueOffset(t, out.NextToken)
@@ -319,7 +320,7 @@ func TestHandler_ApplyProtectionFiltersNoBacking(t *testing.T) {
Protections []map[string]any `json:"Protections"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &filtered))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &filtered, jsonx.V1()))
assert.Len(t, filtered.Protections, 1)
// Unfiltered list must still return all three.
@@ -330,7 +331,7 @@ func TestHandler_ApplyProtectionFiltersNoBacking(t *testing.T) {
Protections []map[string]any `json:"Protections"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &all))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &all, jsonx.V1()))
assert.Len(t, all.Protections, 3)
}
@@ -351,7 +352,7 @@ func TestHandler_ProtectionResponseIncludesALAR(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
prot := resp["Protection"].(map[string]any)
alarRaw, ok := prot["ApplicationLayerAutomaticResponseConfiguration"]
@@ -378,7 +379,7 @@ func TestHandler_ProtectionWithoutALARHasNoConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
prot := resp["Protection"].(map[string]any)
_, hasALAR := prot["ApplicationLayerAutomaticResponseConfiguration"]
@@ -410,7 +411,7 @@ func TestHandler_ListProtectionsPagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
prots := resp["Protections"].([]any)
assert.Len(t, prots, 2)
@@ -445,7 +446,7 @@ func TestHandler_ListProtectionsDefaultMaxResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
prots := resp["Protections"].([]any)
assert.Len(t, prots, 20, "omitted MaxResults must default to 20 per the documented default")
@@ -475,7 +476,7 @@ func TestHandler_ListProtectionsNextPage(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
token := resp1["NextToken"].(string)
// Second page.
@@ -486,7 +487,7 @@ func TestHandler_ListProtectionsNextPage(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
prots2 := resp2["Protections"].([]any)
assert.Len(t, prots2, 2)
@@ -516,7 +517,7 @@ func TestHandler_ListProtectionsInclusionFilterByARN(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
prots := resp["Protections"].([]any)
assert.Len(t, prots, 2)
@@ -542,7 +543,7 @@ func TestHandler_ListProtectionsInclusionFilterByName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
prots := resp["Protections"].([]any)
assert.Len(t, prots, 1)
@@ -643,7 +644,7 @@ func TestHandler_ALARInListProtections(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
prots := resp["Protections"].([]any)
require.Len(t, prots, 1)
diff --git a/services/shield/handler_subscription.go b/services/shield/handler_subscription.go
index d7d81351a6..af2c76d01b 100644
--- a/services/shield/handler_subscription.go
+++ b/services/shield/handler_subscription.go
@@ -2,12 +2,13 @@ package shield
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -91,7 +92,7 @@ func (h *Handler) handleDescribeSubscription() ([]byte, error) {
"Limits": subscriptionResourceLimits(),
"SubscriptionLimits": subscriptionLimits(),
},
- })
+ }, jsonx.V1())
}
// updateSubscriptionRequest is the request body for UpdateSubscription.
@@ -101,7 +102,7 @@ type updateSubscriptionRequest struct {
func (h *Handler) handleUpdateSubscription(body []byte) error {
var req updateSubscriptionRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -117,7 +118,7 @@ func (h *Handler) handleGetSubscriptionState() ([]byte, error) {
return json.Marshal(map[string]string{
"SubscriptionState": state,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleDeleteSubscription() error {
diff --git a/services/shield/handler_subscription_test.go b/services/shield/handler_subscription_test.go
index 5ed4973029..bd4722540b 100644
--- a/services/shield/handler_subscription_test.go
+++ b/services/shield/handler_subscription_test.go
@@ -1,7 +1,7 @@
package shield_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/shield"
)
@@ -79,7 +80,7 @@ func TestHandler_DescribeSubscription(t *testing.T) {
if tt.wantField != "" {
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
assert.Contains(t, result, tt.wantField)
}
})
@@ -102,7 +103,7 @@ func TestHandler_DescribeSubscription_TimeCommitmentInSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
sub, ok := result["Subscription"].(map[string]any)
require.True(t, ok, "response must include a Subscription object")
@@ -148,7 +149,7 @@ func TestHandler_GetSubscriptionState(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var result map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
assert.Equal(t, tt.wantState, result["SubscriptionState"])
})
}
@@ -202,7 +203,7 @@ func TestHandler_DescribeSubscriptionFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sub := resp["Subscription"].(map[string]any)
assert.NotNil(t, sub["ProactiveEngagementStatus"])
@@ -233,7 +234,7 @@ func TestHandler_SubscriptionLimitsStructure(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sub := resp["Subscription"].(map[string]any)
limits := sub["SubscriptionLimits"].(map[string]any)
@@ -255,7 +256,7 @@ func TestHandler_SubscriptionTimestampIsFloat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
sub := raw["Subscription"].(map[string]any)
st, ok := sub["StartTime"].(float64)
@@ -275,7 +276,7 @@ func TestHandler_SubscriptionArnFormat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sub := resp["Subscription"].(map[string]any)
arn := sub["SubscriptionArn"].(string)
@@ -397,7 +398,7 @@ func TestHandler_UpdateSubscriptionOmitAutoRenewPreservesExistingValue(t *testin
require.Equal(t, 200, rec.Code)
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
sub, ok := result["Subscription"].(map[string]any)
require.True(t, ok)
assert.Equal(t, shield.AutoRenewDisabled, sub["AutoRenew"])
@@ -415,7 +416,7 @@ func TestHandler_DescribeSubscriptionIncludesArn(t *testing.T) {
require.Equal(t, 200, rec.Code)
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
sub, ok := result["Subscription"].(map[string]any)
require.True(t, ok)
assert.Contains(t, sub, "SubscriptionArn")
diff --git a/services/shield/handler_tags.go b/services/shield/handler_tags.go
index fa905d7621..919ef508f9 100644
--- a/services/shield/handler_tags.go
+++ b/services/shield/handler_tags.go
@@ -1,9 +1,10 @@
package shield
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -15,7 +16,7 @@ type tagResourceRequest struct {
func (h *Handler) handleTagResource(body []byte) error {
var req tagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -37,7 +38,7 @@ type listTagsForResourceRequest struct {
func (h *Handler) handleListTagsForResource(body []byte) ([]byte, error) {
var req listTagsForResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -52,7 +53,7 @@ func (h *Handler) handleListTagsForResource(body []byte) ([]byte, error) {
return json.Marshal(map[string]any{
"Tags": tags.MapToKV(tagsMap),
- })
+ }, jsonx.V1())
}
// untagResourceRequest is the request body for UntagResource.
@@ -63,7 +64,7 @@ type untagResourceRequest struct {
func (h *Handler) handleUntagResource(body []byte) error {
var req untagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/shield/handler_test.go b/services/shield/handler_test.go
index bb33f0b518..d39c01996d 100644
--- a/services/shield/handler_test.go
+++ b/services/shield/handler_test.go
@@ -3,7 +3,7 @@ package shield_test
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/shield"
)
@@ -59,7 +60,7 @@ func doShieldRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -89,7 +90,7 @@ func opaqueOffset(t *testing.T, token string) int {
O int `json:"o"`
}
- require.NoError(t, json.Unmarshal(raw, &obj), "token must be JSON with 'o' key")
+ require.NoError(t, json.Unmarshal(raw, &obj, jsonx.V1()), "token must be JSON with 'o' key")
return obj.O
}
diff --git a/services/shield/persistence.go b/services/shield/persistence.go
index 2754518c3d..265d12b363 100644
--- a/services/shield/persistence.go
+++ b/services/shield/persistence.go
@@ -2,7 +2,7 @@ package shield
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -41,15 +41,15 @@ type alarConfigDTO struct {
// incompatible (older or newer) build of this backend as though it were the
// current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Subscription *Subscription `json:"subscription,omitempty"`
- DRTAccess *DRTAccess `json:"drtAccess,omitempty"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- ProactiveEngagementStatus string `json:"proactiveEngagementStatus,omitempty"`
- ALARConfigs []alarConfigDTO `json:"alarConfigs,omitempty"`
- EmergencyContacts []EmergencyContact `json:"emergencyContacts,omitempty"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Subscription *Subscription `json:"subscription,omitempty"`
+ DRTAccess *DRTAccess `json:"drtAccess,omitempty"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ ProactiveEngagementStatus string `json:"proactiveEngagementStatus,omitempty"`
+ ALARConfigs []alarConfigDTO `json:"alarConfigs,omitempty"`
+ EmergencyContacts []EmergencyContact `json:"emergencyContacts,omitempty"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/shield/request_validation_test.go b/services/shield/request_validation_test.go
index cca6e7183e..7fd4065c56 100644
--- a/services/shield/request_validation_test.go
+++ b/services/shield/request_validation_test.go
@@ -1,13 +1,15 @@
package shield_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_RequestValidation(t *testing.T) {
@@ -158,7 +160,7 @@ func TestHandler_RequestValidation(t *testing.T) {
Type string `json:"__type"`
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantCode, out.Type)
assert.Contains(t, out.Message, tt.wantMsg)
})
@@ -215,7 +217,7 @@ func TestHandler_ErrorMessagesOmitCode(t *testing.T) {
Type string `json:"__type"`
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantCode, out.Type)
assert.NotContains(t, out.Message, tt.wantCode)
assert.NotContains(t, out.Message, "invalid request:")
diff --git a/services/signin/handler.go b/services/signin/handler.go
index 58f0f5f6b4..128357d7d2 100644
--- a/services/signin/handler.go
+++ b/services/signin/handler.go
@@ -2,7 +2,7 @@ package signin
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"strconv"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -310,7 +311,7 @@ func decode(body []byte, v any) error {
return nil
}
- if err := json.Unmarshal(body, v); err != nil {
+ if err := json.Unmarshal(body, v, jsonx.V1()); err != nil {
return validation("malformed request body")
}
diff --git a/services/signin/persistence.go b/services/signin/persistence.go
index 119ed24a6c..313a23815a 100644
--- a/services/signin/persistence.go
+++ b/services/signin/persistence.go
@@ -3,7 +3,7 @@ package signin
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -14,11 +14,11 @@ import (
const signinSnapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- SigningKey string `json:"signingKey"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ SigningKey string `json:"signingKey"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/signin/tokens.go b/services/signin/tokens.go
index c5dbefb04b..1ffae56faf 100644
--- a/services/signin/tokens.go
+++ b/services/signin/tokens.go
@@ -7,7 +7,7 @@ import (
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/url"
"strings"
"time"
@@ -15,6 +15,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -107,7 +108,7 @@ func (b *InMemoryBackend) sign(payload string) string {
func (b *InMemoryBackend) makeJWT(claims map[string]any) string {
enc := base64.RawURLEncoding
head := enc.EncodeToString([]byte(`{"alg":"HS256","typ":"JWT"}`))
- raw, _ := json.Marshal(claims)
+ raw, _ := json.Marshal(claims, jsonx.V1())
body := enc.EncodeToString(raw)
return head + "." + body + "." + b.sign(head+"."+body)
@@ -133,7 +134,7 @@ func (b *InMemoryBackend) parseJWT(tok string) (string, bool) {
JTI string `json:"jti"`
}
- if json.Unmarshal(raw, &claims) != nil || claims.JTI == "" {
+ if json.Unmarshal(raw, &claims, jsonx.V1()) != nil || claims.JTI == "" {
return "", false
}
diff --git a/services/sns/archive.go b/services/sns/archive.go
index 722bafa24e..b018033976 100644
--- a/services/sns/archive.go
+++ b/services/sns/archive.go
@@ -1,12 +1,13 @@
package sns
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/events"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// parseReplayFromTimestamp parses the replayFromTimestamp field from a ReplayPolicy JSON string.
@@ -20,7 +21,7 @@ func parseReplayFromTimestamp(replayPolicy string) (time.Time, error) {
ReplayFromTimestamp string `json:"replayFromTimestamp"`
}
- if err := json.Unmarshal([]byte(replayPolicy), &p); err != nil {
+ if err := json.Unmarshal([]byte(replayPolicy), &p, jsonx.V1()); err != nil {
return time.Time{}, fmt.Errorf(
"%w: ReplayPolicy is not valid JSON: %s",
ErrInvalidParameter,
diff --git a/services/sns/archive_test.go b/services/sns/archive_test.go
index 61e4e3a5a0..0383673c3d 100644
--- a/services/sns/archive_test.go
+++ b/services/sns/archive_test.go
@@ -1,7 +1,7 @@
package sns_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"testing"
"testing/synctest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sns"
)
@@ -228,7 +229,7 @@ func TestReplayPolicyTriggersLambdaReplay(t *testing.T) {
// Verify all archived messages were replayed, in original publish order.
for i, invocation := range lambda.All() {
var envelope map[string]any
- require.NoError(t, json.Unmarshal(invocation.Payload, &envelope))
+ require.NoError(t, json.Unmarshal(invocation.Payload, &envelope, jsonx.V1()))
records, _ := envelope["Records"].([]any)
require.Len(t, records, 1)
record, _ := records[0].(map[string]any)
@@ -316,7 +317,7 @@ func TestReplayPolicyDeliversToA2AProtocols(t *testing.T) {
require.Equal(t, 1, lambda.Count(), "lambda function was never invoked")
var envelope map[string]any
- require.NoError(t, json.Unmarshal(lambda.Last().Payload, &envelope))
+ require.NoError(t, json.Unmarshal(lambda.Last().Payload, &envelope, jsonx.V1()))
records, _ := envelope["Records"].([]any)
require.Len(t, records, 1)
record, _ := records[0].(map[string]any)
@@ -345,7 +346,7 @@ func TestReplayPolicyDeliversToA2AProtocols(t *testing.T) {
require.Len(t, firehose.RecordsFor(streamName), 1, "firehose stream received no record")
var envelope map[string]any
- require.NoError(t, json.Unmarshal(firehose.RecordsFor(streamName)[0], &envelope))
+ require.NoError(t, json.Unmarshal(firehose.RecordsFor(streamName)[0], &envelope, jsonx.V1()))
assert.Equal(t, archivedMessage, envelope["Message"])
},
}
diff --git a/services/sns/confirmation.go b/services/sns/confirmation.go
index 67ecd49aa3..2807314728 100644
--- a/services/sns/confirmation.go
+++ b/services/sns/confirmation.go
@@ -5,13 +5,15 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"strings"
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -101,7 +103,7 @@ func (b *InMemoryBackend) deliverSubscriptionConfirmation(
SignatureVersion: version,
Signature: b.signer.signWithVersion(canonical, version),
SigningCertURL: certURL,
- })
+ }, jsonx.V1())
if err != nil {
return
}
diff --git a/services/sns/delivery.go b/services/sns/delivery.go
index 528e3de581..4ee786458b 100644
--- a/services/sns/delivery.go
+++ b/services/sns/delivery.go
@@ -2,11 +2,12 @@ package sns
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -16,7 +17,7 @@ func parseTopicEffectivePolicy(topicEffectivePolicy, protocol string) *int {
NumRetries *int `json:"numRetries"`
} `json:"defaultHealthyRetryPolicy"`
}
- if err := json.Unmarshal([]byte(topicEffectivePolicy), &tp); err == nil {
+ if err := json.Unmarshal([]byte(topicEffectivePolicy), &tp, jsonx.V1()); err == nil {
pKey := protocol
if pKey == protocolHTTPS {
pKey = protocolHTTP
@@ -35,7 +36,7 @@ func parseSubPolicy(subPolicy string) *int {
NumRetries *int `json:"numRetries"`
} `json:"healthyRetryPolicy"`
}
- if err := json.Unmarshal([]byte(subPolicy), &sp); err == nil {
+ if err := json.Unmarshal([]byte(subPolicy), &sp, jsonx.V1()); err == nil {
return sp.HealthyRetryPolicy.NumRetries
}
@@ -183,7 +184,7 @@ func buildHTTPDeliveryPayload(d httpDelivery) string {
env.Subject = d.subject
}
- if enc, err := json.Marshal(env); err == nil {
+ if enc, err := json.Marshal(env, jsonx.V1()); err == nil {
body = string(enc)
}
}
@@ -256,7 +257,7 @@ func sendSubscriptionDLQ(ctx context.Context, d httpDelivery) {
DeadLetterTargetArn string `json:"deadLetterTargetArn"`
}
- if err := json.Unmarshal([]byte(d.redrivePolicy), &policy); err != nil {
+ if err := json.Unmarshal([]byte(d.redrivePolicy), &policy, jsonx.V1()); err != nil {
return
}
diff --git a/services/sns/delivery_test.go b/services/sns/delivery_test.go
index b22939b1cb..86399dfd39 100644
--- a/services/sns/delivery_test.go
+++ b/services/sns/delivery_test.go
@@ -3,7 +3,7 @@ package sns_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"sync"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sns"
)
@@ -259,7 +260,7 @@ func TestHTTPDeliveryFailureSendsToDLQ(t *testing.T) {
redrivePolicy, _ := json.Marshal(map[string]string{
"deadLetterTargetArn": dlqARN,
- })
+ }, jsonx.V1())
err = b.SetSubscriptionAttributes(sub.SubscriptionArn, "RedrivePolicy", string(redrivePolicy))
require.NoError(t, err)
diff --git a/services/sns/filter_match.go b/services/sns/filter_match.go
index 01962ba4ec..e13f5a25ef 100644
--- a/services/sns/filter_match.go
+++ b/services/sns/filter_match.go
@@ -1,10 +1,13 @@
package sns
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// matchesFilterPolicyMessageBody evaluates a parsed filter policy against the
@@ -15,8 +18,8 @@ func matchesFilterPolicyMessageBody(policy parsedFilterPolicy, message string) b
return true
}
- var body map[string]json.RawMessage
- if err := json.Unmarshal([]byte(message), &body); err != nil {
+ var body map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(message), &body, jsonx.V1()); err != nil {
return false
}
@@ -42,9 +45,9 @@ func matchesFilterPolicyMessageBody(policy parsedFilterPolicy, message string) b
// are expanded so the condition is satisfied when ANY element matches, mirroring
// AWS message-body array handling.
func matchesBodyKeyConditions(
- body map[string]json.RawMessage,
+ body map[string]jsontext.Value,
key string,
- conditions []json.RawMessage,
+ conditions []jsontext.Value,
) bool {
if subPolicy, nested := nestedSubPolicy(conditions); nested {
return matchesNestedBody(body[key], subPolicy)
@@ -66,7 +69,7 @@ func matchesBodyKeyConditions(
// matchesNestedBody applies a nested sub-policy to an object value, or to any
// element when the value is an array of objects.
-func matchesNestedBody(raw json.RawMessage, subPolicyJSON string) bool {
+func matchesNestedBody(raw jsontext.Value, subPolicyJSON string) bool {
if len(raw) == 0 {
return false
}
@@ -76,8 +79,8 @@ func matchesNestedBody(raw json.RawMessage, subPolicyJSON string) bool {
return false
}
- var arr []json.RawMessage
- if json.Unmarshal(raw, &arr) == nil {
+ var arr []jsontext.Value
+ if json.Unmarshal(raw, &arr, jsonx.V1()) == nil {
for _, elem := range arr {
if matchesFilterPolicyMessageBody(sub, string(elem)) {
return true
@@ -94,13 +97,13 @@ func matchesNestedBody(raw json.RawMessage, subPolicyJSON string) bool {
// body value: a string, number, or boolean yields one candidate; a JSON array of
// scalars yields one candidate per element. A value that cannot be reduced to a
// scalar yields no candidates (the key is then treated as non-matching).
-func bodyMatchValues(raw json.RawMessage) []string {
+func bodyMatchValues(raw jsontext.Value) []string {
if v, ok := scalarBodyValue(raw); ok {
return []string{v}
}
- var arr []json.RawMessage
- if err := json.Unmarshal(raw, &arr); err == nil {
+ var arr []jsontext.Value
+ if err := json.Unmarshal(raw, &arr, jsonx.V1()); err == nil {
out := make([]string, 0, len(arr))
for _, elem := range arr {
if v, ok := scalarBodyValue(elem); ok {
@@ -116,19 +119,19 @@ func bodyMatchValues(raw json.RawMessage) []string {
// scalarBodyValue converts a single JSON scalar (string, number, or boolean) to
// its string form for filter matching. It reports false for non-scalar values.
-func scalarBodyValue(raw json.RawMessage) (string, bool) {
+func scalarBodyValue(raw jsontext.Value) (string, bool) {
var s string
- if err := json.Unmarshal(raw, &s); err == nil {
+ if err := json.Unmarshal(raw, &s, jsonx.V1()); err == nil {
return s, true
}
- var n json.Number
- if err := json.Unmarshal(raw, &n); err == nil {
+ var n jsonx.Number
+ if err := json.Unmarshal(raw, &n, jsonx.V1()); err == nil {
return n.String(), true
}
var b bool
- if err := json.Unmarshal(raw, &b); err == nil {
+ if err := json.Unmarshal(raw, &b, jsonx.V1()); err == nil {
return strconv.FormatBool(b), true
}
@@ -137,7 +140,7 @@ func scalarBodyValue(raw json.RawMessage) (string, bool) {
// matchesOrBody evaluates a recognised "$or" operator against a JSON message
// body: it returns true when AT LEAST ONE sub-policy fully matches.
-func matchesOrBody(subPolicies []json.RawMessage, message string) bool {
+func matchesOrBody(subPolicies []jsontext.Value, message string) bool {
for _, raw := range subPolicies {
sub, err := parseFilterPolicy(string(raw))
if err != nil {
@@ -183,7 +186,7 @@ func matchesParsedFilterPolicy(policy parsedFilterPolicy, attrs map[string]Messa
// matched independently and the condition is satisfied if ANY element matches
// (OR across elements), mirroring AWS String.Array handling.
func matchesAttributeConditions(
- key string, conditions []json.RawMessage, attrs map[string]MessageAttribute,
+ key string, conditions []jsontext.Value, attrs map[string]MessageAttribute,
) bool {
attr, attrExists := attrs[key]
@@ -208,7 +211,7 @@ func attributeMatchValues(attr MessageAttribute, attrExists bool) []string {
if attr.DataType == "String.Array" {
var elems []string
- if err := json.Unmarshal([]byte(attr.StringValue), &elems); err == nil && len(elems) > 0 {
+ if err := json.Unmarshal([]byte(attr.StringValue), &elems, jsonx.V1()); err == nil && len(elems) > 0 {
return elems
}
}
@@ -218,7 +221,7 @@ func attributeMatchValues(attr MessageAttribute, attrExists bool) []string {
// matchesOrAttributes evaluates a recognised "$or" operator against message
// attributes: it returns true when AT LEAST ONE sub-policy fully matches.
-func matchesOrAttributes(subPolicies []json.RawMessage, attrs map[string]MessageAttribute) bool {
+func matchesOrAttributes(subPolicies []jsontext.Value, attrs map[string]MessageAttribute) bool {
for _, raw := range subPolicies {
sub, err := parseFilterPolicy(string(raw))
if err != nil {
@@ -236,7 +239,7 @@ func matchesOrAttributes(subPolicies []json.RawMessage, attrs map[string]Message
// matchObjectCondition evaluates a single JSON-object SNS filter condition such as
// {"prefix": "order-"}, {"suffix": ".jpg"}, {"anything-but": [...]},
// {"equals-ignore-case": "OrderId"}, {"exists": true}, or {"numeric": [">", 0]}.
-func matchObjectCondition(value string, attrExists bool, obj map[string]json.RawMessage) bool {
+func matchObjectCondition(value string, attrExists bool, obj map[string]jsontext.Value) bool {
// String-operand operators share the same shape: decode a single string
// operand and apply a predicate. They require the attribute to exist.
stringOps := map[string]func(value, operand string) bool{
@@ -254,7 +257,7 @@ func matchObjectCondition(value string, attrExists bool, obj map[string]json.Raw
}
var operand string
- if err := json.Unmarshal(raw, &operand); err != nil {
+ if err := json.Unmarshal(raw, &operand, jsonx.V1()); err != nil {
return false
}
@@ -263,7 +266,7 @@ func matchObjectCondition(value string, attrExists bool, obj map[string]json.Raw
if existsRaw, ok := obj["exists"]; ok {
var existsVal bool
- if err := json.Unmarshal(existsRaw, &existsVal); err == nil {
+ if err := json.Unmarshal(existsRaw, &existsVal, jsonx.V1()); err == nil {
return attrExists == existsVal
}
@@ -355,32 +358,32 @@ func matchCIDR(value, cidr string) bool {
//
// In every case the operator is satisfied only when the attribute exists and the
// value does NOT match the negated condition.
-func matchAnythingBut(value string, attrExists bool, raw json.RawMessage) bool {
+func matchAnythingBut(value string, attrExists bool, raw jsontext.Value) bool {
if !attrExists {
return false
}
// Try as string literal.
var s string
- if errStr := json.Unmarshal(raw, &s); errStr == nil {
+ if errStr := json.Unmarshal(raw, &s, jsonx.V1()); errStr == nil {
return value != s
}
// Try as number literal.
- var n json.Number
- if errNum := json.Unmarshal(raw, &n); errNum == nil {
+ var n jsonx.Number
+ if errNum := json.Unmarshal(raw, &n, jsonx.V1()); errNum == nil {
return value != n.String()
}
// Try as array of literals.
- var arr []json.RawMessage
- if errArr := json.Unmarshal(raw, &arr); errArr == nil {
+ var arr []jsontext.Value
+ if errArr := json.Unmarshal(raw, &arr, jsonx.V1()); errArr == nil {
return matchAnythingButArray(value, arr)
}
// Try as nested operator object: {"anything-but": {"prefix"|"suffix"|...: ...}}.
- var obj map[string]json.RawMessage
- if errObj := json.Unmarshal(raw, &obj); errObj == nil {
+ var obj map[string]jsontext.Value
+ if errObj := json.Unmarshal(raw, &obj, jsonx.V1()); errObj == nil {
return matchAnythingButObject(value, obj)
}
@@ -390,10 +393,10 @@ func matchAnythingBut(value string, attrExists bool, raw json.RawMessage) bool {
// matchAnythingButObject negates a nested string operator inside an
// "anything-but" condition. It returns true when the value does NOT satisfy the
// inner operator.
-func matchAnythingButObject(value string, obj map[string]json.RawMessage) bool {
+func matchAnythingButObject(value string, obj map[string]jsontext.Value) bool {
if prefixRaw, ok := obj["prefix"]; ok {
var prefix string
- if err := json.Unmarshal(prefixRaw, &prefix); err == nil {
+ if err := json.Unmarshal(prefixRaw, &prefix, jsonx.V1()); err == nil {
return !strings.HasPrefix(value, prefix)
}
@@ -402,7 +405,7 @@ func matchAnythingButObject(value string, obj map[string]json.RawMessage) bool {
if suffixRaw, ok := obj["suffix"]; ok {
var suffix string
- if err := json.Unmarshal(suffixRaw, &suffix); err == nil {
+ if err := json.Unmarshal(suffixRaw, &suffix, jsonx.V1()); err == nil {
return !strings.HasSuffix(value, suffix)
}
@@ -411,7 +414,7 @@ func matchAnythingButObject(value string, obj map[string]json.RawMessage) bool {
if eqICaseRaw, ok := obj["equals-ignore-case"]; ok {
var want string
- if err := json.Unmarshal(eqICaseRaw, &want); err == nil {
+ if err := json.Unmarshal(eqICaseRaw, &want, jsonx.V1()); err == nil {
return !strings.EqualFold(value, want)
}
@@ -420,7 +423,7 @@ func matchAnythingButObject(value string, obj map[string]json.RawMessage) bool {
if wildcardRaw, ok := obj["wildcard"]; ok {
var pattern string
- if err := json.Unmarshal(wildcardRaw, &pattern); err == nil {
+ if err := json.Unmarshal(wildcardRaw, &pattern, jsonx.V1()); err == nil {
return !matchWildcard(value, pattern)
}
@@ -431,10 +434,10 @@ func matchAnythingButObject(value string, obj map[string]json.RawMessage) bool {
}
// matchAnythingButArray checks that value does not equal any element in the "anything-but" array.
-func matchAnythingButArray(value string, arr []json.RawMessage) bool {
+func matchAnythingButArray(value string, arr []jsontext.Value) bool {
for _, item := range arr {
var sv string
- if errI := json.Unmarshal(item, &sv); errI == nil {
+ if errI := json.Unmarshal(item, &sv, jsonx.V1()); errI == nil {
if value == sv {
return false
}
@@ -442,8 +445,8 @@ func matchAnythingButArray(value string, arr []json.RawMessage) bool {
continue
}
- var nv json.Number
- if errN := json.Unmarshal(item, &nv); errN == nil && value == nv.String() {
+ var nv jsonx.Number
+ if errN := json.Unmarshal(item, &nv, jsonx.V1()); errN == nil && value == nv.String() {
return false
}
}
@@ -453,25 +456,25 @@ func matchAnythingButArray(value string, arr []json.RawMessage) bool {
// matchNumericCondition evaluates {"numeric": [op, num, ...]} conditions.
// Conditions are pairs of [operator, number] and ALL pairs must be satisfied (AND semantics).
-func matchNumericCondition(value string, raw json.RawMessage) bool {
+func matchNumericCondition(value string, raw jsontext.Value) bool {
valFloat, errParse := strconv.ParseFloat(value, 64)
if errParse != nil {
return false
}
- var conditions []json.RawMessage
- if errUnm := json.Unmarshal(raw, &conditions); errUnm != nil {
+ var conditions []jsontext.Value
+ if errUnm := json.Unmarshal(raw, &conditions, jsonx.V1()); errUnm != nil {
return false
}
for i := 0; i+1 < len(conditions); i += 2 {
var op string
- if errOp := json.Unmarshal(conditions[i], &op); errOp != nil {
+ if errOp := json.Unmarshal(conditions[i], &op, jsonx.V1()); errOp != nil {
return false
}
- var num json.Number
- if errNum := json.Unmarshal(conditions[i+1], &num); errNum != nil {
+ var num jsonx.Number
+ if errNum := json.Unmarshal(conditions[i+1], &num, jsonx.V1()); errNum != nil {
return false
}
@@ -509,22 +512,22 @@ func numericOpMatches(op string, value, threshold float64) bool {
}
}
-func matchCondition(value string, attrExists bool, raw json.RawMessage) bool {
+func matchCondition(value string, attrExists bool, raw jsontext.Value) bool {
// Object conditions: prefix, exists, anything-but, numeric.
- var obj map[string]json.RawMessage
- if err := json.Unmarshal(raw, &obj); err == nil {
+ var obj map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &obj, jsonx.V1()); err == nil {
return matchObjectCondition(value, attrExists, obj)
}
// String exact match — attribute must exist.
var s string
- if err := json.Unmarshal(raw, &s); err == nil {
+ if err := json.Unmarshal(raw, &s, jsonx.V1()); err == nil {
return attrExists && value == s
}
// Number exact match — attribute must exist.
- var n json.Number
- if err := json.Unmarshal(raw, &n); err == nil {
+ var n jsonx.Number
+ if err := json.Unmarshal(raw, &n, jsonx.V1()); err == nil {
return attrExists && value == n.String()
}
@@ -532,7 +535,7 @@ func matchCondition(value string, attrExists bool, raw json.RawMessage) bool {
}
// matchesConditions returns true if value/existence satisfies at least one condition in the list.
-func matchesConditions(value string, attrExists bool, conditions []json.RawMessage) bool {
+func matchesConditions(value string, attrExists bool, conditions []jsontext.Value) bool {
for _, raw := range conditions {
if matchCondition(value, attrExists, raw) {
return true
diff --git a/services/sns/filter_match_test.go b/services/sns/filter_match_test.go
index 5703f82e93..ecda758620 100644
--- a/services/sns/filter_match_test.go
+++ b/services/sns/filter_match_test.go
@@ -1,7 +1,7 @@
package sns_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/url"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sns"
)
@@ -46,7 +47,7 @@ func TestFilterPolicyAnythingButMatchDelivery(t *testing.T) {
select {
case raw := <-received:
var env snsNotificationEnvelope
- if json.Unmarshal([]byte(raw), &env) == nil {
+ if json.Unmarshal([]byte(raw), &env, jsonx.V1()) == nil {
assert.Equal(t, "blue-msg", env.Message)
}
case <-time.After(2 * time.Second):
@@ -127,7 +128,7 @@ func TestFilterPolicyPrefixMatch(t *testing.T) {
select {
case raw := <-received:
var env snsNotificationEnvelope
- if json.Unmarshal([]byte(raw), &env) == nil {
+ if json.Unmarshal([]byte(raw), &env, jsonx.V1()) == nil {
assert.Equal(t, "order-msg", env.Message)
}
case <-time.After(2 * time.Second):
@@ -138,7 +139,7 @@ func TestFilterPolicyPrefixMatch(t *testing.T) {
select {
case raw := <-received:
var env snsNotificationEnvelope
- if json.Unmarshal([]byte(raw), &env) == nil {
+ if json.Unmarshal([]byte(raw), &env, jsonx.V1()) == nil {
t.Fatalf("non-matching message was delivered: %s", env.Message)
}
case <-time.After(300 * time.Millisecond):
@@ -175,7 +176,7 @@ func TestFilterPolicySuffixMatch(t *testing.T) {
select {
case raw := <-received:
var env snsNotificationEnvelope
- if json.Unmarshal([]byte(raw), &env) == nil {
+ if json.Unmarshal([]byte(raw), &env, jsonx.V1()) == nil {
assert.Equal(t, "log-msg", env.Message)
}
case <-time.After(2 * time.Second):
@@ -212,7 +213,7 @@ func TestFilterPolicyEqualsIgnoreCase(t *testing.T) {
select {
case raw := <-received:
var env snsNotificationEnvelope
- if json.Unmarshal([]byte(raw), &env) == nil {
+ if json.Unmarshal([]byte(raw), &env, jsonx.V1()) == nil {
assert.Equal(t, "prod-msg", env.Message)
}
case <-time.After(2 * time.Second):
diff --git a/services/sns/filter_policy.go b/services/sns/filter_policy.go
index 4346c69d49..1b5700f154 100644
--- a/services/sns/filter_policy.go
+++ b/services/sns/filter_policy.go
@@ -1,11 +1,14 @@
package sns
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// parseFilterPolicy parses and validates a FilterPolicy JSON string. It returns
@@ -46,8 +49,8 @@ func parseFilterPolicy(filterPolicy string) (parsedFilterPolicy, error) {
)
}
- var rawPolicy map[string]json.RawMessage
- if err := json.Unmarshal([]byte(filterPolicy), &rawPolicy); err != nil {
+ var rawPolicy map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(filterPolicy), &rawPolicy, jsonx.V1()); err != nil {
return nil, fmt.Errorf(
"%w: FilterPolicy is not valid JSON: %s",
ErrInvalidParameter,
@@ -89,15 +92,15 @@ const nestedPolicyKey = "$nestedPolicy"
// parseFilterPolicyKey validates one top-level FilterPolicy key and returns its
// stored conditions plus how many count toward the total-condition budget.
-func parseFilterPolicyKey(key string, rawConditions json.RawMessage) ([]json.RawMessage, int, error) {
+func parseFilterPolicyKey(key string, rawConditions jsontext.Value) ([]jsontext.Value, int, error) {
if isNestedPolicyValue(rawConditions) {
nested, err := parseNestedFilterPolicy(key, rawConditions)
return nested, 0, err
}
- var conditions []json.RawMessage
- if err := json.Unmarshal(rawConditions, &conditions); err != nil {
+ var conditions []jsontext.Value
+ if err := json.Unmarshal(rawConditions, &conditions, jsonx.V1()); err != nil {
return nil, 0, fmt.Errorf(
"%w: FilterPolicy attribute %q must be a JSON array",
ErrInvalidParameter, key,
@@ -119,7 +122,7 @@ func parseFilterPolicyKey(key string, rawConditions json.RawMessage) ([]json.Raw
return conditions, len(conditions), validateConditionShapes(key, conditions)
}
-func isNestedPolicyValue(raw json.RawMessage) bool {
+func isNestedPolicyValue(raw jsontext.Value) bool {
return strings.HasPrefix(strings.TrimSpace(string(raw)), "{")
}
@@ -134,27 +137,27 @@ func checkConditionBudget(total int) error {
return nil
}
-func parseNestedFilterPolicy(key string, raw json.RawMessage) ([]json.RawMessage, error) {
+func parseNestedFilterPolicy(key string, raw jsontext.Value) ([]jsontext.Value, error) {
if _, err := parseFilterPolicy(string(raw)); err != nil {
return nil, fmt.Errorf("%w (nested key %q)", err, key)
}
- wrapped, err := json.Marshal(map[string]json.RawMessage{nestedPolicyKey: raw})
+ wrapped, err := json.Marshal(map[string]jsontext.Value{nestedPolicyKey: raw}, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("%w: FilterPolicy attribute %q: %s", ErrInvalidParameter, key, err.Error())
}
- return []json.RawMessage{wrapped}, nil
+ return []jsontext.Value{wrapped}, nil
}
// nestedSubPolicy returns the nested sub-policy JSON stored by parseNestedFilterPolicy.
-func nestedSubPolicy(conditions []json.RawMessage) (string, bool) {
+func nestedSubPolicy(conditions []jsontext.Value) (string, bool) {
if len(conditions) != 1 {
return "", false
}
- var wrapper map[string]json.RawMessage
- if err := json.Unmarshal(conditions[0], &wrapper); err != nil {
+ var wrapper map[string]jsontext.Value
+ if err := json.Unmarshal(conditions[0], &wrapper, jsonx.V1()); err != nil {
return "", false
}
@@ -177,14 +180,14 @@ const minOrOperatorElems = 2
// - no element uses a reserved operator keyword as a top-level field name.
//
// When any rule is violated, AWS treats "$or" as an ordinary attribute key.
-func isRecognisedOrOperator(elems []json.RawMessage) bool {
+func isRecognisedOrOperator(elems []jsontext.Value) bool {
if len(elems) < minOrOperatorElems {
return false
}
for _, elem := range elems {
- var obj map[string]json.RawMessage
- if err := json.Unmarshal(elem, &obj); err != nil {
+ var obj map[string]jsontext.Value
+ if err := json.Unmarshal(elem, &obj, jsonx.V1()); err != nil {
return false
}
@@ -206,7 +209,7 @@ func isRecognisedOrOperator(elems []json.RawMessage) bool {
// array, rejecting malformed operators or numeric operands. It returns the total
// number of conditions contained across all sub-policies so the caller can
// enforce the global condition cap.
-func validateOrSubPolicies(elems []json.RawMessage) (int, error) {
+func validateOrSubPolicies(elems []jsontext.Value) (int, error) {
total := 0
for _, elem := range elems {
@@ -244,10 +247,10 @@ var knownFilterPolicyOperators = map[string]struct{}{
// attribute and rejects unknown operator names and malformed numeric operand
// structures. Scalar conditions (plain strings, numbers, booleans, null) and
// known object operators are tolerated as-is and matched lazily at evaluation.
-func validateConditionShapes(key string, conditions []json.RawMessage) error {
+func validateConditionShapes(key string, conditions []jsontext.Value) error {
for _, raw := range conditions {
- var obj map[string]json.RawMessage
- if err := json.Unmarshal(raw, &obj); err != nil {
+ var obj map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &obj, jsonx.V1()); err != nil {
// Scalar conditions (e.g. plain strings) are valid; skip object-only checks.
continue
}
@@ -281,9 +284,9 @@ func validateConditionShapes(key string, conditions []json.RawMessage) error {
// containing a valid IPv4/IPv6 address (bare host route) or CIDR block,
// rejecting it eagerly at Subscribe/SetSubscriptionAttributes time rather
// than letting it silently never match at evaluation (matchCIDR).
-func validateCIDROperand(key string, raw json.RawMessage) error {
+func validateCIDROperand(key string, raw jsontext.Value) error {
var operand string
- if err := json.Unmarshal(raw, &operand); err != nil {
+ if err := json.Unmarshal(raw, &operand, jsonx.V1()); err != nil {
return fmt.Errorf(
"%w: FilterPolicy attribute %q cidr operand must be a string",
ErrInvalidParameter, key,
@@ -313,9 +316,9 @@ func validateCIDROperand(key string, raw json.RawMessage) error {
// validateNumericOperands enforces that a "numeric" condition operand is a JSON
// array of even length where each pair is (operator-string, number).
-func validateNumericOperands(key string, raw json.RawMessage) error {
- var operands []json.RawMessage
- if err := json.Unmarshal(raw, &operands); err != nil {
+func validateNumericOperands(key string, raw jsontext.Value) error {
+ var operands []jsontext.Value
+ if err := json.Unmarshal(raw, &operands, jsonx.V1()); err != nil {
return fmt.Errorf(
"%w: FilterPolicy attribute %q numeric operand must be a JSON array",
ErrInvalidParameter, key,
@@ -338,7 +341,7 @@ func validateNumericOperands(key string, raw json.RawMessage) error {
for i := 0; i+1 < len(operands); i += 2 {
var op string
- if err := json.Unmarshal(operands[i], &op); err != nil {
+ if err := json.Unmarshal(operands[i], &op, jsonx.V1()); err != nil {
return fmt.Errorf(
"%w: FilterPolicy attribute %q numeric operator must be a string",
ErrInvalidParameter, key,
@@ -352,8 +355,8 @@ func validateNumericOperands(key string, raw json.RawMessage) error {
)
}
- var num json.Number
- if err := json.Unmarshal(operands[i+1], &num); err != nil {
+ var num jsonx.Number
+ if err := json.Unmarshal(operands[i+1], &num, jsonx.V1()); err != nil {
return fmt.Errorf(
"%w: FilterPolicy attribute %q numeric threshold must be a number",
ErrInvalidParameter, key,
@@ -378,7 +381,7 @@ func validateRedrivePolicy(policy string) error {
DeadLetterTargetArn string `json:"deadLetterTargetArn"`
}
- if err := json.Unmarshal([]byte(policy), &parsed); err != nil {
+ if err := json.Unmarshal([]byte(policy), &parsed, jsonx.V1()); err != nil {
return fmt.Errorf(
"%w: RedrivePolicy is not valid JSON: %s",
ErrInvalidParameter,
@@ -414,7 +417,7 @@ func (b *InMemoryBackend) checkDLQExists(policy string) error {
}
// Shape is already validated by validateRedrivePolicy, so an unmarshal failure
// here cannot happen for a well-formed policy; ignore it and skip verification.
- _ = json.Unmarshal([]byte(policy), &parsed)
+ _ = json.Unmarshal([]byte(policy), &parsed, jsonx.V1())
if parsed.DeadLetterTargetArn == "" {
return nil
}
diff --git a/services/sns/golden_perf_mixed_test.go b/services/sns/golden_perf_mixed_test.go
index 45d492c204..013aee3493 100644
--- a/services/sns/golden_perf_mixed_test.go
+++ b/services/sns/golden_perf_mixed_test.go
@@ -19,7 +19,7 @@ package sns_test
// To regenerate: SNS_GOLDEN_UPDATE=1 go test -run TestGolden ./services/sns/...
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/url"
@@ -28,6 +28,7 @@ import (
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sns"
)
@@ -109,7 +110,7 @@ func TestSignature_SharedAcrossHTTPSubscribers(t *testing.T) {
select {
case body := <-received:
var env map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &env))
+ require.NoError(t, json.Unmarshal([]byte(body), &env, jsonx.V1()))
envelopes = append(envelopes, env)
case <-time.After(2 * time.Second):
t.Fatal("HTTP delivery did not arrive in time")
diff --git a/services/sns/golden_perf_test.go b/services/sns/golden_perf_test.go
index cd2963a7d3..d714c753b4 100644
--- a/services/sns/golden_perf_test.go
+++ b/services/sns/golden_perf_test.go
@@ -12,7 +12,8 @@ package sns_test
// To regenerate: SNS_GOLDEN_UPDATE=1 go test -run TestGolden ./services/sns/...
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/url"
@@ -24,6 +25,7 @@ import (
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sns"
)
@@ -94,7 +96,7 @@ func redactDeliveredEnvelope(t *testing.T, body []byte) []byte {
t.Helper()
var env map[string]any
- require.NoError(t, json.Unmarshal(body, &env))
+ require.NoError(t, json.Unmarshal(body, &env, jsonx.V1()))
for _, k := range []string{"MessageId", "Timestamp", "Signature", "UnsubscribeURL"} {
if _, ok := env[k]; ok {
@@ -102,7 +104,7 @@ func redactDeliveredEnvelope(t *testing.T, body []byte) []byte {
}
}
- out, err := json.MarshalIndent(env, "", " ")
+ out, err := json.Marshal(env, jsonx.V1(), jsontext.Multiline(true), jsontext.WithIndent(" "))
require.NoError(t, err)
return out
diff --git a/services/sns/handler_test.go b/services/sns/handler_test.go
index 1c563a9279..6fbe6392f7 100644
--- a/services/sns/handler_test.go
+++ b/services/sns/handler_test.go
@@ -2,7 +2,7 @@ package sns_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sns"
)
@@ -70,7 +71,7 @@ func extractSNSHTTPMessage(body string) string {
Message string `json:"Message"`
Type string `json:"Type"`
}
- if err := json.Unmarshal([]byte(body), &env); err == nil && env.Type == "Notification" {
+ if err := json.Unmarshal([]byte(body), &env, jsonx.V1()); err == nil && env.Type == "Notification" {
return env.Message
}
diff --git a/services/sns/lambda_firehose_delivery.go b/services/sns/lambda_firehose_delivery.go
index 0d146e9756..e10a0ee433 100644
--- a/services/sns/lambda_firehose_delivery.go
+++ b/services/sns/lambda_firehose_delivery.go
@@ -2,13 +2,14 @@ package sns
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"strings"
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/apptrace"
"github.com/blackbirdworks/gopherstack/pkgs/events"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/roleauth"
)
@@ -87,7 +88,7 @@ func buildLambdaPayload(
},
}
- payload, err := json.Marshal(map[string]any{"Records": []any{record}})
+ payload, err := json.Marshal(map[string]any{"Records": []any{record}}, jsonx.V1())
if err != nil {
return []byte(`{"Records":[]}`)
}
@@ -115,7 +116,7 @@ func buildFirehoseEnvelope(ev *events.SNSPublishedEvent, sub events.SNSSubscript
env.Subject = ev.Subject
}
- enc, err := json.Marshal(env)
+ enc, err := json.Marshal(env, jsonx.V1())
if err != nil {
return []byte(sub.Body(ev.Message))
}
@@ -357,7 +358,12 @@ func sendLambdaDLQ(ctx context.Context, sender SQSSender, redrivePolicy, body st
var policy struct {
DeadLetterTargetArn string `json:"deadLetterTargetArn"`
}
- if err := json.Unmarshal([]byte(redrivePolicy), &policy); err != nil || policy.DeadLetterTargetArn == "" {
+ if err := json.Unmarshal(
+ []byte(redrivePolicy),
+ &policy,
+ jsonx.V1(),
+ ); err != nil ||
+ policy.DeadLetterTargetArn == "" {
return
}
_ = sender.SendMessageToQueue(ctx, policy.DeadLetterTargetArn, body)
diff --git a/services/sns/lambda_firehose_delivery_test.go b/services/sns/lambda_firehose_delivery_test.go
index e901472d7a..56e380f843 100644
--- a/services/sns/lambda_firehose_delivery_test.go
+++ b/services/sns/lambda_firehose_delivery_test.go
@@ -2,7 +2,7 @@ package sns_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/url"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sns"
)
@@ -149,7 +150,7 @@ func TestSNS_LambdaDelivery_InvokesFunction(t *testing.T) {
// Verify payload is valid AWS SNS → Lambda envelope.
var envelope map[string]any
- err = json.Unmarshal(inv.Payload, &envelope)
+ err = json.Unmarshal(inv.Payload, &envelope, jsonx.V1())
require.NoError(t, err)
records, ok := envelope["Records"].([]any)
@@ -255,7 +256,7 @@ func TestSNS_FirehoseDelivery_PutsRecord(t *testing.T) {
// subscription created above never sets it), so the delivered record is the
// full SNS Notification JSON envelope, not the bare message body.
var envelope map[string]any
- require.NoError(t, json.Unmarshal(records[0], &envelope))
+ require.NoError(t, json.Unmarshal(records[0], &envelope, jsonx.V1()))
assert.Equal(t, "Notification", envelope["Type"])
assert.Equal(t, "firehose-message", envelope["Message"])
assert.Equal(t, topicARN, envelope["TopicArn"])
@@ -501,7 +502,7 @@ func TestFirehoseDeliveryFailureSendsToDLQ(t *testing.T) {
redrivePolicy, _ := json.Marshal(map[string]string{
"deadLetterTargetArn": dlqARN,
- })
+ }, jsonx.V1())
err = b.SetSubscriptionAttributes(sub.SubscriptionArn, "RedrivePolicy", string(redrivePolicy))
require.NoError(t, err)
@@ -534,7 +535,7 @@ func TestFirehoseDeliveryFailureNoSQSSenderNoError(t *testing.T) {
redrivePolicy, _ := json.Marshal(map[string]string{
"deadLetterTargetArn": "arn:aws:sqs:us-east-1:123456789012:dlq",
- })
+ }, jsonx.V1())
err = b.SetSubscriptionAttributes(sub.SubscriptionArn, "RedrivePolicy", string(redrivePolicy))
require.NoError(t, err)
@@ -623,7 +624,7 @@ func TestLambdaDeliveryFailureSendsToDLQ(t *testing.T) {
redrivePolicy, _ := json.Marshal(map[string]string{
"deadLetterTargetArn": dlqARN,
- })
+ }, jsonx.V1())
err = b.SetSubscriptionAttributes(sub.SubscriptionArn, "RedrivePolicy", string(redrivePolicy))
require.NoError(t, err)
@@ -656,7 +657,7 @@ func TestLambdaDeliveryFailureNoSenderNoError(t *testing.T) {
redrivePolicy, _ := json.Marshal(map[string]string{
"deadLetterTargetArn": "arn:aws:sqs:us-east-1:123456789012:dlq",
- })
+ }, jsonx.V1())
err = b.SetSubscriptionAttributes(sub.SubscriptionArn, "RedrivePolicy", string(redrivePolicy))
require.NoError(t, err)
diff --git a/services/sns/message_structure_protocols_test.go b/services/sns/message_structure_protocols_test.go
index a8af37806e..8cd7ed84f0 100644
--- a/services/sns/message_structure_protocols_test.go
+++ b/services/sns/message_structure_protocols_test.go
@@ -1,7 +1,7 @@
package sns_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"testing/synctest"
"time"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/events"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sns"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -86,7 +87,7 @@ func receiveBody(t *testing.T, sqsClient *sqssdk.Client, queueURL string, raw bo
Message string `json:"Message"`
}
- require.NoError(t, json.Unmarshal([]byte(body), &env))
+ require.NoError(t, json.Unmarshal([]byte(body), &env, jsonx.V1()))
return env.Message
}
@@ -222,7 +223,7 @@ func TestMessageStructureJSON_AllProtocols(t *testing.T) {
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(lambda.Last().Payload, &lenv))
+ require.NoError(t, json.Unmarshal(lambda.Last().Payload, &lenv, jsonx.V1()))
require.Len(t, lenv.Records, 1)
assert.Equal(t, tt.want["lambda"], lenv.Records[0].Sns.Message)
@@ -237,7 +238,7 @@ func TestMessageStructureJSON_AllProtocols(t *testing.T) {
Message string `json:"Message"`
}
- require.NoError(t, json.Unmarshal(envRecs[0], &fenv))
+ require.NoError(t, json.Unmarshal(envRecs[0], &fenv, jsonx.V1()))
assert.Equal(t, tt.want["firehose-env"], fenv.Message)
got := map[string]string{}
@@ -353,7 +354,7 @@ func TestMessageStructureJSON_Replay(t *testing.T) {
}
require.Equal(t, 1, lambda.Count())
- require.NoError(t, json.Unmarshal(lambda.Last().Payload, &env))
+ require.NoError(t, json.Unmarshal(lambda.Last().Payload, &env, jsonx.V1()))
assert.Equal(t, tt.want, env.Records[0].Sns.Message)
})
})
diff --git a/services/sns/metrics.go b/services/sns/metrics.go
index 3d34bed3e1..31885ed144 100644
--- a/services/sns/metrics.go
+++ b/services/sns/metrics.go
@@ -1,10 +1,12 @@
package sns
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/cwmetric"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// AWS/SNS metrics per docs.aws.amazon.com/sns/latest/dg/sns-monitoring-using-cloudwatch.html (dimension TopicName).
@@ -64,7 +66,7 @@ func classifyAttributeRejection(attrs map[string]MessageAttribute) filterRejecti
}
var elems []any
- if json.Unmarshal([]byte(a.StringValue), &elems) != nil {
+ if json.Unmarshal([]byte(a.StringValue), &elems, jsonx.V1()) != nil {
return rejectInvalidAttributes
}
}
@@ -74,8 +76,8 @@ func classifyAttributeRejection(attrs map[string]MessageAttribute) filterRejecti
// classifyBodyRejection names why message failed a body-scope filter policy.
func classifyBodyRejection(message string) filterRejection {
- var body map[string]json.RawMessage
- if json.Unmarshal([]byte(message), &body) != nil {
+ var body map[string]jsontext.Value
+ if json.Unmarshal([]byte(message), &body, jsonx.V1()) != nil {
return rejectInvalidBody
}
diff --git a/services/sns/models.go b/services/sns/models.go
index 33d9d2e090..0b1a76991d 100644
--- a/services/sns/models.go
+++ b/services/sns/models.go
@@ -3,7 +3,7 @@ package sns
import (
"context"
"crypto/rsa"
- "encoding/json"
+ "encoding/json/jsontext"
"encoding/xml"
"net/http"
"sync"
@@ -718,7 +718,7 @@ type publishTargets struct {
filteredOut filteredCounts
}
-type parsedFilterPolicy map[string][]json.RawMessage
+type parsedFilterPolicy map[string][]jsontext.Value
// snsHTTPNotification is the SNS notification JSON envelope sent to HTTP/HTTPS subscribers.
// When RawMessageDelivery is false, this struct is serialised as the POST body.
diff --git a/services/sns/persistence.go b/services/sns/persistence.go
index ac866341bc..c7e92a5cdf 100644
--- a/services/sns/persistence.go
+++ b/services/sns/persistence.go
@@ -2,7 +2,7 @@ package sns
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -32,7 +32,7 @@ const snsSnapshotVersion = 1
// type is not a pure keyed identity or is slice/scalar-valued -- see the
// comment on registerAllTables in store_setup.go for why each one.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
TopicTags map[string]*svcTags.Tags `json:"topicTags"`
OptedOutPhoneNumbers map[string]bool `json:"optedOutPhoneNumbers,omitempty"`
SMSAttributes map[string]string `json:"smsAttributes,omitempty"`
diff --git a/services/sns/platform_endpoints.go b/services/sns/platform_endpoints.go
index a9328c33b9..b1b7d80b9f 100644
--- a/services/sns/platform_endpoints.go
+++ b/services/sns/platform_endpoints.go
@@ -1,7 +1,7 @@
package sns
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -11,6 +11,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// CreatePlatformEndpoint registers a device token as an endpoint for a platform application.
@@ -267,6 +268,6 @@ func (b *InMemoryBackend) fireEndpointEvent(appArn, eventAttr string, payload ma
return
}
- msg, _ := json.Marshal(payload)
+ msg, _ := json.Marshal(payload, jsonx.V1())
_, _ = b.Publish(topicArn, string(msg), "", "", nil)
}
diff --git a/services/sns/publish.go b/services/sns/publish.go
index 9694d5b40a..ec4595e797 100644
--- a/services/sns/publish.go
+++ b/services/sns/publish.go
@@ -2,7 +2,7 @@ package sns
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/apptrace"
"github.com/blackbirdworks/gopherstack/pkgs/events"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// buildMessageResolver returns a function that picks the correct message body for a given protocol,
@@ -152,7 +153,7 @@ func validateStructuredMessage(message, messageStructure string) error {
}
var pm map[string]string
- if err := json.Unmarshal([]byte(message), &pm); err != nil {
+ if err := json.Unmarshal([]byte(message), &pm, jsonx.V1()); err != nil {
return fmt.Errorf(
"%w: Invalid JSON in Message when MessageStructure is json: %s",
ErrInvalidParameter,
@@ -179,7 +180,7 @@ func parsePerProtocolMessages(message, messageStructure string) map[string]strin
}
var pm map[string]string
- if err := json.Unmarshal([]byte(message), &pm); err != nil {
+ if err := json.Unmarshal([]byte(message), &pm, jsonx.V1()); err != nil {
return nil
}
diff --git a/services/sns/publish_test.go b/services/sns/publish_test.go
index 249191fce2..b450eb51bb 100644
--- a/services/sns/publish_test.go
+++ b/services/sns/publish_test.go
@@ -1,7 +1,7 @@
package sns_test
import (
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"fmt"
"io"
@@ -18,6 +18,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sns"
)
@@ -742,7 +743,7 @@ func TestBatchMessageStructureJSON(t *testing.T) {
case raw := <-received:
// The delivered body should be the http-protocol-specific value.
var env snsNotificationEnvelope
- if json.Unmarshal([]byte(raw), &env) == nil {
+ if json.Unmarshal([]byte(raw), &env, jsonx.V1()) == nil {
assert.Equal(t, "http-specific", env.Message)
} else {
assert.Equal(t, "http-specific", raw)
@@ -784,7 +785,7 @@ func TestBatchMessageStructureDefault(t *testing.T) {
select {
case raw := <-received:
var env snsNotificationEnvelope
- if json.Unmarshal([]byte(raw), &env) == nil {
+ if json.Unmarshal([]byte(raw), &env, jsonx.V1()) == nil {
assert.Equal(t, "plain-text", env.Message)
} else {
assert.Equal(t, "plain-text", raw)
@@ -988,7 +989,7 @@ func TestMessageStructureJSONSinglePublish(t *testing.T) {
select {
case raw := <-received:
var env snsNotificationEnvelope
- if json.Unmarshal([]byte(raw), &env) == nil {
+ if json.Unmarshal([]byte(raw), &env, jsonx.V1()) == nil {
assert.Equal(t, "http-only", env.Message)
}
case <-time.After(2 * time.Second):
diff --git a/services/sns/realism_sdk_test.go b/services/sns/realism_sdk_test.go
index d5fdd1b650..843c02ebe1 100644
--- a/services/sns/realism_sdk_test.go
+++ b/services/sns/realism_sdk_test.go
@@ -1,7 +1,7 @@
package sns_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/sns"
)
@@ -298,7 +299,7 @@ func newConfirmationEndpoint(out chan<- map[string]string) *httptest.Server {
body, _ := io.ReadAll(r.Body)
var m map[string]string
- if json.Unmarshal(body, &m) == nil && m["Type"] == "SubscriptionConfirmation" {
+ if json.Unmarshal(body, &m, jsonx.V1()) == nil && m["Type"] == "SubscriptionConfirmation" {
select {
case out <- m:
default:
diff --git a/services/sns/signing_channels_test.go b/services/sns/signing_channels_test.go
index 89b691e275..4c2336e28e 100644
--- a/services/sns/signing_channels_test.go
+++ b/services/sns/signing_channels_test.go
@@ -7,7 +7,7 @@ import (
"crypto/sha1"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"sync"
@@ -18,6 +18,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/events"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sns"
)
@@ -66,7 +67,7 @@ func TestPublishSignaturesVerifyOnEveryChannel(t *testing.T) {
body, _ := io.ReadAll(r.Body)
var env snsNotificationEnvelope
- if json.Unmarshal(body, &env) == nil {
+ if json.Unmarshal(body, &env, jsonx.V1()) == nil {
httpEnvs <- env
}
diff --git a/services/sns/signing_test.go b/services/sns/signing_test.go
index 3af027acd3..a86ebae249 100644
--- a/services/sns/signing_test.go
+++ b/services/sns/signing_test.go
@@ -7,7 +7,7 @@ import (
"crypto/sha256"
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"io"
"net/http"
@@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sns"
)
@@ -76,7 +77,7 @@ func parseNotificationEnvelope(t *testing.T, body string) snsNotificationEnvelop
t.Helper()
var env snsNotificationEnvelope
- require.NoError(t, json.Unmarshal([]byte(body), &env), "invalid SNS notification envelope")
+ require.NoError(t, json.Unmarshal([]byte(body), &env, jsonx.V1()), "invalid SNS notification envelope")
return env
}
diff --git a/services/sns/subscription_attributes_test.go b/services/sns/subscription_attributes_test.go
index a81f372ff7..10e8636f66 100644
--- a/services/sns/subscription_attributes_test.go
+++ b/services/sns/subscription_attributes_test.go
@@ -2,7 +2,7 @@ package sns_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/url"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sns"
)
@@ -300,7 +301,7 @@ func TestSetSubscriptionAttributesDLQExistenceCheck(t *testing.T) {
redrivePolicy, _ := json.Marshal(map[string]string{
"deadLetterTargetArn": tt.dlqARN,
- })
+ }, jsonx.V1())
err = b.SetSubscriptionAttributes(sub.SubscriptionArn, "RedrivePolicy", string(redrivePolicy))
if tt.wantErr {
@@ -327,7 +328,7 @@ func TestSetSubscriptionAttributesDLQNoCheckerSkips(t *testing.T) {
redrivePolicy, _ := json.Marshal(map[string]string{
"deadLetterTargetArn": "arn:aws:sqs:us-east-1:123456789012:any-dlq",
- })
+ }, jsonx.V1())
err = b.SetSubscriptionAttributes(sub.SubscriptionArn, "RedrivePolicy", string(redrivePolicy))
require.NoError(t, err, "no checker means existence check is skipped")
}
diff --git a/services/sns/topic_attributes.go b/services/sns/topic_attributes.go
index 4bd19e0e2d..229a164028 100644
--- a/services/sns/topic_attributes.go
+++ b/services/sns/topic_attributes.go
@@ -1,13 +1,16 @@
package sns
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"regexp"
"sort"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// GetTopicAttributes returns the attributes of a topic.
@@ -89,9 +92,9 @@ func (b *InMemoryBackend) GetTopicAttributes(topicArn string) (map[string]string
// policyDocument mirrors the shape of an SNS access-control policy document
// (docs.aws.amazon.com/sns/latest/dg/sns-access-policy-use-cases.html).
type policyDocument struct {
- Version string `json:"Version"`
- ID string `json:"Id,omitempty"`
- Statement []json.RawMessage `json:"Statement"`
+ Version string `json:"Version"`
+ ID string `json:"Id,omitempty"`
+ Statement []jsontext.Value `json:"Statement"`
}
// policyStatement is a single Allow statement built from an AddPermission grant.
@@ -120,7 +123,7 @@ func mergePermissionsIntoPolicy(basePolicy, topicArn string, perms map[string]*T
}
var doc policyDocument
- if err := json.Unmarshal([]byte(basePolicy), &doc); err != nil || doc.Version == "" {
+ if err := json.Unmarshal([]byte(basePolicy), &doc, jsonx.V1()); err != nil || doc.Version == "" {
doc.Version = "2012-10-17"
}
@@ -131,13 +134,13 @@ func mergePermissionsIntoPolicy(basePolicy, topicArn string, perms map[string]*T
sort.Strings(labels)
- kept := make([]json.RawMessage, 0, len(doc.Statement))
+ kept := make([]jsontext.Value, 0, len(doc.Statement))
for _, raw := range doc.Statement {
var sid struct {
Sid string `json:"Sid"`
}
- if err := json.Unmarshal(raw, &sid); err == nil {
+ if err := json.Unmarshal(raw, &sid, jsonx.V1()); err == nil {
if _, isPermission := perms[sid.Sid]; isPermission {
continue
}
@@ -147,7 +150,7 @@ func mergePermissionsIntoPolicy(basePolicy, topicArn string, perms map[string]*T
}
for _, label := range labels {
- raw, err := json.Marshal(permissionStatement(topicArn, perms[label]))
+ raw, err := json.Marshal(permissionStatement(topicArn, perms[label]), jsonx.V1())
if err != nil {
continue
}
@@ -157,7 +160,7 @@ func mergePermissionsIntoPolicy(basePolicy, topicArn string, perms map[string]*T
doc.Statement = kept
- out, err := json.Marshal(doc)
+ out, err := json.Marshal(doc, jsonx.V1())
if err != nil {
return basePolicy
}
@@ -478,8 +481,8 @@ func validateDataProtectionPolicy(policy string) error {
)
}
- var doc map[string]json.RawMessage
- if err := json.Unmarshal([]byte(policy), &doc); err != nil {
+ var doc map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(policy), &doc, jsonx.V1()); err != nil {
return fmt.Errorf("%w: DataProtectionPolicy must be a valid JSON object", ErrInvalidParameter)
}
diff --git a/services/sqs/bench_protocol_test.go b/services/sqs/bench_protocol_test.go
index 59280edf57..abc316e193 100644
--- a/services/sqs/bench_protocol_test.go
+++ b/services/sqs/bench_protocol_test.go
@@ -2,7 +2,7 @@ package sqs_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"net/url"
@@ -13,6 +13,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -82,7 +83,7 @@ func jsonSendBody(b *testing.B, qURL string) []byte {
"attr-one": map[string]any{"DataType": "String", "StringValue": "value-one"},
"attr-two": map[string]any{"DataType": "Number", "StringValue": "42"},
},
- })
+ }, jsonx.V1())
if err != nil {
b.Fatalf("marshal send body: %v", err)
}
@@ -144,7 +145,7 @@ func BenchmarkJSONSendMessageBatch10(b *testing.B) {
}
}
- body, err := json.Marshal(map[string]any{"QueueUrl": qURL, "Entries": entries})
+ body, err := json.Marshal(map[string]any{"QueueUrl": qURL, "Entries": entries}, jsonx.V1())
if err != nil {
b.Fatalf("marshal batch body: %v", err)
}
@@ -196,7 +197,7 @@ func BenchmarkJSONReceiveMessage10_Depth10000(b *testing.B) {
h := sqs.NewHandler(backend)
qURL := setupReceiveDepthQueue(b, backend, "bench-json-recv-q")
- body, err := json.Marshal(map[string]any{"QueueUrl": qURL, "MaxNumberOfMessages": 10})
+ body, err := json.Marshal(map[string]any{"QueueUrl": qURL, "MaxNumberOfMessages": 10}, jsonx.V1())
if err != nil {
b.Fatalf("marshal receive body: %v", err)
}
@@ -271,7 +272,7 @@ func BenchmarkJSONReceiveMessage10_FIFOManyGroups(b *testing.B) {
h := sqs.NewHandler(backend)
qURL := setupFIFOManyGroups(b, backend, "bench-json-fifo-recv-q", 1000, 10)
- body, err := json.Marshal(map[string]any{"QueueUrl": qURL, "MaxNumberOfMessages": 10})
+ body, err := json.Marshal(map[string]any{"QueueUrl": qURL, "MaxNumberOfMessages": 10}, jsonx.V1())
if err != nil {
b.Fatalf("marshal receive body: %v", err)
}
@@ -332,7 +333,7 @@ func BenchmarkJSONDeleteMessage(b *testing.B) {
bodies := make([][]byte, len(handles))
for i, rh := range handles {
- body, err := json.Marshal(map[string]any{"QueueUrl": qURL, "ReceiptHandle": rh})
+ body, err := json.Marshal(map[string]any{"QueueUrl": qURL, "ReceiptHandle": rh}, jsonx.V1())
if err != nil {
b.Fatalf("marshal delete body: %v", err)
}
@@ -385,7 +386,7 @@ func BenchmarkJSONDeleteMessageBatch10(b *testing.B) {
for i, m := range recv.Messages {
entries[i] = map[string]any{"Id": strconv.Itoa(i), "ReceiptHandle": m.ReceiptHandle}
}
- body, err := json.Marshal(map[string]any{"QueueUrl": qURL, "Entries": entries})
+ body, err := json.Marshal(map[string]any{"QueueUrl": qURL, "Entries": entries}, jsonx.V1())
if err != nil {
b.Fatalf("marshal delete batch body: %v", err)
}
diff --git a/services/sqs/checksums_test.go b/services/sqs/checksums_test.go
index 38dfd87a18..450f01380b 100644
--- a/services/sqs/checksums_test.go
+++ b/services/sqs/checksums_test.go
@@ -1,12 +1,13 @@
package sqs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -190,7 +191,7 @@ func TestComputeMD5OfMessageAttributes_FullSetUsesPrecomputed(t *testing.T) {
require.Equal(t, 200, recvRec.Code)
var recvResp recvResponse
- require.NoError(t, json.Unmarshal(recvRec.Body.Bytes(), &recvResp))
+ require.NoError(t, json.Unmarshal(recvRec.Body.Bytes(), &recvResp, jsonx.V1()))
require.Len(t, recvResp.Messages, 1)
// The MD5 must be non-empty when attributes are returned.
@@ -261,7 +262,7 @@ func TestReceiveMessage_MD5Consistency(t *testing.T) {
require.Equal(t, 200, recvRec.Code)
var recvResp recvMD5Only
- require.NoError(t, json.Unmarshal(recvRec.Body.Bytes(), &recvResp))
+ require.NoError(t, json.Unmarshal(recvRec.Body.Bytes(), &recvResp, jsonx.V1()))
require.Len(t, recvResp.Messages, 1)
assert.Equal(t, tt.wantMD5, recvResp.Messages[0].MD5OfMessageAttributes,
diff --git a/services/sqs/dead_letter.go b/services/sqs/dead_letter.go
index bdc508a915..02b8e48cf9 100644
--- a/services/sqs/dead_letter.go
+++ b/services/sqs/dead_letter.go
@@ -1,20 +1,21 @@
package sqs
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"slices"
"sort"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
// redrivePolicy represents the JSON structure of an SQS RedrivePolicy attribute.
type redrivePolicy struct {
- DeadLetterTargetArn string `json:"deadLetterTargetArn"`
- MaxReceiveCount json.Number `json:"maxReceiveCount"`
+ DeadLetterTargetArn string `json:"deadLetterTargetArn"`
+ MaxReceiveCount jsonx.Number `json:"maxReceiveCount"`
}
// applyRedrivePolicy parses the RedrivePolicy attribute and wires up DLQ fields on q.
@@ -32,11 +33,11 @@ func applyRedrivePolicy(q *Queue, attrs map[string]string, backend *InMemoryBack
var pol redrivePolicy
- if err := json.Unmarshal([]byte(raw), &pol); err != nil {
+ if err := json.Unmarshal([]byte(raw), &pol, jsonx.V1()); err != nil {
return &InvalidParameterError{Message: "Invalid value for the parameter RedrivePolicy."}
}
- count, err := pol.MaxReceiveCount.Int64()
+ count, err := pol.MaxReceiveCount.Int()
if err != nil || count <= 0 {
return &InvalidParameterError{Message: "Invalid value for the parameter RedrivePolicy."}
}
@@ -72,7 +73,7 @@ func applyRedrivePolicy(q *Queue, attrs map[string]string, backend *InMemoryBack
}
}
- q.MaxReceiveCount = int(count)
+ q.MaxReceiveCount = count
q.dlq = dlq
now := backend.now()
@@ -134,7 +135,7 @@ func checkRedriveAllowPolicy(dlq *Queue, srcArn string) error {
// never left with a malformed value; treat it permissively rather than
// blocking on a defensive parse error.
//nolint:nilerr // intentional fail-open on a defensive parse error, see comment above
- if json.Unmarshal([]byte(raw), &policy) != nil {
+ if json.Unmarshal([]byte(raw), &policy, jsonx.V1()) != nil {
return nil
}
@@ -178,11 +179,11 @@ func (b *InMemoryBackend) ListDeadLetterSourceQueues(
}
var pol redrivePolicy
- if err := json.Unmarshal([]byte(raw), &pol); err != nil {
+ if err := json.Unmarshal([]byte(raw), &pol, jsonx.V1()); err != nil {
continue
}
- count, err := pol.MaxReceiveCount.Int64()
+ count, err := pol.MaxReceiveCount.Int()
if err != nil || count <= 0 {
continue
}
diff --git a/services/sqs/dead_letter_test.go b/services/sqs/dead_letter_test.go
index 8bd599ee53..dc4ed7f088 100644
--- a/services/sqs/dead_letter_test.go
+++ b/services/sqs/dead_letter_test.go
@@ -1,7 +1,7 @@
package sqs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strconv"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -29,7 +30,7 @@ func TestRedriveAllowPolicy(t *testing.T) {
rap, _ := json.Marshal(map[string]any{
"redrivePermission": "byQueue",
"sourceQueueArns": []string{dlqARN},
- })
+ }, jsonx.V1())
_, err = b.CreateQueue(&sqs.CreateQueueInput{
QueueName: "src",
@@ -54,7 +55,7 @@ func TestRedrivePolicy_MovesToDLQAfterMaxReceiveCount(t *testing.T) {
redriveJSON, _ := json.Marshal(map[string]any{
"deadLetterTargetArn": dlqARN,
"maxReceiveCount": 2,
- })
+ }, jsonx.V1())
srcURL, err := b.CreateQueue(&sqs.CreateQueueInput{
QueueName: "src-with-dlq",
@@ -104,7 +105,7 @@ func TestRedrivePolicy_SetViaSetQueueAttributes(t *testing.T) {
redriveJSON, _ := json.Marshal(map[string]any{
"deadLetterTargetArn": dlqARN,
"maxReceiveCount": 3,
- })
+ }, jsonx.V1())
err := b.SetQueueAttributes(&sqs.SetQueueAttributesInput{
QueueURL: srcURL,
@@ -117,7 +118,7 @@ func TestRedrivePolicy_SetViaSetQueueAttributes(t *testing.T) {
// Verify round-trip via GetQueueAttributes
attrs := b2getAttrs(t, b, srcURL, "RedrivePolicy")
var pol map[string]any
- require.NoError(t, json.Unmarshal([]byte(attrs["RedrivePolicy"]), &pol))
+ require.NoError(t, json.Unmarshal([]byte(attrs["RedrivePolicy"]), &pol, jsonx.V1()))
assert.Equal(t, dlqARN, pol["deadLetterTargetArn"])
}
@@ -130,7 +131,7 @@ func TestListDeadLetterSourceQueues_SingleSource(t *testing.T) {
dlqARN := dlqAttrs["QueueArn"]
makeRedriveAttr := func(arn string, count int) string {
- v, _ := json.Marshal(map[string]any{"deadLetterTargetArn": arn, "maxReceiveCount": count})
+ v, _ := json.Marshal(map[string]any{"deadLetterTargetArn": arn, "maxReceiveCount": count}, jsonx.V1())
return string(v)
}
@@ -178,7 +179,7 @@ func TestRedriveAllowPolicy_AllowAll(t *testing.T) {
t.Parallel()
b := b2newBackend(t)
- policy, _ := json.Marshal(map[string]any{"redrivePermission": "allowAll"})
+ policy, _ := json.Marshal(map[string]any{"redrivePermission": "allowAll"}, jsonx.V1())
_, err := b.CreateQueue(&sqs.CreateQueueInput{
QueueName: "rap-allow-all",
@@ -194,7 +195,7 @@ func TestRedriveAllowPolicy_DenyAll(t *testing.T) {
t.Parallel()
b := b2newBackend(t)
- policy, _ := json.Marshal(map[string]any{"redrivePermission": "denyAll"})
+ policy, _ := json.Marshal(map[string]any{"redrivePermission": "denyAll"}, jsonx.V1())
_, err := b.CreateQueue(&sqs.CreateQueueInput{
QueueName: "rap-deny-all",
@@ -213,7 +214,7 @@ func TestRedriveAllowPolicy_ByQueue(t *testing.T) {
policy, _ := json.Marshal(map[string]any{
"redrivePermission": "byQueue",
"sourceQueueArns": []string{"arn:aws:sqs:us-east-1:123456789012:src-queue"},
- })
+ }, jsonx.V1())
_, err := b.CreateQueue(&sqs.CreateQueueInput{
QueueName: "rap-by-queue",
@@ -232,7 +233,7 @@ func TestRedriveAllowPolicy_AllowAllWithArns_Rejected(t *testing.T) {
policy, _ := json.Marshal(map[string]any{
"redrivePermission": "allowAll",
"sourceQueueArns": []string{"arn:aws:sqs:us-east-1:123456789012:q"},
- })
+ }, jsonx.V1())
_, err := b.CreateQueue(&sqs.CreateQueueInput{
QueueName: "rap-invalid",
@@ -255,7 +256,7 @@ func TestRedriveAllowPolicy_ByQueueTooManyArns_Rejected(t *testing.T) {
policy, _ := json.Marshal(map[string]any{
"redrivePermission": "byQueue",
"sourceQueueArns": arns,
- })
+ }, jsonx.V1())
_, err := b.CreateQueue(&sqs.CreateQueueInput{
QueueName: "rap-toomany",
@@ -271,7 +272,7 @@ func TestRedriveAllowPolicy_RoundTrip(t *testing.T) {
t.Parallel()
b := b2newBackend(t)
- policy, _ := json.Marshal(map[string]any{"redrivePermission": "denyAll"})
+ policy, _ := json.Marshal(map[string]any{"redrivePermission": "denyAll"}, jsonx.V1())
qURL := b2createQueue(t, b, "rap-roundtrip")
require.NoError(t, b.SetQueueAttributes(&sqs.SetQueueAttributesInput{
@@ -700,7 +701,7 @@ func TestSQS_DLQRedrive_DefaultDestination(t *testing.T) {
redrivePolicy, _ := json.Marshal(map[string]any{
"deadLetterTargetArn": dlqARN,
"maxReceiveCount": 3,
- })
+ }, jsonx.V1())
_, err = b.CreateQueue(&sqs.CreateQueueInput{
QueueName: srcName,
@@ -824,7 +825,7 @@ func makeRedrivePolicy(dlqARN string, maxReceiveCount int) string {
b, _ := json.Marshal(map[string]any{
"deadLetterTargetArn": dlqARN,
"maxReceiveCount": maxReceiveCount,
- })
+ }, jsonx.V1())
return string(b)
}
diff --git a/services/sqs/errsweep_wire_shape_test.go b/services/sqs/errsweep_wire_shape_test.go
index 07d8b0515f..40d200236f 100644
--- a/services/sqs/errsweep_wire_shape_test.go
+++ b/services/sqs/errsweep_wire_shape_test.go
@@ -3,7 +3,7 @@ package sqs_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -17,6 +17,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/sqs"
@@ -56,7 +57,7 @@ func TestSDK_GetQueueUrl_NonExistentQueue_TypedError(t *testing.T) {
rawBody := rawSQSErrorBody(t, rawURL, "GetQueueUrl", `{"QueueName":"does-not-exist"}`)
var decoded map[string]any
- require.NoError(t, json.Unmarshal(rawBody, &decoded))
+ require.NoError(t, json.Unmarshal(rawBody, &decoded, jsonx.V1()))
require.Equal(t, "com.amazonaws.sqs#QueueDoesNotExist", decoded["__type"],
"raw response body must carry __type; got: %s", rawBody)
_, hasBareCode := decoded["code"]
diff --git a/services/sqs/fifo_test.go b/services/sqs/fifo_test.go
index 3a17d94fe3..a98685c0c1 100644
--- a/services/sqs/fifo_test.go
+++ b/services/sqs/fifo_test.go
@@ -2,7 +2,8 @@ package sqs_test
import (
"encoding/hex"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -201,7 +203,7 @@ func TestSetQueueAttributesFifoQueueRejectedViaHandler(t *testing.T) {
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Contains(t, errResp.Type, "InvalidAttributeName")
}
@@ -344,8 +346,8 @@ func TestSendMessage_Standard_SequenceNumberAbsent(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
// Unmarshal into a raw map so we can check key presence, not just value.
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
_, hasSeqNum := raw["SequenceNumber"]
assert.False(t, hasSeqNum, "standard queue SendMessage response must not contain SequenceNumber")
@@ -371,7 +373,7 @@ func TestSendMessage_FIFO_SequenceNumberPresent(t *testing.T) {
var createResp struct {
QueueURL string `json:"QueueUrl"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
rec := doRequest(t, h, "SendMessage", map[string]any{
"QueueUrl": createResp.QueueURL,
@@ -380,14 +382,14 @@ func TestSendMessage_FIFO_SequenceNumberPresent(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
rawSeq, hasSeqNum := raw["SequenceNumber"]
require.True(t, hasSeqNum, "FIFO queue SendMessage response must contain SequenceNumber")
var seq string
- require.NoError(t, json.Unmarshal(rawSeq, &seq))
+ require.NoError(t, json.Unmarshal(rawSeq, &seq, jsonx.V1()))
assert.NotEmpty(t, seq, "SequenceNumber must be non-empty for FIFO queue")
assert.Len(t, seq, 20, "SequenceNumber must be 20 digits")
}
diff --git a/services/sqs/handler.go b/services/sqs/handler.go
index b6f9bc4d47..d87a14964b 100644
--- a/services/sqs/handler.go
+++ b/services/sqs/handler.go
@@ -2,7 +2,7 @@ package sqs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"strings"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/endpointstrategy"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -293,7 +294,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
var req extractQueueURLInput
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
return ""
}
@@ -368,7 +369,7 @@ func (h *Handler) sqsRoute(
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// handleError writes an SQS error response using the standard error details mapping.
@@ -386,7 +387,7 @@ func (h *Handler) sqsRoute(
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
errType, message, status := errorDetails(err)
- payload, marshalErr := json.Marshal(jsonSQSError{Type: errType, Message: message})
+ payload, marshalErr := json.Marshal(jsonSQSError{Type: errType, Message: message}, jsonx.V1())
if marshalErr != nil {
return marshalErr
}
diff --git a/services/sqs/handler_dead_letter.go b/services/sqs/handler_dead_letter.go
index 9e6747ddb8..c909c17dec 100644
--- a/services/sqs/handler_dead_letter.go
+++ b/services/sqs/handler_dead_letter.go
@@ -2,8 +2,10 @@ package sqs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type jsonListDeadLetterSourceQueuesReq struct {
@@ -23,7 +25,7 @@ func (h *Handler) handleListDeadLetterSourceQueues(
body []byte,
) (any, error) {
var req jsonListDeadLetterSourceQueuesReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
diff --git a/services/sqs/handler_dead_letter_test.go b/services/sqs/handler_dead_letter_test.go
index 6ef6f501e0..a6922bbe39 100644
--- a/services/sqs/handler_dead_letter_test.go
+++ b/services/sqs/handler_dead_letter_test.go
@@ -1,13 +1,14 @@
package sqs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -36,7 +37,7 @@ func TestHandlerActions_ListDeadLetterSourceQueues(t *testing.T) {
var attrResp struct {
Attributes map[string]string `json:"Attributes"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &attrResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &attrResp, jsonx.V1()))
dlqARN := attrResp.Attributes["QueueArn"]
policy := `{"deadLetterTargetArn":"` + dlqARN + `","maxReceiveCount":3}`
@@ -97,7 +98,7 @@ func TestHandlerActions_ListDeadLetterSourceQueues(t *testing.T) {
NextToken string `json:"NextToken"`
QueueURLs []string `json:"queueUrls"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.QueueURLs, tt.wantURLCount)
}
})
diff --git a/services/sqs/handler_message_move_tasks.go b/services/sqs/handler_message_move_tasks.go
index cf32fbc95c..92b5668ee5 100644
--- a/services/sqs/handler_message_move_tasks.go
+++ b/services/sqs/handler_message_move_tasks.go
@@ -2,8 +2,10 @@ package sqs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type jsonStartMessageMoveTaskReq struct {
@@ -54,7 +56,7 @@ func (h *Handler) handleStartMessageMoveTask(
body []byte,
) (any, error) {
var req jsonStartMessageMoveTaskReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
@@ -76,7 +78,7 @@ func (h *Handler) handleCancelMessageMoveTask(
body []byte,
) (any, error) {
var req jsonCancelMessageMoveTaskReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
@@ -99,7 +101,7 @@ func (h *Handler) handleListMessageMoveTasks(
) (any, error) {
var req jsonListMessageMoveTasksReq
// Body may be empty; ignore unmarshal errors.
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
out, err := h.Backend.ListMessageMoveTasks(&ListMessageMoveTasksInput{
SourceArn: req.SourceArn,
diff --git a/services/sqs/handler_message_visibility.go b/services/sqs/handler_message_visibility.go
index f782b511ab..bba39f76db 100644
--- a/services/sqs/handler_message_visibility.go
+++ b/services/sqs/handler_message_visibility.go
@@ -2,8 +2,10 @@ package sqs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type jsonChangeVisibilityReq struct {
@@ -29,7 +31,7 @@ func (h *Handler) handleChangeMessageVisibility(
body []byte,
) (any, error) {
var req jsonChangeVisibilityReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
@@ -52,7 +54,7 @@ func (h *Handler) handleChangeMessageVisibilityBatch(
body []byte,
) (any, error) {
var req jsonChangeVisibilityBatchReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
diff --git a/services/sqs/handler_message_visibility_test.go b/services/sqs/handler_message_visibility_test.go
index 348fb69c2e..b5d4e73273 100644
--- a/services/sqs/handler_message_visibility_test.go
+++ b/services/sqs/handler_message_visibility_test.go
@@ -1,12 +1,14 @@
package sqs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestSQSHandler_ChangeMessageVisibility_InvalidJSON(t *testing.T) {
@@ -73,7 +75,7 @@ func TestSQSHandler_ChangeMessageVisibility_Success(t *testing.T) {
} `json:"Messages"`
}
- require.NoError(t, json.Unmarshal(recvRec.Body.Bytes(), &recvResp))
+ require.NoError(t, json.Unmarshal(recvRec.Body.Bytes(), &recvResp, jsonx.V1()))
require.Len(t, recvResp.Messages, 1)
rec := doRequest(t, h, "ChangeMessageVisibility", map[string]any{
@@ -107,7 +109,7 @@ func TestHandlerActions_ChangeMessageVisibility(t *testing.T) {
ReceiptHandle string `json:"ReceiptHandle"`
} `json:"Messages"`
}
- require.NoError(t, json.Unmarshal(recvRec.Body.Bytes(), &recvResp))
+ require.NoError(t, json.Unmarshal(recvRec.Body.Bytes(), &recvResp, jsonx.V1()))
require.Len(t, recvResp.Messages, 1)
receipt := recvResp.Messages[0].ReceiptHandle
@@ -162,7 +164,7 @@ func TestHandlerActions_ChangeMessageVisibilityBatch(t *testing.T) {
ReceiptHandle string `json:"ReceiptHandle"`
} `json:"Messages"`
}
- require.NoError(t, json.Unmarshal(rcvRec.Body.Bytes(), &rcvResp))
+ require.NoError(t, json.Unmarshal(rcvRec.Body.Bytes(), &rcvResp, jsonx.V1()))
require.Len(t, rcvResp.Messages, 1)
handle := rcvResp.Messages[0].ReceiptHandle
@@ -213,7 +215,7 @@ func TestHandlerActions_ChangeMessageVisibilityBatch(t *testing.T) {
ReceiptHandle string `json:"ReceiptHandle"`
} `json:"Messages"`
}
- require.NoError(t, json.Unmarshal(rcvRec.Body.Bytes(), &rcvResp))
+ require.NoError(t, json.Unmarshal(rcvRec.Body.Bytes(), &rcvResp, jsonx.V1()))
require.Len(t, rcvResp.Messages, 2)
rec := doRequest(t, h, "ChangeMessageVisibilityBatch", map[string]any{
@@ -236,7 +238,7 @@ func TestHandlerActions_ChangeMessageVisibilityBatch(t *testing.T) {
SenderFault bool `json:"SenderFault"`
} `json:"Failed"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
require.Len(t, body.Successful, 2)
assert.ElementsMatch(t, []string{"good-1", "good-2"}, []string{body.Successful[0].ID, body.Successful[1].ID})
@@ -261,7 +263,7 @@ func TestHandlerActions_ChangeMessageVisibilityBatch(t *testing.T) {
ReceiptHandle string `json:"ReceiptHandle"`
} `json:"Messages"`
}
- require.NoError(t, json.Unmarshal(reRcvRec.Body.Bytes(), &reRcvResp))
+ require.NoError(t, json.Unmarshal(reRcvRec.Body.Bytes(), &reRcvResp, jsonx.V1()))
assert.Len(t, reRcvResp.Messages, 2)
})
}
diff --git a/services/sqs/handler_messages.go b/services/sqs/handler_messages.go
index 599a40f6be..1168cc632e 100644
--- a/services/sqs/handler_messages.go
+++ b/services/sqs/handler_messages.go
@@ -2,9 +2,11 @@ package sqs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type jsonMsgAttr struct {
@@ -113,7 +115,7 @@ func (h *Handler) handleSendMessage(
body []byte,
) (any, error) {
var req jsonSendMessageReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
@@ -146,7 +148,7 @@ func (h *Handler) handleReceiveMessage(
body []byte,
) (any, error) {
var req jsonReceiveMessageReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
@@ -237,7 +239,7 @@ func (h *Handler) handleDeleteMessage(
body []byte,
) (any, error) {
var req jsonDeleteMessageReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
@@ -258,7 +260,7 @@ func (h *Handler) handleSendMessageBatch(
body []byte,
) (any, error) {
var req jsonSendMessageBatchReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
@@ -314,7 +316,7 @@ func (h *Handler) handleDeleteMessageBatch(
body []byte,
) (any, error) {
var req jsonDeleteMessageBatchReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
diff --git a/services/sqs/handler_messages_test.go b/services/sqs/handler_messages_test.go
index 017633eab3..afd2341246 100644
--- a/services/sqs/handler_messages_test.go
+++ b/services/sqs/handler_messages_test.go
@@ -1,7 +1,7 @@
package sqs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -28,7 +29,7 @@ func TestHandlerActions_SendMessage(t *testing.T) {
MessageID string `json:"MessageId"`
MD5OfMessageBody string `json:"MD5OfMessageBody"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.MessageID)
assert.NotEmpty(t, resp.MD5OfMessageBody)
}
@@ -59,7 +60,7 @@ func TestHandlerActions_ReceiveMessage(t *testing.T) {
Body string `json:"Body"`
} `json:"Messages"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Messages, 1)
assert.Equal(t, "hello", resp.Messages[0].Body)
})
@@ -86,7 +87,7 @@ func TestHandlerActions_ReceiveMessage(t *testing.T) {
Body string `json:"Body"`
} `json:"Messages"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Messages, 1)
})
}
@@ -153,7 +154,7 @@ func TestHandlerActions_ReceiveMessageAttributeNamesFilter(t *testing.T) {
MessageAttributes map[string]map[string]any `json:"MessageAttributes"`
} `json:"Messages"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Messages, 1)
assert.Len(t, resp.Messages[0].MessageAttributes, tt.wantAttrNamesLen)
for _, name := range tt.wantAttrNames {
@@ -181,7 +182,7 @@ func sendForMD5(t *testing.T, h *sqs.Handler, queueURL string, attrs map[string]
var resp struct {
MD5OfMessageAttributes string `json:"MD5OfMessageAttributes"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp.MD5OfMessageAttributes
}
@@ -261,7 +262,7 @@ func TestHandlerActions_ReceiveMessageMD5OverSubset(t *testing.T) {
MD5OfMessageAttributes string `json:"MD5OfMessageAttributes"`
} `json:"Messages"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Messages, 1)
if tt.wantEmptyMD5 {
@@ -305,7 +306,7 @@ func TestHandlerActions_DeleteMessage(t *testing.T) {
ReceiptHandle string `json:"ReceiptHandle"`
} `json:"Messages"`
}
- require.NoError(t, json.Unmarshal(recvRec.Body.Bytes(), &recvResp))
+ require.NoError(t, json.Unmarshal(recvRec.Body.Bytes(), &recvResp, jsonx.V1()))
require.Len(t, recvResp.Messages, 1)
receipt := recvResp.Messages[0].ReceiptHandle
@@ -354,7 +355,7 @@ func TestHandlerActions_SendMessageBatch(t *testing.T) {
ID string `json:"Id"`
} `json:"Successful"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Successful, 2)
})
@@ -370,7 +371,7 @@ func TestHandlerActions_SendMessageBatch(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp jsonErr
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "com.amazonaws.sqs#QueueDoesNotExist", errResp.Type)
})
@@ -387,7 +388,7 @@ func TestHandlerActions_SendMessageBatch(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp jsonErr
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "com.amazonaws.sqs#EmptyBatchRequest", errResp.Type)
})
@@ -416,7 +417,7 @@ func TestHandlerActions_SendMessageBatch(t *testing.T) {
ID string `json:"Id"`
} `json:"Successful"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Successful, 10)
})
}
@@ -442,7 +443,7 @@ func TestHandlerActions_DeleteMessageBatch(t *testing.T) {
ReceiptHandle string `json:"ReceiptHandle"`
} `json:"Messages"`
}
- require.NoError(t, json.Unmarshal(recvRec.Body.Bytes(), &recvResp))
+ require.NoError(t, json.Unmarshal(recvRec.Body.Bytes(), &recvResp, jsonx.V1()))
require.Len(t, recvResp.Messages, 1)
receipt := recvResp.Messages[0].ReceiptHandle
@@ -458,7 +459,7 @@ func TestHandlerActions_DeleteMessageBatch(t *testing.T) {
ID string `json:"Id"`
} `json:"Successful"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Successful, 1)
})
@@ -474,7 +475,7 @@ func TestHandlerActions_DeleteMessageBatch(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp jsonErr
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "com.amazonaws.sqs#QueueDoesNotExist", errResp.Type)
})
@@ -495,7 +496,7 @@ func TestHandlerActions_DeleteMessageBatch(t *testing.T) {
ID string `json:"Id"`
} `json:"Failed"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Failed, 1)
})
@@ -512,7 +513,7 @@ func TestHandlerActions_DeleteMessageBatch(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp jsonErr
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "com.amazonaws.sqs#EmptyBatchRequest", errResp.Type)
})
}
diff --git a/services/sqs/handler_permissions.go b/services/sqs/handler_permissions.go
index b77f678b52..b1c84e2292 100644
--- a/services/sqs/handler_permissions.go
+++ b/services/sqs/handler_permissions.go
@@ -2,8 +2,10 @@ package sqs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type jsonAddPermissionReq struct {
@@ -24,7 +26,7 @@ func (h *Handler) handleAddPermission(
body []byte,
) (any, error) {
var req jsonAddPermissionReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
@@ -47,7 +49,7 @@ func (h *Handler) handleRemovePermission(
body []byte,
) (any, error) {
var req jsonRemovePermissionReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
diff --git a/services/sqs/handler_queue_attributes.go b/services/sqs/handler_queue_attributes.go
index de8c6898ae..3b44edb589 100644
--- a/services/sqs/handler_queue_attributes.go
+++ b/services/sqs/handler_queue_attributes.go
@@ -2,8 +2,10 @@ package sqs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type jsonGetQueueAttributesReq struct {
@@ -26,7 +28,7 @@ func (h *Handler) handleGetQueueAttributes(
body []byte,
) (any, error) {
var req jsonGetQueueAttributesReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
@@ -53,7 +55,7 @@ func (h *Handler) handleSetQueueAttributes(
body []byte,
) (any, error) {
var req jsonSetQueueAttributesReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
diff --git a/services/sqs/handler_queues.go b/services/sqs/handler_queues.go
index c3f09df6da..8dfceaf053 100644
--- a/services/sqs/handler_queues.go
+++ b/services/sqs/handler_queues.go
@@ -2,10 +2,11 @@ package sqs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -46,7 +47,7 @@ func (h *Handler) handleCreateQueue(
body []byte,
) (any, error) {
var req jsonCreateQueueReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
@@ -92,7 +93,7 @@ func (h *Handler) handleDeleteQueue(
body []byte,
) (any, error) {
var req jsonQueueURLReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
@@ -112,7 +113,7 @@ func (h *Handler) handleListQueues(
) (any, error) {
var req jsonListQueuesReq
// ListQueues body may be empty; ignore unmarshal errors
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
region := h.requestScope(r)
@@ -140,7 +141,7 @@ func (h *Handler) handleGetQueueURL(
body []byte,
) (any, error) {
var req jsonGetQueueURLReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
@@ -162,7 +163,7 @@ func (h *Handler) handlePurgeQueue(
body []byte,
) (any, error) {
var req jsonQueueURLReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
diff --git a/services/sqs/handler_queues_test.go b/services/sqs/handler_queues_test.go
index d4e847b974..53926a7edf 100644
--- a/services/sqs/handler_queues_test.go
+++ b/services/sqs/handler_queues_test.go
@@ -1,13 +1,14 @@
package sqs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -150,7 +151,7 @@ func TestHandlerActions_ListQueues(t *testing.T) {
NextToken string `json:"NextToken"`
QueueURLs []string `json:"QueueUrls"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.QueueURLs, tt.wantURLCount)
})
}
@@ -180,7 +181,7 @@ func TestHandlerActions_ListQueues_PaginationRoundTrip(t *testing.T) {
NextToken string `json:"NextToken"`
QueueURLs []string `json:"QueueUrls"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
allURLs = append(allURLs, resp.QueueURLs...)
nextToken = resp.NextToken
@@ -281,7 +282,7 @@ func TestHandlerActions_QueueManagement(t *testing.T) {
var resp struct {
Attributes map[string]string `json:"Attributes"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.Attributes)
})
diff --git a/services/sqs/handler_tags.go b/services/sqs/handler_tags.go
index 290910ad2f..2b0fc99b00 100644
--- a/services/sqs/handler_tags.go
+++ b/services/sqs/handler_tags.go
@@ -2,9 +2,10 @@ package sqs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -30,7 +31,7 @@ func (h *Handler) handleTagQueue(
body []byte,
) (any, error) {
var req jsonTagQueueReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
@@ -51,7 +52,7 @@ func (h *Handler) handleUntagQueue(
body []byte,
) (any, error) {
var req jsonUntagQueueReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
@@ -72,7 +73,7 @@ func (h *Handler) handleListQueueTags(
body []byte,
) (any, error) {
var req jsonQueueURLReq
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, ErrUnknownAction
}
diff --git a/services/sqs/handler_tags_test.go b/services/sqs/handler_tags_test.go
index 7e9983a23a..5cddbc4b66 100644
--- a/services/sqs/handler_tags_test.go
+++ b/services/sqs/handler_tags_test.go
@@ -1,12 +1,14 @@
package sqs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestSQSHandler_UntagQueue_InvalidJSON(t *testing.T) {
@@ -132,7 +134,7 @@ func TestHandlerActions_TagOps(t *testing.T) {
var resp struct {
Tags map[string]string `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp.Tags)
})
diff --git a/services/sqs/handler_test.go b/services/sqs/handler_test.go
index b49869fc95..e3dc1fc0ac 100644
--- a/services/sqs/handler_test.go
+++ b/services/sqs/handler_test.go
@@ -2,7 +2,7 @@ package sqs_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"log/slog"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -73,7 +74,7 @@ func TestSQSHandler_Reset(t *testing.T) {
QueueURLs []string `json:"QueueUrls"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp.QueueURLs, tt.name)
})
}
@@ -156,7 +157,7 @@ func doRequest(t *testing.T, h *sqs.Handler, action string, body any) *httptest.
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -189,7 +190,7 @@ func doCreateQueue(t *testing.T, h *sqs.Handler, name string) string {
QueueURL string `json:"QueueUrl"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp.QueueURL
}
diff --git a/services/sqs/message_move_tasks.go b/services/sqs/message_move_tasks.go
index 18f01847e9..f2a0fb6b39 100644
--- a/services/sqs/message_move_tasks.go
+++ b/services/sqs/message_move_tasks.go
@@ -2,7 +2,7 @@ package sqs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sort"
"sync"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// moveTaskVisibilityTimeoutSecs is the visibility timeout used when receiving
@@ -87,7 +88,7 @@ func (b *InMemoryBackend) findDefaultMoveDestinationLocked(dlqARN string) (strin
var pol redrivePolicy
- if err := json.Unmarshal([]byte(raw), &pol); err != nil {
+ if err := json.Unmarshal([]byte(raw), &pol, jsonx.V1()); err != nil {
continue
}
diff --git a/services/sqs/message_move_tasks_test.go b/services/sqs/message_move_tasks_test.go
index 5dea157ef9..8a3ced4d0b 100644
--- a/services/sqs/message_move_tasks_test.go
+++ b/services/sqs/message_move_tasks_test.go
@@ -1,7 +1,7 @@
package sqs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -539,7 +540,7 @@ func TestSQS_MessageMoveTasks_Handler(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
handle, _ := startResp["TaskHandle"].(string)
require.NotEmpty(t, handle)
diff --git a/services/sqs/message_visibility_test.go b/services/sqs/message_visibility_test.go
index 4e52f1d57c..f4b304c68c 100644
--- a/services/sqs/message_visibility_test.go
+++ b/services/sqs/message_visibility_test.go
@@ -1,7 +1,7 @@
package sqs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"log/slog"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -239,7 +240,7 @@ func TestReceiveMessage_VisibilityTimeout_OutOfRange(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, errResp.Type)
})
}
@@ -282,7 +283,7 @@ func TestReceiveMessage_VisibilityTimeout_ValidBoundaries(t *testing.T) {
Body string `json:"Body"`
} `json:"Messages"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Messages, 1)
})
}
diff --git a/services/sqs/messages_test.go b/services/sqs/messages_test.go
index 9dbe179c20..5e44c65d1a 100644
--- a/services/sqs/messages_test.go
+++ b/services/sqs/messages_test.go
@@ -3,7 +3,7 @@ package sqs_test
import (
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"fmt"
"maps"
@@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -1335,7 +1336,7 @@ func TestReceiveMessage_MaxNumberOfMessages_Validation(t *testing.T) {
if tt.wantType != "" {
var resp jsonErr
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp.Type)
}
})
diff --git a/services/sqs/permissions.go b/services/sqs/permissions.go
index d3ebd9a774..3c9a2c424e 100644
--- a/services/sqs/permissions.go
+++ b/services/sqs/permissions.go
@@ -1,11 +1,12 @@
package sqs
import (
- "encoding/json"
+ "encoding/json/v2"
"slices"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/collections"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// iamPolicyDocument represents the IAM resource policy document stored in the
@@ -81,7 +82,7 @@ func buildQueueIAMPolicy(q *Queue) {
Statement: stmts,
}
- raw, err := json.Marshal(doc)
+ raw, err := json.Marshal(doc, jsonx.V1())
if err != nil {
return
}
diff --git a/services/sqs/permissions_test.go b/services/sqs/permissions_test.go
index 031b31980d..1b8f0842a7 100644
--- a/services/sqs/permissions_test.go
+++ b/services/sqs/permissions_test.go
@@ -1,13 +1,14 @@
package sqs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -253,7 +254,7 @@ func TestAddPermission_UpdatesPolicyAttribute(t *testing.T) {
assert.NotEmpty(t, policyJSON)
var doc map[string]any
- require.NoError(t, json.Unmarshal([]byte(policyJSON), &doc))
+ require.NoError(t, json.Unmarshal([]byte(policyJSON), &doc, jsonx.V1()))
assert.Equal(t, "2012-10-17", doc["Version"])
stmts := doc["Statement"].([]any)
@@ -279,7 +280,7 @@ func TestAddPermission_WildcardAction(t *testing.T) {
attrs := b2getAttrs(t, b, qURL, "Policy")
var doc map[string]any
- require.NoError(t, json.Unmarshal([]byte(attrs["Policy"]), &doc))
+ require.NoError(t, json.Unmarshal([]byte(attrs["Policy"]), &doc, jsonx.V1()))
stmts := doc["Statement"].([]any)
stmt := stmts[0].(map[string]any)
actions := stmt["Action"].([]any)
@@ -330,7 +331,7 @@ func TestAddPermission_MultipleStatements(t *testing.T) {
attrs := b2getAttrs(t, b, qURL, "Policy")
var doc map[string]any
- require.NoError(t, json.Unmarshal([]byte(attrs["Policy"]), &doc))
+ require.NoError(t, json.Unmarshal([]byte(attrs["Policy"]), &doc, jsonx.V1()))
stmts := doc["Statement"].([]any)
assert.Len(t, stmts, 2)
}
@@ -357,7 +358,7 @@ func TestRemovePermission_PartialRemoval(t *testing.T) {
attrs := b2getAttrs(t, b, qURL, "Policy")
var doc map[string]any
- require.NoError(t, json.Unmarshal([]byte(attrs["Policy"]), &doc))
+ require.NoError(t, json.Unmarshal([]byte(attrs["Policy"]), &doc, jsonx.V1()))
stmts := doc["Statement"].([]any)
assert.Len(t, stmts, 1)
stmt := stmts[0].(map[string]any)
diff --git a/services/sqs/persistence.go b/services/sqs/persistence.go
index a93409205d..918b8245a5 100644
--- a/services/sqs/persistence.go
+++ b/services/sqs/persistence.go
@@ -2,10 +2,12 @@ package sqs
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -94,21 +96,21 @@ type backendSnapshot struct {
// wait-before-recreate rule for any queue deleted just before the
// snapshot. Keyed by queueKey(region, name) since the deleted queue no
// longer has a live Queue struct to attach this to.
- RecentlyDeleted map[string]int64 `json:"recentlyDeleted,omitempty"`
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ RecentlyDeleted map[string]int64 `json:"recentlyDeleted,omitempty"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// marshalQueuesLocked marshals each queue's DTO to JSON while q.mu is held, then reassembles the JSON array.
// queues must already be sorted, as [store.Table.Snapshot] returns them, to keep the wire format unchanged.
-func marshalQueuesLocked(queues []*Queue) (json.RawMessage, error) {
+func marshalQueuesLocked(queues []*Queue) (jsontext.Value, error) {
if len(queues) == 0 {
- return json.RawMessage("null"), nil
+ return jsontext.Value("null"), nil
}
- parts := make([]json.RawMessage, len(queues))
+ parts := make([]jsontext.Value, len(queues))
for i, q := range queues {
q.mu.Lock()
@@ -135,7 +137,7 @@ func marshalQueuesLocked(queues []*Queue) (json.RawMessage, error) {
IsFIFO: q.IsFIFO,
FifoSeqCounter: q.fifoSeqCounter,
LastPurgedAtUnixMilli: lastPurgedAtMillis,
- })
+ }, jsonx.V1())
q.mu.Unlock()
@@ -146,7 +148,7 @@ func marshalQueuesLocked(queues []*Queue) (json.RawMessage, error) {
parts[i] = data
}
- return json.Marshal(parts)
+ return json.Marshal(parts, jsonx.V1())
}
// Snapshot serialises the backend state to JSON.
diff --git a/services/sqs/queue_attributes.go b/services/sqs/queue_attributes.go
index 51fd419c3d..8ea9aca476 100644
--- a/services/sqs/queue_attributes.go
+++ b/services/sqs/queue_attributes.go
@@ -1,9 +1,11 @@
package sqs
import (
- "encoding/json"
+ "encoding/json/v2"
"slices"
"strconv"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// GetQueueAttributes returns queue attributes, computing dynamic ones on the fly.
@@ -205,7 +207,7 @@ func validateRedriveAllowPolicy(raw string) error {
SourceQueueArns []string `json:"sourceQueueArns"`
}
- if err := json.Unmarshal([]byte(raw), &policy); err != nil {
+ if err := json.Unmarshal([]byte(raw), &policy, jsonx.V1()); err != nil {
return ErrInvalidAttribute
}
diff --git a/services/sqs/queues_test.go b/services/sqs/queues_test.go
index 37666b250b..4aed59eeeb 100644
--- a/services/sqs/queues_test.go
+++ b/services/sqs/queues_test.go
@@ -1,7 +1,7 @@
package sqs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -640,7 +641,7 @@ func TestCreateQueue_NameValidation(t *testing.T) {
if tt.wantType != "" {
var resp jsonErr
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp.Type)
}
})
diff --git a/services/sqs/sns_delivery.go b/services/sqs/sns_delivery.go
index 85c6f24914..c92732f62d 100644
--- a/services/sqs/sns_delivery.go
+++ b/services/sqs/sns_delivery.go
@@ -2,7 +2,7 @@ package sqs
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"strings"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/apptrace"
"github.com/blackbirdworks/gopherstack/pkgs/events"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/roleauth"
)
@@ -145,7 +146,7 @@ func (b *InMemoryBackend) deliverToDLQ(
DeadLetterTargetArn string `json:"deadLetterTargetArn"`
}
- if err := json.Unmarshal([]byte(redrivePolicy), &policy); err != nil {
+ if err := json.Unmarshal([]byte(redrivePolicy), &policy, jsonx.V1()); err != nil {
return
}
@@ -262,7 +263,7 @@ func buildSNSEnvelope(ev *events.SNSPublishedEvent, sub events.SNSSubscriptionSn
}
}
- b, err := json.Marshal(env)
+ b, err := json.Marshal(env, jsonx.V1())
if err != nil {
return sub.Body(ev.Message)
}
diff --git a/services/sqs/sns_delivery_test.go b/services/sqs/sns_delivery_test.go
index d1053453d2..793de684e0 100644
--- a/services/sqs/sns_delivery_test.go
+++ b/services/sqs/sns_delivery_test.go
@@ -1,13 +1,14 @@
package sqs_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/events"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
snsbackend "github.com/blackbirdworks/gopherstack/services/sns"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -147,7 +148,7 @@ func TestSNSToSQSDelivery(t *testing.T) {
if tt.wantEnvType != "" {
var env map[string]string
- require.NoError(t, json.Unmarshal([]byte(out.Messages[0].Body), &env))
+ require.NoError(t, json.Unmarshal([]byte(out.Messages[0].Body), &env, jsonx.V1()))
assert.Equal(t, tt.wantEnvType, env["Type"])
assert.Equal(t, topic.TopicArn, env["TopicArn"])
assert.Equal(t, tt.wantEnvMessage, env["Message"])
@@ -224,7 +225,7 @@ func TestSNSToSQSRawMessageDelivery(t *testing.T) {
// Ensure the body is NOT an SNS envelope (no "Type": "Notification" wrapper).
var env map[string]any
- if jsonErr := json.Unmarshal([]byte(out.Messages[0].Body), &env); jsonErr == nil {
+ if jsonErr := json.Unmarshal([]byte(out.Messages[0].Body), &env, jsonx.V1()); jsonErr == nil {
assert.NotEqual(t, "Notification", env["Type"], "raw delivery body must not be SNS envelope")
}
@@ -313,7 +314,7 @@ func TestSNSToSQSDLQ(t *testing.T) {
// DLQ receives the same body that was attempted to the failed queue.
// Without RawMessageDelivery, that is the SNS envelope JSON.
var env map[string]string
- require.NoError(t, json.Unmarshal([]byte(out.Messages[0].Body), &env))
+ require.NoError(t, json.Unmarshal([]byte(out.Messages[0].Body), &env, jsonx.V1()))
assert.Equal(t, "Notification", env["Type"])
assert.Equal(t, tt.publishMessage, env["Message"])
} else {
diff --git a/services/sqs/sns_envelope_test.go b/services/sqs/sns_envelope_test.go
index 42792efe6f..9728465cc2 100644
--- a/services/sqs/sns_envelope_test.go
+++ b/services/sqs/sns_envelope_test.go
@@ -3,13 +3,14 @@ package sqs_test
import (
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"encoding/pem"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
snsbackend "github.com/blackbirdworks/gopherstack/services/sns"
"github.com/blackbirdworks/gopherstack/services/sqs"
)
@@ -46,7 +47,7 @@ func receiveOneEnvelope(t *testing.T, snsBk *snsbackend.InMemoryBackend, sqsBk *
require.Len(t, out.Messages, 1)
var env snsEnvelopeFields
- require.NoError(t, json.Unmarshal([]byte(out.Messages[0].Body), &env))
+ require.NoError(t, json.Unmarshal([]byte(out.Messages[0].Body), &env, jsonx.V1()))
return env
}
diff --git a/services/ssm/activations_test.go b/services/ssm/activations_test.go
index 43f1f91132..d43e8d192e 100644
--- a/services/ssm/activations_test.go
+++ b/services/ssm/activations_test.go
@@ -2,7 +2,8 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -86,7 +88,7 @@ func TestResourceDataSync_CRUD(t *testing.T) {
SyncType string `json:"SyncType"`
} `json:"ResourceDataSyncItems"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&listResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &listResp, jsonx.V1()))
require.Len(t, listResp.ResourceDataSyncItems, 1)
item := listResp.ResourceDataSyncItems[0]
assert.Equal(t, "SyncFromSource", item.SyncType)
@@ -369,7 +371,7 @@ func TestStubOps_DeleteActivation(t *testing.T) {
})
require.NoError(t, err)
- body, _ := json.Marshal(map[string]any{"ActivationId": act.ActivationID})
+ body, _ := json.Marshal(map[string]any{"ActivationId": act.ActivationID}, jsonx.V1())
rec := doRequest(t, h, "DeleteActivation", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -425,7 +427,7 @@ func TestCreateActivation_Success(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var resp ssm.CreateActivationOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.ActivationID)
assert.NotEmpty(t, resp.ActivationCode)
assert.True(t, strings.HasPrefix(resp.ActivationID, "act-"))
@@ -545,18 +547,18 @@ func TestCreateActivationWithTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp ssm.CreateActivationOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotEmpty(t, resp.ActivationID)
listBody, _ := json.Marshal(map[string]any{
"ResourceType": "Activation",
"ResourceId": resp.ActivationID,
- })
+ }, jsonx.V1())
listRec := doRequest(t, h, "ListTagsForResource", string(listBody))
require.Equal(t, http.StatusOK, listRec.Code)
var tagResp ssm.ListTagsForResourceOutput
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagResp, jsonx.V1()))
assert.Len(t, tagResp.TagList, tt.tagCount)
})
}
@@ -839,7 +841,7 @@ func TestDeleteActivation_TableDriven(t *testing.T) {
activationID = act.ActivationID
}
- body, _ := json.Marshal(map[string]any{"ActivationId": activationID})
+ body, _ := json.Marshal(map[string]any{"ActivationId": activationID}, jsonx.V1())
rec := doRequest(t, h, "DeleteActivation", string(body))
assert.Equal(t, tt.wantStatus, rec.Code)
@@ -864,7 +866,7 @@ func TestDescribeInstanceProperties_DerivedFromActivations(t *testing.T) {
var activation struct {
ActivationID string `json:"ActivationId"`
}
- require.NoError(t, json.Unmarshal(actResp.Body.Bytes(), &activation))
+ require.NoError(t, json.Unmarshal(actResp.Body.Bytes(), &activation, jsonx.V1()))
require.NotEmpty(t, activation.ActivationID)
propsResp := doRequest(t, h, "DescribeInstanceProperties", `{}`)
diff --git a/services/ssm/agent_messages.go b/services/ssm/agent_messages.go
index 21928cdf1e..bd7bb6e3cb 100644
--- a/services/ssm/agent_messages.go
+++ b/services/ssm/agent_messages.go
@@ -3,7 +3,7 @@ package ssm
import (
"cmp"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strconv"
@@ -11,6 +11,8 @@ import (
"time"
"gopkg.in/yaml.v3"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// The ec2messages wire shapes below follow the open-source amazon-ssm-agent
@@ -244,7 +246,10 @@ func (b *InMemoryBackend) queueAgentCancelLocked(region string, inv CommandInvoc
return
}
- raw, _ := json.Marshal(map[string]string{"CancelMessageId": sendMessageID(inv.CommandID, inv.InstanceID)})
+ raw, _ := json.Marshal(
+ map[string]string{"CancelMessageId": sendMessageID(inv.CommandID, inv.InstanceID)},
+ jsonx.V1(),
+ )
q.push(&agentEnvelope{msg: AgentMessage{
CreatedDate: messageNow(),
Destination: inv.InstanceID,
@@ -278,7 +283,7 @@ func (b *InMemoryBackend) sendCommandPayload(region string, input *SendCommandIn
payload["CloudWatchLogGroupName"] = cw.CloudWatchLogGroupName
}
- raw, _ := json.Marshal(payload)
+ raw, _ := json.Marshal(payload, jsonx.V1())
return string(raw)
}
@@ -290,7 +295,7 @@ func documentAsJSON(content, format string) any {
if format == documentFormatYAML {
err = yaml.Unmarshal([]byte(content), &doc)
} else {
- err = json.Unmarshal([]byte(content), &doc)
+ err = json.Unmarshal([]byte(content), &doc, jsonx.V1())
}
if err != nil {
@@ -496,7 +501,7 @@ func (b *InMemoryBackend) AgentSendReply(ctx context.Context, messageID, payload
}
var reply agentReply
- if uerr := json.Unmarshal([]byte(payload), &reply); uerr != nil {
+ if uerr := json.Unmarshal([]byte(payload), &reply, jsonx.V1()); uerr != nil {
return fmt.Errorf("%w: Payload is not a valid reply document", ErrValidationException)
}
diff --git a/services/ssm/agent_messages_test.go b/services/ssm/agent_messages_test.go
index 909d276c2e..3f59f64f01 100644
--- a/services/ssm/agent_messages_test.go
+++ b/services/ssm/agent_messages_test.go
@@ -1,7 +1,7 @@
package ssm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -39,7 +40,7 @@ type getMessagesResp struct {
func agentCall(t *testing.T, h *ssm.Handler, op string, body any) *httptest.ResponseRecorder {
t.Helper()
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(raw)))
@@ -61,7 +62,7 @@ func agentGetMessages(t *testing.T, h *ssm.Handler) getMessagesResp {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out getMessagesResp
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
@@ -84,7 +85,7 @@ func replyPayload(t *testing.T, docStatus string, plugin map[string]any) string
"DocumentStatus": docStatus,
"DocumentTraceOutput": "",
"RuntimeStatus": map[string]any{"aws:runShellScript": plugin},
- })
+ }, jsonx.V1())
require.NoError(t, err)
return string(raw)
@@ -161,7 +162,7 @@ func TestAgentPollingRunsCommandEndToEnd(t *testing.T) {
} `json:"mainSteps"`
} `json:"DocumentContent"`
}
- require.NoError(t, json.Unmarshal([]byte(msg.Payload), &payload))
+ require.NoError(t, json.Unmarshal([]byte(msg.Payload), &payload, jsonx.V1()))
assert.Equal(t, cmdID, payload.CommandID)
assert.Equal(t, "AWS-RunShellScript", payload.DocumentName)
assert.Equal(t, []string{"echo hello"}, payload.Parameters["commands"])
diff --git a/services/ssm/associations_test.go b/services/ssm/associations_test.go
index ae1c265f63..6b4a5cdd16 100644
--- a/services/ssm/associations_test.go
+++ b/services/ssm/associations_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -241,7 +242,7 @@ func TestStubOps_DeleteAssociation(t *testing.T) {
require.NoError(t, err)
body, _ := json.Marshal(
- map[string]any{"AssociationId": assoc.AssociationDescription.AssociationID},
+ map[string]any{"AssociationId": assoc.AssociationDescription.AssociationID}, jsonx.V1(),
)
rec := doRequest(t, h, "DeleteAssociation", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
@@ -260,7 +261,7 @@ func TestStubOps_DescribeAssociation(t *testing.T) {
require.NoError(t, err)
body, _ := json.Marshal(
- map[string]any{"AssociationId": assoc.AssociationDescription.AssociationID},
+ map[string]any{"AssociationId": assoc.AssociationDescription.AssociationID}, jsonx.V1(),
)
rec := doRequest(t, h, "DescribeAssociation", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
@@ -279,7 +280,7 @@ func TestStubOps_UpdateAssociation(t *testing.T) {
require.NoError(t, err)
body, _ := json.Marshal(
- map[string]any{"AssociationId": assoc.AssociationDescription.AssociationID},
+ map[string]any{"AssociationId": assoc.AssociationDescription.AssociationID}, jsonx.V1(),
)
rec := doRequest(t, h, "UpdateAssociation", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
@@ -356,7 +357,7 @@ func TestCreateAssociation_Success(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var resp ssm.CreateAssociationOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.AssociationDescription.AssociationID)
assert.Equal(t, "MyDoc", resp.AssociationDescription.Name)
assert.Equal(t, 1, backend.AssociationCount())
@@ -423,7 +424,7 @@ func TestCreateAssociationBatch(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var resp ssm.CreateAssociationBatchOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.Successful, tt.wantSuccessful)
assert.Len(t, resp.Failed, tt.wantFailed)
assert.Equal(t, tt.wantSuccessful, backend.AssociationCount())
@@ -703,7 +704,7 @@ func TestDeleteAssociation_TableDriven(t *testing.T) {
assocID = assoc.AssociationDescription.AssociationID
}
- body, _ := json.Marshal(map[string]any{"AssociationId": assocID})
+ body, _ := json.Marshal(map[string]any{"AssociationId": assocID}, jsonx.V1())
rec := doRequest(t, h, "DeleteAssociation", string(body))
assert.Equal(t, tt.wantStatus, rec.Code)
diff --git a/services/ssm/automation_exec.go b/services/ssm/automation_exec.go
index 5635f61fa9..b84ee1af0c 100644
--- a/services/ssm/automation_exec.go
+++ b/services/ssm/automation_exec.go
@@ -1,10 +1,12 @@
package ssm
import (
- "encoding/json"
+ "encoding/json/v2"
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// automationDoc is the minimal shape of an SSM automation (schemaVersion 0.3)
@@ -117,7 +119,7 @@ func parseAutomationDocSteps(content string) []automationDocStep {
}
var doc automationDoc
- if err := json.Unmarshal([]byte(content), &doc); err != nil {
+ if err := json.Unmarshal([]byte(content), &doc, jsonx.V1()); err != nil {
return nil
}
diff --git a/services/ssm/automations_test.go b/services/ssm/automations_test.go
index b382417165..c84e0d249b 100644
--- a/services/ssm/automations_test.go
+++ b/services/ssm/automations_test.go
@@ -2,7 +2,8 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -54,7 +56,7 @@ func TestChangeRequest(t *testing.T) {
var startResp struct {
AutomationExecutionID string `json:"AutomationExecutionId"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&startResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &startResp, jsonx.V1()))
// Runbooks must round-trip on GetAutomationExecution.
rec = doRequest(t, h, "GetAutomationExecution",
@@ -69,7 +71,7 @@ func TestChangeRequest(t *testing.T) {
} `json:"Runbooks"`
} `json:"AutomationExecution"`
}
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&getResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &getResp, jsonx.V1()))
require.Len(t, getResp.AutomationExecution.Runbooks, 1)
assert.Equal(t, "AWS-RunShellScript", getResp.AutomationExecution.Runbooks[0].DocumentName)
assert.Equal(t, "1", getResp.AutomationExecution.Runbooks[0].MaxConcurrency)
@@ -412,17 +414,17 @@ func TestAutomationExecution_ParametersRoundTrip(t *testing.T) {
"DocumentName": "AWS-RunShellScript",
"Parameters": tt.parameters,
"Mode": tt.mode,
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "StartAutomationExecution", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
execID := startResp["AutomationExecutionId"].(string)
assert.NotEmpty(t, execID)
// GetAutomationExecution should return parameters.
- body, _ = json.Marshal(map[string]any{"AutomationExecutionId": execID})
+ body, _ = json.Marshal(map[string]any{"AutomationExecutionId": execID}, jsonx.V1())
rec = doRequest(t, h, "GetAutomationExecution", string(body))
require.Equal(t, http.StatusOK, rec.Code)
@@ -444,19 +446,19 @@ func TestAutomationApprovals_ApproveSignal(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
execID := startResp["AutomationExecutionId"].(string)
// Send Approve signal.
body, _ := json.Marshal(map[string]any{
"AutomationExecutionId": execID,
"SignalType": "Approve",
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "SendAutomationSignal", string(body))
require.Equal(t, http.StatusOK, rec.Code)
// GetAutomationExecution should show Approved status.
- body, _ = json.Marshal(map[string]any{"AutomationExecutionId": execID})
+ body, _ = json.Marshal(map[string]any{"AutomationExecutionId": execID}, jsonx.V1())
rec = doRequest(t, h, "GetAutomationExecution", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "Approved")
@@ -470,17 +472,17 @@ func TestAutomationApprovals_RejectSignal(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
execID := startResp["AutomationExecutionId"].(string)
body, _ := json.Marshal(map[string]any{
"AutomationExecutionId": execID,
"SignalType": "Reject",
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "SendAutomationSignal", string(body))
require.Equal(t, http.StatusOK, rec.Code)
- body, _ = json.Marshal(map[string]any{"AutomationExecutionId": execID})
+ body, _ = json.Marshal(map[string]any{"AutomationExecutionId": execID}, jsonx.V1())
rec = doRequest(t, h, "GetAutomationExecution", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "Rejected")
@@ -524,7 +526,7 @@ func TestAutomationExecution_WarningMessageAbsentFromWire(t *testing.T) {
require.Equal(t, http.StatusOK, start.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(start.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(start.Body.Bytes(), &startResp, jsonx.V1()))
execID, _ := startResp["AutomationExecutionId"].(string)
require.NotEmpty(t, execID)
@@ -542,7 +544,7 @@ func TestGetCalendarState_MissingDocumentReturnsError(t *testing.T) {
body, _ := json.Marshal(map[string]any{
"CalendarNames": []string{"DoesNotExist"},
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "GetCalendarState", string(body))
assert.NotEqual(t, http.StatusOK, rec.Code, "missing calendar should return error")
}
diff --git a/services/ssm/client_token_bound_test.go b/services/ssm/client_token_bound_test.go
index 92fcce6e3b..4e6e187e8b 100644
--- a/services/ssm/client_token_bound_test.go
+++ b/services/ssm/client_token_bound_test.go
@@ -1,13 +1,15 @@
package ssm_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -49,10 +51,10 @@ func TestClientTokenMemoIsBounded(t *testing.T) {
}
var snap struct {
- Idempotency map[string]map[string]json.RawMessage `json:"idempotency"`
+ Idempotency map[string]map[string]jsontext.Value `json:"idempotency"`
}
- require.NoError(t, json.Unmarshal(b.Snapshot(t.Context()), &snap))
+ require.NoError(t, json.Unmarshal(b.Snapshot(t.Context()), &snap, jsonx.V1()))
for _, recs := range snap.Idempotency {
assert.LessOrEqual(t, len(recs), 1024)
diff --git a/services/ssm/cloud_connector_test.go b/services/ssm/cloud_connector_test.go
index 593ee62c24..22414efffd 100644
--- a/services/ssm/cloud_connector_test.go
+++ b/services/ssm/cloud_connector_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -370,7 +371,7 @@ func Test_CloudConnector_HandlerDispatch(t *testing.T) {
},
"DisplayName": "my-connector",
"RoleArn": "arn:aws:iam::123456789012:role/r",
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "CreateCloudConnector", string(createBody))
require.Equal(t, http.StatusOK, rec.Code)
@@ -378,15 +379,15 @@ func Test_CloudConnector_HandlerDispatch(t *testing.T) {
var createResp struct {
CloudConnectorID string `json:"CloudConnectorId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
require.NotEmpty(t, createResp.CloudConnectorID)
- getBody, _ := json.Marshal(map[string]any{"CloudConnectorId": createResp.CloudConnectorID})
+ getBody, _ := json.Marshal(map[string]any{"CloudConnectorId": createResp.CloudConnectorID}, jsonx.V1())
rec = doRequest(t, h, "GetCloudConnector", string(getBody))
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "my-connector", getResp["DisplayName"])
// CreatedAt/UpdatedAt must serialize as epoch-seconds numbers, not ISO8601 strings.
_, isNumber := getResp["CreatedAt"].(float64)
@@ -398,10 +399,10 @@ func Test_CloudConnector_HandlerDispatch(t *testing.T) {
var errResp struct {
Type string `json:"__type"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp.Type)
- deleteBody, _ := json.Marshal(map[string]any{"CloudConnectorId": createResp.CloudConnectorID})
+ deleteBody, _ := json.Marshal(map[string]any{"CloudConnectorId": createResp.CloudConnectorID}, jsonx.V1())
rec = doRequest(t, h, "DeleteCloudConnector", string(deleteBody))
require.Equal(t, http.StatusOK, rec.Code)
}
diff --git a/services/ssm/command_run_doc.go b/services/ssm/command_run_doc.go
index 04d642f502..31dc383d91 100644
--- a/services/ssm/command_run_doc.go
+++ b/services/ssm/command_run_doc.go
@@ -2,7 +2,7 @@ package ssm
import (
"cmp"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"regexp"
@@ -10,6 +10,8 @@ import (
"strings"
"gopkg.in/yaml.v3"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -68,7 +70,7 @@ func parseScriptSteps(content, format string, params map[string][]string) ([]scr
if format == documentFormatYAML {
err = yaml.Unmarshal([]byte(content), &doc)
} else {
- err = json.Unmarshal([]byte(content), &doc)
+ err = json.Unmarshal([]byte(content), &doc, jsonx.V1())
}
if err != nil {
diff --git a/services/ssm/commands_test.go b/services/ssm/commands_test.go
index d648c6b4ed..9ab8802523 100644
--- a/services/ssm/commands_test.go
+++ b/services/ssm/commands_test.go
@@ -2,7 +2,8 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -385,7 +387,7 @@ func TestCancelCommand_Success(t *testing.T) {
require.Equal(t, http.StatusOK, sendRec.Code)
var sendResp map[string]any
- require.NoError(t, json.Unmarshal(sendRec.Body.Bytes(), &sendResp))
+ require.NoError(t, json.Unmarshal(sendRec.Body.Bytes(), &sendResp, jsonx.V1()))
cmd, ok := sendResp["Command"].(map[string]any)
require.True(t, ok)
@@ -454,13 +456,13 @@ func TestCancelCommand_CancelsInvocations(t *testing.T) {
h, _ := newTestHandler(t)
- instanceIDsJSON, _ := json.Marshal(tt.instanceIDs)
+ instanceIDsJSON, _ := json.Marshal(tt.instanceIDs, jsonx.V1())
sendBody := `{"DocumentName":"AWS-RunShellScript","InstanceIds":` + string(instanceIDsJSON) + `}`
sendRec := doRequest(t, h, "SendCommand", sendBody)
require.Equal(t, http.StatusOK, sendRec.Code)
var sendResp map[string]any
- require.NoError(t, json.Unmarshal(sendRec.Body.Bytes(), &sendResp))
+ require.NoError(t, json.Unmarshal(sendRec.Body.Bytes(), &sendResp, jsonx.V1()))
cmd := sendResp["Command"].(map[string]any)
cmdID := cmd["CommandId"].(string)
@@ -473,7 +475,7 @@ func TestCancelCommand_CancelsInvocations(t *testing.T) {
require.Equal(t, http.StatusOK, invRec.Code)
var invResp map[string]any
- require.NoError(t, json.Unmarshal(invRec.Body.Bytes(), &invResp))
+ require.NoError(t, json.Unmarshal(invRec.Body.Bytes(), &invResp, jsonx.V1()))
assert.Equal(t, tt.wantInvocStatus, invResp["Status"])
assert.Equal(t, tt.wantInvocStatus, invResp["StatusDetails"])
}
@@ -498,7 +500,7 @@ func TestCancelCommand_ScopedToInstanceIDs(t *testing.T) {
require.Equal(t, http.StatusOK, sendRec.Code)
var sendResp map[string]any
- require.NoError(t, json.Unmarshal(sendRec.Body.Bytes(), &sendResp))
+ require.NoError(t, json.Unmarshal(sendRec.Body.Bytes(), &sendResp, jsonx.V1()))
cmdID := sendResp["Command"].(map[string]any)["CommandId"].(string)
cancelRec := doRequest(t, h, "CancelCommand", `{"CommandId":"`+cmdID+`","InstanceIds":["i-001"]}`)
@@ -508,14 +510,14 @@ func TestCancelCommand_ScopedToInstanceIDs(t *testing.T) {
require.Equal(t, http.StatusOK, invRec.Code)
var cancelledInv map[string]any
- require.NoError(t, json.Unmarshal(invRec.Body.Bytes(), &cancelledInv))
+ require.NoError(t, json.Unmarshal(invRec.Body.Bytes(), &cancelledInv, jsonx.V1()))
assert.Equal(t, "Cancelled", cancelledInv["Status"])
otherRec := doRequest(t, h, "GetCommandInvocation", `{"CommandId":"`+cmdID+`","InstanceId":"i-002"}`)
require.Equal(t, http.StatusOK, otherRec.Code)
var otherInv map[string]any
- require.NoError(t, json.Unmarshal(otherRec.Body.Bytes(), &otherInv))
+ require.NoError(t, json.Unmarshal(otherRec.Body.Bytes(), &otherInv, jsonx.V1()))
assert.NotEqual(t, "Cancelled", otherInv["Status"], "un-named instance's invocation must not be cancelled")
}
@@ -887,7 +889,7 @@ func TestHandler_SendCommand(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out ssm.SendCommandOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out.Command.CommandID)
assert.Equal(t, "AWS-RunShellScript", out.Command.DocumentName)
}
@@ -936,7 +938,7 @@ func TestHandler_ListCommands(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var out ssm.ListCommandsOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.Commands, tt.wantCount)
})
}
@@ -957,7 +959,7 @@ func TestHandler_GetCommandInvocation(t *testing.T) {
t.Helper()
rec := doRequest(t, h, "SendCommand", `{"DocumentName":"AWS-RunShellScript","InstanceIDs":["i-abc"]}`)
var out ssm.SendCommandOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
return out.Command.CommandID
},
@@ -973,7 +975,7 @@ func TestHandler_GetCommandInvocation(t *testing.T) {
t.Helper()
rec := doRequest(t, h, "SendCommand", `{"DocumentName":"AWS-RunShellScript","InstanceIDs":["i-abc"]}`)
var out ssm.SendCommandOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
return out.Command.CommandID
},
@@ -1007,7 +1009,7 @@ func TestHandler_GetCommandInvocation(t *testing.T) {
if tt.wantCommandStatus != "" {
var out ssm.GetCommandInvocationOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantCommandStatus, out.Status)
assert.Equal(t, tt.wantCommandStatus, out.StatusDetails)
}
@@ -1055,7 +1057,7 @@ func TestHandler_ListCommandInvocations(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var out ssm.ListCommandInvocationsOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.CommandInvocations, tt.wantCount)
})
}
diff --git a/services/ssm/document_metadata_test.go b/services/ssm/document_metadata_test.go
index e25b922b7d..7476a7b700 100644
--- a/services/ssm/document_metadata_test.go
+++ b/services/ssm/document_metadata_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -143,12 +144,12 @@ func Test_DescribeDocument_OmitsContentAndHonorsVersionSelector(t *testing.T) {
// "Content" field at all — assert this at the JSON level since a Go
// zero-value string would round-trip identically to "field absent" in a
// struct-based comparison.
- createJSON, marshalErr := json.Marshal(createOut.DocumentDescription)
+ createJSON, marshalErr := json.Marshal(createOut.DocumentDescription, jsonx.V1())
require.NoError(t, marshalErr)
assert.NotContains(t, string(createJSON), "Content",
"CreateDocumentOutput.DocumentDescription must not serialize a Content field")
- updateJSON, marshalErr := json.Marshal(updateOut.DocumentDescription)
+ updateJSON, marshalErr := json.Marshal(updateOut.DocumentDescription, jsonx.V1())
require.NoError(t, marshalErr)
assert.NotContains(t, string(updateJSON), "Content",
"UpdateDocumentOutput.DocumentDescription must not serialize a Content field")
@@ -172,7 +173,7 @@ func Test_DescribeDocument_OmitsContentAndHonorsVersionSelector(t *testing.T) {
require.NoError(t, describeErr)
assert.Equal(t, tc.wantVersion, describeOut.Document.DocumentVersion)
- describeJSON, jsonErr := json.Marshal(describeOut.Document)
+ describeJSON, jsonErr := json.Marshal(describeOut.Document, jsonx.V1())
require.NoError(t, jsonErr)
assert.NotContains(t, string(describeJSON), "Content",
"DescribeDocumentOutput.Document must not serialize a Content field")
@@ -639,7 +640,7 @@ func TestDocumentVersions_IsDefaultVersion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions, ok := resp["DocumentVersions"].([]any)
require.True(t, ok, "DocumentVersions must be an array")
@@ -649,7 +650,7 @@ func TestDocumentVersions_IsDefaultVersion(t *testing.T) {
body, _ := json.Marshal(map[string]any{
"Name": "MyDoc",
"DocumentVersion": "2",
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "UpdateDocumentDefaultVersion", string(body))
require.Equal(t, http.StatusOK, rec.Code)
@@ -657,7 +658,7 @@ func TestDocumentVersions_IsDefaultVersion(t *testing.T) {
rec = doRequest(t, h, "ListDocumentVersions", `{"Name":"MyDoc"}`)
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions = resp["DocumentVersions"].([]any)
var defaultFound bool
@@ -723,7 +724,7 @@ func TestDocumentVersions_TableDriven(t *testing.T) {
body, _ := json.Marshal(map[string]any{
"Name": "td-doc-" + tt.name,
"DocumentVersion": tt.setDefault,
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "UpdateDocumentDefaultVersion", string(body))
require.Equal(t, http.StatusOK, rec.Code)
}
@@ -733,7 +734,7 @@ func TestDocumentVersions_TableDriven(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions := resp["DocumentVersions"].([]any)
assert.GreaterOrEqual(t, len(versions), tt.wantVersions)
diff --git a/services/ssm/document_parameters.go b/services/ssm/document_parameters.go
index c033542299..79054369b0 100644
--- a/services/ssm/document_parameters.go
+++ b/services/ssm/document_parameters.go
@@ -1,11 +1,13 @@
package ssm
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
"gopkg.in/yaml.v3"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// DocumentParameter is one declared document parameter (types.DocumentParameter).
@@ -37,7 +39,7 @@ func parseDocumentParameters(content, format string) []DocumentParameter {
if format == documentFormatYAML {
err = yaml.Unmarshal([]byte(content), &doc)
} else {
- err = json.Unmarshal([]byte(content), &doc)
+ err = json.Unmarshal([]byte(content), &doc, jsonx.V1())
}
if err != nil || len(doc.Parameters) == 0 {
@@ -69,7 +71,7 @@ func documentDefaultString(v any) string {
case string:
return val
default:
- if b, err := json.Marshal(val); err == nil {
+ if b, err := json.Marshal(val, jsonx.V1()); err == nil {
return string(b)
}
diff --git a/services/ssm/document_permissions_test.go b/services/ssm/document_permissions_test.go
index d5dab4a8cf..c40b9bed1c 100644
--- a/services/ssm/document_permissions_test.go
+++ b/services/ssm/document_permissions_test.go
@@ -2,13 +2,15 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -165,7 +167,7 @@ func TestDistributorPackage_TableDriven(t *testing.T) {
"Filters": []map[string]any{
{"Key": "DocumentType", "Values": []string{tt.filterType}},
},
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "ListDocuments", string(body))
require.Equal(t, http.StatusOK, rec.Code)
@@ -213,7 +215,7 @@ func TestHandler_CreateDocument(t *testing.T) {
if tt.wantName != "" {
var out ssm.CreateDocumentOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantName, out.DocumentDescription.Name)
assert.Equal(t, "1", out.DocumentDescription.DocumentVersion)
}
@@ -319,7 +321,7 @@ func TestHandler_UpdateDocument(t *testing.T) {
if tt.wantVer != "" {
var out ssm.UpdateDocumentOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantVer, out.DocumentDescription.DocumentVersion)
}
})
@@ -367,7 +369,7 @@ func TestHandler_DescribeDocumentPermission(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var out ssm.DescribeDocumentPermissionOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.AccountIDs, tt.wantLen)
}
})
@@ -467,7 +469,7 @@ func TestHandler_ListDocumentVersions(t *testing.T) {
if tt.wantCount > 0 {
var out ssm.ListDocumentVersionsOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Len(t, out.DocumentVersions, tt.wantCount)
}
})
diff --git a/services/ssm/document_test.go b/services/ssm/document_test.go
index 927f94b09c..a57c97e5f1 100644
--- a/services/ssm/document_test.go
+++ b/services/ssm/document_test.go
@@ -2,7 +2,8 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -458,7 +460,7 @@ func TestGetCalendarState_WithCalendarDocument(t *testing.T) {
// GetCalendarState with the calendar name should succeed.
body, _ := json.Marshal(map[string]any{
"CalendarNames": []string{"MyCalendar"},
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "GetCalendarState", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "OPEN")
@@ -478,7 +480,7 @@ func TestGetCalendarState_NonCalendarDocumentReturnsError(t *testing.T) {
body, _ := json.Marshal(map[string]any{
"CalendarNames": []string{"NotACalendar"},
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "GetCalendarState", string(body))
assert.NotEqual(t, http.StatusOK, rec.Code, "non-ChangeCalendar doc should return error")
}
@@ -495,7 +497,7 @@ func TestHandler_CreateDocument_Duplicate(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec2.Code)
var errResp map[string]string
- require.NoError(t, json.NewDecoder(rec2.Body).Decode(&errResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec2.Body), &errResp, jsonx.V1()))
assert.Contains(t, errResp["__type"], "DocumentAlreadyExists")
}
func TestHandler_GetDocument(t *testing.T) {
@@ -549,7 +551,7 @@ func TestHandler_GetDocument(t *testing.T) {
if tt.wantDoc != "" {
var out ssm.GetDocumentOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantDoc, out.Name)
}
})
@@ -596,7 +598,7 @@ func TestHandler_DescribeDocument(t *testing.T) {
if tt.wantDoc != "" {
var out ssm.DescribeDocumentOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.Equal(t, tt.wantDoc, out.Document.Name)
}
})
@@ -643,7 +645,7 @@ func TestHandler_ListDocuments(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var out ssm.ListDocumentsOutput
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &out, jsonx.V1()))
assert.GreaterOrEqual(t, len(out.DocumentIdentifiers), tt.wantMin)
})
}
diff --git a/services/ssm/documents.go b/services/ssm/documents.go
index df1ac7fb23..3f3e8d56c4 100644
--- a/services/ssm/documents.go
+++ b/services/ssm/documents.go
@@ -2,7 +2,7 @@ package ssm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"sort"
@@ -12,6 +12,7 @@ import (
"gopkg.in/yaml.v3"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/store"
)
@@ -1139,7 +1140,7 @@ func validateDocumentContent(docType, format, content string) error {
if format == documentFormatYAML {
err = yaml.Unmarshal([]byte(content), &doc)
} else {
- err = json.Unmarshal([]byte(content), &doc)
+ err = json.Unmarshal([]byte(content), &doc, jsonx.V1())
}
if err != nil || doc == nil {
diff --git a/services/ssm/epoch_seconds_wire_shape_test.go b/services/ssm/epoch_seconds_wire_shape_test.go
index 05c94246b2..6c75f41da2 100644
--- a/services/ssm/epoch_seconds_wire_shape_test.go
+++ b/services/ssm/epoch_seconds_wire_shape_test.go
@@ -26,13 +26,14 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"regexp"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -54,7 +55,7 @@ func TestEpochSecondsWireShape_AssociationExecution(t *testing.T) {
body, err := json.Marshal(ssm.AssociationExecution{
AssociationID: "assoc-1", ExecutionID: "exec-1", Status: "Success", ExecutionDate: 1_700_000_000,
- })
+ }, jsonx.V1())
require.NoError(t, err)
numericJSONField(t, body, "ExecutionDate")
}
@@ -65,7 +66,7 @@ func TestEpochSecondsWireShape_MaintenanceWindowExecution(t *testing.T) {
body, err := json.Marshal(ssm.MaintenanceWindowExecution{
WindowID: "mw-1", WindowExecutionID: "mwexec-1", Status: "SUCCESS",
StartTime: 1_700_000_000, EndTime: 1_700_003_600,
- })
+ }, jsonx.V1())
require.NoError(t, err)
numericJSONField(t, body, "StartTime")
numericJSONField(t, body, "EndTime")
@@ -76,7 +77,7 @@ func TestEpochSecondsWireShape_InstanceInformation(t *testing.T) {
body, err := json.Marshal(ssm.InstanceInformation{
InstanceID: "i-1", PingStatus: "Online", RegistrationDate: 1_700_000_000,
- })
+ }, jsonx.V1())
require.NoError(t, err)
numericJSONField(t, body, "RegistrationDate")
}
@@ -86,7 +87,7 @@ func TestEpochSecondsWireShape_InstanceAssociationStatusInfo(t *testing.T) {
body, err := json.Marshal(ssm.InstanceAssociationStatusInfo{
AssociationID: "assoc-1", Name: "MyDoc", Status: "Success", ExecutionDate: 1_700_000_000,
- })
+ }, jsonx.V1())
require.NoError(t, err)
numericJSONField(t, body, "ExecutionDate")
}
@@ -96,7 +97,7 @@ func TestEpochSecondsWireShape_InstancePatchState(t *testing.T) {
body, err := json.Marshal(ssm.InstancePatchState{
InstanceID: "i-1", PatchGroup: "grp1", Operation: "Scan", OperationStartTime: 1_700_000_000,
- })
+ }, jsonx.V1())
require.NoError(t, err)
numericJSONField(t, body, "OperationStartTime")
}
@@ -106,7 +107,7 @@ func TestEpochSecondsWireShape_PatchComplianceData(t *testing.T) {
body, err := json.Marshal(ssm.PatchComplianceData{
Title: "KB123", State: "INSTALLED", InstalledTime: 1_700_000_000,
- })
+ }, jsonx.V1())
require.NoError(t, err)
numericJSONField(t, body, "InstalledTime")
}
@@ -117,7 +118,7 @@ func TestEpochSecondsWireShape_ResourceDataSync(t *testing.T) {
body, err := json.Marshal(ssm.ResourceDataSync{
SyncName: "sync-1", SyncType: "SyncToDestination", LastStatus: "InProgress",
SyncCreatedTime: 1_700_000_000, LastSyncTime: 1_700_000_100,
- })
+ }, jsonx.V1())
require.NoError(t, err)
numericJSONField(t, body, "SyncCreatedTime")
numericJSONField(t, body, "LastSyncTime")
@@ -129,7 +130,7 @@ func TestEpochSecondsWireShape_InventoryDeletion(t *testing.T) {
body, err := json.Marshal(ssm.InventoryDeletion{
DeletionID: "deletion-1", TypeName: "AWS:Application", LastStatus: "Complete",
DeletionStartTime: 1_700_000_000,
- })
+ }, jsonx.V1())
require.NoError(t, err)
numericJSONField(t, body, "DeletionStartTime")
}
@@ -140,7 +141,7 @@ func TestEpochSecondsWireShape_Node(t *testing.T) {
body, err := json.Marshal(ssm.Node{
ID: "i-1", CaptureTime: 1_700_000_000,
NodeType: &ssm.NodeType{Instance: &ssm.NodeInstanceInfo{PlatformType: "Linux"}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
numericJSONField(t, body, "CaptureTime")
}
diff --git a/services/ssm/handler.go b/services/ssm/handler.go
index 95dff1eac0..e7a2dea719 100644
--- a/services/ssm/handler.go
+++ b/services/ssm/handler.go
@@ -2,7 +2,7 @@ package ssm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -15,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -132,7 +133,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var data map[string]any
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
@@ -185,7 +186,7 @@ type ssmActionFn func(context.Context, []byte) (any, error)
func jsonOp[I, O any](fn func(context.Context, *I) (O, error), checks ...func(*I) error) ssmActionFn {
return func(ctx context.Context, b []byte) (any, error) {
var input I
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -239,7 +240,7 @@ func (h *Handler) ssmTagOps() map[string]ssmActionFn {
return map[string]ssmActionFn{
"AddTagsToResource": func(ctx context.Context, b []byte) (any, error) {
var input AddTagsToResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -247,7 +248,7 @@ func (h *Handler) ssmTagOps() map[string]ssmActionFn {
},
"RemoveTagsFromResource": func(ctx context.Context, b []byte) (any, error) {
var input RemoveTagsFromResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -316,7 +317,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(response)
+ return json.Marshal(response, jsonx.V1())
}
// classifySSMError maps a backend error to an HTTP status code and error type string.
@@ -562,7 +563,7 @@ func (h *Handler) handleError(ctx context.Context, c *echo.Context, action strin
Message: reqErr.Error(),
}
- payload, _ := json.Marshal(errResp)
+ payload, _ := json.Marshal(errResp, jsonx.V1())
return c.JSONBlob(statusCode, payload)
}
diff --git a/services/ssm/handler_agent.go b/services/ssm/handler_agent.go
index 75f095eb6e..737b6b037d 100644
--- a/services/ssm/handler_agent.go
+++ b/services/ssm/handler_agent.go
@@ -2,13 +2,14 @@ package ssm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"slices"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -42,7 +43,7 @@ func (h *Handler) agentOps() map[string]ssmActionFn {
return map[string]ssmActionFn{
"GetEndpoint": func(ctx context.Context, body []byte) (any, error) {
var in agentGetEndpointInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
@@ -56,7 +57,7 @@ func (h *Handler) agentOps() map[string]ssmActionFn {
},
"GetMessages": func(ctx context.Context, body []byte) (any, error) {
var in AgentGetMessagesInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
@@ -77,7 +78,7 @@ func (h *Handler) agentOps() map[string]ssmActionFn {
}),
"SendReply": func(ctx context.Context, body []byte) (any, error) {
var in agentSendReplyInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
@@ -89,7 +90,7 @@ func (h *Handler) agentOps() map[string]ssmActionFn {
func agentMessageOp(fn func(context.Context, *agentMessageIDInput) error) ssmActionFn {
return func(ctx context.Context, body []byte) (any, error) {
var in agentMessageIDInput
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
@@ -117,7 +118,7 @@ func (h *Handler) handleAgent(ctx context.Context, c *echo.Context) error {
return nil, err
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
},
h.handleError,
)
diff --git a/services/ssm/handler_parameters_test.go b/services/ssm/handler_parameters_test.go
index cf757e93b8..c9839cdb64 100644
--- a/services/ssm/handler_parameters_test.go
+++ b/services/ssm/handler_parameters_test.go
@@ -2,7 +2,8 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -620,9 +622,9 @@ func TestHandlerParameterListingOperations(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var out struct {
- Parameters []json.RawMessage `json:"Parameters"`
+ Parameters []jsontext.Value `json:"Parameters"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Parameters, tt.wantCount)
})
}
@@ -659,7 +661,7 @@ func TestTagOperations(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code)
var listOut ssm.ListTagsForResourceOutput
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.TagList, 2)
assert.Equal(t, "env", listOut.TagList[0].Key)
assert.Equal(t, "prod", listOut.TagList[0].Value)
diff --git a/services/ssm/handler_test.go b/services/ssm/handler_test.go
index 95b2483da7..c9a84f6e31 100644
--- a/services/ssm/handler_test.go
+++ b/services/ssm/handler_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -255,7 +256,7 @@ func TestHandler_ErrorCases(t *testing.T) {
if tt.wantErrTyp != "" {
var errResp service.JSONErrorResponse
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantErrTyp, errResp.Type)
}
})
@@ -362,7 +363,7 @@ func TestHandler_GetParametersByPathViaHTTP(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out ssm.GetParametersByPathOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Parameters, 2)
}
@@ -381,7 +382,7 @@ func TestHandler_DescribeParametersViaHTTP(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var out ssm.DescribeParametersOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Parameters, 2)
}
diff --git a/services/ssm/idempotency.go b/services/ssm/idempotency.go
index 11750c0697..e33fed7f7a 100644
--- a/services/ssm/idempotency.go
+++ b/services/ssm/idempotency.go
@@ -3,9 +3,11 @@ package ssm
import (
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// maxIdempotencyRecords caps remembered tokens per region; the oldest are evicted first.
@@ -20,19 +22,19 @@ type idempotencyRecord struct {
}
func idempotencyFingerprint(req any) string {
- raw, err := json.Marshal(req)
+ raw, err := json.Marshal(req, jsonx.V1())
if err != nil {
return ""
}
var m map[string]any
- if json.Unmarshal(raw, &m) != nil {
+ if json.Unmarshal(raw, &m, jsonx.V1()) != nil {
return ""
}
delete(m, "ClientToken")
- canonical, err := json.Marshal(m)
+ canonical, err := json.Marshal(m, jsonx.V1())
if err != nil {
return ""
}
diff --git a/services/ssm/inventory_test.go b/services/ssm/inventory_test.go
index 3cd6ea8176..6b1b32966e 100644
--- a/services/ssm/inventory_test.go
+++ b/services/ssm/inventory_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -1050,7 +1051,7 @@ func TestComplianceSummaries_AggregateByCType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["ComplianceSummaryItems"].([]any)
assert.NotEmpty(t, items, "should have compliance summary items")
@@ -1087,7 +1088,7 @@ func TestResourceComplianceSummaries_PerResource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items := resp["ResourceComplianceSummaryItems"].([]any)
assert.NotEmpty(t, items)
@@ -1125,7 +1126,7 @@ func TestResourceComplianceSummaries_NonCompliantStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
for _, item := range resp["ResourceComplianceSummaryItems"].([]any) {
m := item.(map[string]any)
@@ -1143,7 +1144,7 @@ func TestGetInventorySchema_ReturnsBuiltInTypes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
schemas := resp["Schemas"].([]any)
assert.NotEmpty(t, schemas, "GetInventorySchema should return built-in schema types")
@@ -1187,7 +1188,7 @@ func TestGetInventorySchema_FilterByTypeName(t *testing.T) {
h, _ := newTestHandler(t)
- body, _ := json.Marshal(map[string]any{"TypeName": tt.typeName})
+ body, _ := json.Marshal(map[string]any{"TypeName": tt.typeName}, jsonx.V1())
rec := doRequest(t, h, "GetInventorySchema", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), tt.wantContains)
@@ -1219,7 +1220,7 @@ func TestInventory_TypeMergeAndLookup(t *testing.T) {
body, _ := json.Marshal(map[string]any{
"InstanceId": "i-inv001",
"TypeName": "AWS:Application",
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "ListInventoryEntries", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "aws-cli")
@@ -1257,7 +1258,7 @@ func TestInventory_TypeMergeAndLookup(t *testing.T) {
body, _ = json.Marshal(map[string]any{
"InstanceId": "i-inv001",
"TypeName": "AWS:Application",
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "ListInventoryEntries", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "2.1")
diff --git a/services/ssm/janitor.go b/services/ssm/janitor.go
index 2b50f2c0c8..0843e4bdf0 100644
--- a/services/ssm/janitor.go
+++ b/services/ssm/janitor.go
@@ -2,10 +2,11 @@ package ssm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/telemetry"
"github.com/blackbirdworks/gopherstack/pkgs/worker"
@@ -303,7 +304,7 @@ func parameterExpiresAt(policiesJSON string) (time.Time, bool) {
}
var policies []parameterExpirationPolicy
- if err := json.Unmarshal([]byte(policiesJSON), &policies); err != nil {
+ if err := json.Unmarshal([]byte(policiesJSON), &policies, jsonx.V1()); err != nil {
return time.Time{}, false
}
diff --git a/services/ssm/maintenance_window_execution_test.go b/services/ssm/maintenance_window_execution_test.go
index 8f211e14e2..9b195b790f 100644
--- a/services/ssm/maintenance_window_execution_test.go
+++ b/services/ssm/maintenance_window_execution_test.go
@@ -2,13 +2,14 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -195,7 +196,7 @@ func TestCancelMaintenanceWindowExecution(t *testing.T) {
body := tt.makeBody(t, b)
var reqBody map[string]string
- require.NoError(t, json.Unmarshal([]byte(body), &reqBody))
+ require.NoError(t, json.Unmarshal([]byte(body), &reqBody, jsonx.V1()))
wantExecID := reqBody["WindowExecutionId"]
rec := doRequest(t, h, "CancelMaintenanceWindowExecution", body)
@@ -207,7 +208,7 @@ func TestCancelMaintenanceWindowExecution(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, wantExecID, resp["WindowExecutionId"])
})
}
diff --git a/services/ssm/maintenance_window_lifecycle_test.go b/services/ssm/maintenance_window_lifecycle_test.go
index c6bb6d3753..953fd272c4 100644
--- a/services/ssm/maintenance_window_lifecycle_test.go
+++ b/services/ssm/maintenance_window_lifecycle_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -189,7 +190,7 @@ func TestStubOps_DeleteMaintenanceWindow(t *testing.T) {
})
require.NoError(t, err)
- body, _ := json.Marshal(map[string]any{"WindowId": mw.WindowID})
+ body, _ := json.Marshal(map[string]any{"WindowId": mw.WindowID}, jsonx.V1())
rec := doRequest(t, h, "DeleteMaintenanceWindow", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -208,7 +209,7 @@ func TestStubOps_GetMaintenanceWindow(t *testing.T) {
})
require.NoError(t, err)
- body, _ := json.Marshal(map[string]any{"WindowId": mw.WindowID})
+ body, _ := json.Marshal(map[string]any{"WindowId": mw.WindowID}, jsonx.V1())
rec := doRequest(t, h, "GetMaintenanceWindow", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -227,7 +228,7 @@ func TestStubOps_UpdateMaintenanceWindow(t *testing.T) {
})
require.NoError(t, err)
- body, _ := json.Marshal(map[string]any{"WindowId": mw.WindowID, "Name": "updated-window"})
+ body, _ := json.Marshal(map[string]any{"WindowId": mw.WindowID, "Name": "updated-window"}, jsonx.V1())
rec := doRequest(t, h, "UpdateMaintenanceWindow", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -263,7 +264,7 @@ func TestCreateMaintenanceWindow_Success(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var resp ssm.CreateMaintenanceWindowOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.WindowID)
assert.True(t, strings.HasPrefix(resp.WindowID, "mw-"))
assert.Equal(t, 1, backend.MaintenanceWindowCount())
@@ -296,7 +297,7 @@ func TestMaintenanceWindow_ScheduleFieldsRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var created ssm.CreateMaintenanceWindowOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
out, err := b.GetMaintenanceWindow(context.Background(), &ssm.GetMaintenanceWindowInput{WindowID: created.WindowID})
require.NoError(t, err)
@@ -750,7 +751,7 @@ func TestMaintenanceWindowsForTarget_FindsByTarget(t *testing.T) {
"Targets": []map[string]any{
{"Key": "InstanceIds", "Values": []string{"i-aabbcc112233"}},
},
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "RegisterTargetWithMaintenanceWindow", string(body))
require.Equal(t, http.StatusOK, rec.Code)
@@ -760,7 +761,7 @@ func TestMaintenanceWindowsForTarget_FindsByTarget(t *testing.T) {
"Targets": []map[string]any{
{"Key": "InstanceIds", "Values": []string{"i-aabbcc112233"}},
},
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "DescribeMaintenanceWindowsForTarget", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), mw.WindowID)
diff --git a/services/ssm/maintenance_window_targets_test.go b/services/ssm/maintenance_window_targets_test.go
index 585da73657..257aa6aa1a 100644
--- a/services/ssm/maintenance_window_targets_test.go
+++ b/services/ssm/maintenance_window_targets_test.go
@@ -2,13 +2,14 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -32,7 +33,7 @@ func TestStubOps_RegisterTargetWithMaintenanceWindow(t *testing.T) {
"Targets": []map[string]any{
{"Key": "InstanceIds", "Values": []string{"i-1234567890abcdef0"}},
},
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "RegisterTargetWithMaintenanceWindow", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -55,7 +56,7 @@ func TestStubOps_RegisterTaskWithMaintenanceWindow(t *testing.T) {
"WindowId": mw.WindowID,
"TaskType": "RUN_COMMAND",
"TaskArn": "AWS-RunShellScript",
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "RegisterTaskWithMaintenanceWindow", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -104,19 +105,19 @@ func TestMaintenanceWindowTask_ServiceRoleArn(t *testing.T) {
"ServiceRoleArn": tt.serviceRoleArn,
"MaxConcurrency": tt.maxConcurrency,
"MaxErrors": tt.maxErrors,
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "RegisterTaskWithMaintenanceWindow", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var regResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp, jsonx.V1()))
taskID := regResp["WindowTaskId"].(string)
// GetMaintenanceWindowTask should return ServiceRoleArn.
body, _ = json.Marshal(map[string]any{
"WindowId": mw.WindowID,
"WindowTaskId": taskID,
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "GetMaintenanceWindowTask", string(body))
require.Equal(t, http.StatusOK, rec.Code)
@@ -147,12 +148,12 @@ func TestMaintenanceWindowTask_UpdateServiceRoleArn(t *testing.T) {
"WindowId": mw.WindowID,
"TaskType": "RUN_COMMAND",
"TaskArn": "AWS-RunShellScript",
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "RegisterTaskWithMaintenanceWindow", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var regResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), ®Resp, jsonx.V1()))
taskID := regResp["WindowTaskId"].(string)
// Update with ServiceRoleArn.
@@ -160,7 +161,7 @@ func TestMaintenanceWindowTask_UpdateServiceRoleArn(t *testing.T) {
"WindowId": mw.WindowID,
"WindowTaskId": taskID,
"ServiceRoleArn": "arn:aws:iam::123456789012:role/UpdatedRole",
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "UpdateMaintenanceWindowTask", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "UpdatedRole")
@@ -230,13 +231,13 @@ func TestDeregisterTargetFromMaintenanceWindow_TableDriven(t *testing.T) {
body, _ := json.Marshal(map[string]any{
"WindowId": mw.WindowID,
"WindowTargetId": windowTargetID,
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "DeregisterTargetFromMaintenanceWindow", string(body))
assert.Equal(t, tt.wantStatus, rec.Code)
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tt.wantWindowID {
assert.Equal(t, mw.WindowID, resp["WindowId"])
}
@@ -310,13 +311,13 @@ func TestDeregisterTaskFromMaintenanceWindow_TableDriven(t *testing.T) {
body, _ := json.Marshal(map[string]any{
"WindowId": mw.WindowID,
"WindowTaskId": windowTaskID,
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "DeregisterTaskFromMaintenanceWindow", string(body))
assert.Equal(t, tt.wantStatus, rec.Code)
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tt.wantWindowID {
assert.Equal(t, mw.WindowID, resp["WindowId"])
}
diff --git a/services/ssm/maintenance_window_test.go b/services/ssm/maintenance_window_test.go
index f371bf910f..fd53c7eea8 100644
--- a/services/ssm/maintenance_window_test.go
+++ b/services/ssm/maintenance_window_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -165,7 +166,7 @@ func TestPatchBaseline_DefaultOS(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp ssm.CreatePatchBaselineOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.NotEmpty(t, resp.BaselineID)
bl := backend.GetPatchBaselineInternal(resp.BaselineID)
@@ -734,12 +735,12 @@ func TestMaintenanceWindowsForTarget_NoMatchReturnsEmpty(t *testing.T) {
"Targets": []map[string]any{
{"Key": "InstanceIds", "Values": []string{"i-notregistered"}},
},
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "DescribeMaintenanceWindowsForTarget", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
identities := resp["WindowIdentities"].([]any)
assert.Empty(t, identities)
diff --git a/services/ssm/ops_items_test.go b/services/ssm/ops_items_test.go
index c886985060..3dbad0e73f 100644
--- a/services/ssm/ops_items_test.go
+++ b/services/ssm/ops_items_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -30,7 +31,7 @@ func TestStubOps_DeleteOpsItem(t *testing.T) {
})
require.NoError(t, err)
- body, _ := json.Marshal(map[string]any{"OpsItemId": item.OpsItemID})
+ body, _ := json.Marshal(map[string]any{"OpsItemId": item.OpsItemID}, jsonx.V1())
rec := doRequest(t, h, "DeleteOpsItem", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -59,7 +60,7 @@ func TestStubOps_GetOpsItem(t *testing.T) {
})
require.NoError(t, err)
- body, _ := json.Marshal(map[string]any{"OpsItemId": item.OpsItemID})
+ body, _ := json.Marshal(map[string]any{"OpsItemId": item.OpsItemID}, jsonx.V1())
rec := doRequest(t, h, "GetOpsItem", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -79,7 +80,7 @@ func TestStubOps_UpdateOpsItem(t *testing.T) {
})
require.NoError(t, err)
- body, _ := json.Marshal(map[string]any{"OpsItemId": item.OpsItemID, "Title": "updated-item"})
+ body, _ := json.Marshal(map[string]any{"OpsItemId": item.OpsItemID, "Title": "updated-item"}, jsonx.V1())
rec := doRequest(t, h, "UpdateOpsItem", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -114,7 +115,7 @@ func TestCreateOpsItem_Success(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var resp ssm.CreateOpsItemOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.OpsItemID)
assert.NotEmpty(t, resp.OpsItemArn)
assert.True(t, strings.HasPrefix(resp.OpsItemID, "oi-"))
@@ -201,7 +202,7 @@ func TestAssociateOpsItemRelatedItem(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp ssm.CreateOpsItemOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
body = `{"OpsItemId":"` + createResp.OpsItemID +
`","AssociationType":"RelatesTo","ResourceType":"AWS::EC2::Instance",` +
@@ -215,7 +216,7 @@ func TestAssociateOpsItemRelatedItem(t *testing.T) {
assert.Contains(t, rec.Body.String(), tt.wantErr)
} else {
var resp ssm.AssociateOpsItemRelatedItemOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.AssociationID)
}
})
@@ -901,7 +902,7 @@ func TestOpsItem_OperationalData_CreateAndUpdate(t *testing.T) {
require.NoError(t, err)
// GetOpsItem should have initial OperationalData.
- body, _ := json.Marshal(map[string]any{"OpsItemId": item.OpsItemID})
+ body, _ := json.Marshal(map[string]any{"OpsItemId": item.OpsItemID}, jsonx.V1())
rec := doRequest(t, h, "GetOpsItem", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "/aws/resources")
@@ -915,12 +916,12 @@ func TestOpsItem_OperationalData_CreateAndUpdate(t *testing.T) {
"Value": `[{"automationId":"auto-123"}]`,
},
},
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "UpdateOpsItem", string(body))
require.Equal(t, http.StatusOK, rec.Code)
// GetOpsItem should have both keys.
- body, _ = json.Marshal(map[string]any{"OpsItemId": item.OpsItemID})
+ body, _ = json.Marshal(map[string]any{"OpsItemId": item.OpsItemID}, jsonx.V1())
rec = doRequest(t, h, "GetOpsItem", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "/aws/resources")
@@ -974,11 +975,11 @@ func TestOpsItem_OperationalData_TableDriven(t *testing.T) {
body, _ := json.Marshal(map[string]any{
"OpsItemId": item.OpsItemID,
"OperationalData": tt.updateData,
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "UpdateOpsItem", string(body))
require.Equal(t, http.StatusOK, rec.Code)
- body, _ = json.Marshal(map[string]any{"OpsItemId": item.OpsItemID})
+ body, _ = json.Marshal(map[string]any{"OpsItemId": item.OpsItemID}, jsonx.V1())
rec = doRequest(t, h, "GetOpsItem", string(body))
require.Equal(t, http.StatusOK, rec.Code)
@@ -1001,12 +1002,12 @@ func TestOpsItemEvents_TrackCreates(t *testing.T) {
require.NoError(t, err)
// ListOpsItemEvents for this item should return at least one event.
- body, _ := json.Marshal(map[string]any{"OpsItemId": item.OpsItemID})
+ body, _ := json.Marshal(map[string]any{"OpsItemId": item.OpsItemID}, jsonx.V1())
rec := doRequest(t, h, "ListOpsItemEvents", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["Summaries"].([]any)
assert.NotEmpty(t, summaries, "should have at least one event after create")
@@ -1027,16 +1028,16 @@ func TestOpsItemEvents_TrackUpdates(t *testing.T) {
body, _ := json.Marshal(map[string]any{
"OpsItemId": item.OpsItemID,
"Description": "Updated description",
- })
+ }, jsonx.V1())
doRequest(t, h, "UpdateOpsItem", string(body))
// ListOpsItemEvents should have multiple events now.
- body, _ = json.Marshal(map[string]any{"OpsItemId": item.OpsItemID})
+ body, _ = json.Marshal(map[string]any{"OpsItemId": item.OpsItemID}, jsonx.V1())
rec := doRequest(t, h, "ListOpsItemEvents", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries := resp["Summaries"].([]any)
assert.GreaterOrEqual(t, len(summaries), 2, "create + update should each produce an event")
@@ -1054,7 +1055,7 @@ func TestOpsItemEvents_FilterByOpsItemID(t *testing.T) {
require.NoError(t, err)
// Filter by item1 — should only get item1 events.
- body, _ := json.Marshal(map[string]any{"OpsItemId": item1.OpsItemID})
+ body, _ := json.Marshal(map[string]any{"OpsItemId": item1.OpsItemID}, jsonx.V1())
rec := doRequest(t, h, "ListOpsItemEvents", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), item1.OpsItemID)
@@ -1109,7 +1110,7 @@ func TestUpdateOpsItem_TableDriven(t *testing.T) {
payload := map[string]any{"OpsItemId": opsItemID}
maps.Copy(payload, tt.update)
- body, _ := json.Marshal(payload)
+ body, _ := json.Marshal(payload, jsonx.V1())
rec := doRequest(t, h, "UpdateOpsItem", string(body))
assert.Equal(t, tt.wantStatus, rec.Code)
diff --git a/services/ssm/ops_metadata_test.go b/services/ssm/ops_metadata_test.go
index 8f1e2a8abf..a97328e026 100644
--- a/services/ssm/ops_metadata_test.go
+++ b/services/ssm/ops_metadata_test.go
@@ -2,13 +2,14 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -45,7 +46,7 @@ func TestStubOps_DeleteOpsMetadata(t *testing.T) {
})
require.NoError(t, err)
- body, _ := json.Marshal(map[string]any{"OpsMetadataArn": meta.OpsMetadataArn})
+ body, _ := json.Marshal(map[string]any{"OpsMetadataArn": meta.OpsMetadataArn}, jsonx.V1())
rec := doRequest(t, h, "DeleteOpsMetadata", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -61,7 +62,7 @@ func TestStubOps_GetOpsMetadata(t *testing.T) {
})
require.NoError(t, err)
- body, _ := json.Marshal(map[string]any{"OpsMetadataArn": meta.OpsMetadataArn})
+ body, _ := json.Marshal(map[string]any{"OpsMetadataArn": meta.OpsMetadataArn}, jsonx.V1())
rec := doRequest(t, h, "GetOpsMetadata", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -77,7 +78,7 @@ func TestStubOps_UpdateOpsMetadata(t *testing.T) {
})
require.NoError(t, err)
- body, _ := json.Marshal(map[string]any{"OpsMetadataArn": meta.OpsMetadataArn})
+ body, _ := json.Marshal(map[string]any{"OpsMetadataArn": meta.OpsMetadataArn}, jsonx.V1())
rec := doRequest(t, h, "UpdateOpsMetadata", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -111,7 +112,7 @@ func TestCreateOpsMetadata_Success(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var resp ssm.CreateOpsMetadataOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.OpsMetadataArn)
assert.Contains(t, resp.OpsMetadataArn, "arn:aws:ssm:")
assert.Equal(t, 1, backend.OpsMetadataCount())
@@ -566,7 +567,7 @@ func TestClassifySSMErrorExtended(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantErrType, resp["__type"])
})
}
@@ -592,7 +593,7 @@ func TestOpsMetadata_FullCRUD(t *testing.T) {
assert.Contains(t, rec.Body.String(), "OpsMetadataList")
// Get.
- body, _ := json.Marshal(map[string]any{"OpsMetadataArn": meta.OpsMetadataArn})
+ body, _ := json.Marshal(map[string]any{"OpsMetadataArn": meta.OpsMetadataArn}, jsonx.V1())
rec = doRequest(t, h, "GetOpsMetadata", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "production")
@@ -604,19 +605,19 @@ func TestOpsMetadata_FullCRUD(t *testing.T) {
"MetadataToUpdate": map[string]any{
"version": map[string]any{"Value": "2"},
},
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "UpdateOpsMetadata", string(body))
require.Equal(t, http.StatusOK, rec.Code)
// Get updated — both keys should be present.
- body, _ = json.Marshal(map[string]any{"OpsMetadataArn": meta.OpsMetadataArn})
+ body, _ = json.Marshal(map[string]any{"OpsMetadataArn": meta.OpsMetadataArn}, jsonx.V1())
rec = doRequest(t, h, "GetOpsMetadata", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "production")
assert.Contains(t, rec.Body.String(), "version")
// Delete.
- body, _ = json.Marshal(map[string]any{"OpsMetadataArn": meta.OpsMetadataArn})
+ body, _ = json.Marshal(map[string]any{"OpsMetadataArn": meta.OpsMetadataArn}, jsonx.V1())
rec = doRequest(t, h, "DeleteOpsMetadata", string(body))
require.Equal(t, http.StatusOK, rec.Code)
}
diff --git a/services/ssm/parameter_encryption_test.go b/services/ssm/parameter_encryption_test.go
index b1672cd327..589d1b4dde 100644
--- a/services/ssm/parameter_encryption_test.go
+++ b/services/ssm/parameter_encryption_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -493,7 +494,7 @@ func TestSSMHandler_GetParameters(t *testing.T) {
t.Helper()
var out ssm.GetParametersOutput
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
assert.Len(t, out.Parameters, 2)
assert.Empty(t, out.InvalidParameters)
},
@@ -512,7 +513,7 @@ func TestSSMHandler_GetParameters(t *testing.T) {
t.Helper()
var out ssm.GetParametersOutput
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
assert.Len(t, out.Parameters, 1)
assert.Len(t, out.InvalidParameters, 1)
assert.Equal(t, "/absent", out.InvalidParameters[0])
@@ -533,7 +534,7 @@ func TestSSMHandler_GetParameters(t *testing.T) {
t.Helper()
var out ssm.GetParametersOutput
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
assert.Len(t, out.Parameters, 1)
assert.Equal(t, "topsecret", out.Parameters[0].Value)
},
@@ -708,7 +709,7 @@ func TestSecureStringHistory_WithDecryption(t *testing.T) {
body, _ := json.Marshal(map[string]any{
"Name": "/secure/pwd",
"WithDecryption": tt.withDecryption,
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "GetParameterHistory", string(body))
require.Equal(t, http.StatusOK, rec.Code)
diff --git a/services/ssm/parameter_labels_test.go b/services/ssm/parameter_labels_test.go
index 1d8838c8fb..9a2cad438c 100644
--- a/services/ssm/parameter_labels_test.go
+++ b/services/ssm/parameter_labels_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -393,7 +394,7 @@ func TestParameterLabels_VersionSpecific(t *testing.T) {
"Name": "/app/key",
"Labels": []string{"stable"},
"ParameterVersion": 1,
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "LabelParameterVersion", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "ParameterVersion")
@@ -403,7 +404,7 @@ func TestParameterLabels_VersionSpecific(t *testing.T) {
"Name": "/app/key",
"Labels": []string{"latest"},
"ParameterVersion": 2,
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "LabelParameterVersion", string(body))
require.Equal(t, http.StatusOK, rec.Code)
@@ -464,7 +465,7 @@ func TestParameterLabels_UnlabelSpecificVersion(t *testing.T) {
"Name": "/app/ver",
"Labels": []string{"prod", "stable"},
"ParameterVersion": 1,
- })
+ }, jsonx.V1())
doRequest(t, h, "LabelParameterVersion", string(body))
// Remove one label.
@@ -472,7 +473,7 @@ func TestParameterLabels_UnlabelSpecificVersion(t *testing.T) {
"Name": "/app/ver",
"Labels": []string{"prod"},
"ParameterVersion": 1,
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "UnlabelParameterVersion", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "RemovedLabels")
diff --git a/services/ssm/parameter_policy_notifications.go b/services/ssm/parameter_policy_notifications.go
index 6f94899c82..1fac2e9867 100644
--- a/services/ssm/parameter_policy_notifications.go
+++ b/services/ssm/parameter_policy_notifications.go
@@ -2,11 +2,13 @@ package ssm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"sort"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ParameterPolicyNotifier receives Parameter Store policy-action
@@ -67,7 +69,7 @@ func parseParameterPolicies(policiesJSON string) []parameterStorePolicy {
}
var policies []parameterStorePolicy
- if err := json.Unmarshal([]byte(policiesJSON), &policies); err != nil {
+ if err := json.Unmarshal([]byte(policiesJSON), &policies, jsonx.V1()); err != nil {
return nil
}
@@ -93,7 +95,7 @@ func parameterPoliciesToWire(policiesJSON string, lastModified float64, now time
wire := make([]ParameterInlinePolicy, 0, len(parsed))
for _, p := range parsed {
- text, err := json.Marshal(p)
+ text, err := json.Marshal(p, jsonx.V1())
if err != nil {
continue
}
diff --git a/services/ssm/parameters_handler_test.go b/services/ssm/parameters_handler_test.go
index a6e5b26a4b..928b0cb2b0 100644
--- a/services/ssm/parameters_handler_test.go
+++ b/services/ssm/parameters_handler_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/http/httptest"
@@ -14,6 +14,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -77,7 +78,7 @@ func TestSSMHandler_UnknownOperation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["__type"], "UnknownOperationException")
}
func TestSSMHandler_InternalError(t *testing.T) {
@@ -198,7 +199,7 @@ func TestSSMHandler_ValidationError(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ParameterPatternMismatchException", resp["__type"])
}
@@ -216,7 +217,7 @@ func TestSSMHandler_ErrInvalidKeyID(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["__type"], "UnknownOperationException")
}
@@ -328,14 +329,14 @@ func TestDeepCopy_Association(t *testing.T) {
doRequest(t, h, "CreateDocument",
`{"Name":"`+tt.docName+`","Content":"{\"schemaVersion\":\"2.2\"}"}`)
- paramsJSON, _ := json.Marshal(tt.params)
+ paramsJSON, _ := json.Marshal(tt.params, jsonx.V1())
createBody := `{"Name":"` + tt.docName + `","Parameters":` + string(paramsJSON) + `}`
rec := doRequest(t, h, "CreateAssociation", createBody)
require.Equal(t, http.StatusOK, rec.Code)
var createResp ssm.CreateAssociationOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
originalCmds := createResp.AssociationDescription.Parameters[tt.mutateKey]
// Verify original value was stored correctly
@@ -600,7 +601,7 @@ func TestAdvancedTier_AcceptsLargeValue(t *testing.T) {
"Value": string(largeVal),
"Type": "String",
"Tier": "Advanced",
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "PutParameter", string(body))
assert.Equal(t, http.StatusOK, rec.Code, "Advanced tier should accept 5KiB value")
}
@@ -620,7 +621,7 @@ func TestStandardTier_RejectsLargeValue(t *testing.T) {
"Value": string(largeVal),
"Type": "String",
"Tier": "Standard",
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "PutParameter", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code, "Standard tier should reject 5KiB value")
}
diff --git a/services/ssm/parameters_test.go b/services/ssm/parameters_test.go
index f92ca2dcaf..10e5f7d32f 100644
--- a/services/ssm/parameters_test.go
+++ b/services/ssm/parameters_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/kms"
"github.com/blackbirdworks/gopherstack/services/ssm"
@@ -557,13 +558,13 @@ func TestDeleteParameter_RegionCleanup(t *testing.T) {
func postJSON(t *testing.T, h *ssm.Handler, op string, body any) (int, map[string]any) {
t.Helper()
- payload, err := json.Marshal(body)
+ payload, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, op, string(payload))
var out map[string]any
- _ = json.Unmarshal(rec.Body.Bytes(), &out)
+ _ = json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1())
return rec.Code, out
}
diff --git a/services/ssm/patch_baseline_approval_rules_test.go b/services/ssm/patch_baseline_approval_rules_test.go
index f5d328cad0..b9b1dcead2 100644
--- a/services/ssm/patch_baseline_approval_rules_test.go
+++ b/services/ssm/patch_baseline_approval_rules_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -400,7 +401,7 @@ func TestSendCommand_RunPatchBaseline_ApprovalRules_Compliance(t *testing.T) {
var created struct {
BaselineID string `json:"BaselineId"`
}
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &created, jsonx.V1()))
regResp := doRequest(t, h, "RegisterPatchBaselineForPatchGroup", `{
"BaselineId":"`+created.BaselineID+`","PatchGroup":"rule-group"
@@ -423,7 +424,7 @@ func TestSendCommand_RunPatchBaseline_ApprovalRules_Compliance(t *testing.T) {
State string `json:"State"`
} `json:"Patches"`
}
- require.NoError(t, json.Unmarshal(patchesResp.Body.Bytes(), &patches))
+ require.NoError(t, json.Unmarshal(patchesResp.Body.Bytes(), &patches, jsonx.V1()))
states := map[string]string{}
for _, p := range patches.Patches {
diff --git a/services/ssm/patch_baselines_lifecycle_test.go b/services/ssm/patch_baselines_lifecycle_test.go
index dab2db2cee..652d6edd63 100644
--- a/services/ssm/patch_baselines_lifecycle_test.go
+++ b/services/ssm/patch_baselines_lifecycle_test.go
@@ -2,13 +2,14 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -241,7 +242,7 @@ func TestPatchBaseline_OSFilter(t *testing.T) {
"Filters": []map[string]any{
{"Key": "OPERATING_SYSTEM", "Values": []string{"AMAZON_LINUX_2"}},
},
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "DescribePatchBaselines", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "linux-baseline")
@@ -252,7 +253,7 @@ func TestPatchBaseline_OSFilter(t *testing.T) {
"Filters": []map[string]any{
{"Key": "OPERATING_SYSTEM", "Values": []string{"WINDOWS"}},
},
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "DescribePatchBaselines", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.NotContains(t, rec.Body.String(), "linux-baseline")
@@ -286,7 +287,7 @@ func TestPatchBaseline_ComplianceLevelRoundTrip(t *testing.T) {
require.NoError(t, err)
// GetPatchBaseline should return the compliance level.
- body, _ := json.Marshal(map[string]any{"BaselineId": bl.BaselineID})
+ body, _ := json.Marshal(map[string]any{"BaselineId": bl.BaselineID}, jsonx.V1())
rec := doRequest(t, h, "GetPatchBaseline", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), tt.complianceLevel)
@@ -295,7 +296,7 @@ func TestPatchBaseline_ComplianceLevelRoundTrip(t *testing.T) {
body, _ = json.Marshal(map[string]any{
"BaselineId": bl.BaselineID,
"ApprovedPatchesComplianceLevel": "LOW",
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "UpdatePatchBaseline", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "LOW")
@@ -318,7 +319,7 @@ func TestPatchGroups_RegisterAndLookup(t *testing.T) {
body, _ := json.Marshal(map[string]any{
"BaselineId": bl.BaselineID,
"PatchGroup": "ubuntu-servers",
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "RegisterPatchBaselineForPatchGroup", string(body))
require.Equal(t, http.StatusOK, rec.Code)
@@ -328,7 +329,7 @@ func TestPatchGroups_RegisterAndLookup(t *testing.T) {
assert.Contains(t, rec.Body.String(), "ubuntu-servers")
// GetPatchBaselineForPatchGroup should return the baseline.
- body, _ = json.Marshal(map[string]any{"PatchGroup": "ubuntu-servers"})
+ body, _ = json.Marshal(map[string]any{"PatchGroup": "ubuntu-servers"}, jsonx.V1())
rec = doRequest(t, h, "GetPatchBaselineForPatchGroup", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), bl.BaselineID)
@@ -337,7 +338,7 @@ func TestPatchGroups_RegisterAndLookup(t *testing.T) {
body, _ = json.Marshal(map[string]any{
"BaselineId": bl.BaselineID,
"PatchGroup": "ubuntu-servers",
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "DeregisterPatchBaselineForPatchGroup", string(body))
require.Equal(t, http.StatusOK, rec.Code)
}
@@ -380,14 +381,14 @@ func TestDeregisterPatchBaselineForPatchGroup_TableDriven(t *testing.T) {
body, _ := json.Marshal(map[string]any{
"BaselineId": tt.baselineID,
"PatchGroup": tt.patchGroup,
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "DeregisterPatchBaselineForPatchGroup", string(body))
require.Equal(t, tt.wantStatus, rec.Code)
if tt.wantStatus == http.StatusOK && tt.wantBaselineID != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantBaselineID, resp["BaselineId"])
assert.Equal(t, tt.wantPatchGroup, resp["PatchGroup"])
}
@@ -408,7 +409,7 @@ func TestDescribeAvailablePatches_SeededCatalog(t *testing.T) {
Name string `json:"Name"`
} `json:"Patches"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out.Patches, "the catalog must be seeded with real built-in patches")
}
@@ -432,7 +433,7 @@ func TestSendCommand_RunPatchBaseline_PopulatesInstanceState(t *testing.T) {
var created struct {
BaselineID string `json:"BaselineId"`
}
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &created, jsonx.V1()))
require.NotEmpty(t, created.BaselineID)
regResp := doRequest(t, h, "RegisterPatchBaselineForPatchGroup", `{
@@ -457,7 +458,7 @@ func TestSendCommand_RunPatchBaseline_PopulatesInstanceState(t *testing.T) {
PatchGroup string `json:"PatchGroup"`
} `json:"InstancePatchStates"`
}
- require.NoError(t, json.Unmarshal(stateResp.Body.Bytes(), &states))
+ require.NoError(t, json.Unmarshal(stateResp.Body.Bytes(), &states, jsonx.V1()))
require.Len(t, states.InstancePatchStates, 1)
assert.Equal(t, "i-patchtest", states.InstancePatchStates[0].InstanceID)
assert.Equal(t, created.BaselineID, states.InstancePatchStates[0].BaselineID)
@@ -472,7 +473,7 @@ func TestSendCommand_RunPatchBaseline_PopulatesInstanceState(t *testing.T) {
State string `json:"State"`
} `json:"Patches"`
}
- require.NoError(t, json.Unmarshal(patchesResp.Body.Bytes(), &patches))
+ require.NoError(t, json.Unmarshal(patchesResp.Body.Bytes(), &patches, jsonx.V1()))
require.NotEmpty(t, patches.Patches)
var sawInstalled bool
diff --git a/services/ssm/patch_baselines_test.go b/services/ssm/patch_baselines_test.go
index 48a9b10245..c0875b469f 100644
--- a/services/ssm/patch_baselines_test.go
+++ b/services/ssm/patch_baselines_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -295,7 +296,7 @@ func TestStubOps_DeletePatchBaseline(t *testing.T) {
})
require.NoError(t, err)
- body, _ := json.Marshal(map[string]any{"BaselineId": pb.BaselineID})
+ body, _ := json.Marshal(map[string]any{"BaselineId": pb.BaselineID}, jsonx.V1())
rec := doRequest(t, h, "DeletePatchBaseline", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -344,7 +345,7 @@ func TestStubOps_GetPatchBaseline(t *testing.T) {
})
require.NoError(t, err)
- body, _ := json.Marshal(map[string]any{"BaselineId": pb.BaselineID})
+ body, _ := json.Marshal(map[string]any{"BaselineId": pb.BaselineID}, jsonx.V1())
rec := doRequest(t, h, "GetPatchBaseline", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -361,7 +362,7 @@ func TestStubOps_UpdatePatchBaseline(t *testing.T) {
})
require.NoError(t, err)
- body, _ := json.Marshal(map[string]any{"BaselineId": pb.BaselineID, "Name": "updated-baseline"})
+ body, _ := json.Marshal(map[string]any{"BaselineId": pb.BaselineID, "Name": "updated-baseline"}, jsonx.V1())
rec := doRequest(t, h, "UpdatePatchBaseline", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -378,7 +379,7 @@ func TestStubOps_RegisterPatchBaselineForPatchGroup(t *testing.T) {
})
require.NoError(t, err)
- body, _ := json.Marshal(map[string]any{"BaselineId": pb.BaselineID, "PatchGroup": "test-group"})
+ body, _ := json.Marshal(map[string]any{"BaselineId": pb.BaselineID, "PatchGroup": "test-group"}, jsonx.V1())
rec := doRequest(t, h, "RegisterPatchBaselineForPatchGroup", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
}
@@ -498,7 +499,7 @@ func TestCreatePatchBaseline_Success(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var resp ssm.CreatePatchBaselineOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp.BaselineID)
assert.True(t, strings.HasPrefix(resp.BaselineID, "pb-"))
assert.Equal(t, 1, backend.PatchBaselineCount())
@@ -540,7 +541,7 @@ func TestPatchBaseline_ApprovalRulesGlobalFiltersSourcesRoundTrip(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var created ssm.CreatePatchBaselineOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
out, err := b.GetPatchBaseline(context.Background(), &ssm.GetPatchBaselineInput{BaselineID: created.BaselineID})
require.NoError(t, err)
@@ -577,7 +578,7 @@ func TestCreatePatchBaseline_DefaultsGlobalFiltersAndApprovalRulesNonNil(t *test
require.Equal(t, http.StatusOK, rec.Code)
var created ssm.CreatePatchBaselineOutput
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
out, err := b.GetPatchBaseline(context.Background(), &ssm.GetPatchBaselineInput{BaselineID: created.BaselineID})
require.NoError(t, err)
@@ -591,7 +592,7 @@ func TestCreatePatchBaseline_DefaultsGlobalFiltersAndApprovalRulesNonNil(t *test
require.Equal(t, http.StatusOK, rawRec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rawRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rawRec.Body.Bytes(), &raw, jsonx.V1()))
assert.Contains(t, raw, "GlobalFilters", "GlobalFilters key must be present, not omitted")
assert.Contains(t, raw, "ApprovalRules", "ApprovalRules key must be present, not omitted")
}
diff --git a/services/ssm/persistence.go b/services/ssm/persistence.go
index 9ddfc360a8..401de17991 100644
--- a/services/ssm/persistence.go
+++ b/services/ssm/persistence.go
@@ -2,10 +2,12 @@ package ssm
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
@@ -37,7 +39,7 @@ const ssmSnapshotVersion = 1
// store_setup.go's package doc for why each one doesn't fit Table's
// func(*V) string keying requirement.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
History map[string]map[string][]ParameterHistory `json:"history"`
Tags map[string]map[string]*tags.Tags `json:"tags"`
DocumentVersions map[string]map[string][]DocumentVersion `json:"document_versions"`
@@ -221,7 +223,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
NotifiedParameterPolicies: b.notifiedParameterPolicies,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "ssm: failed to marshal snapshot", "error", err)
@@ -240,7 +242,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
// silently dropped instead of restored. Each table name has the shape
// "/" (see store_setup.go's getOrCreateTable); regions
// never contain "/", so splitting on the first one recovers both parts.
-func (b *InMemoryBackend) preRegisterSnapshotTables(tables map[string]json.RawMessage) {
+func (b *InMemoryBackend) preRegisterSnapshotTables(tables map[string]jsontext.Value) {
for key := range tables {
name, region, found := strings.Cut(key, "/")
if !found {
diff --git a/services/ssm/public_parameters.go b/services/ssm/public_parameters.go
index c839819e23..b847b4ef92 100644
--- a/services/ssm/public_parameters.go
+++ b/services/ssm/public_parameters.go
@@ -2,7 +2,7 @@ package ssm
import (
_ "embed" // public_parameters.json
- "encoding/json"
+ "encoding/json/v2"
"slices"
"sort"
"strconv"
@@ -10,6 +10,7 @@ import (
"sync"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
//go:embed public_parameters.json
@@ -45,7 +46,7 @@ type publicCatalog struct {
//nolint:gochecknoglobals // read-only catalog built once from embedded data
var loadPublicCatalog = sync.OnceValue(func() *publicCatalog {
var f publicParametersFile
- if err := json.Unmarshal(publicParametersJSON, &f); err != nil {
+ if err := json.Unmarshal(publicParametersJSON, &f, jsonx.V1()); err != nil {
panic("ssm: corrupt embedded public_parameters.json: " + err.Error())
}
diff --git a/services/ssm/public_parameters_sdk_test.go b/services/ssm/public_parameters_sdk_test.go
index 2068b233b8..bb3f045e88 100644
--- a/services/ssm/public_parameters_sdk_test.go
+++ b/services/ssm/public_parameters_sdk_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -60,7 +61,7 @@ func TestGetParameter_PublicCatalog_SDK(t *testing.T) {
t.Helper()
var doc map[string]any
- require.NoError(t, json.Unmarshal([]byte(aws.ToString(p.Value)), &doc))
+ require.NoError(t, json.Unmarshal([]byte(aws.ToString(p.Value)), &doc, jsonx.V1()))
assert.Contains(t, doc, "image_id")
assert.Contains(t, doc, "ecs_agent_version")
}},
@@ -474,7 +475,7 @@ func TestSecretsManagerReference_SDK(t *testing.T) {
assert.Contains(t, aws.ToString(p.ARN), ":parameter/aws/reference/secretsmanager/")
var src map[string]any
- require.NoError(t, json.Unmarshal([]byte(aws.ToString(p.SourceResult)), &src))
+ require.NoError(t, json.Unmarshal([]byte(aws.ToString(p.SourceResult)), &src, jsonx.V1()))
assert.Equal(t, secretARN, src["ARN"])
assert.Equal(t, "db", src["Name"])
assert.Equal(t, tc.wantValue, src["SecretString"])
diff --git a/services/ssm/resource_policies_test.go b/services/ssm/resource_policies_test.go
index d646e65ae4..ba020167d1 100644
--- a/services/ssm/resource_policies_test.go
+++ b/services/ssm/resource_policies_test.go
@@ -1,7 +1,7 @@
package ssm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -69,19 +70,19 @@ func TestResourcePolicies_FullCRUD(t *testing.T) {
body, _ := json.Marshal(map[string]any{
"ResourceArn": tt.resourceArn,
"Policy": tt.policy,
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "PutResourcePolicy", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
policyID := putResp["PolicyId"].(string)
policyHash := putResp["PolicyHash"].(string)
assert.NotEmpty(t, policyID)
assert.NotEmpty(t, policyHash)
// Get policies.
- body, _ = json.Marshal(map[string]any{"ResourceArn": tt.resourceArn})
+ body, _ = json.Marshal(map[string]any{"ResourceArn": tt.resourceArn}, jsonx.V1())
rec = doRequest(t, h, "GetResourcePolicies", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), policyID)
@@ -91,12 +92,12 @@ func TestResourcePolicies_FullCRUD(t *testing.T) {
"ResourceArn": tt.resourceArn,
"PolicyId": policyID,
"PolicyHash": policyHash,
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "DeleteResourcePolicy", string(body))
require.Equal(t, http.StatusOK, rec.Code)
// Get policies — should be empty.
- body, _ = json.Marshal(map[string]any{"ResourceArn": tt.resourceArn})
+ body, _ = json.Marshal(map[string]any{"ResourceArn": tt.resourceArn}, jsonx.V1())
rec = doRequest(t, h, "GetResourcePolicies", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), `"Policies":[]`)
diff --git a/services/ssm/secret_references.go b/services/ssm/secret_references.go
index 50993458ff..8a8636678e 100644
--- a/services/ssm/secret_references.go
+++ b/services/ssm/secret_references.go
@@ -2,7 +2,7 @@ package ssm
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"regexp"
@@ -10,6 +10,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -146,7 +147,7 @@ func (b *InMemoryBackend) getSecretReference(
value = string(secret.SecretBinary)
}
- source, err := json.Marshal(secretSourceResult(*secret))
+ source, err := json.Marshal(secretSourceResult(*secret), jsonx.V1())
if err != nil {
return ParameterOutput{}, fmt.Errorf("marshal secret source result: %w", err)
}
diff --git a/services/ssm/service_settings_test.go b/services/ssm/service_settings_test.go
index be11b23c97..7f25ac3ee1 100644
--- a/services/ssm/service_settings_test.go
+++ b/services/ssm/service_settings_test.go
@@ -1,7 +1,7 @@
package ssm_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -101,7 +102,7 @@ func TestServiceSettings_LifecycleRoundTrip(t *testing.T) {
h, _ := newTestHandler(t)
// Default state.
- body, _ := json.Marshal(map[string]any{"SettingId": tt.settingID})
+ body, _ := json.Marshal(map[string]any{"SettingId": tt.settingID}, jsonx.V1())
rec := doRequest(t, h, "GetServiceSetting", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "Default")
@@ -110,24 +111,24 @@ func TestServiceSettings_LifecycleRoundTrip(t *testing.T) {
body, _ = json.Marshal(map[string]any{
"SettingId": tt.settingID,
"SettingValue": tt.setValue,
- })
+ }, jsonx.V1())
rec = doRequest(t, h, "UpdateServiceSetting", string(body))
require.Equal(t, http.StatusOK, rec.Code)
// Get updated.
- body, _ = json.Marshal(map[string]any{"SettingId": tt.settingID})
+ body, _ = json.Marshal(map[string]any{"SettingId": tt.settingID}, jsonx.V1())
rec = doRequest(t, h, "GetServiceSetting", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), tt.setValue)
assert.Contains(t, rec.Body.String(), "Customized")
// Reset.
- body, _ = json.Marshal(map[string]any{"SettingId": tt.settingID})
+ body, _ = json.Marshal(map[string]any{"SettingId": tt.settingID}, jsonx.V1())
rec = doRequest(t, h, "ResetServiceSetting", string(body))
require.Equal(t, http.StatusOK, rec.Code)
// Default again.
- body, _ = json.Marshal(map[string]any{"SettingId": tt.settingID})
+ body, _ = json.Marshal(map[string]any{"SettingId": tt.settingID}, jsonx.V1())
rec = doRequest(t, h, "GetServiceSetting", string(body))
require.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), "Default")
diff --git a/services/ssm/sessions_test.go b/services/ssm/sessions_test.go
index 23dcd2f217..c9be4a9702 100644
--- a/services/ssm/sessions_test.go
+++ b/services/ssm/sessions_test.go
@@ -2,7 +2,7 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -108,7 +109,7 @@ func TestStartSession_DefaultsToStandardAccessType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
sessions := out["Sessions"].([]any)
require.Len(t, sessions, 1)
assert.Equal(t, "Standard", sessions[0].(map[string]any)["AccessType"])
@@ -208,12 +209,12 @@ func TestSession_Parameters_RoundTrip(t *testing.T) {
body, _ := json.Marshal(map[string]any{
"Target": "i-1234567890abcdef0",
"Parameters": tt.parameters,
- })
+ }, jsonx.V1())
rec := doRequest(t, h, "StartSession", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
assert.NotEmpty(t, startResp["SessionId"])
// DescribeSessions should show the session, but never a
@@ -250,7 +251,7 @@ func TestSession_StateFilter(t *testing.T) {
assert.NotContains(t, rec.Body.String(), sess.SessionID)
// Terminate session.
- body, _ := json.Marshal(map[string]any{"SessionId": sess.SessionID})
+ body, _ := json.Marshal(map[string]any{"SessionId": sess.SessionID}, jsonx.V1())
doRequest(t, h, "TerminateSession", string(body))
// Filter by Active — should NOT find the terminated session.
diff --git a/services/ssm/ssmmessages_port_test.go b/services/ssm/ssmmessages_port_test.go
index a0d658b3c1..22ee923f9f 100644
--- a/services/ssm/ssmmessages_port_test.go
+++ b/services/ssm/ssmmessages_port_test.go
@@ -3,7 +3,7 @@ package ssm_test
import (
"context"
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -79,7 +80,7 @@ func portHandshake(t *testing.T, c *dcClient, acked map[int64]bool, wantProps ma
require.Equal(t, ssm.PayloadHandshakeRequest, req.PayloadType)
var hr ssm.HandshakeRequestPayload
- require.NoError(t, json.Unmarshal(req.Payload, &hr))
+ require.NoError(t, json.Unmarshal(req.Payload, &hr, jsonx.V1()))
require.Len(t, hr.RequestedClientActions, 1)
assert.Equal(t, "2.3.0.0", hr.AgentVersion)
assert.Equal(t, "Port", hr.RequestedClientActions[0].ActionParameters["SessionType"])
@@ -88,7 +89,7 @@ func portHandshake(t *testing.T, c *dcClient, acked map[int64]bool, wantProps ma
resp, _ := json.Marshal(ssm.HandshakeResponsePayload{
ClientVersion: "1.2.0.0",
ProcessedClientActions: []ssm.ProcessedClientAction{{ActionType: "SessionType", ActionStatus: 1}},
- })
+ }, jsonx.V1())
c.input(ssm.PayloadHandshakeResponse, resp)
done := c.nextNonAck(acked)
@@ -291,7 +292,7 @@ func TestSessionPortForwardingErrors(t *testing.T) {
resp, _ := json.Marshal(ssm.HandshakeResponsePayload{
ProcessedClientActions: []ssm.ProcessedClientAction{{ActionType: "SessionType", ActionStatus: 1}},
- })
+ }, jsonx.V1())
c.input(ssm.PayloadHandshakeResponse, resp)
done := c.nextNonAck(map[int64]bool{})
diff --git a/services/ssm/ssmmessages_server.go b/services/ssm/ssmmessages_server.go
index 2a376d9b47..72fb314e54 100644
--- a/services/ssm/ssmmessages_server.go
+++ b/services/ssm/ssmmessages_server.go
@@ -3,7 +3,7 @@ package ssm
import (
"context"
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net"
@@ -16,6 +16,7 @@ import (
"github.com/gorilla/websocket"
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -107,7 +108,7 @@ func (d *dataChannel) serve(parent context.Context) {
_ = d.ws.SetReadDeadline(time.Time{})
var open OpenDataChannelInput
- if jerr := json.Unmarshal(raw, &open); jerr != nil {
+ if jerr := json.Unmarshal(raw, &open, jsonx.V1()); jerr != nil {
d.reject("malformed OpenDataChannel request")
return
@@ -228,7 +229,7 @@ func (d *dataChannel) process(ctx context.Context, m *StreamMessage) bool {
return d.completeHandshake(ctx, m.Payload)
case PayloadSize:
var sz SizePayload
- if json.Unmarshal(m.Payload, &sz) == nil && sz.Cols > 0 && sz.Rows > 0 {
+ if json.Unmarshal(m.Payload, &sz, jsonx.V1()) == nil && sz.Cols > 0 && sz.Rows > 0 {
d.cols, d.rows = sz.Cols, sz.Rows
if d.shell != nil {
_ = d.shell.Resize(ctx, sz.Cols, sz.Rows)
@@ -270,14 +271,14 @@ func (d *dataChannel) sendHandshakeRequest() error {
"Properties": props,
},
}},
- })
+ }, jsonx.V1())
return d.sendOutput(PayloadHandshakeRequest, body)
}
func (d *dataChannel) completeHandshake(ctx context.Context, payload []byte) bool {
var resp HandshakeResponsePayload
- if err := json.Unmarshal(payload, &resp); err != nil {
+ if err := json.Unmarshal(payload, &resp, jsonx.V1()); err != nil {
d.endSession("Malformed handshake response.")
return true
@@ -291,7 +292,7 @@ func (d *dataChannel) completeHandshake(ctx context.Context, payload []byte) boo
}
}
- done, _ := json.Marshal(HandshakeCompletePayload{HandshakeTimeToComplete: int64(time.Since(d.started))})
+ done, _ := json.Marshal(HandshakeCompletePayload{HandshakeTimeToComplete: int64(time.Since(d.started))}, jsonx.V1())
if d.sendOutput(PayloadHandshakeComplete, done) != nil {
return true
}
@@ -392,7 +393,7 @@ func (d *dataChannel) finish(reason string) {
SessionID: d.sessionID,
SchemaVersion: msgSchemaVersion,
Output: reason,
- })
+ }, jsonx.V1())
_ = d.send(NewStreamMessage(MsgChannelClosed, 0, 0, body))
d.closeNormal()
@@ -410,7 +411,7 @@ func (d *dataChannel) sendAck(m *StreamMessage) error {
body, _ := json.Marshal(AckPayload{
MessageType: m.MessageType, MessageID: m.MessageID.String(),
SequenceNumber: m.SequenceNumber, IsSequentialMessage: true,
- })
+ }, jsonx.V1())
ack := NewStreamMessage(MsgAcknowledge, 0, 0, body)
ack.Flags = ackFlags
diff --git a/services/ssm/ssmmessages_test.go b/services/ssm/ssmmessages_test.go
index 7cb7bec9d3..99ebd3fc1e 100644
--- a/services/ssm/ssmmessages_test.go
+++ b/services/ssm/ssmmessages_test.go
@@ -4,7 +4,8 @@ import (
"context"
"crypto/sha256"
"encoding/binary"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"net/http/httptest"
@@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -188,7 +190,7 @@ func newServer(t *testing.T, withShell bool) *httptest.Server {
func ssmCall(t *testing.T, srv *httptest.Server, op string, in any) map[string]any {
t.Helper()
- body, err := json.Marshal(in)
+ body, err := json.Marshal(in, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, srv.URL, strings.NewReader(string(body)))
@@ -202,7 +204,7 @@ func ssmCall(t *testing.T, srv *httptest.Server, op string, in any) map[string]a
defer resp.Body.Close()
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
return out
}
@@ -232,7 +234,7 @@ func (c *dcClient) open(token string) {
b, _ := json.Marshal(ssm.OpenDataChannelInput{
MessageSchemaVersion: "1.0", RequestID: uuid.NewString(), TokenValue: token,
ClientID: uuid.NewString(), ClientVersion: "1.2.0.0",
- })
+ }, jsonx.V1())
require.NoError(c.t, c.ws.WriteMessage(websocket.TextMessage, b))
}
@@ -280,7 +282,7 @@ func (c *dcClient) nextNonAck(acked map[int64]bool) *ssm.StreamMessage {
}
var a ssm.AckPayload
- require.NoError(c.t, json.Unmarshal(m.Payload, &a))
+ require.NoError(c.t, json.Unmarshal(m.Payload, &a, jsonx.V1()))
acked[a.SequenceNumber] = true
}
}
@@ -296,7 +298,7 @@ func (c *dcClient) handshake(acked map[int64]bool) {
resp, _ := json.Marshal(ssm.HandshakeResponsePayload{
ClientVersion: "1.2.0.0",
ProcessedClientActions: []ssm.ProcessedClientAction{{ActionType: "SessionType", ActionStatus: 1}},
- })
+ }, jsonx.V1())
c.input(ssm.PayloadHandshakeResponse, resp)
done := c.nextNonAck(acked)
diff --git a/services/ssm/tags_test.go b/services/ssm/tags_test.go
index 046a820d67..89475cb661 100644
--- a/services/ssm/tags_test.go
+++ b/services/ssm/tags_test.go
@@ -2,13 +2,14 @@ package ssm_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/ssm"
)
@@ -49,7 +50,7 @@ func TestTagsForNonParameterResources(t *testing.T) {
"ResourceType": tt.resourceType,
"ResourceId": tt.resourceID,
"Tags": tt.tags,
- })
+ }, jsonx.V1())
addRec := doRequest(t, h, "AddTagsToResource", string(addBody))
require.Equal(t, http.StatusOK, addRec.Code)
@@ -57,13 +58,13 @@ func TestTagsForNonParameterResources(t *testing.T) {
listBody, _ := json.Marshal(map[string]any{
"ResourceType": tt.resourceType,
"ResourceId": tt.resourceID,
- })
+ }, jsonx.V1())
listRec := doRequest(t, h, "ListTagsForResource", string(listBody))
require.Equal(t, http.StatusOK, listRec.Code)
var listResp ssm.ListTagsForResourceOutput
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp.TagList, tt.wantTagCount)
})
}
@@ -109,25 +110,25 @@ func TestRemoveTagsForNonParameter(t *testing.T) {
"ResourceType": tt.resourceType,
"ResourceId": tt.resourceID,
"Tags": tt.addTags,
- })
+ }, jsonx.V1())
require.Equal(t, http.StatusOK, doRequest(t, h, "AddTagsToResource", string(addBody)).Code)
removeBody, _ := json.Marshal(map[string]any{
"ResourceType": tt.resourceType,
"ResourceId": tt.resourceID,
"TagKeys": tt.removeKeys,
- })
+ }, jsonx.V1())
require.Equal(t, http.StatusOK, doRequest(t, h, "RemoveTagsFromResource", string(removeBody)).Code)
listBody, _ := json.Marshal(map[string]any{
"ResourceType": tt.resourceType,
"ResourceId": tt.resourceID,
- })
+ }, jsonx.V1())
listRec := doRequest(t, h, "ListTagsForResource", string(listBody))
require.Equal(t, http.StatusOK, listRec.Code)
var resp ssm.ListTagsForResourceOutput
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp.TagList, tt.wantRemain)
})
}
diff --git a/services/ssoadmin/application_auth_methods.go b/services/ssoadmin/application_auth_methods.go
index 59d91248ff..e3244c4804 100644
--- a/services/ssoadmin/application_auth_methods.go
+++ b/services/ssoadmin/application_auth_methods.go
@@ -1,7 +1,7 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"sort"
@@ -32,7 +32,7 @@ func (b *InMemoryBackend) DeleteApplicationAuthenticationMethod(applicationArn,
// authMethodType must be IAM; body is the full AuthenticationMethod JSON object.
func (b *InMemoryBackend) PutApplicationAuthenticationMethod(
applicationArn, authMethodType string,
- body json.RawMessage,
+ body jsontext.Value,
) error {
b.mu.Lock("PutApplicationAuthenticationMethod")
defer b.mu.Unlock()
@@ -45,10 +45,10 @@ func (b *InMemoryBackend) PutApplicationAuthenticationMethod(
return ErrApplicationNotFound
}
if b.applicationAuthMethods[applicationArn] == nil {
- b.applicationAuthMethods[applicationArn] = make(map[string]json.RawMessage)
+ b.applicationAuthMethods[applicationArn] = make(map[string]jsontext.Value)
}
if len(body) == 0 {
- body = json.RawMessage("null")
+ body = jsontext.Value("null")
}
b.applicationAuthMethods[applicationArn][authMethodType] = body
@@ -66,7 +66,7 @@ func (b *InMemoryBackend) ListApplicationAuthenticationMethods(applicationArn st
methods := b.applicationAuthMethods[applicationArn]
result := make([]AuthMethod, 0, len(methods))
for mType, body := range methods {
- bodyCopy := make(json.RawMessage, len(body))
+ bodyCopy := make(jsontext.Value, len(body))
copy(bodyCopy, body)
result = append(result, AuthMethod{AuthMethodType: mType, Body: bodyCopy})
}
@@ -78,7 +78,7 @@ func (b *InMemoryBackend) ListApplicationAuthenticationMethods(applicationArn st
// GetApplicationAuthenticationMethod returns a specific auth method body.
func (b *InMemoryBackend) GetApplicationAuthenticationMethod(
applicationArn, authMethodType string,
-) (json.RawMessage, error) {
+) (jsontext.Value, error) {
b.mu.RLock("GetApplicationAuthenticationMethod")
defer b.mu.RUnlock()
@@ -93,7 +93,7 @@ func (b *InMemoryBackend) GetApplicationAuthenticationMethod(
if !exists {
return nil, ErrAuthMethodNotFound
}
- result := make(json.RawMessage, len(body))
+ result := make(jsontext.Value, len(body))
copy(result, body)
return result, nil
diff --git a/services/ssoadmin/application_grants.go b/services/ssoadmin/application_grants.go
index 6032a1c4b0..25f6183235 100644
--- a/services/ssoadmin/application_grants.go
+++ b/services/ssoadmin/application_grants.go
@@ -1,7 +1,7 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"sort"
@@ -22,7 +22,7 @@ func isValidGrantType(grantType string) bool {
}
// PutApplicationGrant stores a typed grant with its full body.
-func (b *InMemoryBackend) PutApplicationGrant(applicationArn, grantType string, body json.RawMessage) error {
+func (b *InMemoryBackend) PutApplicationGrant(applicationArn, grantType string, body jsontext.Value) error {
b.mu.Lock("PutApplicationGrant")
defer b.mu.Unlock()
@@ -35,10 +35,10 @@ func (b *InMemoryBackend) PutApplicationGrant(applicationArn, grantType string,
return ErrApplicationNotFound
}
if b.applicationGrants[applicationArn] == nil {
- b.applicationGrants[applicationArn] = make(map[string]json.RawMessage)
+ b.applicationGrants[applicationArn] = make(map[string]jsontext.Value)
}
if len(body) == 0 {
- body = json.RawMessage("null")
+ body = jsontext.Value("null")
}
b.applicationGrants[applicationArn][grantType] = body
@@ -76,7 +76,7 @@ func (b *InMemoryBackend) ListApplicationGrants(applicationArn string) ([]Applic
grants := b.applicationGrants[applicationArn]
result := make([]ApplicationGrant, 0, len(grants))
for gType, body := range grants {
- bodyCopy := make(json.RawMessage, len(body))
+ bodyCopy := make(jsontext.Value, len(body))
copy(bodyCopy, body)
result = append(result, ApplicationGrant{GrantType: gType, Grant: bodyCopy})
}
@@ -86,7 +86,7 @@ func (b *InMemoryBackend) ListApplicationGrants(applicationArn string) ([]Applic
}
// GetApplicationGrant returns a specific grant body.
-func (b *InMemoryBackend) GetApplicationGrant(applicationArn, grantType string) (json.RawMessage, error) {
+func (b *InMemoryBackend) GetApplicationGrant(applicationArn, grantType string) (jsontext.Value, error) {
b.mu.RLock("GetApplicationGrant")
defer b.mu.RUnlock()
@@ -101,7 +101,7 @@ func (b *InMemoryBackend) GetApplicationGrant(applicationArn, grantType string)
if !exists {
return nil, ErrGrantNotFound
}
- result := make(json.RawMessage, len(body))
+ result := make(jsontext.Value, len(body))
copy(result, body)
return result, nil
diff --git a/services/ssoadmin/handler.go b/services/ssoadmin/handler.go
index 5d71526cd3..b729c874da 100644
--- a/services/ssoadmin/handler.go
+++ b/services/ssoadmin/handler.go
@@ -2,7 +2,7 @@ package ssoadmin
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -94,7 +95,7 @@ func listApplicationItems[T any](
ApplicationArn string `json:"ApplicationArn"`
NextToken string `json:"NextToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -232,7 +233,7 @@ func listProvisioningStatusMetadata[T any](
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -268,7 +269,7 @@ func listPermissionSetSubItems[T any](
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -427,7 +428,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
var req struct {
InstanceArn string `json:"InstanceArn"`
}
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
return ""
}
@@ -736,13 +737,11 @@ func toPermissionSetProvisioningStatusMetadataView(s *ProvisioningStatus) permis
}
func writeJSON(c *echo.Context, status int, v any) error {
- data, err := json.Marshal(v)
+ data, err := json.Marshal(v, jsonx.V1())
if err != nil {
return err
}
- c.Response().Header().Set("Content-Type", "application/x-amz-json-1.1")
- c.Response().WriteHeader(status)
- _, _ = c.Response().Write(data)
+ httputils.WriteAmzJSON11Bytes(c.Response(), status, data)
return nil
}
@@ -754,7 +753,7 @@ func checkPaging(body []byte) string {
NextToken *string `json:"NextToken"`
}
- if json.Unmarshal(body, &req) != nil {
+ if json.Unmarshal(body, &req, jsonx.V1()) != nil {
return ""
}
diff --git a/services/ssoadmin/handler_account_assignments.go b/services/ssoadmin/handler_account_assignments.go
index 95939f8e4f..d5df8bb8c6 100644
--- a/services/ssoadmin/handler_account_assignments.go
+++ b/services/ssoadmin/handler_account_assignments.go
@@ -1,10 +1,12 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type assignmentView struct {
@@ -23,7 +25,7 @@ func (h *Handler) handleCreateAccountAssignment(c *echo.Context, body []byte) er
PrincipalID string `json:"PrincipalId"`
PrincipalType string `json:"PrincipalType"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
@@ -71,7 +73,7 @@ func (h *Handler) handleDescribeAccountAssignmentCreationStatus(c *echo.Context,
InstanceArn string `json:"InstanceArn"`
AccountAssignmentCreationRequestID string `json:"AccountAssignmentCreationRequestId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -97,7 +99,7 @@ func (h *Handler) handleDeleteAccountAssignment(c *echo.Context, body []byte) er
PrincipalID string `json:"PrincipalId"`
PrincipalType string `json:"PrincipalType"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.PrincipalType != "" && req.PrincipalType != principalTypeUser && req.PrincipalType != principalTypeGroup {
@@ -130,7 +132,7 @@ func (h *Handler) handleDescribeAccountAssignmentDeletionStatus(c *echo.Context,
InstanceArn string `json:"InstanceArn"`
AccountAssignmentDeletionRequestID string `json:"AccountAssignmentDeletionRequestId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -155,7 +157,7 @@ func (h *Handler) handleListAccountAssignments(c *echo.Context, body []byte) err
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -212,7 +214,7 @@ func (h *Handler) handleListAccountAssignmentsForPrincipal(c *echo.Context, body
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
diff --git a/services/ssoadmin/handler_application_access_scopes.go b/services/ssoadmin/handler_application_access_scopes.go
index 2356be03a5..e2155be79b 100644
--- a/services/ssoadmin/handler_application_access_scopes.go
+++ b/services/ssoadmin/handler_application_access_scopes.go
@@ -1,10 +1,12 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDeleteApplicationAccessScope(c *echo.Context, body []byte) error {
@@ -12,7 +14,7 @@ func (h *Handler) handleDeleteApplicationAccessScope(c *echo.Context, body []byt
ApplicationArn string `json:"ApplicationArn"`
Scope string `json:"Scope"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -29,7 +31,7 @@ func (h *Handler) handleListApplicationAccessScopes(c *echo.Context, body []byte
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
scopes, err := h.Backend.ListApplicationAccessScopes(req.ApplicationArn)
@@ -53,7 +55,7 @@ func (h *Handler) handlePutApplicationAccessScope(c *echo.Context, body []byte)
Scope string `json:"Scope"`
AuthorizedTargets []string `json:"AuthorizedTargets"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if err := h.Backend.PutApplicationAccessScope(req.ApplicationArn, req.Scope, req.AuthorizedTargets); err != nil {
@@ -68,7 +70,7 @@ func (h *Handler) handleGetApplicationAccessScope(c *echo.Context, body []byte)
ApplicationArn string `json:"ApplicationArn"`
Scope string `json:"Scope"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.ApplicationArn == "" {
diff --git a/services/ssoadmin/handler_application_assignments.go b/services/ssoadmin/handler_application_assignments.go
index 2aedb891ec..666318d4b1 100644
--- a/services/ssoadmin/handler_application_assignments.go
+++ b/services/ssoadmin/handler_application_assignments.go
@@ -1,10 +1,12 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateApplicationAssignment(c *echo.Context, body []byte) error {
@@ -13,7 +15,7 @@ func (h *Handler) handleCreateApplicationAssignment(c *echo.Context, body []byte
PrincipalID string `json:"PrincipalId"`
PrincipalType string `json:"PrincipalType"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -34,7 +36,7 @@ func (h *Handler) handleDeleteApplicationAssignment(c *echo.Context, body []byte
PrincipalID string `json:"PrincipalId"`
PrincipalType string `json:"PrincipalType"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -55,7 +57,7 @@ func (h *Handler) handleDescribeApplicationAssignment(c *echo.Context, body []by
PrincipalID string `json:"PrincipalId"`
PrincipalType string `json:"PrincipalType"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -82,7 +84,7 @@ func (h *Handler) handleListApplicationAssignments(c *echo.Context, body []byte)
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
assignments, err := h.Backend.ListApplicationAssignments(req.ApplicationArn)
@@ -121,7 +123,7 @@ func (h *Handler) handlePutApplicationAssignmentConfiguration(c *echo.Context, b
ApplicationArn string `json:"ApplicationArn"`
AssignmentRequired bool `json:"AssignmentRequired"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -142,7 +144,7 @@ func (h *Handler) handlePutApplicationSessionConfiguration(c *echo.Context, body
ApplicationArn string `json:"ApplicationArn"`
UserBackgroundSessionApplicationStatus string `json:"UserBackgroundSessionApplicationStatus"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if err := h.Backend.PutApplicationSessionConfiguration(
@@ -158,7 +160,7 @@ func (h *Handler) handleGetApplicationAssignmentConfiguration(c *echo.Context, b
var req struct {
ApplicationArn string `json:"ApplicationArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.ApplicationArn == "" {
@@ -178,7 +180,7 @@ func (h *Handler) handleGetApplicationSessionConfiguration(c *echo.Context, body
var req struct {
ApplicationArn string `json:"ApplicationArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.ApplicationArn == "" {
@@ -209,7 +211,7 @@ func (h *Handler) handleListApplicationAssignmentsForPrincipal(c *echo.Context,
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
diff --git a/services/ssoadmin/handler_application_auth_methods.go b/services/ssoadmin/handler_application_auth_methods.go
index 60902b2b95..1a11703e35 100644
--- a/services/ssoadmin/handler_application_auth_methods.go
+++ b/services/ssoadmin/handler_application_auth_methods.go
@@ -1,10 +1,13 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDeleteApplicationAuthenticationMethod(c *echo.Context, body []byte) error {
@@ -12,7 +15,7 @@ func (h *Handler) handleDeleteApplicationAuthenticationMethod(c *echo.Context, b
ApplicationArn string `json:"ApplicationArn"`
AuthMethodType string `json:"AuthenticationMethodType"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -35,12 +38,12 @@ func (h *Handler) handleListApplicationAuthenticationMethods(c *echo.Context, bo
func (h *Handler) handlePutApplicationAuthenticationMethod(c *echo.Context, body []byte) error {
var req struct {
- ApplicationArn string `json:"ApplicationArn"`
- AuthMethodType string `json:"AuthenticationMethodType"`
- AuthenticationType string `json:"AuthenticationType"`
- AuthenticationMethod json.RawMessage `json:"AuthenticationMethod"`
+ ApplicationArn string `json:"ApplicationArn"`
+ AuthMethodType string `json:"AuthenticationMethodType"`
+ AuthenticationType string `json:"AuthenticationType"`
+ AuthenticationMethod jsontext.Value `json:"AuthenticationMethod"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
methodType := req.AuthMethodType
@@ -61,7 +64,7 @@ func (h *Handler) handleGetApplicationAuthenticationMethod(c *echo.Context, body
ApplicationArn string `json:"ApplicationArn"`
AuthenticationMethodType string `json:"AuthenticationMethodType"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.ApplicationArn == "" {
diff --git a/services/ssoadmin/handler_application_auth_methods_test.go b/services/ssoadmin/handler_application_auth_methods_test.go
index a21abc9868..a898327ee6 100644
--- a/services/ssoadmin/handler_application_auth_methods_test.go
+++ b/services/ssoadmin/handler_application_auth_methods_test.go
@@ -1,12 +1,15 @@
package ssoadmin_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestApplicationAuthMethodStructured verifies PutApplicationAuthenticationMethod stores structured body.
@@ -41,13 +44,13 @@ func TestApplicationAuthMethodStructured(t *testing.T) {
instanceArn := createInstance(t, h, "auth-method-inst")
appArn := createApplication(t, h, instanceArn, "AuthMethodApp")
- bodyBytes, err := json.Marshal(tt.authBody)
+ bodyBytes, err := json.Marshal(tt.authBody, jsonx.V1())
require.NoError(t, err)
rec := doRequest(t, h, "PutApplicationAuthenticationMethod", map[string]any{
"ApplicationArn": appArn,
"AuthenticationMethodType": tt.authMethodType,
- "AuthenticationMethod": json.RawMessage(bodyBytes),
+ "AuthenticationMethod": jsontext.Value(bodyBytes),
})
assert.Equal(t, tt.wantStatus, rec.Code)
})
diff --git a/services/ssoadmin/handler_application_grants.go b/services/ssoadmin/handler_application_grants.go
index 9e99fced9c..a6d7b99445 100644
--- a/services/ssoadmin/handler_application_grants.go
+++ b/services/ssoadmin/handler_application_grants.go
@@ -1,10 +1,13 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDeleteApplicationGrant(c *echo.Context, body []byte) error {
@@ -12,7 +15,7 @@ func (h *Handler) handleDeleteApplicationGrant(c *echo.Context, body []byte) err
ApplicationArn string `json:"ApplicationArn"`
GrantType string `json:"GrantType"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if err := h.Backend.DeleteApplicationGrant(req.ApplicationArn, req.GrantType); err != nil {
@@ -34,11 +37,11 @@ func (h *Handler) handleListApplicationGrants(c *echo.Context, body []byte) erro
func (h *Handler) handlePutApplicationGrant(c *echo.Context, body []byte) error {
var req struct {
- ApplicationArn string `json:"ApplicationArn"`
- GrantType string `json:"GrantType"`
- Grant json.RawMessage `json:"Grant"`
+ ApplicationArn string `json:"ApplicationArn"`
+ GrantType string `json:"GrantType"`
+ Grant jsontext.Value `json:"Grant"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if err := h.Backend.PutApplicationGrant(req.ApplicationArn, req.GrantType, req.Grant); err != nil {
@@ -53,7 +56,7 @@ func (h *Handler) handleGetApplicationGrant(c *echo.Context, body []byte) error
ApplicationArn string `json:"ApplicationArn"`
GrantType string `json:"GrantType"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.ApplicationArn == "" {
diff --git a/services/ssoadmin/handler_applications.go b/services/ssoadmin/handler_applications.go
index 3d1bda9f02..525f499c87 100644
--- a/services/ssoadmin/handler_applications.go
+++ b/services/ssoadmin/handler_applications.go
@@ -1,12 +1,14 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"sort"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type applicationView struct {
@@ -40,7 +42,7 @@ func (h *Handler) handleCreateApplication(c *echo.Context, body []byte) error {
ClientToken string `json:"ClientToken"`
Tags []tagView `json:"Tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
@@ -101,7 +103,7 @@ func (h *Handler) handleDeleteApplication(c *echo.Context, body []byte) error {
var req struct {
ApplicationArn string `json:"ApplicationArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.ApplicationArn == "" {
@@ -119,7 +121,7 @@ func (h *Handler) handleDescribeApplication(c *echo.Context, body []byte) error
var req struct {
ApplicationArn string `json:"ApplicationArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -163,7 +165,7 @@ func (h *Handler) handleDescribeApplicationProvider(c *echo.Context, body []byte
var req struct {
ApplicationProviderArn string `json:"ApplicationProviderArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -184,7 +186,7 @@ func (h *Handler) handleListApplicationProviders(c *echo.Context, body []byte) e
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -221,7 +223,7 @@ func (h *Handler) handleListApplications(c *echo.Context, body []byte) error {
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
apps := h.Backend.ListApplications(req.InstanceArn)
@@ -276,7 +278,7 @@ func (h *Handler) handleUpdateApplication(c *echo.Context, body []byte) error {
Description string `json:"Description"`
Status string `json:"Status"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
diff --git a/services/ssoadmin/handler_inline_policies.go b/services/ssoadmin/handler_inline_policies.go
index e737873cc0..a9bb68673c 100644
--- a/services/ssoadmin/handler_inline_policies.go
+++ b/services/ssoadmin/handler_inline_policies.go
@@ -1,10 +1,12 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handlePutInlinePolicyToPermissionSet(c *echo.Context, body []byte) error {
@@ -13,7 +15,7 @@ func (h *Handler) handlePutInlinePolicyToPermissionSet(c *echo.Context, body []b
PermissionSetArn string `json:"PermissionSetArn"`
InlinePolicy string `json:"InlinePolicy"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -33,7 +35,7 @@ func (h *Handler) handleGetInlinePolicyForPermissionSet(c *echo.Context, body []
InstanceArn string `json:"InstanceArn"`
PermissionSetArn string `json:"PermissionSetArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -52,7 +54,7 @@ func (h *Handler) handleDeleteInlinePolicyFromPermissionSet(c *echo.Context, bod
InstanceArn string `json:"InstanceArn"`
PermissionSetArn string `json:"PermissionSetArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
diff --git a/services/ssoadmin/handler_instance_access_control.go b/services/ssoadmin/handler_instance_access_control.go
index d71ff604e0..c9cdcb1fed 100644
--- a/services/ssoadmin/handler_instance_access_control.go
+++ b/services/ssoadmin/handler_instance_access_control.go
@@ -1,11 +1,13 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleCreateInstanceAccessControlAttributeConfiguration(c *echo.Context, body []byte) error {
@@ -20,7 +22,7 @@ func (h *Handler) handleCreateInstanceAccessControlAttributeConfiguration(c *ech
} `json:"AccessControlAttributes"`
} `json:"InstanceAccessControlAttributeConfiguration"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -52,7 +54,7 @@ func (h *Handler) handleDeleteInstanceAccessControlAttributeConfiguration(c *ech
var req struct {
InstanceArn string `json:"InstanceArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if err := h.Backend.DeleteInstanceAccessControlAttributeConfiguration(req.InstanceArn); err != nil {
@@ -66,7 +68,7 @@ func (h *Handler) handleDescribeInstanceAccessControlAttributeConfiguration(c *e
var req struct {
InstanceArn string `json:"InstanceArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
cfg, err := h.Backend.DescribeInstanceAccessControlAttributeConfiguration(req.InstanceArn)
@@ -105,7 +107,7 @@ func (h *Handler) handleUpdateInstanceAccessControlAttributeConfiguration(c *ech
} `json:"AccessControlAttributes"`
} `json:"InstanceAccessControlAttributeConfiguration"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
diff --git a/services/ssoadmin/handler_instances.go b/services/ssoadmin/handler_instances.go
index 34f221512d..a30948a3ff 100644
--- a/services/ssoadmin/handler_instances.go
+++ b/services/ssoadmin/handler_instances.go
@@ -1,10 +1,12 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type instanceView struct {
@@ -34,7 +36,7 @@ func (h *Handler) handleListInstances(c *echo.Context, body []byte) error {
}
// Body is optional for ListInstances; ignore unmarshal errors on empty/garbage.
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
instances := h.Backend.ListInstances()
@@ -78,7 +80,7 @@ func (h *Handler) handleCreateInstance(c *echo.Context, body []byte) error {
ClientToken string `json:"ClientToken"`
Tags []tagView `json:"Tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -105,7 +107,7 @@ func (h *Handler) handleDescribeInstance(c *echo.Context, body []byte) error {
var req struct {
InstanceArn string `json:"InstanceArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
@@ -162,7 +164,7 @@ func (h *Handler) handleDeleteInstance(c *echo.Context, body []byte) error {
var req struct {
InstanceArn string `json:"InstanceArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
@@ -186,7 +188,7 @@ func (h *Handler) handleUpdateInstance(c *echo.Context, body []byte) error {
InstanceArn string `json:"InstanceArn"`
Name string `json:"Name"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
diff --git a/services/ssoadmin/handler_managed_policies.go b/services/ssoadmin/handler_managed_policies.go
index 65560d35aa..ddf0837cf7 100644
--- a/services/ssoadmin/handler_managed_policies.go
+++ b/services/ssoadmin/handler_managed_policies.go
@@ -1,11 +1,13 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type managedPolicyView struct {
@@ -19,7 +21,7 @@ func (h *Handler) handleAttachManagedPolicyToPermissionSet(c *echo.Context, body
PermissionSetArn string `json:"PermissionSetArn"`
ManagedPolicyArn string `json:"ManagedPolicyArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -59,7 +61,7 @@ func (h *Handler) handleDetachManagedPolicyFromPermissionSet(c *echo.Context, bo
PermissionSetArn string `json:"PermissionSetArn"`
ManagedPolicyArn string `json:"ManagedPolicyArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -106,7 +108,7 @@ func (h *Handler) handleAttachCustomerManagedPolicyReferenceToPermissionSet(c *e
PermissionSetArn string `json:"PermissionSetArn"`
CustomerManagedPolicyReference customerManagedPolicyReferenceView `json:"CustomerManagedPolicyReference"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -152,7 +154,7 @@ func (h *Handler) handleDetachCustomerManagedPolicyReferenceFromPermissionSet(
PermissionSetArn string `json:"PermissionSetArn"`
CustomerManagedPolicyReference customerManagedPolicyReferenceView `json:"CustomerManagedPolicyReference"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
diff --git a/services/ssoadmin/handler_permission_sets.go b/services/ssoadmin/handler_permission_sets.go
index acf5ebefd6..2753d7dbf1 100644
--- a/services/ssoadmin/handler_permission_sets.go
+++ b/services/ssoadmin/handler_permission_sets.go
@@ -1,12 +1,14 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"sort"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type permissionSetView struct {
@@ -27,7 +29,7 @@ func (h *Handler) handleCreatePermissionSet(c *echo.Context, body []byte) error
RelayState string `json:"RelayState"`
Tags []tagView `json:"Tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
@@ -85,7 +87,7 @@ func (h *Handler) handleDescribePermissionSet(c *echo.Context, body []byte) erro
InstanceArn string `json:"InstanceArn"`
PermissionSetArn string `json:"PermissionSetArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -112,7 +114,7 @@ func (h *Handler) handleListPermissionSets(c *echo.Context, body []byte) error {
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -139,7 +141,7 @@ func (h *Handler) handleDeletePermissionSet(c *echo.Context, body []byte) error
InstanceArn string `json:"InstanceArn"`
PermissionSetArn string `json:"PermissionSetArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -167,7 +169,7 @@ func (h *Handler) handleUpdatePermissionSet(c *echo.Context, body []byte) error
SessionDuration string `json:"SessionDuration"`
RelayState string `json:"RelayState"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -191,7 +193,7 @@ func (h *Handler) handleProvisionPermissionSet(c *echo.Context, body []byte) err
TargetType string `json:"TargetType"`
TargetID string `json:"TargetId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
@@ -248,7 +250,7 @@ func (h *Handler) handleDescribePermissionSetProvisioningStatus(
InstanceArn string `json:"InstanceArn"`
ProvisionPermissionSetRequestID string `json:"ProvisionPermissionSetRequestId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -290,7 +292,7 @@ func (h *Handler) handleListPermissionSetsProvisionedToAccount(c *echo.Context,
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
@@ -338,7 +340,7 @@ func (h *Handler) handleListAccountsForProvisionedPermissionSet(
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
diff --git a/services/ssoadmin/handler_permissions_boundary.go b/services/ssoadmin/handler_permissions_boundary.go
index 9122b2b3f9..045651695f 100644
--- a/services/ssoadmin/handler_permissions_boundary.go
+++ b/services/ssoadmin/handler_permissions_boundary.go
@@ -1,10 +1,12 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handlePutPermissionsBoundaryToPermissionSet(c *echo.Context, body []byte) error {
@@ -19,7 +21,7 @@ func (h *Handler) handlePutPermissionsBoundaryToPermissionSet(c *echo.Context, b
InstanceArn string `json:"InstanceArn"`
PermissionSetArn string `json:"PermissionSetArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -49,7 +51,7 @@ func (h *Handler) handleGetPermissionsBoundaryForPermissionSet(c *echo.Context,
InstanceArn string `json:"InstanceArn"`
PermissionSetArn string `json:"PermissionSetArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -79,7 +81,7 @@ func (h *Handler) handleDeletePermissionsBoundaryFromPermissionSet(c *echo.Conte
InstanceArn string `json:"InstanceArn"`
PermissionSetArn string `json:"PermissionSetArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
diff --git a/services/ssoadmin/handler_regions.go b/services/ssoadmin/handler_regions.go
index c055a88e99..566180910e 100644
--- a/services/ssoadmin/handler_regions.go
+++ b/services/ssoadmin/handler_regions.go
@@ -1,10 +1,12 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleAddRegion(c *echo.Context, body []byte) error {
@@ -12,7 +14,7 @@ func (h *Handler) handleAddRegion(c *echo.Context, body []byte) error {
InstanceArn string `json:"InstanceArn"`
RegionName string `json:"RegionName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -31,7 +33,7 @@ func (h *Handler) handleRemoveRegion(c *echo.Context, body []byte) error {
InstanceArn string `json:"InstanceArn"`
RegionName string `json:"RegionName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
@@ -56,7 +58,7 @@ func (h *Handler) handleListRegions(c *echo.Context, body []byte) error {
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
@@ -100,7 +102,7 @@ func (h *Handler) handleDescribeRegion(c *echo.Context, body []byte) error {
InstanceArn string `json:"InstanceArn"`
RegionName string `json:"RegionName"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
diff --git a/services/ssoadmin/handler_tags.go b/services/ssoadmin/handler_tags.go
index 099f2ab0bc..6463dad0b4 100644
--- a/services/ssoadmin/handler_tags.go
+++ b/services/ssoadmin/handler_tags.go
@@ -1,12 +1,14 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"sort"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleTagResource(c *echo.Context, body []byte) error {
@@ -15,7 +17,7 @@ func (h *Handler) handleTagResource(c *echo.Context, body []byte) error {
ResourceArn string `json:"ResourceArn"`
Tags []tagView `json:"Tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -42,7 +44,7 @@ func (h *Handler) handleUntagResource(c *echo.Context, body []byte) error {
ResourceArn string `json:"ResourceArn"`
TagKeys []string `json:"TagKeys"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -59,7 +61,7 @@ func (h *Handler) handleListTagsForResource(c *echo.Context, body []byte) error
ResourceArn string `json:"ResourceArn"`
NextToken string `json:"NextToken"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
diff --git a/services/ssoadmin/handler_test.go b/services/ssoadmin/handler_test.go
index 6cb7a7dcdd..c6635e08c5 100644
--- a/services/ssoadmin/handler_test.go
+++ b/services/ssoadmin/handler_test.go
@@ -2,7 +2,7 @@ package ssoadmin_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/ssoadmin"
)
@@ -33,7 +34,7 @@ func doRequest(
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -53,7 +54,7 @@ func doRequest(
func parseResponse(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
@@ -444,7 +445,7 @@ func TestHandler_ExtractResource(t *testing.T) {
var bodyBytes []byte
if tt.body != nil {
var err error
- bodyBytes, err = json.Marshal(tt.body)
+ bodyBytes, err = json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
}
diff --git a/services/ssoadmin/handler_trusted_token_issuers.go b/services/ssoadmin/handler_trusted_token_issuers.go
index fd1dfb54ec..0d37f2c0a0 100644
--- a/services/ssoadmin/handler_trusted_token_issuers.go
+++ b/services/ssoadmin/handler_trusted_token_issuers.go
@@ -1,11 +1,13 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// trustedTokenIssuerView is the wire shape for types.TrustedTokenIssuerMetadata
@@ -34,7 +36,7 @@ func (h *Handler) handleCreateTrustedTokenIssuer(c *echo.Context, body []byte) e
ClientToken string `json:"ClientToken"`
Tags []tagView `json:"Tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.InstanceArn == "" {
@@ -93,7 +95,7 @@ func (h *Handler) handleDeleteTrustedTokenIssuer(c *echo.Context, body []byte) e
var req struct {
TrustedTokenIssuerArn string `json:"TrustedTokenIssuerArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
if req.TrustedTokenIssuerArn == "" {
@@ -110,7 +112,7 @@ func (h *Handler) handleDescribeTrustedTokenIssuer(c *echo.Context, body []byte)
var req struct {
TrustedTokenIssuerArn string `json:"TrustedTokenIssuerArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
@@ -153,7 +155,7 @@ func (h *Handler) handleListTrustedTokenIssuers(c *echo.Context, body []byte) er
NextToken string `json:"NextToken"`
MaxResults int `json:"MaxResults"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
issuers := h.Backend.ListTrustedTokenIssuers(req.InstanceArn)
@@ -189,7 +191,7 @@ func (h *Handler) handleUpdateTrustedTokenIssuer(c *echo.Context, body []byte) e
Name string `json:"Name"`
TrustedTokenIssuerType string `json:"TrustedTokenIssuerType"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return writeError(c, http.StatusBadRequest, "ValidationException", "invalid request body")
}
diff --git a/services/ssoadmin/interfaces.go b/services/ssoadmin/interfaces.go
index 2643333c8e..3dd9aa1c0a 100644
--- a/services/ssoadmin/interfaces.go
+++ b/services/ssoadmin/interfaces.go
@@ -2,7 +2,7 @@ package ssoadmin
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
)
// StorageBackend is the interface for SSO Admin storage operations.
@@ -74,9 +74,9 @@ type StorageBackend interface {
PutApplicationAccessScope(applicationArn, scope string, authorizedTargets []string) error
PutApplicationAssignmentConfiguration(applicationArn string, assignmentRequired bool) error
// PutApplicationAuthenticationMethod stores a structured auth method body; authMethodType must be IAM.
- PutApplicationAuthenticationMethod(applicationArn, authMethodType string, body json.RawMessage) error
+ PutApplicationAuthenticationMethod(applicationArn, authMethodType string, body jsontext.Value) error
// PutApplicationGrant stores a structured grant body; grantType must be a valid AWS grant type enum value.
- PutApplicationGrant(applicationArn, grantType string, body json.RawMessage) error
+ PutApplicationGrant(applicationArn, grantType string, body jsontext.Value) error
// PutApplicationSessionConfiguration sets the UserBackgroundSessionApplicationStatus
// (ENABLED or DISABLED) for an application. Matches the real
// PutApplicationSessionConfigurationInput wire shape -- this is NOT a
@@ -116,8 +116,8 @@ type StorageBackend interface {
GetApplicationAssignmentConfiguration(applicationArn string) (bool, error)
GetApplicationSessionConfiguration(applicationArn string) (string, error)
GetApplicationAccessScope(applicationArn, scope string) ([]string, error)
- GetApplicationAuthenticationMethod(applicationArn, authMethodType string) (json.RawMessage, error)
- GetApplicationGrant(applicationArn, grantType string) (json.RawMessage, error)
+ GetApplicationAuthenticationMethod(applicationArn, authMethodType string) (jsontext.Value, error)
+ GetApplicationGrant(applicationArn, grantType string) (jsontext.Value, error)
ListCustomerManagedPolicyReferencesInPermissionSet(
instanceArn, permissionSetArn string,
) ([]CustomerManagedPolicyReference, error)
diff --git a/services/ssoadmin/models.go b/services/ssoadmin/models.go
index b39cb1d11e..e9b98cefd4 100644
--- a/services/ssoadmin/models.go
+++ b/services/ssoadmin/models.go
@@ -1,7 +1,7 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/jsontext"
"regexp"
"time"
)
@@ -348,12 +348,12 @@ type ScopeDetails struct {
// AuthMethod holds a typed authentication method with its full structured body.
type AuthMethod struct {
- AuthMethodType string `json:"AuthenticationMethodType"`
- Body json.RawMessage `json:"AuthenticationMethod"`
+ AuthMethodType string `json:"AuthenticationMethodType"`
+ Body jsontext.Value `json:"AuthenticationMethod"`
}
// ApplicationGrant holds a typed grant with its structured body.
type ApplicationGrant struct {
- GrantType string `json:"GrantType"`
- Grant json.RawMessage `json:"Grant"`
+ GrantType string `json:"GrantType"`
+ Grant jsontext.Value `json:"Grant"`
}
diff --git a/services/ssoadmin/multi_region_test.go b/services/ssoadmin/multi_region_test.go
index 6cc2def6c8..dca0aee1fa 100644
--- a/services/ssoadmin/multi_region_test.go
+++ b/services/ssoadmin/multi_region_test.go
@@ -2,7 +2,8 @@ package ssoadmin_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/ssoadmin"
)
@@ -172,8 +174,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/ssoadmin/persistence.go b/services/ssoadmin/persistence.go
index 840e985b40..0b944935a3 100644
--- a/services/ssoadmin/persistence.go
+++ b/services/ssoadmin/persistence.go
@@ -2,11 +2,13 @@ package ssoadmin
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -74,7 +76,7 @@ func newDirtyDTORegistry() (
// newer) build of this backend as though it were the current shape; see
// Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Assignments map[string][]*AccountAssignment `json:"assignments"`
AssignmentCreationIDs map[string]string `json:"assignmentCreationIDs"`
ProvisionedAt map[string]time.Time `json:"provisionedAt"`
@@ -82,8 +84,8 @@ type backendSnapshot struct {
CustomerManagedPolicies map[string][]CustomerManagedPolicyReference `json:"customerManagedPolicies"`
ApplicationAssignments map[string][]*ApplicationAssignment `json:"applicationAssignments"`
ApplicationScopes map[string]map[string][]string `json:"applicationScopes"`
- ApplicationAuthMethods map[string]map[string]json.RawMessage `json:"applicationAuthMethods"`
- ApplicationGrants map[string]map[string]json.RawMessage `json:"applicationGrants"`
+ ApplicationAuthMethods map[string]map[string]jsontext.Value `json:"applicationAuthMethods"`
+ ApplicationGrants map[string]map[string]jsontext.Value `json:"applicationGrants"`
ApplicationAssignConfig map[string]bool `json:"applicationAssignConfig"`
ApplicationSessions map[string]string `json:"applicationSessions"`
AccountID string `json:"accountID"`
@@ -140,7 +142,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Region: b.region,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "ssoadmin: failed to marshal snapshot", "error", err)
@@ -182,8 +184,8 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
b.customerManagedPolicies = make(map[string][]CustomerManagedPolicyReference)
b.applicationAssignments = make(map[string][]*ApplicationAssignment)
b.applicationScopes = make(map[string]map[string][]string)
- b.applicationAuthMethods = make(map[string]map[string]json.RawMessage)
- b.applicationGrants = make(map[string]map[string]json.RawMessage)
+ b.applicationAuthMethods = make(map[string]map[string]jsontext.Value)
+ b.applicationGrants = make(map[string]map[string]jsontext.Value)
b.applicationAssignConfig = make(map[string]bool)
b.applicationSessions = make(map[string]string)
b.seedDefaultInstance()
@@ -222,7 +224,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// permissionBoundaries) from tables via the ephemeral DTO registry,
// converting each DTO back to its live type. The caller MUST hold b.mu for
// writing.
-func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]jsontext.Value) error {
dtoReg, acaDTOs, pbDTOs := newDirtyDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
diff --git a/services/ssoadmin/store.go b/services/ssoadmin/store.go
index ed35b44a08..ce79de0a4b 100644
--- a/services/ssoadmin/store.go
+++ b/services/ssoadmin/store.go
@@ -1,7 +1,7 @@
package ssoadmin
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
@@ -34,9 +34,9 @@ type InMemoryBackend struct {
applicationAssignments map[string][]*ApplicationAssignment
applicationScopes map[string]map[string][]string
// applicationAuthMethods stores per-app authentication methods: appArn → methodType → full JSON body.
- applicationAuthMethods map[string]map[string]json.RawMessage
+ applicationAuthMethods map[string]map[string]jsontext.Value
// applicationGrants stores per-app grants: appArn → grantType → full JSON body.
- applicationGrants map[string]map[string]json.RawMessage
+ applicationGrants map[string]map[string]jsontext.Value
applicationAssignConfig map[string]bool
applicationSessions map[string]string
instanceACAs *store.Table[ABACConfig]
@@ -81,8 +81,8 @@ func NewInMemoryBackend(accountID, region string) *InMemoryBackend {
customerManagedPolicies: make(map[string][]CustomerManagedPolicyReference),
applicationAssignments: make(map[string][]*ApplicationAssignment),
applicationScopes: make(map[string]map[string][]string),
- applicationAuthMethods: make(map[string]map[string]json.RawMessage),
- applicationGrants: make(map[string]map[string]json.RawMessage),
+ applicationAuthMethods: make(map[string]map[string]jsontext.Value),
+ applicationGrants: make(map[string]map[string]jsontext.Value),
applicationAssignConfig: make(map[string]bool),
applicationSessions: make(map[string]string),
mu: lockmetrics.New("ssoadmin"),
@@ -115,8 +115,8 @@ func (b *InMemoryBackend) Reset() {
b.customerManagedPolicies = make(map[string][]CustomerManagedPolicyReference)
b.applicationAssignments = make(map[string][]*ApplicationAssignment)
b.applicationScopes = make(map[string]map[string][]string)
- b.applicationAuthMethods = make(map[string]map[string]json.RawMessage)
- b.applicationGrants = make(map[string]map[string]json.RawMessage)
+ b.applicationAuthMethods = make(map[string]map[string]jsontext.Value)
+ b.applicationGrants = make(map[string]map[string]jsontext.Value)
b.applicationAssignConfig = make(map[string]bool)
b.applicationSessions = make(map[string]string)
// Re-seed the default instance.
diff --git a/services/ssooidc/handler.go b/services/ssooidc/handler.go
index a51f5b51e0..3db3cd1c0d 100644
--- a/services/ssooidc/handler.go
+++ b/services/ssooidc/handler.go
@@ -1,7 +1,7 @@
package ssooidc
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/url"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -151,7 +152,7 @@ func decodeBody(c *echo.Context, v any) *APIError {
return nil
}
- if json.Unmarshal(body, v) != nil {
+ if json.Unmarshal(body, v, jsonx.V1()) != nil {
return errInvalidRequest("the request body is not valid JSON")
}
diff --git a/services/ssooidc/persistence.go b/services/ssooidc/persistence.go
index 61ed403621..8d0624a1ac 100644
--- a/services/ssooidc/persistence.go
+++ b/services/ssooidc/persistence.go
@@ -2,7 +2,7 @@ package ssooidc
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,8 +12,8 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ Version int `json:"version"`
}
// Snapshot serializes the backend state. It implements persistence.Persistable.
diff --git a/services/stepfunctions/aliases_test.go b/services/stepfunctions/aliases_test.go
index 876b30c698..bf1bd81bdb 100644
--- a/services/stepfunctions/aliases_test.go
+++ b/services/stepfunctions/aliases_test.go
@@ -2,7 +2,7 @@ package stepfunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions"
)
@@ -29,7 +30,7 @@ func createSFNStateMachineCov(
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn, _ := resp["stateMachineArn"].(string)
require.NotEmpty(t, arn)
@@ -299,14 +300,14 @@ func TestCreateStateMachineAlias_RoutingViaHandler(t *testing.T) {
"routingConfiguration": []map[string]any{
{"stateMachineVersionArn": "arn:fake:version:1", "weight": 50},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "CreateStateMachineAlias", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// CHANGED (orphan-code sweep, gopherstack-yatn): this assertion previously
// checked for "InvalidRoutingConfiguration", which is not a real sfn SDK
// error type -- CreateStateMachineAlias's own deserializeOpError switch
diff --git a/services/stepfunctions/asl/distributed_scope.go b/services/stepfunctions/asl/distributed_scope.go
index e6d7ab3298..40517f53bc 100644
--- a/services/stepfunctions/asl/distributed_scope.go
+++ b/services/stepfunctions/asl/distributed_scope.go
@@ -1,11 +1,13 @@
package asl
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"regexp"
"slices"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var varRefRe = regexp.MustCompile(
@@ -30,13 +32,13 @@ func checkDistributedScope(mapName string, states map[string]*State, outer map[s
continue
}
- b, err := json.Marshal(st)
+ b, err := json.Marshal(st, jsonx.V1())
if err != nil {
return fmt.Errorf("%w: state %q: %w", ErrParseError, name, err)
}
var doc any
- if err = json.Unmarshal(b, &doc); err != nil {
+ if err = json.Unmarshal(b, &doc, jsonx.V1()); err != nil {
return fmt.Errorf("%w: state %q: %w", ErrParseError, name, err)
}
diff --git a/services/stepfunctions/asl/executor.go b/services/stepfunctions/asl/executor.go
index c1071f55c4..dcd5de891d 100644
--- a/services/stepfunctions/asl/executor.go
+++ b/services/stepfunctions/asl/executor.go
@@ -7,7 +7,8 @@ import (
cryptorand "crypto/rand"
"encoding/base64"
"encoding/csv"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -26,6 +27,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/apptrace"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
"github.com/blackbirdworks/gopherstack/pkgs/awstime"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/roleauth"
)
@@ -747,7 +749,7 @@ func (e *Executor) Execute(
) (*ExecutionResult, error) {
var input any
if inputJSON != "" {
- if err := json.Unmarshal([]byte(inputJSON), &input); err != nil {
+ if err := json.Unmarshal([]byte(inputJSON), &input, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid input JSON: %w", err)
}
}
@@ -981,7 +983,7 @@ func (e *Executor) executeState(
func (e *Executor) executePass(state *State, input any) (string, any, error) {
if len(state.Result) > 0 {
var result any
- if err := json.Unmarshal(state.Result, &result); err != nil {
+ if err := json.Unmarshal(state.Result, &result, jsonx.V1()); err != nil {
return "", nil, fmt.Errorf("invalid Pass Result: %w", err)
}
@@ -1381,7 +1383,7 @@ func (e *Executor) invokeTaskAttempt(
func parseCallbackOutput(output string) any {
var result any
if output != "" {
- if err := json.Unmarshal([]byte(output), &result); err == nil {
+ if err := json.Unmarshal([]byte(output), &result, jsonx.V1()); err == nil {
return result
}
}
@@ -1679,7 +1681,7 @@ func (e *Executor) invokeActivityTask(ctx context.Context, state *State, input a
}
var result any
- if unmarshalErr := json.Unmarshal([]byte(output), &result); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal([]byte(output), &result, jsonx.V1()); unmarshalErr != nil {
// Non-JSON output is valid; return the raw string. Intentionally ignoring unmarshalErr.
return output, nil //nolint:nilerr // non-JSON activity output is valid; return as string
}
@@ -1697,7 +1699,7 @@ func (e *Executor) invokeLambdaTask(ctx context.Context, state *State, input any
return nil, err
}
- payload, err := json.Marshal(input)
+ payload, err := json.Marshal(input, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("failed to marshal task input: %w", err)
}
@@ -1723,7 +1725,7 @@ func (e *Executor) invokeLambdaTask(ctx context.Context, state *State, input any
var result any
// Non-JSON Lambda responses are valid; fall back to raw string.
- if json.Unmarshal(respBytes, &result) != nil {
+ if json.Unmarshal(respBytes, &result, jsonx.V1()) != nil {
result = string(respBytes)
}
@@ -2538,7 +2540,7 @@ func batchItems(items []any, maxItemsPerBatch, maxInputBytesPerBatch int) []any
return batches
}
-func (e *Executor) batchInput(b *ItemBatcher) json.RawMessage {
+func (e *Executor) batchInput(b *ItemBatcher) jsontext.Value {
if e.jxMapCfg != nil {
return e.jxMapCfg.batchInput
}
@@ -2552,10 +2554,10 @@ func (e *Executor) batchInput(b *ItemBatcher) json.RawMessage {
// input-output-itembatcher.html, "Batch input"). Previously each batch was
// passed through as a bare array, which is not the shape AWS sends to child
// executions.
-func wrapItemBatcherBatches(rawBatches []any, batchInput json.RawMessage) ([]any, error) {
+func wrapItemBatcherBatches(rawBatches []any, batchInput jsontext.Value) ([]any, error) {
var decodedBatchInput any
if len(batchInput) > 0 {
- if err := json.Unmarshal(batchInput, &decodedBatchInput); err != nil {
+ if err := json.Unmarshal(batchInput, &decodedBatchInput, jsonx.V1()); err != nil {
return nil, fmt.Errorf("ItemBatcher.BatchInput error: %w", err)
}
}
@@ -2576,7 +2578,7 @@ func wrapItemBatcherBatches(rawBatches []any, batchInput json.RawMessage) ([]any
func applyMapItemSelector(
slot *atomic.Pointer[parsedTemplate],
- itemSelector json.RawMessage,
+ itemSelector jsontext.Value,
items []any,
vars varFunc,
) ([]any, error) {
@@ -2984,7 +2986,7 @@ func (e *Executor) resolveS3InventoryManifest(
cfg *ReaderConfig,
) ([]any, error) {
var manifest s3InventoryManifest
- if err := json.Unmarshal(manifestData, &manifest); err != nil {
+ if err := json.Unmarshal(manifestData, &manifest, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: manifest.json: %w", ErrItemReaderInvalidData, err)
}
@@ -3078,7 +3080,7 @@ func decodeJSONItems(data []byte, cfg *ReaderConfig) ([]any, error) {
}
var doc any
- if err := json.Unmarshal(data, &doc); err != nil {
+ if err := json.Unmarshal(data, &doc, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", ErrItemReaderInvalidData, err)
}
@@ -3155,7 +3157,7 @@ func stepJSONPointer(cur any, tok string) (any, error) {
func decodeJSONAuto(data []byte) ([]any, error) {
var arr []any
- if jsonErr := json.Unmarshal(data, &arr); jsonErr == nil {
+ if jsonErr := json.Unmarshal(data, &arr, jsonx.V1()); jsonErr == nil {
return arr, nil
}
@@ -3174,7 +3176,7 @@ func decodeJSONLines(data []byte) ([]any, error) {
}
var item any
- if jsonErr := json.Unmarshal([]byte(line), &item); jsonErr != nil {
+ if jsonErr := json.Unmarshal([]byte(line), &item, jsonx.V1()); jsonErr != nil {
return nil, fmt.Errorf("%w: %w", ErrItemReaderInvalidData, jsonErr)
}
@@ -4469,7 +4471,7 @@ func marshalInput(v any) string {
return "{}"
}
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return "{}"
}
@@ -4485,13 +4487,13 @@ type parsedTemplate struct {
}
// loadTemplate returns the slot's parsed template, decoding raw on first use.
-func loadTemplate(slot *atomic.Pointer[parsedTemplate], raw json.RawMessage) *parsedTemplate {
+func loadTemplate(slot *atomic.Pointer[parsedTemplate], raw jsontext.Value) *parsedTemplate {
if pt := slot.Load(); pt != nil {
return pt
}
pt := &parsedTemplate{usesContext: bytes.Contains(raw, []byte("$$"))}
- if err := json.Unmarshal(raw, &pt.tmpl); err != nil {
+ if err := json.Unmarshal(raw, &pt.tmpl, jsonx.V1()); err != nil {
pt.err = fmt.Errorf("invalid template: %w", err)
}
diff --git a/services/stepfunctions/asl/executor_test.go b/services/stepfunctions/asl/executor_test.go
index 3535599a67..c654d4d0b5 100644
--- a/services/stepfunctions/asl/executor_test.go
+++ b/services/stepfunctions/asl/executor_test.go
@@ -2,7 +2,7 @@ package asl_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"sync/atomic"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions/asl"
)
@@ -2112,7 +2113,7 @@ func TestExecutor_UnknownStateType(t *testing.T) {
// Parse will succeed but execute will fail on unknown type.
var sm asl.StateMachine
- require.NoError(t, json.Unmarshal([]byte(tt.def), &sm))
+ require.NoError(t, json.Unmarshal([]byte(tt.def), &sm, jsonx.V1()))
exec := asl.NewExecutor(&sm, nil, nil)
_, err := exec.Execute(t.Context(), "test", tt.input)
require.Error(t, err)
diff --git a/services/stepfunctions/asl/intrinsics.go b/services/stepfunctions/asl/intrinsics.go
index ac8b113e46..8988b9b6bc 100644
--- a/services/stepfunctions/asl/intrinsics.go
+++ b/services/stepfunctions/asl/intrinsics.go
@@ -6,7 +6,7 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"hash"
@@ -15,6 +15,8 @@ import (
"slices"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Sentinel errors for intrinsic function evaluation.
@@ -260,7 +262,7 @@ func intrinsicFormat(args []any) (any, error) {
case nil:
result.WriteString("null")
default:
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("States.Format: cannot serialize argument: %w", err)
}
@@ -289,7 +291,7 @@ func intrinsicStringToJSON(args []any) (any, error) {
}
var result any
- if err := json.Unmarshal([]byte(s), &result); err != nil {
+ if err := json.Unmarshal([]byte(s), &result, jsonx.V1()); err != nil {
return nil, fmt.Errorf("States.StringToJson: invalid JSON: %w", err)
}
@@ -302,7 +304,7 @@ func intrinsicJSONToString(args []any) (any, error) {
return nil, ErrStatesJSONToStringRequiresArg
}
- b, err := json.Marshal(args[0])
+ b, err := json.Marshal(args[0], jsonx.V1())
if err != nil {
return nil, fmt.Errorf("States.JsonToString: cannot serialize: %w", err)
}
diff --git a/services/stepfunctions/asl/item_reader_parquet.go b/services/stepfunctions/asl/item_reader_parquet.go
index f98cec716a..2f6dde0f6e 100644
--- a/services/stepfunctions/asl/item_reader_parquet.go
+++ b/services/stepfunctions/asl/item_reader_parquet.go
@@ -2,10 +2,11 @@ package asl
import (
"encoding/binary"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/parquetio"
)
@@ -66,13 +67,13 @@ func decodeParquetItems(data []byte) ([]any, error) {
// normalizeJSONNumbers round-trips items so numbers carry the float64 type
// every other ItemReader decoder yields.
func normalizeJSONNumbers(items []any) ([]any, error) {
- raw, err := json.Marshal(items)
+ raw, err := json.Marshal(items, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("%w: %w", ErrItemReaderInvalidData, err)
}
var out []any
- if err = json.Unmarshal(raw, &out); err != nil {
+ if err = json.Unmarshal(raw, &out, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", ErrItemReaderInvalidData, err)
}
diff --git a/services/stepfunctions/asl/jsonata.go b/services/stepfunctions/asl/jsonata.go
index 978adea651..2349a97a6a 100644
--- a/services/stepfunctions/asl/jsonata.go
+++ b/services/stepfunctions/asl/jsonata.go
@@ -2,7 +2,7 @@ package asl
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -11,6 +11,8 @@ import (
"time"
"github.com/recolabs/gnata"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -114,13 +116,13 @@ func normalizeJSONata(v any) (any, error) {
return v, nil
}
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return nil, queryEvalError(fmt.Errorf("%w: result is not valid JSON: %w", errJSONataEval, err))
}
var out any
- if err = json.Unmarshal(b, &out); err != nil {
+ if err = json.Unmarshal(b, &out, jsonx.V1()); err != nil {
return nil, queryEvalError(err)
}
diff --git a/services/stepfunctions/asl/jsonata_exec.go b/services/stepfunctions/asl/jsonata_exec.go
index 4aa9ad90a8..ed6736a581 100644
--- a/services/stepfunctions/asl/jsonata_exec.go
+++ b/services/stepfunctions/asl/jsonata_exec.go
@@ -2,13 +2,16 @@ package asl
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
"math"
"slices"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// runJSONataState runs one JSONata-mode state (Arguments, body, then the
@@ -396,7 +399,7 @@ func (e *Executor) jxItemSelector(state *State, items []any) ([]any, error) {
type jxMapConfig struct {
readerParams map[string]any
writerParams map[string]any
- batchInput json.RawMessage
+ batchInput jsontext.Value
maxItems int
maxBytes int
}
@@ -445,7 +448,7 @@ func (s *jxScope) resolveBatcher(b *ItemBatcher, cfg *jxMapConfig) error {
return err
}
- if cfg.batchInput, err = json.Marshal(v); err != nil {
+ if cfg.batchInput, err = json.Marshal(v, jsonx.V1()); err != nil {
return queryEvalError(err)
}
diff --git a/services/stepfunctions/asl/jsonata_validate.go b/services/stepfunctions/asl/jsonata_validate.go
index c0517b57c5..d6603a346f 100644
--- a/services/stepfunctions/asl/jsonata_validate.go
+++ b/services/stepfunctions/asl/jsonata_validate.go
@@ -1,12 +1,15 @@
package asl
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"regexp"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -144,20 +147,20 @@ func walkNested(name string, st *State, lang string, inner map[string]struct{})
return nil
}
-func decodeAssign(raw json.RawMessage) (map[string]any, error) {
+func decodeAssign(raw jsontext.Value) (map[string]any, error) {
if len(raw) == 0 {
return nil, nil //nolint:nilnil // field absent
}
var m map[string]any
- if err := json.Unmarshal(raw, &m); err != nil || m == nil {
+ if err := json.Unmarshal(raw, &m, jsonx.V1()); err != nil || m == nil {
return nil, fmt.Errorf("%w: Assign must be a JSON object", ErrParseError)
}
return m, nil
}
-func assignNames(raw json.RawMessage, into map[string]struct{}) error {
+func assignNames(raw jsontext.Value, into map[string]struct{}) error {
m, err := decodeAssign(raw)
if err != nil {
return err
@@ -373,9 +376,9 @@ func rejectJSONPathFields(name string, st *State) error {
return nil
}
-func decodeRaw(name, field string, raw json.RawMessage) (any, error) {
+func decodeRaw(name, field string, raw jsontext.Value) (any, error) {
var v any
- if err := json.Unmarshal(raw, &v); err != nil {
+ if err := json.Unmarshal(raw, &v, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: state %q: invalid %s: %w", ErrParseError, name, field, err)
}
@@ -487,7 +490,7 @@ func (j *jxState) prepareFields(name string, st *State) error {
dst *any
set *bool
field string
- raw json.RawMessage
+ raw jsontext.Value
ctx exprCtx
}{
{&j.args, &j.hasArgs, "Arguments", st.Arguments, plain},
@@ -519,7 +522,7 @@ func (j *jxState) prepareFields(name string, st *State) error {
return j.prepareRest(name, st, exprCtx{result: hasResult})
}
-func readerArguments(st *State) json.RawMessage {
+func readerArguments(st *State) jsontext.Value {
if st.ItemReader == nil {
return nil
}
@@ -527,7 +530,7 @@ func readerArguments(st *State) json.RawMessage {
return st.ItemReader.Arguments
}
-func writerArguments(st *State) json.RawMessage {
+func writerArguments(st *State) jsontext.Value {
if st.ResultWriter == nil {
return nil
}
@@ -535,7 +538,7 @@ func writerArguments(st *State) json.RawMessage {
return st.ResultWriter.Arguments
}
-func batchInputRaw(st *State) json.RawMessage {
+func batchInputRaw(st *State) jsontext.Value {
if st.ItemBatcher == nil {
return nil
}
diff --git a/services/stepfunctions/asl/mock.go b/services/stepfunctions/asl/mock.go
index 82ff496a15..23d2d4c4ff 100644
--- a/services/stepfunctions/asl/mock.go
+++ b/services/stepfunctions/asl/mock.go
@@ -1,7 +1,8 @@
package asl
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"os"
@@ -9,6 +10,8 @@ import (
"strconv"
"strings"
"sync/atomic"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Mock config errors.
@@ -29,7 +32,7 @@ type MockConfig struct {
type mockStep struct {
throw *mockThrow
- ret json.RawMessage
+ ret jsontext.Value
lo int
hi int
}
@@ -40,8 +43,8 @@ type mockThrow struct {
}
type mockFileStep struct {
- Throw *mockThrow `json:"Throw"`
- Return json.RawMessage `json:"Return"`
+ Throw *mockThrow `json:"Throw"`
+ Return jsontext.Value `json:"Return"`
}
type mockFile struct {
@@ -64,7 +67,7 @@ func LoadMockConfig(path string) (*MockConfig, error) {
// ParseMockConfig validates mock configuration JSON.
func ParseMockConfig(data []byte) (*MockConfig, error) {
var f mockFile
- if err := json.Unmarshal(data, &f); err != nil {
+ if err := json.Unmarshal(data, &f, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", ErrMockConfigInvalid, err)
}
@@ -204,7 +207,7 @@ func (r *MockRun) invoke(stateName string) (any, bool, error) {
}
var out any
- if uerr := json.Unmarshal(s.ret, &out); uerr != nil {
+ if uerr := json.Unmarshal(s.ret, &out, jsonx.V1()); uerr != nil {
return nil, true, &FailError{ErrCode: errCodeStatesRuntime, Cause: uerr.Error()}
}
diff --git a/services/stepfunctions/asl/parser.go b/services/stepfunctions/asl/parser.go
index 864f7de619..f96daa3cb0 100644
--- a/services/stepfunctions/asl/parser.go
+++ b/services/stepfunctions/asl/parser.go
@@ -4,10 +4,13 @@ package asl
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"sync/atomic"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrParseError is returned when the state machine definition cannot be parsed.
@@ -70,37 +73,37 @@ type ItemBatcher struct {
maxItemsExpr string
maxBytesExpr string
// BatchInput is fixed JSON merged into every batch (input-output-itembatcher.html).
- BatchInput json.RawMessage `json:"BatchInput,omitempty"`
+ BatchInput jsontext.Value `json:"BatchInput,omitempty"`
// MaxItemsPerBatch and MaxInputBytesPerBatch: zero means no limit.
MaxItemsPerBatch int `json:"MaxItemsPerBatch,omitempty"`
MaxInputBytesPerBatch int `json:"MaxInputBytesPerBatch,omitempty"`
}
type itemBatcherWire struct {
- MaxItemsPerBatchPath string `json:"MaxItemsPerBatchPath,omitempty"`
- MaxInputBytesPerBatchPath string `json:"MaxInputBytesPerBatchPath,omitempty"`
- MaxItemsPerBatch json.RawMessage `json:"MaxItemsPerBatch,omitempty"`
- MaxInputBytesPerBatch json.RawMessage `json:"MaxInputBytesPerBatch,omitempty"`
- BatchInput json.RawMessage `json:"BatchInput,omitempty"`
+ MaxItemsPerBatchPath string `json:"MaxItemsPerBatchPath,omitempty"`
+ MaxInputBytesPerBatchPath string `json:"MaxInputBytesPerBatchPath,omitempty"`
+ MaxItemsPerBatch jsontext.Value `json:"MaxItemsPerBatch,omitempty"`
+ MaxInputBytesPerBatch jsontext.Value `json:"MaxInputBytesPerBatch,omitempty"`
+ BatchInput jsontext.Value `json:"BatchInput,omitempty"`
}
// decodeBatchLimit reads an integer limit, or a JSONata string into expr.
-func decodeBatchLimit(raw json.RawMessage, n *int, expr *string) error {
+func decodeBatchLimit(raw jsontext.Value, n *int, expr *string) error {
if len(raw) == 0 {
return nil
}
if raw[0] == '"' {
- return json.Unmarshal(raw, expr)
+ return json.Unmarshal(raw, expr, jsonx.V1())
}
- return json.Unmarshal(raw, n)
+ return json.Unmarshal(raw, n, jsonx.V1())
}
// UnmarshalJSON accepts a JSONata string for either batch limit.
func (b *ItemBatcher) UnmarshalJSON(data []byte) error {
var w itemBatcherWire
- if err := json.Unmarshal(data, &w); err != nil {
+ if err := json.Unmarshal(data, &w, jsonx.V1()); err != nil {
return err
}
@@ -134,15 +137,15 @@ func (b ItemBatcher) MarshalJSON() ([]byte, error) {
return nil, err
}
- return json.Marshal(w)
+ return json.Marshal(w, jsonx.V1())
}
-func encodeBatchLimit(n int, expr string) (json.RawMessage, error) {
+func encodeBatchLimit(n int, expr string) (jsontext.Value, error) {
switch {
case expr != "":
- return json.Marshal(expr)
+ return json.Marshal(expr, jsonx.V1())
case n != 0:
- return json.Marshal(n)
+ return json.Marshal(n, jsonx.V1())
}
return nil, nil
@@ -150,10 +153,10 @@ func encodeBatchLimit(n int, expr string) (json.RawMessage, error) {
// ItemReader configures reading items from S3 for a Map state's Distributed Map.
type ItemReader struct {
- Parameters map[string]any `json:"Parameters,omitempty"`
- ReaderConfig *ReaderConfig `json:"ReaderConfig,omitempty"`
- Resource string `json:"Resource,omitempty"`
- Arguments json.RawMessage `json:"Arguments,omitempty"`
+ Parameters map[string]any `json:"Parameters,omitempty"`
+ ReaderConfig *ReaderConfig `json:"ReaderConfig,omitempty"`
+ Resource string `json:"Resource,omitempty"`
+ Arguments jsontext.Value `json:"Arguments,omitempty"`
}
// ReaderConfig describes how the ItemReader should interpret S3 object data.
@@ -196,7 +199,7 @@ type ResultWriter struct {
Parameters map[string]any `json:"Parameters,omitempty"`
WriterConfig *ResultWriterConfig `json:"WriterConfig,omitempty"`
Resource string `json:"Resource,omitempty"`
- Arguments json.RawMessage `json:"Arguments,omitempty"`
+ Arguments jsontext.Value `json:"Arguments,omitempty"`
}
// ResultWriterConfig is ResultWriter.WriterConfig: Transformation
@@ -215,22 +218,22 @@ type State struct {
jx *jxState
assignVals map[string]any
// numExprs holds JSONata strings for fields that are integers in JSONPath.
- Assign json.RawMessage `json:"Assign,omitempty"`
- Arguments json.RawMessage `json:"Arguments,omitempty"`
- Output json.RawMessage `json:"Output,omitempty"`
- Items json.RawMessage `json:"Items,omitempty"`
+ Assign jsontext.Value `json:"Assign,omitempty"`
+ Arguments jsontext.Value `json:"Arguments,omitempty"`
+ Output jsontext.Value `json:"Output,omitempty"`
+ Items jsontext.Value `json:"Items,omitempty"`
numExprs map[string]string
QueryLanguage string `json:"QueryLanguage,omitempty"`
lang string
- Iterator *StateMachine `json:"Iterator,omitempty"`
- ItemProcessor *StateMachine `json:"ItemProcessor,omitempty"`
- ItemBatcher *ItemBatcher `json:"ItemBatcher,omitempty"`
- ItemReader *ItemReader `json:"ItemReader,omitempty"`
- ResultWriter *ResultWriter `json:"ResultWriter,omitempty"`
- ItemSelector json.RawMessage `json:"ItemSelector,omitempty"`
- SecondsPath string `json:"SecondsPath,omitempty"`
- TimestampPath string `json:"TimestampPath,omitempty"`
- ItemsPath string `json:"ItemsPath,omitempty"`
+ Iterator *StateMachine `json:"Iterator,omitempty"`
+ ItemProcessor *StateMachine `json:"ItemProcessor,omitempty"`
+ ItemBatcher *ItemBatcher `json:"ItemBatcher,omitempty"`
+ ItemReader *ItemReader `json:"ItemReader,omitempty"`
+ ResultWriter *ResultWriter `json:"ResultWriter,omitempty"`
+ ItemSelector jsontext.Value `json:"ItemSelector,omitempty"`
+ SecondsPath string `json:"SecondsPath,omitempty"`
+ TimestampPath string `json:"TimestampPath,omitempty"`
+ ItemsPath string `json:"ItemsPath,omitempty"`
// ToleratedFailureCount/Percentage (and their *Path variants) bound how many
// Map iterations may fail before the Map state itself fails with
// States.ExceedToleratedFailureThreshold. AWS supports these only for
@@ -261,20 +264,20 @@ type State struct {
// reference-path siblings, mutually exclusive with them and resolved against
// the Task state's pre-Parameters input (AWS docs:
// amazon-states-language-task-state.html, GlueJobTask example).
- TimeoutSecondsPath string `json:"TimeoutSecondsPath,omitempty"`
- HeartbeatSecondsPath string `json:"HeartbeatSecondsPath,omitempty"`
- Retry []Retrier `json:"Retry,omitempty"`
- Catch []Catcher `json:"Catch,omitempty"`
- Choices []ChoiceRule `json:"Choices,omitempty"`
- Result json.RawMessage `json:"Result,omitempty"`
- Branches []Branch `json:"Branches,omitempty"`
- Parameters json.RawMessage `json:"Parameters,omitempty"`
- ResultSelector json.RawMessage `json:"ResultSelector,omitempty"`
- TimeoutSeconds int `json:"TimeoutSeconds,omitempty"`
- HeartbeatSeconds int `json:"HeartbeatSeconds,omitempty"`
- Seconds int `json:"Seconds,omitempty"`
- MaxConcurrency int `json:"MaxConcurrency,omitempty"`
- End bool `json:"End,omitempty"`
+ TimeoutSecondsPath string `json:"TimeoutSecondsPath,omitempty"`
+ HeartbeatSecondsPath string `json:"HeartbeatSecondsPath,omitempty"`
+ Retry []Retrier `json:"Retry,omitempty"`
+ Catch []Catcher `json:"Catch,omitempty"`
+ Choices []ChoiceRule `json:"Choices,omitempty"`
+ Result jsontext.Value `json:"Result,omitempty"`
+ Branches []Branch `json:"Branches,omitempty"`
+ Parameters jsontext.Value `json:"Parameters,omitempty"`
+ ResultSelector jsontext.Value `json:"ResultSelector,omitempty"`
+ TimeoutSeconds int `json:"TimeoutSeconds,omitempty"`
+ HeartbeatSeconds int `json:"HeartbeatSeconds,omitempty"`
+ Seconds int `json:"Seconds,omitempty"`
+ MaxConcurrency int `json:"MaxConcurrency,omitempty"`
+ End bool `json:"End,omitempty"`
}
// Retrier defines retry behavior for a Task state on error.
@@ -294,8 +297,8 @@ type Retrier struct {
// Catcher defines catch behavior for a Task state on error.
type Catcher struct {
- Assign json.RawMessage `json:"Assign,omitempty"`
- Output json.RawMessage `json:"Output,omitempty"`
+ Assign jsontext.Value `json:"Assign,omitempty"`
+ Output jsontext.Value `json:"Output,omitempty"`
assignVals map[string]any
outputVal any
Next string `json:"Next"`
@@ -314,8 +317,8 @@ type Branch struct {
// ChoiceRule represents a single condition/transition in a Choice state.
type ChoiceRule struct {
// Condition and Assign are the JSONata-mode rule fields.
- Condition string `json:"Condition,omitempty"`
- Assign json.RawMessage `json:"Assign,omitempty"`
+ Condition string `json:"Condition,omitempty"`
+ Assign jsontext.Value `json:"Assign,omitempty"`
assignVals map[string]any
// Numeric comparisons
NumericEquals *float64 `json:"NumericEquals,omitempty"`
@@ -386,7 +389,7 @@ func ParseIsolatedState(definition string) (*StateMachine, error) {
func parse(definition string, checkTransitions bool) (*StateMachine, error) {
var sm StateMachine
- if err := json.Unmarshal([]byte(definition), &sm); err != nil {
+ if err := json.Unmarshal([]byte(definition), &sm, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", ErrParseError, err)
}
@@ -610,7 +613,7 @@ func (s *State) UnmarshalJSON(b []byte) error {
}
}
- if err := json.Unmarshal(b, (*stateAlias)(s)); err != nil {
+ if err := json.Unmarshal(b, (*stateAlias)(s), jsonx.V1()); err != nil {
return err
}
@@ -622,8 +625,8 @@ func (s *State) UnmarshalJSON(b []byte) error {
// liftNumExprs removes string-valued integer fields from the state object and
// returns them separately; the object is returned untouched if there are none.
func liftNumExprs(b []byte) ([]byte, map[string]string, error) {
- var raw map[string]json.RawMessage
- if err := json.Unmarshal(b, &raw); err != nil {
+ var raw map[string]jsontext.Value
+ if err := json.Unmarshal(b, &raw, jsonx.V1()); err != nil {
return nil, nil, err
}
@@ -636,7 +639,7 @@ func liftNumExprs(b []byte) ([]byte, map[string]string, error) {
}
var str string
- if err := json.Unmarshal(v, &str); err != nil {
+ if err := json.Unmarshal(v, &str, jsonx.V1()); err != nil {
return nil, nil, err
}
@@ -652,7 +655,7 @@ func liftNumExprs(b []byte) ([]byte, map[string]string, error) {
return b, nil, nil
}
- out, err := json.Marshal(raw)
+ out, err := json.Marshal(raw, jsonx.V1())
return out, exprs, err
}
diff --git a/services/stepfunctions/asl/result_writer.go b/services/stepfunctions/asl/result_writer.go
index f2f101f8d6..06b51ab3d3 100644
--- a/services/stepfunctions/asl/result_writer.go
+++ b/services/stepfunctions/asl/result_writer.go
@@ -3,11 +3,12 @@ package asl
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -172,7 +173,7 @@ func (e *Executor) exportMapResults(
DestinationBucket: bucket,
MapRunArn: mapRunARN,
ResultFiles: files,
- })
+ }, jsonx.V1())
if err != nil {
return nil, 0, fmt.Errorf("ResultWriter: marshal manifest: %w", err)
}
@@ -267,7 +268,7 @@ func buildMapItemRecord(
}
func stringifyJSON(v any) string {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return aslNullLiteral
}
@@ -358,7 +359,7 @@ func encodeJSONLines(entries []any) ([]byte, error) {
buf.WriteByte('\n')
}
- b, err := json.Marshal(e)
+ b, err := json.Marshal(e, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("marshal JSONL entry: %w", err)
}
@@ -501,7 +502,7 @@ func (e *Executor) writeMapResultFile(
if outputType == outputTypeJSONL {
data, err = encodeJSONLines(entries)
} else {
- data, err = json.Marshal(entries)
+ data, err = json.Marshal(entries, jsonx.V1())
}
if err != nil {
diff --git a/services/stepfunctions/asl/sdk_integration.go b/services/stepfunctions/asl/sdk_integration.go
index 0f912a5203..8d2aeee2d9 100644
--- a/services/stepfunctions/asl/sdk_integration.go
+++ b/services/stepfunctions/asl/sdk_integration.go
@@ -3,9 +3,12 @@ package asl
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -150,7 +153,7 @@ func (e *Executor) invokeLambdaOptimized(ctx context.Context, input any) (any, e
var payload []byte
if raw, ok := params["Payload"]; ok {
- b, err := json.Marshal(raw)
+ b, err := json.Marshal(raw, jsonx.V1())
if err != nil {
return nil, &FailError{ErrCode: errCodeStatesRuntime, Cause: err.Error()}
}
@@ -176,7 +179,7 @@ func (e *Executor) invokeLambdaOptimized(ctx context.Context, input any) (any, e
if len(bytes.TrimSpace(resp)) > 0 {
var parsed any
- if json.Unmarshal(resp, &parsed) != nil {
+ if json.Unmarshal(resp, &parsed, jsonx.V1()) != nil {
parsed = string(resp)
}
@@ -222,11 +225,11 @@ func lambdaFunctionError(resp []byte) error {
}
var probe struct {
- ErrorMessage *json.RawMessage `json:"errorMessage"`
- ErrorType *string `json:"errorType"`
+ ErrorMessage *jsontext.Value `json:"errorMessage"`
+ ErrorType *string `json:"errorType"`
}
- if json.Unmarshal(trimmed, &probe) == nil && (probe.ErrorMessage != nil || probe.ErrorType != nil) {
+ if json.Unmarshal(trimmed, &probe, jsonx.V1()) == nil && (probe.ErrorMessage != nil || probe.ErrorType != nil) {
code := lambdaErrUnknown
if probe.ErrorType != nil && *probe.ErrorType != "" {
code = *probe.ErrorType
diff --git a/services/stepfunctions/asl/variables.go b/services/stepfunctions/asl/variables.go
index 28a52f7c4c..e8769890a9 100644
--- a/services/stepfunctions/asl/variables.go
+++ b/services/stepfunctions/asl/variables.go
@@ -1,12 +1,14 @@
package asl
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
"strings"
"unicode"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// maxVariableNameLen is the AWS limit ("Variable name syntax").
@@ -75,7 +77,7 @@ func (e *Executor) setVars(vals map[string]any) error {
assignTotal := 0
for name, v := range vals {
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return dataLimitError("variable %q cannot be serialized: %v", name, err)
}
diff --git a/services/stepfunctions/distributed_map.go b/services/stepfunctions/distributed_map.go
index 59ef3a566a..4480a4a310 100644
--- a/services/stepfunctions/distributed_map.go
+++ b/services/stepfunctions/distributed_map.go
@@ -2,10 +2,11 @@ package stepfunctions
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions/asl"
)
@@ -89,7 +90,7 @@ func marshalDistributedMapInput(item any) string {
return "{}"
}
- b, err := json.Marshal(item)
+ b, err := json.Marshal(item, jsonx.V1())
if err != nil {
return "{}"
}
diff --git a/services/stepfunctions/eks_integration.go b/services/stepfunctions/eks_integration.go
index c860f18033..47f846a676 100644
--- a/services/stepfunctions/eks_integration.go
+++ b/services/stepfunctions/eks_integration.go
@@ -6,7 +6,7 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -18,6 +18,7 @@ import (
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"github.com/aws/smithy-go"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions/asl"
)
@@ -105,7 +106,7 @@ func invokeModelOutput(out any) any {
}
var parsed any
- if json.Unmarshal(raw, &parsed) != nil {
+ if json.Unmarshal(raw, &parsed, jsonx.V1()) != nil {
parsed = string(raw)
}
@@ -183,7 +184,7 @@ func (s eksCallSpec) request(ctx context.Context, token string) (*http.Request,
var body io.Reader
if s.body != nil {
- b, err := json.Marshal(s.body)
+ b, err := json.Marshal(s.body, jsonx.V1())
if err != nil {
return nil, runtimeFailure("invalid RequestBody: %v", err)
}
@@ -279,7 +280,7 @@ func doEKSCall(client *http.Client, req *http.Request) (any, error) {
}
var parsed any
- if json.Unmarshal(data, &parsed) != nil {
+ if json.Unmarshal(data, &parsed, jsonx.V1()) != nil {
parsed = string(data)
}
@@ -287,7 +288,7 @@ func doEKSCall(client *http.Client, req *http.Request) (any, error) {
return map[string]any{"ResponseBody": parsed}, nil
}
- cause, _ := json.Marshal(parsed)
+ cause, _ := json.Marshal(parsed, jsonx.V1())
return nil, &asl.FailError{ErrCode: errCodeTaskFailed, Cause: string(cause)}
}
diff --git a/services/stepfunctions/error_path_sweep_test.go b/services/stepfunctions/error_path_sweep_test.go
index d1b1f3e5b2..0d888256c3 100644
--- a/services/stepfunctions/error_path_sweep_test.go
+++ b/services/stepfunctions/error_path_sweep_test.go
@@ -1,7 +1,7 @@
package stepfunctions_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions"
)
@@ -267,13 +268,13 @@ func Test_TagResource_TooManyTags_WireType(t *testing.T) {
body, err := json.Marshal(map[string]any{
"resourceArn": smARN,
"tags": tagList,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(t.Context(), t, h, e, "TagResource", string(body))
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "TooManyTags", resp["__type"])
}
@@ -313,7 +314,7 @@ func Test_CreateStateMachineAlias_ErrorCodes(t *testing.T) {
)
var pubResp map[string]any
- require.NoError(t, json.Unmarshal(pubRec.Body.Bytes(), &pubResp))
+ require.NoError(t, json.Unmarshal(pubRec.Body.Bytes(), &pubResp, jsonx.V1()))
versionARN, _ := pubResp["stateMachineVersionArn"].(string)
require.NotEmpty(t, versionARN)
@@ -338,7 +339,7 @@ func Test_CreateStateMachineAlias_ErrorCodes(t *testing.T) {
rec := sfnPost(t.Context(), t, h, e, "CreateStateMachineAlias", tc.body(h, e))
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tc.wantType, resp["__type"])
})
}
diff --git a/services/stepfunctions/execution_history.go b/services/stepfunctions/execution_history.go
index 99959dc8a8..88d576d4e9 100644
--- a/services/stepfunctions/execution_history.go
+++ b/services/stepfunctions/execution_history.go
@@ -1,11 +1,12 @@
package stepfunctions
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -307,7 +308,7 @@ func historyValueToJSON(v any) string {
return ""
}
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
if err != nil {
return ""
}
diff --git a/services/stepfunctions/execution_history_test.go b/services/stepfunctions/execution_history_test.go
index c910f7c59e..3d493da6e6 100644
--- a/services/stepfunctions/execution_history_test.go
+++ b/services/stepfunctions/execution_history_test.go
@@ -2,7 +2,7 @@ package stepfunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions"
)
@@ -252,7 +253,7 @@ func TestHandler_GetExecutionHistory(t *testing.T) {
if tt.wantEvents > 0 {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["events"].([]any), tt.wantEvents)
}
})
diff --git a/services/stepfunctions/executions.go b/services/stepfunctions/executions.go
index 941d98ee38..4f6fdb4878 100644
--- a/services/stepfunctions/executions.go
+++ b/services/stepfunctions/executions.go
@@ -2,7 +2,8 @@ package stepfunctions
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"slices"
@@ -13,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/apptrace"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions/asl"
)
@@ -272,7 +274,7 @@ func finalizeSyncExecutionResult(
return syncResult
}
- outputBytes, _ := json.Marshal(result.Output)
+ outputBytes, _ := json.Marshal(result.Output, jsonx.V1())
syncResult.Status = statusSucceeded
syncResult.Output = string(outputBytes)
@@ -674,7 +676,7 @@ func (b *InMemoryBackend) finalizeExecutionRecordLocked(
return
}
- outputBytes, _ := json.Marshal(result.Output)
+ outputBytes, _ := json.Marshal(result.Output, jsonx.V1())
exec.Status = statusSucceeded
exec.Output = string(outputBytes)
exec.OutputDetails = &CloudWatchEventsExecutionDataDetails{Included: true}
@@ -1142,7 +1144,7 @@ func validateExecutionInput(input string) error {
return fmt.Errorf("%w: input exceeds %d bytes", ErrInvalidExecutionInput, maxExecutionInputBytes)
}
- if input != "" && !json.Valid([]byte(input)) {
+ if input != "" && !jsontext.Value([]byte(input)).IsValid(jsonx.V1()) {
return fmt.Errorf("%w: input is not valid JSON", ErrInvalidExecutionInput)
}
diff --git a/services/stepfunctions/handler.go b/services/stepfunctions/handler.go
index 46a6e6b03b..22a28165d8 100644
--- a/services/stepfunctions/handler.go
+++ b/services/stepfunctions/handler.go
@@ -2,7 +2,7 @@ package stepfunctions
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -194,7 +195,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var data map[string]any
- if uerr := json.Unmarshal(body, &data); uerr != nil {
+ if uerr := json.Unmarshal(body, &data, jsonx.V1()); uerr != nil {
return ""
}
@@ -249,14 +250,14 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
case "CreateStateMachine":
resp, err := h.createStateMachineAction(ctx, body)
if err != nil {
return nil, err
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
case "ListStateMachines":
return h.listStateMachinesAction(ctx, body)
case "CreateActivity":
@@ -265,14 +266,14 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
case "ListActivities":
resp, err := h.handleListActivities(ctx, body)
if err != nil {
return nil, err
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
fn, ok := h.dispatchTable()[action]
@@ -285,7 +286,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(response)
+ return json.Marshal(response, jsonx.V1())
}
// handleError writes a standardized JSON error response.
@@ -311,7 +312,7 @@ func (h *Handler) handleError(
Message: errorMessage(reqErr),
}
- payload, _ := json.Marshal(errResp)
+ payload, _ := json.Marshal(errResp, jsonx.V1())
return c.JSONBlob(statusCode, payload)
}
@@ -458,7 +459,7 @@ const actionStartExecution = "StartExecution"
func (h *Handler) listStateMachinesAction(ctx context.Context, body []byte) ([]byte, error) {
var input listStateMachinesInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -472,5 +473,5 @@ func (h *Handler) listStateMachinesAction(ctx context.Context, body []byte) ([]b
items[i] = newStateMachineListItem(&sms[i])
}
- return json.Marshal(&listStateMachinesOutput{StateMachines: items, NextToken: next})
+ return json.Marshal(&listStateMachinesOutput{StateMachines: items, NextToken: next}, jsonx.V1())
}
diff --git a/services/stepfunctions/handler_activities.go b/services/stepfunctions/handler_activities.go
index cf92f3419b..427f10cfa1 100644
--- a/services/stepfunctions/handler_activities.go
+++ b/services/stepfunctions/handler_activities.go
@@ -2,7 +2,9 @@ package stepfunctions
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createActivityInput struct {
@@ -100,7 +102,7 @@ func (h *Handler) activityActions() map[string]actionFn {
func (h *Handler) handleCreateActivity(ctx context.Context, b []byte) (any, error) {
var input createActivityInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -131,7 +133,7 @@ func (h *Handler) handleCreateActivity(ctx context.Context, b []byte) (any, erro
func (h *Handler) handleDeleteActivity(b []byte) (any, error) {
var input deleteActivityInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -154,7 +156,7 @@ func (h *Handler) handleDeleteActivity(b []byte) (any, error) {
func (h *Handler) handleDescribeActivity(b []byte) (any, error) {
var input describeActivityInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -163,7 +165,7 @@ func (h *Handler) handleDescribeActivity(b []byte) (any, error) {
func (h *Handler) handleListActivities(ctx context.Context, b []byte) (any, error) {
var input listActivitiesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -182,7 +184,7 @@ func (h *Handler) handleListActivities(ctx context.Context, b []byte) (any, erro
func (h *Handler) handleSendTaskSuccess(b []byte) (any, error) {
var input sendTaskSuccessInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -195,7 +197,7 @@ func (h *Handler) handleSendTaskSuccess(b []byte) (any, error) {
func (h *Handler) handleSendTaskFailure(b []byte) (any, error) {
var input sendTaskFailureInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -208,7 +210,7 @@ func (h *Handler) handleSendTaskFailure(b []byte) (any, error) {
func (h *Handler) handleSendTaskHeartbeat(b []byte) (any, error) {
var input sendTaskHeartbeatInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -222,7 +224,7 @@ func (h *Handler) handleSendTaskHeartbeat(b []byte) (any, error) {
// handleGetActivityTask handles GetActivityTask with context support for long-polling.
func (h *Handler) handleGetActivityTask(ctx context.Context, body []byte) ([]byte, error) {
var input getActivityTaskInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -232,11 +234,11 @@ func (h *Handler) handleGetActivityTask(ctx context.Context, body []byte) ([]byt
}
if task == nil {
- return json.Marshal(&getActivityTaskOutput{})
+ return json.Marshal(&getActivityTaskOutput{}, jsonx.V1())
}
return json.Marshal(&getActivityTaskOutput{
TaskToken: task.TaskToken,
Input: task.Input,
- })
+ }, jsonx.V1())
}
diff --git a/services/stepfunctions/handler_activities_test.go b/services/stepfunctions/handler_activities_test.go
index 167ce463b6..ea681a9e5e 100644
--- a/services/stepfunctions/handler_activities_test.go
+++ b/services/stepfunctions/handler_activities_test.go
@@ -2,7 +2,7 @@ package stepfunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions"
)
@@ -104,7 +105,7 @@ func TestHandler_ActivityOperations(t *testing.T) {
rec := sfnPost(ctx, t, h, e, "CreateActivity", `{"name":"setup-act-`+tt.name+`"}`)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
actARN = resp["activityArn"].(string)
}
case "CreateActivity":
@@ -162,7 +163,7 @@ func TestHandler_CreateAndListActivities(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
activities, ok := resp["activities"].([]any)
require.True(t, ok)
@@ -249,7 +250,7 @@ func TestHandler_SendTaskSuccess_WithRealToken(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var actResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &actResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &actResp, jsonx.V1()))
actARN := actResp["activityArn"].(string)
// Enqueue a task by calling InvokeActivity from the backend.
@@ -276,7 +277,7 @@ func TestHandler_SendTaskSuccess_WithRealToken(t *testing.T) {
body, _ := json.Marshal(map[string]string{
"taskToken": task.TaskToken,
"output": tt.output,
- })
+ }, jsonx.V1())
rec = sfnPost(ctx, t, h, e, "SendTaskSuccess", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
@@ -315,19 +316,19 @@ func TestHandler_GetActivityTask_HTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var actResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &actResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &actResp, jsonx.V1()))
actARN := actResp["activityArn"].(string)
// GetActivityTask with a short timeout — should return empty.
pollCtx, cancel := context.WithTimeout(ctx, tt.timeout)
defer cancel()
- body, _ := json.Marshal(map[string]string{"activityArn": actARN, "workerName": "w1"})
+ body, _ := json.Marshal(map[string]string{"activityArn": actARN, "workerName": "w1"}, jsonx.V1())
rec = sfnPost(pollCtx, t, h, e, "GetActivityTask", string(body))
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// With no task available, taskToken should be absent or empty.
token, _ := resp["taskToken"].(string)
assert.Empty(t, token)
@@ -402,20 +403,20 @@ func TestHandler_StartSyncExecution_HTTP(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var smResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &smResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &smResp, jsonx.V1()))
smARN := smResp["stateMachineArn"].(string)
body, _ := json.Marshal(map[string]string{
"stateMachineArn": smARN,
"name": "sync-exec-" + tt.name,
"input": `{}`,
- })
+ }, jsonx.V1())
rec = sfnPost(ctx, t, h, e, "StartSyncExecution", string(body))
assert.Equal(t, tt.wantCode, rec.Code)
if tt.wantStatus != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantStatus, resp["status"])
}
})
@@ -466,7 +467,7 @@ func TestHandler_Reset(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["tags"])
})
}
@@ -718,7 +719,7 @@ func TestActivityName_Validation(t *testing.T) {
ctx := t.Context()
h, e := newSFNHandler(t)
- body, err := json.Marshal(map[string]string{"name": tt.actName})
+ body, err := json.Marshal(map[string]string{"name": tt.actName}, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "CreateActivity", string(body))
@@ -1429,23 +1430,23 @@ func TestCreateActivity_EncryptionConfiguration(t *testing.T) {
"kmsKeyId": "arn:aws:kms:us-east-1:123456789012:key/test-key",
"kmsDataKeyReusePeriodSeconds": 300,
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "CreateActivity", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
actARN, _ := createResp["activityArn"].(string)
require.NotEmpty(t, actARN)
- descBody, _ := json.Marshal(map[string]string{"activityArn": actARN})
+ descBody, _ := json.Marshal(map[string]string{"activityArn": actARN}, jsonx.V1())
descRec := sfnPost(ctx, t, h, e, "DescribeActivity", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
encCfg, ok := descResp["encryptionConfiguration"].(map[string]any)
require.True(t, ok, "expected encryptionConfiguration echoed back on DescribeActivity")
assert.Equal(t, "CUSTOMER_MANAGED_KMS_KEY", encCfg["type"])
@@ -1465,23 +1466,23 @@ func TestCreateActivity_InlineTags(t *testing.T) {
"tags": []map[string]string{
{"key": "env", "value": "prod"},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "CreateActivity", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
actARN, _ := createResp["activityArn"].(string)
require.NotEmpty(t, actARN)
- tagsBody, _ := json.Marshal(map[string]string{"resourceArn": actARN})
+ tagsBody, _ := json.Marshal(map[string]string{"resourceArn": actARN}, jsonx.V1())
tagsRec := sfnPost(ctx, t, h, e, "ListTagsForResource", string(tagsBody))
require.Equal(t, http.StatusOK, tagsRec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp, jsonx.V1()))
tagList, _ := tagsResp["tags"].([]any)
require.Len(t, tagList, 1)
@@ -1499,32 +1500,32 @@ func TestDeleteActivity_ClearsTags(t *testing.T) {
ctx := t.Context()
h, e := newSFNHandler(t)
- createBody, _ := json.Marshal(map[string]any{"name": "del-tagged-activity"})
+ createBody, _ := json.Marshal(map[string]any{"name": "del-tagged-activity"}, jsonx.V1())
rec := sfnPost(ctx, t, h, e, "CreateActivity", string(createBody))
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
actARN, _ := createResp["activityArn"].(string)
require.NotEmpty(t, actARN)
tagBody, _ := json.Marshal(map[string]any{
"resourceArn": actARN,
"tags": []map[string]string{{"key": "k", "value": "v"}},
- })
+ }, jsonx.V1())
tagRec := sfnPost(ctx, t, h, e, "TagResource", string(tagBody))
require.Equal(t, http.StatusOK, tagRec.Code)
- delBody, _ := json.Marshal(map[string]string{"activityArn": actARN})
+ delBody, _ := json.Marshal(map[string]string{"activityArn": actARN}, jsonx.V1())
delRec := sfnPost(ctx, t, h, e, "DeleteActivity", string(delBody))
require.Equal(t, http.StatusOK, delRec.Code)
- tagsBody, _ := json.Marshal(map[string]string{"resourceArn": actARN})
+ tagsBody, _ := json.Marshal(map[string]string{"resourceArn": actARN}, jsonx.V1())
tagsRec := sfnPost(ctx, t, h, e, "ListTagsForResource", string(tagsBody))
require.Equal(t, http.StatusOK, tagsRec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(tagsRec.Body.Bytes(), &tagsResp, jsonx.V1()))
tagList, _ := tagsResp["tags"].([]any)
assert.Empty(t, tagList, "tags must be cleared after DeleteActivity")
}
diff --git a/services/stepfunctions/handler_aliases.go b/services/stepfunctions/handler_aliases.go
index 084fee5320..85cbeb3dce 100644
--- a/services/stepfunctions/handler_aliases.go
+++ b/services/stepfunctions/handler_aliases.go
@@ -1,7 +1,9 @@
package stepfunctions
import (
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// createStateMachineAliasInput has no StateMachineArn member: the real
@@ -95,7 +97,7 @@ func (h *Handler) aliasActions() map[string]actionFn {
func (h *Handler) handleCreateStateMachineAlias(b []byte) (any, error) {
var input createStateMachineAliasInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -111,7 +113,7 @@ func (h *Handler) handleCreateStateMachineAlias(b []byte) (any, error) {
func (h *Handler) handleUpdateStateMachineAlias(b []byte) (any, error) {
var input updateStateMachineAliasInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -127,7 +129,7 @@ func (h *Handler) handleUpdateStateMachineAlias(b []byte) (any, error) {
func (h *Handler) handleDeleteStateMachineAlias(b []byte) (any, error) {
var input deleteStateMachineAliasInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -140,7 +142,7 @@ func (h *Handler) handleDeleteStateMachineAlias(b []byte) (any, error) {
func (h *Handler) handleDescribeStateMachineAlias(b []byte) (any, error) {
var input describeStateMachineAliasInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -154,7 +156,7 @@ func (h *Handler) handleDescribeStateMachineAlias(b []byte) (any, error) {
func (h *Handler) handleListStateMachineAliases(b []byte) (any, error) {
var input listStateMachineAliasesInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/stepfunctions/handler_executions.go b/services/stepfunctions/handler_executions.go
index 3b94fbd13f..f764031652 100644
--- a/services/stepfunctions/handler_executions.go
+++ b/services/stepfunctions/handler_executions.go
@@ -2,8 +2,10 @@ package stepfunctions
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type startExecutionInput struct {
@@ -150,7 +152,7 @@ func (h *Handler) executionActions() map[string]actionFn {
func (h *Handler) handleRedriveExecution(b []byte) (any, error) {
var input redriveExecutionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -164,7 +166,7 @@ func (h *Handler) handleRedriveExecution(b []byte) (any, error) {
func (h *Handler) handleDescribeStateMachineForExecution(b []byte) (any, error) {
var input describeStateMachineForExecutionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -189,7 +191,7 @@ func (h *Handler) handleStartExecution(b []byte) (any, error) {
func (h *Handler) handleStartExecutionCtx(ctx context.Context, b []byte) (any, error) {
var input startExecutionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -209,7 +211,7 @@ func (h *Handler) handleStartExecutionCtx(ctx context.Context, b []byte) (any, e
func (h *Handler) handleStartSyncExecution(b []byte) (any, error) {
var input startSyncExecutionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -218,7 +220,7 @@ func (h *Handler) handleStartSyncExecution(b []byte) (any, error) {
func (h *Handler) handleStopExecution(b []byte) (any, error) {
var input stopExecutionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -236,7 +238,7 @@ func (h *Handler) handleStopExecution(b []byte) (any, error) {
func (h *Handler) handleDescribeExecution(b []byte) (any, error) {
var input describeExecutionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -264,7 +266,7 @@ func validateRedriveFilter(filter string, hasMapRun bool) error {
func (h *Handler) handleListExecutions(b []byte) (any, error) {
var input listExecutionsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -313,7 +315,7 @@ func (h *Handler) handleListExecutions(b []byte) (any, error) {
func (h *Handler) handleGetExecutionHistory(b []byte) (any, error) {
var input getExecutionHistoryInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/stepfunctions/handler_executions_test.go b/services/stepfunctions/handler_executions_test.go
index c5b46a5248..2a204579d3 100644
--- a/services/stepfunctions/handler_executions_test.go
+++ b/services/stepfunctions/handler_executions_test.go
@@ -2,7 +2,7 @@ package stepfunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions"
)
@@ -42,7 +43,7 @@ func TestHandler_StartExecution(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["executionArn"].(string), "exec1")
},
},
@@ -239,7 +240,7 @@ func TestHandler_ListExecutions(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["executions"].([]any), tt.wantCount)
})
}
@@ -292,7 +293,7 @@ func TestExecutionName_Validation(t *testing.T) {
"stateMachineArn": smARN,
"name": tt.execName,
"input": "{}",
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "StartExecution", string(body))
@@ -312,14 +313,14 @@ func TestStartExecution_ResponseContainsARNAndStartDate(t *testing.T) {
"stateMachineArn": smArn,
"name": "start-resp-exec",
"input": "{}",
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "StartExecution", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["executionArn"])
assert.Greater(t, out["startDate"].(float64), float64(0))
@@ -339,7 +340,7 @@ func TestListExecutions_OrderedByStartDateDesc_ViaHandler(t *testing.T) {
"stateMachineArn": smARN,
"name": name,
"input": "{}",
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "StartExecution", string(body))
@@ -347,14 +348,14 @@ func TestListExecutions_OrderedByStartDateDesc_ViaHandler(t *testing.T) {
time.Sleep(5 * time.Millisecond)
}
- listBody, err := json.Marshal(map[string]string{"stateMachineArn": smARN})
+ listBody, err := json.Marshal(map[string]string{"stateMachineArn": smARN}, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "ListExecutions", string(listBody))
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rawExecs, _ := resp["executions"].([]any)
require.Len(t, rawExecs, 3)
@@ -381,7 +382,7 @@ func TestSFN_DescribeStateMachineForExecution(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
execARN, _ := resp["executionArn"].(string)
require.NotEmpty(t, execARN)
diff --git a/services/stepfunctions/handler_map_runs.go b/services/stepfunctions/handler_map_runs.go
index 9c023f4061..1e3934901d 100644
--- a/services/stepfunctions/handler_map_runs.go
+++ b/services/stepfunctions/handler_map_runs.go
@@ -1,7 +1,9 @@
package stepfunctions
import (
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type describeMapRunInput struct {
@@ -53,7 +55,7 @@ func (h *Handler) mapRunActions() map[string]actionFn {
return map[string]actionFn{
"DescribeMapRun": func(b []byte) (any, error) {
var input describeMapRunInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -61,7 +63,7 @@ func (h *Handler) mapRunActions() map[string]actionFn {
},
"ListMapRuns": func(b []byte) (any, error) {
var input listMapRunsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -86,7 +88,7 @@ func (h *Handler) mapRunActions() map[string]actionFn {
},
"UpdateMapRun": func(b []byte) (any, error) {
var input updateMapRunInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/stepfunctions/handler_state_machine_versions.go b/services/stepfunctions/handler_state_machine_versions.go
index 0a7e613ed2..e3b7a5c48a 100644
--- a/services/stepfunctions/handler_state_machine_versions.go
+++ b/services/stepfunctions/handler_state_machine_versions.go
@@ -1,7 +1,9 @@
package stepfunctions
import (
- "encoding/json"
+ "encoding/json/v2"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type publishStateMachineVersionInput struct {
@@ -47,7 +49,7 @@ func (h *Handler) versionActions() map[string]actionFn {
return map[string]actionFn{
"PublishStateMachineVersion": func(b []byte) (any, error) {
var input publishStateMachineVersionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -59,7 +61,7 @@ func (h *Handler) versionActions() map[string]actionFn {
},
"DeleteStateMachineVersion": func(b []byte) (any, error) {
var input deleteStateMachineVersionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.DeleteStateMachineVersion(input.StateMachineVersionArn); err != nil {
@@ -70,7 +72,7 @@ func (h *Handler) versionActions() map[string]actionFn {
},
"ListStateMachineVersions": func(b []byte) (any, error) {
var input listStateMachineVersionsInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/stepfunctions/handler_state_machines.go b/services/stepfunctions/handler_state_machines.go
index 2de5167950..0846429d8f 100644
--- a/services/stepfunctions/handler_state_machines.go
+++ b/services/stepfunctions/handler_state_machines.go
@@ -2,9 +2,11 @@ package stepfunctions
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type createStateMachineInput struct {
@@ -89,7 +91,7 @@ type updateStateMachineOutput struct {
// configuration and inline tags when supplied in the request body.
func (h *Handler) createStateMachineAction(ctx context.Context, b []byte) (any, error) {
var input createStateMachineInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -153,7 +155,7 @@ func (h *Handler) createStateMachineAction(ctx context.Context, b []byte) (any,
// configuration when supplied in the request body.
func (h *Handler) updateStateMachineAction(b []byte) (any, error) {
var input updateStateMachineInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -211,7 +213,7 @@ func (h *Handler) stateMachineActions() map[string]actionFn {
// CreateStateMachine and ListStateMachines are handled in dispatch (ctx-aware).
"DeleteStateMachine": func(b []byte) (any, error) {
var input deleteStateMachineInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
if err := h.Backend.DeleteStateMachine(input.StateMachineArn); err != nil {
@@ -230,7 +232,7 @@ func (h *Handler) stateMachineActions() map[string]actionFn {
},
"DescribeStateMachine": func(b []byte) (any, error) {
var input describeStateMachineInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/services/stepfunctions/handler_state_machines_test.go b/services/stepfunctions/handler_state_machines_test.go
index 59b132e987..961c742a0c 100644
--- a/services/stepfunctions/handler_state_machines_test.go
+++ b/services/stepfunctions/handler_state_machines_test.go
@@ -2,7 +2,7 @@ package stepfunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions"
)
@@ -33,7 +34,7 @@ func TestHandler_CreateStateMachine(t *testing.T) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["stateMachineArn"].(string), "test-sm")
},
},
@@ -149,7 +150,7 @@ func TestHandler_StartExecution_StateMachineDeleting(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
smArn, ok := createResp["stateMachineArn"].(string)
require.True(t, ok)
@@ -165,7 +166,7 @@ func TestHandler_StartExecution_StateMachineDeleting(t *testing.T) {
require.Equal(t, http.StatusOK, runningRec.Code)
var runningDesc map[string]any
- require.NoError(t, json.Unmarshal(runningRec.Body.Bytes(), &runningDesc))
+ require.NoError(t, json.Unmarshal(runningRec.Body.Bytes(), &runningDesc, jsonx.V1()))
require.Equal(t, "RUNNING", runningDesc["status"])
delRec := sfnPost(ctx, t, h, e, "DeleteStateMachine", `{"stateMachineArn":"`+smArn+`"}`)
@@ -175,7 +176,7 @@ func TestHandler_StartExecution_StateMachineDeleting(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code, "DELETING state machine must still be describable")
var desc map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &desc, jsonx.V1()))
assert.Equal(t, "DELETING", desc["status"])
startRec := sfnPost(ctx, t, h, e, "StartExecution",
@@ -183,7 +184,7 @@ func TestHandler_StartExecution_StateMachineDeleting(t *testing.T) {
assert.Equal(t, http.StatusConflict, startRec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "StateMachineDeleting", errResp["__type"])
}
@@ -219,7 +220,7 @@ func TestHandler_ListStateMachines(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Len(t, resp["stateMachines"].([]any), tt.wantCount)
})
}
@@ -245,7 +246,7 @@ func TestHandler_DescribeStateMachine(t *testing.T) {
makeSMBody("desc-sm", validPassDef, "EXPRESS"))
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["stateMachineArn"].(string)
},
@@ -255,7 +256,7 @@ func TestHandler_DescribeStateMachine(t *testing.T) {
t.Helper()
var sm map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &sm))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &sm, jsonx.V1()))
assert.Equal(t, "EXPRESS", sm["type"])
},
},
@@ -344,7 +345,7 @@ func TestStateMachineName_Validation(t *testing.T) {
"name": tt.smName,
"definition": sfnPassDefinition,
"roleArn": "arn:aws:iam::123456789012:role/role",
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "CreateStateMachine", string(body))
@@ -352,7 +353,7 @@ func TestStateMachineName_Validation(t *testing.T) {
if tt.wantCode == http.StatusBadRequest {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidName", resp["__type"])
}
})
@@ -405,24 +406,24 @@ func TestCreateStateMachine_InlineTags(t *testing.T) {
reqBody["tags"] = tt.tags
}
- body, err := json.Marshal(reqBody)
+ body, err := json.Marshal(reqBody, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "CreateStateMachine", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
smARN, _ := createResp["stateMachineArn"].(string)
require.NotEmpty(t, smARN)
// Verify tags were applied.
- listBody, _ := json.Marshal(map[string]string{"resourceArn": smARN})
+ listBody, _ := json.Marshal(map[string]string{"resourceArn": smARN}, jsonx.V1())
listRec := sfnPost(ctx, t, h, e, "ListTagsForResource", string(listBody))
require.Equal(t, http.StatusOK, listRec.Code)
var tagsResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsResp, jsonx.V1()))
rawTags, _ := tagsResp["tags"].([]any)
got := make(map[string]string, len(rawTags))
@@ -485,24 +486,24 @@ func TestCreateStateMachine_EncryptionConfiguration(t *testing.T) {
reqBody["encryptionConfiguration"] = tt.encCfg
}
- body, err := json.Marshal(reqBody)
+ body, err := json.Marshal(reqBody, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "CreateStateMachine", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
smARN, _ := createResp["stateMachineArn"].(string)
require.NotEmpty(t, smARN)
// Describe to verify encryption config is stored.
- descBody, _ := json.Marshal(map[string]string{"stateMachineArn": smARN})
+ descBody, _ := json.Marshal(map[string]string{"stateMachineArn": smARN}, jsonx.V1())
descRec := sfnPost(ctx, t, h, e, "DescribeStateMachine", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
if tt.wantType == "" {
// No encryption config present in response or nil.
@@ -531,19 +532,19 @@ func TestUpdateStateMachine_EncryptionConfiguration(t *testing.T) {
"type": "CUSTOMER_MANAGED_KMS_KEY",
"kmsKeyId": "arn:aws:kms:us-east-1:123456789012:key/my-key",
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "UpdateStateMachine", string(body))
assert.Equal(t, http.StatusOK, rec.Code)
// Verify via describe.
- descBody, _ := json.Marshal(map[string]string{"stateMachineArn": smARN})
+ descBody, _ := json.Marshal(map[string]string{"stateMachineArn": smARN}, jsonx.V1())
descRec := sfnPost(ctx, t, h, e, "DescribeStateMachine", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
enc, ok := descResp["encryptionConfiguration"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "CUSTOMER_MANAGED_KMS_KEY", enc["type"])
@@ -583,14 +584,14 @@ func TestCreateStateMachine_WithPublish(t *testing.T) {
"definition": sfnPassDefinition,
"roleArn": "arn:aws:iam::123456789012:role/role",
"publish": tt.publish,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "CreateStateMachine", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
smARN, _ := createResp["stateMachineArn"].(string)
require.NotEmpty(t, smARN)
@@ -606,12 +607,12 @@ func TestCreateStateMachine_WithPublish(t *testing.T) {
}
// Check versions.
- versBody, _ := json.Marshal(map[string]string{"stateMachineArn": smARN})
+ versBody, _ := json.Marshal(map[string]string{"stateMachineArn": smARN}, jsonx.V1())
versRec := sfnPost(ctx, t, h, e, "ListStateMachineVersions", string(versBody))
require.Equal(t, http.StatusOK, versRec.Code)
var versResp map[string]any
- require.NoError(t, json.Unmarshal(versRec.Body.Bytes(), &versResp))
+ require.NoError(t, json.Unmarshal(versRec.Body.Bytes(), &versResp, jsonx.V1()))
versions, _ := versResp["stateMachineVersions"].([]any)
if tt.wantVersion {
@@ -636,23 +637,23 @@ func TestUpdateStateMachine_WithPublish(t *testing.T) {
"stateMachineArn": smARN,
"definition": newDef,
"publish": true,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "UpdateStateMachine", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.NotEmpty(t, updateResp["revisionId"], "expected a non-empty revisionId after update")
assert.NotEmpty(t, updateResp["stateMachineVersionArn"], "expected stateMachineVersionArn when publish=true")
- versBody, _ := json.Marshal(map[string]string{"stateMachineArn": smARN})
+ versBody, _ := json.Marshal(map[string]string{"stateMachineArn": smARN}, jsonx.V1())
versRec := sfnPost(ctx, t, h, e, "ListStateMachineVersions", string(versBody))
require.Equal(t, http.StatusOK, versRec.Code)
var versResp map[string]any
- require.NoError(t, json.Unmarshal(versRec.Body.Bytes(), &versResp))
+ require.NoError(t, json.Unmarshal(versRec.Body.Bytes(), &versResp, jsonx.V1()))
versions, _ := versResp["stateMachineVersions"].([]any)
assert.Len(t, versions, 1, "expected one version after update with publish=true")
}
@@ -671,14 +672,14 @@ func TestUpdateStateMachine_NoPublish_NoVersionArn(t *testing.T) {
body, err := json.Marshal(map[string]any{
"stateMachineArn": smARN,
"definition": newDef,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "UpdateStateMachine", string(body))
require.Equal(t, http.StatusOK, rec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.NotEmpty(t, updateResp["revisionId"])
_, hasVersionArn := updateResp["stateMachineVersionArn"]
assert.False(t, hasVersionArn, "expected no stateMachineVersionArn when publish=false")
@@ -699,7 +700,7 @@ func TestUpdateStateMachine_VersionDescriptionRequiresPublish(t *testing.T) {
"definition": `{"StartAt":"S","States":{"S":{"Type":"Succeed"}}}`,
"versionDescription": "should be rejected",
"publish": false,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "UpdateStateMachine", string(body))
@@ -747,7 +748,7 @@ func TestCreateStateMachine_ResponseContainsARNAndDate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.NotEmpty(t, out["stateMachineArn"])
assert.Greater(t, out["creationDate"].(float64), float64(0))
@@ -765,18 +766,18 @@ func TestCreateStateMachine_Idempotent_ViaHandler(t *testing.T) {
"name": "idem-handler-sm",
"definition": sfnPassDefinition,
"roleArn": "arn:aws:iam::123456789012:role/role",
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec1 := sfnPost(ctx, t, h, e, "CreateStateMachine", string(body))
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
rec2 := sfnPost(ctx, t, h, e, "CreateStateMachine", string(body))
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Equal(t, resp1["stateMachineArn"], resp2["stateMachineArn"])
}
@@ -831,7 +832,7 @@ func TestCreateStateMachineRequiresRoleArn(t *testing.T) {
h, e := newSFNHandler(t)
ctx := context.Background()
- body, err := json.Marshal(tt.body)
+ body, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "CreateStateMachine", string(body))
@@ -840,7 +841,7 @@ func TestCreateStateMachineRequiresRoleArn(t *testing.T) {
if tt.wantType != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"],
"error type for case %q", tt.name)
}
@@ -880,7 +881,7 @@ func TestCreateStateMachine_NameValidation(t *testing.T) {
"name": tt.smName,
"definition": validPassDef,
"roleArn": "arn:aws:iam::123456789012:role/role",
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "CreateStateMachine", string(body))
@@ -888,7 +889,7 @@ func TestCreateStateMachine_NameValidation(t *testing.T) {
if tt.wantType != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
}
})
@@ -930,14 +931,14 @@ func TestUpdateStateMachine_ArnValidation(t *testing.T) {
body, err := json.Marshal(map[string]any{
"stateMachineArn": tt.arn,
"definition": validPassDef,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "UpdateStateMachine", string(body))
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
})
}
@@ -1025,14 +1026,14 @@ func TestHandler_UpdateStateMachine(t *testing.T) {
body, err = json.Marshal(map[string]any{
"stateMachineArn": smARN,
"roleArn": "arn:aws:iam::123456789012:role/new-role",
- })
+ }, jsonx.V1())
require.NoError(t, err)
} else {
var err error
body, err = json.Marshal(map[string]any{
"stateMachineArn": smARN,
"definition": sfnPassDefinition,
- })
+ }, jsonx.V1())
require.NoError(t, err)
}
@@ -1040,7 +1041,7 @@ func TestHandler_UpdateStateMachine(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "updateDate")
})
}
diff --git a/services/stepfunctions/handler_tags.go b/services/stepfunctions/handler_tags.go
index 0efef17690..0b9d36c2e9 100644
--- a/services/stepfunctions/handler_tags.go
+++ b/services/stepfunctions/handler_tags.go
@@ -1,8 +1,10 @@
package stepfunctions
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type sfnListTagsForResourceInput struct {
@@ -82,7 +84,7 @@ func (h *Handler) stateMachineTagActions() map[string]actionFn {
return map[string]actionFn{
"ListTagsForResource": func(b []byte) (any, error) {
var input sfnListTagsForResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -96,7 +98,7 @@ func (h *Handler) stateMachineTagActions() map[string]actionFn {
},
"TagResource": func(b []byte) (any, error) {
var input sfnTagResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -116,7 +118,7 @@ func (h *Handler) stateMachineTagActions() map[string]actionFn {
},
"UntagResource": func(b []byte) (any, error) {
var input sfnUntagResourceInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
h.removeTags(input.ResourceArn, input.TagKeys)
diff --git a/services/stepfunctions/handler_test.go b/services/stepfunctions/handler_test.go
index 07817e1896..e8ca9fa534 100644
--- a/services/stepfunctions/handler_test.go
+++ b/services/stepfunctions/handler_test.go
@@ -2,7 +2,7 @@ package stepfunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/stepfunctions"
)
@@ -67,7 +68,7 @@ func makeSMBody(name, def, smType string) string {
input["type"] = smType
}
- b, err := json.Marshal(input)
+ b, err := json.Marshal(input, jsonx.V1())
if err != nil {
panic(err)
}
@@ -83,7 +84,7 @@ func createSM(ctx context.Context, t *testing.T, h *stepfunctions.Handler, e *ec
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["stateMachineArn"].(string)
}
@@ -102,7 +103,7 @@ func startExec(
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["executionArn"].(string)
}
@@ -148,7 +149,7 @@ func TestHandler_Routing(t *testing.T) {
t.Helper()
var ops []string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops, jsonx.V1()))
assert.Contains(t, ops, "CreateStateMachine")
},
},
@@ -252,14 +253,14 @@ func TestHandler_Shutdown_CancelsRunningExecutions(t *testing.T) {
var smResp struct {
StateMachineArn string `json:"stateMachineArn"`
}
- require.NoError(t, json.Unmarshal(smRec.Body.Bytes(), &smResp))
+ require.NoError(t, json.Unmarshal(smRec.Body.Bytes(), &smResp, jsonx.V1()))
// Start an execution.
execBody, err := json.Marshal(map[string]string{
"stateMachineArn": smResp.StateMachineArn,
"name": "exec-1",
"input": "{}",
- })
+ }, jsonx.V1())
require.NoError(t, err)
execRec := sfnPost(ctx, t, h, e, "StartExecution", string(execBody))
@@ -268,7 +269,7 @@ func TestHandler_Shutdown_CancelsRunningExecutions(t *testing.T) {
var execResp struct {
ExecutionArn string `json:"executionArn"`
}
- require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp))
+ require.NoError(t, json.Unmarshal(execRec.Body.Bytes(), &execResp, jsonx.V1()))
// Shutdown should cancel the long-running Wait execution and return promptly.
h.Shutdown(t.Context())
@@ -276,7 +277,10 @@ func TestHandler_Shutdown_CancelsRunningExecutions(t *testing.T) {
// After Shutdown, the cancelled execution goroutine should eventually
// transition the execution out of RUNNING (to FAILED due to cancellation).
require.Eventuallyf(t, func() bool {
- descBody, marshalErr := json.Marshal(map[string]string{"executionArn": execResp.ExecutionArn})
+ descBody, marshalErr := json.Marshal(
+ map[string]string{"executionArn": execResp.ExecutionArn},
+ jsonx.V1(),
+ )
if marshalErr != nil {
return false
}
@@ -290,7 +294,8 @@ func TestHandler_Shutdown_CancelsRunningExecutions(t *testing.T) {
Status string `json:"status"`
}
- return json.Unmarshal(descRec.Body.Bytes(), &descResp) == nil && descResp.Status != "RUNNING"
+ return json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()) == nil &&
+ descResp.Status != "RUNNING"
}, 5*time.Second, 50*time.Millisecond,
"[%s] execution should leave RUNNING state after Shutdown", tt.name)
})
@@ -331,7 +336,7 @@ func TestClassifyError_InvalidExecutionInput(t *testing.T) {
body, err := json.Marshal(map[string]string{
"stateMachineArn": smARN,
"input": tt.input,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "StartExecution", string(body))
@@ -339,7 +344,7 @@ func TestClassifyError_InvalidExecutionInput(t *testing.T) {
if tt.wantErr {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidExecutionInput", resp["__type"])
} else {
assert.Equal(t, http.StatusOK, rec.Code)
@@ -384,7 +389,7 @@ func TestClassifyError_InvalidArn(t *testing.T) {
"name": "arn-check-sm-" + tt.name,
"definition": sfnPassDefinition,
"roleArn": tt.roleArn,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "CreateStateMachine", string(body))
@@ -392,7 +397,7 @@ func TestClassifyError_InvalidArn(t *testing.T) {
if tt.wantCode == http.StatusBadRequest {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidArn", resp["__type"])
}
})
@@ -438,7 +443,7 @@ func TestHTTP_StateMachineAlreadyExists_Returns409(t *testing.T) {
assert.Equal(t, http.StatusConflict, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "StateMachineAlreadyExists", out["__type"])
}
@@ -453,7 +458,7 @@ func TestHTTP_InvalidDefinition_Returns400(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "InvalidDefinition", out["__type"])
}
diff --git a/services/stepfunctions/handler_tracing_logging_test.go b/services/stepfunctions/handler_tracing_logging_test.go
index d95559eacc..b4e4328c26 100644
--- a/services/stepfunctions/handler_tracing_logging_test.go
+++ b/services/stepfunctions/handler_tracing_logging_test.go
@@ -1,12 +1,14 @@
package stepfunctions_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateStateMachine_PropagatesTracingAndLogging(t *testing.T) {
@@ -30,7 +32,7 @@ func TestHandler_CreateStateMachine_PropagatesTracingAndLogging(t *testing.T) {
},
}
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
ctx := t.Context()
@@ -40,19 +42,19 @@ func TestHandler_CreateStateMachine_PropagatesTracingAndLogging(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
arnStr, _ := createOut["stateMachineArn"].(string)
require.NotEmpty(t, arnStr)
- descBody, err := json.Marshal(map[string]any{"stateMachineArn": arnStr})
+ descBody, err := json.Marshal(map[string]any{"stateMachineArn": arnStr}, jsonx.V1())
require.NoError(t, err)
descRec := sfnPost(ctx, t, h, e, "DescribeStateMachine", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
tracing, _ := descOut["tracingConfiguration"].(map[string]any)
require.NotNil(t, tracing)
@@ -74,19 +76,19 @@ func TestHandler_UpdateStateMachine_PropagatesTracing(t *testing.T) {
updBody, err := json.Marshal(map[string]any{
"stateMachineArn": arn,
"tracingConfiguration": map[string]any{"enabled": true},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "UpdateStateMachine", string(updBody))
require.Equal(t, http.StatusOK, rec.Code)
- descBody, _ := json.Marshal(map[string]any{"stateMachineArn": arn})
+ descBody, _ := json.Marshal(map[string]any{"stateMachineArn": arn}, jsonx.V1())
descRec := sfnPost(ctx, t, h, e, "DescribeStateMachine", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
tracing, _ := descOut["tracingConfiguration"].(map[string]any)
require.NotNil(t, tracing)
diff --git a/services/stepfunctions/handler_util.go b/services/stepfunctions/handler_util.go
index 54705da4f1..143eb89ff3 100644
--- a/services/stepfunctions/handler_util.go
+++ b/services/stepfunctions/handler_util.go
@@ -1,10 +1,12 @@
package stepfunctions
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strings"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/ptrconv"
"github.com/blackbirdworks/gopherstack/services/stepfunctions/asl"
)
@@ -39,7 +41,7 @@ func (h *Handler) utilActions() map[string]actionFn {
return map[string]actionFn{
"ValidateStateMachineDefinition": func(b []byte) (any, error) {
var input validateStateMachineDefinitionInput
- if err := json.Unmarshal(b, &input); err != nil {
+ if err := json.Unmarshal(b, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -132,11 +134,11 @@ func testStateMockRun(m *testStateMock, stateName string) (*asl.MockRun, error)
var step map[string]any
if m.Result != nil {
- if !json.Valid([]byte(*m.Result)) {
+ if !jsontext.Value([]byte(*m.Result)).IsValid(jsonx.V1()) {
return nil, fmt.Errorf("%w: mock result is not valid JSON", ErrValidation)
}
- step = map[string]any{"Return": json.RawMessage(*m.Result)}
+ step = map[string]any{"Return": jsontext.Value(*m.Result)}
} else {
if m.ErrorOutput.Error == nil || *m.ErrorOutput.Error == "" {
return nil, fmt.Errorf("%w: mock errorOutput.error is required", ErrValidation)
@@ -152,7 +154,7 @@ func testStateMockRun(m *testStateMock, stateName string) (*asl.MockRun, error)
"StateMachines": map[string]any{
"s": map[string]any{"TestCases": map[string]any{"t": map[string]string{stateName: "m"}}},
},
- })
+ }, jsonx.V1())
if err != nil {
return nil, err
}
@@ -194,7 +196,7 @@ func inspectionDataFor(level string, executor *asl.Executor, trace *httpTrace) m
}
for key, value := range executor.Inspection() {
- if raw, err := json.Marshal(value); err == nil {
+ if raw, err := json.Marshal(value, jsonx.V1()); err == nil {
out[key] = string(raw)
}
}
@@ -208,23 +210,23 @@ func validInspectionLevel(level string) bool {
// detachNext swaps Next for End:true so TestState can run a non-terminal
// state without a synthetic next state, returning the new definition and Next.
-func detachNext(definition, stateName string, raw json.RawMessage) (string, string) {
+func detachNext(definition, stateName string, raw jsontext.Value) (string, string) {
var (
next string
- rawState map[string]json.RawMessage
+ rawState map[string]jsontext.Value
)
- if json.Unmarshal(raw, &rawState) != nil {
+ if json.Unmarshal(raw, &rawState, jsonx.V1()) != nil {
return definition, next
}
if nextRaw, hasNext := rawState["Next"]; hasNext {
- _ = json.Unmarshal(nextRaw, &next)
+ _ = json.Unmarshal(nextRaw, &next, jsonx.V1())
delete(rawState, "Next")
- rawState["End"] = json.RawMessage(`true`)
+ rawState["End"] = jsontext.Value(`true`)
}
- if modified, err := json.Marshal(map[string]any{stateName: rawState}); err == nil {
+ if modified, err := json.Marshal(map[string]any{stateName: rawState}, jsonx.V1()); err == nil {
definition = string(modified)
}
@@ -234,7 +236,7 @@ func detachNext(definition, stateName string, raw json.RawMessage) (string, stri
// handleTestState executes a single state definition in isolation and returns its output.
func (h *Handler) handleTestState(body []byte) (any, error) {
var input testStateInput
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, err
}
@@ -243,13 +245,13 @@ func (h *Handler) handleTestState(body []byte) (any, error) {
}
// Wrap the state definition in a minimal state machine.
- var states map[string]json.RawMessage
- if err := json.Unmarshal([]byte(input.Definition), &states); err != nil {
+ var states map[string]jsontext.Value
+ if err := json.Unmarshal([]byte(input.Definition), &states, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid state definition JSON: %w", ErrInvalidDefinition, err)
}
if _, bare := states["Type"]; bare {
- states = map[string]json.RawMessage{bareStateName: json.RawMessage(input.Definition)}
+ states = map[string]jsontext.Value{bareStateName: jsontext.Value(input.Definition)}
input.Definition = fmt.Sprintf(`{%q:%s}`, bareStateName, input.Definition)
}
@@ -326,7 +328,7 @@ func testStateResult(
}
}
- outputBytes, _ := json.Marshal(result.Output)
+ outputBytes, _ := json.Marshal(result.Output, jsonx.V1())
out := &testStateOutput{
Status: "SUCCEEDED", Output: string(outputBytes), NextState: nextState, InspectionData: inspection,
@@ -342,7 +344,7 @@ func testStateResult(
// configureTestStateExecutor applies the request's mock, context and
// stateConfiguration to executor, rejecting combinations AWS rejects.
func configureTestStateExecutor(
- executor *asl.Executor, input *testStateInput, stateName string, rawState json.RawMessage,
+ executor *asl.Executor, input *testStateInput, stateName string, rawState jsontext.Value,
) error {
if input.Context != nil && input.Mock == nil {
return fmt.Errorf("%w: context may only be specified together with a mock", ErrValidation)
@@ -350,7 +352,7 @@ func configureTestStateExecutor(
if input.Context != nil {
var ctxObj map[string]any
- if err := json.Unmarshal([]byte(*input.Context), &ctxObj); err != nil {
+ if err := json.Unmarshal([]byte(*input.Context), &ctxObj, jsonx.V1()); err != nil {
return fmt.Errorf("%w: context must be a JSON object", ErrValidation)
}
@@ -369,7 +371,7 @@ func configureTestStateExecutor(
Type string `json:"Type"`
}
- _ = json.Unmarshal(rawState, &typed)
+ _ = json.Unmarshal(rawState, &typed, jsonx.V1())
if typed.Type != stateTypeTask && typed.Type != stateTypeMap && typed.Type != stateTypeParallel {
return fmt.Errorf("%w: mock is supported for Task, Map and Parallel states only", ErrValidation)
@@ -386,20 +388,20 @@ func configureTestStateExecutor(
}
// isHTTPTaskState reports whether raw is a Task state calling http:invoke.
-func isHTTPTaskState(raw json.RawMessage) bool {
+func isHTTPTaskState(raw jsontext.Value) bool {
var st struct {
Type string `json:"Type"`
Resource string `json:"Resource"`
}
- _ = json.Unmarshal(raw, &st)
+ _ = json.Unmarshal(raw, &st, jsonx.V1())
return st.Type == stateTypeTask && strings.HasSuffix(st.Resource, ":::http:invoke")
}
// validateTestStateTrace rejects the TRACE and revealSecrets combinations AWS
// rejects (test-state-isolation.html, "TRACE inspectionLevel").
-func validateTestStateTrace(input *testStateInput, raw json.RawMessage) error {
+func validateTestStateTrace(input *testStateInput, raw jsontext.Value) error {
if input.InspectionLevel == inspectionTrace && !isHTTPTaskState(raw) {
return fmt.Errorf("%w: inspectionLevel TRACE is only applicable to an HTTP Task state", ErrValidation)
}
diff --git a/services/stepfunctions/handler_util_test.go b/services/stepfunctions/handler_util_test.go
index 2b918337dc..464c6726f5 100644
--- a/services/stepfunctions/handler_util_test.go
+++ b/services/stepfunctions/handler_util_test.go
@@ -2,12 +2,14 @@ package stepfunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_ValidateStateMachineDefinition(t *testing.T) {
@@ -55,14 +57,14 @@ func TestHandler_ValidateStateMachineDefinition(t *testing.T) {
ctx := t.Context()
h, e := newSFNHandler(t)
- reqBody, err := json.Marshal(map[string]string{"definition": tt.definition})
+ reqBody, err := json.Marshal(map[string]string{"definition": tt.definition}, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "ValidateStateMachineDefinition", string(reqBody))
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantResult, resp["result"])
diag, ok := resp["diagnostics"].([]any)
@@ -83,7 +85,7 @@ func TestValidateStateMachineDefinition_Valid(t *testing.T) {
ctx := t.Context()
h, e := newSFNHandler(t)
- body, err := json.Marshal(map[string]any{"definition": validPassDef})
+ body, err := json.Marshal(map[string]any{"definition": validPassDef}, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "ValidateStateMachineDefinition", string(body))
@@ -96,7 +98,7 @@ func TestValidateStateMachineDefinition_Invalid(t *testing.T) {
ctx := t.Context()
h, e := newSFNHandler(t)
- body, err := json.Marshal(map[string]any{"definition": `{"not":"valid-asl"}`})
+ body, err := json.Marshal(map[string]any{"definition": `{"not":"valid-asl"}`}, jsonx.V1())
require.NoError(t, err)
// AWS returns 200 with Result:"FAIL" and diagnostics — not a 4xx.
@@ -104,7 +106,7 @@ func TestValidateStateMachineDefinition_Invalid(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "FAIL", out["result"])
}
@@ -168,7 +170,7 @@ func TestTestState_NextState(t *testing.T) {
body, err := json.Marshal(map[string]any{
"definition": tt.definition,
"input": tt.input,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "TestState", string(body))
@@ -179,7 +181,7 @@ func TestTestState_NextState(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantStatus, resp["status"])
@@ -223,14 +225,14 @@ func TestValidateStateMachineDefinition(t *testing.T) {
t.Parallel()
h, e := newSFNHandler(t)
- body, err := json.Marshal(map[string]string{"definition": tt.def})
+ body, err := json.Marshal(map[string]string{"definition": tt.def}, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(t.Context(), t, h, e, "ValidateStateMachineDefinition", string(body))
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantResult, resp["result"])
})
}
diff --git a/services/stepfunctions/http_task.go b/services/stepfunctions/http_task.go
index f0c6f6e920..4124487f94 100644
--- a/services/stepfunctions/http_task.go
+++ b/services/stepfunctions/http_task.go
@@ -3,7 +3,7 @@ package stepfunctions
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -19,6 +19,7 @@ import (
"unicode/utf8"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions/asl"
)
@@ -374,7 +375,7 @@ func mergeHTTPBody(spec httpSpec, connBody []NameValue) ([]byte, error) {
}
}
- out, err := json.Marshal(body)
+ out, err := json.Marshal(body, jsonx.V1())
if err != nil {
return nil, runtimeFailure("RequestBody is not serializable")
}
@@ -455,7 +456,7 @@ func fetchOAuthToken(ctx context.Context, client *http.Client, o *OAuthCredentia
AccessToken string `json:"access_token"`
}
- if json.Unmarshal(data, &tok) != nil || tok.AccessToken == "" {
+ if json.Unmarshal(data, &tok, jsonx.V1()) != nil || tok.AccessToken == "" {
return "", errOAuthToken
}
@@ -510,7 +511,7 @@ func doHTTP(client *http.Client, req *http.Request, trace *httpTrace) (any, erro
return out, nil
}
- cause, _ := json.Marshal(out)
+ cause, _ := json.Marshal(out, jsonx.V1())
return nil, &asl.FailError{ErrCode: httpStatusPrefix + strconv.Itoa(resp.StatusCode), Cause: string(cause)}
}
@@ -559,7 +560,7 @@ func httpOutput(resp *http.Response, data []byte) map[string]any {
media, _, _ := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if media == "application/json" || strings.HasSuffix(media, "+json") {
var parsed any
- if json.Unmarshal(data, &parsed) == nil {
+ if json.Unmarshal(data, &parsed, jsonx.V1()) == nil {
body = parsed
}
}
diff --git a/services/stepfunctions/http_trace.go b/services/stepfunctions/http_trace.go
index 93c05ee1de..1255f3d32f 100644
--- a/services/stepfunctions/http_trace.go
+++ b/services/stepfunctions/http_trace.go
@@ -3,11 +3,13 @@ package stepfunctions
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"strconv"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const traceMask = "***"
@@ -79,7 +81,7 @@ func (t *httpTrace) headers(h http.Header, dropAuth string) string {
flat[k] = t.scrub(strings.Join(vs, ","))
}
- raw, _ := json.Marshal(flat)
+ raw, _ := json.Marshal(flat, jsonx.V1())
return string(raw)
}
diff --git a/services/stepfunctions/integrations.go b/services/stepfunctions/integrations.go
index 4f03132ff2..a8f5ce9534 100644
--- a/services/stepfunctions/integrations.go
+++ b/services/stepfunctions/integrations.go
@@ -3,7 +3,7 @@ package stepfunctions
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
@@ -12,6 +12,7 @@ import (
awss3 "github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
dynamodbpkg "github.com/blackbirdworks/gopherstack/services/dynamodb"
ecsbackend "github.com/blackbirdworks/gopherstack/services/ecs"
gluebackend "github.com/blackbirdworks/gopherstack/services/glue"
@@ -182,12 +183,12 @@ func (a *s3ResultWriterAdapter) PutObjectBytes(ctx context.Context, bucket, key
// convertViaJSON converts a value to a target type by marshaling to JSON and back.
func convertViaJSON(input, target any) error {
- b, err := json.Marshal(input)
+ b, err := json.Marshal(input, jsonx.V1())
if err != nil {
return err
}
- return json.Unmarshal(b, target)
+ return json.Unmarshal(b, target, jsonx.V1())
}
// outputToAny converts a typed SDK output struct to an untyped map via JSON round-trip.
diff --git a/services/stepfunctions/item_reader_parquet_test.go b/services/stepfunctions/item_reader_parquet_test.go
index 5ff112abad..cae59ca1e0 100644
--- a/services/stepfunctions/item_reader_parquet_test.go
+++ b/services/stepfunctions/item_reader_parquet_test.go
@@ -2,7 +2,7 @@ package stepfunctions_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
@@ -10,6 +10,7 @@ import (
sfntypes "github.com/aws/aws-sdk-go-v2/service/sfn/types"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/parquetio"
)
@@ -159,7 +160,7 @@ func TestItemReader_ParquetFlattenAndAthena(t *testing.T) {
require.Equal(t, sfntypes.ExecutionStatusSucceeded, status, "error=%s cause=%s", errCode, cause)
var items []map[string]any
- require.NoError(t, json.Unmarshal([]byte(output), &items))
+ require.NoError(t, json.Unmarshal([]byte(output), &items, jsonx.V1()))
assert.Len(t, items, 6)
})
}
diff --git a/services/stepfunctions/item_reader_s3_resource_test.go b/services/stepfunctions/item_reader_s3_resource_test.go
index 4051b5a7ce..1740e7a729 100644
--- a/services/stepfunctions/item_reader_s3_resource_test.go
+++ b/services/stepfunctions/item_reader_s3_resource_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"compress/gzip"
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
s3pkg "github.com/blackbirdworks/gopherstack/services/s3"
"github.com/blackbirdworks/gopherstack/services/stepfunctions"
)
@@ -126,7 +127,7 @@ func TestItemReader_S3ListObjectsV2(t *testing.T) {
require.Equal(t, sfntypes.ExecutionStatusSucceeded, status, "error=%s cause=%s", errCode, cause)
var items []map[string]any
- require.NoError(t, json.Unmarshal([]byte(output), &items))
+ require.NoError(t, json.Unmarshal([]byte(output), &items, jsonx.V1()))
require.Len(t, items, 2)
assert.Equal(t, "data/a.txt", items[0]["Key"])
@@ -217,7 +218,7 @@ func TestItemReader_S3Manifest(t *testing.T) {
"files": []map[string]any{{"key": dataKey, "size": len(inventoryCSV)}},
}
- b, err := json.Marshal(manifest)
+ b, err := json.Marshal(manifest, jsonx.V1())
require.NoError(t, err)
return string(b)
@@ -242,7 +243,7 @@ func TestItemReader_S3Manifest(t *testing.T) {
require.Equal(t, sfntypes.ExecutionStatusSucceeded, status, "error=%s cause=%s", errCode, cause)
var items []map[string]any
- require.NoError(t, json.Unmarshal([]byte(output), &items))
+ require.NoError(t, json.Unmarshal([]byte(output), &items, jsonx.V1()))
require.Len(t, items, 2)
assert.Equal(t, map[string]any{
"Bucket": "src-bucket", "Key": "csvDataset/titles.csv",
@@ -269,7 +270,7 @@ func TestItemReader_S3Manifest(t *testing.T) {
require.Equal(t, sfntypes.ExecutionStatusSucceeded, status, "error=%s cause=%s", errCode, cause)
var items []map[string]any
- require.NoError(t, json.Unmarshal([]byte(output), &items))
+ require.NoError(t, json.Unmarshal([]byte(output), &items, jsonx.V1()))
require.Len(t, items, 2)
assert.Equal(t, "imageDataset/pic.jpg", items[1]["Key"])
})
@@ -295,7 +296,7 @@ func TestItemReader_S3Manifest(t *testing.T) {
require.Equal(t, sfntypes.ExecutionStatusSucceeded, status, "error=%s cause=%s", errCode, cause)
var items []map[string]any
- require.NoError(t, json.Unmarshal([]byte(output), &items))
+ require.NoError(t, json.Unmarshal([]byte(output), &items, jsonx.V1()))
require.Len(t, items, 1)
assert.Equal(t, "csvDataset/titles.csv", items[0]["Key"])
})
diff --git a/services/stepfunctions/jsonata_distributed_map_test.go b/services/stepfunctions/jsonata_distributed_map_test.go
index d403b25215..1c2c159b07 100644
--- a/services/stepfunctions/jsonata_distributed_map_test.go
+++ b/services/stepfunctions/jsonata_distributed_map_test.go
@@ -2,13 +2,14 @@ package stepfunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"testing/synctest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions"
)
@@ -260,7 +261,7 @@ func runS3ArgumentsCase(t *testing.T, reader, writer, input, wantOutput string,
}
var out mapExportOutput
- require.NoError(t, json.Unmarshal([]byte(d.Output), &out))
+ require.NoError(t, json.Unmarshal([]byte(d.Output), &out, jsonx.V1()))
assert.Equal(t, "data-bucket", out.ResultWriterDetails.Bucket)
assert.NotEmpty(t, getS3ObjectBytes(t, s3Bk, "data-bucket", out.ResultWriterDetails.Key))
}
diff --git a/services/stepfunctions/list_executions_golden_test.go b/services/stepfunctions/list_executions_golden_test.go
index 95c63e645a..697ce63e67 100644
--- a/services/stepfunctions/list_executions_golden_test.go
+++ b/services/stepfunctions/list_executions_golden_test.go
@@ -2,7 +2,7 @@ package stepfunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"os"
@@ -15,6 +15,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions"
)
@@ -99,7 +100,7 @@ func TestListExecutions_PageGolden(t *testing.T) {
var resp struct {
NextToken string `json:"nextToken"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if resp.NextToken == "" {
break
diff --git a/services/stepfunctions/map_runs_test.go b/services/stepfunctions/map_runs_test.go
index 2612f72f0c..a18a4c5b8a 100644
--- a/services/stepfunctions/map_runs_test.go
+++ b/services/stepfunctions/map_runs_test.go
@@ -2,13 +2,14 @@ package stepfunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions"
)
@@ -43,14 +44,14 @@ func TestListMapRuns_ReturnsEmptyList(t *testing.T) {
smARN := createSM(ctx, t, h, e, "no-maprun-sm")
execARN := startExec(ctx, t, h, e, smARN, "no-maprun-exec")
- listBody, err := json.Marshal(map[string]any{"executionArn": execARN})
+ listBody, err := json.Marshal(map[string]any{"executionArn": execARN}, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "ListMapRuns", string(listBody))
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
mapRuns, _ := out["mapRuns"].([]any)
assert.Empty(t, mapRuns)
@@ -118,24 +119,24 @@ func TestMapRunStorage(t *testing.T) {
"definition": mapIterStateDef,
"roleArn": "arn:aws:iam::123456789012:role/test",
"type": "EXPRESS",
- })
+ }, jsonx.V1())
rec := sfnPost(ctx, t, h, e, "CreateStateMachine", string(createBody))
require.Equal(t, http.StatusOK, rec.Code)
var smResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &smResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &smResp, jsonx.V1()))
smARN := smResp["stateMachineArn"].(string)
syncBody, _ := json.Marshal(map[string]any{
"stateMachineArn": smARN,
"name": "sync-exec-1",
"input": `[1,2,3]`,
- })
+ }, jsonx.V1())
syncRec := sfnPost(ctx, t, h, e, "StartSyncExecution", string(syncBody))
require.Equal(t, http.StatusOK, syncRec.Code)
var syncResp map[string]any
- require.NoError(t, json.Unmarshal(syncRec.Body.Bytes(), &syncResp))
+ require.NoError(t, json.Unmarshal(syncRec.Body.Bytes(), &syncResp, jsonx.V1()))
execARN := syncResp["executionArn"].(string)
assert.GreaterOrEqual(t, bk.MapRunCountForTest(execARN), 1,
@@ -156,35 +157,35 @@ func TestMapRunStorage(t *testing.T) {
"definition": mapIterStateDef,
"roleArn": "arn:aws:iam::123456789012:role/test",
"type": "EXPRESS",
- })
+ }, jsonx.V1())
rec := sfnPost(ctx, t, h, e, "CreateStateMachine", string(createBody))
require.Equal(t, http.StatusOK, rec.Code)
var smResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &smResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &smResp, jsonx.V1()))
smARN := smResp["stateMachineArn"].(string)
syncBody, _ := json.Marshal(map[string]any{
"stateMachineArn": smARN,
"name": "sync-exec-2",
"input": `[1,2]`,
- })
+ }, jsonx.V1())
syncRec := sfnPost(ctx, t, h, e, "StartSyncExecution", string(syncBody))
require.Equal(t, http.StatusOK, syncRec.Code)
var syncResp map[string]any
- require.NoError(t, json.Unmarshal(syncRec.Body.Bytes(), &syncResp))
+ require.NoError(t, json.Unmarshal(syncRec.Body.Bytes(), &syncResp, jsonx.V1()))
execARN := syncResp["executionArn"].(string)
require.GreaterOrEqual(t, bk.MapRunCountForTest(execARN), 1)
// ListMapRuns via handler.
- listBody, _ := json.Marshal(map[string]any{"executionArn": execARN})
+ listBody, _ := json.Marshal(map[string]any{"executionArn": execARN}, jsonx.V1())
listRec := sfnPost(ctx, t, h, e, "ListMapRuns", string(listBody))
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
mapRuns, _ := listResp["mapRuns"].([]any)
require.NotEmpty(t, mapRuns, "expected non-empty mapRuns list")
@@ -192,12 +193,12 @@ func TestMapRunStorage(t *testing.T) {
mapRunARN, _ := firstRun["mapRunArn"].(string)
require.NotEmpty(t, mapRunARN)
- descBody, _ := json.Marshal(map[string]any{"mapRunArn": mapRunARN})
+ descBody, _ := json.Marshal(map[string]any{"mapRunArn": mapRunARN}, jsonx.V1())
descRec := sfnPost(ctx, t, h, e, "DescribeMapRun", string(descBody))
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
assert.Equal(t, mapRunARN, descResp["mapRunArn"])
assert.Equal(t, execARN, descResp["executionArn"])
assert.Equal(t, "SUCCEEDED", descResp["status"])
@@ -217,34 +218,34 @@ func TestMapRunStorage(t *testing.T) {
"definition": mapIterStateDef,
"roleArn": "arn:aws:iam::123456789012:role/test",
"type": "EXPRESS",
- })
+ }, jsonx.V1())
rec := sfnPost(ctx, t, h, e, "CreateStateMachine", string(createBody))
require.Equal(t, http.StatusOK, rec.Code)
var smResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &smResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &smResp, jsonx.V1()))
smARN := smResp["stateMachineArn"].(string)
syncBody, _ := json.Marshal(map[string]any{
"stateMachineArn": smARN,
"name": "sync-exec-3b",
"input": `[1]`,
- })
+ }, jsonx.V1())
syncRec := sfnPost(ctx, t, h, e, "StartSyncExecution", string(syncBody))
require.Equal(t, http.StatusOK, syncRec.Code)
var syncResp map[string]any
- require.NoError(t, json.Unmarshal(syncRec.Body.Bytes(), &syncResp))
+ require.NoError(t, json.Unmarshal(syncRec.Body.Bytes(), &syncResp, jsonx.V1()))
execARN := syncResp["executionArn"].(string)
require.GreaterOrEqual(t, bk.MapRunCountForTest(execARN), 1)
- listBody, _ := json.Marshal(map[string]any{"executionArn": execARN})
+ listBody, _ := json.Marshal(map[string]any{"executionArn": execARN}, jsonx.V1())
listRec := sfnPost(ctx, t, h, e, "ListMapRuns", string(listBody))
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
mapRuns, _ := listResp["mapRuns"].([]any)
require.NotEmpty(t, mapRuns)
@@ -254,12 +255,12 @@ func TestMapRunStorage(t *testing.T) {
updateBody, _ := json.Marshal(map[string]any{
"mapRunArn": mapRunARN,
"maxConcurrency": 10,
- })
+ }, jsonx.V1())
updateRec := sfnPost(ctx, t, h, e, "UpdateMapRun", string(updateBody))
require.Equal(t, http.StatusOK, updateRec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
maxConc, _ := updateResp["maxConcurrency"].(float64)
assert.InEpsilon(t, float64(10), maxConc, 1e-9)
},
diff --git a/services/stepfunctions/overwide_list_test.go b/services/stepfunctions/overwide_list_test.go
index 24bb88c00c..1f17da1d5e 100644
--- a/services/stepfunctions/overwide_list_test.go
+++ b/services/stepfunctions/overwide_list_test.go
@@ -2,13 +2,16 @@ package stepfunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestOverWideListResponses asserts on the raw response body that every
@@ -55,14 +58,14 @@ func TestOverWideListResponses(t *testing.T) {
"tracingConfiguration": map[string]any{"enabled": true},
"loggingConfiguration": map[string]any{"level": "ALL"},
"encryptionConfiguration": map[string]any{"type": "AWS_OWNED_KEY"},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "CreateStateMachine", string(createBody))
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
smARN, _ := created["stateMachineArn"].(string)
require.NotEmpty(t, smARN)
@@ -71,7 +74,7 @@ func TestOverWideListResponses(t *testing.T) {
"stateMachineArn": smARN,
"definition": validPassDef,
"roleArn": "arn:aws:iam::123456789012:role/test",
- })
+ }, jsonx.V1())
require.NoError(t, err)
updRec := sfnPost(ctx, t, h, e, "UpdateStateMachine", string(updateBody))
@@ -100,7 +103,7 @@ func TestOverWideListResponses(t *testing.T) {
"type": "CUSTOMER_MANAGED_KMS_KEY",
"kmsKeyId": "arn:aws:kms:us-east-1:123456789012:key/test-key",
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "CreateActivity", string(createBody))
@@ -128,18 +131,18 @@ func TestOverWideListResponses(t *testing.T) {
"stateMachineArn": smARN,
"name": "wide-exec",
"input": `{"secret":"do-not-leak"}`,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "StartExecution", string(execBody))
require.Equal(t, http.StatusOK, rec.Code)
var started map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &started))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &started, jsonx.V1()))
execARN, _ := started["executionArn"].(string)
require.NotEmpty(t, execARN)
- descBody, err := json.Marshal(map[string]string{"executionArn": execARN})
+ descBody, err := json.Marshal(map[string]string{"executionArn": execARN}, jsonx.V1())
require.NoError(t, err)
require.Eventuallyf(t, func() bool {
@@ -149,14 +152,14 @@ func TestOverWideListResponses(t *testing.T) {
}
var desc map[string]any
- if unmarshalErr := json.Unmarshal(descRec.Body.Bytes(), &desc); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(descRec.Body.Bytes(), &desc, jsonx.V1()); unmarshalErr != nil {
return false
}
return desc["status"] != "RUNNING"
}, 5*time.Second, 20*time.Millisecond, "execution should finish")
- listBody, err := json.Marshal(map[string]string{"stateMachineArn": smARN})
+ listBody, err := json.Marshal(map[string]string{"stateMachineArn": smARN}, jsonx.V1())
require.NoError(t, err)
listRec := sfnPost(ctx, t, h, e, "ListExecutions", string(listBody))
@@ -187,14 +190,14 @@ func TestOverWideListResponses(t *testing.T) {
"definition": mapIterStateDef,
"roleArn": "arn:aws:iam::123456789012:role/test",
"type": "EXPRESS",
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "CreateStateMachine", string(createBody))
require.Equal(t, http.StatusOK, rec.Code)
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
smARN, _ := created["stateMachineArn"].(string)
require.NotEmpty(t, smARN)
@@ -202,25 +205,25 @@ func TestOverWideListResponses(t *testing.T) {
"stateMachineArn": smARN,
"name": "wide-maprun-exec",
"input": `[1,2,3]`,
- })
+ }, jsonx.V1())
require.NoError(t, err)
syncRec := sfnPost(ctx, t, h, e, "StartSyncExecution", string(syncBody))
require.Equal(t, http.StatusOK, syncRec.Code)
var synced map[string]any
- require.NoError(t, json.Unmarshal(syncRec.Body.Bytes(), &synced))
+ require.NoError(t, json.Unmarshal(syncRec.Body.Bytes(), &synced, jsonx.V1()))
execARN, _ := synced["executionArn"].(string)
require.NotEmpty(t, execARN)
- listBody, err := json.Marshal(map[string]any{"executionArn": execARN})
+ listBody, err := json.Marshal(map[string]any{"executionArn": execARN}, jsonx.V1())
require.NoError(t, err)
listRec := sfnPost(ctx, t, h, e, "ListMapRuns", string(listBody))
require.Equal(t, http.StatusOK, listRec.Code)
var listed map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listed))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listed, jsonx.V1()))
runs, _ := listed["mapRuns"].([]any)
require.NotEmpty(t, runs, "expected at least one MapRun")
first, _ := runs[0].(map[string]any)
@@ -234,7 +237,7 @@ func TestOverWideListResponses(t *testing.T) {
"maxConcurrency": 5,
"toleratedFailureCount": 1,
"toleratedFailurePercentage": 10.0,
- })
+ }, jsonx.V1())
require.NoError(t, err)
updRec := sfnPost(ctx, t, h, e, "UpdateMapRun", string(updateBody))
@@ -261,14 +264,14 @@ func TestOverWideListResponses(t *testing.T) {
h, e := newSFNHandler(t)
smARN := createSM(ctx, t, h, e, "wide-alias-sm")
- pubBody, err := json.Marshal(map[string]any{"stateMachineArn": smARN})
+ pubBody, err := json.Marshal(map[string]any{"stateMachineArn": smARN}, jsonx.V1())
require.NoError(t, err)
pubRec := sfnPost(ctx, t, h, e, "PublishStateMachineVersion", string(pubBody))
require.Equal(t, http.StatusOK, pubRec.Code)
var published map[string]any
- require.NoError(t, json.Unmarshal(pubRec.Body.Bytes(), &published))
+ require.NoError(t, json.Unmarshal(pubRec.Body.Bytes(), &published, jsonx.V1()))
versionARN, _ := published["stateMachineVersionArn"].(string)
require.NotEmpty(t, versionARN)
@@ -279,14 +282,14 @@ func TestOverWideListResponses(t *testing.T) {
"routingConfiguration": []map[string]any{
{"stateMachineVersionArn": versionARN, "weight": 100},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
aliasRec := sfnPost(ctx, t, h, e, "CreateStateMachineAlias", string(createBody))
require.Equal(t, http.StatusOK, aliasRec.Code)
var alias map[string]any
- require.NoError(t, json.Unmarshal(aliasRec.Body.Bytes(), &alias))
+ require.NoError(t, json.Unmarshal(aliasRec.Body.Bytes(), &alias, jsonx.V1()))
aliasARN, _ := alias["stateMachineAliasArn"].(string)
require.NotEmpty(t, aliasARN)
@@ -294,13 +297,13 @@ func TestOverWideListResponses(t *testing.T) {
updateBody, err := json.Marshal(map[string]any{
"stateMachineAliasArn": aliasARN,
"description": "updated",
- })
+ }, jsonx.V1())
require.NoError(t, err)
updRec := sfnPost(ctx, t, h, e, "UpdateStateMachineAlias", string(updateBody))
require.Equal(t, http.StatusOK, updRec.Code)
- listBody, err := json.Marshal(map[string]string{"stateMachineArn": smARN})
+ listBody, err := json.Marshal(map[string]string{"stateMachineArn": smARN}, jsonx.V1())
require.NoError(t, err)
listRec := sfnPost(ctx, t, h, e, "ListStateMachineAliases", string(listBody))
@@ -331,13 +334,13 @@ func TestOverWideListResponses(t *testing.T) {
"stateMachineArn": smARN,
"description": "leaks if present",
"revisionId": "leaks-if-present",
- })
+ }, jsonx.V1())
require.NoError(t, err)
pubRec := sfnPost(ctx, t, h, e, "PublishStateMachineVersion", string(pubBody))
require.Equal(t, http.StatusOK, pubRec.Code)
- listBody, err := json.Marshal(map[string]string{"stateMachineArn": smARN})
+ listBody, err := json.Marshal(map[string]string{"stateMachineArn": smARN}, jsonx.V1())
require.NoError(t, err)
listRec := sfnPost(ctx, t, h, e, "ListStateMachineVersions", string(listBody))
@@ -354,14 +357,14 @@ func TestOverWideListResponses(t *testing.T) {
body := tt.raw(t)
- var resp map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(body, &resp))
+ var resp map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
itemsRaw, ok := resp[tt.itemsKey]
require.True(t, ok, "response missing %q key", tt.itemsKey)
- var items []map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(itemsRaw, &items))
+ var items []map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(itemsRaw, &items, jsonx.V1()))
require.NotEmpty(t, items, "expected at least one item in %q", tt.itemsKey)
for _, item := range items {
diff --git a/services/stepfunctions/persistence.go b/services/stepfunctions/persistence.go
index b678eed6a4..8522628345 100644
--- a/services/stepfunctions/persistence.go
+++ b/services/stepfunctions/persistence.go
@@ -2,9 +2,11 @@ package stepfunctions
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
"github.com/blackbirdworks/gopherstack/pkgs/store"
@@ -93,17 +95,17 @@ func (b *InMemoryBackend) newPersistedDTORegistry() (
// guards against decoding a snapshot from an incompatible (older or newer)
// build of this backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// handlerSnapshot wraps the backend snapshot together with handler-level state
// (tags) so both are persisted and restored together.
type handlerSnapshot struct {
Tags map[string]map[string]string `json:"tags,omitempty"`
- Backend json.RawMessage `json:"backend"`
+ Backend jsontext.Value `json:"backend"`
}
// Snapshot serialises the backend state to JSON.
@@ -329,7 +331,7 @@ func (h *Handler) Snapshot(ctx context.Context) []byte {
Snapshot(ctx context.Context) []byte
}
- var backendBytes json.RawMessage
+ var backendBytes jsontext.Value
if s, ok := h.Backend.(snapshotter); ok {
backendBytes = s.Snapshot(ctx)
}
@@ -346,7 +348,7 @@ func (h *Handler) Snapshot(ctx context.Context) []byte {
Tags: tagsData,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
return backendBytes
}
diff --git a/services/stepfunctions/result_writer_test.go b/services/stepfunctions/result_writer_test.go
index 900a5d0f21..2a0cb654f5 100644
--- a/services/stepfunctions/result_writer_test.go
+++ b/services/stepfunctions/result_writer_test.go
@@ -2,7 +2,8 @@ package stepfunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"log/slog"
"strings"
@@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
s3pkg "github.com/blackbirdworks/gopherstack/services/s3"
"github.com/blackbirdworks/gopherstack/services/stepfunctions"
@@ -198,7 +200,7 @@ func TestDistributedMapResultWriter(t *testing.T) {
require.Equal(t, "SUCCEEDED", d.Status, "cause=%s error=%s", d.Cause, d.Error)
var out mapExportOutput
- require.NoError(t, json.Unmarshal([]byte(d.Output), &out))
+ require.NoError(t, json.Unmarshal([]byte(d.Output), &out, jsonx.V1()))
assert.Equal(t, bucket, out.ResultWriterDetails.Bucket)
assert.True(t, strings.HasPrefix(out.ResultWriterDetails.Key, "jobs/"))
assert.True(t, strings.HasSuffix(out.ResultWriterDetails.Key, "manifest.json"))
@@ -207,7 +209,7 @@ func TestDistributedMapResultWriter(t *testing.T) {
manifestBytes := getS3ObjectBytes(t, s3Bk, bucket, out.ResultWriterDetails.Key)
var manifest resultManifest
- require.NoError(t, json.Unmarshal(manifestBytes, &manifest))
+ require.NoError(t, json.Unmarshal(manifestBytes, &manifest, jsonx.V1()))
assert.Equal(t, bucket, manifest.DestinationBucket)
assert.Equal(t, out.MapRunArn, manifest.MapRunArn)
require.Len(t, manifest.ResultFiles.Succeeded, 1)
@@ -219,7 +221,7 @@ func TestDistributedMapResultWriter(t *testing.T) {
succeededBytes := getS3ObjectBytes(t, s3Bk, bucket, manifest.ResultFiles.Succeeded[0].Key)
var records []map[string]any
- require.NoError(t, json.Unmarshal(succeededBytes, &records))
+ require.NoError(t, json.Unmarshal(succeededBytes, &records, jsonx.V1()))
require.Len(t, records, 3)
assert.Equal(t, "SUCCEEDED", records[0]["Status"])
// Default Transformation (NONE, since ResultWriter exports without a
@@ -255,7 +257,7 @@ func TestDistributedMapResultWriter(t *testing.T) {
require.Equal(t, "SUCCEEDED", d.Status, "cause=%s error=%s", d.Cause, d.Error)
var arr []float64
- require.NoError(t, json.Unmarshal([]byte(d.Output), &arr))
+ require.NoError(t, json.Unmarshal([]byte(d.Output), &arr, jsonx.V1()))
assert.Equal(t, []float64{1, 2, 3}, arr)
runs, _, err := b.ListMapRuns(exec.ExecutionArn, "", 0)
@@ -292,7 +294,7 @@ func TestDistributedMapResultWriter(t *testing.T) {
// NONE, same as the writes-to-S3 case -- the missing S3 writer only
// changes whether the formatted result is exported, not its shape.
var records []map[string]any
- require.NoError(t, json.Unmarshal([]byte(d.Output), &records))
+ require.NoError(t, json.Unmarshal([]byte(d.Output), &records, jsonx.V1()))
require.Len(t, records, 3)
assert.Equal(t, "SUCCEEDED", records[0]["Status"])
assert.Equal(t, "1", records[0]["Input"])
@@ -401,7 +403,7 @@ func startWriterConfigExecution(
// assertTransformationEntries checks succeeded-file/preview entries against
// what each Transformation must produce for three items whose child output
// is always [10, 20] (AWS docs: input-output-resultwriter.html).
-func assertTransformationEntries(t *testing.T, transformation string, raw []json.RawMessage) {
+func assertTransformationEntries(t *testing.T, transformation string, raw []jsontext.Value) {
t.Helper()
switch transformation {
@@ -411,7 +413,7 @@ func assertTransformationEntries(t *testing.T, transformation string, raw []json
want := []float64{10, 20, 10, 20, 10, 20}
for i, r := range raw {
var v float64
- require.NoError(t, json.Unmarshal(r, &v))
+ require.NoError(t, json.Unmarshal(r, &v, jsonx.V1()))
assert.InDelta(t, want[i], v, 0)
}
case "COMPACT":
@@ -419,7 +421,7 @@ func assertTransformationEntries(t *testing.T, transformation string, raw []json
for _, r := range raw {
var v []float64
- require.NoError(t, json.Unmarshal(r, &v))
+ require.NoError(t, json.Unmarshal(r, &v, jsonx.V1()))
assert.Equal(t, []float64{10, 20}, v)
}
default: // NONE
@@ -427,7 +429,7 @@ func assertTransformationEntries(t *testing.T, transformation string, raw []json
for _, r := range raw {
var rec map[string]any
- require.NoError(t, json.Unmarshal(r, &rec))
+ require.NoError(t, json.Unmarshal(r, &rec, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", rec["Status"])
assert.Equal(t, "[10,20]", rec["Output"], "NONE stringifies Output like a real DescribeExecution")
assert.Equal(t, true, rec["InputDetails"].(map[string]any)["Included"])
@@ -438,21 +440,21 @@ func assertTransformationEntries(t *testing.T, transformation string, raw []json
// decodeResultEntries parses a SUCCEEDED_0.json/FAILED_0.json file's bytes
// into individual JSON values, per OutputType: JSON is one array, JSONL is
// one value per newline.
-func decodeResultEntries(t *testing.T, data []byte, outputType string) []json.RawMessage {
+func decodeResultEntries(t *testing.T, data []byte, outputType string) []jsontext.Value {
t.Helper()
if outputType != "JSONL" {
- var arr []json.RawMessage
- require.NoError(t, json.Unmarshal(data, &arr))
+ var arr []jsontext.Value
+ require.NoError(t, json.Unmarshal(data, &arr, jsonx.V1()))
return arr
}
lines := strings.Split(strings.TrimSpace(string(data)), "\n")
- raw := make([]json.RawMessage, len(lines))
+ raw := make([]jsontext.Value, len(lines))
for i, l := range lines {
- raw[i] = json.RawMessage(l)
+ raw[i] = jsontext.Value(l)
}
return raw
@@ -488,7 +490,7 @@ func TestDistributedMapResultWriter_TransformationOutputType(t *testing.T) {
"cause=%s error=%s", aws.ToString(desc.Cause), aws.ToString(desc.Error))
var out mapExportOutput
- require.NoError(t, json.Unmarshal([]byte(aws.ToString(desc.Output)), &out))
+ require.NoError(t, json.Unmarshal([]byte(aws.ToString(desc.Output)), &out, jsonx.V1()))
require.Equal(t, bucket, out.ResultWriterDetails.Bucket)
assert.True(t, strings.HasPrefix(out.ResultWriterDetails.Key, "jobs/"))
assert.True(t, strings.HasSuffix(out.ResultWriterDetails.Key, "manifest.json"))
@@ -496,7 +498,7 @@ func TestDistributedMapResultWriter_TransformationOutputType(t *testing.T) {
manifestBytes := getS3ObjectBytes(t, s3Bk, bucket, out.ResultWriterDetails.Key)
var manifest resultManifest
- require.NoError(t, json.Unmarshal(manifestBytes, &manifest))
+ require.NoError(t, json.Unmarshal(manifestBytes, &manifest, jsonx.V1()))
require.Len(t, manifest.ResultFiles.Succeeded, 1)
assert.Empty(t, manifest.ResultFiles.Failed)
assert.True(t, strings.HasSuffix(manifest.ResultFiles.Succeeded[0].Key, "SUCCEEDED_0.json"))
@@ -566,26 +568,26 @@ func TestDistributedMapResultWriter_FailedItemsKeepFullRecord(t *testing.T) {
)
var out mapExportOutput
- require.NoError(t, json.Unmarshal([]byte(aws.ToString(desc.Output)), &out))
+ require.NoError(t, json.Unmarshal([]byte(aws.ToString(desc.Output)), &out, jsonx.V1()))
manifestBytes := getS3ObjectBytes(t, s3Bk, bucket, out.ResultWriterDetails.Key)
var manifest resultManifest
- require.NoError(t, json.Unmarshal(manifestBytes, &manifest))
+ require.NoError(t, json.Unmarshal(manifestBytes, &manifest, jsonx.V1()))
require.Len(t, manifest.ResultFiles.Succeeded, 1)
require.Len(t, manifest.ResultFiles.Failed, 1)
succeededBytes := getS3ObjectBytes(t, s3Bk, bucket, manifest.ResultFiles.Succeeded[0].Key)
var succeeded [][]float64
- require.NoError(t, json.Unmarshal(succeededBytes, &succeeded))
+ require.NoError(t, json.Unmarshal(succeededBytes, &succeeded, jsonx.V1()))
require.Len(t, succeeded, 2, "2 of 3 items succeed")
assert.Equal(t, []float64{10, 20}, succeeded[0])
failedBytes := getS3ObjectBytes(t, s3Bk, bucket, manifest.ResultFiles.Failed[0].Key)
var failed []map[string]any
- require.NoError(t, json.Unmarshal(failedBytes, &failed))
+ require.NoError(t, json.Unmarshal(failedBytes, &failed, jsonx.V1()))
require.Len(t, failed, 1)
assert.Equal(t, "FAILED", failed[0]["Status"])
assert.Equal(t, "2", failed[0]["Input"])
@@ -636,18 +638,18 @@ func TestDistributedMapResultWriter_DistributedChildIdentity(t *testing.T) {
"cause=%s error=%s", aws.ToString(desc.Cause), aws.ToString(desc.Error))
var out mapExportOutput
- require.NoError(t, json.Unmarshal([]byte(aws.ToString(desc.Output)), &out))
+ require.NoError(t, json.Unmarshal([]byte(aws.ToString(desc.Output)), &out, jsonx.V1()))
manifestBytes := getS3ObjectBytes(t, s3Bk, bucket, out.ResultWriterDetails.Key)
var manifest resultManifest
- require.NoError(t, json.Unmarshal(manifestBytes, &manifest))
+ require.NoError(t, json.Unmarshal(manifestBytes, &manifest, jsonx.V1()))
require.Len(t, manifest.ResultFiles.Succeeded, 1)
succeededBytes := getS3ObjectBytes(t, s3Bk, bucket, manifest.ResultFiles.Succeeded[0].Key)
var records []map[string]any
- require.NoError(t, json.Unmarshal(succeededBytes, &records))
+ require.NoError(t, json.Unmarshal(succeededBytes, &records, jsonx.V1()))
require.Len(t, records, 3)
seen := map[string]bool{}
@@ -694,20 +696,20 @@ func TestDistributedMapResultWriter_PreviewWithoutExport(t *testing.T) {
if tt.outputType == "JSONL" {
var jsonl string
- require.NoError(t, json.Unmarshal([]byte(output), &jsonl))
+ require.NoError(t, json.Unmarshal([]byte(output), &jsonl, jsonx.V1()))
lines := strings.Split(jsonl, "\n")
require.Len(t, lines, 6)
var v float64
- require.NoError(t, json.Unmarshal([]byte(lines[0]), &v))
+ require.NoError(t, json.Unmarshal([]byte(lines[0]), &v, jsonx.V1()))
assert.InDelta(t, 10, v, 0)
return
}
var arr []float64
- require.NoError(t, json.Unmarshal([]byte(output), &arr))
+ require.NoError(t, json.Unmarshal([]byte(output), &arr, jsonx.V1()))
assert.Equal(t, []float64{10, 20, 10, 20, 10, 20}, arr)
})
}
diff --git a/services/stepfunctions/s3_item_reader_test.go b/services/stepfunctions/s3_item_reader_test.go
index cb29db16d2..f4bc6d167b 100644
--- a/services/stepfunctions/s3_item_reader_test.go
+++ b/services/stepfunctions/s3_item_reader_test.go
@@ -2,13 +2,14 @@ package stepfunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"testing/synctest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions"
)
@@ -73,7 +74,7 @@ func TestStartExecution_MapItemReader_S3(t *testing.T) {
require.Equal(t, "SUCCEEDED", described.Status, "cause=%s error=%s", described.Cause, described.Error)
var output []map[string]any
- require.NoError(t, json.Unmarshal([]byte(described.Output), &output))
+ require.NoError(t, json.Unmarshal([]byte(described.Output), &output, jsonx.V1()))
assert.Len(t, output, 3, "expected one output entry per S3-sourced item")
})
}
diff --git a/services/stepfunctions/sdk_convert.go b/services/stepfunctions/sdk_convert.go
index 2e614db78e..7cc20736f0 100644
--- a/services/stepfunctions/sdk_convert.go
+++ b/services/stepfunctions/sdk_convert.go
@@ -2,7 +2,7 @@ package stepfunctions
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -10,6 +10,8 @@ import (
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -63,7 +65,7 @@ func streamString(src any) string {
return s
}
- b, _ := json.Marshal(src)
+ b, _ := json.Marshal(src, jsonx.V1())
return string(b)
}
diff --git a/services/stepfunctions/sdk_integration.go b/services/stepfunctions/sdk_integration.go
index 377ecd46e8..f3c735160f 100644
--- a/services/stepfunctions/sdk_integration.go
+++ b/services/stepfunctions/sdk_integration.go
@@ -3,7 +3,7 @@ package stepfunctions
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -20,6 +20,7 @@ import (
"github.com/aws/smithy-go"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/safemap"
"github.com/blackbirdworks/gopherstack/services/stepfunctions/asl"
"github.com/blackbirdworks/gopherstack/services/stepfunctions/sdkwire"
@@ -418,7 +419,7 @@ func withJSONString(m map[string]any, key string, blob bool) map[string]any {
raw, isStr := val.(string)
if !isStr {
- b, _ := json.Marshal(val)
+ b, _ := json.Marshal(val, jsonx.V1())
raw = string(b)
}
diff --git a/services/stepfunctions/sdk_sync.go b/services/stepfunctions/sdk_sync.go
index 557373c4a0..4086c1954d 100644
--- a/services/stepfunctions/sdk_sync.go
+++ b/services/stepfunctions/sdk_sync.go
@@ -2,10 +2,11 @@ package stepfunctions
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"slices"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions/asl"
)
@@ -172,7 +173,7 @@ func runTaskFailures(call asl.SDKCall, out any) error {
return nil
}
- cause, _ := json.Marshal(failures)
+ cause, _ := json.Marshal(failures, jsonx.V1())
return &asl.FailError{ErrCode: "AmazonECS.Unknown", Cause: string(cause)}
}
@@ -186,7 +187,7 @@ func executionSyncResult(p map[string]any, pattern string) any {
if s, ok := p["Output"].(string); ok {
var parsed any
- if json.Unmarshal([]byte(s), &parsed) == nil {
+ if json.Unmarshal([]byte(s), &parsed, jsonx.V1()) == nil {
p["Output"] = parsed
}
}
@@ -257,7 +258,7 @@ func finishSync(req syncRequest, poll map[string]any, state string) (any, error)
return result, nil
}
- cause, _ := json.Marshal(result)
+ cause, _ := json.Marshal(result, jsonx.V1())
return nil, &asl.FailError{ErrCode: errCodeTaskFailed, Cause: string(cause)}
}
diff --git a/services/stepfunctions/sdkwire/call_test.go b/services/stepfunctions/sdkwire/call_test.go
index cb1f7326a1..fb0fa2dcbd 100644
--- a/services/stepfunctions/sdkwire/call_test.go
+++ b/services/stepfunctions/sdkwire/call_test.go
@@ -2,7 +2,7 @@ package sdkwire_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions/sdkwire"
)
@@ -85,7 +86,7 @@ func TestCall_Request(t *testing.T) {
require.NoError(t, err)
var got map[string]any
- require.NoError(t, json.Unmarshal(seen, &got))
+ require.NoError(t, json.Unmarshal(seen, &got, jsonx.V1()))
for k, v := range tt.wantJSON {
assert.Equal(t, v, got[k], k)
@@ -107,7 +108,7 @@ func TestCall_IdempotencyToken(t *testing.T) {
require.NoError(t, err)
var got map[string]any
- require.NoError(t, json.Unmarshal(seen, &got))
+ require.NoError(t, json.Unmarshal(seen, &got, jsonx.V1()))
assert.NotEmpty(t, got["clientToken"])
}
diff --git a/services/stepfunctions/sdkwire/legacy.go b/services/stepfunctions/sdkwire/legacy.go
index caa13817fe..16457a0e69 100644
--- a/services/stepfunctions/sdkwire/legacy.go
+++ b/services/stepfunctions/sdkwire/legacy.go
@@ -3,7 +3,8 @@ package sdkwire
import (
"compress/gzip"
"embed"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"sort"
@@ -11,6 +12,7 @@ import (
"github.com/aws/smithy-go/traits"
smithyhttp "github.com/aws/smithy-go/transport/http"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions/sdkwire/ir"
)
@@ -36,7 +38,7 @@ func legacyService(key, signing, userAgentAPI string, protocol protocolFor) *Ser
}
var m ir.Model
- if derr := json.NewDecoder(zr).Decode(&m); derr != nil {
+ if derr := json.UnmarshalDecode(jsontext.NewDecoder(zr), &m, jsonx.V1()); derr != nil {
panic(fmt.Sprintf("sdkwire: model %s: %v", key, derr))
}
diff --git a/services/stepfunctions/sdkwire/parity_whitebox_test.go b/services/stepfunctions/sdkwire/parity_whitebox_test.go
index c437ea0dfd..e2583852d2 100644
--- a/services/stepfunctions/sdkwire/parity_whitebox_test.go
+++ b/services/stepfunctions/sdkwire/parity_whitebox_test.go
@@ -3,7 +3,7 @@ package sdkwire
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -25,6 +25,8 @@ import (
"github.com/aws/smithy-go/transport/http/protocol/restxml"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const testEndpoint = "http://sfn-sdk-integration.local"
@@ -166,7 +168,7 @@ func decodeJSONBody(raw []byte) (any, bool) {
var v any
- return v, json.Unmarshal(raw, &v) == nil
+ return v, json.Unmarshal(raw, &v, jsonx.V1()) == nil
}
func bodyDiff(contentType string, want, got []byte) string {
diff --git a/services/stepfunctions/sdkwire/schemair/encode.go b/services/stepfunctions/sdkwire/schemair/encode.go
index 780df9a83f..7e80d25ea7 100644
--- a/services/stepfunctions/sdkwire/schemair/encode.go
+++ b/services/stepfunctions/sdkwire/schemair/encode.go
@@ -3,12 +3,14 @@ package schemair
import (
"bytes"
"compress/gzip"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"reflect"
"sort"
"github.com/aws/smithy-go"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// OpRef is an operation's schemas as the SDK package declares them.
@@ -99,7 +101,7 @@ func (e *encoder) addOp(m *Model, ref OpRef) (Op, error) {
// Write gzips the model as JSON.
func (m *Model) Write() ([]byte, error) {
- raw, err := json.Marshal(m)
+ raw, err := json.Marshal(m, jsonx.V1())
if err != nil {
return nil, err
}
@@ -198,7 +200,7 @@ func (e *encoder) traits(s *smithy.Schema, direct bool) ([]Trait, error) {
continue
}
- raw, err := json.Marshal(t)
+ raw, err := json.Marshal(t, jsonx.V1())
if err != nil {
return nil, err
}
diff --git a/services/stepfunctions/sdkwire/schemair/schemair.go b/services/stepfunctions/sdkwire/schemair/schemair.go
index ba59e7b8e4..f7ee9feb89 100644
--- a/services/stepfunctions/sdkwire/schemair/schemair.go
+++ b/services/stepfunctions/sdkwire/schemair/schemair.go
@@ -5,7 +5,8 @@ package schemair
import (
"compress/gzip"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -14,6 +15,8 @@ import (
"github.com/aws/smithy-go"
"github.com/aws/smithy-go/prelude"
"github.com/aws/smithy-go/traits"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ErrModel marks an invalid or mismatched schema model.
@@ -58,8 +61,8 @@ type Op struct {
// Trait is a smithy-go trait by codec name and JSON-encoded fields.
type Trait struct {
- Name string `json:"n"`
- Value json.RawMessage `json:"v,omitempty"`
+ Name string `json:"n"`
+ Value jsontext.Value `json:"v,omitempty"`
}
// Operation is a decoded operation.
@@ -161,7 +164,7 @@ func Read(r io.Reader) (*Model, error) {
defer zr.Close()
var m Model
- if derr := json.NewDecoder(zr).Decode(&m); derr != nil {
+ if derr := json.UnmarshalDecode(jsontext.NewDecoder(zr), &m, jsonx.V1()); derr != nil {
return nil, fmt.Errorf("schemair: %w", derr)
}
@@ -245,7 +248,7 @@ func decodeTraits(cs map[string]codec, in []Trait) ([]smithy.Trait, error) {
v := reflect.New(c.typ)
if len(t.Value) > 0 {
- if err := json.Unmarshal(t.Value, v.Interface()); err != nil {
+ if err := json.Unmarshal(t.Value, v.Interface(), jsonx.V1()); err != nil {
return nil, fmt.Errorf("schemair: trait %s: %w", t.Name, err)
}
}
diff --git a/services/stepfunctions/state_machine_versions_test.go b/services/stepfunctions/state_machine_versions_test.go
index 1f2a6fbf63..bbe3da7dfe 100644
--- a/services/stepfunctions/state_machine_versions_test.go
+++ b/services/stepfunctions/state_machine_versions_test.go
@@ -2,7 +2,7 @@ package stepfunctions_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/stepfunctions"
)
@@ -172,7 +173,7 @@ func TestHandler_ListStateMachineVersions(t *testing.T) {
body := tt.body
if body == "" {
- b, err := json.Marshal(map[string]string{"stateMachineArn": smARN})
+ b, err := json.Marshal(map[string]string{"stateMachineArn": smARN}, jsonx.V1())
require.NoError(t, err)
body = string(b)
}
@@ -181,7 +182,7 @@ func TestHandler_ListStateMachineVersions(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "stateMachineVersions")
})
}
@@ -271,24 +272,24 @@ func TestListStateMachineVersions(t *testing.T) {
"definition": validPassDef,
"roleArn": "arn:aws:iam::000:role/r",
"type": "STANDARD",
- })
+ }, jsonx.V1())
require.NoError(t, err)
createRec := sfnPost(t.Context(), t, h, e, "CreateStateMachine", string(createBody))
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
smARN := createResp["stateMachineArn"].(string)
- listBody, merr := json.Marshal(map[string]string{"stateMachineArn": smARN})
+ listBody, merr := json.Marshal(map[string]string{"stateMachineArn": smARN}, jsonx.V1())
require.NoError(t, merr)
rec := sfnPost(t.Context(), t, h, e, "ListStateMachineVersions", string(listBody))
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
versions, ok := resp["stateMachineVersions"].([]any)
require.True(t, ok, "stateMachineVersions should be an array")
assert.Empty(t, versions)
diff --git a/services/stepfunctions/tags_test.go b/services/stepfunctions/tags_test.go
index 53e082481f..a02ef80d48 100644
--- a/services/stepfunctions/tags_test.go
+++ b/services/stepfunctions/tags_test.go
@@ -1,13 +1,15 @@
package stepfunctions_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_TagResource(t *testing.T) {
@@ -67,7 +69,7 @@ func TestHandler_ListTagsForResource(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags := resp["tags"].([]any)
assert.NotEmpty(t, tags)
})
@@ -112,7 +114,7 @@ func TestHandler_UntagResource(t *testing.T) {
listRec := sfnPost(ctx, t, h, e, "ListTagsForResource", `{"resourceArn":"`+arn+`"}`)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
tags := resp["tags"].([]any)
assert.Len(t, tags, tt.wantTagCount)
@@ -140,24 +142,24 @@ func TestTags_CreateWithInlineTags(t *testing.T) {
},
}
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "CreateStateMachine", string(raw))
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
arnStr := out["stateMachineArn"].(string)
- listBody, err := json.Marshal(map[string]any{"resourceArn": arnStr})
+ listBody, err := json.Marshal(map[string]any{"resourceArn": arnStr}, jsonx.V1())
require.NoError(t, err)
listRec := sfnPost(ctx, t, h, e, "ListTagsForResource", string(listBody))
require.Equal(t, http.StatusOK, listRec.Code)
var tags map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tags))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tags, jsonx.V1()))
tagList, _ := tags["tags"].([]any)
assert.Len(t, tagList, 2)
@@ -173,21 +175,21 @@ func TestTags_TagAndUntag(t *testing.T) {
tagBody, err := json.Marshal(map[string]any{
"resourceArn": arnStr,
"tags": []map[string]string{{"key": "k1", "value": "v1"}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "TagResource", string(tagBody))
require.Equal(t, http.StatusOK, rec.Code)
// Verify
- listBody, err := json.Marshal(map[string]any{"resourceArn": arnStr})
+ listBody, err := json.Marshal(map[string]any{"resourceArn": arnStr}, jsonx.V1())
require.NoError(t, err)
listRec := sfnPost(ctx, t, h, e, "ListTagsForResource", string(listBody))
require.Equal(t, http.StatusOK, listRec.Code)
var tagOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagOut, jsonx.V1()))
tagList, _ := tagOut["tags"].([]any)
assert.Len(t, tagList, 1)
@@ -196,7 +198,7 @@ func TestTags_TagAndUntag(t *testing.T) {
untagBody, err := json.Marshal(map[string]any{
"resourceArn": arnStr,
"tagKeys": []string{"k1"},
- })
+ }, jsonx.V1())
require.NoError(t, err)
untagRec := sfnPost(ctx, t, h, e, "UntagResource", string(untagBody))
@@ -206,7 +208,7 @@ func TestTags_TagAndUntag(t *testing.T) {
require.Equal(t, http.StatusOK, listRec2.Code)
var after map[string]any
- require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &after))
+ require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &after, jsonx.V1()))
tagList2, _ := after["tags"].([]any)
assert.Empty(t, tagList2)
@@ -225,14 +227,14 @@ func TestTagResource_KeyTooLong_Error(t *testing.T) {
body, err := json.Marshal(map[string]any{
"resourceArn": smARN,
"tags": []map[string]string{{"key": longKey, "value": "val"}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "TagResource", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "TagPolicyViolation", resp["__type"])
}
@@ -246,14 +248,14 @@ func TestTagResource_EmptyKey_Error(t *testing.T) {
body, err := json.Marshal(map[string]any{
"resourceArn": smARN,
"tags": []map[string]string{{"key": "", "value": "val"}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "TagResource", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "TagPolicyViolation", resp["__type"])
}
@@ -268,14 +270,14 @@ func TestTagResource_ValueTooLong_Error(t *testing.T) {
body, err := json.Marshal(map[string]any{
"resourceArn": smARN,
"tags": []map[string]string{{"key": "mykey", "value": longVal}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "TagResource", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "TagPolicyViolation", resp["__type"])
}
@@ -297,7 +299,7 @@ func TestTagResource_MaxTagsExceeded_Error(t *testing.T) {
body, err := json.Marshal(map[string]any{
"resourceArn": smARN,
"tags": batch,
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "TagResource", string(body))
require.Equal(t, http.StatusOK, rec.Code)
@@ -307,14 +309,14 @@ func TestTagResource_MaxTagsExceeded_Error(t *testing.T) {
body, err := json.Marshal(map[string]any{
"resourceArn": smARN,
"tags": []map[string]string{{"key": "overflow-key", "value": "val"}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "TagResource", string(body))
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// AWS: TagResource's own error switch models TooManyTags for exceeding
// the per-resource tag limit -- "TagPolicyViolation" names no type
// anywhere in this SDK.
@@ -345,7 +347,7 @@ func TestTagResource_ValidTagsAccepted(t *testing.T) {
body, err := json.Marshal(map[string]any{
"resourceArn": smARN,
"tags": []map[string]string{{"key": tt.key, "value": tt.value}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
rec := sfnPost(ctx, t, h, e, "TagResource", string(body))
@@ -395,7 +397,7 @@ func TestHandler_GetTags_EmptyAndNonEmpty(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags := resp["tags"].([]any)
assert.Len(t, tags, tt.wantTagsLen)
diff --git a/services/sts/persistence.go b/services/sts/persistence.go
index 7a6d9befe3..5051e465e3 100644
--- a/services/sts/persistence.go
+++ b/services/sts/persistence.go
@@ -2,9 +2,10 @@ package sts
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -45,7 +46,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
OpsGetFedToken: b.cntGetFederationToken.Load(),
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "sts: failed to snapshot backend state", "error", err)
diff --git a/services/sts/token_validation.go b/services/sts/token_validation.go
index 8525f58b5a..f7063bd935 100644
--- a/services/sts/token_validation.go
+++ b/services/sts/token_validation.go
@@ -2,7 +2,7 @@ package sts
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"errors"
"fmt"
@@ -10,6 +10,8 @@ import (
"slices"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// clockSkewLeeway is the tolerance applied to JWT/SAML temporal claims so that
@@ -87,7 +89,7 @@ func parseJWTPayloadClaims(token string) map[string]any {
}
var claims map[string]any
- if err = json.Unmarshal(decoded, &claims); err != nil {
+ if err = json.Unmarshal(decoded, &claims, jsonx.V1()); err != nil {
return nil
}
@@ -215,7 +217,7 @@ func jwtTimeClaim(claims map[string]any, key string) (time.Time, bool, error) {
switch n := v.(type) {
case float64:
return time.Unix(int64(n), 0).UTC(), true, nil
- case json.Number:
+ case jsonx.Number:
secs, err := n.Int64()
if err != nil {
return time.Time{}, false, fmt.Errorf(
diff --git a/services/sts/token_validation_test.go b/services/sts/token_validation_test.go
index 38c4b41cde..a30711461f 100644
--- a/services/sts/token_validation_test.go
+++ b/services/sts/token_validation_test.go
@@ -2,7 +2,7 @@ package sts_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"testing"
"time"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sts"
)
@@ -19,7 +20,7 @@ func makeUnitJWT(t *testing.T, claims map[string]any) string {
header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"none","typ":"JWT"}`))
- payload, err := json.Marshal(claims)
+ payload, err := json.Marshal(claims, jsonx.V1())
if err != nil {
t.Fatalf("marshal claims: %v", err)
}
@@ -232,7 +233,7 @@ func TestJWTTimeClaim(t *testing.T) {
},
{
name: "json_number",
- claims: map[string]any{"exp": json.Number("2000")},
+ claims: map[string]any{"exp": jsonx.Number("2000")},
key: "exp",
wantOK: true,
wantEpoch: 2000,
diff --git a/services/sts/trust_policy.go b/services/sts/trust_policy.go
index f7ddf906eb..dd08ba8275 100644
--- a/services/sts/trust_policy.go
+++ b/services/sts/trust_policy.go
@@ -2,7 +2,8 @@ package sts
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"slices"
"strconv"
@@ -10,6 +11,7 @@ import (
"time"
"github.com/blackbirdworks/gopherstack/pkgs/condeval"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -126,10 +128,10 @@ func (e trustEval) conditionValue(key string) (string, bool) {
// trustStatementDoc is a single parsed trust-policy statement.
type trustStatementDoc struct {
- Condition map[string]map[string]json.RawMessage `json:"Condition"`
- Effect string `json:"Effect"`
- Principal json.RawMessage `json:"Principal"`
- Action json.RawMessage `json:"Action"`
+ Condition map[string]map[string]jsontext.Value `json:"Condition"`
+ Effect string `json:"Effect"`
+ Principal jsontext.Value `json:"Principal"`
+ Action jsontext.Value `json:"Action"`
}
// principalSet is the parsed Principal element of a statement.
@@ -147,7 +149,7 @@ type trustPolicy struct {
// trustStatement is a single statement in a trust policy.
type trustStatement struct {
- Condition map[string]map[string]json.RawMessage `json:"Condition"`
+ Condition map[string]map[string]jsontext.Value `json:"Condition"`
}
// evaluateAssumeRoleTrust evaluates a role's trust policy against the caller
@@ -213,10 +215,10 @@ func parseTrustStatements(policyJSON string) ([]trustStatementDoc, bool) {
}
var doc struct {
- Statement json.RawMessage `json:"Statement"`
+ Statement jsontext.Value `json:"Statement"`
}
- if err := json.Unmarshal([]byte(policyJSON), &doc); err != nil {
+ if err := json.Unmarshal([]byte(policyJSON), &doc, jsonx.V1()); err != nil {
return nil, false
}
@@ -227,7 +229,7 @@ func parseTrustStatements(policyJSON string) ([]trustStatementDoc, bool) {
if raw[0] == '[' {
var arr []trustStatementDoc
- if err := json.Unmarshal(raw, &arr); err != nil {
+ if err := json.Unmarshal(raw, &arr, jsonx.V1()); err != nil {
return nil, false
}
@@ -235,7 +237,7 @@ func parseTrustStatements(policyJSON string) ([]trustStatementDoc, bool) {
}
var single trustStatementDoc
- if err := json.Unmarshal(raw, &single); err != nil {
+ if err := json.Unmarshal(raw, &single, jsonx.V1()); err != nil {
return nil, false
}
@@ -244,7 +246,7 @@ func parseTrustStatements(policyJSON string) ([]trustStatementDoc, bool) {
// trimLeadingSpace drops leading ASCII whitespace from a raw JSON message so the
// first significant byte can be inspected without importing bytes helpers.
-func trimLeadingSpace(raw json.RawMessage) json.RawMessage {
+func trimLeadingSpace(raw jsontext.Value) jsontext.Value {
i := 0
for i < len(raw) {
switch raw[i] {
@@ -260,7 +262,7 @@ func trimLeadingSpace(raw json.RawMessage) json.RawMessage {
// actionMatches reports whether the requested action is granted by the
// statement Action element (a string or array; wildcards supported).
-func actionMatches(raw json.RawMessage, requested string) bool {
+func actionMatches(raw jsontext.Value, requested string) bool {
if len(raw) == 0 {
// A statement without an explicit Action is treated as matching so that
// condition-only test policies (which omit Action) keep working.
@@ -282,7 +284,7 @@ func actionMatches(raw json.RawMessage, requested string) bool {
}
// principalMatches reports whether the caller satisfies the statement Principal.
-func principalMatches(raw json.RawMessage, ev trustEval) bool {
+func principalMatches(raw jsontext.Value, ev trustEval) bool {
if len(raw) == 0 {
// No Principal specified. Real trust policies always name a principal,
// but ExternalId-only test fixtures omit it; treat as matching so those
@@ -320,11 +322,11 @@ func principalMatches(raw json.RawMessage, ev trustEval) bool {
// parsePrincipal parses the Principal element, which may be the string "*" or an
// object keyed by AWS / Federated / Service / CanonicalUser.
-func parsePrincipal(raw json.RawMessage) principalSet {
+func parsePrincipal(raw jsontext.Value) principalSet {
var out principalSet
var asString string
- if err := json.Unmarshal(raw, &asString); err == nil {
+ if err := json.Unmarshal(raw, &asString, jsonx.V1()); err == nil {
if asString == "*" {
out.wildcard = true
} else {
@@ -334,8 +336,8 @@ func parsePrincipal(raw json.RawMessage) principalSet {
return out
}
- var obj map[string]json.RawMessage
- if err := json.Unmarshal(raw, &obj); err != nil {
+ var obj map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &obj, jsonx.V1()); err != nil {
return out
}
@@ -476,7 +478,7 @@ func assumedRoleArnMatches(callerArn, account, roleName string) bool {
// conditionsSatisfied reports whether every condition in a statement holds for
// the caller context. Unmodelled keys and operators are treated as satisfied.
-func conditionsSatisfied(cond map[string]map[string]json.RawMessage, ev trustEval) bool {
+func conditionsSatisfied(cond map[string]map[string]jsontext.Value, ev trustEval) bool {
for op, kv := range cond {
for key, raw := range kv {
if !conditionOperatorHolds(op, key, raw, ev) {
@@ -497,7 +499,7 @@ func conditionsSatisfied(cond map[string]map[string]json.RawMessage, ev trustEva
// cannot evaluate does not silently break existing callers. Both fallback
// paths log loudly at WARN so the gap is discoverable at runtime instead of
// silent -- that is the fix for gopherstack-yg95, not a change of default.
-func conditionOperatorHolds(op, key string, raw json.RawMessage, ev trustEval) bool {
+func conditionOperatorHolds(op, key string, raw jsontext.Value, ev trustEval) bool {
// AWS docs: IfExists may suffix any operator except Null (which already
// tests presence), so "NullIfExists" is left as an unrecognized operator.
//nolint:lll // AWS doc URL, cannot be split
@@ -615,7 +617,7 @@ func dateOperatorHolds(normOp string, want []string, actual string) bool {
// be present. Unlike every other operator, Null tests key existence, not the
// key's value, so it is driven by conditionValue's known result rather than a
// string/bool comparison against actual.
-func nullConditionHolds(raw json.RawMessage, known bool) bool {
+func nullConditionHolds(raw jsontext.Value, known bool) bool {
for _, v := range extractStringValues(raw) {
wantAbsent, err := strconv.ParseBool(v)
if err == nil && wantAbsent == !known {
@@ -728,7 +730,7 @@ func validateExternalID(trustPolicyJSON, externalID string) error {
// policy leaves tp with a zero value (empty Statements), so no ExternalId
// condition will be found and the call proceeds without validation — the
// permissive behaviour is intentional for a mock implementation.
- _ = json.Unmarshal([]byte(trustPolicyJSON), &tp)
+ _ = json.Unmarshal([]byte(trustPolicyJSON), &tp, jsonx.V1())
var hasExternalIDCondition bool
@@ -757,7 +759,7 @@ func validateExternalID(trustPolicyJSON, externalID string) error {
// requiredExternalIDs extracts all sts:ExternalId values from a trust-statement Condition map.
// Returns nil when no ExternalId condition is present.
-func requiredExternalIDs(condition map[string]map[string]json.RawMessage) []string {
+func requiredExternalIDs(condition map[string]map[string]jsontext.Value) []string {
for condOp, condMap := range condition {
if !strings.EqualFold(condOp, "StringEquals") && !strings.EqualFold(condOp, "StringLike") {
continue
@@ -791,7 +793,7 @@ func validateMFACondition(trustPolicyJSON string, mfaPresent bool) error {
// Unmarshal errors leave tp with a zero value (empty Statements): a malformed
// trust policy is treated permissively, same as validateExternalID.
- _ = json.Unmarshal([]byte(trustPolicyJSON), &tp)
+ _ = json.Unmarshal([]byte(trustPolicyJSON), &tp, jsonx.V1())
var hasMFACondition bool
@@ -821,7 +823,7 @@ func validateMFACondition(trustPolicyJSON string, mfaPresent bool) error {
// requiredMFAPresent extracts the required aws:MultiFactorAuthPresent Bool value
// from a trust-statement Condition map. The second result reports whether the
// condition is present at all.
-func requiredMFAPresent(condition map[string]map[string]json.RawMessage) (bool, bool) {
+func requiredMFAPresent(condition map[string]map[string]jsontext.Value) (bool, bool) {
for condOp, condMap := range condition {
if normalizeConditionOp(condOp) != condOperatorBool {
continue
@@ -845,14 +847,14 @@ func requiredMFAPresent(condition map[string]map[string]json.RawMessage) (bool,
// extractStringValues unmarshals a JSON RawMessage that may be either a string
// or an array of strings and returns the values as a Go string slice.
-func extractStringValues(raw json.RawMessage) []string {
+func extractStringValues(raw jsontext.Value) []string {
var single string
- if err := json.Unmarshal(raw, &single); err == nil {
+ if err := json.Unmarshal(raw, &single, jsonx.V1()); err == nil {
return []string{single}
}
var many []string
- if err := json.Unmarshal(raw, &many); err == nil {
+ if err := json.Unmarshal(raw, &many, jsonx.V1()); err == nil {
return many
}
diff --git a/services/sts/validation.go b/services/sts/validation.go
index 6578a4cd91..fc2679cbfa 100644
--- a/services/sts/validation.go
+++ b/services/sts/validation.go
@@ -1,11 +1,13 @@
package sts
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// roleSessionNameRe is the AWS-allowed character set for RoleSessionName/FederationToken Name.
@@ -198,7 +200,7 @@ func validateInlinePolicy(policy string) error {
}
var doc map[string]any
- if err := json.Unmarshal([]byte(policy), &doc); err != nil {
+ if err := json.Unmarshal([]byte(policy), &doc, jsonx.V1()); err != nil {
return fmt.Errorf("%w: %w", ErrMalformedPolicyDocument, err)
}
@@ -306,7 +308,7 @@ func validateJWTNotExpired(token string) error {
switch v := exp.(type) {
case float64:
expTime = time.Unix(int64(v), 0).UTC()
- case json.Number:
+ case jsonx.Number:
n, err := v.Int64()
if err != nil {
return err
diff --git a/services/sts/web_identity.go b/services/sts/web_identity.go
index e2d93964eb..1e037ec9c0 100644
--- a/services/sts/web_identity.go
+++ b/services/sts/web_identity.go
@@ -2,13 +2,15 @@ package sts
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strings"
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// webIdentitySubjectPlaceholder is returned when the sub claim cannot be extracted from the token.
@@ -479,7 +481,7 @@ func (b *InMemoryBackend) GetWebIdentityToken(
claims[jwtClaimTags] = tagMap
}
- payload, err := json.Marshal(claims)
+ payload, err := json.Marshal(claims, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("build token payload: %w", err)
}
diff --git a/services/sts/web_identity_irsa_test.go b/services/sts/web_identity_irsa_test.go
index e74d29212a..4fd339ec4d 100644
--- a/services/sts/web_identity_irsa_test.go
+++ b/services/sts/web_identity_irsa_test.go
@@ -6,7 +6,7 @@ import (
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"strings"
"testing"
"time"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sts"
)
@@ -53,10 +54,10 @@ func (l irsaOIDCLookup) OIDCProviderClientIDs(issuer string) ([]string, bool) {
func signIRSAToken(t *testing.T, key *rsa.PrivateKey, claims map[string]any) string {
t.Helper()
- hdr, err := json.Marshal(map[string]string{"alg": "RS256", "kid": "k"})
+ hdr, err := json.Marshal(map[string]string{"alg": "RS256", "kid": "k"}, jsonx.V1())
require.NoError(t, err)
- body, err := json.Marshal(claims)
+ body, err := json.Marshal(claims, jsonx.V1())
require.NoError(t, err)
signing := base64.RawURLEncoding.EncodeToString(hdr) + "." + base64.RawURLEncoding.EncodeToString(body)
@@ -77,7 +78,7 @@ func irsaTrustPolicy(cond string) string {
func forgeIRSAPayload(t *testing.T, token string, claims map[string]any) string {
t.Helper()
- forged, err := json.Marshal(claims)
+ forged, err := json.Marshal(claims, jsonx.V1())
require.NoError(t, err)
parts := strings.Split(token, ".")
@@ -89,7 +90,7 @@ func forgeIRSAPayload(t *testing.T, token string, claims map[string]any) string
func unsignedIRSAToken(t *testing.T, claims map[string]any) string {
t.Helper()
- body, err := json.Marshal(claims)
+ body, err := json.Marshal(claims, jsonx.V1())
require.NoError(t, err)
return base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"none"}`)) + "." +
diff --git a/services/sts/web_identity_test.go b/services/sts/web_identity_test.go
index b9457e16c8..697294bbd1 100644
--- a/services/sts/web_identity_test.go
+++ b/services/sts/web_identity_test.go
@@ -2,7 +2,7 @@ package sts_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"encoding/xml"
"net/http"
"net/url"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/sts"
)
@@ -41,7 +42,7 @@ func buildJWT(t *testing.T, claims map[string]any) string {
t.Helper()
header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"mock","typ":"JWT"}`))
- payload, err := json.Marshal(claims)
+ payload, err := json.Marshal(claims, jsonx.V1())
require.NoError(t, err)
return header + "." + base64.RawURLEncoding.EncodeToString(payload) + ".fakesig"
@@ -52,7 +53,7 @@ func buildParityJWT(t *testing.T, expUnix int64) string {
t.Helper()
header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"none","typ":"JWT"}`))
- payload, err := json.Marshal(map[string]any{"sub": "test", "exp": expUnix})
+ payload, err := json.Marshal(map[string]any{"sub": "test", "exp": expUnix}, jsonx.V1())
require.NoError(t, err)
return header + "." + base64.RawURLEncoding.EncodeToString(payload) + "."
@@ -69,7 +70,7 @@ func parseJWTClaims(t *testing.T, tokenStr string) map[string]any {
require.NoError(t, err)
var claims map[string]any
- require.NoError(t, json.Unmarshal(payloadBytes, &claims))
+ require.NoError(t, json.Unmarshal(payloadBytes, &claims, jsonx.V1()))
return claims
}
diff --git a/services/sts/web_identity_verify.go b/services/sts/web_identity_verify.go
index 5f3e137886..c06b2e5ffa 100644
--- a/services/sts/web_identity_verify.go
+++ b/services/sts/web_identity_verify.go
@@ -5,10 +5,12 @@ import (
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const jwtAlgRS256 = "RS256"
@@ -37,7 +39,7 @@ func verifyRS256(token string, keys []*rsa.PublicKey) error {
Alg string `json:"alg"`
}
- if err = json.Unmarshal(rawHdr, &hdr); err != nil || hdr.Alg != jwtAlgRS256 {
+ if err = json.Unmarshal(rawHdr, &hdr, jsonx.V1()); err != nil || hdr.Alg != jwtAlgRS256 {
return fmt.Errorf("%w: unsupported JWT signing algorithm", ErrInvalidIdentityToken)
}
diff --git a/services/support/cases.go b/services/support/cases.go
index 6da4dd5c0a..3ea1463308 100644
--- a/services/support/cases.go
+++ b/services/support/cases.go
@@ -2,7 +2,7 @@ package support
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"sort"
@@ -10,6 +10,8 @@ import (
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -382,7 +384,7 @@ func pageOffset(token string) (int, error) {
}
var t pageTokenJSON
- if unmarshalErr := json.Unmarshal(decoded, &t); unmarshalErr != nil || t.Offset < 0 {
+ if unmarshalErr := json.Unmarshal(decoded, &t, jsonx.V1()); unmarshalErr != nil || t.Offset < 0 {
return 0, fmt.Errorf("%w: invalid nextToken", ErrValidation)
}
@@ -390,7 +392,7 @@ func pageOffset(token string) (int, error) {
}
func encodePageToken(offset int) string {
- data, _ := json.Marshal(pageTokenJSON{Offset: offset})
+ data, _ := json.Marshal(pageTokenJSON{Offset: offset}, jsonx.V1())
return base64.RawURLEncoding.EncodeToString(data)
}
diff --git a/services/support/cases_test.go b/services/support/cases_test.go
index 7b9d67dcf0..125bb77968 100644
--- a/services/support/cases_test.go
+++ b/services/support/cases_test.go
@@ -2,7 +2,7 @@ package support_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/support"
)
@@ -25,7 +26,7 @@ func opaqueToken(t *testing.T, token string) int {
O int `json:"o"`
}
- require.NoError(t, json.Unmarshal(raw, &obj), "token must be JSON with 'o' key")
+ require.NoError(t, json.Unmarshal(raw, &obj, jsonx.V1()), "token must be JSON with 'o' key")
return obj.O
}
diff --git a/services/support/handler.go b/services/support/handler.go
index af7d7163ac..cdcbe1ffdd 100644
--- a/services/support/handler.go
+++ b/services/support/handler.go
@@ -2,7 +2,7 @@ package support
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -12,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -152,7 +153,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var req extractSupportResourceInput
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if req.CaseID != "" {
return req.CaseID
@@ -231,7 +232,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// handleError writes an AWS JSON-1.1 error envelope: the "__type" field is
@@ -256,7 +257,7 @@ func (h *Handler) handleError(ctx context.Context, c *echo.Context, action strin
payload, marshalErr := json.Marshal(service.JSONErrorResponse{
Type: errType,
Message: errorMessage(errType, err),
- })
+ }, jsonx.V1())
if marshalErr != nil {
return c.String(http.StatusInternalServerError, "internal server error")
}
@@ -269,9 +270,6 @@ func (h *Handler) handleError(ctx context.Context, c *echo.Context, action strin
// resolveErrorType maps a backend error to the wire "__type" exception name
// and HTTP status a real AWS Support (awsjson1.1) response would carry.
func resolveErrorType(err error) (string, int) {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, ErrNotFound):
return "CaseIdNotFound", http.StatusBadRequest
@@ -294,7 +292,7 @@ func resolveErrorType(err error) (string, int) {
case errors.Is(err, ErrDescribeAttachmentLimitExceeded):
return "DescribeAttachmentLimitExceeded", http.StatusBadRequest
case errors.Is(err, ErrValidation), errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
return "ValidationException", http.StatusBadRequest
default:
return "InternalServerError", http.StatusInternalServerError
diff --git a/services/support/handler_test.go b/services/support/handler_test.go
index 1ffe2462be..62bf8de29c 100644
--- a/services/support/handler_test.go
+++ b/services/support/handler_test.go
@@ -2,7 +2,7 @@ package support_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/support"
)
@@ -24,7 +25,7 @@ func newTestSupportHandler(t *testing.T) *support.Handler {
func doSupportRequest(t *testing.T, h *support.Handler, action string, body any) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -47,7 +48,7 @@ func decodeSupportResponse(t *testing.T, rec *httptest.ResponseRecorder) map[str
t.Helper()
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
return result
}
diff --git a/services/support/persistence.go b/services/support/persistence.go
index f9b9e00574..634948bd81 100644
--- a/services/support/persistence.go
+++ b/services/support/persistence.go
@@ -2,10 +2,12 @@ package support
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
@@ -55,7 +57,7 @@ func newAttachmentSetDTORegistry() (*store.Registry, *store.Table[attachmentSetS
// is order-sensitive and left a plain map -- see store_setup.go -- so it is
// persisted here directly instead of through a store.Table.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Communications map[string][]Communication `json:"communications"`
NextDisplayID uint64 `json:"nextDisplayId"`
Version int `json:"version"`
@@ -96,7 +98,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
NextDisplayID: b.nextDisplayID,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "support: snapshot marshal failed", "error", err)
@@ -161,7 +163,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreAttachmentSetsLocked rebuilds the "dirty" attachmentSets table from
// tables via the ephemeral DTO registry, converting each DTO back to its live
// type. The caller MUST hold b.mu for writing.
-func (b *InMemoryBackend) restoreAttachmentSetsLocked(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreAttachmentSetsLocked(tables map[string]jsontext.Value) error {
dtoReg, asDTOs := newAttachmentSetDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
return fmt.Errorf("support: restore snapshot DTO tables: %w", err)
diff --git a/services/support/whitebox_test.go b/services/support/whitebox_test.go
index 0964201d54..4654c03405 100644
--- a/services/support/whitebox_test.go
+++ b/services/support/whitebox_test.go
@@ -2,7 +2,7 @@ package support
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strconv"
@@ -12,12 +12,14 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func doWhiteboxSupportRequest(t *testing.T, h *Handler, action string, body any) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -184,6 +186,6 @@ func TestSupport_CreateCase_CaseCreationLimitExceeded_WireType(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
assert.Equal(t, "CaseCreationLimitExceeded", result["__type"])
}
diff --git a/services/swf/decision_lifecycle_test.go b/services/swf/decision_lifecycle_test.go
index aad14c2a07..288c3407ed 100644
--- a/services/swf/decision_lifecycle_test.go
+++ b/services/swf/decision_lifecycle_test.go
@@ -1,13 +1,14 @@
package swf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/swf"
)
@@ -597,7 +598,7 @@ func TestRespondDecisionTask_ViaHandler(t *testing.T) {
if tt.wantExecStatus != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// Verify via describe - pick the right domain/workflowID
var domainName, wfID string
if tt.wantExecStatus == "COMPLETED" {
diff --git a/services/swf/decision_orchestration_test.go b/services/swf/decision_orchestration_test.go
index 06068e2dcc..43b41e6462 100644
--- a/services/swf/decision_orchestration_test.go
+++ b/services/swf/decision_orchestration_test.go
@@ -7,13 +7,14 @@
package swf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/swf"
)
@@ -80,7 +81,7 @@ func TestStartChildWorkflowExecutionDecision_Success(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
openCounts, ok := body["openCounts"].(map[string]any)
require.True(t, ok)
assert.InDelta(t, 1, openCounts["openChildWorkflowExecutions"], 0)
diff --git a/services/swf/handler.go b/services/swf/handler.go
index 78e04381e0..912871bfa7 100644
--- a/services/swf/handler.go
+++ b/services/swf/handler.go
@@ -2,7 +2,7 @@ package swf
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
@@ -136,7 +137,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
return ""
}
var req extractSWFResourceInput
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if req.Name != "" {
return req.Name
}
@@ -211,13 +212,10 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
code := http.StatusInternalServerError
var errType string
@@ -253,7 +251,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
code = http.StatusBadRequest
errType = "OperationNotPermittedFault"
case errors.Is(err, errInvalidRequest), errors.Is(err, ErrUnknownOperation),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
code = http.StatusBadRequest
}
diff --git a/services/swf/handler_activity_types_test.go b/services/swf/handler_activity_types_test.go
index 66802b5b61..29a189feea 100644
--- a/services/swf/handler_activity_types_test.go
+++ b/services/swf/handler_activity_types_test.go
@@ -1,13 +1,14 @@
package swf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/swf"
)
@@ -119,7 +120,7 @@ func TestHandler_DeprecateActivityType(t *testing.T) {
if tt.wantType != "" {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
}
})
diff --git a/services/swf/handler_test.go b/services/swf/handler_test.go
index 5553f9c97c..8a9b0555a4 100644
--- a/services/swf/handler_test.go
+++ b/services/swf/handler_test.go
@@ -2,7 +2,7 @@ package swf_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http"
"net/http/httptest"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/swf"
)
@@ -29,7 +30,7 @@ func doSWFRequest(t *testing.T, h *swf.Handler, action string, body any) *httpte
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
} else {
bodyBytes = []byte("{}")
@@ -54,7 +55,7 @@ func parseSWFResp(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
@@ -252,7 +253,7 @@ func TestSWFHandler_Actions(t *testing.T) {
if tt.wantNotEmptyField != "" {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp[tt.wantNotEmptyField])
}
})
@@ -578,7 +579,7 @@ func TestSWFHandler_ErrorTypes(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
})
}
diff --git a/services/swf/handler_workflow_types_test.go b/services/swf/handler_workflow_types_test.go
index 73dbbc9e5b..7d8cd30ad6 100644
--- a/services/swf/handler_workflow_types_test.go
+++ b/services/swf/handler_workflow_types_test.go
@@ -1,12 +1,14 @@
package swf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_ListWorkflowTypes_Pagination(t *testing.T) {
@@ -251,7 +253,7 @@ func TestHandler_DeprecateWorkflowType(t *testing.T) {
if tt.wantType != "" {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
}
})
diff --git a/services/swf/lambda_tasks.go b/services/swf/lambda_tasks.go
index 7656935f08..1455d89539 100644
--- a/services/swf/lambda_tasks.go
+++ b/services/swf/lambda_tasks.go
@@ -2,11 +2,13 @@ package swf
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"slices"
"strconv"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -189,7 +191,7 @@ func lambdaFailureDetails(functionError string, payload []byte) (string, string)
ErrorType string `json:"errorType"`
}
- if json.Unmarshal(payload, &body) == nil && body.ErrorType != "" {
+ if json.Unmarshal(payload, &body, jsonx.V1()) == nil && body.ErrorType != "" {
return body.ErrorType, string(payload)
}
diff --git a/services/swf/models.go b/services/swf/models.go
index e5b2ec11b3..03f3de479b 100644
--- a/services/swf/models.go
+++ b/services/swf/models.go
@@ -1,9 +1,12 @@
package swf
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"maps"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -118,24 +121,24 @@ func (e HistoryEvent) MarshalJSON() ([]byte, error) {
}
maps.Copy(m, e.Attributes)
- return json.Marshal(m)
+ return json.Marshal(m, jsonx.V1())
}
// UnmarshalJSON restores a HistoryEvent from its JSON representation,
// capturing any unknown keys (the attributes block) into Attributes.
func (e *HistoryEvent) UnmarshalJSON(data []byte) error {
- var raw map[string]json.RawMessage
- if err := json.Unmarshal(data, &raw); err != nil {
+ var raw map[string]jsontext.Value
+ if err := json.Unmarshal(data, &raw, jsonx.V1()); err != nil {
return err
}
if v, ok := raw["eventType"]; ok {
- _ = json.Unmarshal(v, &e.EventType)
+ _ = json.Unmarshal(v, &e.EventType, jsonx.V1())
}
if v, ok := raw["eventId"]; ok {
- _ = json.Unmarshal(v, &e.EventID)
+ _ = json.Unmarshal(v, &e.EventID, jsonx.V1())
}
if v, ok := raw["eventTimestamp"]; ok {
- _ = json.Unmarshal(v, &e.Timestamp)
+ _ = json.Unmarshal(v, &e.Timestamp, jsonx.V1())
}
known := map[string]bool{
"eventType": true, "eventId": true, "eventTimestamp": true,
@@ -144,7 +147,7 @@ func (e *HistoryEvent) UnmarshalJSON(data []byte) error {
for k, v := range raw {
if !known[k] {
var x any
- _ = json.Unmarshal(v, &x)
+ _ = json.Unmarshal(v, &x, jsonx.V1())
e.Attributes[k] = x
}
}
diff --git a/services/swf/multi_region_test.go b/services/swf/multi_region_test.go
index 4a137d5c0d..f79de1272a 100644
--- a/services/swf/multi_region_test.go
+++ b/services/swf/multi_region_test.go
@@ -2,7 +2,8 @@ package swf_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/swf"
)
@@ -180,8 +182,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/swf/persistence.go b/services/swf/persistence.go
index 2b082cb974..b67b04e7ae 100644
--- a/services/swf/persistence.go
+++ b/services/swf/persistence.go
@@ -2,7 +2,7 @@ package swf
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -78,7 +78,7 @@ func buildPersistenceDTORegistry() persistenceDTOTables {
// activeDecisionTasks. History, ActivityQueues, and DecisionQueues stay plain
// maps by design (order-sensitive) and are written as-is.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
History map[string][]HistoryEvent `json:"history,omitempty"`
Tags map[string]map[string]string `json:"tags,omitempty"`
// ActivityQueues/DecisionQueues are the pending FIFO task lists, persisted in order.
@@ -284,7 +284,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// activeDecisionTasks; see store_setup.go's registerAllTables doc) from
// tables via the ephemeral DTO registry, factored out of Restore to keep
// Restore's own cognitive complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreDirtyTablesLocked(tables map[string]jsontext.Value) error {
dtos := buildPersistenceDTORegistry()
if err := dtos.registry.RestoreAll(tables); err != nil {
return fmt.Errorf("swf: restore snapshot DTO tables: %w", err)
diff --git a/services/swf/persistence_test.go b/services/swf/persistence_test.go
index dc6db5daf0..bf5453f4da 100644
--- a/services/swf/persistence_test.go
+++ b/services/swf/persistence_test.go
@@ -1,13 +1,14 @@
package swf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/swf"
)
@@ -375,7 +376,7 @@ func TestSnapshotRestore_ChildLinkAndOpenTimers(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var body map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
openCounts, ok := body["openCounts"].(map[string]any)
require.True(t, ok)
assert.InDelta(
diff --git a/services/textract/handler.go b/services/textract/handler.go
index d957c29a66..733abeac74 100644
--- a/services/textract/handler.go
+++ b/services/textract/handler.go
@@ -2,7 +2,7 @@ package textract
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -203,7 +204,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var req documentLocationInput
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
bucket := req.DocumentLocation.S3Object.Bucket
key := req.DocumentLocation.S3Object.Name
@@ -280,7 +281,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// Operation name constants for the ops referenced by
@@ -330,9 +331,6 @@ var opsWithoutValidationException = map[string]bool{ //nolint:gochecknoglobals /
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, action string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
var code string
var status int
@@ -345,7 +343,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, action string,
code, status = "InvalidS3ObjectException", http.StatusBadRequest
case errors.Is(err, ErrValidation), errors.Is(err, errInvalidRequest),
errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
if opsWithoutValidationException[action] {
code, status = "InvalidParameterException", http.StatusBadRequest
} else {
@@ -355,7 +353,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, action string,
code, status = "InternalServerError", http.StatusInternalServerError
}
- payload, marshalErr := json.Marshal(service.JSONErrorResponse{Type: code, Message: errMessage(err)})
+ payload, marshalErr := json.Marshal(service.JSONErrorResponse{Type: code, Message: errMessage(err)}, jsonx.V1())
if marshalErr != nil {
return c.String(http.StatusInternalServerError, "internal server error")
}
diff --git a/services/textract/handler_adapter_versions_test.go b/services/textract/handler_adapter_versions_test.go
index ac9c2983a1..7937dc6588 100644
--- a/services/textract/handler_adapter_versions_test.go
+++ b/services/textract/handler_adapter_versions_test.go
@@ -1,7 +1,7 @@
package textract_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/textract"
)
@@ -38,7 +39,7 @@ func TestHandler_AdapterVersionLifecycle(t *testing.T) {
require.Equal(t, tt.wantStatus, createAdapterRec.Code)
var createAdapterResp map[string]string
- require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &createAdapterResp))
+ require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &createAdapterResp, jsonx.V1()))
adapterID := createAdapterResp["AdapterId"]
// CreateAdapterVersion
@@ -59,7 +60,7 @@ func TestHandler_AdapterVersionLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, createVersionRec.Code)
var createVersionResp map[string]string
- require.NoError(t, json.Unmarshal(createVersionRec.Body.Bytes(), &createVersionResp))
+ require.NoError(t, json.Unmarshal(createVersionRec.Body.Bytes(), &createVersionResp, jsonx.V1()))
adapterVersion := createVersionResp["AdapterVersion"]
assert.NotEmpty(t, adapterVersion)
assert.Equal(t, adapterID, createVersionResp["AdapterId"])
@@ -73,7 +74,7 @@ func TestHandler_AdapterVersionLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, getVersionRec.Code)
var getVersionResp map[string]any
- require.NoError(t, json.Unmarshal(getVersionRec.Body.Bytes(), &getVersionResp))
+ require.NoError(t, json.Unmarshal(getVersionRec.Body.Bytes(), &getVersionResp, jsonx.V1()))
assert.Equal(t, adapterID, getVersionResp["AdapterId"])
assert.Equal(t, adapterVersion, getVersionResp["AdapterVersion"])
assert.Equal(t, "ACTIVE", getVersionResp["Status"])
@@ -152,7 +153,7 @@ func TestHandler_CreateAdapterVersion_RequiresDatasetAndOutputConfig(t *testing.
require.Equal(t, http.StatusOK, createAdapterRec.Code)
var createAdapterResp map[string]string
- require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &createAdapterResp))
+ require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &createAdapterResp, jsonx.V1()))
body := map[string]any{"AdapterId": createAdapterResp["AdapterId"]}
if tt.datasetConfig != nil {
@@ -229,7 +230,7 @@ func TestHandler_CreateAdapterVersion_DatasetConfig(t *testing.T) {
require.Equal(t, http.StatusOK, createAdapterRec.Code)
var createAdapterResp map[string]string
- require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &createAdapterResp))
+ require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &createAdapterResp, jsonx.V1()))
adapterID := createAdapterResp["AdapterId"]
createVersionRec := doTextractRequest(t, h, "CreateAdapterVersion", map[string]any{
@@ -248,7 +249,7 @@ func TestHandler_CreateAdapterVersion_DatasetConfig(t *testing.T) {
require.Equal(t, http.StatusOK, createVersionRec.Code)
var createVersionResp map[string]string
- require.NoError(t, json.Unmarshal(createVersionRec.Body.Bytes(), &createVersionResp))
+ require.NoError(t, json.Unmarshal(createVersionRec.Body.Bytes(), &createVersionResp, jsonx.V1()))
adapterVersion := createVersionResp["AdapterVersion"]
getVersionRec := doTextractRequest(t, h, "GetAdapterVersion", map[string]any{
@@ -258,7 +259,7 @@ func TestHandler_CreateAdapterVersion_DatasetConfig(t *testing.T) {
require.Equal(t, http.StatusOK, getVersionRec.Code)
var getVersionResp map[string]any
- require.NoError(t, json.Unmarshal(getVersionRec.Body.Bytes(), &getVersionResp))
+ require.NoError(t, json.Unmarshal(getVersionRec.Body.Bytes(), &getVersionResp, jsonx.V1()))
datasetConfig, ok := getVersionResp["DatasetConfig"].(map[string]any)
require.True(t, ok, "GetAdapterVersion should include DatasetConfig")
@@ -287,7 +288,7 @@ func TestHandler_AdapterVersion_InProgressThenActive(t *testing.T) {
require.Equal(t, http.StatusOK, createAdapterRec.Code)
var createAdapterResp map[string]string
- require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &createAdapterResp))
+ require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &createAdapterResp, jsonx.V1()))
adapterID := createAdapterResp["AdapterId"]
createVersionRec := doTextractRequest(t, h, "CreateAdapterVersion", map[string]any{
@@ -305,7 +306,7 @@ func TestHandler_AdapterVersion_InProgressThenActive(t *testing.T) {
require.Equal(t, http.StatusOK, createVersionRec.Code)
var createVersionResp map[string]string
- require.NoError(t, json.Unmarshal(createVersionRec.Body.Bytes(), &createVersionResp))
+ require.NoError(t, json.Unmarshal(createVersionRec.Body.Bytes(), &createVersionResp, jsonx.V1()))
adapterVersion := createVersionResp["AdapterVersion"]
// Immediately check: should be CREATION_IN_PROGRESS.
@@ -316,7 +317,7 @@ func TestHandler_AdapterVersion_InProgressThenActive(t *testing.T) {
require.Equal(t, http.StatusOK, getRec1.Code)
var getResp1 map[string]any
- require.NoError(t, json.Unmarshal(getRec1.Body.Bytes(), &getResp1))
+ require.NoError(t, json.Unmarshal(getRec1.Body.Bytes(), &getResp1, jsonx.V1()))
assert.Equal(t, "CREATION_IN_PROGRESS", getResp1["Status"])
// After delay, should be ACTIVE.
@@ -330,7 +331,7 @@ func TestHandler_AdapterVersion_InProgressThenActive(t *testing.T) {
require.Equal(t, http.StatusOK, getRec2.Code)
var getResp2 map[string]any
- require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getResp2))
+ require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getResp2, jsonx.V1()))
assert.Equal(t, "ACTIVE", getResp2["Status"])
})
}
@@ -352,7 +353,7 @@ func TestHandler_AdapterVersion_EvaluationMetrics(t *testing.T) {
require.Equal(t, http.StatusOK, createAdapterRec.Code)
var createAdapterResp map[string]string
- require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &createAdapterResp))
+ require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &createAdapterResp, jsonx.V1()))
adapterID := createAdapterResp["AdapterId"]
createVersionRec := doTextractRequest(t, h, "CreateAdapterVersion", map[string]any{
@@ -370,7 +371,7 @@ func TestHandler_AdapterVersion_EvaluationMetrics(t *testing.T) {
require.Equal(t, http.StatusOK, createVersionRec.Code)
var createVersionResp map[string]string
- require.NoError(t, json.Unmarshal(createVersionRec.Body.Bytes(), &createVersionResp))
+ require.NoError(t, json.Unmarshal(createVersionRec.Body.Bytes(), &createVersionResp, jsonx.V1()))
adapterVersion := createVersionResp["AdapterVersion"]
getVersionRec := doTextractRequest(t, h, "GetAdapterVersion", map[string]any{
@@ -380,7 +381,7 @@ func TestHandler_AdapterVersion_EvaluationMetrics(t *testing.T) {
require.Equal(t, http.StatusOK, getVersionRec.Code)
var getVersionResp map[string]any
- require.NoError(t, json.Unmarshal(getVersionRec.Body.Bytes(), &getVersionResp))
+ require.NoError(t, json.Unmarshal(getVersionRec.Body.Bytes(), &getVersionResp, jsonx.V1()))
evalMetricsList, ok := getVersionResp["EvaluationMetrics"].([]any)
require.True(t, ok, "GetAdapterVersion should include EvaluationMetrics as a list")
@@ -417,7 +418,7 @@ func TestHandler_DeleteAdapterVersion_GetReturnsErrorAfterDelete(t *testing.T) {
})
require.Equal(t, http.StatusOK, createAdapterRec.Code)
var adapterResp map[string]string
- require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &adapterResp))
+ require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &adapterResp, jsonx.V1()))
adapterID := adapterResp["AdapterId"]
createVerRec := doTextractRequest(t, h, "CreateAdapterVersion", map[string]any{
@@ -434,7 +435,7 @@ func TestHandler_DeleteAdapterVersion_GetReturnsErrorAfterDelete(t *testing.T) {
})
require.Equal(t, http.StatusOK, createVerRec.Code)
var verResp map[string]string
- require.NoError(t, json.Unmarshal(createVerRec.Body.Bytes(), &verResp))
+ require.NoError(t, json.Unmarshal(createVerRec.Body.Bytes(), &verResp, jsonx.V1()))
version := verResp["AdapterVersion"]
require.NotEmpty(t, version)
@@ -456,7 +457,7 @@ func TestHandler_DeleteAdapterVersion_GetReturnsErrorAfterDelete(t *testing.T) {
})
assert.Equal(t, http.StatusBadRequest, getAfterRec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(getAfterRec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(getAfterRec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp["__type"],
"deleted version must return ResourceNotFoundException")
}
@@ -472,7 +473,7 @@ func TestHandler_ListAdapterVersions_HappyPath(t *testing.T) {
"FeatureTypes": []string{"FORMS"},
})
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
adapterID := createResp["AdapterId"]
for range 3 {
@@ -496,7 +497,7 @@ func TestHandler_ListAdapterVersions_HappyPath(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
versions, ok := resp["AdapterVersions"].([]any)
assert.True(t, ok)
assert.Len(t, versions, 3)
@@ -541,7 +542,7 @@ func TestHandler_ListAdapterVersions_SummaryShape(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
adapterID := createResp["AdapterId"]
verRec := doTextractRequest(t, h, "CreateAdapterVersion", map[string]any{
@@ -565,7 +566,7 @@ func TestHandler_ListAdapterVersions_SummaryShape(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
versions, ok := resp["AdapterVersions"].([]any)
require.True(t, ok)
@@ -604,7 +605,7 @@ func TestHandler_AdapterVersion_CreationTimeIsEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, createAdapterRec.Code)
var createAdapterResp map[string]string
- require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &createAdapterResp))
+ require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &createAdapterResp, jsonx.V1()))
adapterID := createAdapterResp["AdapterId"]
createVersionRec := doTextractRequest(t, h, "CreateAdapterVersion", map[string]any{
@@ -622,7 +623,7 @@ func TestHandler_AdapterVersion_CreationTimeIsEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, createVersionRec.Code)
var createVersionResp map[string]string
- require.NoError(t, json.Unmarshal(createVersionRec.Body.Bytes(), &createVersionResp))
+ require.NoError(t, json.Unmarshal(createVersionRec.Body.Bytes(), &createVersionResp, jsonx.V1()))
adapterVersion := createVersionResp["AdapterVersion"]
getRec := doTextractRequest(t, h, "GetAdapterVersion", map[string]any{
@@ -632,7 +633,7 @@ func TestHandler_AdapterVersion_CreationTimeIsEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
ct, ok := getResp["CreationTime"].(float64)
assert.True(t, ok, "GetAdapterVersion CreationTime must be a JSON number")
assert.Positive(t, ct)
@@ -641,7 +642,7 @@ func TestHandler_AdapterVersion_CreationTimeIsEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
versions, ok := listResp["AdapterVersions"].([]any)
require.True(t, ok)
require.NotEmpty(t, versions)
@@ -670,7 +671,7 @@ func TestHandler_ListAdapterVersions_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
adapterID := createResp["AdapterId"]
for range 3 {
@@ -699,7 +700,7 @@ func TestHandler_ListAdapterVersions_Pagination(t *testing.T) {
NextToken string `json:"NextToken"`
AdapterVersions []map[string]any `json:"AdapterVersions"`
}
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
assert.Len(t, resp1.AdapterVersions, 2)
require.NotEmpty(t, resp1.NextToken, "first page must return a NextToken")
@@ -713,7 +714,7 @@ func TestHandler_ListAdapterVersions_Pagination(t *testing.T) {
NextToken string `json:"NextToken"`
AdapterVersions []map[string]any `json:"AdapterVersions"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Len(t, resp2.AdapterVersions, 1, "remaining 1 version on the second page")
assert.Empty(t, resp2.NextToken, "no more pages")
}
diff --git a/services/textract/handler_adapters_test.go b/services/textract/handler_adapters_test.go
index 3aca1cc31e..d1bdd937ec 100644
--- a/services/textract/handler_adapters_test.go
+++ b/services/textract/handler_adapters_test.go
@@ -1,7 +1,7 @@
package textract_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/textract"
)
@@ -55,7 +56,7 @@ func TestHandler_AdapterLifecycle(t *testing.T) {
}
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
adapterID := createResp["AdapterId"]
assert.NotEmpty(t, adapterID)
@@ -65,7 +66,7 @@ func TestHandler_AdapterLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, tt.adapterName, getResp["AdapterName"])
assert.Equal(t, tt.description, getResp["Description"])
@@ -114,7 +115,7 @@ func TestHandler_GetAdapter_IncludesTags(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
getRec := doTextractRequest(t, h, "GetAdapter", map[string]any{
"AdapterId": createResp["AdapterId"],
@@ -122,7 +123,7 @@ func TestHandler_GetAdapter_IncludesTags(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
tags, hasTags := getResp["Tags"].(map[string]any)
assert.True(t, hasTags, "GetAdapter must include Tags")
@@ -151,7 +152,7 @@ func TestHandler_DeleteAdapter_CascadesVersions(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.Code)
var cr map[string]string
- require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &cr))
+ require.NoError(t, json.Unmarshal(createResp.Body.Bytes(), &cr, jsonx.V1()))
adapterID := cr["AdapterId"]
// Create a version
@@ -170,7 +171,7 @@ func TestHandler_DeleteAdapter_CascadesVersions(t *testing.T) {
require.Equal(t, http.StatusOK, versionResp.Code)
var vr map[string]string
- require.NoError(t, json.Unmarshal(versionResp.Body.Bytes(), &vr))
+ require.NoError(t, json.Unmarshal(versionResp.Body.Bytes(), &vr, jsonx.V1()))
adapterVersion := vr["AdapterVersion"]
// Delete adapter should cascade to versions
@@ -217,13 +218,13 @@ func TestHandler_CreateAdapter_ClientRequestTokenDedup(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]string
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
rec2 := doTextractRequest(t, h, "CreateAdapter", body)
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]string
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Equal(t, resp1["AdapterId"], resp2["AdapterId"], "same token should return same AdapterId")
}
@@ -246,7 +247,7 @@ func TestHandler_UpdateAdapter_PreservesNameAndFeatureTypes(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
adapterID := createResp["AdapterId"]
require.NotEmpty(t, adapterID)
@@ -258,7 +259,7 @@ func TestHandler_UpdateAdapter_PreservesNameAndFeatureTypes(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Equal(t, "immutable-name", updateResp["AdapterName"],
"AdapterName must be unchanged after UpdateAdapter")
@@ -375,7 +376,7 @@ func TestHandler_UpdateAdapter_HappyPath(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
adapterID := createResp["AdapterId"]
updateRec := doTextractRequest(t, h, "UpdateAdapter", map[string]any{
@@ -386,7 +387,7 @@ func TestHandler_UpdateAdapter_HappyPath(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Equal(t, "updated description", updateResp["Description"])
assert.Equal(t, "ENABLED", updateResp["AutoUpdate"])
@@ -406,7 +407,7 @@ func TestHandler_UpdateAdapter_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp["__type"])
}
@@ -468,7 +469,7 @@ func TestHandler_UpdateAdapter_PartialUpdateSemantics(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
adapterID := createResp["AdapterId"]
body := map[string]any{"AdapterId": adapterID}
@@ -482,7 +483,7 @@ func TestHandler_UpdateAdapter_PartialUpdateSemantics(t *testing.T) {
}
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.Equal(t, tt.wantName, updateResp["AdapterName"])
assert.Equal(t, tt.wantDesc, updateResp["Description"])
})
@@ -507,7 +508,7 @@ func TestHandler_ListAdapters_HappyPath(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
adapters, ok := resp["Adapters"].([]any)
assert.True(t, ok)
assert.Len(t, adapters, 3)
@@ -533,7 +534,7 @@ func TestHandler_ListAdapters_SummaryShape(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
adapters, ok := resp["Adapters"].([]any)
require.True(t, ok)
@@ -569,14 +570,14 @@ func TestHandler_Adapter_CreationTimeIsEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
adapterID := createResp["AdapterId"]
getRec := doTextractRequest(t, h, "GetAdapter", map[string]any{"AdapterId": adapterID})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
ct, ok := getResp["CreationTime"].(float64)
assert.True(t, ok, "GetAdapter CreationTime must be a JSON number")
assert.Positive(t, ct)
@@ -588,7 +589,7 @@ func TestHandler_Adapter_CreationTimeIsEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
uct, ok := updateResp["CreationTime"].(float64)
assert.True(t, ok, "UpdateAdapter CreationTime must be a JSON number")
assert.Positive(t, uct)
@@ -597,7 +598,7 @@ func TestHandler_Adapter_CreationTimeIsEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
adapters, ok := listResp["Adapters"].([]any)
require.True(t, ok)
require.NotEmpty(t, adapters)
@@ -633,7 +634,7 @@ func TestHandler_ListAdapters_Pagination(t *testing.T) {
NextToken string `json:"NextToken"`
Adapters []map[string]any `json:"Adapters"`
}
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
assert.Len(t, resp1.Adapters, 2)
require.NotEmpty(t, resp1.NextToken, "first page must return a NextToken")
@@ -644,7 +645,7 @@ func TestHandler_ListAdapters_Pagination(t *testing.T) {
NextToken string `json:"NextToken"`
Adapters []map[string]any `json:"Adapters"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Len(t, resp2.Adapters, 1, "remaining 1 adapter on the second page")
assert.Empty(t, resp2.NextToken, "no more pages")
}
diff --git a/services/textract/handler_analyze_document_config_test.go b/services/textract/handler_analyze_document_config_test.go
index 27a015b638..2b8c757eef 100644
--- a/services/textract/handler_analyze_document_config_test.go
+++ b/services/textract/handler_analyze_document_config_test.go
@@ -1,7 +1,7 @@
package textract_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/textract"
)
@@ -103,7 +104,7 @@ func TestAnalyzeDocument_AdaptersConfig(t *testing.T) {
}
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, tt.wantType, errResp["__type"])
})
}
@@ -159,7 +160,7 @@ func TestAnalyzeDocument_HumanLoopConfig(t *testing.T) {
var out struct {
HumanLoopActivationOutput *struct{} `json:"HumanLoopActivationOutput"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Nil(
t,
out.HumanLoopActivationOutput,
@@ -189,7 +190,7 @@ func newTestHandlerT(t *testing.T) *testHandlerT {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
versionRec := doTextractRequest(t, h, "CreateAdapterVersion", map[string]any{
"AdapterId": createResp["AdapterId"],
@@ -206,7 +207,7 @@ func newTestHandlerT(t *testing.T) *testHandlerT {
require.Equal(t, http.StatusOK, versionRec.Code)
var versionResp map[string]string
- require.NoError(t, json.Unmarshal(versionRec.Body.Bytes(), &versionResp))
+ require.NoError(t, json.Unmarshal(versionRec.Body.Bytes(), &versionResp, jsonx.V1()))
return &testHandlerT{
h: h,
diff --git a/services/textract/handler_document_analysis_test.go b/services/textract/handler_document_analysis_test.go
index b4be85e43d..b185d2c0b5 100644
--- a/services/textract/handler_document_analysis_test.go
+++ b/services/textract/handler_document_analysis_test.go
@@ -2,7 +2,7 @@ package textract_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/textract"
)
@@ -27,7 +28,7 @@ func startDocumentAnalysisJob(t *testing.T, h *textract.Handler) string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
jobID := resp["JobId"]
require.NotEmpty(t, jobID)
@@ -46,7 +47,7 @@ func startTextDetectionJob(t *testing.T, h *textract.Handler) string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
jobID := resp["JobId"]
require.NotEmpty(t, jobID)
@@ -58,7 +59,7 @@ func blocksOfType(t *testing.T, body []byte, blockType string) []map[string]any
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
raw, ok := resp["Blocks"].([]any)
require.True(t, ok, "response should have Blocks array")
@@ -121,7 +122,7 @@ func TestHandler_AnalyzeDocument(t *testing.T) {
if tt.wantBlocks {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
blocks, ok := resp["Blocks"].([]any)
assert.True(t, ok)
assert.NotEmpty(t, blocks)
@@ -164,7 +165,7 @@ func TestHandler_StartAndGetDocumentAnalysis(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
assert.NotEmpty(t, jobID)
@@ -174,7 +175,7 @@ func TestHandler_StartAndGetDocumentAnalysis(t *testing.T) {
assert.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", getResp["JobStatus"])
blocks, ok := getResp["Blocks"].([]any)
assert.True(t, ok)
@@ -235,7 +236,7 @@ func TestHandler_GetDocumentAnalysis_RejectsTextDetectionJobID(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidJobIdException", resp["__type"],
"cross-type access must return InvalidJobIdException, not a different error")
}
@@ -475,7 +476,7 @@ func TestHandler_AnalyzeDocument_ModelVersion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "1.0", resp["AnalyzeDocumentModelVersion"])
}
@@ -494,7 +495,7 @@ func TestHandler_AnalyzeDocument_DocumentMetadataPages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
meta, ok := m["DocumentMetadata"].(map[string]any)
require.True(t, ok, "AnalyzeDocument response must have DocumentMetadata")
pages, ok2 := meta["Pages"].(float64)
@@ -518,7 +519,7 @@ func TestHandler_AnalyzeDocument_BytesInput(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
blocks, ok := m["Blocks"].([]any)
assert.True(t, ok && len(blocks) > 0, "AnalyzeDocument with Bytes must return Blocks")
}
@@ -539,7 +540,7 @@ func TestHandler_GetDocumentAnalysis_PaginationNextToken(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
getRec := doTextractRequest(t, h, "GetDocumentAnalysis", map[string]any{
@@ -549,7 +550,7 @@ func TestHandler_GetDocumentAnalysis_PaginationNextToken(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
blocks, _ := getResp["Blocks"].([]any)
assert.Len(t, blocks, 2)
@@ -574,14 +575,14 @@ func TestHandler_GetDocumentAnalysis_BlocksHaveGeometry(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
getRec := doTextractRequest(t, h, "GetDocumentAnalysis", map[string]any{"JobId": jobID})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
blocks, _ := getResp["Blocks"].([]any)
require.NotEmpty(t, blocks)
@@ -619,14 +620,14 @@ func TestHandler_GetDocumentAnalysis_ModelVersion(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
getRec := doTextractRequest(t, h, "GetDocumentAnalysis", map[string]string{"JobId": jobID})
require.Equal(t, http.StatusOK, getRec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &m, jsonx.V1()))
ver, ok := m["AnalyzeDocumentModelVersion"].(string)
assert.True(t, ok, "GetDocumentAnalysis must have AnalyzeDocumentModelVersion")
assert.Equal(t, "1.0", ver)
@@ -647,14 +648,14 @@ func TestHandler_GetDocumentAnalysis_JobStatusSucceeded(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
getRec := doTextractRequest(t, h, "GetDocumentAnalysis", map[string]string{"JobId": jobID})
require.Equal(t, http.StatusOK, getRec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &m, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", m["JobStatus"], "synchronous backend job must SUCCEED immediately")
}
@@ -679,7 +680,7 @@ func TestHandler_StartDocumentAnalysis_AsyncInProgressThenSucceeded(t *testing.T
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
// Immediately after start, job should be IN_PROGRESS.
@@ -687,7 +688,7 @@ func TestHandler_StartDocumentAnalysis_AsyncInProgressThenSucceeded(t *testing.T
require.Equal(t, http.StatusOK, getRec1.Code)
var getResp1 map[string]any
- require.NoError(t, json.Unmarshal(getRec1.Body.Bytes(), &getResp1))
+ require.NoError(t, json.Unmarshal(getRec1.Body.Bytes(), &getResp1, jsonx.V1()))
assert.Equal(t, "IN_PROGRESS", getResp1["JobStatus"])
// After delay, job should be SUCCEEDED.
@@ -698,7 +699,7 @@ func TestHandler_StartDocumentAnalysis_AsyncInProgressThenSucceeded(t *testing.T
require.Equal(t, http.StatusOK, getRec2.Code)
var getResp2 map[string]any
- require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getResp2))
+ require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getResp2, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", getResp2["JobStatus"])
})
}
@@ -725,7 +726,7 @@ func TestHandler_StartDocumentAnalysis_AsyncInitialStatusInProgress(t *testing.T
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
require.NotEmpty(t, jobID)
@@ -734,7 +735,7 @@ func TestHandler_StartDocumentAnalysis_AsyncInitialStatusInProgress(t *testing.T
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "IN_PROGRESS", getResp["JobStatus"],
"job must be IN_PROGRESS immediately after Start when async delay is non-zero")
}
@@ -758,14 +759,14 @@ func TestHandler_StartDocumentAnalysis_ClientRequestTokenIdempotency(t *testing.
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]string
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
jobID1 := resp1["JobId"]
rec2 := doTextractRequest(t, h, "StartDocumentAnalysis", startBody)
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]string
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
jobID2 := resp2["JobId"]
assert.Equal(t, jobID1, jobID2, "same ClientRequestToken should return the same JobId")
@@ -786,7 +787,7 @@ func TestHandler_StartDocumentAnalysis_JobIdNonEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
jobID, ok := m["JobId"].(string)
assert.True(t, ok, "StartDocumentAnalysis response must have string JobId")
assert.NotEmpty(t, jobID, "JobId must not be empty")
@@ -879,7 +880,7 @@ func TestHandler_AnalyzeDocumentAndStart_ValidationErrorsUseInvalidParameterExce
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidParameterException", errResp["__type"])
})
}
diff --git a/services/textract/handler_document_detection_test.go b/services/textract/handler_document_detection_test.go
index c754be8aec..eff4ae4bd1 100644
--- a/services/textract/handler_document_detection_test.go
+++ b/services/textract/handler_document_detection_test.go
@@ -1,13 +1,15 @@
package textract_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_DetectDocumentText(t *testing.T) {
@@ -76,7 +78,7 @@ func TestHandler_StartAndGetDocumentTextDetection(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
assert.NotEmpty(t, jobID)
@@ -85,7 +87,7 @@ func TestHandler_StartAndGetDocumentTextDetection(t *testing.T) {
assert.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", getResp["JobStatus"])
})
}
@@ -143,7 +145,7 @@ func TestHandler_GetDocumentTextDetection_RejectsAnalysisJobID(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidJobIdException", resp["__type"],
"cross-type access must return InvalidJobIdException")
}
@@ -192,7 +194,7 @@ func TestHandler_DetectDocumentText_ModelVersion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "1.0", resp["DetectDocumentTextModelVersion"])
}
@@ -212,7 +214,7 @@ func TestHandler_GetDocumentTextDetection_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
// Get first page with MaxResults=3.
@@ -223,7 +225,7 @@ func TestHandler_GetDocumentTextDetection_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, getRec1.Code)
var getResp1 map[string]any
- require.NoError(t, json.Unmarshal(getRec1.Body.Bytes(), &getResp1))
+ require.NoError(t, json.Unmarshal(getRec1.Body.Bytes(), &getResp1, jsonx.V1()))
blocks1, _ := getResp1["Blocks"].([]any)
assert.Len(t, blocks1, 3, "first page should have 3 blocks")
@@ -240,7 +242,7 @@ func TestHandler_GetDocumentTextDetection_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, getRec2.Code)
var getResp2 map[string]any
- require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getResp2))
+ require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &getResp2, jsonx.V1()))
blocks2, _ := getResp2["Blocks"].([]any)
assert.NotEmpty(t, blocks2, "second page should have remaining blocks")
@@ -260,7 +262,7 @@ func TestHandler_DetectDocumentText_BlockIDsAreUUIDs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
blocks, ok := m["Blocks"].([]any)
require.True(t, ok)
require.NotEmpty(t, blocks)
@@ -291,7 +293,7 @@ func TestHandler_DetectDocumentText_ConfidenceInRange(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
blocks, _ := m["Blocks"].([]any)
require.NotEmpty(t, blocks)
@@ -325,7 +327,7 @@ func TestHandler_DetectDocumentText_WordHasTextType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
blocks, _ := m["Blocks"].([]any)
require.NotEmpty(t, blocks)
@@ -361,7 +363,7 @@ func TestHandler_DetectDocumentText_PageIsSet(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
blocks, _ := m["Blocks"].([]any)
require.NotEmpty(t, blocks)
@@ -392,7 +394,7 @@ func TestHandler_DetectDocumentText_GeometryBoundingBoxValid(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
blocks, _ := m["Blocks"].([]any)
require.NotEmpty(t, blocks)
@@ -439,7 +441,7 @@ func TestHandler_DetectDocumentText_DocumentMetadataPages(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
meta, ok := m["DocumentMetadata"].(map[string]any)
require.True(t, ok, "response must have DocumentMetadata")
pages, ok2 := meta["Pages"].(float64)
@@ -462,7 +464,7 @@ func TestHandler_DetectDocumentText_BytesInput(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
blocks, ok := m["Blocks"].([]any)
assert.True(t, ok && len(blocks) > 0, "DetectDocumentText with Bytes must return Blocks")
}
@@ -481,14 +483,14 @@ func TestHandler_GetDocumentTextDetection_ModelVersion(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
getRec := doTextractRequest(t, h, "GetDocumentTextDetection", map[string]string{"JobId": jobID})
require.Equal(t, http.StatusOK, getRec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &m, jsonx.V1()))
ver, ok := m["DetectDocumentTextModelVersion"].(string)
assert.True(t, ok, "GetDocumentTextDetection must have DetectDocumentTextModelVersion")
assert.Equal(t, "1.0", ver)
@@ -511,7 +513,7 @@ func TestHandler_StartDocumentTextDetection_ClientRequestTokenIdempotency(t *tes
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]string
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
jobID1 := resp1["JobId"]
require.NotEmpty(t, jobID1)
@@ -519,7 +521,7 @@ func TestHandler_StartDocumentTextDetection_ClientRequestTokenIdempotency(t *tes
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]string
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Equal(t, jobID1, resp2["JobId"], "same ClientRequestToken must return same JobId")
}
diff --git a/services/textract/handler_expense_analysis_test.go b/services/textract/handler_expense_analysis_test.go
index fa360a6412..38cd0e58b4 100644
--- a/services/textract/handler_expense_analysis_test.go
+++ b/services/textract/handler_expense_analysis_test.go
@@ -1,13 +1,14 @@
package textract_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/textract"
)
@@ -57,7 +58,7 @@ func TestHandler_AnalyzeExpense(t *testing.T) {
if tt.wantDocs {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
docs, ok := resp["ExpenseDocuments"].([]any)
assert.True(t, ok)
assert.NotEmpty(t, docs)
@@ -110,7 +111,7 @@ func TestHandler_StartExpenseAnalysis_ClientRequestTokenIdempotency(t *testing.T
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]string
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
jobID1 := resp1["JobId"]
require.NotEmpty(t, jobID1)
@@ -118,7 +119,7 @@ func TestHandler_StartExpenseAnalysis_ClientRequestTokenIdempotency(t *testing.T
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]string
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Equal(t, jobID1, resp2["JobId"], "same ClientRequestToken must return same JobId")
assert.Equal(t, 1, textract.ExpenseJobCount(h.Backend.(*textract.InMemoryBackend)))
@@ -140,7 +141,7 @@ func TestHandler_StartExpenseAnalysis_HappyPath(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
require.NotEmpty(t, jobID)
@@ -148,7 +149,7 @@ func TestHandler_StartExpenseAnalysis_HappyPath(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", getResp["JobStatus"])
assert.Equal(t, "1.0", getResp["AnalyzeExpenseModelVersion"])
docs, ok := getResp["ExpenseDocuments"].([]any)
@@ -185,7 +186,7 @@ func TestHandler_AnalyzeExpense_SummaryFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
docs, ok := resp["ExpenseDocuments"].([]any)
require.True(t, ok)
@@ -232,7 +233,7 @@ func TestHandler_AnalyzeExpense_LineItemGroups(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
docs, _ := resp["ExpenseDocuments"].([]any)
require.NotEmpty(t, docs)
@@ -264,7 +265,7 @@ func TestHandler_AnalyzeExpense_ExpenseIndex(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
docs, ok := m["ExpenseDocuments"].([]any)
require.True(t, ok)
require.NotEmpty(t, docs)
@@ -290,7 +291,7 @@ func TestHandler_GetExpenseAnalysis_ModelVersion(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
require.NotEmpty(t, jobID)
@@ -298,7 +299,7 @@ func TestHandler_GetExpenseAnalysis_ModelVersion(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &m, jsonx.V1()))
ver, ok := m["AnalyzeExpenseModelVersion"].(string)
assert.True(t, ok, "GetExpenseAnalysis must have AnalyzeExpenseModelVersion")
assert.Equal(t, "1.0", ver)
@@ -344,14 +345,14 @@ func TestHandler_GetExpenseAnalysis_RejectsDocumentJobID(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
getRec := doTextractRequest(t, h, "GetExpenseAnalysis", map[string]any{"JobId": jobID})
assert.Equal(t, http.StatusBadRequest, getRec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidJobIdException", errResp["__type"],
"expense analysis must not accept a document job ID")
})
@@ -376,7 +377,7 @@ func TestHandler_AnalyzeExpense_CurrencyAndTypeWireShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
docs, ok := resp["ExpenseDocuments"].([]any)
require.True(t, ok)
@@ -453,7 +454,7 @@ func TestHandler_GetExpenseAnalysis_Pagination(t *testing.T) {
NextToken string `json:"NextToken"`
ExpenseDocuments []map[string]any `json:"ExpenseDocuments"`
}
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
assert.Len(t, resp1.ExpenseDocuments, 2)
require.NotEmpty(t, resp1.NextToken, "first page must return a NextToken")
@@ -467,7 +468,7 @@ func TestHandler_GetExpenseAnalysis_Pagination(t *testing.T) {
NextToken string `json:"NextToken"`
ExpenseDocuments []map[string]any `json:"ExpenseDocuments"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Len(t, resp2.ExpenseDocuments, 3, "remaining 3 documents on the second page")
assert.Empty(t, resp2.NextToken, "no more pages")
}
diff --git a/services/textract/handler_id_analysis_test.go b/services/textract/handler_id_analysis_test.go
index f939855920..717705bbd5 100644
--- a/services/textract/handler_id_analysis_test.go
+++ b/services/textract/handler_id_analysis_test.go
@@ -1,12 +1,14 @@
package textract_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_AnalyzeID(t *testing.T) {
@@ -57,7 +59,7 @@ func TestHandler_AnalyzeID(t *testing.T) {
if !tt.wantErr {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
docs, ok := resp["IdentityDocuments"].([]any)
assert.True(t, ok)
assert.Len(t, docs, 2)
@@ -83,7 +85,7 @@ func TestHandler_AnalyzeID_IdentityDocumentFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
docs, ok := resp["IdentityDocuments"].([]any)
require.True(t, ok)
@@ -133,7 +135,7 @@ func TestHandler_AnalyzeID_NormalizedValueDate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
docs, _ := resp["IdentityDocuments"].([]any)
doc, _ := docs[0].(map[string]any)
@@ -180,7 +182,7 @@ func TestHandler_AnalyzeID_DocumentIndex(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
docs, ok := m["IdentityDocuments"].([]any)
require.True(t, ok)
require.NotEmpty(t, docs)
@@ -206,7 +208,7 @@ func TestHandler_AnalyzeID_ModelVersion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
ver, ok := m["AnalyzeIDModelVersion"].(string)
assert.True(t, ok, "AnalyzeID response must have AnalyzeIDModelVersion")
assert.Equal(t, "1.0", ver)
@@ -227,7 +229,7 @@ func TestHandler_AnalyzeID_MultiPageDocumentIndex(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
docs, ok := m["IdentityDocuments"].([]any)
require.True(t, ok)
require.Len(t, docs, 2, "two DocumentPages should yield two IdentityDocuments")
@@ -277,7 +279,7 @@ func TestHandler_AnalyzeID_DocumentMetadataPagesMatchesInputCount(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
meta, _ := resp["DocumentMetadata"].(map[string]any)
gotPages, _ := meta["Pages"].(float64)
assert.InDelta(t, float64(tc.pageCount), gotPages, 0,
@@ -310,7 +312,7 @@ func TestHandler_AnalyzeID_NoFabricatedGeometry(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
docs, ok := resp["IdentityDocuments"].([]any)
require.True(t, ok)
diff --git a/services/textract/handler_lending_analysis_test.go b/services/textract/handler_lending_analysis_test.go
index 32260ae93e..703b712df7 100644
--- a/services/textract/handler_lending_analysis_test.go
+++ b/services/textract/handler_lending_analysis_test.go
@@ -1,13 +1,14 @@
package textract_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/textract"
)
@@ -55,7 +56,7 @@ func TestHandler_StartLendingAnalysis_ClientRequestTokenIdempotency(t *testing.T
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]string
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
jobID1 := resp1["JobId"]
require.NotEmpty(t, jobID1)
@@ -63,7 +64,7 @@ func TestHandler_StartLendingAnalysis_ClientRequestTokenIdempotency(t *testing.T
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]string
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Equal(t, jobID1, resp2["JobId"], "same ClientRequestToken must return same JobId")
assert.Equal(t, 1, textract.LendingJobCount(h.Backend.(*textract.InMemoryBackend)))
@@ -83,7 +84,7 @@ func TestHandler_GetLendingAnalysisSummary_IncludesWarnings(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
textract.AddLendingJobInternal(b, &textract.LendingJob{
@@ -103,7 +104,7 @@ func TestHandler_GetLendingAnalysisSummary_IncludesWarnings(t *testing.T) {
Pages []int `json:"Pages"`
} `json:"Warnings"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Warnings, 1)
assert.Equal(t, "InvalidPageException", resp.Warnings[0].ErrorCode)
}
@@ -123,14 +124,14 @@ func TestHandler_GetLendingAnalysis_ExpandedLendingResult(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
getRec := doTextractRequest(t, h, "GetLendingAnalysis", map[string]any{"JobId": jobID})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", getResp["JobStatus"])
results, ok := getResp["Results"].([]any)
@@ -163,14 +164,14 @@ func TestHandler_GetLendingAnalysisSummary_PopulatedSummary(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
getRec := doTextractRequest(t, h, "GetLendingAnalysisSummary", map[string]any{"JobId": jobID})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", getResp["JobStatus"])
summary, ok := getResp["Summary"].(map[string]any)
@@ -195,14 +196,14 @@ func TestHandler_GetLendingAnalysis_ModelVersion(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
getRec := doTextractRequest(t, h, "GetLendingAnalysis", map[string]string{"JobId": jobID})
require.Equal(t, http.StatusOK, getRec.Code)
var m map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &m, jsonx.V1()))
ver, ok := m["AnalyzeLendingModelVersion"].(string)
assert.True(t, ok, "GetLendingAnalysis must have AnalyzeLendingModelVersion")
assert.Equal(t, "1.0", ver)
@@ -224,7 +225,7 @@ func TestHandler_StartLendingAnalysis_HappyPath(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
require.NotEmpty(t, jobID)
@@ -232,7 +233,7 @@ func TestHandler_StartLendingAnalysis_HappyPath(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", getResp["JobStatus"])
}
@@ -264,14 +265,14 @@ func TestHandler_GetLendingAnalysisSummary_HappyPath(t *testing.T) {
})
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
rec := doTextractRequest(t, h, "GetLendingAnalysisSummary", map[string]string{"JobId": jobID})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "SUCCEEDED", resp["JobStatus"])
assert.Equal(t, "1.0", resp["AnalyzeLendingModelVersion"])
}
@@ -285,7 +286,7 @@ func TestHandler_GetLendingAnalysisSummary_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidJobIdException", errResp["__type"])
}
@@ -308,14 +309,14 @@ func TestHandler_GetLendingAnalysis_LendingFieldWireShape(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
getRec := doTextractRequest(t, h, "GetLendingAnalysis", map[string]any{"JobId": jobID})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
results, ok := getResp["Results"].([]any)
require.True(t, ok)
@@ -372,14 +373,14 @@ func TestHandler_GetLendingAnalysis_PageClassificationUsesPrediction(t *testing.
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]string
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
jobID := startResp["JobId"]
getRec := doTextractRequest(t, h, "GetLendingAnalysis", map[string]any{"JobId": jobID})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
results, ok := getResp["Results"].([]any)
require.True(t, ok)
@@ -441,7 +442,7 @@ func TestHandler_GetLendingAnalysis_Pagination(t *testing.T) {
NextToken string `json:"NextToken"`
Results []map[string]any `json:"Results"`
}
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
assert.Len(t, resp1.Results, 2)
require.NotEmpty(t, resp1.NextToken, "first page must return a NextToken")
@@ -455,7 +456,7 @@ func TestHandler_GetLendingAnalysis_Pagination(t *testing.T) {
NextToken string `json:"NextToken"`
Results []map[string]any `json:"Results"`
}
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Len(t, resp2.Results, 3, "remaining 3 results on the second page")
assert.Empty(t, resp2.NextToken, "no more pages")
}
diff --git a/services/textract/handler_tags_test.go b/services/textract/handler_tags_test.go
index ef98543190..c345450b8c 100644
--- a/services/textract/handler_tags_test.go
+++ b/services/textract/handler_tags_test.go
@@ -1,13 +1,14 @@
package textract_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/textract"
)
@@ -22,7 +23,7 @@ func TestHandler_TagResource_HappyPath(t *testing.T) {
"FeatureTypes": []string{"FORMS"},
})
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
adapterID := createResp["AdapterId"]
tagRec := doTextractRequest(t, h, "TagResource", map[string]any{
@@ -37,7 +38,7 @@ func TestHandler_TagResource_HappyPath(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags, ok := listResp["Tags"].(map[string]any)
assert.True(t, ok)
assert.Equal(t, "prod", tags["env"])
@@ -56,7 +57,7 @@ func TestHandler_UntagResource_HappyPath(t *testing.T) {
"Tags": map[string]string{"env": "dev", "team": "ml"},
})
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
adapterID := createResp["AdapterId"]
untagRec := doTextractRequest(t, h, "UntagResource", map[string]any{
@@ -69,7 +70,7 @@ func TestHandler_UntagResource_HappyPath(t *testing.T) {
"ResourceARN": adapterID,
})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags, _ := listResp["Tags"].(map[string]any)
_, hasEnv := tags["env"]
assert.False(t, hasEnv)
@@ -93,7 +94,7 @@ func TestHandler_TagResource_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp["__type"])
}
@@ -129,7 +130,7 @@ func TestHandler_UntagResourceAndListTagsForResource_NotFound(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp["__type"])
})
}
@@ -150,7 +151,7 @@ func TestHandler_TagResource_ByARNRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]string
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
adapterID := createResp["AdapterId"]
// Build an ARN using the backend helper.
@@ -172,7 +173,7 @@ func TestHandler_TagResource_ByARNRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags, ok := listResp["Tags"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "prod", tags["env"])
@@ -194,7 +195,7 @@ func TestHandler_TagResource_AdapterVersionARN(t *testing.T) {
require.Equal(t, http.StatusOK, createAdapterRec.Code)
var createAdapterResp map[string]string
- require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &createAdapterResp))
+ require.NoError(t, json.Unmarshal(createAdapterRec.Body.Bytes(), &createAdapterResp, jsonx.V1()))
adapterID := createAdapterResp["AdapterId"]
createVersionRec := doTextractRequest(t, h, "CreateAdapterVersion", map[string]any{
@@ -212,7 +213,7 @@ func TestHandler_TagResource_AdapterVersionARN(t *testing.T) {
require.Equal(t, http.StatusOK, createVersionRec.Code)
var createVersionResp map[string]string
- require.NoError(t, json.Unmarshal(createVersionRec.Body.Bytes(), &createVersionResp))
+ require.NoError(t, json.Unmarshal(createVersionRec.Body.Bytes(), &createVersionResp, jsonx.V1()))
adapterVersion := createVersionResp["AdapterVersion"]
versionARN := b.BuildAdapterVersionARN(adapterID, adapterVersion)
@@ -230,7 +231,7 @@ func TestHandler_TagResource_AdapterVersionARN(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags, ok := listResp["Tags"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "v1", tags["version-tag"])
diff --git a/services/textract/handler_test.go b/services/textract/handler_test.go
index 196f205ebe..00f8f59d45 100644
--- a/services/textract/handler_test.go
+++ b/services/textract/handler_test.go
@@ -2,7 +2,7 @@ package textract_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/textract"
)
@@ -34,7 +35,7 @@ func doTextractRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -122,7 +123,7 @@ func TestHandler_UnknownAction_ErrorEnvelope(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ValidationException", errResp["__type"])
}
@@ -214,7 +215,7 @@ func TestHandler_ExtractResource(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
@@ -380,7 +381,7 @@ func TestHandler_HandleError_AdapterNotFound(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp["__type"])
}
@@ -397,7 +398,7 @@ func TestHandler_HandleError_JobNotFound(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidJobIdException", errResp["__type"])
}
@@ -520,7 +521,7 @@ func TestHandler_ErrorEnvelope_TypeAndMessage(t *testing.T) {
assert.GreaterOrEqual(t, rec.Code, http.StatusBadRequest)
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
assert.Equal(t, tt.wantType, m["__type"], "__type must match expected error code")
assert.NotEmpty(t, m["message"], "message must be present in error envelope")
})
diff --git a/services/textract/job_output_test.go b/services/textract/job_output_test.go
index d2c45a3029..8cafc9579d 100644
--- a/services/textract/job_output_test.go
+++ b/services/textract/job_output_test.go
@@ -2,7 +2,7 @@ package textract_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"sync"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/textract"
)
@@ -109,7 +110,7 @@ func TestAsyncJobWritesOutputConfig(t *testing.T) {
assert.Contains(t, w.objects, "out/pre/"+id+"/.s3_access_check")
var page map[string]any
- require.NoError(t, json.Unmarshal(w.objects["out/pre/"+id+"/1"], &page))
+ require.NoError(t, json.Unmarshal(w.objects["out/pre/"+id+"/1"], &page, jsonx.V1()))
assert.Contains(t, page, tt.wantKey)
assert.Contains(t, page, "DocumentMetadata")
diff --git a/services/textract/persistence.go b/services/textract/persistence.go
index e00de2a504..390bb84291 100644
--- a/services/textract/persistence.go
+++ b/services/textract/persistence.go
@@ -2,7 +2,7 @@ package textract
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"time"
@@ -117,7 +117,7 @@ func newDTORegistry() *dtoRegistry {
// values are strings, not *T, so they do not fit store.Table's
// keyed-by-identity-value shape and are unaffected by this conversion.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ClientTokenToJobID map[string]map[string]string `json:"clientTokenToJobId,omitempty"`
AdapterClientTokenToID map[string]map[string]string `json:"adapterClientTokenToId,omitempty"`
ExpenseClientTokenToJobID map[string]map[string]string `json:"expenseClientTokenToJobId,omitempty"`
diff --git a/services/textract/persistence_test.go b/services/textract/persistence_test.go
index b849634a86..929095bd94 100644
--- a/services/textract/persistence_test.go
+++ b/services/textract/persistence_test.go
@@ -2,11 +2,14 @@ package textract //nolint:testpackage // needs access to the unexported region c
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Test_PersistenceRoundTrip proves that every store.Table-backed resource
@@ -201,11 +204,11 @@ func Test_PersistenceVersionMismatch(t *testing.T) {
rewrite: func(t *testing.T, snap []byte) []byte {
t.Helper()
- var m map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &m))
+ var m map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &m, jsonx.V1()))
delete(m, "version")
- out, err := json.Marshal(m)
+ out, err := json.Marshal(m, jsonx.V1())
require.NoError(t, err)
return out
@@ -264,16 +267,16 @@ func Test_PersistenceVersionMismatch(t *testing.T) {
func rewriteSnapshotVersion(t *testing.T, snap []byte, version int) []byte {
t.Helper()
- var m map[string]json.RawMessage
+ var m map[string]jsontext.Value
- require.NoError(t, json.Unmarshal(snap, &m))
+ require.NoError(t, json.Unmarshal(snap, &m, jsonx.V1()))
- versionJSON, err := json.Marshal(version)
+ versionJSON, err := json.Marshal(version, jsonx.V1())
require.NoError(t, err)
m["version"] = versionJSON
- out, err := json.Marshal(m)
+ out, err := json.Marshal(m, jsonx.V1())
require.NoError(t, err)
return out
diff --git a/services/textract/realism_test.go b/services/textract/realism_test.go
index 2afde33da9..745a624b08 100644
--- a/services/textract/realism_test.go
+++ b/services/textract/realism_test.go
@@ -1,13 +1,15 @@
package textract_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestRealism_RequestValidation(t *testing.T) {
@@ -92,7 +94,7 @@ func TestRealism_RequestValidation(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code, rec.Body.String())
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
assert.Contains(t, resp["message"], tt.wantMsg)
assert.NotContains(t, resp["message"], "Exception: ")
diff --git a/services/textract/s3_object_test.go b/services/textract/s3_object_test.go
index a96e632326..1082f40a0a 100644
--- a/services/textract/s3_object_test.go
+++ b/services/textract/s3_object_test.go
@@ -2,7 +2,7 @@ package textract_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/textract"
)
@@ -100,7 +101,7 @@ func TestSyncDocumentOps_S3ObjectValidation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidS3ObjectException", resp["__type"])
rec = doTextractRequest(t, h, tt.action, tt.body("good-bucket", "file.pdf"))
@@ -125,7 +126,7 @@ func TestAnalyzeID_S3ObjectValidation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidS3ObjectException", resp["__type"])
rec = doTextractRequest(t, h, "AnalyzeID", map[string]any{
@@ -174,7 +175,7 @@ func TestStartOps_S3ObjectValidation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidS3ObjectException", resp["__type"])
rec = doTextractRequest(t, h, tt.action, body("good-bucket"))
@@ -230,7 +231,7 @@ func createAdapterForS3Test(t *testing.T, h *textract.Handler) string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["AdapterId"]
}
@@ -263,7 +264,7 @@ func TestCreateAdapterVersion_S3ObjectValidation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidS3ObjectException", resp["__type"])
rec = doTextractRequest(t, h, "CreateAdapterVersion",
diff --git a/services/textract/s3_output.go b/services/textract/s3_output.go
index c7beeb3244..32196112b7 100644
--- a/services/textract/s3_output.go
+++ b/services/textract/s3_output.go
@@ -3,13 +3,14 @@ package textract
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"path"
"strings"
"github.com/aws/aws-sdk-go-v2/aws"
awss3 "github.com/aws/aws-sdk-go-v2/service/s3"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
s3backend "github.com/blackbirdworks/gopherstack/services/s3"
)
@@ -87,7 +88,7 @@ func (b *InMemoryBackend) writeJobOutputLocked(cfg *OutputConfig, jobID string,
return true
}
- body, err := json.Marshal(page)
+ body, err := json.Marshal(page, jsonx.V1())
if err != nil {
return false
}
diff --git a/services/timestreaminfluxdb/docker_engine_ops_test.go b/services/timestreaminfluxdb/docker_engine_ops_test.go
index 24bd69df7f..aacb8c7e38 100644
--- a/services/timestreaminfluxdb/docker_engine_ops_test.go
+++ b/services/timestreaminfluxdb/docker_engine_ops_test.go
@@ -1,7 +1,7 @@
package timestreaminfluxdb_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
"os"
"path/filepath"
"testing"
@@ -192,7 +192,7 @@ func TestParameterEnv(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- assert.Equal(t, tt.want, timestreaminfluxdb.ParameterEnv(json.RawMessage(tt.in)))
+ assert.Equal(t, tt.want, timestreaminfluxdb.ParameterEnv(jsontext.Value(tt.in)))
})
}
}
diff --git a/services/timestreaminfluxdb/engine_ops_test.go b/services/timestreaminfluxdb/engine_ops_test.go
index 8a83202322..448cf4187f 100644
--- a/services/timestreaminfluxdb/engine_ops_test.go
+++ b/services/timestreaminfluxdb/engine_ops_test.go
@@ -2,7 +2,7 @@ package timestreaminfluxdb_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"maps"
@@ -20,6 +20,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/timestreaminfluxdb"
)
@@ -273,7 +274,7 @@ func TestAuthParametersSecret(t *testing.T) {
assert.Equal(t, arn, aws.ToString(got.InfluxAuthParametersSecretArn))
var secret map[string]string
- require.NoError(t, json.Unmarshal([]byte(e.secrets.all()[arn]), &secret))
+ require.NoError(t, json.Unmarshal([]byte(e.secrets.all()[arn]), &secret, jsonx.V1()))
specs, _, _, _ := e.eng.snapshot()
assert.Equal(t, map[string]string{
diff --git a/services/timestreaminfluxdb/engine_params.go b/services/timestreaminfluxdb/engine_params.go
index 0ad52b6d50..d800823b49 100644
--- a/services/timestreaminfluxdb/engine_params.go
+++ b/services/timestreaminfluxdb/engine_params.go
@@ -1,13 +1,16 @@
package timestreaminfluxdb
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"slices"
"strconv"
"strings"
"time"
"unicode"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const hoursPerDay = 24
@@ -15,14 +18,14 @@ const hoursPerDay = 24
// ParameterEnv translates an InfluxDBv2 parameter group document into influxd environment settings.
// Every InfluxDBv2 parameter has an influxd flag of the same name in kebab case (INFLUXD_);
// groups for other engines are stored but not applied.
-func ParameterEnv(raw json.RawMessage) []string {
- var union map[string]json.RawMessage
- if json.Unmarshal(raw, &union) != nil {
+func ParameterEnv(raw jsontext.Value) []string {
+ var union map[string]jsontext.Value
+ if json.Unmarshal(raw, &union, jsonx.V1()) != nil {
return nil
}
- var fields map[string]json.RawMessage
- if json.Unmarshal(union["InfluxDBv2"], &fields) != nil {
+ var fields map[string]jsontext.Value
+ if json.Unmarshal(union["InfluxDBv2"], &fields, jsonx.V1()) != nil {
return nil
}
@@ -54,13 +57,13 @@ func envName(param string) string {
return sb.String()
}
-func paramValue(name string, raw json.RawMessage) (string, bool) {
+func paramValue(name string, raw jsontext.Value) (string, bool) {
if isDurationParam(name) {
return durationValue(raw)
}
var v any
- if json.Unmarshal(raw, &v) != nil {
+ if json.Unmarshal(raw, &v, jsonx.V1()) != nil {
return "", false
}
@@ -76,12 +79,12 @@ func paramValue(name string, raw json.RawMessage) (string, bool) {
return "", false
}
-func durationValue(raw json.RawMessage) (string, bool) {
+func durationValue(raw jsontext.Value) (string, bool) {
var d struct {
DurationType string `json:"durationType"`
Value int64 `json:"value"`
}
- if json.Unmarshal(raw, &d) != nil {
+ if json.Unmarshal(raw, &d, jsonx.V1()) != nil {
return "", false
}
diff --git a/services/timestreaminfluxdb/handler.go b/services/timestreaminfluxdb/handler.go
index 708974587a..277ee19cb6 100644
--- a/services/timestreaminfluxdb/handler.go
+++ b/services/timestreaminfluxdb/handler.go
@@ -2,7 +2,7 @@ package timestreaminfluxdb
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"slices"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -39,7 +40,7 @@ func bind[I, O any](fn func(*Handler, context.Context, *I) (*O, error)) opFunc {
var in I
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, invalid("malformed request body: %v", err)
}
}
diff --git a/services/timestreaminfluxdb/handler_other.go b/services/timestreaminfluxdb/handler_other.go
index 334cbd731a..f5d6abce67 100644
--- a/services/timestreaminfluxdb/handler_other.go
+++ b/services/timestreaminfluxdb/handler_other.go
@@ -2,7 +2,7 @@ package timestreaminfluxdb
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
)
func registerGroupOps(ops map[string]opFunc) {
@@ -15,7 +15,7 @@ type createGroupReq struct {
Tags map[string]string `json:"tags"`
Name string `json:"name"`
Description string `json:"description"`
- Parameters json.RawMessage `json:"parameters"`
+ Parameters jsontext.Value `json:"parameters"`
}
func (h *Handler) createGroup(ctx context.Context, in *createGroupReq) (*wParameterGroup, error) {
diff --git a/services/timestreaminfluxdb/models.go b/services/timestreaminfluxdb/models.go
index 8fcb08506b..7c70c494fa 100644
--- a/services/timestreaminfluxdb/models.go
+++ b/services/timestreaminfluxdb/models.go
@@ -1,7 +1,7 @@
package timestreaminfluxdb
import (
- "encoding/json"
+ "encoding/json/jsontext"
"time"
)
@@ -149,7 +149,7 @@ type DBParameterGroup struct {
Description string `json:"description,omitempty"`
AccountID string `json:"accountId"`
Region string `json:"region"`
- Parameters json.RawMessage `json:"parameters,omitempty"`
+ Parameters jsontext.Value `json:"parameters,omitempty"`
}
// DBBackup is a backup of an instance or cluster.
diff --git a/services/timestreaminfluxdb/parametergroups.go b/services/timestreaminfluxdb/parametergroups.go
index 2929af5acc..c6a709cefc 100644
--- a/services/timestreaminfluxdb/parametergroups.go
+++ b/services/timestreaminfluxdb/parametergroups.go
@@ -3,7 +3,7 @@ package timestreaminfluxdb
import (
"cmp"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"slices"
"github.com/blackbirdworks/gopherstack/pkgs/page"
@@ -16,7 +16,7 @@ type GroupInput struct {
Tags map[string]string
Name string
Description string
- Parameters json.RawMessage
+ Parameters jsontext.Value
}
// CreateDBParameterGroup creates a parameter group.
diff --git a/services/timestreaminfluxdb/persistence.go b/services/timestreaminfluxdb/persistence.go
index 4b69fffcc7..3d8bb9960e 100644
--- a/services/timestreaminfluxdb/persistence.go
+++ b/services/timestreaminfluxdb/persistence.go
@@ -2,7 +2,7 @@ package timestreaminfluxdb
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -12,10 +12,10 @@ import (
const snapshotVersion = 1
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serialises the backend state.
diff --git a/services/timestreaminfluxdb/secrets.go b/services/timestreaminfluxdb/secrets.go
index 36253ea804..dfcb154398 100644
--- a/services/timestreaminfluxdb/secrets.go
+++ b/services/timestreaminfluxdb/secrets.go
@@ -1,9 +1,10 @@
package timestreaminfluxdb
import (
- "encoding/json"
+ "encoding/json/v2"
"github.com/blackbirdworks/gopherstack/pkgs/awsid"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -39,7 +40,7 @@ func (b *InMemoryBackend) authSecretLocked(sc scope, id string, a authSecret) (s
return "", nil
}
- body, err := json.Marshal(a) //nolint:gosec // the secret holds the credentials by design
+ body, err := json.Marshal(a, jsonx.V1())
if err != nil {
return "", fail(ErrInternalServer, "encode auth secret")
}
diff --git a/services/timestreaminfluxdb/validate.go b/services/timestreaminfluxdb/validate.go
index f05afcded1..1f70ff5abf 100644
--- a/services/timestreaminfluxdb/validate.go
+++ b/services/timestreaminfluxdb/validate.go
@@ -1,11 +1,14 @@
package timestreaminfluxdb
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"regexp"
"slices"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -220,13 +223,13 @@ func validateListArgs(maxResults int32) error {
}
// validateParameters checks the union shape: one engine key holding a parameter object.
-func validateParameters(raw json.RawMessage) error {
+func validateParameters(raw jsontext.Value) error {
if len(raw) == 0 {
return nil
}
- var union map[string]json.RawMessage
- if err := json.Unmarshal(raw, &union); err != nil || len(union) != 1 {
+ var union map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &union, jsonx.V1()); err != nil || len(union) != 1 {
return invalid("parameters must set exactly one of %s", strings.Join(paramGroups(), ", "))
}
@@ -235,8 +238,8 @@ func validateParameters(raw json.RawMessage) error {
return invalid("parameters must set exactly one of %s", strings.Join(paramGroups(), ", "))
}
- var fields map[string]json.RawMessage
- if err := json.Unmarshal(body, &fields); err != nil {
+ var fields map[string]jsontext.Value
+ if err := json.Unmarshal(body, &fields, jsonx.V1()); err != nil {
return invalid("%s parameters must be an object", engine)
}
@@ -248,7 +251,7 @@ func validateParameters(raw json.RawMessage) error {
return nil
}
-func validateParameterFields(engine string, fields map[string]json.RawMessage) error {
+func validateParameterFields(engine string, fields map[string]jsontext.Value) error {
for name, val := range fields {
if !paramKeys[name] {
return invalid("%s has no parameter named %q", engine, name)
@@ -275,21 +278,21 @@ func validateParameterFields(engine string, fields map[string]json.RawMessage) e
return nil
}
-func checkEnum(name string, raw json.RawMessage, allowed []string) error {
+func checkEnum(name string, raw jsontext.Value, allowed []string) error {
var s string
- if err := json.Unmarshal(raw, &s); err != nil {
+ if err := json.Unmarshal(raw, &s, jsonx.V1()); err != nil {
return invalid("%s must be a string", name)
}
return oneOf(name, s, allowed)
}
-func checkDuration(name string, raw json.RawMessage) error {
+func checkDuration(name string, raw jsontext.Value) error {
var d struct {
Value *int64 `json:"value"`
DurationType string `json:"durationType"`
}
- if err := json.Unmarshal(raw, &d); err != nil || d.Value == nil {
+ if err := json.Unmarshal(raw, &d, jsonx.V1()); err != nil || d.Value == nil {
return invalid("%s must be a duration with durationType and value", name)
}
diff --git a/services/timestreaminfluxdb/wire.go b/services/timestreaminfluxdb/wire.go
index 916d0d7a38..62e86e4d46 100644
--- a/services/timestreaminfluxdb/wire.go
+++ b/services/timestreaminfluxdb/wire.go
@@ -1,7 +1,7 @@
package timestreaminfluxdb
import (
- "encoding/json"
+ "encoding/json/jsontext"
"maps"
"slices"
"strings"
@@ -129,11 +129,11 @@ type wClusterSummary struct {
}
type wParameterGroup struct {
- Arn string `json:"arn"`
- ID string `json:"id"`
- Name string `json:"name"`
- Description string `json:"description,omitempty"`
- Parameters json.RawMessage `json:"parameters,omitempty"`
+ Arn string `json:"arn"`
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Description string `json:"description,omitempty"`
+ Parameters jsontext.Value `json:"parameters,omitempty"`
}
type wParameterGroupSummary struct {
diff --git a/services/timestreamquery/column_info_marshal_test.go b/services/timestreamquery/column_info_marshal_test.go
index 7f405f8d68..81ee388a6a 100644
--- a/services/timestreamquery/column_info_marshal_test.go
+++ b/services/timestreamquery/column_info_marshal_test.go
@@ -1,11 +1,13 @@
package timestreamquery //nolint:testpackage // needs unexported marshalColumnInfos for the wire-shape check.
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestMarshalColumnInfos_PreservesNestedUnion verifies that marshalColumnInfos
@@ -95,12 +97,12 @@ func TestMarshalColumnInfos_PreservesNestedUnion(t *testing.T) {
require.Len(t, marshalled, 1)
// Round-trip through JSON, exactly as the handler's response does.
- raw, err := json.Marshal(marshalled)
+ raw, err := json.Marshal(marshalled, jsonx.V1())
require.NoError(t, err)
var decoded []map[string]any
- require.NoError(t, json.Unmarshal(raw, &decoded))
+ require.NoError(t, json.Unmarshal(raw, &decoded, jsonx.V1()))
require.Len(t, decoded, 1)
typ, ok := decoded[0]["Type"].(map[string]any)
diff --git a/services/timestreamquery/handler.go b/services/timestreamquery/handler.go
index 2b64e96643..b7290cc2ba 100644
--- a/services/timestreamquery/handler.go
+++ b/services/timestreamquery/handler.go
@@ -2,7 +2,7 @@ package timestreamquery
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -161,7 +162,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
Name string `json:"Name"`
}
- if unmarshalErr := json.Unmarshal(body, &req); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &req, jsonx.V1()); unmarshalErr != nil {
return ""
}
@@ -272,7 +273,7 @@ func (h *Handler) handleDescribeEndpoints(host string) ([]byte, error) {
"CachePeriodInMinutes": endpointCachePeriod,
},
},
- })
+ }, jsonx.V1())
}
func (h *Handler) handleError(c *echo.Context, err error) error {
@@ -296,7 +297,7 @@ func errorPayload(errType, msg string) []byte {
b, _ := json.Marshal(map[string]string{
"__type": errType,
"message": msg,
- })
+ }, jsonx.V1())
return b
}
diff --git a/services/timestreamquery/handler_account_settings.go b/services/timestreamquery/handler_account_settings.go
index 4af40c7836..85f9270add 100644
--- a/services/timestreamquery/handler_account_settings.go
+++ b/services/timestreamquery/handler_account_settings.go
@@ -2,14 +2,16 @@ package timestreamquery
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleDescribeAccountSettings(ctx context.Context) ([]byte, error) {
settings := h.Backend.DescribeAccountSettings(ctx)
- return json.Marshal(buildAccountSettingsResponse(settings))
+ return json.Marshal(buildAccountSettingsResponse(settings), jsonx.V1())
}
func buildAccountSettingsResponse(settings AccountSettings) map[string]any {
@@ -32,7 +34,7 @@ func (h *Handler) handlePrepareQuery(ctx context.Context, body []byte) ([]byte,
ValidateOnly bool `json:"ValidateOnly"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
@@ -49,7 +51,7 @@ func (h *Handler) handlePrepareQuery(ctx context.Context, body []byte) ([]byte,
"QueryString": result.QueryString,
"Columns": marshalSelectColumns(result.Selected),
"Parameters": marshalColumnInfos(result.Parameters),
- })
+ }, jsonx.V1())
}
// updateAccountSettingsRequest is the parsed request body for
@@ -119,7 +121,7 @@ func (nc *accountSettingsNotificationConfigWire) toModel() *AccountSettingsNotif
func (h *Handler) handleUpdateAccountSettings(ctx context.Context, body []byte) ([]byte, error) {
var req updateAccountSettingsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
@@ -130,7 +132,7 @@ func (h *Handler) handleUpdateAccountSettings(ctx context.Context, body []byte)
return nil, err
}
- return json.Marshal(buildAccountSettingsResponse(settings))
+ return json.Marshal(buildAccountSettingsResponse(settings), jsonx.V1())
}
func marshalSelectColumns(cols []SelectColumn) []map[string]any {
diff --git a/services/timestreamquery/handler_query_execution.go b/services/timestreamquery/handler_query_execution.go
index a146ae5e6a..ff8cfe58cc 100644
--- a/services/timestreamquery/handler_query_execution.go
+++ b/services/timestreamquery/handler_query_execution.go
@@ -2,8 +2,10 @@ package timestreamquery
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleQuery(ctx context.Context, body []byte) ([]byte, error) {
@@ -17,7 +19,7 @@ func (h *Handler) handleQuery(ctx context.Context, body []byte) ([]byte, error)
MaxRows int32 `json:"MaxRows"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
@@ -58,7 +60,7 @@ func (h *Handler) handleQuery(ctx context.Context, body []byte) ([]byte, error)
resp["QueryInsightsResponse"] = page.Insights
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// marshalRows converts []Row to JSON-serialisable form.
@@ -99,7 +101,7 @@ func (h *Handler) handleCancelQuery(ctx context.Context, body []byte) ([]byte, e
QueryID string `json:"QueryId"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
@@ -113,5 +115,5 @@ func (h *Handler) handleCancelQuery(ctx context.Context, body []byte) ([]byte, e
return json.Marshal(map[string]any{
"CancellationMessage": "Query has been successfully cancelled.",
- })
+ }, jsonx.V1())
}
diff --git a/services/timestreamquery/handler_scheduled_queries.go b/services/timestreamquery/handler_scheduled_queries.go
index 123cb33990..569f31cf75 100644
--- a/services/timestreamquery/handler_scheduled_queries.go
+++ b/services/timestreamquery/handler_scheduled_queries.go
@@ -2,9 +2,11 @@ package timestreamquery
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// multiMeasureAttributeMappingInput mirrors types.MultiMeasureAttributeMapping.
@@ -137,7 +139,7 @@ func convertMultiMeasureAttributeMappings(
func (h *Handler) handleCreateScheduledQuery(ctx context.Context, body []byte) ([]byte, error) {
var req createScheduledQueryInput
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
@@ -211,7 +213,7 @@ func (h *Handler) handleCreateScheduledQuery(ctx context.Context, body []byte) (
return json.Marshal(map[string]any{
keyArn: sq.Arn,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleDeleteScheduledQuery(ctx context.Context, body []byte) ([]byte, error) {
@@ -219,7 +221,7 @@ func (h *Handler) handleDeleteScheduledQuery(ctx context.Context, body []byte) (
ScheduledQueryArn string `json:"ScheduledQueryArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
@@ -239,7 +241,7 @@ func (h *Handler) handleDescribeScheduledQuery(ctx context.Context, body []byte)
ScheduledQueryArn string `json:"ScheduledQueryArn"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
@@ -254,7 +256,7 @@ func (h *Handler) handleDescribeScheduledQuery(ctx context.Context, body []byte)
return json.Marshal(map[string]any{
"ScheduledQuery": scheduledQueryToView(sq),
- })
+ }, jsonx.V1())
}
func (h *Handler) handleExecuteScheduledQuery(ctx context.Context, body []byte) ([]byte, error) {
@@ -263,7 +265,7 @@ func (h *Handler) handleExecuteScheduledQuery(ctx context.Context, body []byte)
InvocationTime float64 `json:"InvocationTime"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
@@ -290,7 +292,7 @@ func (h *Handler) handleListScheduledQueries(ctx context.Context, body []byte) (
MaxResults int32 `json:"MaxResults"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
}
if err := validateScheduledQueryPaging(req.NextToken, req.MaxResults); err != nil {
@@ -306,7 +308,7 @@ func (h *Handler) handleListScheduledQueries(ctx context.Context, body []byte) (
resp["NextToken"] = result.NextToken
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
func (h *Handler) handleUpdateScheduledQuery(ctx context.Context, body []byte) ([]byte, error) {
@@ -315,7 +317,7 @@ func (h *Handler) handleUpdateScheduledQuery(ctx context.Context, body []byte) (
State string `json:"State"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
diff --git a/services/timestreamquery/handler_tags.go b/services/timestreamquery/handler_tags.go
index 02ba40296b..78efcec040 100644
--- a/services/timestreamquery/handler_tags.go
+++ b/services/timestreamquery/handler_tags.go
@@ -2,8 +2,10 @@ package timestreamquery
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleTagResource(ctx context.Context, body []byte) ([]byte, error) {
@@ -15,7 +17,7 @@ func (h *Handler) handleTagResource(ctx context.Context, body []byte) ([]byte, e
} `json:"Tags"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
@@ -33,7 +35,7 @@ func (h *Handler) handleTagResource(ctx context.Context, body []byte) ([]byte, e
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
func (h *Handler) handleUntagResource(ctx context.Context, body []byte) ([]byte, error) {
@@ -42,7 +44,7 @@ func (h *Handler) handleUntagResource(ctx context.Context, body []byte) ([]byte,
TagKeys []string `json:"TagKeys"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
@@ -54,7 +56,7 @@ func (h *Handler) handleUntagResource(ctx context.Context, body []byte) ([]byte,
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
func (h *Handler) handleListTagsForResource(ctx context.Context, body []byte) ([]byte, error) {
@@ -62,7 +64,7 @@ func (h *Handler) handleListTagsForResource(ctx context.Context, body []byte) ([
ResourceARN string `json:"ResourceARN"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
@@ -77,5 +79,5 @@ func (h *Handler) handleListTagsForResource(ctx context.Context, body []byte) ([
return json.Marshal(map[string]any{
"Tags": tags,
- })
+ }, jsonx.V1())
}
diff --git a/services/timestreamquery/handler_test.go b/services/timestreamquery/handler_test.go
index 3e20f852ef..89b4d276bf 100644
--- a/services/timestreamquery/handler_test.go
+++ b/services/timestreamquery/handler_test.go
@@ -2,7 +2,7 @@ package timestreamquery_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/config"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/timestreamquery"
)
@@ -38,7 +39,7 @@ func doRequest(
var bodyBytes []byte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -80,7 +81,7 @@ func parseResponse(t *testing.T, rec *httptest.ResponseRecorder) map[string]any
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
@@ -238,7 +239,7 @@ func TestTimestreamQueryHandler_ExtractResource(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
h := newTestHandler()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(bodyBytes))
e := echo.New()
diff --git a/services/timestreamquery/persistence.go b/services/timestreamquery/persistence.go
index c5a70c57a9..b1e84282d2 100644
--- a/services/timestreamquery/persistence.go
+++ b/services/timestreamquery/persistence.go
@@ -2,9 +2,11 @@ package timestreamquery
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -30,7 +32,7 @@ const timestreamquerySnapshotVersion = 1
// guards against decoding a snapshot from an incompatible (older or newer)
// build of this backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
AccountSettings map[string]accountSettingsSnapshot `json:"account_settings"`
Version int `json:"version"`
}
@@ -84,7 +86,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
Version: timestreamquerySnapshotVersion,
Tables: tables,
AccountSettings: settingsSnap,
- })
+ }, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "timestreamquery: failed to marshal snapshot", "error", err)
diff --git a/services/timestreamquery/query_execution_test.go b/services/timestreamquery/query_execution_test.go
index 4f4756eab0..8c84fd42b9 100644
--- a/services/timestreamquery/query_execution_test.go
+++ b/services/timestreamquery/query_execution_test.go
@@ -1,13 +1,14 @@
package timestreamquery_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/timestreamquery"
)
@@ -295,7 +296,7 @@ func TestCancelQuery_UnknownIDReturnsValidationException(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
diff --git a/services/timestreamquery/scheduled_queries_test.go b/services/timestreamquery/scheduled_queries_test.go
index 2fe517f4aa..ae3c84ae6d 100644
--- a/services/timestreamquery/scheduled_queries_test.go
+++ b/services/timestreamquery/scheduled_queries_test.go
@@ -1,7 +1,7 @@
package timestreamquery_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/timestreamquery"
)
@@ -565,7 +566,7 @@ func TestCreateScheduledQuery_ConflictReturns409(t *testing.T) {
assert.Equal(t, http.StatusConflict, rec2.Code)
var errBody map[string]string
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "ConflictException", errBody["__type"])
}
@@ -629,7 +630,7 @@ func TestCreateScheduledQuery_RequiresNotificationAndErrorReport(t *testing.T) {
if tt.wantCode == http.StatusBadRequest {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.NotEmpty(t, errResp["__type"], "error response must include __type")
}
})
diff --git a/services/timestreamquery/ts_meta.go b/services/timestreamquery/ts_meta.go
index 132699a2e4..6a4859b14d 100644
--- a/services/timestreamquery/ts_meta.go
+++ b/services/timestreamquery/ts_meta.go
@@ -1,10 +1,12 @@
package timestreamquery
import (
- "encoding/json"
+ "encoding/json/v2"
"regexp"
"sort"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func varcharResult(names []string, rows [][]string) *resultSet {
@@ -188,7 +190,7 @@ func (b *InMemoryBackend) showMeasures(region, q string, src RecordSource) (*res
dims = append(dims, map[string]string{"dimension_name": d, "data_type": types[d]})
}
- enc, _ := json.Marshal(dims)
+ enc, _ := json.Marshal(dims, jsonx.V1())
rows = append(rows, []string{m.name, m.typ, string(enc)})
}
diff --git a/services/timestreamwrite/handler.go b/services/timestreamwrite/handler.go
index 24e93b8b2a..71c56bd86e 100644
--- a/services/timestreamwrite/handler.go
+++ b/services/timestreamwrite/handler.go
@@ -2,7 +2,7 @@ package timestreamwrite
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/ctxval"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -284,12 +285,10 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
var rejectedErr *RejectedRecordsError
switch {
@@ -316,8 +315,8 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
case errors.Is(err, awserr.ErrInvalidParameter) ||
errors.Is(err, errInvalidRequest) ||
errors.Is(err, errUnknownAction) ||
- errors.As(err, &syntaxErr) ||
- errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err) ||
+ jsonx.IsTypeError(err):
return c.JSON(http.StatusBadRequest, map[string]string{
keyTypeField: "ValidationException",
keyMessageField: awserr.StripCode("ValidationException", err.Error()),
diff --git a/services/timestreamwrite/handler_batch_load_tasks_test.go b/services/timestreamwrite/handler_batch_load_tasks_test.go
index f06f40e985..3d29547254 100644
--- a/services/timestreamwrite/handler_batch_load_tasks_test.go
+++ b/services/timestreamwrite/handler_batch_load_tasks_test.go
@@ -1,7 +1,7 @@
package timestreamwrite_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/timestreamwrite"
)
@@ -72,7 +73,7 @@ func TestHandler_CreateBatchLoadTask(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(result.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(result.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["TaskId"])
}
})
@@ -102,7 +103,7 @@ func TestHandler_DescribeBatchLoadTask(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
taskID, _ := createResp["TaskId"].(string)
require.NotEmpty(t, taskID)
@@ -137,7 +138,7 @@ func TestHandler_DescribeBatchLoadTask(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(result.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(result.Body.Bytes(), &resp, jsonx.V1()))
desc, ok := resp["BatchLoadTaskDescription"].(map[string]any)
assert.True(t, ok)
assert.Equal(t, taskID, desc["TaskId"])
@@ -207,7 +208,7 @@ func TestHandler_ListBatchLoadTasks(t *testing.T) {
assert.Equal(t, tt.wantStatus, result.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(result.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(result.Body.Bytes(), &resp, jsonx.V1()))
tasks, ok := resp["BatchLoadTasks"].([]any)
assert.True(t, ok)
assert.Len(t, tasks, tt.wantLen)
@@ -238,7 +239,7 @@ func TestHandler_ResumeBatchLoadTask(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
taskID, _ := createResp["TaskId"].(string)
require.NotEmpty(t, taskID)
@@ -349,7 +350,7 @@ func TestHandler_CreateBatchLoadTask_DataSourceConfigRequired(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var errBody map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "ValidationException", errBody["__type"])
}
})
@@ -380,7 +381,7 @@ func TestHandler_CreateBatchLoadTask_WithDataSourceConfig(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
taskID := createOut["TaskId"].(string)
require.NotEmpty(t, taskID)
@@ -388,7 +389,7 @@ func TestHandler_CreateBatchLoadTask_WithDataSourceConfig(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
desc := descOut["BatchLoadTaskDescription"].(map[string]any)
dsc := desc["DataSourceConfiguration"].(map[string]any)
@@ -424,14 +425,14 @@ func TestHandler_CreateBatchLoadTask_WithReportConfig(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
taskID := createOut["TaskId"].(string)
descRec := doRequest(t, h, "DescribeBatchLoadTask", map[string]string{"TaskId": taskID})
require.Equal(t, http.StatusOK, descRec.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
desc := descOut["BatchLoadTaskDescription"].(map[string]any)
rpt := desc["ReportConfiguration"].(map[string]any)
@@ -504,7 +505,7 @@ func TestHandler_ListBatchLoadTasks_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
tasks1 := out1["BatchLoadTasks"].([]any)
assert.Len(t, tasks1, 2)
@@ -516,7 +517,7 @@ func TestHandler_ListBatchLoadTasks_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
tasks2 := out2["BatchLoadTasks"].([]any)
assert.Len(t, tasks2, 2)
@@ -555,7 +556,7 @@ func TestHandler_ListBatchLoadTasks_StatusFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tasks := out["BatchLoadTasks"].([]any)
assert.Len(t, tasks, 1)
@@ -594,7 +595,7 @@ func TestHandler_ListBatchLoadTasks_SummaryResumableUntil(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tasks := out["BatchLoadTasks"].([]any)
require.Len(t, tasks, 1)
@@ -633,7 +634,7 @@ func TestHandler_DescribeBatchLoadTask_ErrorMessage(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
desc := out["BatchLoadTaskDescription"].(map[string]any)
assert.Equal(t, "S3 object not found", desc["ErrorMessage"])
@@ -660,14 +661,14 @@ func TestHandler_BatchLoadTaskDescriptionView_MissingResumableUntilOmitted(t *te
require.Equal(t, http.StatusOK, cr.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(cr.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(cr.Body.Bytes(), &createOut, jsonx.V1()))
taskID := createOut["TaskId"].(string)
dr := doRequest(t, h, "DescribeBatchLoadTask", map[string]string{"TaskId": taskID})
require.Equal(t, http.StatusOK, dr.Code)
var descOut map[string]any
- require.NoError(t, json.Unmarshal(dr.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(dr.Body.Bytes(), &descOut, jsonx.V1()))
desc := descOut["BatchLoadTaskDescription"].(map[string]any)
_, hasResumable := desc["ResumableUntil"]
@@ -698,7 +699,7 @@ func TestHandler_DescribeBatchLoadTask_RecordVersion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
desc := out["BatchLoadTaskDescription"].(map[string]any)
assert.InDelta(t, float64(42), desc["RecordVersion"], 0,
@@ -726,14 +727,14 @@ func TestHandler_DescribeBatchLoadTask_RecordVersionZeroOmitted(t *testing.T) {
require.Equal(t, http.StatusOK, cr.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(cr.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(cr.Body.Bytes(), &createOut, jsonx.V1()))
taskID := createOut["TaskId"].(string)
dr := doRequest(t, h, "DescribeBatchLoadTask", map[string]string{"TaskId": taskID})
require.Equal(t, http.StatusOK, dr.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(dr.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(dr.Body.Bytes(), &out, jsonx.V1()))
desc := out["BatchLoadTaskDescription"].(map[string]any)
_, hasRV := desc["RecordVersion"]
@@ -771,7 +772,7 @@ func TestHandler_DescribeBatchLoadTask_ProgressReport(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
desc := out["BatchLoadTaskDescription"].(map[string]any)
report := desc["ProgressReport"].(map[string]any)
@@ -803,14 +804,14 @@ func TestHandler_DescribeBatchLoadTask_NoProgressReport(t *testing.T) {
require.Equal(t, http.StatusOK, cr.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(cr.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(cr.Body.Bytes(), &createOut, jsonx.V1()))
taskID := createOut["TaskId"].(string)
dr := doRequest(t, h, "DescribeBatchLoadTask", map[string]string{"TaskId": taskID})
require.Equal(t, http.StatusOK, dr.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(dr.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(dr.Body.Bytes(), &out, jsonx.V1()))
desc := out["BatchLoadTaskDescription"].(map[string]any)
_, hasPR := desc["ProgressReport"]
@@ -840,14 +841,14 @@ func TestHandler_DescribeBatchLoadTask_TimestampsAreFloats(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
taskID := createOut["TaskId"].(string)
rec2 := doRequest(t, h, "DescribeBatchLoadTask", map[string]any{"TaskId": taskID})
require.Equal(t, http.StatusOK, rec2.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &raw, jsonx.V1()))
desc := raw["BatchLoadTaskDescription"].(map[string]any)
ct, ok := desc["CreationTime"].(float64)
diff --git a/services/timestreamwrite/handler_databases_test.go b/services/timestreamwrite/handler_databases_test.go
index 881d7aa0f6..05fa63c23c 100644
--- a/services/timestreamwrite/handler_databases_test.go
+++ b/services/timestreamwrite/handler_databases_test.go
@@ -1,7 +1,7 @@
package timestreamwrite_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateDatabase(t *testing.T) {
@@ -41,7 +43,7 @@ func TestHandler_CreateDatabase(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
db, ok := resp["Database"].(map[string]any)
assert.True(t, ok)
assert.Equal(t, "my-db", db["DatabaseName"])
@@ -65,7 +67,7 @@ func TestHandler_CreateDatabase_Conflict(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errBody map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "ConflictException", errBody["__type"])
}
@@ -122,7 +124,7 @@ func TestHandler_ListDatabases(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dbs, ok := resp["Databases"].([]any)
assert.True(t, ok)
assert.Len(t, dbs, 2)
@@ -141,7 +143,7 @@ func TestHandler_ListDatabases_Sorted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
dbs := out["Databases"].([]any)
require.Len(t, dbs, 3)
@@ -165,7 +167,7 @@ func TestHandler_ListDatabases_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
dbs1 := out1["Databases"].([]any)
assert.Len(t, dbs1, 2)
@@ -178,7 +180,7 @@ func TestHandler_ListDatabases_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
dbs2 := out2["Databases"].([]any)
assert.Len(t, dbs2, 2)
@@ -189,7 +191,7 @@ func TestHandler_ListDatabases_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var out3 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &out3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &out3, jsonx.V1()))
dbs3 := out3["Databases"].([]any)
assert.Len(t, dbs3, 1)
@@ -209,7 +211,7 @@ func TestHandler_ListDatabases_NoNextTokenWhenFits(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Empty(t, out["NextToken"])
}
@@ -302,7 +304,7 @@ func TestHandler_DeleteDatabase_RequiresEmpty(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
})
@@ -417,7 +419,7 @@ func TestHandler_CreateDatabase_MinimumNameLength(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
})
@@ -489,7 +491,7 @@ func TestHandler_CreateDatabase_NameFormatValidation(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
})
@@ -510,7 +512,7 @@ func TestHandler_CreateDatabase_NameValidationErrorBody(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
assert.NotEmpty(t, body["message"])
}
@@ -577,7 +579,7 @@ func TestHandler_CreateDatabase_TagKeyValidation(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
})
@@ -599,7 +601,7 @@ func TestHandler_CreateDatabase_WithKmsKeyID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createOut, jsonx.V1()))
db := createOut["Database"].(map[string]any)
assert.Equal(t, kmsKey, db["KmsKeyId"])
@@ -622,7 +624,7 @@ func TestHandler_CreateDatabase_KmsKeyRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
db := out["Database"].(map[string]any)
assert.Equal(t, kmsKey, db["KmsKeyId"], "KmsKeyId should survive DescribeDatabase")
@@ -645,7 +647,7 @@ func TestHandler_ListDatabases_IncludesKmsKeyID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
dbs := out["Databases"].([]any)
require.Len(t, dbs, 1)
@@ -684,7 +686,7 @@ func TestHandler_CreateDatabase_ConflictException(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ConflictException", body["__type"])
assert.NotEmpty(t, body["message"])
}
@@ -703,7 +705,7 @@ func TestHandler_DescribeDatabase_TableCountTracking(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
db := out["Database"].(map[string]any)
@@ -735,7 +737,7 @@ func TestHandler_CreateDatabase_ARNFormat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
db := out["Database"].(map[string]any)
arn := db["Arn"].(string)
@@ -759,7 +761,7 @@ func TestHandler_CreateDatabase_TimestampsAreFloats(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
db := raw["Database"].(map[string]any)
ct, ok := db["CreationTime"].(float64)
@@ -786,7 +788,7 @@ func TestHandler_CreateDatabase_WithTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// Fetch the ARN then list tags
db := out["Database"].(map[string]any)
@@ -798,7 +800,7 @@ func TestHandler_CreateDatabase_WithTags(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var tagsOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsOut, jsonx.V1()))
tags := tagsOut["Tags"].([]any)
assert.Len(t, tags, 2)
diff --git a/services/timestreamwrite/handler_endpoints_test.go b/services/timestreamwrite/handler_endpoints_test.go
index 6f6345aca0..d73383e251 100644
--- a/services/timestreamwrite/handler_endpoints_test.go
+++ b/services/timestreamwrite/handler_endpoints_test.go
@@ -2,7 +2,7 @@ package timestreamwrite_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_DescribeEndpoints(t *testing.T) {
@@ -20,7 +22,7 @@ func TestHandler_DescribeEndpoints(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
endpoints, ok := resp["Endpoints"].([]any)
assert.True(t, ok)
assert.NotEmpty(t, endpoints)
@@ -40,7 +42,7 @@ func TestHandler_DescribeEndpoints_CachePeriodIs1440(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
endpoints := out["Endpoints"].([]any)
require.NotEmpty(t, endpoints)
@@ -60,7 +62,7 @@ func TestHandler_DescribeEndpoints_CachePeriodPositive(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
endpoints, ok := out["Endpoints"].([]any)
require.True(t, ok)
@@ -95,7 +97,7 @@ func TestHandler_DescribeEndpoints_EchoesRequestHost(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
endpoints := resp["Endpoints"].([]any)
require.NotEmpty(t, endpoints)
diff --git a/services/timestreamwrite/handler_internal_error_test.go b/services/timestreamwrite/handler_internal_error_test.go
index 67659813f0..5548926aab 100644
--- a/services/timestreamwrite/handler_internal_error_test.go
+++ b/services/timestreamwrite/handler_internal_error_test.go
@@ -1,7 +1,7 @@
package timestreamwrite //nolint:testpackage // needs access to the unexported handleError method.
import (
- "encoding/json"
+ "encoding/json/v2"
"errors"
"net/http"
"net/http/httptest"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errUnmatchedForTest = errors.New("boom: matches none of handleError's sentinel checks")
@@ -46,6 +48,6 @@ func TestHandleError_DefaultBranchEmitsInternalServerException(t *testing.T) {
assert.Equal(t, http.StatusInternalServerError, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InternalServerException", body[keyTypeField])
}
diff --git a/services/timestreamwrite/handler_records_test.go b/services/timestreamwrite/handler_records_test.go
index f555fc289f..6402f0cca6 100644
--- a/services/timestreamwrite/handler_records_test.go
+++ b/services/timestreamwrite/handler_records_test.go
@@ -1,7 +1,7 @@
package timestreamwrite_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/timestreamwrite"
)
@@ -149,7 +150,7 @@ func TestHandler_WriteRecords_RequiresMeasureName(t *testing.T) {
if tt.wantCode == http.StatusBadRequest {
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.NotEmpty(t, errResp["__type"], "error response must include __type")
}
@@ -369,7 +370,7 @@ func TestHandler_WriteRecords_InvalidMeasureValueType(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
})
@@ -450,7 +451,7 @@ func TestHandler_WriteRecords_InvalidTimeUnit(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
})
@@ -527,7 +528,7 @@ func TestHandler_WriteRecords_DimensionCountLimit(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
})
@@ -562,7 +563,7 @@ func TestHandler_WriteRecords_InvalidMeasureValueTypeViaCommonAttributes(t *test
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
@@ -594,7 +595,7 @@ func TestHandler_WriteRecords_InvalidTimeUnitViaCommonAttributes(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
@@ -683,7 +684,7 @@ func TestHandler_WriteRecords_MultiMeasure(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
ingested := out["RecordsIngested"].(map[string]any)
assert.Equal(t, 1, int(ingested["Total"].(float64)))
@@ -722,7 +723,7 @@ func TestHandler_WriteRecords_CommonAttributes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
ingested := out["RecordsIngested"].(map[string]any)
assert.Equal(t, 1, int(ingested["Total"].(float64)))
@@ -770,7 +771,7 @@ func TestHandler_WriteRecords_CommonAttributesMerge(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
ingested := out["RecordsIngested"].(map[string]any)
assert.Equal(t, 2, int(ingested["Total"].(float64)))
@@ -814,7 +815,7 @@ func TestHandler_WriteRecords_CommonAttributesDimensionOverlapRejected(t *testin
require.Equal(t, http.StatusBadRequest, rec.Code)
var errBody map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "ValidationException", errBody["__type"])
}
@@ -891,7 +892,7 @@ func TestHandler_WriteRecords_RejectedRecordsException(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec2.Code)
var errBody map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errBody))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errBody, jsonx.V1()))
assert.Equal(t, "RejectedRecordsException", errBody["__type"])
rejected, ok := errBody["RejectedRecords"].([]any)
@@ -963,7 +964,7 @@ func TestHandler_WriteRecords_MaxRecordsLimit(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
})
@@ -988,7 +989,7 @@ func TestHandler_WriteRecords_EmptyBatch(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
ingested := out["RecordsIngested"].(map[string]any)
assert.Equal(t, 0, int(ingested["Total"].(float64)))
@@ -1035,7 +1036,7 @@ func TestHandler_WriteRecords_MagneticStoreInResponse(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
ingested := out["RecordsIngested"].(map[string]any)
assert.Equal(t, 1, int(ingested["Total"].(float64)))
@@ -1069,7 +1070,7 @@ func TestHandler_WriteRecords_ReturnsIngestedTotal(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
ingested := out["RecordsIngested"].(map[string]any)
assert.Equal(t, 2, int(ingested["Total"].(float64)))
@@ -1097,7 +1098,7 @@ func TestHandler_WriteRecords_ReturnsMemoryStoreCount(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
ingested := out["RecordsIngested"].(map[string]any)
assert.Equal(t, int(3), int(ingested["Total"].(float64)))
diff --git a/services/timestreamwrite/handler_tables_test.go b/services/timestreamwrite/handler_tables_test.go
index 769c5d3ac4..411b444d9c 100644
--- a/services/timestreamwrite/handler_tables_test.go
+++ b/services/timestreamwrite/handler_tables_test.go
@@ -1,7 +1,7 @@
package timestreamwrite_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"net/http"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/timestreamwrite"
)
@@ -119,7 +120,7 @@ func TestHandler_ListTables(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tbls, ok := resp["Tables"].([]any)
assert.True(t, ok)
assert.Len(t, tbls, 2)
@@ -150,7 +151,7 @@ func TestHandler_ListTables_DatabaseNameOptional(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tbls, ok := resp["Tables"].([]any)
require.True(t, ok)
assert.Len(t, tbls, 2)
@@ -255,7 +256,7 @@ func TestHandler_ListTables_Sorted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbls := out["Tables"].([]any)
require.Len(t, tbls, 2)
@@ -278,7 +279,7 @@ func TestHandler_ListTables_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
tbls1 := out1["Tables"].([]any)
assert.Len(t, tbls1, 2)
@@ -294,7 +295,7 @@ func TestHandler_ListTables_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
tbls2 := out2["Tables"].([]any)
assert.Len(t, tbls2, 1)
@@ -323,7 +324,7 @@ func TestHandler_CreateTable_WithSchema(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
schema, ok := tbl["Schema"].(map[string]any)
@@ -361,7 +362,7 @@ func TestHandler_DescribeTable_ReturnsSchema(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
schema, ok := tbl["Schema"].(map[string]any)
@@ -394,7 +395,7 @@ func TestHandler_UpdateTable_WithSchema(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
schema := tbl["Schema"].(map[string]any)
@@ -423,7 +424,7 @@ func TestHandler_ListTables_IncludesSchema(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tables := out["Tables"].([]any)
require.Len(t, tables, 1)
@@ -458,7 +459,7 @@ func TestHandler_CreateTable_MagneticStoreRejectedDataLocation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
mswp := tbl["MagneticStoreWriteProperties"].(map[string]any)
@@ -501,7 +502,7 @@ func TestHandler_DescribeTable_MagneticStoreRejectedDataLocationRoundTrip(t *tes
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
mswp := tbl["MagneticStoreWriteProperties"].(map[string]any)
@@ -578,7 +579,7 @@ func TestHandler_CreateTable_NameFormatValidation(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
})
@@ -690,7 +691,7 @@ func TestHandler_CreateTable_RetentionPropertiesRangeValidation(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
})
@@ -817,7 +818,7 @@ func TestHandler_CreateTable_PartitionKeyTypeValidation(t *testing.T) {
if tt.wantStatus == http.StatusBadRequest {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
})
@@ -846,7 +847,7 @@ func TestHandler_UpdateTable_PartitionKeyValidation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
// Valid DIMENSION with Name should succeed.
@@ -879,7 +880,7 @@ func TestHandler_CreateTable_TagValidation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ValidationException", body["__type"])
}
@@ -897,7 +898,7 @@ func TestHandler_CreateTable_ConflictException(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ConflictException", body["__type"])
}
@@ -916,7 +917,7 @@ func TestHandler_CreateTable_StatusActive(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
assert.Equal(t, "ACTIVE", tbl["TableStatus"])
@@ -941,7 +942,7 @@ func TestHandler_DescribeTable_StatusActive(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
assert.Equal(t, "ACTIVE", tbl["TableStatus"])
@@ -966,7 +967,7 @@ func TestHandler_ListTables_StatusActive(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tables := out["Tables"].([]any)
require.Len(t, tables, 3)
@@ -994,7 +995,7 @@ func TestHandler_CreateTable_ARNFormat(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
arn := tbl["Arn"].(string)
@@ -1018,7 +1019,7 @@ func TestHandler_CreateTable_TimestampsAreFloats(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
tbl := raw["Table"].(map[string]any)
ct, ok := tbl["CreationTime"].(float64)
@@ -1043,7 +1044,7 @@ func TestHandler_CreateTable_WithTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
arn := tbl["Arn"].(string)
@@ -1052,7 +1053,7 @@ func TestHandler_CreateTable_WithTags(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var tagsOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsOut, jsonx.V1()))
tags := tagsOut["Tags"].([]any)
assert.Len(t, tags, 1)
@@ -1081,7 +1082,7 @@ func TestHandler_CreateTable_RetentionProperties(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
rp := tbl["RetentionProperties"].(map[string]any)
@@ -1108,7 +1109,7 @@ func TestHandler_CreateTable_MagneticStoreWriteProperties(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
mswp := tbl["MagneticStoreWriteProperties"].(map[string]any)
@@ -1136,7 +1137,7 @@ func TestHandler_UpdateTable_RetentionProperties(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
rp := tbl["RetentionProperties"].(map[string]any)
@@ -1170,7 +1171,7 @@ func TestHandler_UpdateTable_MagneticStoreWriteProperties(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
mswp := tbl["MagneticStoreWriteProperties"].(map[string]any)
assert.Equal(t, true, mswp["EnableMagneticStoreWrites"])
@@ -1224,7 +1225,7 @@ func TestHandler_CreateTable_DefaultRetentionProperties(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
rp := tbl["RetentionProperties"].(map[string]any)
@@ -1250,7 +1251,7 @@ func TestHandler_CreateTable_NoPropertiesReturnsDefaults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
rp, hasRP := tbl["RetentionProperties"]
@@ -1290,7 +1291,7 @@ func TestHandler_DescribeTable_RetentionAfterUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tbl := out["Table"].(map[string]any)
rp := tbl["RetentionProperties"].(map[string]any)
@@ -1318,7 +1319,7 @@ func TestHandler_ListTables_IncludesRetentionProperties(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
tables := out["Tables"].([]any)
require.Len(t, tables, 1)
diff --git a/services/timestreamwrite/handler_tags_test.go b/services/timestreamwrite/handler_tags_test.go
index 3938c6e4f6..7bcf0b8c4c 100644
--- a/services/timestreamwrite/handler_tags_test.go
+++ b/services/timestreamwrite/handler_tags_test.go
@@ -1,13 +1,15 @@
package timestreamwrite_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_TagResource_UntagResource_ListTagsForResource(t *testing.T) {
@@ -34,7 +36,7 @@ func TestHandler_TagResource_UntagResource_ListTagsForResource(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags, ok := listResp["Tags"].([]any)
assert.True(t, ok)
assert.Len(t, tags, 2)
@@ -48,7 +50,7 @@ func TestHandler_TagResource_UntagResource_ListTagsForResource(t *testing.T) {
rec = doRequest(t, h, "ListTagsForResource", map[string]string{"ResourceARN": arn})
assert.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tags, ok = listResp["Tags"].([]any)
assert.True(t, ok)
assert.Len(t, tags, 1)
@@ -96,7 +98,7 @@ func TestHandler_ListTagsForResource_Sorted(t *testing.T) {
descRec := doRequest(t, h, "DescribeDatabase", map[string]any{"DatabaseName": "sort-db"})
var descOut map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
arn := descOut["Database"].(map[string]any)["Arn"].(string)
doRequest(t, h, "TagResource", map[string]any{
@@ -112,7 +114,7 @@ func TestHandler_ListTagsForResource_Sorted(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var tagsOut map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &tagsOut, jsonx.V1()))
tags := tagsOut["Tags"].([]any)
require.Len(t, tags, 3)
@@ -185,7 +187,7 @@ func TestHandler_TagResource_HTTP400OnUnknownARN(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", body["__type"])
}
@@ -207,7 +209,7 @@ func TestHandler_TagResource_ScheduledQueryARN(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
tags := out["Tags"].([]any)
require.Len(t, tags, 1)
@@ -249,7 +251,7 @@ func TestHandler_ListTagsForResource_EmptyWhenNoTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// Tags field should be present and empty (not absent).
tags, ok := out["Tags"]
@@ -287,7 +289,7 @@ func TestHandler_UntagResource_FromKnownARN(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
tags := out["Tags"].([]any)
require.Len(t, tags, 1)
@@ -327,7 +329,7 @@ func TestHandler_TagResource_CumulativeUpdates(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &out, jsonx.V1()))
tags := out["Tags"].([]any)
require.Len(t, tags, 2, "should have two distinct tags")
diff --git a/services/timestreamwrite/handler_test.go b/services/timestreamwrite/handler_test.go
index e732428fdd..7e47171be2 100644
--- a/services/timestreamwrite/handler_test.go
+++ b/services/timestreamwrite/handler_test.go
@@ -2,7 +2,7 @@ package timestreamwrite_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strconv"
@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/timestreamwrite"
)
@@ -78,7 +79,7 @@ func doRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
diff --git a/services/timestreamwrite/multi_region_test.go b/services/timestreamwrite/multi_region_test.go
index 1c37796172..cb5a3a8398 100644
--- a/services/timestreamwrite/multi_region_test.go
+++ b/services/timestreamwrite/multi_region_test.go
@@ -2,7 +2,8 @@ package timestreamwrite_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/timestreamwrite"
)
@@ -173,8 +175,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/timestreamwrite/partition_key_enforcement_test.go b/services/timestreamwrite/partition_key_enforcement_test.go
index cf091d750b..c0230b541b 100644
--- a/services/timestreamwrite/partition_key_enforcement_test.go
+++ b/services/timestreamwrite/partition_key_enforcement_test.go
@@ -1,12 +1,14 @@
package timestreamwrite_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_WriteRecords_RequiredPartitionKey(t *testing.T) {
@@ -63,7 +65,7 @@ func TestHandler_WriteRecords_RequiredPartitionKey(t *testing.T) {
if tt.wantRejected {
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Contains(t, out["RejectedRecords"].([]any)[0].(map[string]any)["Reason"], "region")
}
})
diff --git a/services/timestreamwrite/persistence.go b/services/timestreamwrite/persistence.go
index 50cedf4c66..907fb25d82 100644
--- a/services/timestreamwrite/persistence.go
+++ b/services/timestreamwrite/persistence.go
@@ -2,10 +2,12 @@ package timestreamwrite
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
@@ -52,7 +54,7 @@ const timestreamwriteSnapshotVersion = 1
// snapshot from an incompatible (older or newer) build of this backend as
// though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Records map[string]map[string][]Record `json:"records"`
Tags map[string]map[string]string `json:"tags"`
NextTaskID int `json:"next_task_id"`
@@ -107,7 +109,7 @@ func (b *InMemoryBackend) Snapshot() ([]byte, error) {
// Marshal failure is returned to the caller (the Persistable wrapper logs it
// via the context-aware logger); do not log through slog.Default() here.
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
return nil, fmt.Errorf("timestreamwrite: marshal snapshot: %w", err)
}
diff --git a/services/transcribe/call_analytics_test.go b/services/transcribe/call_analytics_test.go
index 98eb1e52dc..5f55131c5c 100644
--- a/services/transcribe/call_analytics_test.go
+++ b/services/transcribe/call_analytics_test.go
@@ -1,7 +1,8 @@
package transcribe_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transcribe"
)
@@ -515,7 +517,7 @@ func TestCallAnalyticsJobIncludesTranscriptURI(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
job, ok := resp["CallAnalyticsJob"].(map[string]any)
require.True(t, ok, "CallAnalyticsJob must be present")
@@ -557,7 +559,7 @@ func TestChannelId_JSONKey(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw, jsonx.V1()))
job, _ := raw["CallAnalyticsJob"].(map[string]any)
require.NotNil(t, job, "CallAnalyticsJob must be present")
@@ -651,16 +653,21 @@ func TestCallAnalyticsCategory_CreateTimeAndLastUpdateTimeEchoed(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code, createRec.Body.String())
var createRaw struct {
- CategoryProperties map[string]json.RawMessage `json:"CategoryProperties"`
+ CategoryProperties map[string]jsontext.Value `json:"CategoryProperties"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createRaw))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createRaw, jsonx.V1()))
for _, field := range []string{"CreateTime", "LastUpdateTime"} {
val, ok := createRaw.CategoryProperties[field]
require.True(t, ok, "expected field %s in CreateCallAnalyticsCategory response", field)
- var num json.Number
- require.NoError(t, json.Unmarshal(val, &num), "field %s must be a JSON number (epoch seconds)", field)
+ var num jsonx.Number
+ require.NoError(
+ t,
+ json.Unmarshal(val, &num, jsonx.V1()),
+ "field %s must be a JSON number (epoch seconds)",
+ field,
+ )
}
updateRec := doTranscribeRequest(t, h, "UpdateCallAnalyticsCategory", map[string]any{
@@ -673,9 +680,9 @@ func TestCallAnalyticsCategory_CreateTimeAndLastUpdateTimeEchoed(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateRaw struct {
- CategoryProperties map[string]json.RawMessage `json:"CategoryProperties"`
+ CategoryProperties map[string]jsontext.Value `json:"CategoryProperties"`
}
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateRaw))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateRaw, jsonx.V1()))
assert.Contains(t, updateRaw.CategoryProperties, "LastUpdateTime")
}
@@ -706,9 +713,9 @@ func TestListCallAnalyticsJobs_JobNameContainsAndStartTime(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var raw struct {
- CallAnalyticsJobSummaries []map[string]json.RawMessage `json:"CallAnalyticsJobSummaries"`
+ CallAnalyticsJobSummaries []map[string]jsontext.Value `json:"CallAnalyticsJobSummaries"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &raw, jsonx.V1()))
require.Len(t, raw.CallAnalyticsJobSummaries, 1)
assert.Contains(t, raw.CallAnalyticsJobSummaries[0], "StartTime")
}
diff --git a/services/transcribe/handler.go b/services/transcribe/handler.go
index b93734b9e0..92b599043c 100644
--- a/services/transcribe/handler.go
+++ b/services/transcribe/handler.go
@@ -2,7 +2,7 @@ package transcribe
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -106,7 +107,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var req extractResourceRequest
- if jsonErr := json.Unmarshal(body, &req); jsonErr != nil {
+ if jsonErr := json.Unmarshal(body, &req, jsonx.V1()); jsonErr != nil {
return ""
}
@@ -215,20 +216,17 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, awserr.ErrNotFound):
return c.JSON(http.StatusNotFound, errorBody("NotFoundException", errMessage(err)))
case errors.Is(err, awserr.ErrAlreadyExists):
return c.JSON(http.StatusConflict, errorBody("ConflictException", errMessage(err)))
case errors.Is(err, awserr.ErrInvalidParameter), errors.Is(err, errInvalidRequest),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
return c.JSON(http.StatusBadRequest, errorBody("BadRequestException", errMessage(err)))
case errors.Is(err, errUnknownAction):
return c.JSON(http.StatusBadRequest, errorBody("UnknownOperationException", errMessage(err)))
diff --git a/services/transcribe/handler_test.go b/services/transcribe/handler_test.go
index 020306fe75..1da37a246e 100644
--- a/services/transcribe/handler_test.go
+++ b/services/transcribe/handler_test.go
@@ -2,7 +2,8 @@ package transcribe_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transcribe"
)
@@ -35,7 +37,7 @@ func newHandlerWithBackend(t *testing.T) (*transcribe.Handler, *transcribe.InMem
func doTranscribeRequest(t *testing.T, h *transcribe.Handler, action string, body any) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -502,7 +504,7 @@ func TestTranscribe_BadParams_NotInternalFailure(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"], "expected %s, not InternalFailureException", tt.wantType)
})
@@ -671,13 +673,13 @@ func TestTranscribe_TimestampFields_AreJSONNumbers(t *testing.T) {
rec := doTranscribeRequest(t, h, tt.action, tt.body)
require.Equal(t, http.StatusOK, rec.Code)
- var raw map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ var raw map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
// The response wraps the resource under a single top-level key; find it.
- var inner map[string]json.RawMessage
+ var inner map[string]jsontext.Value
for _, v := range raw {
- _ = json.Unmarshal(v, &inner)
+ _ = json.Unmarshal(v, &inner, jsonx.V1())
if inner != nil {
break
@@ -689,8 +691,8 @@ func TestTranscribe_TimestampFields_AreJSONNumbers(t *testing.T) {
val, ok := inner[field]
require.True(t, ok, "expected field %s in response %s", field, rec.Body.String())
- var num json.Number
- err := json.Unmarshal(val, &num)
+ var num jsonx.Number
+ err := json.Unmarshal(val, &num, jsonx.V1())
assert.NoError(t, err, "field %s must decode as a JSON number (epoch seconds), got %s", field, val)
}
})
diff --git a/services/transcribe/language_models_test.go b/services/transcribe/language_models_test.go
index 8014cb228d..d4ec69f7b3 100644
--- a/services/transcribe/language_models_test.go
+++ b/services/transcribe/language_models_test.go
@@ -1,13 +1,15 @@
package transcribe_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transcribe"
)
@@ -289,7 +291,7 @@ func TestDescribeLanguageModel_TimeFields(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw, jsonx.V1()))
lm, _ := raw["LanguageModel"].(map[string]any)
require.NotNil(t, lm, "LanguageModel must be present")
@@ -320,7 +322,7 @@ func TestDescribeLanguageModel_UpgradeAvailability(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw, jsonx.V1()))
lm, _ := raw["LanguageModel"].(map[string]any)
require.NotNil(t, lm)
@@ -354,16 +356,16 @@ func TestDescribeLanguageModel_TimestampFieldsAreJSONNumbers(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var out struct {
- LanguageModel map[string]json.RawMessage `json:"LanguageModel"`
+ LanguageModel map[string]jsontext.Value `json:"LanguageModel"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
for _, field := range []string{"CreateTime", "LastModifiedTime"} {
val, ok := out.LanguageModel[field]
require.True(t, ok, "expected field %s in response %s", field, rec.Body.String())
- var num json.Number
- err := json.Unmarshal(val, &num)
+ var num jsonx.Number
+ err := json.Unmarshal(val, &num, jsonx.V1())
assert.NoError(t, err, "field %s must decode as a JSON number (epoch seconds), got %s", field, val)
}
}
@@ -436,7 +438,7 @@ func TestCreateLanguageModel_EchoesInputDataConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
cfg, ok := raw["InputDataConfig"].(map[string]any)
require.True(t, ok, "CreateLanguageModelOutput must echo InputDataConfig")
assert.Equal(t, "s3://my-bucket/training/", cfg["S3Uri"])
diff --git a/services/transcribe/medical_scribe_test.go b/services/transcribe/medical_scribe_test.go
index aa97e6bb99..02660a619d 100644
--- a/services/transcribe/medical_scribe_test.go
+++ b/services/transcribe/medical_scribe_test.go
@@ -1,13 +1,15 @@
package transcribe_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transcribe"
)
@@ -292,9 +294,9 @@ func TestListMedicalScribeJobs_JobNameContainsAndSummaryShape(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var raw struct {
- MedicalScribeJobSummaries []map[string]json.RawMessage `json:"MedicalScribeJobSummaries"`
+ MedicalScribeJobSummaries []map[string]jsontext.Value `json:"MedicalScribeJobSummaries"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &raw, jsonx.V1()))
require.Len(t, raw.MedicalScribeJobSummaries, 1)
summary := raw.MedicalScribeJobSummaries[0]
@@ -334,7 +336,7 @@ func TestMedicalScribeJob_OutputURIsPresentWhenCompleted(t *testing.T) {
} `json:"MedicalScribeOutput"`
} `json:"MedicalScribeJob"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &raw, jsonx.V1()))
assert.Contains(t, raw.MedicalScribeJob.MedicalScribeOutput.ClinicalDocumentURI, "scribe-output-bucket")
assert.Contains(t, raw.MedicalScribeJob.MedicalScribeOutput.TranscriptFileURI, "scribe-output-bucket")
}
diff --git a/services/transcribe/medical_transcription_jobs_test.go b/services/transcribe/medical_transcription_jobs_test.go
index ff4777b6d3..e552ccbf4d 100644
--- a/services/transcribe/medical_transcription_jobs_test.go
+++ b/services/transcribe/medical_transcription_jobs_test.go
@@ -1,7 +1,8 @@
package transcribe_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transcribe"
)
@@ -121,7 +123,7 @@ func TestHTTP_StartMedicalTranscriptionJob_ContentIdentificationType(t *testing.
require.Equal(t, http.StatusOK, rec.Code)
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
job := out["MedicalTranscriptionJob"].(map[string]any)
assert.Equal(t, "PHI", job["ContentIdentificationType"])
}
@@ -272,7 +274,7 @@ func TestMedicalTranscriptionJobTranscriptURI(t *testing.T) {
} `json:"Transcript"`
} `json:"MedicalTranscriptionJob"`
}
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &out, jsonx.V1()))
uri := out.MedicalTranscriptionJob.Transcript.TranscriptFileURI
assert.NotEmpty(t, uri, "Transcript.TranscriptFileURI must not be empty")
@@ -312,7 +314,7 @@ func TestStartMedicalTranscriptionJob_TranscriptURIInStartResponse(t *testing.T)
} `json:"Transcript"`
} `json:"MedicalTranscriptionJob"`
}
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &out, jsonx.V1()))
uri := out.MedicalTranscriptionJob.Transcript.TranscriptFileURI
assert.NotEmpty(t, uri, "StartMedicalTranscriptionJob response must include TranscriptFileUri")
@@ -369,9 +371,9 @@ func TestListMedicalTranscriptionJobs_JobNameContainsAndSummaryShape(t *testing.
require.Equal(t, http.StatusOK, listRec.Code)
var raw struct {
- MedicalTranscriptionJobSummaries []map[string]json.RawMessage `json:"MedicalTranscriptionJobSummaries"`
+ MedicalTranscriptionJobSummaries []map[string]jsontext.Value `json:"MedicalTranscriptionJobSummaries"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &raw, jsonx.V1()))
require.Len(t, raw.MedicalTranscriptionJobSummaries, 1)
summary := raw.MedicalTranscriptionJobSummaries[0]
@@ -380,6 +382,6 @@ func TestListMedicalTranscriptionJobs_JobNameContainsAndSummaryShape(t *testing.
assert.NotContains(t, summary, "Tags", "MedicalTranscriptionJobSummary must not include Tags")
var outputLocationType string
- require.NoError(t, json.Unmarshal(summary["OutputLocationType"], &outputLocationType))
+ require.NoError(t, json.Unmarshal(summary["OutputLocationType"], &outputLocationType, jsonx.V1()))
assert.Equal(t, "CUSTOMER_BUCKET", outputLocationType)
}
diff --git a/services/transcribe/medical_vocabularies_test.go b/services/transcribe/medical_vocabularies_test.go
index eb61bc2c16..645d396562 100644
--- a/services/transcribe/medical_vocabularies_test.go
+++ b/services/transcribe/medical_vocabularies_test.go
@@ -1,13 +1,14 @@
package transcribe_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transcribe"
)
@@ -160,7 +161,7 @@ func TestGetMedicalVocabulary_NotFound(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "BadRequestException", body["__type"])
})
}
@@ -201,7 +202,7 @@ func TestMedicalVocabulary_LastModifiedTimeAndFailureReason(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code, createRec.Body.String())
var createRaw map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createRaw))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createRaw, jsonx.V1()))
assert.Contains(t, createRaw, "LastModifiedTime")
getRec := doTranscribeRequest(t, h, "GetMedicalVocabulary", map[string]any{
@@ -210,7 +211,7 @@ func TestMedicalVocabulary_LastModifiedTimeAndFailureReason(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getRaw map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getRaw))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getRaw, jsonx.V1()))
_, hasFailureReason := getRaw["FailureReason"]
assert.False(t, hasFailureReason, "FailureReason must be omitted (empty) on success")
@@ -221,6 +222,6 @@ func TestMedicalVocabulary_LastModifiedTimeAndFailureReason(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code)
var updateRaw map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateRaw))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateRaw, jsonx.V1()))
assert.Contains(t, updateRaw, "LastModifiedTime")
}
diff --git a/services/transcribe/multi_region_test.go b/services/transcribe/multi_region_test.go
index 45aa198007..c3410b63f6 100644
--- a/services/transcribe/multi_region_test.go
+++ b/services/transcribe/multi_region_test.go
@@ -2,7 +2,8 @@ package transcribe_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/transcribe"
)
@@ -182,8 +184,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/transcribe/persistence.go b/services/transcribe/persistence.go
index 0ba1cd05cb..d647308592 100644
--- a/services/transcribe/persistence.go
+++ b/services/transcribe/persistence.go
@@ -2,9 +2,11 @@ package transcribe
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -34,7 +36,7 @@ const transcribeSnapshotVersion = 2
// decoding a snapshot from an incompatible (older or newer) build of this
// backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ResourceTags map[string]map[string]string `json:"resourceTags"`
Version int `json:"version"`
}
@@ -57,7 +59,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
b.mu.RLock("Snapshot")
defer b.mu.RUnlock()
- var tables map[string]json.RawMessage
+ var tables map[string]jsontext.Value
tables, tblErr = b.registry.SnapshotAll()
@@ -74,7 +76,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
return nil
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "transcribe: failed to marshal snapshot", "error", err)
diff --git a/services/transcribe/realism_test.go b/services/transcribe/realism_test.go
index d3cf01ac9c..6d14402b79 100644
--- a/services/transcribe/realism_test.go
+++ b/services/transcribe/realism_test.go
@@ -1,7 +1,7 @@
package transcribe_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"testing/synctest"
"time"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transcribe"
)
@@ -115,7 +116,7 @@ func TestRealism_RequestValidation(t *testing.T) {
rec := doTranscribeRequest(t, newTestTranscribeHandler(t), tt.action, tt.body)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
assert.Contains(t, resp["message"], tt.wantMsg)
assert.NotContains(t, resp["message"], "Exception: ")
diff --git a/services/transcribe/s3_output_test.go b/services/transcribe/s3_output_test.go
index f7cbea84f4..82dc9c203f 100644
--- a/services/transcribe/s3_output_test.go
+++ b/services/transcribe/s3_output_test.go
@@ -2,7 +2,8 @@ package transcribe_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"sync"
"testing"
@@ -10,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transcribe"
)
@@ -84,10 +86,10 @@ func TestTranscriptionJobWritesOutput(t *testing.T) {
Transcripts []struct {
Transcript string `json:"transcript"`
} `json:"transcripts"`
- Items []json.RawMessage `json:"items"`
+ Items []jsontext.Value `json:"items"`
} `json:"results"`
}
- require.NoError(t, json.Unmarshal(w.objects[tt.wantKey], &doc))
+ require.NoError(t, json.Unmarshal(w.objects[tt.wantKey], &doc, jsonx.V1()))
assert.Equal(t, "j", doc.JobName)
assert.Equal(t, "COMPLETED", doc.Status)
assert.NotEmpty(t, doc.Results.Transcripts)
@@ -146,7 +148,7 @@ func TestCallAnalyticsJobWritesOutput(t *testing.T) {
}
var doc map[string]any
- require.NoError(t, json.Unmarshal(w.objects[tt.wantKey], &doc))
+ require.NoError(t, json.Unmarshal(w.objects[tt.wantKey], &doc, jsonx.V1()))
assert.Equal(t, "COMPLETED", doc["JobStatus"])
assert.NotEmpty(t, doc["Transcript"])
assert.NotEmpty(t, doc["Participants"])
diff --git a/services/transcribe/tags_test.go b/services/transcribe/tags_test.go
index f8f96d0225..b1f583a9b2 100644
--- a/services/transcribe/tags_test.go
+++ b/services/transcribe/tags_test.go
@@ -1,13 +1,14 @@
package transcribe_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transcribe"
)
@@ -307,7 +308,7 @@ func TestHTTP_ListTagsForResource_ReturnsStoredTags(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tagMap := make(map[string]string, len(resp.Tags))
for _, tag := range resp.Tags {
tagMap[tag.Key] = tag.Value
@@ -344,7 +345,7 @@ func TestHTTP_UntagResource(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tagMap := make(map[string]string, len(resp.Tags))
for _, tag := range resp.Tags {
tagMap[tag.Key] = tag.Value
diff --git a/services/transcribe/transcript_synthesis.go b/services/transcribe/transcript_synthesis.go
index fc8a8bc5e6..9c91ea9f3d 100644
--- a/services/transcribe/transcript_synthesis.go
+++ b/services/transcribe/transcript_synthesis.go
@@ -1,9 +1,11 @@
package transcribe
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// syntheticWordBaseDuration is the base duration in seconds for synthetic word timing.
@@ -17,7 +19,7 @@ const syntheticWordGap = 0.05
// marshalJSON marshals v to JSON bytes, returning nil on error (only used for synthetic output).
func marshalJSON(v any) ([]byte, error) {
- return json.Marshal(v)
+ return json.Marshal(v, jsonx.V1())
}
// deriveTranscriptText produces a transcript string from the job name and media URI.
diff --git a/services/transcribe/transcription_jobs_test.go b/services/transcribe/transcription_jobs_test.go
index de1ac43899..487737e88c 100644
--- a/services/transcribe/transcription_jobs_test.go
+++ b/services/transcribe/transcription_jobs_test.go
@@ -1,7 +1,7 @@
package transcribe_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transcribe"
)
@@ -898,7 +899,7 @@ func TestListTranscriptionJobsPagination(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, summariesOK := resp["TranscriptionJobSummaries"].([]any)
require.True(t, summariesOK)
@@ -919,7 +920,7 @@ func TestListTranscriptionJobsPagination(t *testing.T) {
assert.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
summaries2, summaries2OK := resp2["TranscriptionJobSummaries"].([]any)
require.True(t, summaries2OK)
@@ -1069,7 +1070,7 @@ func TestMediaFileUri_JSONKey(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw, jsonx.V1()))
job, _ := raw["TranscriptionJob"].(map[string]any)
require.NotNil(t, job, "TranscriptionJob must be present")
@@ -1102,7 +1103,7 @@ func TestTranscriptionJob_MediaInResponse(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &raw, jsonx.V1()))
job, _ := raw["TranscriptionJob"].(map[string]any)
require.NotNil(t, job)
@@ -1131,7 +1132,7 @@ func TestTranscriptionJob_OutputBucketNotInResponse(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
job, _ := raw["TranscriptionJob"].(map[string]any)
require.NotNil(t, job)
@@ -1194,7 +1195,7 @@ func TestTranscriptionJob_LanguageIdSettings_Echoed(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
job, _ := raw["TranscriptionJob"].(map[string]any)
require.NotNil(t, job)
diff --git a/services/transcribe/vocabularies_test.go b/services/transcribe/vocabularies_test.go
index 2dcb0f5ece..26f810a331 100644
--- a/services/transcribe/vocabularies_test.go
+++ b/services/transcribe/vocabularies_test.go
@@ -1,7 +1,7 @@
package transcribe_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transcribe"
)
@@ -278,7 +279,7 @@ func TestGetVocabulary_DownloadUri(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &raw, jsonx.V1()))
assert.NotEmpty(t, raw["DownloadUri"], "DownloadUri must be present in GetVocabulary response")
}
@@ -301,7 +302,7 @@ func TestGetVocabulary_LastModifiedTime(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var raw map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &raw, jsonx.V1()))
assert.NotNil(t, raw["LastModifiedTime"], "LastModifiedTime must be present in GetVocabulary response")
}
@@ -464,7 +465,7 @@ func TestCreateVocabulary_LastModifiedTimeAndFailureReasonEchoed(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
_, hasLastModified := raw["LastModifiedTime"]
assert.True(t, hasLastModified, "CreateVocabularyOutput must include LastModifiedTime")
@@ -488,6 +489,6 @@ func TestListVocabularies_EchoesStatusFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var raw map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &raw, jsonx.V1()))
assert.Equal(t, "READY", raw["Status"])
}
diff --git a/services/transcribe/vocabulary_filters_test.go b/services/transcribe/vocabulary_filters_test.go
index eb4b4ac4fb..dd099ab53b 100644
--- a/services/transcribe/vocabulary_filters_test.go
+++ b/services/transcribe/vocabulary_filters_test.go
@@ -1,13 +1,14 @@
package transcribe_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transcribe"
)
@@ -340,7 +341,7 @@ func TestVocabularyFilter_LastModifiedTimeAndDownloadUri(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code, createRec.Body.String())
var createRaw map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createRaw))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createRaw, jsonx.V1()))
assert.Contains(t, createRaw, "LastModifiedTime", "CreateVocabularyFilterOutput must include LastModifiedTime")
getRec := doTranscribeRequest(t, h, "GetVocabularyFilter", map[string]any{
@@ -349,7 +350,7 @@ func TestVocabularyFilter_LastModifiedTimeAndDownloadUri(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code, getRec.Body.String())
var getRaw map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getRaw))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getRaw, jsonx.V1()))
assert.Contains(t, getRaw, "LastModifiedTime", "GetVocabularyFilterOutput must include LastModifiedTime")
assert.Equal(t, "s3://bucket/filter.txt", getRaw["DownloadUri"])
@@ -360,6 +361,6 @@ func TestVocabularyFilter_LastModifiedTimeAndDownloadUri(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code, updateRec.Body.String())
var updateRaw map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateRaw))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateRaw, jsonx.V1()))
assert.Contains(t, updateRaw, "LastModifiedTime", "UpdateVocabularyFilterOutput must include LastModifiedTime")
}
diff --git a/services/transfer/handler.go b/services/transfer/handler.go
index 29e3f9c55c..e99c7501a9 100644
--- a/services/transfer/handler.go
+++ b/services/transfer/handler.go
@@ -2,7 +2,7 @@ package transfer
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/collections"
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
@@ -221,7 +222,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
ServerID string `json:"ServerId"`
UserName string `json:"UserName"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if req.ServerID != "" && req.UserName != "" {
return req.ServerID + "/" + req.UserName
@@ -340,13 +341,10 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, errInvalidNextToken):
return c.JSON(http.StatusBadRequest, map[string]string{
@@ -366,8 +364,8 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
case errors.Is(err, awserr.ErrInvalidParameter),
errors.Is(err, errInvalidRequest),
errors.Is(err, errUnknownAction),
- errors.As(err, &syntaxErr),
- errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err),
+ jsonx.IsTypeError(err):
return c.JSON(http.StatusBadRequest, map[string]string{
keyTypeField: "InvalidRequestException",
keyMessageField: publicMessage(err),
diff --git a/services/transfer/handler_access_test.go b/services/transfer/handler_access_test.go
index 14f7df68f3..464d323a65 100644
--- a/services/transfer/handler_access_test.go
+++ b/services/transfer/handler_access_test.go
@@ -1,7 +1,7 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transfer"
)
@@ -26,7 +27,7 @@ func TestHandler_UpdateAccessFullFieldsRoundtrip(t *testing.T) {
var srvOut struct {
ServerID string `json:"ServerId"`
}
- require.NoError(t, json.Unmarshal(createSrvRec.Body.Bytes(), &srvOut))
+ require.NoError(t, json.Unmarshal(createSrvRec.Body.Bytes(), &srvOut, jsonx.V1()))
createAccessRec := doTransferRequest(t, h, "CreateAccess", map[string]any{
"ServerId": srvOut.ServerID,
@@ -65,7 +66,7 @@ func TestHandler_UpdateAccessFullFieldsRoundtrip(t *testing.T) {
Policy string `json:"Policy"`
} `json:"Access"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, "LOGICAL", descOut.Access.HomeDirectoryType)
assert.JSONEq(t, `{"Version":"2012-10-17","Statement":[]}`, descOut.Access.Policy)
@@ -118,7 +119,7 @@ func TestHandler_ListAccessesIncludesHomeDirectoryType(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
accesses := listResp["Accesses"].([]any)
require.Len(t, accesses, 1)
@@ -167,7 +168,7 @@ func TestHandler_CreateAccess(t *testing.T) {
if tt.wantKey != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp[tt.wantKey])
}
})
@@ -302,7 +303,7 @@ func TestHandler_DescribeAccess(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Access"])
}
@@ -381,7 +382,7 @@ func TestHandler_CreateAccessPosixProfile(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
access := descResp["Access"].(map[string]any)
posix := access["PosixProfile"].(map[string]any)
diff --git a/services/transfer/handler_agreements_test.go b/services/transfer/handler_agreements_test.go
index 8f7b8ea524..2a59af863e 100644
--- a/services/transfer/handler_agreements_test.go
+++ b/services/transfer/handler_agreements_test.go
@@ -1,7 +1,7 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transfer"
)
@@ -23,7 +24,7 @@ func TestHandler_DescribeAgreementIncludesArnAndTags(t *testing.T) {
})
require.Equal(t, http.StatusOK, localRec.Code)
var localResp map[string]any
- require.NoError(t, json.Unmarshal(localRec.Body.Bytes(), &localResp))
+ require.NoError(t, json.Unmarshal(localRec.Body.Bytes(), &localResp, jsonx.V1()))
localProfileID := localResp["ProfileId"].(string)
partnerRec := doTransferRequest(t, h, "CreateProfile", map[string]any{
@@ -31,13 +32,13 @@ func TestHandler_DescribeAgreementIncludesArnAndTags(t *testing.T) {
})
require.Equal(t, http.StatusOK, partnerRec.Code)
var partnerResp map[string]any
- require.NoError(t, json.Unmarshal(partnerRec.Body.Bytes(), &partnerResp))
+ require.NoError(t, json.Unmarshal(partnerRec.Body.Bytes(), &partnerResp, jsonx.V1()))
partnerProfileID := partnerResp["ProfileId"].(string)
serverRec := doTransferRequest(t, h, "CreateServer", map[string]any{})
require.Equal(t, http.StatusOK, serverRec.Code)
var serverResp map[string]any
- require.NoError(t, json.Unmarshal(serverRec.Body.Bytes(), &serverResp))
+ require.NoError(t, json.Unmarshal(serverRec.Body.Bytes(), &serverResp, jsonx.V1()))
serverID := serverResp["ServerId"].(string)
agRec := doTransferRequest(t, h, "CreateAgreement", map[string]any{
@@ -50,7 +51,7 @@ func TestHandler_DescribeAgreementIncludesArnAndTags(t *testing.T) {
})
require.Equal(t, http.StatusOK, agRec.Code)
var agResp map[string]any
- require.NoError(t, json.Unmarshal(agRec.Body.Bytes(), &agResp))
+ require.NoError(t, json.Unmarshal(agRec.Body.Bytes(), &agResp, jsonx.V1()))
agreementID := agResp["AgreementId"].(string)
rec := doTransferRequest(t, h, "DescribeAgreement", map[string]any{
@@ -60,7 +61,7 @@ func TestHandler_DescribeAgreementIncludesArnAndTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ag := resp["Agreement"].(map[string]any)
arn, hasArn := ag["Arn"].(string)
@@ -116,7 +117,7 @@ func TestHandler_CreateAgreement(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["AgreementId"])
}
})
@@ -248,7 +249,7 @@ func TestHandler_DescribeAgreement(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
agreementID := createResp["AgreementId"].(string)
rec := doTransferRequest(t, h, "DescribeAgreement", map[string]any{
@@ -291,7 +292,7 @@ func TestHandler_UpdateAgreement(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
agreementID := createResp["AgreementId"].(string)
rec := doTransferRequest(t, h, "UpdateAgreement", map[string]any{
@@ -328,7 +329,7 @@ func TestHandler_ListAgreementsEnrichedFields(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
agreements := listResp["Agreements"].([]any)
require.Len(t, agreements, 1)
@@ -376,7 +377,7 @@ func TestHandler_CreateAgreementInactiveStatus(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
agID := createResp["AgreementId"].(string)
descRec := doTransferRequest(t, h, "DescribeAgreement", map[string]any{
@@ -386,7 +387,7 @@ func TestHandler_CreateAgreementInactiveStatus(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
ag := descResp["Agreement"].(map[string]any)
assert.Equal(t, "INACTIVE", ag["Status"])
}
diff --git a/services/transfer/handler_certificates_test.go b/services/transfer/handler_certificates_test.go
index d6ad6485a9..c3f42d3086 100644
--- a/services/transfer/handler_certificates_test.go
+++ b/services/transfer/handler_certificates_test.go
@@ -1,13 +1,14 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transfer"
)
@@ -79,7 +80,7 @@ func TestHandler_DescribeCertificateBodyReturnedWhenPresent(t *testing.T) {
var importOut struct {
CertificateID string `json:"CertificateId"`
}
- require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importOut))
+ require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importOut, jsonx.V1()))
descRec := doTransferRequest(t, h, "DescribeCertificate", map[string]any{
"CertificateId": importOut.CertificateID,
@@ -91,7 +92,7 @@ func TestHandler_DescribeCertificateBodyReturnedWhenPresent(t *testing.T) {
Certificate string `json:"Certificate"`
} `json:"Certificate"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, testCertPEM, descOut.Certificate.Certificate,
"DescribeCertificate must return the certificate body when present")
@@ -108,7 +109,7 @@ func TestHandler_DescribeCertificateIncludesArn(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
certID := createResp["CertificateId"].(string)
rec := doTransferRequest(t, h, "DescribeCertificate", map[string]any{
@@ -117,7 +118,7 @@ func TestHandler_DescribeCertificateIncludesArn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cert := resp["Certificate"].(map[string]any)
arn, hasArn := cert["Arn"].(string)
@@ -139,7 +140,7 @@ func TestHandler_ListCertificatesIncludesArn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
certs := resp["Certificates"].([]any)
require.NotEmpty(t, certs)
@@ -189,7 +190,7 @@ func TestHandler_CertificateARNContainsAccountAndRegion(t *testing.T) {
})
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
certID := createResp["CertificateId"].(string)
rec := doTransferRequest(t, h, "DescribeCertificate", map[string]any{
@@ -198,7 +199,7 @@ func TestHandler_CertificateARNContainsAccountAndRegion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cert := resp["Certificate"].(map[string]any)
// newTestHandler uses testAccountID = "123456789012" and testRegion = "us-east-1"
@@ -284,7 +285,7 @@ func TestHandler_DescribeCertificate(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
certID := createResp["CertificateId"].(string)
rec := doTransferRequest(t, h, "DescribeCertificate", map[string]any{
@@ -314,7 +315,7 @@ func TestHandler_UpdateCertificate(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
certID := createResp["CertificateId"].(string)
rec := doTransferRequest(t, h, "UpdateCertificate", map[string]any{
@@ -357,7 +358,7 @@ func TestHandler_ImportCertificateMalformedPEM(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", resp["__type"])
}
@@ -373,7 +374,7 @@ func TestHandler_ImportCertificateValidPEMExtractsDates(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
certID := createResp["CertificateId"].(string)
descRec := doTransferRequest(t, h, "DescribeCertificate", map[string]any{
@@ -382,7 +383,7 @@ func TestHandler_ImportCertificateValidPEMExtractsDates(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
cert := descResp["Certificate"].(map[string]any)
assert.NotEmpty(t, cert["NotBeforeDate"], "NotBeforeDate must be set from PEM")
diff --git a/services/transfer/handler_connectors_test.go b/services/transfer/handler_connectors_test.go
index a16bdfd297..68934b9352 100644
--- a/services/transfer/handler_connectors_test.go
+++ b/services/transfer/handler_connectors_test.go
@@ -1,13 +1,14 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transfer"
)
@@ -34,7 +35,7 @@ func TestHandler_UpdateConnectorLoggingRoleAndSecurityPolicyName(t *testing.T) {
var createOut struct {
ConnectorID string `json:"ConnectorId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
updateRec := doTransferRequest(t, h, "UpdateConnector", map[string]any{
"ConnectorId": createOut.ConnectorID,
@@ -54,7 +55,7 @@ func TestHandler_UpdateConnectorLoggingRoleAndSecurityPolicyName(t *testing.T) {
SecurityPolicyName string `json:"SecurityPolicyName"`
} `json:"Connector"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, "arn:aws:iam::123456789012:role/TransferLogging", descOut.Connector.LoggingRole)
assert.Equal(t, "TransferSecurityPolicy-2024-01", descOut.Connector.SecurityPolicyName)
@@ -107,7 +108,7 @@ func TestHandler_CreateConnector(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["ConnectorId"])
}
})
@@ -174,7 +175,7 @@ func TestHandler_DescribeConnector(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
connectorID := createResp["ConnectorId"].(string)
rec := doTransferRequest(t, h, "DescribeConnector", map[string]any{
@@ -203,7 +204,7 @@ func TestHandler_UpdateConnector(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
connectorID := createResp["ConnectorId"].(string)
rec := doTransferRequest(t, h, "UpdateConnector", map[string]any{
@@ -225,7 +226,7 @@ func TestHandler_TestConnection(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
connectorID := createResp["ConnectorId"].(string)
rec := doTransferRequest(t, h, "TestConnection", map[string]any{
@@ -246,7 +247,7 @@ func TestHandler_StartFileTransfer(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
connectorID := createResp["ConnectorId"].(string)
rec := doTransferRequest(t, h, "StartFileTransfer", map[string]any{
@@ -268,7 +269,7 @@ func TestHandler_StartDirectoryListing(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
connectorID := createResp["ConnectorId"].(string)
rec := doTransferRequest(t, h, "StartDirectoryListing", map[string]any{
@@ -279,7 +280,7 @@ func TestHandler_StartDirectoryListing(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["ListingId"], "ListingId is a required field on StartDirectoryListingOutput")
assert.Equal(t, connectorID+"-"+resp["ListingId"].(string)+".json", resp["OutputFileName"])
}
@@ -296,7 +297,7 @@ func TestHandler_ListFileTransferResults(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
connectorID := createResp["ConnectorId"].(string)
rec := doTransferRequest(t, h, "ListFileTransferResults", map[string]any{
@@ -318,7 +319,7 @@ func TestHandler_StartRemoteDelete(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
connectorID := createResp["ConnectorId"].(string)
rec := doTransferRequest(t, h, "StartRemoteDelete", map[string]any{
@@ -340,7 +341,7 @@ func TestHandler_StartRemoteMove(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
connectorID := createResp["ConnectorId"].(string)
rec := doTransferRequest(t, h, "StartRemoteMove", map[string]any{
@@ -368,7 +369,7 @@ func TestHandler_StartFileTransferPersistsRecord(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
transferID := startResp["TransferId"].(string)
assert.NotEmpty(t, transferID)
@@ -379,7 +380,7 @@ func TestHandler_StartFileTransferPersistsRecord(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
results := listResp["FileTransferResults"].([]any)
require.Len(t, results, 1)
@@ -407,7 +408,7 @@ func TestHandler_DescribeConnectorLoggingRole(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
connectorID := createResp["ConnectorId"].(string)
descRec := doTransferRequest(t, h, "DescribeConnector", map[string]any{
@@ -416,7 +417,7 @@ func TestHandler_DescribeConnectorLoggingRole(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
connector := descResp["Connector"].(map[string]any)
assert.Equal(t, "arn:aws:iam::000000000000:role/logging", connector["LoggingRole"])
@@ -450,8 +451,8 @@ func TestHandler_StartDirectoryListingUniqueId(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var r1, r2 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1))
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &r1, jsonx.V1()))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &r2, jsonx.V1()))
id1 := r1["ListingId"].(string)
id2 := r2["ListingId"].(string)
@@ -513,7 +514,7 @@ func TestHandler_StartRemoteDelete_ReturnsDeleteId(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["DeleteId"])
assert.NotContains(t, resp, "TransferId", "StartRemoteDeleteOutput has no TransferId field in real AWS")
@@ -540,7 +541,7 @@ func TestHandler_StartRemoteMove_ReturnsMoveId(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["MoveId"])
assert.NotContains(t, resp, "TransferId", "StartRemoteMoveOutput has no TransferId field in real AWS")
@@ -577,7 +578,7 @@ func TestHandler_ConnectorIPAddressType(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code, createRec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
connectorID := createResp["ConnectorId"].(string)
descRec := doTransferRequest(t, h, "DescribeConnector", map[string]any{
@@ -586,7 +587,7 @@ func TestHandler_ConnectorIPAddressType(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
connector := descResp["Connector"].(map[string]any)
assert.Equal(t, tt.ipAddressType, connector["IpAddressType"])
assert.Contains(t, descRec.Body.String(), `"IpAddressType":"`+tt.ipAddressType+`"`)
@@ -603,7 +604,7 @@ func TestHandler_ConnectorIPAddressType(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
connectorID := createResp["ConnectorId"].(string)
descRec := doTransferRequest(t, h, "DescribeConnector", map[string]any{
@@ -612,7 +613,7 @@ func TestHandler_ConnectorIPAddressType(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
connector := descResp["Connector"].(map[string]any)
_, hasIPAddressType := connector["IpAddressType"]
assert.False(t, hasIPAddressType, "IpAddressType must be absent, not an empty string, when unset")
@@ -634,7 +635,7 @@ func TestHandler_UpdateConnectorIPAddressType(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
connectorID := createResp["ConnectorId"].(string)
updateRec := doTransferRequest(t, h, "UpdateConnector", map[string]any{
@@ -649,7 +650,7 @@ func TestHandler_UpdateConnectorIPAddressType(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
connector := descResp["Connector"].(map[string]any)
assert.Equal(t, "DUALSTACK", connector["IpAddressType"])
}
diff --git a/services/transfer/handler_host_keys_test.go b/services/transfer/handler_host_keys_test.go
index 9eaeba8036..1ed9d2fcff 100644
--- a/services/transfer/handler_host_keys_test.go
+++ b/services/transfer/handler_host_keys_test.go
@@ -1,12 +1,14 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// testSSHHostKeyBody is a syntactically valid (but not cryptographically
@@ -25,7 +27,7 @@ func TestHandler_DescribeHostKeyIncludesFingerprint(t *testing.T) {
serverRec := doTransferRequest(t, h, "CreateServer", map[string]any{})
require.Equal(t, http.StatusOK, serverRec.Code)
var serverResp map[string]any
- require.NoError(t, json.Unmarshal(serverRec.Body.Bytes(), &serverResp))
+ require.NoError(t, json.Unmarshal(serverRec.Body.Bytes(), &serverResp, jsonx.V1()))
serverID := serverResp["ServerId"].(string)
importRec := doTransferRequest(t, h, "ImportHostKey", map[string]any{
@@ -36,7 +38,7 @@ func TestHandler_DescribeHostKeyIncludesFingerprint(t *testing.T) {
})
require.Equal(t, http.StatusOK, importRec.Code)
var importResp map[string]any
- require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importResp))
+ require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importResp, jsonx.V1()))
hostKeyID := importResp["HostKeyId"].(string)
rec := doTransferRequest(t, h, "DescribeHostKey", map[string]any{
@@ -46,7 +48,7 @@ func TestHandler_DescribeHostKeyIncludesFingerprint(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
hk := resp["HostKey"].(map[string]any)
fp, hasFp := hk["HostKeyFingerprint"].(string)
@@ -75,7 +77,7 @@ func TestHandler_ListHostKeysIncludesFingerprintAndArn(t *testing.T) {
serverRec := doTransferRequest(t, h, "CreateServer", map[string]any{})
require.Equal(t, http.StatusOK, serverRec.Code)
var serverResp map[string]any
- require.NoError(t, json.Unmarshal(serverRec.Body.Bytes(), &serverResp))
+ require.NoError(t, json.Unmarshal(serverRec.Body.Bytes(), &serverResp, jsonx.V1()))
serverID := serverResp["ServerId"].(string)
doTransferRequest(t, h, "ImportHostKey", map[string]any{
@@ -90,7 +92,7 @@ func TestHandler_ListHostKeysIncludesFingerprintAndArn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
hostKeys := resp["HostKeys"].([]any)
require.NotEmpty(t, hostKeys)
@@ -150,7 +152,7 @@ func TestHandler_DescribeHostKey(t *testing.T) {
require.Equal(t, http.StatusOK, importRec.Code)
var importResp map[string]any
- require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importResp))
+ require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importResp, jsonx.V1()))
hostKeyID := importResp["HostKeyId"].(string)
rec := doTransferRequest(t, h, "DescribeHostKey", map[string]any{
@@ -174,7 +176,7 @@ func TestHandler_DeleteHostKey(t *testing.T) {
require.Equal(t, http.StatusOK, importRec.Code)
var importResp map[string]any
- require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importResp))
+ require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importResp, jsonx.V1()))
hostKeyID := importResp["HostKeyId"].(string)
rec := doTransferRequest(t, h, "DeleteHostKey", map[string]any{
@@ -198,7 +200,7 @@ func TestHandler_UpdateHostKey(t *testing.T) {
require.Equal(t, http.StatusOK, importRec.Code)
var importResp map[string]any
- require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importResp))
+ require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importResp, jsonx.V1()))
hostKeyID := importResp["HostKeyId"].(string)
rec := doTransferRequest(t, h, "UpdateHostKey", map[string]any{
diff --git a/services/transfer/handler_profiles_test.go b/services/transfer/handler_profiles_test.go
index 6c88bec2de..d1e943b2af 100644
--- a/services/transfer/handler_profiles_test.go
+++ b/services/transfer/handler_profiles_test.go
@@ -1,12 +1,14 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_ProfileCertificateIDsRoundtrip verifies that CertificateIds set via UpdateProfile
@@ -25,7 +27,7 @@ func TestHandler_ProfileCertificateIDsRoundtrip(t *testing.T) {
var createOut struct {
ProfileID string `json:"ProfileId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
certIDs := []string{"cert-aaa111", "cert-bbb222"}
updateRec := doTransferRequest(t, h, "UpdateProfile", map[string]any{
@@ -44,7 +46,7 @@ func TestHandler_ProfileCertificateIDsRoundtrip(t *testing.T) {
CertificateIDs []string `json:"CertificateIds"`
} `json:"Profile"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descOut, jsonx.V1()))
assert.Equal(t, certIDs, descOut.Profile.CertificateIDs)
}
@@ -62,7 +64,7 @@ func TestHandler_DescribeProfileIncludesArnAndTags(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
profileID := createResp["ProfileId"].(string)
rec := doTransferRequest(t, h, "DescribeProfile", map[string]any{
@@ -71,7 +73,7 @@ func TestHandler_DescribeProfileIncludesArnAndTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
profile := resp["Profile"].(map[string]any)
arn, hasArn := profile["Arn"].(string)
@@ -98,7 +100,7 @@ func TestHandler_ListProfilesIncludesArn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
profiles := resp["Profiles"].([]any)
require.NotEmpty(t, profiles)
@@ -119,7 +121,7 @@ func TestHandler_ProfileARNContainsAccountAndRegion(t *testing.T) {
})
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
profileID := createResp["ProfileId"].(string)
rec := doTransferRequest(t, h, "DescribeProfile", map[string]any{
@@ -128,7 +130,7 @@ func TestHandler_ProfileARNContainsAccountAndRegion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
profile := resp["Profile"].(map[string]any)
// newTestHandler uses testAccountID = "123456789012" and testRegion = "us-east-1"
@@ -192,7 +194,7 @@ func TestHandler_CreateProfileReturnsProfileID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["ProfileId"])
}
@@ -237,7 +239,7 @@ func TestHandler_CreateProfile(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["ProfileId"])
}
})
@@ -256,7 +258,7 @@ func TestHandler_DescribeProfile(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
profileID := createResp["ProfileId"].(string)
rec := doTransferRequest(t, h, "DescribeProfile", map[string]any{
@@ -285,7 +287,7 @@ func TestHandler_DeleteProfile(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
profileID := createResp["ProfileId"].(string)
rec := doTransferRequest(t, h, "DeleteProfile", map[string]any{
@@ -306,7 +308,7 @@ func TestHandler_UpdateProfile(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
profileID := createResp["ProfileId"].(string)
rec := doTransferRequest(t, h, "UpdateProfile", map[string]any{
diff --git a/services/transfer/handler_security_policies_test.go b/services/transfer/handler_security_policies_test.go
index 3408cb257c..af63279761 100644
--- a/services/transfer/handler_security_policies_test.go
+++ b/services/transfer/handler_security_policies_test.go
@@ -1,12 +1,14 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_ListSecurityPolicies(t *testing.T) {
@@ -42,7 +44,7 @@ func TestHandler_ListSecurityPoliciesIncludesCurrentNames(t *testing.T) {
var resp struct {
SecurityPolicyNames []string `json:"SecurityPolicyNames"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
names := make(map[string]bool, len(resp.SecurityPolicyNames))
for _, n := range resp.SecurityPolicyNames {
@@ -85,7 +87,7 @@ func TestHandler_DescribeSecurityPolicy_ServerShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pol := resp["SecurityPolicy"].(map[string]any)
assert.Equal(t, "SERVER", pol["Type"])
@@ -109,7 +111,7 @@ func TestHandler_DescribeSecurityPolicy_ConnectorShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pol := resp["SecurityPolicy"].(map[string]any)
assert.Equal(t, "CONNECTOR", pol["Type"])
@@ -129,6 +131,6 @@ func TestHandler_DescribeSecurityPolicy_UnknownName(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp["__type"])
}
diff --git a/services/transfer/handler_servers_fields_test.go b/services/transfer/handler_servers_fields_test.go
index e43501f48e..2d2e6a3074 100644
--- a/services/transfer/handler_servers_fields_test.go
+++ b/services/transfer/handler_servers_fields_test.go
@@ -1,7 +1,7 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Test 1: CreateServer with IdentityProviderType=API_GATEWAY is echoed by DescribeServer.
@@ -26,7 +28,7 @@ func TestHandler_CreateServerIdentityProviderTypeAPIGateway(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
descRec := doTransferRequest(t, h, "DescribeServer", map[string]any{
@@ -35,7 +37,7 @@ func TestHandler_CreateServerIdentityProviderTypeAPIGateway(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
server := descResp["Server"].(map[string]any)
assert.Equal(t, "API_GATEWAY", server["IdentityProviderType"])
@@ -65,7 +67,7 @@ func TestHandler_CreateServerDomainEFS(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
descRec := doTransferRequest(t, h, "DescribeServer", map[string]any{
@@ -74,7 +76,7 @@ func TestHandler_CreateServerDomainEFS(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
server := descResp["Server"].(map[string]any)
assert.Equal(t, "EFS", server["Domain"])
@@ -89,7 +91,7 @@ func TestHandler_UpdateServerLoggingRoleAndBanners(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
updateRec := doTransferRequest(t, h, "UpdateServer", map[string]any{
@@ -106,7 +108,7 @@ func TestHandler_UpdateServerLoggingRoleAndBanners(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
server := descResp["Server"].(map[string]any)
assert.Equal(t, "arn:aws:iam::000000000000:role/logging", server["LoggingRole"])
@@ -124,7 +126,7 @@ func TestHandler_StartServerStartingState(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
// Stop the server first so we can start it.
@@ -134,7 +136,7 @@ func TestHandler_StartServerStartingState(t *testing.T) {
descRec := doTransferRequest(t, h, "DescribeServer", map[string]any{"ServerId": serverID})
var resp map[string]any
- _ = json.Unmarshal(descRec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1())
require.Equal(t, "OFFLINE", resp["Server"].(map[string]any)["State"].(string))
// Start the server: immediate state should be STARTING.
@@ -144,14 +146,14 @@ func TestHandler_StartServerStartingState(t *testing.T) {
// Check immediately — should be STARTING (async transition hasn't fired yet).
descRec = doTransferRequest(t, h, "DescribeServer", map[string]any{"ServerId": serverID})
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
immediateState := descResp["Server"].(map[string]any)["State"].(string)
assert.Equal(t, "STARTING", immediateState)
// Wait past the async transition to ONLINE.
time.Sleep(serverTransitionWait)
descRec = doTransferRequest(t, h, "DescribeServer", map[string]any{"ServerId": serverID})
- _ = json.Unmarshal(descRec.Body.Bytes(), &descResp)
+ _ = json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1())
assert.Equal(t, "ONLINE", descResp["Server"].(map[string]any)["State"].(string))
})
}
@@ -173,7 +175,7 @@ func TestHandler_TestIdentityProviderKnownUser(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.EqualValues(t, 200, resp["StatusCode"])
}
@@ -192,7 +194,7 @@ func TestHandler_TestIdentityProviderUnknownUser(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.EqualValues(t, 401, resp["StatusCode"])
}
@@ -205,7 +207,7 @@ func TestHandler_UpdateServerProtocolDetails(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
updateRec := doTransferRequest(t, h, "UpdateServer", map[string]any{
@@ -224,7 +226,7 @@ func TestHandler_UpdateServerProtocolDetails(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
server := descResp["Server"].(map[string]any)
pd := server["ProtocolDetails"].(map[string]any)
@@ -249,14 +251,14 @@ func TestHandler_CreateServerEndpointTypeVPC(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
descRec := doTransferRequest(t, h, "DescribeServer", map[string]any{"ServerId": serverID})
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
server := descResp["Server"].(map[string]any)
assert.Equal(t, "VPC", server["EndpointType"])
@@ -279,14 +281,14 @@ func TestHandler_CreateServerWorkflowDetails(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
descRec := doTransferRequest(t, h, "DescribeServer", map[string]any{"ServerId": serverID})
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
server := descResp["Server"].(map[string]any)
wd := server["WorkflowDetails"].(map[string]any)
onUpload := wd["OnUpload"].([]any)
@@ -308,14 +310,14 @@ func TestHandler_CreateServerS3StorageOptions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
descRec := doTransferRequest(t, h, "DescribeServer", map[string]any{"ServerId": serverID})
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
server := descResp["Server"].(map[string]any)
s3 := server["S3StorageOptions"].(map[string]any)
assert.Equal(t, "ENABLED", s3["DirectoryListingOptimization"])
@@ -332,14 +334,14 @@ func TestHandler_CreateServerStructuredLogDestinations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
descRec := doTransferRequest(t, h, "DescribeServer", map[string]any{"ServerId": serverID})
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
server := descResp["Server"].(map[string]any)
dests := server["StructuredLogDestinations"].([]any)
require.Len(t, dests, 1)
@@ -357,14 +359,14 @@ func TestHandler_CreateServerIpAddressType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
descRec := doTransferRequest(t, h, "DescribeServer", map[string]any{"ServerId": serverID})
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
server := descResp["Server"].(map[string]any)
assert.Equal(t, "DUALSTACK", server["IpAddressType"])
}
diff --git a/services/transfer/handler_servers_test.go b/services/transfer/handler_servers_test.go
index 7c6c32d6cc..b679da8f8e 100644
--- a/services/transfer/handler_servers_test.go
+++ b/services/transfer/handler_servers_test.go
@@ -1,7 +1,7 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"testing/synctest"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transfer"
)
@@ -23,7 +24,7 @@ func mustCreateServer(t *testing.T, h *transfer.Handler) string {
var out struct {
ServerID string `json:"ServerId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out.ServerID
}
@@ -40,7 +41,7 @@ func TestHandler_DeleteServerCascade(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
// Create access and agreement on the server
@@ -66,7 +67,7 @@ func TestHandler_DeleteServerCascade(t *testing.T) {
descRec := doTransferRequest(t, h, "DescribeServer", map[string]any{"ServerId": serverID})
var resp map[string]any
- _ = json.Unmarshal(descRec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1())
require.Equal(t, "OFFLINE", resp["Server"].(map[string]any)["State"].(string))
deleteRec := doTransferRequest(t, h, "DeleteServer", map[string]any{"ServerId": serverID})
@@ -93,7 +94,7 @@ func TestHandler_DeleteServerOnlineSucceeds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
// A real server self-starts after CreateServer; see TestHandler_CreateServer.
@@ -101,7 +102,7 @@ func TestHandler_DeleteServerOnlineSucceeds(t *testing.T) {
descRec := doTransferRequest(t, h, "DescribeServer", map[string]any{"ServerId": serverID})
var resp map[string]any
- _ = json.Unmarshal(descRec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1())
require.Equal(t, "ONLINE", resp["Server"].(map[string]any)["State"].(string))
delRec := doTransferRequest(t, h, "DeleteServer", map[string]any{"ServerId": serverID})
@@ -125,7 +126,7 @@ func TestHandler_ListServersIncludesIdentityProviderType(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
// Add a user to check UserCount.
@@ -136,7 +137,7 @@ func TestHandler_ListServersIncludesIdentityProviderType(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
servers := listResp["Servers"].([]any)
require.Len(t, servers, 1)
@@ -161,7 +162,7 @@ func TestHandler_DescribeServerUserCount(t *testing.T) {
descRec := doTransferRequest(t, h, "DescribeServer", map[string]any{"ServerId": s.ServerID})
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
server := descResp["Server"].(map[string]any)
assert.EqualValues(t, 0, server["UserCount"])
@@ -173,7 +174,7 @@ func TestHandler_DescribeServerUserCount(t *testing.T) {
descRec = doTransferRequest(t, h, "DescribeServer", map[string]any{"ServerId": s.ServerID})
require.Equal(t, http.StatusOK, descRec.Code)
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
server = descResp["Server"].(map[string]any)
assert.EqualValues(t, 2, server["UserCount"])
}
@@ -226,7 +227,7 @@ func TestHandler_CreateServer(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp[tt.wantKey])
})
}
@@ -242,7 +243,7 @@ func TestHandler_DescribeServer(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
tests := []struct {
@@ -286,7 +287,7 @@ func TestHandler_ListServers(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.IsType(t, []any{}, resp["Servers"])
}
@@ -355,7 +356,7 @@ func TestHandler_DeleteServer(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
// AWS requires server to be OFFLINE before deletion; stop it first.
@@ -365,7 +366,7 @@ func TestHandler_DeleteServer(t *testing.T) {
descRec := doTransferRequest(t, h, "DescribeServer", map[string]any{"ServerId": serverID})
var resp map[string]any
- _ = json.Unmarshal(descRec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1())
require.Equal(t, "OFFLINE", resp["Server"].(map[string]any)["State"].(string))
rec := doTransferRequest(t, h, "DeleteServer", map[string]any{"ServerId": serverID})
@@ -386,7 +387,7 @@ func TestHandler_UpdateServer(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
rec := doTransferRequest(t, h, "UpdateServer", map[string]any{
@@ -437,7 +438,7 @@ func TestHandler_ListServers_Pagination(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
servers := resp["Servers"].([]any)
assert.GreaterOrEqual(t, len(servers), tt.wantMinCount)
@@ -497,7 +498,7 @@ func TestHandler_StartStopDeleteServer(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
body := tt.body
@@ -513,7 +514,7 @@ func TestHandler_StartStopDeleteServer(t *testing.T) {
descRec := doTransferRequest(t, h, "DescribeServer", map[string]any{"ServerId": serverID})
var resp map[string]any
- _ = json.Unmarshal(descRec.Body.Bytes(), &resp)
+ _ = json.Unmarshal(descRec.Body.Bytes(), &resp, jsonx.V1())
require.Equal(t, "OFFLINE", resp["Server"].(map[string]any)["State"].(string))
}
diff --git a/services/transfer/handler_ssh_keys_test.go b/services/transfer/handler_ssh_keys_test.go
index 167e1f2105..1ca9733cea 100644
--- a/services/transfer/handler_ssh_keys_test.go
+++ b/services/transfer/handler_ssh_keys_test.go
@@ -1,12 +1,14 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_ImportSSHPublicKey(t *testing.T) {
@@ -53,7 +55,7 @@ func TestHandler_DeleteSSHPublicKey(t *testing.T) {
require.Equal(t, http.StatusOK, importRec.Code)
var importResp map[string]any
- require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importResp))
+ require.NoError(t, json.Unmarshal(importRec.Body.Bytes(), &importResp, jsonx.V1()))
sshKeyID := importResp["SshPublicKeyId"].(string)
rec := doTransferRequest(t, h, "DeleteSshPublicKey", map[string]any{
@@ -92,7 +94,7 @@ func TestHandler_ImportSshPublicKeyDuplicateBody(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, secondRec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(secondRec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(secondRec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceExistsException", errResp["__type"])
}
@@ -114,6 +116,6 @@ func TestHandler_ImportSshPublicKeyUnknownUserReturnsResourceNotFound(t *testing
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp["__type"])
}
diff --git a/services/transfer/handler_tags_test.go b/services/transfer/handler_tags_test.go
index 746540b920..1a34c0f5a4 100644
--- a/services/transfer/handler_tags_test.go
+++ b/services/transfer/handler_tags_test.go
@@ -1,7 +1,7 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transfer"
)
@@ -31,7 +32,7 @@ func TestHandler_ListTagsForResourceCreationTagsVisible(t *testing.T) {
var createOut struct {
ServerID string `json:"ServerId"`
}
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createOut, jsonx.V1()))
serverARN := "arn:aws:transfer:us-east-1:123456789012:server/" + createOut.ServerID
listRec := doTransferRequest(t, h, "ListTagsForResource", map[string]any{
@@ -45,7 +46,7 @@ func TestHandler_ListTagsForResourceCreationTagsVisible(t *testing.T) {
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
tagMap := make(map[string]string, len(listOut.Tags))
@@ -88,7 +89,7 @@ func TestHandler_ListTagsForResourceCreationTagsVisibleAcrossResources(t *testin
var out struct {
AgreementID string `json:"AgreementId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return "arn:aws:transfer:us-east-1:123456789012:server/" + serverID + "/agreement/" + out.AgreementID
},
@@ -107,7 +108,7 @@ func TestHandler_ListTagsForResourceCreationTagsVisibleAcrossResources(t *testin
var out struct {
ProfileID string `json:"ProfileId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return "arn:aws:transfer:us-east-1:123456789012:profile/" + out.ProfileID
},
@@ -150,7 +151,7 @@ func TestHandler_ListTagsForResourceCreationTagsVisibleAcrossResources(t *testin
var out struct {
WebAppID string `json:"WebAppId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return "arn:aws:transfer:us-east-1:123456789012:webapp/" + out.WebAppID
},
@@ -169,7 +170,7 @@ func TestHandler_ListTagsForResourceCreationTagsVisibleAcrossResources(t *testin
var out struct {
CertificateID string `json:"CertificateId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return "arn:aws:transfer:us-east-1:123456789012:certificate/" + out.CertificateID
},
@@ -191,7 +192,7 @@ func TestHandler_ListTagsForResourceCreationTagsVisibleAcrossResources(t *testin
var out struct {
HostKeyID string `json:"HostKeyId"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return "arn:aws:transfer:us-east-1:123456789012:host-key/" + serverID + "/" + out.HostKeyID
},
@@ -214,7 +215,7 @@ func TestHandler_ListTagsForResourceCreationTagsVisibleAcrossResources(t *testin
Value string `json:"Value"`
} `json:"Tags"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
tagMap := make(map[string]string, len(listOut.Tags))
for _, tag := range listOut.Tags {
@@ -254,7 +255,7 @@ func TestHandler_TagResourceListTagsRoundTripServerARN(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags := listResp["Tags"].([]any)
tagMap := make(map[string]string)
@@ -293,7 +294,7 @@ func TestHandler_UntagResourceRoundTrip(t *testing.T) {
listRec := doTransferRequest(t, h, "ListTagsForResource", map[string]any{"Arn": arnStr})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags := listResp["Tags"].([]any)
tagMap := make(map[string]string)
@@ -342,7 +343,7 @@ func TestHandler_ListTagsForResource(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotNil(t, resp["Tags"])
}
diff --git a/services/transfer/handler_test.go b/services/transfer/handler_test.go
index 5790c69a53..7dd0ff3111 100644
--- a/services/transfer/handler_test.go
+++ b/services/transfer/handler_test.go
@@ -2,7 +2,7 @@ package transfer_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/transfer"
)
@@ -147,7 +148,7 @@ func doTransferRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
diff --git a/services/transfer/handler_users_test.go b/services/transfer/handler_users_test.go
index 59518d6f6b..2602eb7f1d 100644
--- a/services/transfer/handler_users_test.go
+++ b/services/transfer/handler_users_test.go
@@ -1,7 +1,7 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"net/http"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_ListUsersSshPublicKeyCountAndHomeDirectoryType verifies that ListUsers returns
@@ -25,7 +27,7 @@ func TestHandler_ListUsersSshPublicKeyCountAndHomeDirectoryType(t *testing.T) {
var srvOut struct {
ServerID string `json:"ServerId"`
}
- require.NoError(t, json.Unmarshal(createSrvRec.Body.Bytes(), &srvOut))
+ require.NoError(t, json.Unmarshal(createSrvRec.Body.Bytes(), &srvOut, jsonx.V1()))
createUserRec := doTransferRequest(t, h, "CreateUser", map[string]any{
"ServerId": srvOut.ServerID,
@@ -57,7 +59,7 @@ func TestHandler_ListUsersSshPublicKeyCountAndHomeDirectoryType(t *testing.T) {
SSHPublicKeyCount int `json:"SshPublicKeyCount"`
} `json:"Users"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listOut, jsonx.V1()))
require.Len(t, listOut.Users, 1)
assert.Equal(t, "LOGICAL", listOut.Users[0].HomeDirectoryType)
@@ -95,7 +97,7 @@ func TestHandler_UserARNFormat(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
user := descResp["User"].(map[string]any)
// newTestHandler uses testAccountID = "123456789012" and testRegion = "us-east-1"
@@ -130,7 +132,7 @@ func TestHandler_CreateUserPosixProfile(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
user := descResp["User"].(map[string]any)
posix := user["PosixProfile"].(map[string]any)
@@ -164,7 +166,7 @@ func TestHandler_CreateUserHomeDirectoryLogical(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
user := descResp["User"].(map[string]any)
assert.Equal(t, "LOGICAL", user["HomeDirectoryType"])
@@ -207,7 +209,7 @@ func TestHandler_DescribeUserIncludesSshPublicKeys(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
user := descResp["User"].(map[string]any)
keys, ok := user["SshPublicKeys"].([]any)
require.True(t, ok, "SshPublicKeys must be present")
@@ -235,7 +237,7 @@ func TestHandler_ListUsersPagination(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
users := listResp["Users"].([]any)
assert.Len(t, users, 2)
@@ -269,7 +271,7 @@ func TestHandler_UpdateUserPosixProfile(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
user := descResp["User"].(map[string]any)
posix := user["PosixProfile"].(map[string]any)
assert.EqualValues(t, 9999, posix["Uid"])
@@ -285,7 +287,7 @@ func TestHandler_UserCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
// Create user
@@ -298,7 +300,7 @@ func TestHandler_UserCRUD(t *testing.T) {
assert.Equal(t, http.StatusOK, createUserRec.Code)
var userResp map[string]any
- require.NoError(t, json.Unmarshal(createUserRec.Body.Bytes(), &userResp))
+ require.NoError(t, json.Unmarshal(createUserRec.Body.Bytes(), &userResp, jsonx.V1()))
assert.Equal(t, "alice", userResp["UserName"])
// Describe user
@@ -315,7 +317,7 @@ func TestHandler_UserCRUD(t *testing.T) {
assert.Equal(t, http.StatusOK, listUsersRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listUsersRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listUsersRec.Body.Bytes(), &listResp, jsonx.V1()))
users := listResp["Users"].([]any)
assert.Len(t, users, 1)
@@ -341,7 +343,7 @@ func TestHandler_UserCRUD(t *testing.T) {
assert.Equal(t, http.StatusOK, listUsersRec2.Code)
var listResp2 map[string]any
- require.NoError(t, json.Unmarshal(listUsersRec2.Body.Bytes(), &listResp2))
+ require.NoError(t, json.Unmarshal(listUsersRec2.Body.Bytes(), &listResp2, jsonx.V1()))
assert.Empty(t, listResp2["Users"])
}
@@ -354,7 +356,7 @@ func TestHandler_CreateUser_MissingFields(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
tests := []struct {
@@ -395,7 +397,7 @@ func TestHandler_ListUsers_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
serverID := createResp["ServerId"].(string)
for _, username := range []string{"alice", "bob", "charlie"} {
@@ -434,7 +436,7 @@ func TestHandler_ListUsers_Pagination(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
users := resp["Users"].([]any)
assert.GreaterOrEqual(t, len(users), tt.wantMinCount)
@@ -503,7 +505,7 @@ func TestHandler_DescribeDeleteUpdateUser(t *testing.T) {
require.Equal(t, http.StatusOK, createServerRec.Code)
var serverResp map[string]any
- require.NoError(t, json.Unmarshal(createServerRec.Body.Bytes(), &serverResp))
+ require.NoError(t, json.Unmarshal(createServerRec.Body.Bytes(), &serverResp, jsonx.V1()))
serverID := serverResp["ServerId"].(string)
doTransferRequest(t, h, "CreateUser", map[string]any{
diff --git a/services/transfer/handler_web_apps_test.go b/services/transfer/handler_web_apps_test.go
index 6e520b0602..a83bd5783c 100644
--- a/services/transfer/handler_web_apps_test.go
+++ b/services/transfer/handler_web_apps_test.go
@@ -1,12 +1,14 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// webAppCreateBody returns a minimal CreateWebApp request body satisfying the
@@ -60,7 +62,7 @@ func TestHandler_WebAppCustomizationValidatesWebAppID(t *testing.T) {
"%s on non-existent WebAppId must return 400 (ResourceNotFoundException)", tt.operation)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
errType, _ := errResp["__type"].(string)
assert.Contains(t, errType, "ResourceNotFoundException",
@@ -81,7 +83,7 @@ func TestHandler_WebAppCustomizationRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
webAppID, _ := createResp["WebAppId"].(string)
require.NotEmpty(t, webAppID)
@@ -99,7 +101,7 @@ func TestHandler_WebAppCustomizationRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, describeRec.Code, "DescribeWebAppCustomization must succeed")
var descResp map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &descResp, jsonx.V1()))
customization, ok := descResp["WebAppCustomization"].(map[string]any)
require.True(t, ok, "WebAppCustomization must be present in response")
@@ -123,7 +125,7 @@ func TestHandler_CreateWebAppReturnsWebAppID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["WebAppId"])
}
@@ -138,7 +140,7 @@ func TestHandler_CreateWebApp_MissingIdentityProviderDetails(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", errResp["__type"])
}
@@ -158,7 +160,7 @@ func TestHandler_DescribeWebAppIncludesArnTagsAndIdentityProvider(t *testing.T)
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
webAppID := createResp["WebAppId"].(string)
updateRec := doTransferRequest(t, h, "UpdateWebApp", map[string]any{
@@ -175,7 +177,7 @@ func TestHandler_DescribeWebAppIncludesArnTagsAndIdentityProvider(t *testing.T)
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
webApp := descResp["WebApp"].(map[string]any)
assert.Equal(t,
@@ -212,14 +214,14 @@ func TestHandler_ListWebAppsIncludesArn(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
webAppID := createResp["WebAppId"].(string)
listRec := doTransferRequest(t, h, "ListWebApps", map[string]any{})
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
webApps := listResp["WebApps"].([]any)
require.Len(t, webApps, 1)
@@ -261,7 +263,7 @@ func TestHandler_CreateWebApp(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["WebAppId"])
})
}
@@ -294,14 +296,14 @@ func TestHandler_CreateWebAppVpcEndpoint(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
webAppID := createResp["WebAppId"].(string)
descRec := doTransferRequest(t, h, "DescribeWebApp", map[string]any{"WebAppId": webAppID})
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
webApp := descResp["WebApp"].(map[string]any)
assert.Equal(t, "VPC", webApp["EndpointType"])
@@ -327,7 +329,7 @@ func TestHandler_DescribeWebApp(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
webAppID := createResp["WebAppId"].(string)
rec := doTransferRequest(t, h, "DescribeWebApp", map[string]any{
@@ -353,7 +355,7 @@ func TestHandler_DeleteWebApp(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
webAppID := createResp["WebAppId"].(string)
rec := doTransferRequest(t, h, "DeleteWebApp", map[string]any{
@@ -371,7 +373,7 @@ func TestHandler_UpdateWebApp(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
webAppID := createResp["WebAppId"].(string)
rec := doTransferRequest(t, h, "UpdateWebApp", map[string]any{
@@ -393,7 +395,7 @@ func TestHandler_UpdateWebAppVpcIPAddressType(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
webAppID := createResp["WebAppId"].(string)
updateRec := doTransferRequest(t, h, "UpdateWebApp", map[string]any{
@@ -423,7 +425,7 @@ func TestHandler_UpdateWebApp_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", errResp["__type"])
}
@@ -436,7 +438,7 @@ func TestHandler_WebAppCustomization(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
webAppID := createResp["WebAppId"].(string)
// DescribeWebAppCustomization
diff --git a/services/transfer/handler_workflows_test.go b/services/transfer/handler_workflows_test.go
index c95e649dfb..faf2a3707c 100644
--- a/services/transfer/handler_workflows_test.go
+++ b/services/transfer/handler_workflows_test.go
@@ -1,12 +1,14 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestHandler_CreateWorkflowInvalidStepTypeRejected verifies that CreateWorkflow returns 400
@@ -61,7 +63,7 @@ func TestHandler_DescribeWorkflowIncludesArnAndTags(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
workflowID := createResp["WorkflowId"].(string)
rec := doTransferRequest(t, h, "DescribeWorkflow", map[string]any{
@@ -70,7 +72,7 @@ func TestHandler_DescribeWorkflowIncludesArnAndTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wf := resp["Workflow"].(map[string]any)
arn, hasArn := wf["Arn"].(string)
@@ -96,7 +98,7 @@ func TestHandler_ListWorkflowsIncludesArn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
workflows := resp["Workflows"].([]any)
require.NotEmpty(t, workflows)
@@ -117,7 +119,7 @@ func TestHandler_CreateWorkflowReturnsWorkflowID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["WorkflowId"])
}
@@ -205,7 +207,7 @@ func TestHandler_CreateWorkflow(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["WorkflowId"])
})
}
@@ -222,7 +224,7 @@ func TestHandler_DescribeWorkflow(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
workflowID := createResp["WorkflowId"].(string)
rec := doTransferRequest(t, h, "DescribeWorkflow", map[string]any{
@@ -250,7 +252,7 @@ func TestHandler_DeleteWorkflow(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
workflowID := createResp["WorkflowId"].(string)
rec := doTransferRequest(t, h, "DeleteWorkflow", map[string]any{
@@ -270,7 +272,7 @@ func TestHandler_ListExecutions(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
workflowID := createResp["WorkflowId"].(string)
rec := doTransferRequest(t, h, "ListExecutions", map[string]any{
@@ -321,7 +323,7 @@ func TestHandler_DescribeWorkflowTypedCopyStepDetails(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
workflowID := createResp["WorkflowId"].(string)
descRec := doTransferRequest(t, h, "DescribeWorkflow", map[string]any{
@@ -330,7 +332,7 @@ func TestHandler_DescribeWorkflowTypedCopyStepDetails(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
wf := descResp["Workflow"].(map[string]any)
steps := wf["Steps"].([]any)
require.Len(t, steps, 1)
diff --git a/services/transfer/multi_region_test.go b/services/transfer/multi_region_test.go
index 21aeadc5fb..346fabcfa9 100644
--- a/services/transfer/multi_region_test.go
+++ b/services/transfer/multi_region_test.go
@@ -2,7 +2,7 @@ package transfer_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transfer"
)
@@ -41,7 +42,7 @@ func decode(t *testing.T, body string) map[string]any {
out := map[string]any{}
if body != "" {
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
}
return out
diff --git a/services/transfer/paging.go b/services/transfer/paging.go
index ffec79288c..b15d4f6ecc 100644
--- a/services/transfer/paging.go
+++ b/services/transfer/paging.go
@@ -1,9 +1,10 @@
package transfer
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -36,7 +37,7 @@ func validateListPaging(body []byte) error {
func decodeListPaging(body []byte) (listPagingInput, bool) {
var in listPagingInput
- err := json.Unmarshal(body, &in)
+ err := json.Unmarshal(body, &in, jsonx.V1())
return in, err == nil
}
diff --git a/services/transfer/persistence.go b/services/transfer/persistence.go
index 3208b9db17..e847ac65d7 100644
--- a/services/transfer/persistence.go
+++ b/services/transfer/persistence.go
@@ -2,7 +2,7 @@ package transfer
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -31,7 +31,7 @@ const transferSnapshotVersion = 1
// (and TagsStore is handled separately below) -- see store_setup.go and the
// field comments on InMemoryBackend for why.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
TagsStore map[string]map[string]string `json:"tags_store"`
Version int `json:"version"`
}
diff --git a/services/transfer/realism_test.go b/services/transfer/realism_test.go
index 9a00ae6c4d..ef604813aa 100644
--- a/services/transfer/realism_test.go
+++ b/services/transfer/realism_test.go
@@ -1,12 +1,14 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const realismRole = "arn:aws:iam::123456789012:role/TransferUserRole"
@@ -18,7 +20,7 @@ func transferErr(t *testing.T, body []byte) (string, string) {
Type string `json:"__type"`
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
return out.Type, out.Message
}
diff --git a/services/transfer/resource_id_shape_test.go b/services/transfer/resource_id_shape_test.go
index 4b514f4574..4886999b1f 100644
--- a/services/transfer/resource_id_shape_test.go
+++ b/services/transfer/resource_id_shape_test.go
@@ -1,12 +1,14 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// Patterns are the SDK-documented ID shapes; terraform-provider-aws validates workflow and server IDs against them.
@@ -87,11 +89,11 @@ func TestResourceIDShapes(t *testing.T) {
require.Equal(t, http.StatusOK, srv.Code)
var srvOut, profOut map[string]any
- require.NoError(t, json.Unmarshal(srv.Body.Bytes(), &srvOut))
+ require.NoError(t, json.Unmarshal(srv.Body.Bytes(), &srvOut, jsonx.V1()))
prof := doTransferRequest(t, h, "CreateProfile", map[string]any{"As2Id": "AS2", "ProfileType": "LOCAL"})
require.Equal(t, http.StatusOK, prof.Code)
- require.NoError(t, json.Unmarshal(prof.Body.Bytes(), &profOut))
+ require.NoError(t, json.Unmarshal(prof.Body.Bytes(), &profOut, jsonx.V1()))
rec := doTransferRequest(
t,
@@ -102,7 +104,7 @@ func TestResourceIDShapes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Regexp(t, tt.pattern, out[tt.idField])
})
}
@@ -116,7 +118,7 @@ func TestSSHKeyAndExecutionIDShapes(t *testing.T) {
srv := doTransferRequest(t, h, "CreateServer", map[string]any{})
var srvOut map[string]any
- require.NoError(t, json.Unmarshal(srv.Body.Bytes(), &srvOut))
+ require.NoError(t, json.Unmarshal(srv.Body.Bytes(), &srvOut, jsonx.V1()))
doTransferRequest(t, h, "CreateUser", map[string]any{
"ServerId": srvOut["ServerId"], "UserName": "alice", "Role": "arn:aws:iam::000000000000:role/r",
@@ -128,6 +130,6 @@ func TestSSHKeyAndExecutionIDShapes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var out map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Regexp(t, `^key-[0-9a-f]{17}$`, out["SshPublicKeyId"])
}
diff --git a/services/transfer/sftp_apigw.go b/services/transfer/sftp_apigw.go
index 65e7b031ce..166a8178eb 100644
--- a/services/transfer/sftp_apigw.go
+++ b/services/transfer/sftp_apigw.go
@@ -2,7 +2,7 @@ package transfer
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -15,6 +15,7 @@ import (
"golang.org/x/crypto/ssh"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -205,7 +206,7 @@ func (s *sftpServer) apiGatewayLogin(
}
var resp idpAuthResponse
- if jerr := json.Unmarshal(body, &resp); jerr != nil {
+ if jerr := json.Unmarshal(body, &resp, jsonx.V1()); jerr != nil {
return nil, errSFTPDenied
}
diff --git a/services/transfer/sftp_server.go b/services/transfer/sftp_server.go
index 317aa0bc9e..0c2602f86d 100644
--- a/services/transfer/sftp_server.go
+++ b/services/transfer/sftp_server.go
@@ -3,7 +3,7 @@ package transfer
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"io"
"net"
@@ -14,6 +14,7 @@ import (
"github.com/pkg/sftp"
"golang.org/x/crypto/ssh"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -106,7 +107,7 @@ func (s *sftpServer) serve(conn net.Conn) {
var sess sftpSession
if raw := sc.Permissions.Extensions[sftpSessionExtension]; raw != "" {
- if jerr := json.Unmarshal([]byte(raw), &sess); jerr != nil {
+ if jerr := json.Unmarshal([]byte(raw), &sess, jsonx.V1()); jerr != nil {
return
}
}
@@ -176,7 +177,7 @@ func (s *sftpServer) authenticate(c ssh.ConnMetadata, key ssh.PublicKey, passwor
sess.User = c.User()
- raw, err := json.Marshal(sess)
+ raw, err := json.Marshal(sess, jsonx.V1())
if err != nil {
return nil, errSFTPDenied
}
@@ -288,7 +289,7 @@ func (s *sftpServer) lambdaLogin(
event["password"] = password
}
- payload, err := json.Marshal(event)
+ payload, err := json.Marshal(event, jsonx.V1())
if err != nil {
return nil, err
}
@@ -299,7 +300,7 @@ func (s *sftpServer) lambdaLogin(
}
var resp idpAuthResponse
- if jerr := json.Unmarshal(out, &resp); jerr != nil {
+ if jerr := json.Unmarshal(out, &resp, jsonx.V1()); jerr != nil {
return nil, errSFTPDenied
}
@@ -340,7 +341,7 @@ func parseLambdaMappings(details string) ([]HomeDirectoryMapEntry, error) {
Entry string `json:"Entry"`
Target string `json:"Target"`
}
- if err := json.Unmarshal([]byte(details), &raw); err != nil {
+ if err := json.Unmarshal([]byte(details), &raw, jsonx.V1()); err != nil {
return nil, errSFTPDenied
}
diff --git a/services/transfer/undeclared_members_test.go b/services/transfer/undeclared_members_test.go
index 0dac04035a..129d86df9c 100644
--- a/services/transfer/undeclared_members_test.go
+++ b/services/transfer/undeclared_members_test.go
@@ -1,12 +1,14 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_DescribeUser_SshKeyOmitsKeyType(t *testing.T) {
@@ -45,7 +47,7 @@ func TestHandler_DescribeUser_SshKeyOmitsKeyType(t *testing.T) {
SSHPublicKeys []map[string]any `json:"SshPublicKeys"`
} `json:"User"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.User.SSHPublicKeys, 1)
assert.NotContains(t, out.User.SSHPublicKeys[0], "KeyType")
assert.Equal(t, tc.body, out.User.SSHPublicKeys[0]["SshPublicKeyBody"])
diff --git a/services/transfer/wire_field_fixes_test.go b/services/transfer/wire_field_fixes_test.go
index fcf2838991..ae67e2faed 100644
--- a/services/transfer/wire_field_fixes_test.go
+++ b/services/transfer/wire_field_fixes_test.go
@@ -1,7 +1,7 @@
package transfer_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/transfer"
)
@@ -78,7 +79,7 @@ func TestDescribeWorkflow_CustomStepTimeoutSecondsKey_RealClient(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code, createRec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
workflowID := createResp["WorkflowId"].(string)
rec := doTransferRequest(t, h, "DescribeWorkflow", map[string]any{
@@ -230,7 +231,7 @@ func TestListExecutionsAndDescribeExecution_NoFabricatedWorkflowId(t *testing.T)
var listResp struct {
Executions []map[string]any `json:"Executions"`
}
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
require.Len(t, listResp.Executions, 1, "must exercise a non-empty collection")
require.Contains(t, listResp.Executions[0], "ExecutionId")
@@ -247,7 +248,7 @@ func TestListExecutionsAndDescribeExecution_NoFabricatedWorkflowId(t *testing.T)
var descResp struct {
Execution map[string]any `json:"Execution"`
}
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
require.Contains(t, descResp.Execution, "ExecutionId")
_, descHasWorkflowID := descResp.Execution["WorkflowId"]
diff --git a/services/translate/handler.go b/services/translate/handler.go
index 1f5118d4d1..2deec69537 100644
--- a/services/translate/handler.go
+++ b/services/translate/handler.go
@@ -2,7 +2,7 @@ package translate
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -12,6 +12,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -106,7 +107,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
}
var values map[string]any
- if unmarshalErr := json.Unmarshal(body, &values); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(body, &values, jsonx.V1()); unmarshalErr != nil {
return ""
}
@@ -147,7 +148,7 @@ func (h *Handler) dispatch(_ context.Context, action string, body []byte) ([]byt
var input map[string]any
if len(body) > 0 {
- if err := json.Unmarshal(body, &input); err != nil {
+ if err := json.Unmarshal(body, &input, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid JSON: %w", ErrValidation, err)
}
}
@@ -161,7 +162,7 @@ func (h *Handler) dispatch(_ context.Context, action string, body []byte) ([]byt
return nil, err
}
- return json.Marshal(output)
+ return json.Marshal(output, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
diff --git a/services/translate/handler_languages_test.go b/services/translate/handler_languages_test.go
index cc6355c5ab..95e0dde86e 100644
--- a/services/translate/handler_languages_test.go
+++ b/services/translate/handler_languages_test.go
@@ -1,12 +1,14 @@
package translate_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestListLanguages(t *testing.T) {
@@ -154,7 +156,7 @@ func TestListLanguages_UnsupportedDisplayLanguageCodeRejected(t *testing.T) {
if tt.wantCode == http.StatusBadRequest {
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "UnsupportedDisplayLanguageCodeException", body["__type"])
}
})
diff --git a/services/translate/handler_parallel_data_test.go b/services/translate/handler_parallel_data_test.go
index 822fd0cf01..82f9ccffce 100644
--- a/services/translate/handler_parallel_data_test.go
+++ b/services/translate/handler_parallel_data_test.go
@@ -1,13 +1,14 @@
package translate_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/translate"
)
@@ -255,7 +256,7 @@ func TestUpdateParallelData_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", body["__type"])
}
@@ -283,7 +284,7 @@ func TestCreateParallelData_DuplicateNameReturnsConflictException(t *testing.T)
assert.Equal(t, http.StatusBadRequest, rec.Code)
var respBody map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &respBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &respBody, jsonx.V1()))
assert.Equal(t, "ConflictException", respBody["__type"])
}
@@ -435,7 +436,7 @@ func TestUpdateParallelData_ConcurrentModification(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ConcurrentModificationException", body["__type"])
})
}
@@ -494,7 +495,7 @@ func TestCreateParallelData_TooManyTagsRejected(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "TooManyTagsException", body["__type"])
}
diff --git a/services/translate/handler_tags_test.go b/services/translate/handler_tags_test.go
index 0003fdf509..744a6b2dd9 100644
--- a/services/translate/handler_tags_test.go
+++ b/services/translate/handler_tags_test.go
@@ -1,12 +1,14 @@
package translate_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestTagResource(t *testing.T) {
@@ -85,7 +87,7 @@ func TestTagResource_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", body["__type"])
}
@@ -103,7 +105,7 @@ func TestUntagResource_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", body["__type"])
}
@@ -120,7 +122,7 @@ func TestListTagsForResource_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", body["__type"])
}
@@ -180,7 +182,7 @@ func TestTagResource_TooManyTagsRejected(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "TooManyTagsException", body["__type"])
}
diff --git a/services/translate/handler_test.go b/services/translate/handler_test.go
index df32822a18..ee41a8524a 100644
--- a/services/translate/handler_test.go
+++ b/services/translate/handler_test.go
@@ -3,7 +3,7 @@ package translate_test
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/translate"
)
@@ -38,7 +39,7 @@ func doRequest(t *testing.T, h *translate.Handler, operation string, body any) *
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -63,7 +64,7 @@ func unmarshalJSON(t *testing.T, data []byte) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(data, &m))
+ require.NoError(t, json.Unmarshal(data, &m, jsonx.V1()))
return m
}
diff --git a/services/translate/handler_text_translation_jobs_test.go b/services/translate/handler_text_translation_jobs_test.go
index ae9278a072..56fa152d58 100644
--- a/services/translate/handler_text_translation_jobs_test.go
+++ b/services/translate/handler_text_translation_jobs_test.go
@@ -1,7 +1,7 @@
package translate_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/translate"
)
@@ -199,7 +200,7 @@ func TestStopTextTranslationJob_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", body["__type"])
}
@@ -213,7 +214,7 @@ func TestDescribeTextTranslationJob_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", body["__type"])
}
@@ -306,7 +307,7 @@ func TestStartTextTranslationJob_UnknownTerminologyRejected(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var respBody map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &respBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &respBody, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", respBody["__type"])
})
}
@@ -354,7 +355,7 @@ func TestStartTextTranslationJob_RequiredFieldsValidated(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var respBody map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &respBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &respBody, jsonx.V1()))
assert.Equal(t, "InvalidRequestException", respBody["__type"])
})
}
@@ -409,7 +410,7 @@ func TestStartTextTranslationJob_UnsupportedLanguagePairRejected(t *testing.T) {
if tt.wantCode == http.StatusBadRequest {
var respBody map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &respBody))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &respBody, jsonx.V1()))
assert.Equal(t, "UnsupportedLanguagePairException", respBody["__type"])
}
})
@@ -544,7 +545,7 @@ func TestListTextTranslationJobs_InvalidFilterRejected(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var body map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
assert.Equal(t, "InvalidFilterException", body["__type"])
}
diff --git a/services/translate/multi_region_test.go b/services/translate/multi_region_test.go
index 4dc043b372..83f9c4e69a 100644
--- a/services/translate/multi_region_test.go
+++ b/services/translate/multi_region_test.go
@@ -2,7 +2,8 @@ package translate_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/translate"
)
@@ -183,8 +185,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/translate/persistence.go b/services/translate/persistence.go
index 37de19b1ef..d103f65279 100644
--- a/services/translate/persistence.go
+++ b/services/translate/persistence.go
@@ -2,7 +2,7 @@ package translate
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
@@ -38,7 +38,7 @@ const translateSnapshotVersion = 1
// (see backend.go's InMemoryBackend field doc comment). It is persisted
// directly here.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
AccountID string `json:"accountId"`
Region string `json:"region"`
diff --git a/services/verifiedpermissions/cedar_attributes.go b/services/verifiedpermissions/cedar_attributes.go
index cdd9e948bc..0612b68c1c 100644
--- a/services/verifiedpermissions/cedar_attributes.go
+++ b/services/verifiedpermissions/cedar_attributes.go
@@ -1,11 +1,14 @@
package verifiedpermissions
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
cedar "github.com/cedar-policy/cedar-go"
cedartypes "github.com/cedar-policy/cedar-go/types"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// entityIdentifierJSON already exists in handler_authorization.go (entityType/entityId).
@@ -18,9 +21,9 @@ import (
// the same as cedar-go's own implicit JSON value format (types.UnmarshalJSON:
// bare JSON literals plus __entity/__extn tags), so this needs its own converter
// rather than reusing cedar-go's decoder.
-func attributeValueToCedar(raw json.RawMessage) (cedar.Value, error) {
- var m map[string]json.RawMessage
- if err := json.Unmarshal(raw, &m); err != nil {
+func attributeValueToCedar(raw jsontext.Value) (cedar.Value, error) {
+ var m map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &m, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid attribute value: %w", errInvalidRequest, err)
}
@@ -38,25 +41,25 @@ func attributeValueToCedar(raw json.RawMessage) (cedar.Value, error) {
// per-member would scatter the union's shape across more functions than it clarifies.
//
//nolint:cyclop // one discriminated-union switch over AWS's 9 AttributeValue member types; decomposing
-func attributeValueMemberToCedar(key string, v json.RawMessage) (cedar.Value, error) {
+func attributeValueMemberToCedar(key string, v jsontext.Value) (cedar.Value, error) {
switch key {
case "boolean":
var b bool
- if err := json.Unmarshal(v, &b); err != nil {
+ if err := json.Unmarshal(v, &b, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid boolean attribute: %w", errInvalidRequest, err)
}
return cedar.Boolean(b), nil
case "string":
var s string
- if err := json.Unmarshal(v, &s); err != nil {
+ if err := json.Unmarshal(v, &s, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid string attribute: %w", errInvalidRequest, err)
}
return cedar.String(s), nil
case "long":
var n int64
- if err := json.Unmarshal(v, &n); err != nil {
+ if err := json.Unmarshal(v, &n, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid long attribute: %w", errInvalidRequest, err)
}
@@ -71,7 +74,7 @@ func attributeValueMemberToCedar(key string, v json.RawMessage) (cedar.Value, er
return parseCedarStringAttr(v, cedartypes.ParseIPAddr)
case "entityIdentifier":
var eid entityIdentifierJSON
- if err := json.Unmarshal(v, &eid); err != nil {
+ if err := json.Unmarshal(v, &eid, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid entityIdentifier attribute: %w", errInvalidRequest, err)
}
@@ -85,9 +88,9 @@ func attributeValueMemberToCedar(key string, v json.RawMessage) (cedar.Value, er
}
}
-func parseCedarStringAttr[T cedar.Value](raw json.RawMessage, parse func(string) (T, error)) (cedar.Value, error) {
+func parseCedarStringAttr[T cedar.Value](raw jsontext.Value, parse func(string) (T, error)) (cedar.Value, error) {
var s string
- if err := json.Unmarshal(raw, &s); err != nil {
+ if err := json.Unmarshal(raw, &s, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid string-encoded attribute: %w", errInvalidRequest, err)
}
@@ -99,9 +102,9 @@ func parseCedarStringAttr[T cedar.Value](raw json.RawMessage, parse func(string)
return val, nil
}
-func recordAttributeToCedar(raw json.RawMessage) (cedar.Value, error) {
- var fields map[string]json.RawMessage
- if err := json.Unmarshal(raw, &fields); err != nil {
+func recordAttributeToCedar(raw jsontext.Value) (cedar.Value, error) {
+ var fields map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &fields, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid record attribute: %w", errInvalidRequest, err)
}
@@ -119,9 +122,9 @@ func recordAttributeToCedar(raw json.RawMessage) (cedar.Value, error) {
return cedar.NewRecord(rec), nil
}
-func setAttributeToCedar(raw json.RawMessage) (cedar.Value, error) {
- var items []json.RawMessage
- if err := json.Unmarshal(raw, &items); err != nil {
+func setAttributeToCedar(raw jsontext.Value) (cedar.Value, error) {
+ var items []jsontext.Value
+ if err := json.Unmarshal(raw, &items, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid set attribute: %w", errInvalidRequest, err)
}
@@ -141,7 +144,7 @@ func setAttributeToCedar(raw json.RawMessage) (cedar.Value, error) {
// attributeMapToCedarRecord converts an AWS "attributes"/"contextMap" object
// (map of name -> AttributeValue) into a cedar-go Record.
-func attributeMapToCedarRecord(m map[string]json.RawMessage) (cedar.Record, error) {
+func attributeMapToCedarRecord(m map[string]jsontext.Value) (cedar.Record, error) {
rec := make(cedar.RecordMap, len(m))
for k, v := range m {
@@ -161,10 +164,10 @@ func attributeMapToCedarRecord(m map[string]json.RawMessage) (cedar.Record, erro
// awsAwsjson10_serializeDocumentEntityItem). CedarTagValue has the same members
// as AttributeValue (serializers.go:2334), so tags reuse the attribute converter.
type entityItemJSON struct {
- Identifier *entityIdentifierJSON `json:"identifier,omitempty"`
- Attributes map[string]json.RawMessage `json:"attributes,omitempty"`
- Tags map[string]json.RawMessage `json:"tags,omitempty"`
- Parents []entityIdentifierJSON `json:"parents,omitempty"`
+ Identifier *entityIdentifierJSON `json:"identifier,omitempty"`
+ Attributes map[string]jsontext.Value `json:"attributes,omitempty"`
+ Tags map[string]jsontext.Value `json:"tags,omitempty"`
+ Parents []entityIdentifierJSON `json:"parents,omitempty"`
}
// entitiesDefinitionJSON mirrors the real SDK's types.EntitiesDefinition union
@@ -183,8 +186,8 @@ type entitiesDefinitionJSON struct {
// (contextMap: typed AttributeValue objects: cedarJson: a literal Cedar JSON
// object using cedar-go's own native record JSON shape).
type contextDefinitionJSON struct {
- ContextMap map[string]json.RawMessage `json:"contextMap,omitempty"`
- CedarJSON string `json:"cedarJson,omitempty"`
+ ContextMap map[string]jsontext.Value `json:"contextMap,omitempty"`
+ CedarJSON string `json:"cedarJson,omitempty"`
}
// entitiesToCedar converts a parsed entitiesDefinitionJSON into a cedar-go
@@ -198,7 +201,7 @@ func entitiesToCedar(def *entitiesDefinitionJSON) (cedar.EntityMap, error) {
if def.CedarJSON != "" {
var em cedar.EntityMap
- if err := json.Unmarshal([]byte(def.CedarJSON), &em); err != nil {
+ if err := json.Unmarshal([]byte(def.CedarJSON), &em, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid entities cedarJson: %w", errInvalidRequest, err)
}
@@ -245,7 +248,7 @@ func contextToCedar(def *contextDefinitionJSON) (cedar.Record, error) {
if def.CedarJSON != "" {
var rec cedar.Record
- if err := json.Unmarshal([]byte(def.CedarJSON), &rec); err != nil {
+ if err := json.Unmarshal([]byte(def.CedarJSON), &rec, jsonx.V1()); err != nil {
return cedar.Record{}, fmt.Errorf("%w: invalid context cedarJson: %w", errInvalidRequest, err)
}
diff --git a/services/verifiedpermissions/handler.go b/services/verifiedpermissions/handler.go
index 9644569b76..4687845e51 100644
--- a/services/verifiedpermissions/handler.go
+++ b/services/verifiedpermissions/handler.go
@@ -2,7 +2,7 @@ package verifiedpermissions
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -13,6 +13,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -147,7 +148,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
PolicyID string `json:"policyId"`
PolicyTemplateID string `json:"policyTemplateId"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if req.PolicyStoreID != "" && req.PolicyID != "" {
return req.PolicyStoreID + "/" + req.PolicyID
@@ -223,14 +224,11 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
message := stripCodePrefix(err.Error())
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, awserr.ErrNotFound):
return c.JSON(http.StatusBadRequest, map[string]string{
@@ -258,7 +256,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
keyMessageField: message,
})
case errors.Is(err, awserr.ErrInvalidParameter), errors.Is(err, errInvalidRequest),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
return c.JSON(http.StatusBadRequest, map[string]string{
keyTypeField: "ValidationException",
keyMessageField: message,
diff --git a/services/verifiedpermissions/handler_authorization.go b/services/verifiedpermissions/handler_authorization.go
index 55f6ab88d4..934dafa251 100644
--- a/services/verifiedpermissions/handler_authorization.go
+++ b/services/verifiedpermissions/handler_authorization.go
@@ -3,7 +3,7 @@ package verifiedpermissions
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -11,6 +11,8 @@ import (
"strings"
cedar "github.com/cedar-policy/cedar-go"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type entityIdentifierJSON struct {
@@ -414,7 +416,7 @@ func parseJWTClaims(token string) (map[string]any, error) {
}
var claims map[string]any
- if unmarshalErr := json.Unmarshal(payload, &claims); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(payload, &claims, jsonx.V1()); unmarshalErr != nil {
return nil, fmt.Errorf("%w: bad payload JSON: %w", errMalformedJWT, unmarshalErr)
}
diff --git a/services/verifiedpermissions/handler_authorization_test.go b/services/verifiedpermissions/handler_authorization_test.go
index 93a9dd7f2a..819aa227fb 100644
--- a/services/verifiedpermissions/handler_authorization_test.go
+++ b/services/verifiedpermissions/handler_authorization_test.go
@@ -2,7 +2,8 @@ package verifiedpermissions_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,13 +11,14 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/verifiedpermissions"
)
// makeTestJWT builds a minimal unsigned JWT whose payload contains the given claims.
func makeTestJWT(claims map[string]any) string {
b64 := func(v any) string {
- raw, _ := json.Marshal(v)
+ raw, _ := json.Marshal(v, jsonx.V1())
return base64.RawURLEncoding.EncodeToString(raw)
}
@@ -92,7 +94,7 @@ func TestVPHandler_BatchIsAuthorized(t *testing.T) {
if tt.wantLen > 0 {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results := resp["results"].([]any)
assert.Len(t, results, tt.wantLen)
@@ -200,7 +202,7 @@ func TestVPHandler_IsAuthorized_DeterminingPolicies_ObjectShape(t *testing.T) {
require.Equal(t, http.StatusOK, policyRec.Code)
var policyResp map[string]any
- require.NoError(t, json.Unmarshal(policyRec.Body.Bytes(), &policyResp))
+ require.NoError(t, json.Unmarshal(policyRec.Body.Bytes(), &policyResp, jsonx.V1()))
policyID := policyResp["policyId"].(string)
rec := doVPRequest(t, h, "IsAuthorized", map[string]any{
@@ -212,7 +214,7 @@ func TestVPHandler_IsAuthorized_DeterminingPolicies_ObjectShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ALLOW", resp["decision"])
determining := resp["determiningPolicies"].([]any)
@@ -248,7 +250,7 @@ func TestVPHandler_BatchIsAuthorized_RequestEcho_Shape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
results := resp["results"].([]any)
require.Len(t, results, 1)
@@ -283,7 +285,7 @@ func TestVPHandler_IsAuthorizedWithToken_PrincipalFromJWT(t *testing.T) {
})
require.Equal(t, http.StatusOK, storeRec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp, jsonx.V1()))
policyStoreID := storeResp["policyStoreId"].(string)
// Create a permissive policy so we can verify ALLOW.
@@ -357,7 +359,7 @@ func TestVPHandler_IsAuthorizedWithToken_PrincipalFromJWT(t *testing.T) {
if tt.wantDec != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantDec, resp["decision"])
}
})
@@ -374,7 +376,7 @@ func TestVPHandler_IsAuthorizedWithToken_MissingToken(t *testing.T) {
})
require.Equal(t, http.StatusOK, storeRec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp, jsonx.V1()))
policyStoreID := storeResp["policyStoreId"].(string)
rec := doVPRequest(t, h, "IsAuthorizedWithToken", map[string]any{
@@ -393,7 +395,7 @@ func TestVPHandler_IsAuthorized_PolicySetCacheDirtyOnMutation(t *testing.T) {
})
require.Equal(t, http.StatusOK, storeRec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp, jsonx.V1()))
policyStoreID := storeResp["policyStoreId"].(string)
authReq := map[string]any{
@@ -416,7 +418,7 @@ func TestVPHandler_IsAuthorized_PolicySetCacheDirtyOnMutation(t *testing.T) {
rec := doVPRequest(t, h, "IsAuthorized", authReq)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "DENY", resp["decision"], "no policies → DENY")
// Add a permissive policy.
@@ -430,14 +432,14 @@ func TestVPHandler_IsAuthorized_PolicySetCacheDirtyOnMutation(t *testing.T) {
})
require.Equal(t, http.StatusOK, policyRec.Code)
var policyResp map[string]any
- require.NoError(t, json.Unmarshal(policyRec.Body.Bytes(), &policyResp))
+ require.NoError(t, json.Unmarshal(policyRec.Body.Bytes(), &policyResp, jsonx.V1()))
policyID := policyResp["policyId"].(string)
// After adding policy: cache must invalidate, now ALLOW.
rec2 := doVPRequest(t, h, "IsAuthorized", authReq)
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Equal(t, "ALLOW", resp2["decision"], "after CreatePolicy → ALLOW (cache invalidated)")
// Delete the policy: cache must invalidate again, back to DENY.
@@ -450,7 +452,7 @@ func TestVPHandler_IsAuthorized_PolicySetCacheDirtyOnMutation(t *testing.T) {
rec3 := doVPRequest(t, h, "IsAuthorized", authReq)
require.Equal(t, http.StatusOK, rec3.Code)
var resp3 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp3, jsonx.V1()))
assert.Equal(t, "DENY", resp3["decision"], "after DeletePolicy → DENY (cache invalidated)")
}
@@ -503,7 +505,7 @@ func TestVPHandler_IsAuthorized_RequestValidation(t *testing.T) {
require.Equal(t, http.StatusOK, ps.Code)
var psResp map[string]any
- _ = json.NewDecoder(ps.Body).Decode(&psResp)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(ps.Body), &psResp, jsonx.V1())
if v, ok := tt.body["policyStoreId"]; ok && v == "__REPLACE__" {
tt.body["policyStoreId"] = psResp["policyStoreId"]
@@ -514,7 +516,7 @@ func TestVPHandler_IsAuthorized_RequestValidation(t *testing.T) {
if tt.wantField != "" {
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Contains(t, resp, tt.wantField)
}
})
@@ -567,7 +569,7 @@ func TestVPHandler_IsAuthorizedWithToken_RequestValidation(t *testing.T) {
require.Equal(t, http.StatusOK, ps.Code)
var psResp map[string]any
- _ = json.NewDecoder(ps.Body).Decode(&psResp)
+ _ = json.UnmarshalDecode(jsontext.NewDecoder(ps.Body), &psResp, jsonx.V1())
if v, ok := tt.body["policyStoreId"]; ok && v == "__REPLACE__" {
tt.body["policyStoreId"] = psResp["policyStoreId"]
@@ -645,7 +647,7 @@ func TestVPHandler_IsAuthorizedWithToken_MultipleIdentitySources_MatchByIssuer(t
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ALLOW", resp["decision"],
"token from issuer B must resolve to the Customer identity source, not the first-created Employee one")
}
@@ -778,7 +780,7 @@ func TestVPHandler_IsAuthorizedWithToken_ClientIDValidation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantDec, resp["decision"])
})
}
@@ -821,7 +823,7 @@ func TestVPHandler_IsAuthorizedWithToken_ResponseIncludesPrincipal(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
principal, ok := resp["principal"].(map[string]any)
require.True(t, ok, "response must echo the resolved principal, got: %v", resp)
assert.Equal(t, "User", principal["entityType"])
@@ -849,7 +851,7 @@ func TestVPHandler_IsAuthorizedWithToken_ResponseIncludesPrincipal(t *testing.T)
)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "DENY", resp["decision"])
assert.NotContains(t, resp, "principal")
})
@@ -895,7 +897,7 @@ func TestVPHandler_BatchIsAuthorizedWithToken_RequestEcho_Shape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
principal, ok := resp["principal"].(map[string]any)
require.True(t, ok, "top-level principal must be echoed, got: %v", resp)
diff --git a/services/verifiedpermissions/handler_identity_sources_test.go b/services/verifiedpermissions/handler_identity_sources_test.go
index 04de19e264..9d35bc2d34 100644
--- a/services/verifiedpermissions/handler_identity_sources_test.go
+++ b/services/verifiedpermissions/handler_identity_sources_test.go
@@ -1,13 +1,15 @@
package verifiedpermissions_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/verifiedpermissions"
)
@@ -105,7 +107,7 @@ func TestVPHandler_IdentitySource(t *testing.T) {
if tt.check != nil {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tt.check(t, resp)
}
})
@@ -138,7 +140,7 @@ func TestVPHandler_GetIdentitySource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var isResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &isResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &isResp, jsonx.V1()))
return storeID, isResp["identitySourceId"].(string)
},
@@ -206,7 +208,7 @@ func TestVPHandler_DeleteIdentitySource(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var isResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &isResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &isResp, jsonx.V1()))
return storeID, isResp["identitySourceId"].(string)
},
@@ -285,7 +287,7 @@ func TestVPHandler_ListIdentitySources(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sources := resp["identitySources"].([]any)
assert.Len(t, sources, tt.wantCount)
})
@@ -348,7 +350,7 @@ func TestVPHandler_CreateIdentitySource_OIDCIdentityTokenClientIDs(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.NotContains(t, createResp, "configuration",
"CreateIdentitySourceOutput has no configuration field in the real SDK")
@@ -361,7 +363,7 @@ func TestVPHandler_CreateIdentitySource_OIDCIdentityTokenClientIDs(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
oidc := resp["configuration"].(map[string]any)["openIdConnectConfiguration"].(map[string]any)
tokenSelection := oidc["tokenSelection"].(map[string]any)
@@ -396,7 +398,7 @@ func TestVPHandler_CreateIdentitySource_CognitoIssuer(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
assert.NotContains(t, createResp, "configuration",
"CreateIdentitySourceOutput has no configuration field in the real SDK")
@@ -409,7 +411,7 @@ func TestVPHandler_CreateIdentitySource_CognitoIssuer(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cognito := resp["configuration"].(map[string]any)["cognitoUserPoolConfiguration"].(map[string]any)
assert.Equal(t, "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_test", cognito["issuer"])
@@ -446,7 +448,7 @@ func TestVPHandler_ListIdentitySources_FilterByPrincipalEntityType(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sources := resp["identitySources"].([]any)
require.Len(t, sources, 1)
assert.Equal(t, "MyCorp::User", sources[0].(map[string]any)["principalEntityType"])
@@ -535,7 +537,7 @@ func TestVPHandler_UpdateIdentitySource(t *testing.T) {
require.Equal(t, http.StatusOK, psRec.Code)
var psResp map[string]any
- require.NoError(t, json.NewDecoder(psRec.Body).Decode(&psResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(psRec.Body), &psResp, jsonx.V1()))
psID := psResp["policyStoreId"].(string)
@@ -551,7 +553,7 @@ func TestVPHandler_UpdateIdentitySource(t *testing.T) {
require.Equal(t, http.StatusOK, isRec.Code)
var isResp map[string]any
- require.NoError(t, json.NewDecoder(isRec.Body).Decode(&isResp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(isRec.Body), &isResp, jsonx.V1()))
isID := isResp["identitySourceId"].(string)
@@ -588,7 +590,7 @@ func TestVPHandler_CreateUpdateIdentitySource_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code, "body: %s", createRec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
assert.NotContains(t, createResp, "principalEntityType")
assert.NotContains(t, createResp, "configuration")
assert.NotEmpty(t, createResp["identitySourceId"])
@@ -608,7 +610,7 @@ func TestVPHandler_CreateUpdateIdentitySource_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, updateRec.Code, "body: %s", updateRec.Body.String())
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.NotContains(t, updateResp, "principalEntityType")
assert.NotContains(t, updateResp, "configuration")
assert.Equal(t, createResp["identitySourceId"], updateResp["identitySourceId"])
diff --git a/services/verifiedpermissions/handler_policies_test.go b/services/verifiedpermissions/handler_policies_test.go
index 39cdf2be70..6467d4e566 100644
--- a/services/verifiedpermissions/handler_policies_test.go
+++ b/services/verifiedpermissions/handler_policies_test.go
@@ -2,7 +2,7 @@ package verifiedpermissions_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/verifiedpermissions"
)
@@ -42,7 +43,7 @@ func TestVPHandler_PolicyCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp, jsonx.V1()))
storeID := storeResp["policyStoreId"].(string)
// Create policy
@@ -57,7 +58,7 @@ func TestVPHandler_PolicyCRUD(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var policyResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &policyResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &policyResp, jsonx.V1()))
policyID := policyResp["policyId"].(string)
assert.NotEmpty(t, policyID)
@@ -253,7 +254,7 @@ func TestVPHandler_GetPolicy_NotFound(t *testing.T) {
map[string]any{"description": "test", "validationSettings": map[string]any{"mode": "OFF"}},
)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp, jsonx.V1()))
storeID := storeResp["policyStoreId"].(string)
rec = doVPRequest(t, h, "GetPolicy", map[string]any{
@@ -280,7 +281,7 @@ func TestVPHandler_DeletePolicy_IdempotentOnMissingPolicy(t *testing.T) {
map[string]any{"description": "test", "validationSettings": map[string]any{"mode": "OFF"}},
)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp, jsonx.V1()))
storeID := storeResp["policyStoreId"].(string)
rec = doVPRequest(t, h, "DeletePolicy", map[string]any{
@@ -318,7 +319,7 @@ func TestVPHandler_CreatePolicy_TemplateLinked(t *testing.T) {
map[string]any{"description": "test", "validationSettings": map[string]any{"mode": "OFF"}},
)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp, jsonx.V1()))
storeID := storeResp["policyStoreId"].(string)
// Create a policy template first.
@@ -329,7 +330,7 @@ func TestVPHandler_CreatePolicy_TemplateLinked(t *testing.T) {
})
require.Equal(t, http.StatusOK, tmplRec.Code)
var tmplResp map[string]any
- require.NoError(t, json.Unmarshal(tmplRec.Body.Bytes(), &tmplResp))
+ require.NoError(t, json.Unmarshal(tmplRec.Body.Bytes(), &tmplResp, jsonx.V1()))
templateID := tmplResp["policyTemplateId"].(string)
// Create template-linked policy
@@ -346,7 +347,7 @@ func TestVPHandler_CreatePolicy_TemplateLinked(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var policyResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &policyResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &policyResp, jsonx.V1()))
assert.Equal(t, "TEMPLATE_LINKED", policyResp["policyType"])
}
@@ -370,7 +371,7 @@ func TestVPHandler_UpdatePolicy_TemplateLinkedRejected(t *testing.T) {
})
require.Equal(t, http.StatusOK, tmplRec.Code)
var tmplResp map[string]any
- require.NoError(t, json.Unmarshal(tmplRec.Body.Bytes(), &tmplResp))
+ require.NoError(t, json.Unmarshal(tmplRec.Body.Bytes(), &tmplResp, jsonx.V1()))
templateID := tmplResp["policyTemplateId"].(string)
createRec := doVPRequest(t, h, "CreatePolicy", map[string]any{
@@ -385,7 +386,7 @@ func TestVPHandler_UpdatePolicy_TemplateLinkedRejected(t *testing.T) {
})
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
policyID := createResp["policyId"].(string)
t.Run("templateLinked definition rejected outright", func(t *testing.T) {
@@ -429,7 +430,7 @@ func TestVPHandler_UpdatePolicy_TemplateLinkedRejected(t *testing.T) {
})
require.Equal(t, http.StatusOK, staticRec.Code)
var staticResp map[string]any
- require.NoError(t, json.Unmarshal(staticRec.Body.Bytes(), &staticResp))
+ require.NoError(t, json.Unmarshal(staticRec.Body.Bytes(), &staticResp, jsonx.V1()))
staticPolicyID := staticResp["policyId"].(string)
rec := doVPRequest(t, h, "UpdatePolicy", map[string]any{
@@ -449,7 +450,7 @@ func TestVPHandler_UpdatePolicy_TemplateLinkedRejected(t *testing.T) {
getRec := doVPRequest(t, h, "GetPolicy", map[string]any{"policyStoreId": storeID, "policyId": policyID})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
def := getResp["definition"].(map[string]any)["templateLinked"].(map[string]any)
assert.Equal(t, "alice", def["principal"].(map[string]any)["entityId"])
}
@@ -479,7 +480,7 @@ func TestVPHandler_BatchGetPolicy(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var pResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &pResp, jsonx.V1()))
policyID := pResp["policyId"].(string)
return map[string]any{
@@ -544,7 +545,7 @@ func TestVPHandler_BatchGetPolicy(t *testing.T) {
if tt.check != nil {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tt.check(t, resp)
}
})
@@ -562,7 +563,7 @@ func TestVPHandler_BatchGetPolicy_EmptyRequests(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp["results"])
assert.Empty(t, resp["errors"])
}
@@ -580,7 +581,7 @@ func TestVPHandler_ListPolicies_FilterByPrincipalIdentifier(t *testing.T) {
require.Equal(t, http.StatusOK, ptRec.Code)
var ptResp map[string]any
- require.NoError(t, json.Unmarshal(ptRec.Body.Bytes(), &ptResp))
+ require.NoError(t, json.Unmarshal(ptRec.Body.Bytes(), &ptResp, jsonx.V1()))
templateID := ptResp["policyTemplateId"].(string)
linkedRec := doVPRequest(t, h, "CreatePolicy", map[string]any{
@@ -614,7 +615,7 @@ func TestVPHandler_ListPolicies_FilterByPrincipalIdentifier(t *testing.T) {
require.Equal(t, http.StatusOK, filteredRec.Code, "body: %s", filteredRec.Body.String())
var filteredResp map[string]any
- require.NoError(t, json.Unmarshal(filteredRec.Body.Bytes(), &filteredResp))
+ require.NoError(t, json.Unmarshal(filteredRec.Body.Bytes(), &filteredResp, jsonx.V1()))
policies := filteredResp["policies"].([]any)
require.Len(t, policies, 1)
@@ -628,7 +629,7 @@ func TestVPHandler_ListPolicies_FilterByPrincipalIdentifier(t *testing.T) {
require.Equal(t, http.StatusOK, unspecifiedRec.Code, "body: %s", unspecifiedRec.Body.String())
var unspecifiedResp map[string]any
- require.NoError(t, json.Unmarshal(unspecifiedRec.Body.Bytes(), &unspecifiedResp))
+ require.NoError(t, json.Unmarshal(unspecifiedRec.Body.Bytes(), &unspecifiedResp, jsonx.V1()))
unspecifiedPolicies := unspecifiedResp["policies"].([]any)
require.Len(t, unspecifiedPolicies, 1)
}
@@ -649,7 +650,7 @@ func TestVPHandler_BatchGetPolicy_IncludesDefinition(t *testing.T) {
require.Equal(t, http.StatusOK, storeRec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp, jsonx.V1()))
policyStoreID, _ := storeResp["policyStoreId"].(string)
require.NotEmpty(t, policyStoreID)
@@ -667,7 +668,7 @@ func TestVPHandler_BatchGetPolicy_IncludesDefinition(t *testing.T) {
require.Equal(t, http.StatusOK, staticRec.Code)
var staticResp map[string]any
- require.NoError(t, json.Unmarshal(staticRec.Body.Bytes(), &staticResp))
+ require.NoError(t, json.Unmarshal(staticRec.Body.Bytes(), &staticResp, jsonx.V1()))
staticPolicyID, _ := staticResp["policyId"].(string)
require.NotEmpty(t, staticPolicyID)
@@ -681,7 +682,7 @@ func TestVPHandler_BatchGetPolicy_IncludesDefinition(t *testing.T) {
require.Equal(t, http.StatusOK, tmplRec.Code)
var tmplResp map[string]any
- require.NoError(t, json.Unmarshal(tmplRec.Body.Bytes(), &tmplResp))
+ require.NoError(t, json.Unmarshal(tmplRec.Body.Bytes(), &tmplResp, jsonx.V1()))
templateID, _ := tmplResp["policyTemplateId"].(string)
require.NotEmpty(t, templateID)
@@ -705,7 +706,7 @@ func TestVPHandler_BatchGetPolicy_IncludesDefinition(t *testing.T) {
require.Equal(t, http.StatusOK, tlRec.Code)
var tlResp map[string]any
- require.NoError(t, json.Unmarshal(tlRec.Body.Bytes(), &tlResp))
+ require.NoError(t, json.Unmarshal(tlRec.Body.Bytes(), &tlResp, jsonx.V1()))
tlPolicyID, _ := tlResp["policyId"].(string)
require.NotEmpty(t, tlPolicyID)
@@ -748,7 +749,7 @@ func TestVPHandler_BatchGetPolicy_IncludesDefinition(t *testing.T) {
require.Equal(t, http.StatusOK, batchRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &resp, jsonx.V1()))
results, ok := resp["results"].([]any)
require.True(t, ok, "results must be an array")
@@ -779,7 +780,7 @@ func TestVPHandler_ListPolicies_PaginationOpaqueTokens(t *testing.T) {
})
require.Equal(t, http.StatusOK, storeRec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp, jsonx.V1()))
policyStoreID := storeResp["policyStoreId"].(string)
// Create 3 policies.
@@ -803,7 +804,7 @@ func TestVPHandler_ListPolicies_PaginationOpaqueTokens(t *testing.T) {
})
require.Equal(t, http.StatusOK, page1Rec.Code)
var page1 map[string]any
- require.NoError(t, json.Unmarshal(page1Rec.Body.Bytes(), &page1))
+ require.NoError(t, json.Unmarshal(page1Rec.Body.Bytes(), &page1, jsonx.V1()))
nextToken, _ := page1["nextToken"].(string)
require.NotEmpty(t, nextToken, "nextToken must be present when more results exist")
@@ -821,7 +822,7 @@ func TestVPHandler_ListPolicies_PaginationOpaqueTokens(t *testing.T) {
})
require.Equal(t, http.StatusOK, page2Rec.Code, "page2 body: %s", page2Rec.Body.String())
var page2 map[string]any
- require.NoError(t, json.Unmarshal(page2Rec.Body.Bytes(), &page2))
+ require.NoError(t, json.Unmarshal(page2Rec.Body.Bytes(), &page2, jsonx.V1()))
page2Items, _ := page2["policies"].([]any)
assert.Len(t, page2Items, 1, "page2 should have the remaining 1 policy")
@@ -851,7 +852,7 @@ func TestVPHandler_CreatePolicy_StaticScopeEcho(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "Forbid", resp["effect"])
@@ -894,7 +895,7 @@ func TestVPHandler_CreatePolicy_UnconstrainedScopeOmitsEchoFields(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "Permit", resp["effect"])
assert.NotContains(t, resp, "principal")
@@ -919,7 +920,7 @@ func TestVPHandler_GetPolicy_TemplateLinkedScopeEcho(t *testing.T) {
})
require.Equal(t, http.StatusOK, tmplRec.Code)
var tmplResp map[string]any
- require.NoError(t, json.Unmarshal(tmplRec.Body.Bytes(), &tmplResp))
+ require.NoError(t, json.Unmarshal(tmplRec.Body.Bytes(), &tmplResp, jsonx.V1()))
templateID := tmplResp["policyTemplateId"].(string)
rec := doVPRequest(t, h, "CreatePolicy", map[string]any{
@@ -934,7 +935,7 @@ func TestVPHandler_GetPolicy_TemplateLinkedScopeEcho(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
policyID := createResp["policyId"].(string)
assert.Equal(t, "Forbid", createResp["effect"])
@@ -949,7 +950,7 @@ func TestVPHandler_GetPolicy_TemplateLinkedScopeEcho(t *testing.T) {
})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
principal, _ := getResp["principal"].(map[string]any)
require.NotNil(t, principal)
@@ -982,7 +983,7 @@ func TestVPHandler_ListPolicies_StaticItemOmitsStatement(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
items, _ := listResp["policies"].([]any)
require.Len(t, items, 1)
@@ -999,7 +1000,7 @@ func TestVPHandler_ListPolicies_StaticItemOmitsStatement(t *testing.T) {
getRec := doVPRequest(t, h, "GetPolicy", map[string]any{"policyStoreId": storeID, "policyId": policyID})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
getStatic, _ := getResp["definition"].(map[string]any)["static"].(map[string]any)
assert.Equal(t, `permit(principal, action, resource);`, getStatic["statement"])
}
diff --git a/services/verifiedpermissions/handler_policy_stores_test.go b/services/verifiedpermissions/handler_policy_stores_test.go
index ba5a183785..d275f8cf84 100644
--- a/services/verifiedpermissions/handler_policy_stores_test.go
+++ b/services/verifiedpermissions/handler_policy_stores_test.go
@@ -1,7 +1,7 @@
package verifiedpermissions_test
import (
- "encoding/json"
+ "encoding/json/v2"
"maps"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/verifiedpermissions"
)
@@ -55,7 +56,7 @@ func TestVPHandler_CreatePolicyStore(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, tt.wantKey)
})
}
@@ -81,7 +82,7 @@ func TestVPHandler_GetPolicyStore(t *testing.T) {
map[string]any{"description": "test", "validationSettings": map[string]any{"mode": "OFF"}},
)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["policyStoreId"].(string)
},
@@ -148,7 +149,7 @@ func TestVPHandler_ListPolicyStores(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stores := resp["policyStores"].([]any)
assert.Len(t, stores, tt.numStores)
})
@@ -175,7 +176,7 @@ func TestVPHandler_DeletePolicyStore(t *testing.T) {
map[string]any{"description": "test", "validationSettings": map[string]any{"mode": "OFF"}},
)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["policyStoreId"].(string)
},
@@ -225,7 +226,7 @@ func TestVPHandler_UpdatePolicyStore(t *testing.T) {
map[string]any{"description": "original", "validationSettings": map[string]any{"mode": "OFF"}},
)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["policyStoreId"].(string)
},
@@ -302,7 +303,7 @@ func TestVPHandler_CreatePolicyStore_WithTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotEmpty(t, resp["policyStoreId"])
assert.NotEmpty(t, resp["arn"])
}
@@ -353,14 +354,14 @@ func TestVPHandler_DeletePolicyStore_DeletionProtection_InvalidStateException(t
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
storeID := createResp["policyStoreId"].(string)
rec := doVPRequest(t, h, "DeletePolicyStore", map[string]any{"policyStoreId": storeID})
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidStateException", resp["__type"])
}
@@ -409,7 +410,7 @@ func TestVPHandler_CreatePolicyStore_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotContains(t, resp, "validationSettings")
assert.NotEmpty(t, resp["policyStoreId"])
@@ -432,7 +433,7 @@ func TestVPHandler_GetPolicyStore_CedarVersion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "CEDAR_4", resp["cedarVersion"])
}
@@ -451,7 +452,7 @@ func TestVPHandler_ListPolicyStores_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["policyStores"].([]any)
require.NotEmpty(t, items)
@@ -479,7 +480,7 @@ func TestVPHandler_UpdatePolicyStore_WireShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotContains(t, resp, "validationSettings")
assert.NotEmpty(t, resp["createdDate"], "createdDate is a required UpdatePolicyStoreOutput field")
@@ -554,7 +555,7 @@ func TestVPHandler_CreatePolicyStoreAlias(t *testing.T) {
assert.Equal(t, tt.wantCode, rec.Code, "body: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tt.wantType != "" {
assert.Equal(t, tt.wantType, resp["__type"])
@@ -591,13 +592,13 @@ func TestVPHandler_CreatePolicyStoreAlias_IdempotentAndConflict(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
rec2 := doVPRequest(t, h, "CreatePolicyStoreAlias", body)
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Equal(t, resp1["createdAt"], resp2["createdAt"], "same (aliasName, policyStoreId) replays, no duplicate")
rec3 := doVPRequest(t, h, "CreatePolicyStoreAlias", map[string]any{
@@ -606,7 +607,7 @@ func TestVPHandler_CreatePolicyStoreAlias_IdempotentAndConflict(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec3.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ConflictException", errResp["__type"])
}
@@ -657,7 +658,7 @@ func TestVPHandler_GetPolicyStoreAlias(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "Active", resp["state"])
assert.Equal(t, aliasName, resp["aliasName"])
})
@@ -700,7 +701,7 @@ func TestVPHandler_ListPolicyStoreAliases(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["policyStoreAliases"].([]any)
assert.Len(t, items, tt.wantCount)
})
@@ -756,7 +757,7 @@ func TestVPHandler_DeletePolicyStoreAlias(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantState, resp["state"])
})
}
@@ -802,7 +803,7 @@ func TestVPHandler_DeletePolicyStore_CascadesAliases(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["policyStoreAliases"].([]any)
assert.Empty(t, items, "dangling alias row survived policy store delete")
@@ -872,7 +873,7 @@ func TestVPHandler_ResolvePolicyStoreID_AcceptsAlias(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, storeID, resp["policyStoreId"], "response echoes the real ID, not the alias")
})
}
@@ -923,13 +924,13 @@ func TestVPHandler_CreatePolicyStore_ClientTokenIdempotency(t *testing.T) {
rec1 := doVPRequest(t, h, "CreatePolicyStore", body("first"))
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
// Same token, same parameters: replays the original policy store.
rec2 := doVPRequest(t, h, "CreatePolicyStore", body("first"))
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Equal(t, resp1["policyStoreId"], resp2["policyStoreId"])
assert.Equal(t, resp1["createdDate"], resp2["createdDate"])
@@ -938,7 +939,7 @@ func TestVPHandler_CreatePolicyStore_ClientTokenIdempotency(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec3.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ConflictException", errResp["__type"])
// A different token creates a genuinely new policy store.
@@ -949,6 +950,6 @@ func TestVPHandler_CreatePolicyStore_ClientTokenIdempotency(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec4.Code)
var resp4 map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &resp4))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &resp4, jsonx.V1()))
assert.NotEqual(t, resp1["policyStoreId"], resp4["policyStoreId"])
}
diff --git a/services/verifiedpermissions/handler_policy_templates_test.go b/services/verifiedpermissions/handler_policy_templates_test.go
index 946e024005..b5b412c861 100644
--- a/services/verifiedpermissions/handler_policy_templates_test.go
+++ b/services/verifiedpermissions/handler_policy_templates_test.go
@@ -1,12 +1,14 @@
package verifiedpermissions_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestVPHandler_PolicyTemplateCRUD(t *testing.T) {
@@ -38,7 +40,7 @@ func TestVPHandler_PolicyTemplateCRUD(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp, jsonx.V1()))
storeID := storeResp["policyStoreId"].(string)
// Create template
@@ -50,7 +52,7 @@ func TestVPHandler_PolicyTemplateCRUD(t *testing.T) {
require.Equal(t, tt.wantCode, rec.Code)
var tplResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tplResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &tplResp, jsonx.V1()))
templateID := tplResp["policyTemplateId"].(string)
assert.NotEmpty(t, templateID)
@@ -195,7 +197,7 @@ func TestVPHandler_UpdatePolicyTemplate_NotFound(t *testing.T) {
map[string]any{"description": "test", "validationSettings": map[string]any{"mode": "OFF"}},
)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp, jsonx.V1()))
storeID := storeResp["policyStoreId"].(string)
rec = doVPRequest(t, h, "UpdatePolicyTemplate", map[string]any{
@@ -219,7 +221,7 @@ func TestVPHandler_DeletePolicyTemplate_NotFound(t *testing.T) {
map[string]any{"description": "test", "validationSettings": map[string]any{"mode": "OFF"}},
)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &storeResp, jsonx.V1()))
storeID := storeResp["policyStoreId"].(string)
rec = doVPRequest(t, h, "DeletePolicyTemplate", map[string]any{
diff --git a/services/verifiedpermissions/handler_schema_test.go b/services/verifiedpermissions/handler_schema_test.go
index 562b11631a..783fbabd34 100644
--- a/services/verifiedpermissions/handler_schema_test.go
+++ b/services/verifiedpermissions/handler_schema_test.go
@@ -1,13 +1,14 @@
package verifiedpermissions_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/verifiedpermissions"
)
@@ -77,7 +78,7 @@ func TestVPHandler_PutSchema(t *testing.T) {
if tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, storeID, resp["policyStoreId"])
assert.NotEmpty(t, resp["createdDate"])
assert.NotEmpty(t, resp["lastUpdatedDate"])
@@ -147,7 +148,7 @@ func TestVPHandler_GetSchema(t *testing.T) {
if hasPut && tt.wantCode == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, storeID, resp["policyStoreId"])
assert.JSONEq(t, `{"namespace": "MyCorp"}`, resp["schema"].(string))
}
@@ -180,6 +181,6 @@ func TestVPHandler_PutSchema_UpdateSchema(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, `{"v": 2}`, resp["schema"])
}
diff --git a/services/verifiedpermissions/handler_tags_test.go b/services/verifiedpermissions/handler_tags_test.go
index 55e5f0fd5f..80d4394d06 100644
--- a/services/verifiedpermissions/handler_tags_test.go
+++ b/services/verifiedpermissions/handler_tags_test.go
@@ -1,7 +1,7 @@
package verifiedpermissions_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strconv"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/verifiedpermissions"
)
@@ -34,7 +35,7 @@ func TestVPHandler_TagResource(t *testing.T) {
map[string]any{"description": "test", "validationSettings": map[string]any{"mode": "OFF"}},
)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["arn"].(string)
},
@@ -95,7 +96,7 @@ func TestVPHandler_UntagResource(t *testing.T) {
"validationSettings": map[string]any{"mode": "OFF"},
})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["arn"].(string)
},
@@ -156,7 +157,7 @@ func TestVPHandler_ListTagsForResource(t *testing.T) {
"validationSettings": map[string]any{"mode": "OFF"},
})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["arn"].(string)
},
@@ -193,7 +194,7 @@ func TestVPHandler_ListTagsForResource(t *testing.T) {
if tt.wantTags {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "tags")
}
})
@@ -210,7 +211,7 @@ func TestVPHandler_TagResource_TooManyTags(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
resourceARN := getResp["arn"].(string)
tags := make(map[string]any, 51)
@@ -225,7 +226,7 @@ func TestVPHandler_TagResource_TooManyTags(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "TooManyTagsException", resp["__type"])
}
@@ -245,7 +246,7 @@ func TestVPHandler_TagResource_PolicyResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, storeRec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp, jsonx.V1()))
policyStoreID := storeResp["policyStoreId"].(string)
// Get policy store ARN.
@@ -254,7 +255,7 @@ func TestVPHandler_TagResource_PolicyResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, getStoreRec.Code)
var getStoreResp map[string]any
- require.NoError(t, json.Unmarshal(getStoreRec.Body.Bytes(), &getStoreResp))
+ require.NoError(t, json.Unmarshal(getStoreRec.Body.Bytes(), &getStoreResp, jsonx.V1()))
storeARN := getStoreResp["arn"].(string)
_ = storeARN
@@ -270,7 +271,7 @@ func TestVPHandler_TagResource_PolicyResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, policyRec.Code)
var policyResp map[string]any
- require.NoError(t, json.Unmarshal(policyRec.Body.Bytes(), &policyResp))
+ require.NoError(t, json.Unmarshal(policyRec.Body.Bytes(), &policyResp, jsonx.V1()))
policyID := policyResp["policyId"].(string)
// Build the policy ARN (format: arn:aws:verifiedpermissions:::policy//)
@@ -289,7 +290,7 @@ func TestVPHandler_TagResource_PolicyResource(t *testing.T) {
listRec1 := doVPRequest(t, h, "ListTagsForResource", map[string]any{"resourceArn": policyARN})
require.Equal(t, http.StatusOK, listRec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(listRec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(listRec1.Body.Bytes(), &resp1, jsonx.V1()))
tags1, _ := resp1["tags"].(map[string]any)
assert.Equal(t, "test", tags1["Env"])
@@ -303,7 +304,7 @@ func TestVPHandler_TagResource_PolicyResource(t *testing.T) {
listRec2 := doVPRequest(t, h, "ListTagsForResource", map[string]any{"resourceArn": policyARN})
require.Equal(t, http.StatusOK, listRec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(listRec2.Body.Bytes(), &resp2, jsonx.V1()))
tags2, _ := resp2["tags"].(map[string]any)
assert.Equal(t, "prod", tags2["Env"])
@@ -319,7 +320,7 @@ func TestVPHandler_TagResource_PolicyResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags, _ := listResp["tags"].(map[string]any)
_, envPresent := tags["Env"]
assert.False(t, envPresent, "Env tag should be removed after UntagResource")
@@ -335,7 +336,7 @@ func TestVPHandler_TagResource_PolicyTemplateResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, storeRec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp, jsonx.V1()))
policyStoreID := storeResp["policyStoreId"].(string)
tmplRec := doVPRequest(t, h, "CreatePolicyTemplate", map[string]any{
@@ -345,7 +346,7 @@ func TestVPHandler_TagResource_PolicyTemplateResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, tmplRec.Code)
var tmplResp map[string]any
- require.NoError(t, json.Unmarshal(tmplRec.Body.Bytes(), &tmplResp))
+ require.NoError(t, json.Unmarshal(tmplRec.Body.Bytes(), &tmplResp, jsonx.V1()))
templateID := tmplResp["policyTemplateId"].(string)
templateARN := fmt.Sprintf(
@@ -364,7 +365,7 @@ func TestVPHandler_TagResource_PolicyTemplateResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags, _ := listResp["tags"].(map[string]any)
assert.Equal(t, "parity-test", tags["Purpose"])
}
@@ -379,7 +380,7 @@ func TestVPHandler_TagResource_IdentitySourceResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, storeRec.Code)
var storeResp map[string]any
- require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp))
+ require.NoError(t, json.Unmarshal(storeRec.Body.Bytes(), &storeResp, jsonx.V1()))
policyStoreID := storeResp["policyStoreId"].(string)
isRec := doVPRequest(t, h, "CreateIdentitySource", map[string]any{
@@ -394,7 +395,7 @@ func TestVPHandler_TagResource_IdentitySourceResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, isRec.Code, "CreateIdentitySource body: %s", isRec.Body.String())
var isResp map[string]any
- require.NoError(t, json.Unmarshal(isRec.Body.Bytes(), &isResp))
+ require.NoError(t, json.Unmarshal(isRec.Body.Bytes(), &isResp, jsonx.V1()))
identitySourceID := isResp["identitySourceId"].(string)
identitySourceARN := fmt.Sprintf(
@@ -413,7 +414,7 @@ func TestVPHandler_TagResource_IdentitySourceResource(t *testing.T) {
})
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
tags, _ := listResp["tags"].(map[string]any)
assert.Equal(t, "parity", tags["Owner"])
}
diff --git a/services/verifiedpermissions/handler_test.go b/services/verifiedpermissions/handler_test.go
index 9a4bfee5b1..0eb2a67d03 100644
--- a/services/verifiedpermissions/handler_test.go
+++ b/services/verifiedpermissions/handler_test.go
@@ -2,7 +2,8 @@ package verifiedpermissions_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/verifiedpermissions"
)
@@ -33,7 +35,7 @@ func doVPRequest(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -75,7 +77,7 @@ func createTestPolicyStore(t *testing.T, h *verifiedpermissions.Handler) string
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp["policyStoreId"].(string)
}
@@ -235,7 +237,7 @@ func TestVPHandler_ExtractResource(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -315,7 +317,7 @@ func TestVPHandler_Snapshot_Restore(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stores := resp["policyStores"].([]any)
assert.Len(t, stores, tt.numStores)
})
@@ -384,7 +386,7 @@ func TestVPHandler_Snapshot_Restore_WithNewResources(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var isResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &isResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &isResp, jsonx.V1()))
isID := isResp["identitySourceId"].(string)
// Put schema
@@ -414,7 +416,7 @@ func TestVPHandler_Snapshot_Restore_WithNewResources(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var schemaResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &schemaResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &schemaResp, jsonx.V1()))
assert.JSONEq(t, `{"ns": "test"}`, schemaResp["schema"].(string))
}
@@ -475,7 +477,7 @@ func TestVPHandler_HandleError_ValidationException(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(rec.Body), &resp, jsonx.V1()))
assert.Equal(t, "ValidationException", resp["__type"])
}
@@ -515,7 +517,7 @@ func TestVPHandler_UnknownOp_ReturnsUnknownOperationException(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "UnknownOperationException", resp["__type"], "body: %s", rec.Body.String())
})
}
diff --git a/services/verifiedpermissions/idempotency_test.go b/services/verifiedpermissions/idempotency_test.go
index ea1b1ddff8..9d6ed32bbf 100644
--- a/services/verifiedpermissions/idempotency_test.go
+++ b/services/verifiedpermissions/idempotency_test.go
@@ -1,12 +1,14 @@
package verifiedpermissions_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestVPHandler_CreateOps_ClientTokenIdempotency table-drives the shared
@@ -93,19 +95,19 @@ func TestVPHandler_CreateOps_ClientTokenIdempotency(t *testing.T) {
rec1 := doVPRequest(t, h, tt.action, tt.body(storeID, "fixed-token", "v1"))
require.Equal(t, http.StatusOK, rec1.Code, "body: %s", rec1.Body.String())
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
// Same token, same parameters: replays the original resource.
rec2 := doVPRequest(t, h, tt.action, tt.body(storeID, "fixed-token", "v1"))
require.Equal(t, http.StatusOK, rec2.Code, "body: %s", rec2.Body.String())
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
assert.Equal(t, resp1[tt.idField], resp2[tt.idField])
listRec := doVPRequest(t, h, tt.listAction, map[string]any{"policyStoreId": storeID})
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
items, _ := listResp[tt.listField].([]any)
assert.Len(t, items, 1, "a replayed ClientToken call must not create a duplicate resource")
@@ -113,7 +115,7 @@ func TestVPHandler_CreateOps_ClientTokenIdempotency(t *testing.T) {
rec3 := doVPRequest(t, h, tt.action, tt.body(storeID, "fixed-token", "v2-different"))
assert.Equal(t, http.StatusBadRequest, rec3.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "ConflictException", errResp["__type"])
})
}
diff --git a/services/verifiedpermissions/multi_region_test.go b/services/verifiedpermissions/multi_region_test.go
index f790a3625f..81efc36fa2 100644
--- a/services/verifiedpermissions/multi_region_test.go
+++ b/services/verifiedpermissions/multi_region_test.go
@@ -2,7 +2,8 @@ package verifiedpermissions_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/verifiedpermissions"
)
@@ -180,8 +182,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/verifiedpermissions/oidc_jwks.go b/services/verifiedpermissions/oidc_jwks.go
index e09957cda8..b584091b30 100644
--- a/services/verifiedpermissions/oidc_jwks.go
+++ b/services/verifiedpermissions/oidc_jwks.go
@@ -6,7 +6,7 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -20,6 +20,7 @@ import (
awsarn "github.com/aws/aws-sdk-go-v2/aws/arn"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -103,7 +104,7 @@ func (v *TokenVerifier) getJSON(rawURL string, out any) error {
return fmt.Errorf("read %s: %w", rawURL, err)
}
- if err = json.Unmarshal(body, out); err != nil {
+ if err = json.Unmarshal(body, out, jsonx.V1()); err != nil {
return fmt.Errorf("decode %s: %w", rawURL, err)
}
diff --git a/services/verifiedpermissions/omission_defaults_test.go b/services/verifiedpermissions/omission_defaults_test.go
index a61e124fc7..c1e5a8194b 100644
--- a/services/verifiedpermissions/omission_defaults_test.go
+++ b/services/verifiedpermissions/omission_defaults_test.go
@@ -1,13 +1,15 @@
package verifiedpermissions_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// These tests lock in the documented per-page default the real SDK states
@@ -28,7 +30,7 @@ func TestVPHandler_ListPolicyStores_DefaultPageSize(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stores, _ := resp["policyStores"].([]any)
assert.Len(t, stores, 10, "ListPolicyStores omits maxResults => real SDK defaults to 10 per response")
@@ -58,7 +60,7 @@ func TestVPHandler_ListPolicies_DefaultPageSize(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policies, _ := resp["policies"].([]any)
assert.Len(t, policies, 10, "ListPolicies omits maxResults => real SDK defaults to 10 per response")
@@ -83,7 +85,7 @@ func TestVPHandler_ListPolicyTemplates_DefaultPageSize(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
templates, _ := resp["policyTemplates"].([]any)
assert.Len(t, templates, 10, "ListPolicyTemplates omits maxResults => real SDK defaults to 10 per response")
@@ -114,7 +116,7 @@ func TestVPHandler_ListIdentitySources_DefaultPageSize(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sources, _ := resp["identitySources"].([]any)
assert.Len(t, sources, 10, "ListIdentitySources omits maxResults => real SDK defaults to 10 per response")
@@ -139,7 +141,7 @@ func TestVPHandler_ListPolicyStoreAliases_DefaultPageSize(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
aliases, _ := resp["policyStoreAliases"].([]any)
assert.Len(t, aliases, 5, "ListPolicyStoreAliases omits maxResults => real SDK defaults to 5 per response")
@@ -169,7 +171,7 @@ func TestVPHandler_CreateIdentitySource_CognitoGroupEntityTypeDefault(t *testing
require.Equal(t, http.StatusOK, rec.Code, "body: %s", rec.Body.String())
var created map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &created, jsonx.V1()))
rec = doVPRequest(t, h, "GetIdentitySource", map[string]any{
"policyStoreId": storeID,
@@ -178,7 +180,7 @@ func TestVPHandler_CreateIdentitySource_CognitoGroupEntityTypeDefault(t *testing
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
cfg, _ := resp["configuration"].(map[string]any)
cognito, _ := cfg["cognitoUserPoolConfiguration"].(map[string]any)
diff --git a/services/verifiedpermissions/persistence.go b/services/verifiedpermissions/persistence.go
index 53cb195968..2ad772f1fa 100644
--- a/services/verifiedpermissions/persistence.go
+++ b/services/verifiedpermissions/persistence.go
@@ -2,7 +2,7 @@ package verifiedpermissions
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -57,7 +57,7 @@ func buildSchemaDTORegistry() (*store.Registry, *store.Table[policyStoreSchemaDT
// InMemoryBackend's doc comment in backend.go) and is persisted here
// directly, as before.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ResourceTags map[string]map[string]string `json:"resourceTags,omitempty"`
AccountID string `json:"accountID"`
Region string `json:"region"`
@@ -163,7 +163,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// registerAllTables doc) from tables via the ephemeral DTO registry, factored
// out of Restore to keep Restore's own cognitive complexity low. Callers must
// hold b.mu.Lock.
-func (b *InMemoryBackend) restoreSchemas(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreSchemas(tables map[string]jsontext.Value) error {
dtoReg, dtoTable := buildSchemaDTORegistry()
if err := dtoReg.RestoreAll(tables); err != nil {
return fmt.Errorf("verifiedpermissions: restore snapshot schema DTO table: %w", err)
diff --git a/services/verifiedpermissions/policy_scope.go b/services/verifiedpermissions/policy_scope.go
index 9d6ccc13aa..56f067d4b7 100644
--- a/services/verifiedpermissions/policy_scope.go
+++ b/services/verifiedpermissions/policy_scope.go
@@ -1,10 +1,12 @@
package verifiedpermissions
import (
- "encoding/json"
+ "encoding/json/v2"
"regexp"
cedar "github.com/cedar-policy/cedar-go"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// cedarScopeEntity mirrors the "entity" object nested inside a Cedar JSON
@@ -80,7 +82,7 @@ func instantiateTemplate(templateStatement string, p *Policy) string {
// cedarEntityLiteral renders entityType::"entityId" as a Cedar entity UID literal.
func cedarEntityLiteral(entityType, entityID string) string {
- b, _ := json.Marshal(entityID) // Cedar string-literal escaping matches JSON's for plain ASCII IDs.
+ b, _ := json.Marshal(entityID, jsonx.V1()) // Cedar string-literal escaping matches JSON's for plain ASCII IDs.
return entityType + "::" + string(b)
}
@@ -105,7 +107,7 @@ func parseCedarScope(statement string) *PolicyScopeResult {
}
var pj cedarPolicyJSON
- if unmarshalErr := json.Unmarshal(raw, &pj); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(raw, &pj, jsonx.V1()); unmarshalErr != nil {
return nil
}
diff --git a/services/verifiedpermissions/policy_template_item_shape_test.go b/services/verifiedpermissions/policy_template_item_shape_test.go
index dfeafdbb97..d4461e57d7 100644
--- a/services/verifiedpermissions/policy_template_item_shape_test.go
+++ b/services/verifiedpermissions/policy_template_item_shape_test.go
@@ -1,11 +1,13 @@
package verifiedpermissions_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestListPolicyTemplates_ItemHasNoStatement proves ListPolicyTemplates'
@@ -35,7 +37,7 @@ func TestListPolicyTemplates_ItemHasNoStatement(t *testing.T) {
var body struct {
PolicyTemplates []map[string]any `json:"policyTemplates"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &body, jsonx.V1()))
require.Len(t, body.PolicyTemplates, 1)
item := body.PolicyTemplates[0]
diff --git a/services/verifiedpermissions/policy_validation.go b/services/verifiedpermissions/policy_validation.go
index 0b686b94bf..c5a3db341d 100644
--- a/services/verifiedpermissions/policy_validation.go
+++ b/services/verifiedpermissions/policy_validation.go
@@ -2,19 +2,22 @@ package verifiedpermissions
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"strings"
cedar "github.com/cedar-policy/cedar-go"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var errSchemaMismatch = errors.New("policy validation failed")
type cedarSchemaNamespace struct {
- EntityTypes map[string]json.RawMessage `json:"entityTypes"`
- Actions map[string]json.RawMessage `json:"actions"`
+ EntityTypes map[string]jsontext.Value `json:"entityTypes"`
+ Actions map[string]jsontext.Value `json:"actions"`
}
// splitQualified splits "NS::Sub::Name" into its namespace and local name.
@@ -34,7 +37,7 @@ func (b *InMemoryBackend) loadSchemaNamespacesLocked(policyStoreID string) (map[
}
var ns map[string]cedarSchemaNamespace
- if err := json.Unmarshal([]byte(s.Schema), &ns); err != nil {
+ if err := json.Unmarshal([]byte(s.Schema), &ns, jsonx.V1()); err != nil {
return nil, true
}
@@ -73,7 +76,7 @@ func (b *InMemoryBackend) validateStaticPolicyLocked(policyStoreID, statement st
}
var pj cedarPolicyJSON
- if json.Unmarshal(raw, &pj) != nil {
+ if json.Unmarshal(raw, &pj, jsonx.V1()) != nil {
continue
}
diff --git a/services/verifiedpermissions/schema.go b/services/verifiedpermissions/schema.go
index ce86f7c45b..5fb8f2d4b2 100644
--- a/services/verifiedpermissions/schema.go
+++ b/services/verifiedpermissions/schema.go
@@ -1,18 +1,20 @@
package verifiedpermissions
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"time"
"github.com/blackbirdworks/gopherstack/pkgs/collections"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// extractSchemaNamespaces parses a Cedar JSON schema and returns its top-level namespace keys.
func extractSchemaNamespaces(schemaJSON string) []string {
- var top map[string]json.RawMessage
+ var top map[string]jsontext.Value
- if err := json.Unmarshal([]byte(schemaJSON), &top); err != nil {
+ if err := json.Unmarshal([]byte(schemaJSON), &top, jsonx.V1()); err != nil {
return []string{}
}
@@ -31,7 +33,7 @@ func (b *InMemoryBackend) PutSchema(policyStoreID, schema string) ([]string, err
}
// Validate JSON format.
- if !json.Valid([]byte(schema)) {
+ if !jsontext.Value([]byte(schema)).IsValid(jsonx.V1()) {
return nil, fmt.Errorf("%w: schema is not valid JSON", ErrValidation)
}
diff --git a/services/vpclattice/handler.go b/services/vpclattice/handler.go
index e1fc76a20a..8fff26fd74 100644
--- a/services/vpclattice/handler.go
+++ b/services/vpclattice/handler.go
@@ -1,7 +1,8 @@
package vpclattice
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"net/http"
"strconv"
@@ -13,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -593,7 +595,7 @@ func (h *Handler) handleREST(c *echo.Context) error {
var body map[string]any
if c.Request().ContentLength != 0 {
- if err := json.NewDecoder(c.Request().Body).Decode(&body); err != nil &&
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(c.Request().Body), &body, jsonx.V1()); err != nil &&
err.Error() != "EOF" {
return validationError(c, "invalid JSON body")
}
diff --git a/services/vpclattice/handler_test.go b/services/vpclattice/handler_test.go
index d2224d5337..b7b8d1ac26 100644
--- a/services/vpclattice/handler_test.go
+++ b/services/vpclattice/handler_test.go
@@ -3,7 +3,7 @@ package vpclattice_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/vpclattice"
)
@@ -33,7 +34,7 @@ func doRequestWithRegion(
var buf *bytes.Reader
if body != nil {
- data, err := json.Marshal(body)
+ data, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
buf = bytes.NewReader(data)
} else {
@@ -71,7 +72,7 @@ func doRequest(
var buf *bytes.Reader
if body != nil {
- data, err := json.Marshal(body)
+ data, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
buf = bytes.NewReader(data)
} else {
@@ -95,7 +96,7 @@ func doRequest(
func parseBody(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
diff --git a/services/vpclattice/multi_region_test.go b/services/vpclattice/multi_region_test.go
index b71532f154..5af29fae29 100644
--- a/services/vpclattice/multi_region_test.go
+++ b/services/vpclattice/multi_region_test.go
@@ -2,7 +2,7 @@ package vpclattice_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/vpclattice"
)
@@ -41,7 +42,7 @@ func decode(t *testing.T, body string) map[string]any {
out := map[string]any{}
if body != "" {
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
}
return out
diff --git a/services/vpclattice/persistence.go b/services/vpclattice/persistence.go
index 602637d495..5932ddc433 100644
--- a/services/vpclattice/persistence.go
+++ b/services/vpclattice/persistence.go
@@ -2,7 +2,7 @@ package vpclattice
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -34,7 +34,7 @@ const vpclatticeSnapshotVersion = 1
// incompatible (older or newer) build of this backend as though it were the
// current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Targets map[string][]*storedTarget `json:"targets"`
AuthPolicies map[string]string `json:"authPolicies"`
ResourcePolicies map[string]string `json:"resourcePolicies"`
diff --git a/services/vpclattice/whitebox_test.go b/services/vpclattice/whitebox_test.go
index 89799da154..da03922fbf 100644
--- a/services/vpclattice/whitebox_test.go
+++ b/services/vpclattice/whitebox_test.go
@@ -2,7 +2,7 @@ package vpclattice
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func ruleCount(b *InMemoryBackend) int {
@@ -24,7 +26,7 @@ func doWhiteboxRequest(t *testing.T, h *Handler, method, path string, body any)
var buf *bytes.Reader
if body != nil {
- data, err := json.Marshal(body)
+ data, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
buf = bytes.NewReader(data)
} else {
@@ -49,7 +51,7 @@ func parseWhiteboxBody(t *testing.T, rec *httptest.ResponseRecorder) map[string]
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
diff --git a/services/waf/byte_match_sets_test.go b/services/waf/byte_match_sets_test.go
index 04e8ba472d..060ec8b14a 100644
--- a/services/waf/byte_match_sets_test.go
+++ b/services/waf/byte_match_sets_test.go
@@ -1,13 +1,14 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -23,7 +24,7 @@ func TestWAF_ByteMatchSet_CreateGetUpdateDeleteList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
bmsMap := createResp["ByteMatchSet"].(map[string]any)
id := bmsMap["ByteMatchSetId"].(string)
require.NotEmpty(t, id)
@@ -33,7 +34,7 @@ func TestWAF_ByteMatchSet_CreateGetUpdateDeleteList(t *testing.T) {
rec = wafDo(t, h, "GetByteMatchSet", map[string]any{"ByteMatchSetId": id})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
bms := resp["ByteMatchSet"].(map[string]any)
assert.Equal(t, "my-bms", bms["Name"])
@@ -57,7 +58,7 @@ func TestWAF_ByteMatchSet_CreateGetUpdateDeleteList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
rec = wafDo(t, h, "GetByteMatchSet", map[string]any{"ByteMatchSetId": id})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
bms = resp["ByteMatchSet"].(map[string]any)
tuples := bms["ByteMatchTuples"].([]any)
require.Len(t, tuples, 1)
@@ -69,7 +70,7 @@ func TestWAF_ByteMatchSet_CreateGetUpdateDeleteList(t *testing.T) {
rec = wafDo(t, h, "ListByteMatchSets", map[string]any{"Limit": 100})
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
sets := listResp["ByteMatchSets"].([]any)
assert.Len(t, sets, 1)
@@ -118,7 +119,7 @@ func wafCreateByteMatchSet(t *testing.T, h *waf.Handler, name string) string {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
m := resp["ByteMatchSet"].(map[string]any)
id := m["ByteMatchSetId"].(string)
require.NotEmpty(t, id)
diff --git a/services/waf/change_token_test.go b/services/waf/change_token_test.go
index 96885f93a1..1a60e19932 100644
--- a/services/waf/change_token_test.go
+++ b/services/waf/change_token_test.go
@@ -13,13 +13,14 @@ package waf_test
// itself consume any other token.
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -73,7 +74,7 @@ func TestWAF_GetChangeTokenStatus_Fresh(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "PROVISIONED", resp["ChangeTokenStatus"])
}
@@ -97,7 +98,7 @@ func TestWAF_GetChangeTokenStatus_AfterMutation_INSYNC(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "INSYNC", resp["ChangeTokenStatus"])
}
@@ -202,7 +203,7 @@ func TestChangeTokenStatus_Lifecycle(t *testing.T) {
rec := wafDo(t, h, "GetChangeTokenStatus", map[string]any{"ChangeToken": token})
require.Equal(t, http.StatusOK, rec.Code)
var before map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &before))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &before, jsonx.V1()))
assert.Equal(t, tt.wantBefore, before["ChangeTokenStatus"], "status before mutation")
if tt.mutate != nil {
@@ -213,7 +214,7 @@ func TestChangeTokenStatus_Lifecycle(t *testing.T) {
rec = wafDo(t, h, "GetChangeTokenStatus", map[string]any{"ChangeToken": token})
require.Equal(t, http.StatusOK, rec.Code)
var after map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &after))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &after, jsonx.V1()))
assert.Equal(t, tt.wantAfter, after["ChangeTokenStatus"], "status after mutation")
})
}
@@ -229,7 +230,7 @@ func TestChangeTokenStatus_UnknownReturnsINSYNC(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "INSYNC", resp["ChangeTokenStatus"], "unknown token should return INSYNC per AWS behavior")
}
@@ -299,7 +300,7 @@ func TestChangeToken_AlreadyConsumedTokenRejected(t *testing.T) {
require.Equal(t, http.StatusOK, statusRec.Code)
var statusResp map[string]any
- require.NoError(t, json.Unmarshal(statusRec.Body.Bytes(), &statusResp))
+ require.NoError(t, json.Unmarshal(statusRec.Body.Bytes(), &statusResp, jsonx.V1()))
require.Equal(t, "INSYNC", statusResp["ChangeTokenStatus"], "token must be consumed before reuse attempt")
// Reusing the same, now-INSYNC token for a second mutation must fail.
@@ -315,7 +316,7 @@ func TestChangeToken_AlreadyConsumedTokenRejected(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.Len(t, listResp["Rules"], 1, "the rejected second CreateRule must not have mutated state")
}
diff --git a/services/waf/geo_match_sets_test.go b/services/waf/geo_match_sets_test.go
index afce69e173..0b45020079 100644
--- a/services/waf/geo_match_sets_test.go
+++ b/services/waf/geo_match_sets_test.go
@@ -1,13 +1,14 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -23,7 +24,7 @@ func TestWAF_GeoMatchSet_CreateGetUpdateDeleteList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
gmsMap := createResp["GeoMatchSet"].(map[string]any)
id := gmsMap["GeoMatchSetId"].(string)
require.NotEmpty(t, id)
@@ -48,7 +49,7 @@ func TestWAF_GeoMatchSet_CreateGetUpdateDeleteList(t *testing.T) {
rec = wafDo(t, h, "GetGeoMatchSet", map[string]any{"GeoMatchSetId": id})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
gms := resp["GeoMatchSet"].(map[string]any)
constraints := gms["GeoMatchConstraints"].([]any)
require.Len(t, constraints, 1)
@@ -81,7 +82,7 @@ func TestWAF_GeoMatchSet_CreateGetUpdateDeleteList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
rec = wafDo(t, h, "GetGeoMatchSet", map[string]any{"GeoMatchSetId": id})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
gms = resp["GeoMatchSet"].(map[string]any)
constraints = gms["GeoMatchConstraints"].([]any)
require.Len(t, constraints, 1)
@@ -90,7 +91,7 @@ func TestWAF_GeoMatchSet_CreateGetUpdateDeleteList(t *testing.T) {
// List
rec = wafDo(t, h, "ListGeoMatchSets", map[string]any{"Limit": 100})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
sets := listResp["GeoMatchSets"].([]any)
assert.Len(t, sets, 1)
@@ -137,7 +138,7 @@ func wafCreateGeoMatchSet(t *testing.T, h *waf.Handler, name string) string {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
m := resp["GeoMatchSet"].(map[string]any)
id := m["GeoMatchSetId"].(string)
require.NotEmpty(t, id)
diff --git a/services/waf/handler.go b/services/waf/handler.go
index d850c0875f..32706bf544 100644
--- a/services/waf/handler.go
+++ b/services/waf/handler.go
@@ -2,7 +2,7 @@ package waf
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v5"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -112,12 +113,12 @@ func (h *Handler) dispatch(_ context.Context, action string, body []byte) ([]byt
var tokenHolder struct {
ChangeToken string `json:"ChangeToken"`
}
- if jerr := json.Unmarshal(body, &tokenHolder); jerr == nil && tokenHolder.ChangeToken != "" {
+ if jerr := json.Unmarshal(body, &tokenHolder, jsonx.V1()); jerr == nil && tokenHolder.ChangeToken != "" {
h.Backend.MarkChangeTokenUsed(tokenHolder.ChangeToken)
}
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
// paginate returns a window of items starting after nextMarker, limited to limit items,
@@ -282,7 +283,7 @@ func unmarshal(body []byte, v any) error {
return nil
}
- return json.Unmarshal(body, v)
+ return json.Unmarshal(body, v, jsonx.V1())
}
func tagsToMap(tags []Tag) map[string]string {
diff --git a/services/waf/handler_test.go b/services/waf/handler_test.go
index e8302ad73d..9ceab9987b 100644
--- a/services/waf/handler_test.go
+++ b/services/waf/handler_test.go
@@ -7,7 +7,7 @@ package waf_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -16,6 +16,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -33,7 +34,7 @@ func wafDo(t *testing.T, h *waf.Handler, action string, body any) *httptest.Resp
var b []byte
if body != nil {
var err error
- b, err = json.Marshal(body)
+ b, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -56,7 +57,7 @@ func wafGetToken(t *testing.T, h *waf.Handler) string {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
token, ok := resp["ChangeToken"].(string)
require.True(t, ok)
require.NotEmpty(t, token)
@@ -68,7 +69,7 @@ func errType(t *testing.T, body []byte) string {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(body, &resp))
+ require.NoError(t, json.Unmarshal(body, &resp, jsonx.V1()))
typ, _ := resp["__type"].(string)
diff --git a/services/waf/ip_sets_test.go b/services/waf/ip_sets_test.go
index 283d23a7fc..85af15ad3a 100644
--- a/services/waf/ip_sets_test.go
+++ b/services/waf/ip_sets_test.go
@@ -1,13 +1,14 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -22,7 +23,7 @@ func wafCreateIPSet(t *testing.T, h *waf.Handler, name string) string {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
m := resp["IPSet"].(map[string]any)
id := m["IPSetId"].(string)
require.NotEmpty(t, id)
@@ -41,7 +42,7 @@ func TestWAF_IPSet_CreateGetUpdateDeleteList(t *testing.T) {
rec := wafDo(t, h, "GetIPSet", map[string]any{"IPSetId": id})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ipSet := resp["IPSet"].(map[string]any)
assert.Equal(t, "my-ips", ipSet["Name"])
descs := ipSet["IPSetDescriptors"].([]any)
@@ -66,7 +67,7 @@ func TestWAF_IPSet_CreateGetUpdateDeleteList(t *testing.T) {
// Verify
rec = wafDo(t, h, "GetIPSet", map[string]any{"IPSetId": id})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ipSet = resp["IPSet"].(map[string]any)
descs = ipSet["IPSetDescriptors"].([]any)
require.Len(t, descs, 1)
@@ -92,7 +93,7 @@ func TestWAF_IPSet_CreateGetUpdateDeleteList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
rec = wafDo(t, h, "GetIPSet", map[string]any{"IPSetId": id})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ipSet = resp["IPSet"].(map[string]any)
descs = ipSet["IPSetDescriptors"].([]any)
assert.Empty(t, descs)
@@ -101,7 +102,7 @@ func TestWAF_IPSet_CreateGetUpdateDeleteList(t *testing.T) {
rec = wafDo(t, h, "ListIPSets", map[string]any{"Limit": 100})
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
sets := listResp["IPSets"].([]any)
assert.Len(t, sets, 1)
@@ -139,7 +140,7 @@ func TestWAF_IPSet_IPv6Descriptor(t *testing.T) {
rec = wafDo(t, h, "GetIPSet", map[string]any{"IPSetId": id})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ipSet := resp["IPSet"].(map[string]any)
descs := ipSet["IPSetDescriptors"].([]any)
require.Len(t, descs, 1)
@@ -155,7 +156,7 @@ func TestWAF_MultipleIPSets_List(t *testing.T) {
rec := wafDo(t, h, "ListIPSets", map[string]any{"Limit": 100})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sets := resp["IPSets"].([]any)
assert.Len(t, sets, 2)
}
diff --git a/services/waf/logging_configuration_test.go b/services/waf/logging_configuration_test.go
index 6c69cf2221..1f4b4a4f8d 100644
--- a/services/waf/logging_configuration_test.go
+++ b/services/waf/logging_configuration_test.go
@@ -1,12 +1,14 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestLoggingConfigurationLifecycle(t *testing.T) {
@@ -40,14 +42,14 @@ func TestLoggingConfigurationLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &putResp, jsonx.V1()))
assert.NotNil(t, putResp["LoggingConfiguration"])
// Get
rec = wafDo(t, h, "GetLoggingConfiguration", map[string]any{"ResourceArn": aclARN})
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
cfgMap := getResp["LoggingConfiguration"].(map[string]any)
assert.Equal(t, aclARN, cfgMap["ResourceArn"])
@@ -55,7 +57,7 @@ func TestLoggingConfigurationLifecycle(t *testing.T) {
rec = wafDo(t, h, "ListLoggingConfigurations", nil)
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
cfgs := listResp["LoggingConfigurations"].([]any)
assert.Len(t, cfgs, 1)
@@ -70,7 +72,7 @@ func TestLoggingConfigurationLifecycle(t *testing.T) {
// List after delete → empty
rec = wafDo(t, h, "ListLoggingConfigurations", nil)
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
cfgs = listResp["LoggingConfigurations"].([]any)
assert.Empty(t, cfgs)
})
diff --git a/services/waf/non_empty_entity_test.go b/services/waf/non_empty_entity_test.go
index b763303508..b23cfa87fc 100644
--- a/services/waf/non_empty_entity_test.go
+++ b/services/waf/non_empty_entity_test.go
@@ -10,13 +10,14 @@ package waf_test
// delete succeeds.
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -505,7 +506,7 @@ func wafCreateNamedSet(
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
m, ok := resp[respKey].(map[string]any)
require.True(t, ok)
id, ok := m[idKey].(string)
diff --git a/services/waf/pagination_test.go b/services/waf/pagination_test.go
index 2ea15bc145..196b925a97 100644
--- a/services/waf/pagination_test.go
+++ b/services/waf/pagination_test.go
@@ -4,13 +4,14 @@ package waf_test
// every List* operation.
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -21,7 +22,7 @@ func wafListPage(t *testing.T, h *waf.Handler, op string, body map[string]any) m
rec := wafDo(t, h, op, body)
require.Equal(t, http.StatusOK, rec.Code, op+": "+rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
return resp
}
diff --git a/services/waf/permission_policy_test.go b/services/waf/permission_policy_test.go
index 5f3886b52d..d4c6c5030e 100644
--- a/services/waf/permission_policy_test.go
+++ b/services/waf/permission_policy_test.go
@@ -1,12 +1,14 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestPermissionPolicyLifecycle(t *testing.T) {
@@ -40,7 +42,7 @@ func TestPermissionPolicyLifecycle(t *testing.T) {
rec = wafDo(t, h, "GetPermissionPolicy", map[string]any{"ResourceArn": rgARN})
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
assert.Equal(t, policy, getResp["Policy"])
// Delete
diff --git a/services/waf/persistence.go b/services/waf/persistence.go
index f0cf3fb6b2..e164d896d8 100644
--- a/services/waf/persistence.go
+++ b/services/waf/persistence.go
@@ -2,7 +2,7 @@ package waf
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -33,7 +33,7 @@ const wafSnapshotVersion = 1
// plain maps -- see store_setup.go's file doc comment for why they do not fit
// store.Table's keyed-by-identity-value shape.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
ChangeTokens map[string]string `json:"changeTokens"`
RuleGroupRules map[string][]ActivatedRule `json:"ruleGroupRules"`
PermissionPolicies map[string]string `json:"permissionPolicies"`
diff --git a/services/waf/rate_based_rules_test.go b/services/waf/rate_based_rules_test.go
index 9e690eefdb..98835f1be5 100644
--- a/services/waf/rate_based_rules_test.go
+++ b/services/waf/rate_based_rules_test.go
@@ -1,7 +1,7 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -26,7 +27,7 @@ func wafCreateRateBasedRule(t *testing.T, h *waf.Handler, name string) string {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ruleMap, ok := resp["Rule"].(map[string]any)
require.True(t, ok)
id, ok := ruleMap["RuleId"].(string)
@@ -60,7 +61,7 @@ func TestRateBasedRuleLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
ruleMap := getResp["Rule"].(map[string]any)
assert.Equal(t, "BlockBots", ruleMap["Name"])
assert.Equal(t, "IP", ruleMap["RateKey"])
@@ -88,7 +89,7 @@ func TestRateBasedRuleLifecycle(t *testing.T) {
// Get to verify updates
rec = wafDo(t, h, "GetRateBasedRule", map[string]any{"RuleId": ruleID})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
ruleMap = getResp["Rule"].(map[string]any)
assert.InEpsilon(t, float64(3000), ruleMap["RateLimit"], 0.001)
preds := ruleMap["MatchPredicates"].([]any)
@@ -117,7 +118,7 @@ func TestRateBasedRuleLifecycle(t *testing.T) {
rec = wafDo(t, h, "ListRateBasedRules", nil)
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
rules := listResp["Rules"].([]any)
assert.Len(t, rules, 1)
@@ -125,7 +126,7 @@ func TestRateBasedRuleLifecycle(t *testing.T) {
rec = wafDo(t, h, "GetRateBasedRuleManagedKeys", map[string]any{"RuleId": ruleID})
require.Equal(t, http.StatusOK, rec.Code)
var mkResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mkResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &mkResp, jsonx.V1()))
assert.NotNil(t, mkResp["ManagedKeys"])
// Delete
diff --git a/services/waf/realclient_match_sets_and_rules_test.go b/services/waf/realclient_match_sets_and_rules_test.go
index 7c5ff5c48a..100f1a94ee 100644
--- a/services/waf/realclient_match_sets_and_rules_test.go
+++ b/services/waf/realclient_match_sets_and_rules_test.go
@@ -1,7 +1,7 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -942,7 +943,7 @@ func testPermissionPolicyRealClient(t *testing.T) {
},
}
- policyBytes, err := json.Marshal(policyStatement)
+ policyBytes, err := json.Marshal(policyStatement, jsonx.V1())
require.NoError(t, err)
policy := string(policyBytes)
diff --git a/services/waf/regex_match_sets_test.go b/services/waf/regex_match_sets_test.go
index c90aef1887..3f57ac03ba 100644
--- a/services/waf/regex_match_sets_test.go
+++ b/services/waf/regex_match_sets_test.go
@@ -1,13 +1,14 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -22,7 +23,7 @@ func wafCreateRegexMatchSet(t *testing.T, h *waf.Handler, name string) string {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
setMap, ok := resp["RegexMatchSet"].(map[string]any)
require.True(t, ok)
id, ok := setMap["RegexMatchSetId"].(string)
@@ -57,7 +58,7 @@ func TestRegexMatchSetLifecycle(t *testing.T) {
rec := wafDo(t, h, "GetRegexMatchSet", map[string]any{"RegexMatchSetId": setID})
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
setMap := getResp["RegexMatchSet"].(map[string]any)
assert.Equal(t, "SqlMatchSet", setMap["Name"])
@@ -80,7 +81,7 @@ func TestRegexMatchSetLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
rec = wafDo(t, h, "GetRegexMatchSet", map[string]any{"RegexMatchSetId": setID})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
setMap = getResp["RegexMatchSet"].(map[string]any)
tuples := setMap["RegexMatchTuples"].([]any)
assert.Len(t, tuples, 1)
@@ -107,7 +108,7 @@ func TestRegexMatchSetLifecycle(t *testing.T) {
rec = wafDo(t, h, "ListRegexMatchSets", nil)
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
sets := listResp["RegexMatchSets"].([]any)
assert.Len(t, sets, 1)
diff --git a/services/waf/regex_pattern_sets_test.go b/services/waf/regex_pattern_sets_test.go
index e9a4133096..330dec962d 100644
--- a/services/waf/regex_pattern_sets_test.go
+++ b/services/waf/regex_pattern_sets_test.go
@@ -1,13 +1,14 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -22,7 +23,7 @@ func wafCreateRegexPatternSet(t *testing.T, h *waf.Handler, name string) string
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
setMap, ok := resp["RegexPatternSet"].(map[string]any)
require.True(t, ok)
id, ok := setMap["RegexPatternSetId"].(string)
@@ -55,7 +56,7 @@ func TestRegexPatternSetLifecycle(t *testing.T) {
rec := wafDo(t, h, "GetRegexPatternSet", map[string]any{"RegexPatternSetId": setID})
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
setMap := getResp["RegexPatternSet"].(map[string]any)
assert.Equal(t, "SqlPatterns", setMap["Name"])
assert.Empty(t, setMap["RegexPatternStrings"])
@@ -73,7 +74,7 @@ func TestRegexPatternSetLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
rec = wafDo(t, h, "GetRegexPatternSet", map[string]any{"RegexPatternSetId": setID})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
setMap = getResp["RegexPatternSet"].(map[string]any)
assert.Len(t, setMap["RegexPatternStrings"], 2)
@@ -89,7 +90,7 @@ func TestRegexPatternSetLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
rec = wafDo(t, h, "GetRegexPatternSet", map[string]any{"RegexPatternSetId": setID})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
setMap = getResp["RegexPatternSet"].(map[string]any)
assert.Len(t, setMap["RegexPatternStrings"], 1)
@@ -97,7 +98,7 @@ func TestRegexPatternSetLifecycle(t *testing.T) {
rec = wafDo(t, h, "ListRegexPatternSets", nil)
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
sets := listResp["RegexPatternSets"].([]any)
assert.Len(t, sets, 1)
diff --git a/services/waf/request_validation_test.go b/services/waf/request_validation_test.go
index edb9b1b6a8..64b0684734 100644
--- a/services/waf/request_validation_test.go
+++ b/services/waf/request_validation_test.go
@@ -1,13 +1,15 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_RequestValidation(t *testing.T) {
@@ -128,7 +130,7 @@ func TestHandler_RequestValidation(t *testing.T) {
Type string `json:"__type"`
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, tt.wantCode, out.Type)
assert.Contains(t, out.Message, tt.wantMsg)
assert.False(t, strings.HasPrefix(out.Message, tt.wantCode), "message repeats code: %q", out.Message)
@@ -152,7 +154,7 @@ func TestHandler_StaleTokenMessage(t *testing.T) {
Type string `json:"__type"`
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(second.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(second.Body.Bytes(), &out, jsonx.V1()))
assert.Equal(t, "WAFStaleDataException", out.Type)
assert.Equal(t, "The input token is no longer current.", out.Message)
}
diff --git a/services/waf/rule_groups_test.go b/services/waf/rule_groups_test.go
index ed96f059b1..d95b117381 100644
--- a/services/waf/rule_groups_test.go
+++ b/services/waf/rule_groups_test.go
@@ -1,7 +1,7 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -24,7 +25,7 @@ func wafCreateRuleGroup(t *testing.T, h *waf.Handler, name string) string {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rgMap, ok := resp["RuleGroup"].(map[string]any)
require.True(t, ok)
id, ok := rgMap["RuleGroupId"].(string)
@@ -60,7 +61,7 @@ func TestRuleGroupLifecycle(t *testing.T) {
rec := wafDo(t, h, "GetRuleGroup", map[string]any{"RuleGroupId": rgID})
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
rgMap := getResp["RuleGroup"].(map[string]any)
assert.Equal(t, "MyGroup", rgMap["Name"])
@@ -89,7 +90,7 @@ func TestRuleGroupLifecycle(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
var listARResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listARResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listARResp, jsonx.V1()))
activated := listARResp["ActivatedRules"].([]any)
assert.Len(t, activated, 1)
@@ -116,7 +117,7 @@ func TestRuleGroupLifecycle(t *testing.T) {
rec = wafDo(t, h, "ListRuleGroups", nil)
require.Equal(t, http.StatusOK, rec.Code)
var listRGResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listRGResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listRGResp, jsonx.V1()))
groups := listRGResp["RuleGroups"].([]any)
assert.Len(t, groups, 1)
@@ -216,7 +217,7 @@ func TestUpdateRuleGroup_RejectsDuplicateRuleId(t *testing.T) {
rec = wafDo(t, h, "ListActivatedRulesInRuleGroup", map[string]any{"RuleGroupId": rgID})
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
activated, _ := listResp["ActivatedRules"].([]any)
assert.Len(t, activated, 1, "group must still contain only the one successfully-inserted rule")
}
@@ -291,7 +292,7 @@ func TestListSubscribedRuleGroups(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups := resp["RuleGroups"].([]any)
assert.Empty(t, groups)
})
diff --git a/services/waf/rules_test.go b/services/waf/rules_test.go
index 24019ba617..e379b25e95 100644
--- a/services/waf/rules_test.go
+++ b/services/waf/rules_test.go
@@ -1,7 +1,7 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -24,7 +25,7 @@ func wafCreateRule(t *testing.T, h *waf.Handler, name string) string {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ruleMap := resp["Rule"].(map[string]any)
id := ruleMap["RuleId"].(string)
require.NotEmpty(t, id)
@@ -45,7 +46,7 @@ func TestWAF_Rule_CreateGetDeleteList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rule := resp["Rule"].(map[string]any)
assert.Equal(t, "my-rule", rule["Name"])
assert.Equal(t, id, rule["RuleId"])
@@ -55,7 +56,7 @@ func TestWAF_Rule_CreateGetDeleteList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
rules := listResp["Rules"].([]any)
assert.Len(t, rules, 1)
@@ -97,7 +98,7 @@ func TestWAF_Rule_UpdatePredicates_InsertDelete(t *testing.T) {
// Verify predicate
rec = wafDo(t, h, "GetRule", map[string]any{"RuleId": id})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rule := resp["Rule"].(map[string]any)
predicates := rule["Predicates"].([]any)
require.Len(t, predicates, 1)
@@ -124,7 +125,7 @@ func TestWAF_Rule_UpdatePredicates_InsertDelete(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
rec = wafDo(t, h, "GetRule", map[string]any{"RuleId": id})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rule = resp["Rule"].(map[string]any)
predicates = rule["Predicates"].([]any)
assert.Empty(t, predicates)
diff --git a/services/waf/sampled_requests_test.go b/services/waf/sampled_requests_test.go
index 6909be80ea..970159aea7 100644
--- a/services/waf/sampled_requests_test.go
+++ b/services/waf/sampled_requests_test.go
@@ -1,12 +1,14 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestWAF_GetSampledRequests_EmptyStub(t *testing.T) {
@@ -31,7 +33,7 @@ func TestWAF_GetSampledRequests_EmptyStub(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
samples := resp["SampledRequests"].([]any)
assert.Empty(t, samples)
assert.EqualValues(t, 0, resp["PopulationSize"])
@@ -105,7 +107,7 @@ func TestGetSampledRequests_ReturnsTimeWindow(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tw, ok := resp["TimeWindow"].(map[string]any)
require.True(t, ok, "TimeWindow must be present in response")
diff --git a/services/waf/size_constraint_sets_test.go b/services/waf/size_constraint_sets_test.go
index cee0604e52..acc2c4dd60 100644
--- a/services/waf/size_constraint_sets_test.go
+++ b/services/waf/size_constraint_sets_test.go
@@ -1,13 +1,14 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -23,7 +24,7 @@ func TestWAF_SizeConstraintSet_CreateGetUpdateDeleteList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
scsMap := createResp["SizeConstraintSet"].(map[string]any)
id := scsMap["SizeConstraintSetId"].(string)
require.NotEmpty(t, id)
@@ -50,7 +51,7 @@ func TestWAF_SizeConstraintSet_CreateGetUpdateDeleteList(t *testing.T) {
rec = wafDo(t, h, "GetSizeConstraintSet", map[string]any{"SizeConstraintSetId": id})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
scs := resp["SizeConstraintSet"].(map[string]any)
constraints := scs["SizeConstraints"].([]any)
require.Len(t, constraints, 1)
@@ -61,7 +62,7 @@ func TestWAF_SizeConstraintSet_CreateGetUpdateDeleteList(t *testing.T) {
// List
rec = wafDo(t, h, "ListSizeConstraintSets", map[string]any{"Limit": 100})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
sets := listResp["SizeConstraintSets"].([]any)
assert.Len(t, sets, 1)
@@ -110,7 +111,7 @@ func wafCreateSizeConstraintSet(t *testing.T, h *waf.Handler, name string) strin
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
m := resp["SizeConstraintSet"].(map[string]any)
id := m["SizeConstraintSetId"].(string)
require.NotEmpty(t, id)
diff --git a/services/waf/sql_injection_match_sets_test.go b/services/waf/sql_injection_match_sets_test.go
index 8a0c876b3d..d4e7eaf1ba 100644
--- a/services/waf/sql_injection_match_sets_test.go
+++ b/services/waf/sql_injection_match_sets_test.go
@@ -1,13 +1,14 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -23,7 +24,7 @@ func TestWAF_SqlInjectionMatchSet_CreateGetUpdateDeleteList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
simsMap := createResp["SqlInjectionMatchSet"].(map[string]any)
id := simsMap["SqlInjectionMatchSetId"].(string)
require.NotEmpty(t, id)
@@ -48,7 +49,7 @@ func TestWAF_SqlInjectionMatchSet_CreateGetUpdateDeleteList(t *testing.T) {
rec = wafDo(t, h, "GetSqlInjectionMatchSet", map[string]any{"SqlInjectionMatchSetId": id})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
sims := resp["SqlInjectionMatchSet"].(map[string]any)
tuples := sims["SqlInjectionMatchTuples"].([]any)
require.Len(t, tuples, 1)
@@ -60,7 +61,7 @@ func TestWAF_SqlInjectionMatchSet_CreateGetUpdateDeleteList(t *testing.T) {
// List
rec = wafDo(t, h, "ListSqlInjectionMatchSets", map[string]any{"Limit": 100})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
sets := listResp["SqlInjectionMatchSets"].([]any)
assert.Len(t, sets, 1)
@@ -107,7 +108,7 @@ func wafCreateSQLInjectionMatchSet(t *testing.T, h *waf.Handler, name string) st
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
m := resp["SqlInjectionMatchSet"].(map[string]any)
id := m["SqlInjectionMatchSetId"].(string)
require.NotEmpty(t, id)
diff --git a/services/waf/tag_leak_test.go b/services/waf/tag_leak_test.go
index 4c19d76ccb..0de085f93e 100644
--- a/services/waf/tag_leak_test.go
+++ b/services/waf/tag_leak_test.go
@@ -1,7 +1,7 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -223,7 +224,7 @@ func TestWAF_Delete_ClearsTags(t *testing.T) {
rec := wafDo(t, h, "GetWebACL", map[string]any{"WebACLId": id})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
resourceARN, ok := resp["WebACL"].(map[string]any)["WebACLArn"].(string)
require.True(t, ok)
@@ -257,7 +258,7 @@ func TestWAF_Delete_ClearsTags(t *testing.T) {
rec := wafDo(t, h, "ListTagsForResource", map[string]any{"ResourceARN": resourceARN})
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
require.NotEmpty(t, listResp["TagInfoForResource"].(map[string]any)["TagList"])
token := wafGetToken(t, h)
@@ -277,7 +278,7 @@ func TestWAF_Delete_ClearsTags(t *testing.T) {
// Deleting one resource must not disturb another's tags.
rec = wafDo(t, h, "ListTagsForResource", map[string]any{"ResourceARN": otherARN})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
assert.NotEmpty(t, listResp["TagInfoForResource"].(map[string]any)["TagList"])
})
}
diff --git a/services/waf/tags_test.go b/services/waf/tags_test.go
index a2179ba1c6..ecd07e29be 100644
--- a/services/waf/tags_test.go
+++ b/services/waf/tags_test.go
@@ -1,12 +1,14 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestWAF_Tags_TagAndList(t *testing.T) {
@@ -19,7 +21,7 @@ func TestWAF_Tags_TagAndList(t *testing.T) {
rec := wafDo(t, h, "GetWebACL", map[string]any{"WebACLId": aclID})
require.Equal(t, http.StatusOK, rec.Code)
var aclResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &aclResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &aclResp, jsonx.V1()))
arn := aclResp["WebACL"].(map[string]any)["WebACLArn"].(string)
require.NotEmpty(t, arn)
@@ -37,7 +39,7 @@ func TestWAF_Tags_TagAndList(t *testing.T) {
rec = wafDo(t, h, "ListTagsForResource", map[string]any{"ResourceARN": arn})
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
info := listResp["TagInfoForResource"].(map[string]any)
assert.Equal(t, arn, info["ResourceARN"])
tagList := info["TagList"].([]any)
@@ -62,7 +64,7 @@ func TestWAF_Tags_UntagResource(t *testing.T) {
rec := wafDo(t, h, "GetWebACL", map[string]any{"WebACLId": aclID})
var aclResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &aclResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &aclResp, jsonx.V1()))
arn := aclResp["WebACL"].(map[string]any)["WebACLArn"].(string)
// Tag
@@ -85,7 +87,7 @@ func TestWAF_Tags_UntagResource(t *testing.T) {
// Verify only k2 remains
rec = wafDo(t, h, "ListTagsForResource", map[string]any{"ResourceARN": arn})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tagList := listResp["TagInfoForResource"].(map[string]any)["TagList"].([]any)
require.Len(t, tagList, 1)
assert.Equal(t, "k2", tagList[0].(map[string]any)["Key"])
@@ -108,12 +110,12 @@ func TestWAF_Tags_CreateWebACLWithTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
arn := resp["WebACL"].(map[string]any)["WebACLArn"].(string)
rec = wafDo(t, h, "ListTagsForResource", map[string]any{"ResourceARN": arn})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
tagList := listResp["TagInfoForResource"].(map[string]any)["TagList"].([]any)
require.Len(t, tagList, 1)
assert.Equal(t, "cost-center", tagList[0].(map[string]any)["Key"])
diff --git a/services/waf/web_acls_test.go b/services/waf/web_acls_test.go
index 3e269f8dfc..b60f5ee818 100644
--- a/services/waf/web_acls_test.go
+++ b/services/waf/web_acls_test.go
@@ -1,7 +1,7 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -25,7 +26,7 @@ func wafCreateWebACL(t *testing.T, h *waf.Handler, name string) string {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
aclMap, ok := resp["WebACL"].(map[string]any)
require.True(t, ok)
id, ok := aclMap["WebACLId"].(string)
@@ -49,7 +50,7 @@ func TestWAF_WebACL_CreateGetDeleteList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
acl := getResp["WebACL"].(map[string]any)
assert.Equal(t, "my-acl", acl["Name"])
assert.Equal(t, id, acl["WebACLId"])
@@ -64,7 +65,7 @@ func TestWAF_WebACL_CreateGetDeleteList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
acls := listResp["WebACLs"].([]any)
assert.Len(t, acls, 1)
@@ -92,7 +93,7 @@ func TestWAF_WebACL_DefaultAction_BLOCK(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
acl := resp["WebACL"].(map[string]any)
da := acl["DefaultAction"].(map[string]any)
assert.Equal(t, "BLOCK", da["Type"])
@@ -114,7 +115,7 @@ func TestWAF_WebACL_UpdateDefaultAction(t *testing.T) {
rec = wafDo(t, h, "GetWebACL", map[string]any{"WebACLId": id})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
acl := resp["WebACL"].(map[string]any)
da := acl["DefaultAction"].(map[string]any)
assert.Equal(t, "BLOCK", da["Type"])
@@ -148,7 +149,7 @@ func TestWAF_WebACL_UpdateRules_InsertAndDelete(t *testing.T) {
// Verify rule present
rec = wafDo(t, h, "GetWebACL", map[string]any{"WebACLId": aclID})
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
acl := getResp["WebACL"].(map[string]any)
rules := acl["Rules"].([]any)
require.Len(t, rules, 1)
@@ -175,7 +176,7 @@ func TestWAF_WebACL_UpdateRules_InsertAndDelete(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
rec = wafDo(t, h, "GetWebACL", map[string]any{"WebACLId": aclID})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
acl = getResp["WebACL"].(map[string]any)
rules = acl["Rules"].([]any)
assert.Empty(t, rules)
@@ -263,7 +264,7 @@ func TestWAF_WebACL_RulesPrioritySorted(t *testing.T) {
rec = wafDo(t, h, "GetWebACL", map[string]any{"WebACLId": aclID})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rules := resp["WebACL"].(map[string]any)["Rules"].([]any)
require.Len(t, rules, 2)
assert.EqualValues(t, 1, rules[0].(map[string]any)["Priority"])
@@ -284,7 +285,7 @@ func TestWAF_WebACL_ChangeTokenRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, token, resp["ChangeToken"])
}
@@ -299,7 +300,7 @@ func TestWAF_MultipleWebACLs_List(t *testing.T) {
rec := wafDo(t, h, "ListWebACLs", map[string]any{"Limit": 100})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
acls := resp["WebACLs"].([]any)
assert.Len(t, acls, 3)
}
@@ -369,7 +370,7 @@ func TestCreateWebACLMigrationStack(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
s3URL, ok := resp["S3ObjectUrl"].(string)
require.True(t, ok)
assert.Contains(t, s3URL, "my-migration-bucket")
diff --git a/services/waf/xss_match_sets_test.go b/services/waf/xss_match_sets_test.go
index 77e9177725..e5feca34f1 100644
--- a/services/waf/xss_match_sets_test.go
+++ b/services/waf/xss_match_sets_test.go
@@ -1,13 +1,14 @@
package waf_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/waf"
)
@@ -23,7 +24,7 @@ func TestWAF_XssMatchSet_CreateGetUpdateDeleteList(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
xmsMap := createResp["XssMatchSet"].(map[string]any)
id := xmsMap["XssMatchSetId"].(string)
require.NotEmpty(t, id)
@@ -48,7 +49,7 @@ func TestWAF_XssMatchSet_CreateGetUpdateDeleteList(t *testing.T) {
rec = wafDo(t, h, "GetXssMatchSet", map[string]any{"XssMatchSetId": id})
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
xms := resp["XssMatchSet"].(map[string]any)
tuples := xms["XssMatchTuples"].([]any)
require.Len(t, tuples, 1)
@@ -57,7 +58,7 @@ func TestWAF_XssMatchSet_CreateGetUpdateDeleteList(t *testing.T) {
// List
rec = wafDo(t, h, "ListXssMatchSets", map[string]any{"Limit": 100})
var listResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &listResp, jsonx.V1()))
sets := listResp["XssMatchSets"].([]any)
assert.Len(t, sets, 1)
@@ -104,7 +105,7 @@ func wafCreateXSSMatchSet(t *testing.T, h *waf.Handler, name string) string {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
m := resp["XssMatchSet"].(map[string]any)
id := m["XssMatchSetId"].(string)
require.NotEmpty(t, id)
diff --git a/services/wafv2/capacity_test.go b/services/wafv2/capacity_test.go
index cf932b4901..19896f15d0 100644
--- a/services/wafv2/capacity_test.go
+++ b/services/wafv2/capacity_test.go
@@ -1,13 +1,14 @@
package wafv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/wafv2"
)
@@ -29,7 +30,7 @@ func checkCapacityFor(t *testing.T, h *wafv2.Handler, stmt map[string]any) int {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
consumed, ok := result["Capacity"].(float64)
require.True(t, ok)
@@ -358,7 +359,7 @@ func TestCheckCapacity_MultipleRulesSum(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
consumed, ok := result["Capacity"].(float64)
require.True(t, ok)
assert.InDelta(t, float64(1+40), consumed, 0)
diff --git a/services/wafv2/export_test.go b/services/wafv2/export_test.go
index 03a9a6478a..b857580317 100644
--- a/services/wafv2/export_test.go
+++ b/services/wafv2/export_test.go
@@ -2,7 +2,7 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
)
// CreateWebACLSimple is a test helper that creates a WebACL with minimal parameters,
@@ -13,11 +13,11 @@ func CreateWebACLSimple(
name, scope, description, defaultAction string,
tags map[string]string,
) (*WebACL, error) {
- var da json.RawMessage
+ var da jsontext.Value
if defaultAction == "BLOCK" {
- da = json.RawMessage(`{"Block":{}}`)
+ da = jsontext.Value(`{"Block":{}}`)
} else {
- da = json.RawMessage(`{"Allow":{}}`)
+ da = jsontext.Value(`{"Allow":{}}`)
}
return b.CreateWebACL(
diff --git a/services/wafv2/handler.go b/services/wafv2/handler.go
index 05cebab83f..91951b8eff 100644
--- a/services/wafv2/handler.go
+++ b/services/wafv2/handler.go
@@ -3,7 +3,8 @@ package wafv2
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"maps"
@@ -14,6 +15,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -279,9 +281,6 @@ func (h *Handler) dispatch(ctx context.Context, op string, body []byte) ([]byte,
return fn(ctx, body)
}
func (h *Handler) handleError(c *echo.Context, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
var errType string
var statusCode int
@@ -310,7 +309,7 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
case errors.Is(err, ErrConfigurationWarning):
errType = "WAFConfigurationWarningException"
statusCode = http.StatusBadRequest
- case errors.Is(err, errInvalidRequest), errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ case errors.Is(err, errInvalidRequest), jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
errType = "WAFInvalidParameterException"
statusCode = http.StatusBadRequest
case errors.Is(err, errUnknownAction):
@@ -324,7 +323,7 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
payload, _ := json.Marshal(map[string]string{
keyTypeField: errType,
keyMessageField: publicMessage(err),
- })
+ }, jsonx.V1())
c.Response().Header().Set("X-Amzn-Errortype", errType)
@@ -332,10 +331,10 @@ func (h *Handler) handleError(c *echo.Context, err error) error {
}
// parseVisibilityConfig parses a stored VisibilityConfig JSON string or returns a default.
-func parseVisibilityConfig(stored json.RawMessage, metricName string) map[string]any {
+func parseVisibilityConfig(stored jsontext.Value, metricName string) map[string]any {
if len(stored) > 0 {
var m map[string]any
- if err := json.Unmarshal(stored, &m); err == nil {
+ if err := json.Unmarshal(stored, &m, jsonx.V1()); err == nil {
return m
}
}
@@ -514,7 +513,7 @@ func handleListResourceFamily[T any](
buildSummary func(T) map[string]string,
) ([]byte, error) {
var req listFamilyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -531,5 +530,5 @@ func handleListResourceFamily[T any](
resp["NextMarker"] = nextMarker
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
diff --git a/services/wafv2/handler_api_keys.go b/services/wafv2/handler_api_keys.go
index 7c474db3c5..55a506e7e7 100644
--- a/services/wafv2/handler_api_keys.go
+++ b/services/wafv2/handler_api_keys.go
@@ -3,9 +3,10 @@ package wafv2
import (
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -17,7 +18,7 @@ type createAPIKeyRequest struct {
func (h *Handler) handleCreateAPIKey(ctx context.Context, body []byte) ([]byte, error) {
var req createAPIKeyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -46,7 +47,7 @@ func (h *Handler) handleCreateAPIKey(ctx context.Context, body []byte) ([]byte,
return json.Marshal(map[string]any{
"APIKey": encodedKey,
- })
+ }, jsonx.V1())
}
// deleteAPIKeyRequest is the request body for DeleteAPIKey.
@@ -57,7 +58,7 @@ type deleteAPIKeyRequest struct {
func (h *Handler) handleDeleteAPIKey(ctx context.Context, body []byte) ([]byte, error) {
var req deleteAPIKeyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -94,7 +95,7 @@ type listAPIKeysRequest struct {
func (h *Handler) handleListAPIKeys(ctx context.Context, body []byte) ([]byte, error) {
var req listAPIKeysRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -137,7 +138,7 @@ func (h *Handler) handleListAPIKeys(ctx context.Context, body []byte) ([]byte, e
resp["NextMarker"] = nextMarker
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// getDecryptedAPIKeyRequest is the request body for GetDecryptedAPIKey.
@@ -148,7 +149,7 @@ type getDecryptedAPIKeyRequest struct {
func (h *Handler) handleGetDecryptedAPIKey(ctx context.Context, body []byte) ([]byte, error) {
var req getDecryptedAPIKeyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -176,7 +177,7 @@ func (h *Handler) handleGetDecryptedAPIKey(ctx context.Context, body []byte) ([]
return json.Marshal(map[string]any{
"TokenDomains": a.TokenDomains,
"CreationTimestamp": a.CreatedAt,
- })
+ }, jsonx.V1())
}
// apiKeyDispatchOps returns the API-key-family operation dispatch entries. Each entry is a
diff --git a/services/wafv2/handler_api_keys_test.go b/services/wafv2/handler_api_keys_test.go
index aefcf246f9..e516f90cc7 100644
--- a/services/wafv2/handler_api_keys_test.go
+++ b/services/wafv2/handler_api_keys_test.go
@@ -2,7 +2,7 @@ package wafv2_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/wafv2/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_CreateAndDeleteAPIKey(t *testing.T) {
@@ -79,7 +81,7 @@ func TestHandler_CreateAndDeleteAPIKey(t *testing.T) {
}
var createResult map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResult))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResult, jsonx.V1()))
apiKey, _ := createResult["APIKey"].(string)
deleteRec := doWafv2Request(t, h, "DeleteAPIKey", tt.deleteBody(apiKey))
@@ -100,7 +102,7 @@ func TestAPIKeyBase64Encoding(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
apiKey, ok := resp["APIKey"].(string)
require.True(t, ok)
@@ -249,7 +251,7 @@ func TestHandler_GetDecryptedAPIKey(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
apiKey, _ = resp["APIKey"].(string)
require.NotEmpty(t, apiKey)
}
@@ -267,7 +269,7 @@ func TestHandler_GetDecryptedAPIKey(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotContains(t, resp, "Scope", "GetDecryptedAPIKeyOutput has no Scope member")
assert.Contains(t, resp, "TokenDomains")
}
diff --git a/services/wafv2/handler_ip_sets.go b/services/wafv2/handler_ip_sets.go
index aad5ce9f51..555628eacc 100644
--- a/services/wafv2/handler_ip_sets.go
+++ b/services/wafv2/handler_ip_sets.go
@@ -2,9 +2,10 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -21,7 +22,7 @@ type createIPSetRequest struct {
func (h *Handler) handleCreateIPSet(ctx context.Context, body []byte) ([]byte, error) {
var req createIPSetRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -88,7 +89,7 @@ func (h *Handler) handleCreateIPSet(ctx context.Context, body []byte) ([]byte, e
keyLockToken: s.LockToken,
keyDescription: s.Description,
},
- })
+ }, jsonx.V1())
}
// getIPSetRequest is the request body for GetIPSet.
@@ -100,7 +101,7 @@ type getIPSetRequest struct {
func (h *Handler) handleGetIPSet(ctx context.Context, body []byte) ([]byte, error) {
var req getIPSetRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -134,7 +135,7 @@ func (h *Handler) handleGetIPSet(ctx context.Context, body []byte) ([]byte, erro
keyAddresses: s.Addresses,
},
keyLockToken: s.LockToken,
- })
+ }, jsonx.V1())
}
// updateIPSetRequest is the request body for UpdateIPSet.
@@ -149,7 +150,7 @@ type updateIPSetRequest struct {
func (h *Handler) handleUpdateIPSet(ctx context.Context, body []byte) ([]byte, error) {
var req updateIPSetRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -191,7 +192,7 @@ func (h *Handler) handleUpdateIPSet(ctx context.Context, body []byte) ([]byte, e
return json.Marshal(map[string]string{
keyNextLockToken: s.LockToken,
- })
+ }, jsonx.V1())
}
// deleteIPSetRequest is the request body for DeleteIPSet.
@@ -204,7 +205,7 @@ type deleteIPSetRequest struct {
func (h *Handler) handleDeleteIPSet(ctx context.Context, body []byte) ([]byte, error) {
var req deleteIPSetRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/wafv2/handler_ip_sets_test.go b/services/wafv2/handler_ip_sets_test.go
index f2c4caee6f..a271d4aa62 100644
--- a/services/wafv2/handler_ip_sets_test.go
+++ b/services/wafv2/handler_ip_sets_test.go
@@ -2,7 +2,7 @@ package wafv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/wafv2"
)
@@ -54,7 +55,7 @@ func TestHandler_CreateIPSet(t *testing.T) {
if tt.wantID {
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
summary, ok := result["Summary"].(map[string]any)
require.True(t, ok)
assert.NotEmpty(t, summary["Id"])
@@ -164,7 +165,7 @@ func TestHandler_UpdateIPSet(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var result map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
assert.NotEmpty(t, result["NextLockToken"])
}
})
@@ -250,7 +251,7 @@ func TestHandler_ListIPSets(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
list, ok := result["IPSets"].([]any)
require.True(t, ok)
@@ -281,7 +282,7 @@ func TestHandler_CreateIPSet_Duplicate(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec2.Code)
var result map[string]string
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &result, jsonx.V1()))
assert.Equal(t, "WAFDuplicateItemException", result["__type"])
}
@@ -364,7 +365,7 @@ func TestHandler_ListIPSets_Scope_Filter(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
list, ok := result["IPSets"].([]any)
require.True(t, ok)
assert.Len(t, list, tt.wantCount)
@@ -427,7 +428,7 @@ func createIPSetHelper2(
require.Equal(t, http.StatusOK, rec.Code, "createIPSetHelper2: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summary := resp["Summary"].(map[string]any)
return summary["Id"].(string), summary["LockToken"].(string)
@@ -500,7 +501,7 @@ func TestIPSetListPagination(t *testing.T) {
require.Equal(t, http.StatusOK, page1Rec.Code)
var page1Resp map[string]any
- require.NoError(t, json.Unmarshal(page1Rec.Body.Bytes(), &page1Resp))
+ require.NoError(t, json.Unmarshal(page1Rec.Body.Bytes(), &page1Resp, jsonx.V1()))
sets, _ := page1Resp["IPSets"].([]any)
assert.Len(t, sets, 2)
@@ -580,7 +581,7 @@ func TestIPSet_MaxEntriesRejected(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code, "10001 IPs should exceed limit")
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFLimitsExceededException", errResp["__type"])
}
@@ -661,7 +662,7 @@ func TestUpdateIPSet_ClearAddresses(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "UpdateIPSet with empty addresses: %s", rec.Body.String())
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
newLockToken := updateResp["NextLockToken"].(string)
// Verify addresses were cleared.
@@ -669,7 +670,7 @@ func TestUpdateIPSet_ClearAddresses(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &getResp, jsonx.V1()))
ipSet := getResp["IPSet"].(map[string]any)
addresses, _ := ipSet["Addresses"].([]any)
assert.Empty(t, addresses, "addresses should be empty after clearing")
diff --git a/services/wafv2/handler_logging_config.go b/services/wafv2/handler_logging_config.go
index 1e41925095..dd0a7103fd 100644
--- a/services/wafv2/handler_logging_config.go
+++ b/services/wafv2/handler_logging_config.go
@@ -2,11 +2,13 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"strings"
arnpkg "github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -63,7 +65,7 @@ type deleteLoggingConfigurationRequest struct {
func (h *Handler) handleDeleteLoggingConfiguration(ctx context.Context, body []byte) ([]byte, error) {
var req deleteLoggingConfigurationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -91,24 +93,24 @@ func (h *Handler) handleDeleteLoggingConfiguration(ctx context.Context, body []b
// putLoggingConfigurationRequest is the request body for PutLoggingConfiguration.
type putLoggingConfigurationRequest struct {
- LoggingConfiguration json.RawMessage `json:"LoggingConfiguration"`
+ LoggingConfiguration jsontext.Value `json:"LoggingConfiguration"`
}
func (h *Handler) handlePutLoggingConfiguration(ctx context.Context, body []byte) ([]byte, error) {
var req putLoggingConfigurationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
// Extract ResourceArn for validation.
- var cfg map[string]json.RawMessage
- if err := json.Unmarshal(req.LoggingConfiguration, &cfg); err != nil {
+ var cfg map[string]jsontext.Value
+ if err := json.Unmarshal(req.LoggingConfiguration, &cfg, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: invalid LoggingConfiguration JSON: %w", errInvalidRequest, err)
}
var resourceARN string
if raw, ok := cfg["ResourceArn"]; ok {
- if err := json.Unmarshal(raw, &resourceARN); err != nil || resourceARN == "" {
+ if err := json.Unmarshal(raw, &resourceARN, jsonx.V1()); err != nil || resourceARN == "" {
return nil, fmt.Errorf("%w: LoggingConfiguration.ResourceArn is required", errInvalidRequest)
}
} else {
@@ -118,7 +120,7 @@ func (h *Handler) handlePutLoggingConfiguration(ctx context.Context, body []byte
// Validate destination ARN prefixes.
if destRaw, ok := cfg["LogDestinationConfigs"]; ok {
var destinations []string
- if unmarshalErr := json.Unmarshal(destRaw, &destinations); unmarshalErr == nil {
+ if unmarshalErr := json.Unmarshal(destRaw, &destinations, jsonx.V1()); unmarshalErr == nil {
for _, dest := range destinations {
if destErr := validateLoggingDestination(dest); destErr != nil {
return nil, destErr
@@ -135,13 +137,13 @@ func (h *Handler) handlePutLoggingConfiguration(ctx context.Context, body []byte
log.InfoContext(ctx, "wafv2: put logging configuration", "resourceArn", resourceARN)
var respCfg any
- if err := json.Unmarshal(req.LoggingConfiguration, &respCfg); err != nil {
+ if err := json.Unmarshal(req.LoggingConfiguration, &respCfg, jsonx.V1()); err != nil {
respCfg = map[string]any{"ResourceArn": resourceARN}
}
return json.Marshal(map[string]any{
"LoggingConfiguration": respCfg,
- })
+ }, jsonx.V1())
}
// validateLoggingDestination checks that a destination ARN has an accepted prefix.
@@ -168,7 +170,7 @@ type getLoggingConfigurationRequest struct {
func (h *Handler) handleGetLoggingConfiguration(ctx context.Context, body []byte) ([]byte, error) {
var req getLoggingConfigurationRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -190,13 +192,13 @@ func (h *Handler) handleGetLoggingConfiguration(ctx context.Context, body []byte
}
var cfg any
- if unmarshalErr := json.Unmarshal(cfgJSON, &cfg); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(cfgJSON, &cfg, jsonx.V1()); unmarshalErr != nil {
cfg = map[string]any{"ResourceArn": req.ResourceArn}
}
return json.Marshal(map[string]any{
"LoggingConfiguration": cfg,
- })
+ }, jsonx.V1())
}
// listLoggingConfigurationsRequest mirrors ListLoggingConfigurationsInput
@@ -261,7 +263,7 @@ func skipToLoggingConfigMarker(entries []loggingConfigEntry, nextMarker string)
// and LogScope, paginated by Limit/NextMarker.
func (h *Handler) handleListLoggingConfigurations(ctx context.Context, body []byte) ([]byte, error) {
var req listLoggingConfigurationsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -271,7 +273,7 @@ func (h *Handler) handleListLoggingConfigurations(ctx context.Context, body []by
for _, cfg := range configs {
var v map[string]any
- if err := json.Unmarshal(cfg, &v); err != nil {
+ if err := json.Unmarshal(cfg, &v, jsonx.V1()); err != nil {
continue
}
@@ -298,7 +300,7 @@ func (h *Handler) handleListLoggingConfigurations(ctx context.Context, body []by
resp["NextMarker"] = nextMarker
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// loggingConfigDispatchOps returns the logging-configuration-family operation dispatch
diff --git a/services/wafv2/handler_logging_config_test.go b/services/wafv2/handler_logging_config_test.go
index 2e18078365..9390a75ed3 100644
--- a/services/wafv2/handler_logging_config_test.go
+++ b/services/wafv2/handler_logging_config_test.go
@@ -2,13 +2,15 @@ package wafv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/wafv2"
)
@@ -29,7 +31,7 @@ func TestHandler_DeleteLoggingConfiguration(t *testing.T) {
require.NoError(t, h.Backend.PutLoggingConfiguration(
context.Background(),
arnStr,
- json.RawMessage(`{"ResourceArn":"`+arnStr+`"}`),
+ jsontext.Value(`{"ResourceArn":"`+arnStr+`"}`),
))
return arnStr
@@ -88,7 +90,7 @@ func TestLoggingConfigurationFullRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
webACLARN := createResp["Summary"].(map[string]any)["ARN"].(string)
// Put a full logging configuration.
@@ -111,7 +113,7 @@ func TestLoggingConfigurationFullRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
cfg, ok := getResp["LoggingConfiguration"].(map[string]any)
require.True(t, ok)
@@ -126,7 +128,7 @@ func TestLoggingConfigurationFullRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
configs, ok := listResp["LoggingConfigurations"].([]any)
require.True(t, ok)
assert.Len(t, configs, 1)
@@ -155,7 +157,7 @@ func TestLoggingConfig_FirehoseDestination(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
cfg := resp["LoggingConfiguration"].(map[string]any)
dests := cfg["LogDestinationConfigs"].([]any)
require.Len(t, dests, 1)
@@ -182,7 +184,7 @@ func TestLoggingConfig_S3Destination(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
cfg := resp["LoggingConfiguration"].(map[string]any)
dests := cfg["LogDestinationConfigs"].([]any)
require.Len(t, dests, 1)
@@ -209,7 +211,7 @@ func TestLoggingConfig_CloudWatchDestination(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
cfg := resp["LoggingConfiguration"].(map[string]any)
dests := cfg["LogDestinationConfigs"].([]any)
require.Len(t, dests, 1)
@@ -275,7 +277,7 @@ func TestLoggingConfig_DeleteAndGetNotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, getRec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFNonexistentItemException", errResp["__type"])
}
@@ -290,7 +292,7 @@ func TestLoggingConfig_DeleteNotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFNonexistentItemException", errResp["__type"])
}
@@ -335,7 +337,7 @@ func TestLoggingConfig_FullFieldRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
cfg := resp["LoggingConfiguration"].(map[string]any)
// RedactedFields should round-trip.
@@ -443,7 +445,7 @@ func TestListLoggingConfigurations_ReturnsStoredConfigs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
configs, _ := resp["LoggingConfigurations"].([]any)
assert.Len(t, configs, 2, "ListLoggingConfigurations should return 2 stored configs")
}
diff --git a/services/wafv2/handler_managed_rule_catalog.go b/services/wafv2/handler_managed_rule_catalog.go
index 7d77b7dff5..eaffe087c2 100644
--- a/services/wafv2/handler_managed_rule_catalog.go
+++ b/services/wafv2/handler_managed_rule_catalog.go
@@ -2,9 +2,11 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"sort"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// defaultManagedRuleGroupVersion is this catalog's single hardcoded version
@@ -17,7 +19,7 @@ func (h *Handler) handleDescribeAllManagedProducts(body []byte) ([]byte, error)
var req struct {
Scope string `json:"Scope"`
}
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -36,7 +38,7 @@ func (h *Handler) handleDescribeAllManagedProducts(body []byte) ([]byte, error)
})
}
- return json.Marshal(map[string]any{"ManagedProducts": products})
+ return json.Marshal(map[string]any{"ManagedProducts": products}, jsonx.V1())
}
// describeManagedProductsByVendorRequest is the request body for DescribeManagedProductsByVendor.
@@ -48,7 +50,7 @@ type describeManagedProductsByVendorRequest struct {
// handleDescribeManagedProductsByVendor returns catalog entries filtered by vendor.
func (h *Handler) handleDescribeManagedProductsByVendor(body []byte) ([]byte, error) {
var req describeManagedProductsByVendorRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -75,7 +77,7 @@ func (h *Handler) handleDescribeManagedProductsByVendor(body []byte) ([]byte, er
})
}
- return json.Marshal(map[string]any{"ManagedProducts": products})
+ return json.Marshal(map[string]any{"ManagedProducts": products}, jsonx.V1())
}
// describeManagedRuleGroupRequest is the request body for DescribeManagedRuleGroup.
@@ -89,7 +91,7 @@ type describeManagedRuleGroupRequest struct {
// handleDescribeManagedRuleGroup returns catalog data for the requested managed rule group.
func (h *Handler) handleDescribeManagedRuleGroup(body []byte) ([]byte, error) {
var req describeManagedRuleGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -133,7 +135,7 @@ func (h *Handler) handleDescribeManagedRuleGroup(body []byte) ([]byte, error) {
resp["VersionName"] = defaultManagedRuleGroupVersion
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
}
@@ -205,7 +207,7 @@ type generateMobileSdkReleaseURLRequest struct {
// handleGenerateMobileSdkReleaseURL returns a presigned-style URL for the requested mobile SDK release.
func (h *Handler) handleGenerateMobileSdkReleaseURL(body []byte) ([]byte, error) {
var req generateMobileSdkReleaseURLRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -231,7 +233,7 @@ func (h *Handler) handleGenerateMobileSdkReleaseURL(body []byte) ([]byte, error)
return json.Marshal(map[string]any{
"Url": url,
- })
+ }, jsonx.V1())
}
// getMobileSdkReleaseRequest is the request body for GetMobileSdkRelease.
@@ -243,7 +245,7 @@ type getMobileSdkReleaseRequest struct {
// handleGetMobileSdkRelease returns the mobile SDK release from the catalog.
func (h *Handler) handleGetMobileSdkRelease(body []byte) ([]byte, error) {
var req getMobileSdkReleaseRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -272,7 +274,7 @@ func (h *Handler) handleGetMobileSdkRelease(body []byte) ([]byte, error) {
"ReleaseNotes": release.ReleaseNotes,
"Tags": []any{},
},
- })
+ }, jsonx.V1())
}
// listAvailableManagedRuleGroupVersionsRequest is the request body for ListAvailableManagedRuleGroupVersions.
@@ -287,7 +289,7 @@ type listAvailableManagedRuleGroupVersionsRequest struct {
// handleListAvailableManagedRuleGroupVersions returns versions for managed rule groups that support versioning.
func (h *Handler) handleListAvailableManagedRuleGroupVersions(body []byte) ([]byte, error) {
var req listAvailableManagedRuleGroupVersionsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -299,11 +301,11 @@ func (h *Handler) handleListAvailableManagedRuleGroupVersions(body []byte) ([]by
{"Name": defaultManagedRuleGroupVersion, "LastUpdateTimestamp": nil},
},
"CurrentDefaultVersion": defaultManagedRuleGroupVersion,
- })
+ }, jsonx.V1())
}
}
- return json.Marshal(map[string]any{"Versions": []any{}, "CurrentDefaultVersion": ""})
+ return json.Marshal(map[string]any{"Versions": []any{}, "CurrentDefaultVersion": ""}, jsonx.V1())
}
// listAvailableManagedRuleGroupsRequest is the request body for ListAvailableManagedRuleGroups.
@@ -316,7 +318,7 @@ type listAvailableManagedRuleGroupsRequest struct {
// handleListAvailableManagedRuleGroups returns the catalog of managed rule groups.
func (h *Handler) handleListAvailableManagedRuleGroups(body []byte) ([]byte, error) {
var req listAvailableManagedRuleGroupsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -350,7 +352,7 @@ func (h *Handler) handleListAvailableManagedRuleGroups(body []byte) ([]byte, err
resp["NextMarker"] = nextMarker
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// listMobileSdkReleasesRequest is the request body for ListMobileSdkReleases.
@@ -366,7 +368,7 @@ type listMobileSdkReleasesRequest struct {
// handleListMobileSdkReleases lists mobile SDK releases from the catalog.
func (h *Handler) handleListMobileSdkReleases(body []byte) ([]byte, error) {
var req listMobileSdkReleasesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -398,7 +400,7 @@ func (h *Handler) handleListMobileSdkReleases(body []byte) ([]byte, error) {
resp["NextMarker"] = nextMarker
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// managedRuleCatalogDispatchOps returns the managed-rule-group and mobile-SDK catalog
diff --git a/services/wafv2/handler_managed_rule_catalog_test.go b/services/wafv2/handler_managed_rule_catalog_test.go
index 59772f49c1..efd4365edb 100644
--- a/services/wafv2/handler_managed_rule_catalog_test.go
+++ b/services/wafv2/handler_managed_rule_catalog_test.go
@@ -1,12 +1,14 @@
package wafv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestListAvailableManagedRuleGroups_ReturnsCatalog(t *testing.T) {
@@ -19,7 +21,7 @@ func TestListAvailableManagedRuleGroups_ReturnsCatalog(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups, ok := resp["ManagedRuleGroups"].([]any)
require.True(t, ok)
@@ -53,7 +55,7 @@ func TestDescribeManagedRuleGroup_ReturnsCatalogData(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
capacity, _ := resp["Capacity"].(float64)
assert.InDelta(t, float64(700), capacity, 0, "AWSManagedRulesCommonRuleSet should have capacity 700")
@@ -73,7 +75,7 @@ func TestGetMobileSdkRelease_KnownVersion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "GetMobileSdkRelease Android/3.1.0: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
release := resp["MobileSdkRelease"].(map[string]any)
assert.Equal(t, "3.1.0", release["ReleaseVersion"])
@@ -95,7 +97,7 @@ func TestGetMobileSdkRelease_IOSVersion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
release := resp["MobileSdkRelease"].(map[string]any)
assert.Equal(t, "3.0.0", release["ReleaseVersion"])
}
@@ -112,7 +114,7 @@ func TestGetMobileSdkRelease_UnknownVersion(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFNonexistentItemException", errResp["__type"])
}
@@ -139,7 +141,7 @@ func TestGetMobileSdkRelease_MissingPlatform(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFInvalidParameterException", errResp["__type"])
}
@@ -155,7 +157,7 @@ func TestListMobileSdkReleases_Android(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
releases, ok := resp["ReleaseSummaries"].([]any)
require.True(t, ok)
@@ -180,7 +182,7 @@ func TestListMobileSdkReleases_iOS(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
releases, _ := resp["ReleaseSummaries"].([]any)
assert.NotEmpty(t, releases, "should return at least one iOS release")
@@ -198,7 +200,7 @@ func TestListMobileSdkReleases_UnknownPlatform(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
releases, _ := resp["ReleaseSummaries"].([]any)
assert.Empty(t, releases, "unknown platform should return empty list")
}
@@ -215,7 +217,7 @@ func TestGenerateMobileSdkReleaseUrl_Shape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
url, ok := resp["Url"].(string)
require.True(t, ok, "Url field should be present")
@@ -259,14 +261,14 @@ func TestWebACL_ManagedRuleGroupStatement_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "ManagedRuleGroupStatement: %s", rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Summary"].(map[string]any)["Id"].(string)
getRec := doWafv2Request(t, h, "GetWebACL", map[string]any{"Id": id})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
rules := getResp["WebACL"].(map[string]any)["Rules"].([]any)
require.Len(t, rules, 1)
@@ -333,7 +335,7 @@ func TestDescribeManagedRuleGroup(t *testing.T) {
require.Equal(t, tc.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tc.wantType != "" {
assert.Equal(t, tc.wantType, resp["__type"])
@@ -414,7 +416,7 @@ func TestGenerateMobileSdkReleaseUrl(t *testing.T) {
require.Equal(t, tc.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tc.wantType != "" {
assert.Equal(t, tc.wantType, resp["__type"])
@@ -510,7 +512,7 @@ func TestDescribeManagedRuleGroupRules(t *testing.T) {
} `json:"AvailableLabels"`
Capacity float64 `json:"Capacity"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
// Rules and AvailableLabels must never be null (always an array).
assert.NotNil(t, out.Rules, "Rules must not be null")
diff --git a/services/wafv2/handler_managed_rule_sets.go b/services/wafv2/handler_managed_rule_sets.go
index ef1c37c45a..7584e60c57 100644
--- a/services/wafv2/handler_managed_rule_sets.go
+++ b/services/wafv2/handler_managed_rule_sets.go
@@ -2,9 +2,10 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -33,7 +34,7 @@ type getManagedRuleSetRequest struct {
// handleGetManagedRuleSet returns the stored managed rule set.
func (h *Handler) handleGetManagedRuleSet(ctx context.Context, body []byte) ([]byte, error) {
var req getManagedRuleSetRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -64,7 +65,7 @@ func (h *Handler) handleGetManagedRuleSet(ctx context.Context, body []byte) ([]b
"RecommendedVersion": ms.RecommendedVersion,
},
keyLockToken: ms.LockToken,
- })
+ }, jsonx.V1())
}
// listManagedRuleSetsRequest is the request body for ListManagedRuleSets.
@@ -77,7 +78,7 @@ type listManagedRuleSetsRequest struct {
// handleListManagedRuleSets lists all stored managed rule sets, filtered by scope.
func (h *Handler) handleListManagedRuleSets(ctx context.Context, body []byte) ([]byte, error) {
var req listManagedRuleSetsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -115,7 +116,7 @@ func (h *Handler) handleListManagedRuleSets(ctx context.Context, body []byte) ([
resp["NextMarker"] = nextMarker
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// putManagedRuleSetVersionsRequest is the request body for PutManagedRuleSetVersions.
@@ -130,7 +131,7 @@ type putManagedRuleSetVersionsRequest struct {
func (h *Handler) handlePutManagedRuleSetVersions(ctx context.Context, body []byte) ([]byte, error) {
var req putManagedRuleSetVersionsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -165,7 +166,7 @@ func (h *Handler) handlePutManagedRuleSetVersions(ctx context.Context, body []by
log := logger.Load(ctx)
log.InfoContext(ctx, "wafv2: put managed rule set versions", "id", req.ID)
- return json.Marshal(map[string]string{keyNextLockToken: ms.LockToken})
+ return json.Marshal(map[string]string{keyNextLockToken: ms.LockToken}, jsonx.V1())
}
// updateManagedRuleSetVersionExpiryDateRequest is the request body for
@@ -187,7 +188,7 @@ type updateManagedRuleSetVersionExpiryDateRequest struct {
func (h *Handler) handleUpdateManagedRuleSetVersionExpiryDate(ctx context.Context, body []byte) ([]byte, error) {
var req updateManagedRuleSetVersionExpiryDateRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -235,7 +236,7 @@ func (h *Handler) handleUpdateManagedRuleSetVersionExpiryDate(ctx context.Contex
keyNextLockToken: ms.LockToken,
"ExpiringVersion": req.VersionToExpire,
"ExpiryTimestamp": req.ExpiryTimestamp,
- })
+ }, jsonx.V1())
}
// managedRuleSetDispatchOps returns the ManagedRuleSet-family operation dispatch entries.
diff --git a/services/wafv2/handler_managed_rule_sets_test.go b/services/wafv2/handler_managed_rule_sets_test.go
index 387f961a6d..bda216f6fc 100644
--- a/services/wafv2/handler_managed_rule_sets_test.go
+++ b/services/wafv2/handler_managed_rule_sets_test.go
@@ -1,12 +1,14 @@
package wafv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestManagedRuleSet_PutAndGet(t *testing.T) {
@@ -33,7 +35,7 @@ func TestManagedRuleSet_PutAndGet(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code, "PutManagedRuleSetVersions: %s", putRec.Body.String())
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
lockToken1, _ := putResp["NextLockToken"].(string)
assert.NotEmpty(t, lockToken1, "PutManagedRuleSetVersions should return NextLockToken")
@@ -46,7 +48,7 @@ func TestManagedRuleSet_PutAndGet(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code, "GetManagedRuleSet: %s", getRec.Body.String())
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
ms := getResp["ManagedRuleSet"].(map[string]any)
assert.Equal(t, msID, ms["Id"])
@@ -78,7 +80,7 @@ func TestManagedRuleSet_GetNotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFNonexistentItemException", errResp["__type"])
}
@@ -93,7 +95,7 @@ func TestManagedRuleSet_GetMissingID(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFInvalidParameterException", errResp["__type"])
}
@@ -119,7 +121,7 @@ func TestManagedRuleSet_UpdateExpiryDate(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
lockToken := putResp["NextLockToken"].(string)
expiry := int64(9999999999)
@@ -142,7 +144,7 @@ func TestManagedRuleSet_UpdateExpiryDate(t *testing.T) {
)
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(updateRec.Body.Bytes(), &updateResp, jsonx.V1()))
assert.NotEmpty(t, updateResp["NextLockToken"])
assert.Equal(t, "Version_1.0", updateResp["ExpiringVersion"])
assert.InDelta(t, float64(expiry), updateResp["ExpiryTimestamp"], 0)
@@ -156,7 +158,7 @@ func TestManagedRuleSet_UpdateExpiryDate(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
ms := getResp["ManagedRuleSet"].(map[string]any)
versions := ms["PublishedVersions"].(map[string]any)
v10 := versions["Version_1.0"].(map[string]any)
@@ -180,7 +182,7 @@ func TestManagedRuleSet_UpdateExpiryNotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFNonexistentItemException", errResp["__type"])
}
@@ -199,7 +201,7 @@ func TestManagedRuleSet_UpdateExpiryVersionNotFound(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
lockToken := putResp["NextLockToken"].(string)
// Attempt to expire a version that was never published.
@@ -214,7 +216,7 @@ func TestManagedRuleSet_UpdateExpiryVersionNotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFNonexistentItemException", errResp["__type"])
}
@@ -242,7 +244,7 @@ func TestManagedRuleSet_PutLockTokenEnforcement(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFOptimisticLockException", errResp["__type"])
}
@@ -275,7 +277,7 @@ func TestListManagedRuleSets_ScopeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, filtRec.Code)
var filtResp map[string]any
- require.NoError(t, json.Unmarshal(filtRec.Body.Bytes(), &filtResp))
+ require.NoError(t, json.Unmarshal(filtRec.Body.Bytes(), &filtResp, jsonx.V1()))
filt, _ := filtResp["ManagedRuleSets"].([]any)
assert.Len(t, filt, 1)
assert.Equal(t, "regional-ms", filt[0].(map[string]any)["Name"])
@@ -284,7 +286,7 @@ func TestListManagedRuleSets_ScopeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, cfRec.Code)
var cfResp map[string]any
- require.NoError(t, json.Unmarshal(cfRec.Body.Bytes(), &cfResp))
+ require.NoError(t, json.Unmarshal(cfRec.Body.Bytes(), &cfResp, jsonx.V1()))
cf, _ := cfResp["ManagedRuleSets"].([]any)
assert.Len(t, cf, 1)
assert.Equal(t, "cloudfront-ms", cf[0].(map[string]any)["Name"])
@@ -388,7 +390,7 @@ func TestManagedRuleSet_RequiredFieldValidation(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code, rec.Body.String())
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFInvalidParameterException", errResp["__type"])
})
}
@@ -439,7 +441,7 @@ func TestListManagedRuleSets_DuplicateNamePagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["ManagedRuleSets"].([]any)
for _, item := range items {
diff --git a/services/wafv2/handler_permission_policies.go b/services/wafv2/handler_permission_policies.go
index b97e1839e8..74d9945c56 100644
--- a/services/wafv2/handler_permission_policies.go
+++ b/services/wafv2/handler_permission_policies.go
@@ -2,9 +2,10 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
)
@@ -15,7 +16,7 @@ type deletePermissionPolicyRequest struct {
func (h *Handler) handleDeletePermissionPolicy(ctx context.Context, body []byte) ([]byte, error) {
var req deletePermissionPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -41,7 +42,7 @@ type putPermissionPolicyRequest struct {
func (h *Handler) handlePutPermissionPolicy(ctx context.Context, body []byte) ([]byte, error) {
var req putPermissionPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -66,7 +67,7 @@ type getPermissionPolicyRequest struct {
func (h *Handler) handleGetPermissionPolicy(ctx context.Context, body []byte) ([]byte, error) {
var req getPermissionPolicyRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -79,7 +80,7 @@ func (h *Handler) handleGetPermissionPolicy(ctx context.Context, body []byte) ([
return nil, err
}
- return json.Marshal(map[string]string{"Policy": policy})
+ return json.Marshal(map[string]string{"Policy": policy}, jsonx.V1())
}
// permissionPolicyDispatchOps returns the permission-policy-family operation dispatch
diff --git a/services/wafv2/handler_permission_policies_test.go b/services/wafv2/handler_permission_policies_test.go
index 49803740fb..3b6c2fcd9f 100644
--- a/services/wafv2/handler_permission_policies_test.go
+++ b/services/wafv2/handler_permission_policies_test.go
@@ -2,13 +2,14 @@ package wafv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/wafv2"
)
@@ -107,7 +108,7 @@ func TestPermissionPolicy_PutGetDelete(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code, "GetPermissionPolicy: %s", getRec.Body.String())
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
storedPolicy, ok := getResp["Policy"].(string)
require.True(t, ok, "Policy field should be present")
assert.Equal(t, policy, storedPolicy)
@@ -125,7 +126,7 @@ func TestPermissionPolicy_PutGetDelete(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, getRec2.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(getRec2.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFNonexistentItemException", errResp["__type"])
}
@@ -140,7 +141,7 @@ func TestPermissionPolicy_DeleteNotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFNonexistentItemException", errResp["__type"])
}
@@ -184,7 +185,7 @@ func TestPermissionPolicy_UpdateReplace(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, policy2, resp["Policy"])
}
@@ -259,7 +260,7 @@ func TestHandler_PutAndGetPermissionPolicy(t *testing.T) {
if tt.wantGetStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "Policy")
}
}
diff --git a/services/wafv2/handler_rate_based_rules.go b/services/wafv2/handler_rate_based_rules.go
index 2d32d18d0f..8c3a3679ef 100644
--- a/services/wafv2/handler_rate_based_rules.go
+++ b/services/wafv2/handler_rate_based_rules.go
@@ -2,9 +2,11 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// getRateBasedStatementManagedKeysRequest is the request body for GetRateBasedStatementManagedKeys.
@@ -19,7 +21,7 @@ type getRateBasedStatementManagedKeysRequest struct {
// handleGetRateBasedStatementManagedKeys returns empty rate-based managed keys.
func (h *Handler) handleGetRateBasedStatementManagedKeys(ctx context.Context, body []byte) ([]byte, error) {
var req getRateBasedStatementManagedKeysRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -46,7 +48,7 @@ func (h *Handler) handleGetRateBasedStatementManagedKeys(ctx context.Context, bo
return json.Marshal(map[string]any{
"ManagedKeysIPV4": map[string]any{keyIPAddressVersion: "IPV4", keyAddresses: []any{}},
"ManagedKeysIPV6": map[string]any{keyIPAddressVersion: "IPV6", keyAddresses: []any{}},
- })
+ }, jsonx.V1())
}
// getSampledRequestsRequest is the request body for GetSampledRequests.
@@ -61,7 +63,7 @@ type getSampledRequestsRequest struct {
// handleGetSampledRequests returns an empty sampled requests response.
func (h *Handler) handleGetSampledRequests(ctx context.Context, body []byte) ([]byte, error) {
var req getSampledRequestsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -97,7 +99,7 @@ func (h *Handler) handleGetSampledRequests(ctx context.Context, body []byte) ([]
"SampledRequests": []any{},
"PopulationSize": int64(0),
"TimeWindow": req.TimeWindow,
- })
+ }, jsonx.V1())
}
// getTopPathStatisticsByTrafficRequest is the request body for
@@ -128,7 +130,7 @@ type getTopPathStatisticsByTrafficRequest struct {
// this same structural gap.
func (h *Handler) handleGetTopPathStatisticsByTraffic(ctx context.Context, body []byte) ([]byte, error) {
var req getTopPathStatisticsByTrafficRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -166,7 +168,7 @@ func (h *Handler) handleGetTopPathStatisticsByTraffic(ctx context.Context, body
return json.Marshal(map[string]any{
"PathStatistics": []any{},
"TotalRequestCount": int64(0),
- })
+ }, jsonx.V1())
}
// rateBasedRuleDispatchOps returns the rate-based-rule and traffic-monitoring operation
diff --git a/services/wafv2/handler_rate_based_rules_test.go b/services/wafv2/handler_rate_based_rules_test.go
index 56570b789d..a1cea6a6a4 100644
--- a/services/wafv2/handler_rate_based_rules_test.go
+++ b/services/wafv2/handler_rate_based_rules_test.go
@@ -1,7 +1,7 @@
package wafv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/wafv2"
)
@@ -105,7 +106,7 @@ func TestRateBasedStatement_WithScopeDown(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "RateBasedStatement with ScopeDown: %s", rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Summary"].(map[string]any)["Id"].(string)
// Retrieve and verify the rule is stored with ScopeDownStatement.
@@ -113,7 +114,7 @@ func TestRateBasedStatement_WithScopeDown(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
rules := getResp["WebACL"].(map[string]any)["Rules"].([]any)
require.Len(t, rules, 1)
@@ -275,7 +276,7 @@ func TestGetRateBasedStatementManagedKeys_Valid(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "GetRateBasedStatementManagedKeys: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
ipv4, ok := resp["ManagedKeysIPV4"].(map[string]any)
require.True(t, ok, "ManagedKeysIPV4 should be present")
@@ -349,7 +350,7 @@ func TestValidation_RuleNameUniqueness(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code, "duplicate rule names should be rejected")
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFInvalidParameterException", errResp["__type"])
}
@@ -541,7 +542,7 @@ func TestGetTopPathStatisticsByTraffic(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "PathStatistics", "PathStatistics is a required response member")
assert.Empty(t, resp["PathStatistics"], "no real traffic in emulator")
assert.Contains(t, resp, "TotalRequestCount", "TotalRequestCount is a required response member")
@@ -675,7 +676,7 @@ func TestGetRateBasedStatementManagedKeys(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "ManagedKeysIPV4")
assert.Contains(t, resp, "ManagedKeysIPV6")
}
@@ -809,7 +810,7 @@ func TestGetSampledRequests(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "SampledRequests")
assert.Empty(t, resp["SampledRequests"], "no real traffic in emulator")
assert.Contains(t, resp, "TimeWindow")
@@ -1053,7 +1054,7 @@ func TestGetRevenueStatistics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// No-fabrication assertion: whichever statistic list is populated for this
// StatisticType must be empty -- this emulator has no real AI-bot traffic to
@@ -1135,7 +1136,7 @@ func TestGetRevenueStatisticsSummary(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
breakdown, ok := resp["RevenueBreakdown"].(map[string]any)
require.True(t, ok, "RevenueBreakdown must be present")
@@ -1269,7 +1270,7 @@ func TestGetRevenueStatisticsTimeSeries(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "DataPoints")
assert.Empty(t, resp["DataPoints"], "no revenue events exist in this emulator to bucket into a time series")
})
@@ -1364,7 +1365,7 @@ func TestListSettlementRecords(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "Settlements")
// No-fabrication assertion: no real payment/settlement pipeline exists in
// this emulator, so there must never be an invented PayerAddress,
diff --git a/services/wafv2/handler_regex_pattern_sets.go b/services/wafv2/handler_regex_pattern_sets.go
index 2ea94bc74c..11bc8e5b9d 100644
--- a/services/wafv2/handler_regex_pattern_sets.go
+++ b/services/wafv2/handler_regex_pattern_sets.go
@@ -2,9 +2,11 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -12,16 +14,16 @@ import (
// createRegexPatternSetRequest is the request body for CreateRegexPatternSet.
// RegularExpressionList accepts both []string and []{RegexString:...} shapes.
type createRegexPatternSetRequest struct {
- Name string `json:"Name"`
- Scope string `json:"Scope"`
- Description string `json:"Description"`
- RegularExpressionList json.RawMessage `json:"RegularExpressionList"`
- Tags []tags.KV `json:"Tags"`
+ Name string `json:"Name"`
+ Scope string `json:"Scope"`
+ Description string `json:"Description"`
+ RegularExpressionList jsontext.Value `json:"RegularExpressionList"`
+ Tags []tags.KV `json:"Tags"`
}
func (h *Handler) handleCreateRegexPatternSet(ctx context.Context, body []byte) ([]byte, error) {
var req createRegexPatternSetRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -84,24 +86,24 @@ func (h *Handler) handleCreateRegexPatternSet(ctx context.Context, body []byte)
keyLockToken: rps.LockToken,
keyDescription: rps.Description,
},
- })
+ }, jsonx.V1())
}
// parseRegexEntries accepts either []RegexEntry or []string and normalises to []RegexEntry.
-func parseRegexEntries(raw json.RawMessage) ([]RegexEntry, error) {
+func parseRegexEntries(raw jsontext.Value) ([]RegexEntry, error) {
if len(raw) == 0 {
return []RegexEntry{}, nil
}
// Try object form first: [{"RegexString": "..."}]
var entries []RegexEntry
- if err := json.Unmarshal(raw, &entries); err == nil {
+ if err := json.Unmarshal(raw, &entries, jsonx.V1()); err == nil {
return entries, nil
}
// Fall back to plain string form: ["...", "..."]
var strs []string
- if err := json.Unmarshal(raw, &strs); err != nil {
+ if err := json.Unmarshal(raw, &strs, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: RegularExpressionList must be an array of objects or strings", errInvalidRequest)
}
@@ -123,7 +125,7 @@ type deleteRegexPatternSetRequest struct {
func (h *Handler) handleDeleteRegexPatternSet(ctx context.Context, body []byte) ([]byte, error) {
var req deleteRegexPatternSetRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -150,7 +152,7 @@ type getRegexPatternSetRequest struct {
func (h *Handler) handleGetRegexPatternSet(ctx context.Context, body []byte) ([]byte, error) {
var req getRegexPatternSetRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -188,7 +190,7 @@ func (h *Handler) handleGetRegexPatternSet(ctx context.Context, body []byte) ([]
"RegularExpressionList": r.RegularExpressionList,
},
keyLockToken: r.LockToken,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleListRegexPatternSets(ctx context.Context, body []byte) ([]byte, error) {
@@ -212,17 +214,17 @@ func (h *Handler) handleListRegexPatternSets(ctx context.Context, body []byte) (
// updateRegexPatternSetRequest is the request body for UpdateRegexPatternSet.
type updateRegexPatternSetRequest struct {
- ID string `json:"Id"`
- Name string `json:"Name"`
- Scope string `json:"Scope"`
- LockToken string `json:"LockToken"`
- Description string `json:"Description"`
- RegularExpressionList json.RawMessage `json:"RegularExpressionList"`
+ ID string `json:"Id"`
+ Name string `json:"Name"`
+ Scope string `json:"Scope"`
+ LockToken string `json:"LockToken"`
+ Description string `json:"Description"`
+ RegularExpressionList jsontext.Value `json:"RegularExpressionList"`
}
func (h *Handler) handleUpdateRegexPatternSet(ctx context.Context, body []byte) ([]byte, error) {
var req updateRegexPatternSetRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -259,7 +261,7 @@ func (h *Handler) handleUpdateRegexPatternSet(ctx context.Context, body []byte)
log := logger.Load(ctx)
log.InfoContext(ctx, "wafv2: updated regex pattern set", "id", req.ID)
- return json.Marshal(map[string]string{keyNextLockToken: r.LockToken})
+ return json.Marshal(map[string]string{keyNextLockToken: r.LockToken}, jsonx.V1())
}
// regexPatternSetDispatchOps returns the RegexPatternSet-family operation dispatch
diff --git a/services/wafv2/handler_regex_pattern_sets_test.go b/services/wafv2/handler_regex_pattern_sets_test.go
index 28aad8b1bb..ac3cb40ac2 100644
--- a/services/wafv2/handler_regex_pattern_sets_test.go
+++ b/services/wafv2/handler_regex_pattern_sets_test.go
@@ -2,13 +2,14 @@ package wafv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/wafv2"
)
@@ -74,7 +75,7 @@ func TestHandler_CreateRegexPatternSet(t *testing.T) {
if tt.wantID {
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
summary, ok := result["Summary"].(map[string]any)
require.True(t, ok)
assert.NotEmpty(t, summary["Id"])
@@ -180,7 +181,7 @@ func TestRegexPatternSetObjectShape(t *testing.T) {
require.Equal(t, http.StatusOK, recObj.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(recObj.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(recObj.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Summary"].(map[string]any)["Id"].(string)
// Get and verify the entries are returned as objects.
@@ -190,7 +191,7 @@ func TestRegexPatternSetObjectShape(t *testing.T) {
require.Equal(t, http.StatusOK, recGet.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &getResp, jsonx.V1()))
rps := getResp["RegexPatternSet"].(map[string]any)
entries, ok := rps["RegularExpressionList"].([]any)
@@ -232,7 +233,7 @@ func TestRegexPatternSet_MaxEntries(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code, "11 regex entries should exceed limit")
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFLimitsExceededException", errResp["__type"])
}
@@ -268,7 +269,7 @@ func createRegexPatternSetHelper(t *testing.T, h *wafv2.Handler, name string) st
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summary, ok := resp["Summary"].(map[string]any)
require.True(t, ok)
@@ -331,7 +332,7 @@ func TestHandler_GetRegexPatternSet(t *testing.T) {
if tt.wantField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, tt.wantField)
}
})
@@ -389,7 +390,7 @@ func TestHandler_ListRegexPatternSets(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["RegexPatternSets"].([]any)
require.True(t, ok)
@@ -459,7 +460,7 @@ func TestHandler_UpdateRegexPatternSet(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "NextLockToken")
}
})
diff --git a/services/wafv2/handler_resource_associations.go b/services/wafv2/handler_resource_associations.go
index fe1092bab6..d2afbc12de 100644
--- a/services/wafv2/handler_resource_associations.go
+++ b/services/wafv2/handler_resource_associations.go
@@ -2,9 +2,11 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// associateWebACLRequest is the request body for AssociateWebACL.
@@ -15,7 +17,7 @@ type associateWebACLRequest struct {
func (h *Handler) handleAssociateWebACL(ctx context.Context, body []byte) ([]byte, error) {
var req associateWebACLRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -84,7 +86,7 @@ type disassociateWebACLRequest struct {
func (h *Handler) handleDisassociateWebACL(ctx context.Context, body []byte) ([]byte, error) {
var req disassociateWebACLRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -106,7 +108,7 @@ type getWebACLForResourceRequest struct {
func (h *Handler) handleGetWebACLForResource(ctx context.Context, body []byte) ([]byte, error) {
var req getWebACLForResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -130,7 +132,7 @@ type listResourcesForWebACLRequest struct {
func (h *Handler) handleListResourcesForWebACL(ctx context.Context, body []byte) ([]byte, error) {
var req listResourcesForWebACLRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -156,7 +158,7 @@ func (h *Handler) handleListResourcesForWebACL(ctx context.Context, body []byte)
}
}
- return json.Marshal(map[string]any{"ResourceArns": filtered})
+ return json.Marshal(map[string]any{"ResourceArns": filtered}, jsonx.V1())
}
const (
diff --git a/services/wafv2/handler_resource_associations_test.go b/services/wafv2/handler_resource_associations_test.go
index 71c7d95b79..2d9d0da0bf 100644
--- a/services/wafv2/handler_resource_associations_test.go
+++ b/services/wafv2/handler_resource_associations_test.go
@@ -2,7 +2,7 @@ package wafv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/wafv2"
)
@@ -270,7 +271,7 @@ func TestHandler_GetWebACLForResource(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
webACL, ok := result["WebACL"].(map[string]any)
require.True(t, ok)
assert.NotEmpty(t, webACL["Id"])
@@ -297,7 +298,7 @@ func TestHandler_GetWebACLForResource_WithVisibilityConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
webACL, ok := result["WebACL"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "my-acl", webACL["Name"])
@@ -317,7 +318,7 @@ func TestGetWebACLForResourceReturnsRules(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getRespW map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getRespW))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getRespW, jsonx.V1()))
webACLARN := getRespW["WebACL"].(map[string]any)["ARN"].(string)
// Associate with a resource.
@@ -335,7 +336,7 @@ func TestGetWebACLForResourceReturnsRules(t *testing.T) {
require.Equal(t, http.StatusOK, forResourceRec.Code)
var forResourceResp map[string]any
- require.NoError(t, json.Unmarshal(forResourceRec.Body.Bytes(), &forResourceResp))
+ require.NoError(t, json.Unmarshal(forResourceRec.Body.Bytes(), &forResourceResp, jsonx.V1()))
webACL := forResourceResp["WebACL"].(map[string]any)
rules, ok := webACL["Rules"].([]any)
@@ -371,7 +372,7 @@ func TestListResourcesForWebACL_MultipleResources(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
arns, _ := resp["ResourceArns"].([]any)
assert.Len(t, arns, 3, "should list 3 associated resources")
@@ -451,7 +452,7 @@ func TestHandler_ListResourcesForWebACL(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
resources, ok := resp["ResourceArns"].([]any)
require.True(t, ok)
assert.Len(t, resources, tt.wantResources)
@@ -539,7 +540,7 @@ func TestAssociateWebACL_ReplaceExisting(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
webACL := resp["WebACL"].(map[string]any)
assert.Equal(t, "acl-two", webACL["Name"])
}
@@ -557,7 +558,7 @@ func TestGetWebACLForResource_NotFound(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFNonexistentItemException", errResp["__type"])
}
diff --git a/services/wafv2/handler_revenue_statistics.go b/services/wafv2/handler_revenue_statistics.go
index 531b69d117..afba8c4e8f 100644
--- a/services/wafv2/handler_revenue_statistics.go
+++ b/services/wafv2/handler_revenue_statistics.go
@@ -2,8 +2,10 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// This file implements the AI-bot pay-per-crawl monetization reporting family added to
@@ -333,7 +335,7 @@ func validateGetRevenueStatisticsRequest(req getRevenueStatisticsRequest) error
// dollar amount.
func (h *Handler) handleGetRevenueStatistics(body []byte) ([]byte, error) {
var req getRevenueStatisticsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -366,7 +368,7 @@ func (h *Handler) handleGetRevenueStatistics(body []byte) ([]byte, error) {
resp["RevenuePathStatistics"] = []any{}
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// ---- GetRevenueStatisticsSummary ---------------------------------------------------
@@ -385,7 +387,7 @@ type getRevenueStatisticsSummaryRequest struct {
// ("zero monetized traffic observed") rather than a fabricated dollar figure.
func (h *Handler) handleGetRevenueStatisticsSummary(body []byte) ([]byte, error) {
var req getRevenueStatisticsSummaryRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -414,7 +416,7 @@ func (h *Handler) handleGetRevenueStatisticsSummary(body []byte) ([]byte, error)
"UnverifiedAmount": "0",
"VerifiedAmount": "0",
},
- })
+ }, jsonx.V1())
}
// ---- GetRevenueStatisticsTimeSeries -------------------------------------------------
@@ -487,7 +489,7 @@ func validateGetRevenueStatisticsTimeSeriesRequest(req getRevenueStatisticsTimeS
// time-series intervals, so there is nothing genuine to aggregate.
func (h *Handler) handleGetRevenueStatisticsTimeSeries(body []byte) ([]byte, error) {
var req getRevenueStatisticsTimeSeriesRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -511,7 +513,7 @@ func (h *Handler) handleGetRevenueStatisticsTimeSeries(body []byte) ([]byte, err
return nil, err
}
- return json.Marshal(map[string]any{"DataPoints": []any{}})
+ return json.Marshal(map[string]any{"DataPoints": []any{}}, jsonx.V1())
}
// ---- ListSettlementRecords ----------------------------------------------------------
@@ -565,7 +567,7 @@ func validateListSettlementRecordsRequest(req listSettlementRecordsRequest) erro
// TransactionId, or Amount.
func (h *Handler) handleListSettlementRecords(body []byte) ([]byte, error) {
var req listSettlementRecordsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -589,7 +591,7 @@ func (h *Handler) handleListSettlementRecords(body []byte) ([]byte, error) {
return nil, err
}
- return json.Marshal(map[string]any{"Settlements": []any{}})
+ return json.Marshal(map[string]any{"Settlements": []any{}}, jsonx.V1())
}
// revenueStatisticsDispatchOps returns the AI-bot monetization reporting operation
diff --git a/services/wafv2/handler_rule_groups.go b/services/wafv2/handler_rule_groups.go
index f8da2e8bea..1f00f19c80 100644
--- a/services/wafv2/handler_rule_groups.go
+++ b/services/wafv2/handler_rule_groups.go
@@ -2,9 +2,11 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -17,7 +19,7 @@ type checkCapacityRequest struct {
func (h *Handler) handleCheckCapacity(ctx context.Context, body []byte) ([]byte, error) {
var req checkCapacityRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -32,7 +34,7 @@ func (h *Handler) handleCheckCapacity(ctx context.Context, body []byte) ([]byte,
return json.Marshal(map[string]any{
"Capacity": capacity,
- })
+ }, jsonx.V1())
}
// createRuleGroupRequest is the request body for CreateRuleGroup.
@@ -40,9 +42,9 @@ type createRuleGroupRequest struct {
Name string `json:"Name"`
Scope string `json:"Scope"`
Description string `json:"Description"`
- VisibilityConfig json.RawMessage `json:"VisibilityConfig"`
- CustomResponseBodies json.RawMessage `json:"CustomResponseBodies"`
- MonetizationConfig json.RawMessage `json:"MonetizationConfig"`
+ VisibilityConfig jsontext.Value `json:"VisibilityConfig"`
+ CustomResponseBodies jsontext.Value `json:"CustomResponseBodies"`
+ MonetizationConfig jsontext.Value `json:"MonetizationConfig"`
Rules []map[string]any `json:"Rules"`
Tags []tags.KV `json:"Tags"`
Capacity int64 `json:"Capacity"`
@@ -50,7 +52,7 @@ type createRuleGroupRequest struct {
func (h *Handler) handleCreateRuleGroup(ctx context.Context, body []byte) ([]byte, error) {
var req createRuleGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -121,7 +123,7 @@ func (h *Handler) handleCreateRuleGroup(ctx context.Context, body []byte) ([]byt
keyLockToken: rg.LockToken,
keyDescription: rg.Description,
},
- })
+ }, jsonx.V1())
}
// getRuleGroupRequest is the request body for GetRuleGroup.
@@ -134,7 +136,7 @@ type getRuleGroupRequest struct {
func (h *Handler) handleGetRuleGroup(ctx context.Context, body []byte) ([]byte, error) {
var req getRuleGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -171,7 +173,7 @@ func (h *Handler) handleGetRuleGroup(ctx context.Context, body []byte) ([]byte,
}
arnStr := rg.ARN
- visConfig := parseVisibilityConfig(json.RawMessage(rg.VisibilityConfig), rg.Name)
+ visConfig := parseVisibilityConfig(jsontext.Value(rg.VisibilityConfig), rg.Name)
// LabelNamespace grammar ("awswaf::rulegroup::") confirmed via
@@ -194,14 +196,14 @@ func (h *Handler) handleGetRuleGroup(ctx context.Context, body []byte) ([]byte,
if len(rg.CustomResponseBodies) > 0 {
var crb any
- if json.Unmarshal(rg.CustomResponseBodies, &crb) == nil {
+ if json.Unmarshal(rg.CustomResponseBodies, &crb, jsonx.V1()) == nil {
ruleGroupMap["CustomResponseBodies"] = crb
}
}
if len(rg.MonetizationConfig) > 0 {
var mc any
- if json.Unmarshal(rg.MonetizationConfig, &mc) == nil {
+ if json.Unmarshal(rg.MonetizationConfig, &mc, jsonx.V1()) == nil {
ruleGroupMap["MonetizationConfig"] = mc
}
}
@@ -209,7 +211,7 @@ func (h *Handler) handleGetRuleGroup(ctx context.Context, body []byte) ([]byte,
return json.Marshal(map[string]any{
"RuleGroup": ruleGroupMap,
keyLockToken: rg.LockToken,
- })
+ }, jsonx.V1())
}
func (h *Handler) handleListRuleGroups(ctx context.Context, body []byte) ([]byte, error) {
@@ -238,15 +240,15 @@ type updateRuleGroupRequest struct {
Scope string `json:"Scope"`
LockToken string `json:"LockToken"`
Description string `json:"Description"`
- VisibilityConfig json.RawMessage `json:"VisibilityConfig"`
- CustomResponseBodies json.RawMessage `json:"CustomResponseBodies"`
- MonetizationConfig json.RawMessage `json:"MonetizationConfig"`
+ VisibilityConfig jsontext.Value `json:"VisibilityConfig"`
+ CustomResponseBodies jsontext.Value `json:"CustomResponseBodies"`
+ MonetizationConfig jsontext.Value `json:"MonetizationConfig"`
Rules []map[string]any `json:"Rules"`
}
func (h *Handler) handleUpdateRuleGroup(ctx context.Context, body []byte) ([]byte, error) {
var req updateRuleGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -287,7 +289,7 @@ func (h *Handler) handleUpdateRuleGroup(ctx context.Context, body []byte) ([]byt
log := logger.Load(ctx)
log.InfoContext(ctx, "wafv2: updated rule group", "id", req.ID)
- return json.Marshal(map[string]string{keyNextLockToken: rg.LockToken})
+ return json.Marshal(map[string]string{keyNextLockToken: rg.LockToken}, jsonx.V1())
}
// deleteRuleGroupRequest is the request body for DeleteRuleGroup.
@@ -300,7 +302,7 @@ type deleteRuleGroupRequest struct {
func (h *Handler) handleDeleteRuleGroup(ctx context.Context, body []byte) ([]byte, error) {
var req deleteRuleGroupRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/wafv2/handler_rule_groups_test.go b/services/wafv2/handler_rule_groups_test.go
index 5fb68a5aa3..7caf3fc0ae 100644
--- a/services/wafv2/handler_rule_groups_test.go
+++ b/services/wafv2/handler_rule_groups_test.go
@@ -2,13 +2,14 @@ package wafv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/wafv2"
)
@@ -56,7 +57,7 @@ func TestHandler_CheckCapacity(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
consumed, ok := result["Capacity"].(float64)
require.True(t, ok)
assert.GreaterOrEqual(t, int(consumed), tt.wantMinCap)
@@ -106,7 +107,7 @@ func TestHandler_CreateRuleGroup(t *testing.T) {
if tt.wantID {
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
summary, ok := result["Summary"].(map[string]any)
require.True(t, ok)
assert.NotEmpty(t, summary["Id"])
@@ -136,7 +137,7 @@ func TestHandler_CreateRuleGroup_Duplicate(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec2.Code)
var result map[string]string
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &result, jsonx.V1()))
assert.Equal(t, "WAFDuplicateItemException", result["__type"])
}
@@ -190,7 +191,7 @@ func TestHandler_DeleteFirewallManagerRuleGroups(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var result map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
assert.NotEmpty(t, result["NextWebACLLockToken"])
}
})
@@ -266,7 +267,7 @@ func TestDeleteRuleGroupReferencedByWebACL(t *testing.T) {
require.Equal(t, http.StatusOK, rgRec.Code)
var rgResp map[string]any
- require.NoError(t, json.Unmarshal(rgRec.Body.Bytes(), &rgResp))
+ require.NoError(t, json.Unmarshal(rgRec.Body.Bytes(), &rgResp, jsonx.V1()))
summary := rgResp["Summary"].(map[string]any)
rgID := summary["Id"].(string)
rgARN := summary["ARN"].(string)
@@ -304,7 +305,7 @@ func TestDeleteRuleGroupReferencedByWebACL(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, delRec.Code)
var delResp map[string]any
- require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &delResp))
+ require.NoError(t, json.Unmarshal(delRec.Body.Bytes(), &delResp, jsonx.V1()))
assert.Equal(t, "WAFAssociatedItemException", delResp["__type"])
}
@@ -323,7 +324,7 @@ func TestRuleGroupScopeValidationOnGet(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Summary"].(map[string]any)["Id"].(string)
// Get with wrong scope should fail.
@@ -369,7 +370,7 @@ func TestRuleGroup_UpdateRules(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
rgID := createResp["Summary"].(map[string]any)["Id"].(string)
rgLock := createResp["Summary"].(map[string]any)["LockToken"].(string)
@@ -411,7 +412,7 @@ func TestRuleGroup_UpdateRules(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
rules := getResp["RuleGroup"].(map[string]any)["Rules"].([]any)
assert.Len(t, rules, 2)
}
@@ -437,7 +438,7 @@ func TestListRuleGroups_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, page1Rec.Code)
var page1Resp map[string]any
- require.NoError(t, json.Unmarshal(page1Rec.Body.Bytes(), &page1Resp))
+ require.NoError(t, json.Unmarshal(page1Rec.Body.Bytes(), &page1Resp, jsonx.V1()))
groups1, _ := page1Resp["RuleGroups"].([]any)
assert.Len(t, groups1, 3)
@@ -453,7 +454,7 @@ func TestListRuleGroups_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, page2Rec.Code)
var page2Resp map[string]any
- require.NoError(t, json.Unmarshal(page2Rec.Body.Bytes(), &page2Resp))
+ require.NoError(t, json.Unmarshal(page2Rec.Body.Bytes(), &page2Resp, jsonx.V1()))
groups2, _ := page2Resp["RuleGroups"].([]any)
assert.Len(t, groups2, 2)
@@ -476,7 +477,7 @@ func createRuleGroupHelper(t *testing.T, h *wafv2.Handler, name string) (string,
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summary, ok := resp["Summary"].(map[string]any)
require.True(t, ok)
@@ -541,7 +542,7 @@ func TestHandler_GetRuleGroup(t *testing.T) {
if tt.wantField != "" {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, tt.wantField)
}
})
@@ -599,7 +600,7 @@ func TestHandler_ListRuleGroups(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, ok := resp["RuleGroups"].([]any)
require.True(t, ok)
@@ -666,7 +667,7 @@ func TestHandler_UpdateRuleGroup(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "NextLockToken")
}
})
@@ -764,7 +765,7 @@ func TestCheckCapacity_EdgeCases(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
consumed, ok := result["Capacity"].(float64)
require.True(t, ok)
@@ -787,7 +788,7 @@ func TestCheckCapacity_NilRules(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
capacity, _ := resp["Capacity"].(float64)
assert.InDelta(t, float64(0), capacity, 0, "nil rules capacity should be 0")
@@ -798,7 +799,7 @@ func TestCheckCapacity_NilRules(t *testing.T) {
})
require.Equal(t, http.StatusOK, rec.Code)
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
capacity, _ = resp["Capacity"].(float64)
assert.InDelta(t, float64(0), capacity, 0, "empty rules capacity should be 0")
}
diff --git a/services/wafv2/handler_tags.go b/services/wafv2/handler_tags.go
index 1a49b25505..efd9a72ece 100644
--- a/services/wafv2/handler_tags.go
+++ b/services/wafv2/handler_tags.go
@@ -2,9 +2,10 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
@@ -22,7 +23,7 @@ type tagResourceRequest struct {
func (h *Handler) handleTagResource(ctx context.Context, body []byte) ([]byte, error) {
var req tagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -51,7 +52,7 @@ type listTagsForResourceRequest struct {
func (h *Handler) handleListTagsForResource(ctx context.Context, body []byte) ([]byte, error) {
var req listTagsForResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -78,7 +79,7 @@ func (h *Handler) handleListTagsForResource(ctx context.Context, body []byte) ([
resp["NextMarker"] = pg.Next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// untagResourceRequest is the request body for UntagResource.
@@ -89,7 +90,7 @@ type untagResourceRequest struct {
func (h *Handler) handleUntagResource(ctx context.Context, body []byte) ([]byte, error) {
var req untagResourceRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
diff --git a/services/wafv2/handler_tags_test.go b/services/wafv2/handler_tags_test.go
index 135ada616d..88eb7bfa67 100644
--- a/services/wafv2/handler_tags_test.go
+++ b/services/wafv2/handler_tags_test.go
@@ -2,7 +2,7 @@ package wafv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/wafv2"
)
@@ -55,7 +56,7 @@ func TestHandler_TagResource_and_ListTags(t *testing.T) {
assert.Equal(t, http.StatusOK, listRec.Code)
var result map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &result, jsonx.V1()))
info, ok := result["TagInfoForResource"].(map[string]any)
require.True(t, ok)
@@ -188,7 +189,7 @@ func TestHandler_TagResource_Errors(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var result map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
assert.Equal(t, tt.wantType, result["__type"])
})
}
@@ -228,7 +229,7 @@ func TestHandler_ListTagsForResource_Errors(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var result map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
assert.Equal(t, tt.wantType, result["__type"])
})
}
@@ -264,7 +265,7 @@ func TestTagValidation(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
arnStr := createResp["Summary"].(map[string]any)["ARN"].(string)
// More than 50 tags should fail.
@@ -280,7 +281,7 @@ func TestTagValidation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, tooManyRec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(tooManyRec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(tooManyRec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFTagOperationException", errResp["__type"])
// Reserved prefix "aws:" should fail.
@@ -291,7 +292,7 @@ func TestTagValidation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, reservedRec.Code)
var reservedErr map[string]any
- require.NoError(t, json.Unmarshal(reservedRec.Body.Bytes(), &reservedErr))
+ require.NoError(t, json.Unmarshal(reservedRec.Body.Bytes(), &reservedErr, jsonx.V1()))
assert.Equal(t, "WAFTagOperationException", reservedErr["__type"])
}
@@ -324,7 +325,7 @@ func TestHandler_TagResource_RegexPatternSet(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tagInfo, ok := resp["TagInfoForResource"].(map[string]any)
require.True(t, ok)
tagList, ok := tagInfo["TagList"].([]any)
@@ -390,7 +391,7 @@ func TestHandler_ListTagsForResource_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, firstRec.Code, firstRec.Body.String())
var first map[string]any
- require.NoError(t, json.Unmarshal(firstRec.Body.Bytes(), &first))
+ require.NoError(t, json.Unmarshal(firstRec.Body.Bytes(), &first, jsonx.V1()))
firstInfo, ok := first["TagInfoForResource"].(map[string]any)
require.True(t, ok)
@@ -411,7 +412,7 @@ func TestHandler_ListTagsForResource_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, secondRec.Code, secondRec.Body.String())
var second map[string]any
- require.NoError(t, json.Unmarshal(secondRec.Body.Bytes(), &second))
+ require.NoError(t, json.Unmarshal(secondRec.Body.Bytes(), &second, jsonx.V1()))
secondInfo, ok := second["TagInfoForResource"].(map[string]any)
require.True(t, ok)
@@ -428,7 +429,7 @@ func TestHandler_ListTagsForResource_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, allRec.Code)
var all map[string]any
- require.NoError(t, json.Unmarshal(allRec.Body.Bytes(), &all))
+ require.NoError(t, json.Unmarshal(allRec.Body.Bytes(), &all, jsonx.V1()))
allInfo, ok := all["TagInfoForResource"].(map[string]any)
require.True(t, ok)
allList, ok := allInfo["TagList"].([]any)
diff --git a/services/wafv2/handler_test.go b/services/wafv2/handler_test.go
index d8c576b6d8..d4132c7673 100644
--- a/services/wafv2/handler_test.go
+++ b/services/wafv2/handler_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/services/wafv2"
)
@@ -37,7 +38,7 @@ func doWafv2Request(
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -133,7 +134,7 @@ func TestHandler_UnknownOperation(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var result map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
assert.Equal(t, "WAFInvalidOperationException", result["__type"])
}
@@ -261,7 +262,7 @@ func TestLockTokenEnforcement(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, recBad.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(recBad.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(recBad.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFOptimisticLockException", errResp["__type"])
// Update with correct lock token should succeed.
@@ -388,7 +389,7 @@ func TestLockTokenRequired_MissingTokenRejected(t *testing.T) {
)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "WAFInvalidParameterException", resp["__type"])
})
}
@@ -413,7 +414,7 @@ func TestVisibilityConfigMissingMetricName(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "WAFInvalidParameterException", resp["__type"])
}
@@ -445,7 +446,7 @@ func TestListPagination(t *testing.T) {
require.Equal(t, http.StatusOK, page1Rec.Code)
var page1Resp map[string]any
- require.NoError(t, json.Unmarshal(page1Rec.Body.Bytes(), &page1Resp))
+ require.NoError(t, json.Unmarshal(page1Rec.Body.Bytes(), &page1Resp, jsonx.V1()))
acls1, ok := page1Resp["WebACLs"].([]any)
require.True(t, ok)
@@ -469,7 +470,7 @@ func TestListPagination(t *testing.T) {
require.Equal(t, http.StatusOK, page2Rec.Code)
var page2Resp map[string]any
- require.NoError(t, json.Unmarshal(page2Rec.Body.Bytes(), &page2Resp))
+ require.NoError(t, json.Unmarshal(page2Rec.Body.Bytes(), &page2Resp, jsonx.V1()))
acls2, ok := page2Resp["WebACLs"].([]any)
require.True(t, ok)
@@ -496,7 +497,7 @@ func TestScopeValidationOnGet(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Summary"].(map[string]any)["Id"].(string)
// Get with wrong scope should fail.
@@ -571,7 +572,7 @@ func TestHandler_Reset(t *testing.T) {
// Verify it exists.
rec := doWafv2Request(t, h, "ListWebACLs", map[string]any{"Scope": "REGIONAL"})
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ := resp["WebACLs"].([]any)
assert.Len(t, items, 1)
@@ -580,7 +581,7 @@ func TestHandler_Reset(t *testing.T) {
// Verify it's gone.
rec = doWafv2Request(t, h, "ListWebACLs", map[string]any{"Scope": "REGIONAL"})
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
items, _ = resp["WebACLs"].([]any)
assert.Empty(t, items)
}
@@ -727,7 +728,7 @@ func TestValidation_ScopeMismatch(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code, "scope mismatch should return 400")
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFNonexistentItemException", errResp["__type"])
})
diff --git a/services/wafv2/handler_web_acls.go b/services/wafv2/handler_web_acls.go
index b181635a55..4b98d6d4e0 100644
--- a/services/wafv2/handler_web_acls.go
+++ b/services/wafv2/handler_web_acls.go
@@ -2,25 +2,27 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/tags"
)
// createWebACLRequest is the request body for CreateWebACL.
type createWebACLRequest struct {
- DefaultAction json.RawMessage `json:"DefaultAction"`
- VisibilityConfig json.RawMessage `json:"VisibilityConfig"`
- CustomResponseBodies json.RawMessage `json:"CustomResponseBodies"`
- AssociationConfig json.RawMessage `json:"AssociationConfig"`
- CaptchaConfig json.RawMessage `json:"CaptchaConfig"`
- ChallengeConfig json.RawMessage `json:"ChallengeConfig"`
- MonetizationConfig json.RawMessage `json:"MonetizationConfig"`
- DataProtectionConfig json.RawMessage `json:"DataProtectionConfig"`
- ApplicationConfig json.RawMessage `json:"ApplicationConfig"`
- OnSourceDDoSProtectionConfig json.RawMessage `json:"OnSourceDDoSProtectionConfig"`
+ DefaultAction jsontext.Value `json:"DefaultAction"`
+ VisibilityConfig jsontext.Value `json:"VisibilityConfig"`
+ CustomResponseBodies jsontext.Value `json:"CustomResponseBodies"`
+ AssociationConfig jsontext.Value `json:"AssociationConfig"`
+ CaptchaConfig jsontext.Value `json:"CaptchaConfig"`
+ ChallengeConfig jsontext.Value `json:"ChallengeConfig"`
+ MonetizationConfig jsontext.Value `json:"MonetizationConfig"`
+ DataProtectionConfig jsontext.Value `json:"DataProtectionConfig"`
+ ApplicationConfig jsontext.Value `json:"ApplicationConfig"`
+ OnSourceDDoSProtectionConfig jsontext.Value `json:"OnSourceDDoSProtectionConfig"`
Name string `json:"Name"`
Scope string `json:"Scope"`
Description string `json:"Description"`
@@ -31,7 +33,7 @@ type createWebACLRequest struct {
func (h *Handler) handleCreateWebACL(ctx context.Context, body []byte) ([]byte, error) {
var req createWebACLRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -108,17 +110,17 @@ func (h *Handler) handleCreateWebACL(ctx context.Context, body []byte) ([]byte,
keyLockToken: w.LockToken,
keyDescription: w.Description,
},
- })
+ }, jsonx.V1())
}
// validateDefaultAction validates that exactly one of Allow/Block is set in DefaultAction.
-func validateDefaultAction(raw json.RawMessage) error {
+func validateDefaultAction(raw jsontext.Value) error {
if len(raw) == 0 {
return nil
}
- var m map[string]json.RawMessage
- if err := json.Unmarshal(raw, &m); err != nil {
+ var m map[string]jsontext.Value
+ if err := json.Unmarshal(raw, &m, jsonx.V1()); err != nil {
return fmt.Errorf("%w: invalid DefaultAction JSON: %w", errInvalidRequest, err)
}
@@ -142,7 +144,7 @@ type getWebACLRequest struct {
func (h *Handler) handleGetWebACL(ctx context.Context, body []byte) ([]byte, error) {
var req getWebACLRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -196,11 +198,11 @@ func (h *Handler) marshalWebACL(ctx context.Context, w *WebACL) ([]byte, error)
defaultActionJSON := w.DefaultAction
if len(defaultActionJSON) == 0 {
- defaultActionJSON = json.RawMessage(`{"Allow":{}}`)
+ defaultActionJSON = jsontext.Value(`{"Allow":{}}`)
}
var defaultActionMap any
- if err := json.Unmarshal(defaultActionJSON, &defaultActionMap); err != nil {
+ if err := json.Unmarshal(defaultActionJSON, &defaultActionMap, jsonx.V1()); err != nil {
defaultActionMap = map[string]any{"Allow": map[string]any{}}
}
@@ -231,7 +233,7 @@ func (h *Handler) marshalWebACL(ctx context.Context, w *WebACL) ([]byte, error)
return json.Marshal(map[string]any{
"WebACL": webACLMap,
keyLockToken: w.LockToken,
- })
+ }, jsonx.V1())
}
// addOptionalWebACLConfigs unmarshals each opaque config field on w that is
@@ -240,7 +242,7 @@ func (h *Handler) marshalWebACL(ctx context.Context, w *WebACL) ([]byte, error)
func addOptionalWebACLConfigs(webACLMap map[string]any, w *WebACL) {
configs := []struct {
key string
- raw json.RawMessage
+ raw jsontext.Value
}{
{"CustomResponseBodies", w.CustomResponseBodies},
{"AssociationConfig", w.AssociationConfig},
@@ -258,7 +260,7 @@ func addOptionalWebACLConfigs(webACLMap map[string]any, w *WebACL) {
}
var v any
- if json.Unmarshal(c.raw, &v) == nil {
+ if json.Unmarshal(c.raw, &v, jsonx.V1()) == nil {
webACLMap[c.key] = v
}
}
@@ -266,16 +268,16 @@ func addOptionalWebACLConfigs(webACLMap map[string]any, w *WebACL) {
// updateWebACLRequest is the request body for UpdateWebACL.
type updateWebACLRequest struct {
- DefaultAction json.RawMessage `json:"DefaultAction"`
- VisibilityConfig json.RawMessage `json:"VisibilityConfig"`
- CustomResponseBodies json.RawMessage `json:"CustomResponseBodies"`
- AssociationConfig json.RawMessage `json:"AssociationConfig"`
- CaptchaConfig json.RawMessage `json:"CaptchaConfig"`
- ChallengeConfig json.RawMessage `json:"ChallengeConfig"`
- MonetizationConfig json.RawMessage `json:"MonetizationConfig"`
- DataProtectionConfig json.RawMessage `json:"DataProtectionConfig"`
- ApplicationConfig json.RawMessage `json:"ApplicationConfig"`
- OnSourceDDoSProtectionConfig json.RawMessage `json:"OnSourceDDoSProtectionConfig"`
+ DefaultAction jsontext.Value `json:"DefaultAction"`
+ VisibilityConfig jsontext.Value `json:"VisibilityConfig"`
+ CustomResponseBodies jsontext.Value `json:"CustomResponseBodies"`
+ AssociationConfig jsontext.Value `json:"AssociationConfig"`
+ CaptchaConfig jsontext.Value `json:"CaptchaConfig"`
+ ChallengeConfig jsontext.Value `json:"ChallengeConfig"`
+ MonetizationConfig jsontext.Value `json:"MonetizationConfig"`
+ DataProtectionConfig jsontext.Value `json:"DataProtectionConfig"`
+ ApplicationConfig jsontext.Value `json:"ApplicationConfig"`
+ OnSourceDDoSProtectionConfig jsontext.Value `json:"OnSourceDDoSProtectionConfig"`
ID string `json:"Id"`
Name string `json:"Name"`
Scope string `json:"Scope"`
@@ -287,7 +289,7 @@ type updateWebACLRequest struct {
func (h *Handler) handleUpdateWebACL(ctx context.Context, body []byte) ([]byte, error) {
var req updateWebACLRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -346,7 +348,7 @@ func (h *Handler) handleUpdateWebACL(ctx context.Context, body []byte) ([]byte,
return json.Marshal(map[string]string{
keyNextLockToken: w.LockToken,
- })
+ }, jsonx.V1())
}
// deleteWebACLRequest is the request body for DeleteWebACL.
@@ -359,7 +361,7 @@ type deleteWebACLRequest struct {
func (h *Handler) handleDeleteWebACL(ctx context.Context, body []byte) ([]byte, error) {
var req deleteWebACLRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -405,7 +407,7 @@ type deleteFirewallManagerRuleGroupsRequest struct {
func (h *Handler) handleDeleteFirewallManagerRuleGroups(ctx context.Context, body []byte) ([]byte, error) {
var req deleteFirewallManagerRuleGroupsRequest
- if err := json.Unmarshal(body, &req); err != nil {
+ if err := json.Unmarshal(body, &req, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: %w", errInvalidRequest, err)
}
@@ -427,7 +429,7 @@ func (h *Handler) handleDeleteFirewallManagerRuleGroups(ctx context.Context, bod
return json.Marshal(map[string]string{
"NextWebACLLockToken": w.LockToken,
- })
+ }, jsonx.V1())
}
// webACLDispatchOps returns the WebACL-family operation dispatch entries. Each entry is a
diff --git a/services/wafv2/handler_web_acls_test.go b/services/wafv2/handler_web_acls_test.go
index b4492609ea..50828f0b09 100644
--- a/services/wafv2/handler_web_acls_test.go
+++ b/services/wafv2/handler_web_acls_test.go
@@ -2,13 +2,14 @@ package wafv2_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/wafv2"
)
@@ -57,7 +58,7 @@ func TestHandler_CreateWebACL(t *testing.T) {
if tt.wantID {
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
summary, ok := result["Summary"].(map[string]any)
require.True(t, ok)
assert.NotEmpty(t, summary["Id"])
@@ -162,7 +163,7 @@ func TestHandler_UpdateWebACL(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var result map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
assert.NotEmpty(t, result["NextLockToken"])
}
})
@@ -248,7 +249,7 @@ func TestHandler_ListWebACLs(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
list, ok := result["WebACLs"].([]any)
require.True(t, ok)
@@ -275,7 +276,7 @@ func TestHandler_CreateWebACL_Duplicate(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec2.Code)
var result map[string]string
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &result, jsonx.V1()))
assert.Equal(t, "WAFDuplicateItemException", result["__type"])
}
@@ -287,7 +288,7 @@ func TestHandler_GetWebACL_MissingID(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var result map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
assert.Equal(t, "WAFInvalidParameterException", result["__type"])
}
@@ -362,7 +363,7 @@ func TestHandler_ListWebACLs_Scope_Filter(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
list, ok := result["WebACLs"].([]any)
require.True(t, ok)
assert.Len(t, list, tt.wantCount)
@@ -408,7 +409,7 @@ func TestHandler_GetWebACL_BlockDefaultAction(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
webACL, ok := result["WebACL"].(map[string]any)
require.True(t, ok)
defaultAction, ok := webACL["DefaultAction"].(map[string]any)
@@ -443,7 +444,7 @@ func TestHandler_GetWebACL_WithVisibilityConfig(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResult map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResult))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResult, jsonx.V1()))
summary := createResult["Summary"].(map[string]any)
id := summary["Id"].(string)
@@ -455,7 +456,7 @@ func TestHandler_GetWebACL_WithVisibilityConfig(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var result map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &result, jsonx.V1()))
webACL, ok := result["WebACL"].(map[string]any)
require.True(t, ok)
vis, ok := webACL["VisibilityConfig"].(map[string]any)
@@ -497,7 +498,7 @@ func createWebACLWithRules(t *testing.T, h *wafv2.Handler, name, scope string) (
require.Equal(t, http.StatusOK, rec.Code, "createWebACLWithRules: %s", rec.Body.String())
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summary := resp["Summary"].(map[string]any)
return summary["Id"].(string), summary["LockToken"].(string)
@@ -517,7 +518,7 @@ func TestWebACLRulesRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
webACL := resp["WebACL"].(map[string]any)
rules, ok := webACL["Rules"].([]any)
@@ -548,14 +549,14 @@ func TestDefaultActionRawJSONRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, recAllow.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(recAllow.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(recAllow.Body.Bytes(), &createResp, jsonx.V1()))
idAllow := createResp["Summary"].(map[string]any)["Id"].(string)
recGet := doWafv2Request(t, h, "GetWebACL", map[string]any{"Id": idAllow})
require.Equal(t, http.StatusOK, recGet.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(recGet.Body.Bytes(), &getResp, jsonx.V1()))
da := getResp["WebACL"].(map[string]any)["DefaultAction"].(map[string]any)
_, hasAllow := da["Allow"]
assert.True(t, hasAllow, "DefaultAction should contain Allow key")
@@ -572,14 +573,14 @@ func TestDefaultActionRawJSONRoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, recBlock.Code)
var createRespBlock map[string]any
- require.NoError(t, json.Unmarshal(recBlock.Body.Bytes(), &createRespBlock))
+ require.NoError(t, json.Unmarshal(recBlock.Body.Bytes(), &createRespBlock, jsonx.V1()))
idBlock := createRespBlock["Summary"].(map[string]any)["Id"].(string)
recGetBlock := doWafv2Request(t, h, "GetWebACL", map[string]any{"Id": idBlock})
require.Equal(t, http.StatusOK, recGetBlock.Code)
var getRespBlock map[string]any
- require.NoError(t, json.Unmarshal(recGetBlock.Body.Bytes(), &getRespBlock))
+ require.NoError(t, json.Unmarshal(recGetBlock.Body.Bytes(), &getRespBlock, jsonx.V1()))
daBlock := getRespBlock["WebACL"].(map[string]any)["DefaultAction"].(map[string]any)
_, hasBlock := daBlock["Block"]
assert.True(t, hasBlock, "DefaultAction should contain Block key")
@@ -616,14 +617,14 @@ func TestWebACLExtendedFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Summary"].(map[string]any)["Id"].(string)
getRec := doWafv2Request(t, h, "GetWebACL", map[string]any{"Id": id})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
webACL := getResp["WebACL"].(map[string]any)
// TokenDomains should be round-tripped.
@@ -676,7 +677,7 @@ func TestCaptchaAction_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "Captcha action: %s", rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Summary"].(map[string]any)["Id"].(string)
// Verify round-trip.
@@ -684,7 +685,7 @@ func TestCaptchaAction_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
rules := getResp["WebACL"].(map[string]any)["Rules"].([]any)
require.Len(t, rules, 1)
@@ -724,14 +725,14 @@ func TestChallengeAction_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "Challenge action: %s", rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Summary"].(map[string]any)["Id"].(string)
getRec := doWafv2Request(t, h, "GetWebACL", map[string]any{"Id": id})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
rules := getResp["WebACL"].(map[string]any)["Rules"].([]any)
require.Len(t, rules, 1)
@@ -761,14 +762,14 @@ func TestChallengeConfig_WebACLLevel(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Summary"].(map[string]any)["Id"].(string)
getRec := doWafv2Request(t, h, "GetWebACL", map[string]any{"Id": id})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
webACL := getResp["WebACL"].(map[string]any)
challengeCfg, ok := webACL["ChallengeConfig"].(map[string]any)
@@ -809,14 +810,14 @@ func TestLabelMatchStatement_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "LabelMatchStatement: %s", rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Summary"].(map[string]any)["Id"].(string)
getRec := doWafv2Request(t, h, "GetWebACL", map[string]any{"Id": id})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
rules := getResp["WebACL"].(map[string]any)["Rules"].([]any)
require.Len(t, rules, 1)
@@ -887,14 +888,14 @@ func TestAssociationConfig_RequestBodyDecryption(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "AssociationConfig: %s", rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Summary"].(map[string]any)["Id"].(string)
getRec := doWafv2Request(t, h, "GetWebACL", map[string]any{"Id": id})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
webACL := getResp["WebACL"].(map[string]any)
assocCfg, ok := webACL["AssociationConfig"].(map[string]any)
@@ -1020,14 +1021,14 @@ func TestWebACL_CustomResponseBodies_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "CustomResponseBodies: %s", rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Summary"].(map[string]any)["Id"].(string)
getRec := doWafv2Request(t, h, "GetWebACL", map[string]any{"Id": id})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
webACL := getResp["WebACL"].(map[string]any)
crb, ok := webACL["CustomResponseBodies"].(map[string]any)
@@ -1120,14 +1121,14 @@ func TestWebACL_AllActionTypes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "all action types: %s", rec.Body.String())
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
id := createResp["Summary"].(map[string]any)["Id"].(string)
getRec := doWafv2Request(t, h, "GetWebACL", map[string]any{"Id": id})
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
rules := getResp["WebACL"].(map[string]any)["Rules"].([]any)
assert.Len(t, rules, 6, "all 6 rules should round-trip")
}
@@ -1155,7 +1156,7 @@ func TestWebACL_Update_ClearsOldRules(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
rules := resp["WebACL"].(map[string]any)["Rules"].([]any)
assert.Empty(t, rules, "rules should be empty after clearing")
}
@@ -1171,7 +1172,7 @@ func createWebACLHelper(t *testing.T, h *wafv2.Handler, name, scope string) (str
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summary, ok := resp["Summary"].(map[string]any)
require.True(t, ok)
@@ -1230,7 +1231,7 @@ func TestHandler_DeleteWebACL_CascadeLogging(t *testing.T) {
// Delete the WebACL.
var createResp map[string]any
rec2 := doWafv2Request(t, h, "ListWebACLs", map[string]any{"Scope": "REGIONAL"})
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &createResp, jsonx.V1()))
webACLs, _ := createResp["WebACLs"].([]any)
require.Len(t, webACLs, 1)
webACL := webACLs[0].(map[string]any)
@@ -1274,7 +1275,7 @@ func TestDeleteWebACL_FailsWhenAssociated(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFAssociatedItemException", errResp["__type"])
// After disassociation, delete should succeed.
@@ -1325,7 +1326,7 @@ func TestCreateWebACL_DuplicateNameScope(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rec.Code)
var errResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &errResp, jsonx.V1()))
assert.Equal(t, "WAFDuplicateItemException", errResp["__type"])
}
@@ -1348,7 +1349,7 @@ func TestLockToken_RotatesOnUpdate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "update with correct token: %s", rec.Body.String())
var updateResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &updateResp, jsonx.V1()))
token2 := updateResp["NextLockToken"].(string)
assert.NotEmpty(t, token2)
diff --git a/services/wafv2/interfaces.go b/services/wafv2/interfaces.go
index ad6001944e..c68778a745 100644
--- a/services/wafv2/interfaces.go
+++ b/services/wafv2/interfaces.go
@@ -2,7 +2,7 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
)
// StorageBackend is the interface for WAFv2 storage operations.
@@ -16,11 +16,11 @@ type StorageBackend interface {
CreateWebACL(
ctx context.Context,
name, scope, description string,
- defaultAction, visibilityConfig json.RawMessage,
+ defaultAction, visibilityConfig jsontext.Value,
rules []map[string]any,
tokenDomains []string,
- customResponseBodies, associationConfig, captchaConfig, challengeConfig json.RawMessage,
- monetizationConfig, dataProtectionConfig, applicationConfig, onSourceDDoSProtectionConfig json.RawMessage,
+ customResponseBodies, associationConfig, captchaConfig, challengeConfig jsontext.Value,
+ monetizationConfig, dataProtectionConfig, applicationConfig, onSourceDDoSProtectionConfig jsontext.Value,
tags map[string]string,
) (*WebACL, error)
GetWebACL(ctx context.Context, id string) (*WebACL, error)
@@ -28,11 +28,11 @@ type StorageBackend interface {
UpdateWebACL(
ctx context.Context,
id, description, lockToken string,
- defaultAction, visibilityConfig json.RawMessage,
+ defaultAction, visibilityConfig jsontext.Value,
rules []map[string]any,
tokenDomains []string,
- customResponseBodies, associationConfig, captchaConfig, challengeConfig json.RawMessage,
- monetizationConfig, dataProtectionConfig, applicationConfig, onSourceDDoSProtectionConfig json.RawMessage,
+ customResponseBodies, associationConfig, captchaConfig, challengeConfig jsontext.Value,
+ monetizationConfig, dataProtectionConfig, applicationConfig, onSourceDDoSProtectionConfig jsontext.Value,
) (*WebACL, error)
DeleteWebACL(ctx context.Context, id, lockToken string) error
ListWebACLs(ctx context.Context) []*WebACL
@@ -73,7 +73,7 @@ type StorageBackend interface {
name, scope, description, visibilityConfig string,
capacity int64,
rules []map[string]any,
- customResponseBodies, monetizationConfig json.RawMessage,
+ customResponseBodies, monetizationConfig jsontext.Value,
tags map[string]string,
) (*RuleGroup, error)
GetRuleGroup(ctx context.Context, id string) (*RuleGroup, error)
@@ -83,15 +83,15 @@ type StorageBackend interface {
ctx context.Context,
id, description, visibilityConfig, lockToken string,
rules []map[string]any,
- customResponseBodies, monetizationConfig json.RawMessage,
+ customResponseBodies, monetizationConfig jsontext.Value,
) (*RuleGroup, error)
DeleteRuleGroup(ctx context.Context, id, lockToken string) error
DeleteAPIKey(ctx context.Context, scope, apiKey string) error
DeleteFirewallManagerRuleGroups(ctx context.Context, webACLARN, lockToken string) (*WebACL, error)
- PutLoggingConfiguration(ctx context.Context, resourceARN string, configJSON json.RawMessage) error
+ PutLoggingConfiguration(ctx context.Context, resourceARN string, configJSON jsontext.Value) error
DeleteLoggingConfiguration(ctx context.Context, resourceARN, logScope string) error
- GetLoggingConfiguration(ctx context.Context, resourceARN, logScope string) (json.RawMessage, error)
- ListLoggingConfigurations(ctx context.Context, scope string) []json.RawMessage
+ GetLoggingConfiguration(ctx context.Context, resourceARN, logScope string) (jsontext.Value, error)
+ ListLoggingConfigurations(ctx context.Context, scope string) []jsontext.Value
DeletePermissionPolicy(ctx context.Context, resourceARN string) error
DeleteRegexPatternSet(ctx context.Context, id, lockToken string) error
ListAPIKeys(ctx context.Context, scope string) []*APIKey
diff --git a/services/wafv2/list_summary_shapes_test.go b/services/wafv2/list_summary_shapes_test.go
index 04619e9336..e14f30cdc0 100644
--- a/services/wafv2/list_summary_shapes_test.go
+++ b/services/wafv2/list_summary_shapes_test.go
@@ -1,7 +1,7 @@
package wafv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -9,6 +9,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/wafv2/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestListAPIKeys_SummaryShape locks that ListAPIKeys' APIKeySummary items
@@ -32,7 +34,7 @@ func TestListAPIKeys_SummaryShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["APIKeySummaries"].([]any)
require.True(t, ok)
require.Len(t, summaries, 1)
@@ -58,7 +60,7 @@ func TestGetDecryptedAPIKey_SummaryShape(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
apiKey, ok := createResp["APIKey"].(string)
require.True(t, ok)
@@ -69,7 +71,7 @@ func TestGetDecryptedAPIKey_SummaryShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotContains(t, resp, "Scope", "GetDecryptedAPIKeyOutput has no Scope member")
assert.Contains(t, resp, "TokenDomains")
assert.Contains(t, resp, "CreationTimestamp")
diff --git a/services/wafv2/logging_config.go b/services/wafv2/logging_config.go
index a90af8a895..9d7494247d 100644
--- a/services/wafv2/logging_config.go
+++ b/services/wafv2/logging_config.go
@@ -2,22 +2,25 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"sort"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// PutLoggingConfiguration stores a full logging configuration JSON for the given resource ARN.
func (b *InMemoryBackend) PutLoggingConfiguration(
ctx context.Context,
resourceARN string,
- configJSON json.RawMessage,
+ configJSON jsontext.Value,
) error {
b.mu.Lock("PutLoggingConfiguration")
defer b.mu.Unlock()
region := getRegion(ctx, b.region)
- stored := make(json.RawMessage, len(configJSON))
+ stored := make(jsontext.Value, len(configJSON))
copy(stored, configJSON)
b.loggingConfigsStore(region)[resourceARN] = stored
@@ -50,7 +53,7 @@ func (b *InMemoryBackend) DeleteLoggingConfiguration(ctx context.Context, resour
func (b *InMemoryBackend) GetLoggingConfiguration(
ctx context.Context,
resourceARN, logScope string,
-) (json.RawMessage, error) {
+) (jsontext.Value, error) {
b.mu.RLock("GetLoggingConfiguration")
defer b.mu.RUnlock()
@@ -65,7 +68,7 @@ func (b *InMemoryBackend) GetLoggingConfiguration(
)
}
- out := make(json.RawMessage, len(cfg))
+ out := make(jsontext.Value, len(cfg))
copy(out, cfg)
return out, nil
@@ -73,7 +76,7 @@ func (b *InMemoryBackend) GetLoggingConfiguration(
// loggingConfigInScope reports whether cfg's LogScope (default CUSTOMER when the
// document omits it) matches wantScope ("" matches the default, CUSTOMER).
-func loggingConfigInScope(cfg json.RawMessage, wantScope string) bool {
+func loggingConfigInScope(cfg jsontext.Value, wantScope string) bool {
if wantScope == "" {
wantScope = defaultLogScope
}
@@ -82,7 +85,7 @@ func loggingConfigInScope(cfg json.RawMessage, wantScope string) bool {
LogScope string `json:"LogScope"`
}
- if err := json.Unmarshal(cfg, &doc); err != nil {
+ if err := json.Unmarshal(cfg, &doc, jsonx.V1()); err != nil {
return false
}
@@ -97,7 +100,7 @@ func loggingConfigInScope(cfg json.RawMessage, wantScope string) bool {
// ListLoggingConfigurations returns all stored logging configuration JSONs for the
// given scope (REGIONAL configs live under the request's actual region; CLOUDFRONT
// configs are global and stored under the "" region key -- see arnRegionForScope).
-func (b *InMemoryBackend) ListLoggingConfigurations(ctx context.Context, scope string) []json.RawMessage {
+func (b *InMemoryBackend) ListLoggingConfigurations(ctx context.Context, scope string) []jsontext.Value {
b.mu.RLock("ListLoggingConfigurations")
defer b.mu.RUnlock()
@@ -111,11 +114,11 @@ func (b *InMemoryBackend) ListLoggingConfigurations(ctx context.Context, scope s
sort.Strings(arns)
- result := make([]json.RawMessage, 0, len(arns))
+ result := make([]jsontext.Value, 0, len(arns))
for _, arn := range arns {
cfg := regionMap[arn]
- out := make(json.RawMessage, len(cfg))
+ out := make(jsontext.Value, len(cfg))
copy(out, cfg)
result = append(result, out)
}
diff --git a/services/wafv2/models.go b/services/wafv2/models.go
index 6566ae452a..3f6ef3f80d 100644
--- a/services/wafv2/models.go
+++ b/services/wafv2/models.go
@@ -1,6 +1,8 @@
package wafv2
-import "encoding/json"
+import (
+ "encoding/json/jsontext"
+)
const (
// ScopeRegional is the REGIONAL WAFv2 scope.
@@ -53,16 +55,16 @@ type VisibilityConfig struct {
type WebACL struct {
Tags map[string]string `json:"tags,omitempty"`
ARN string `json:"arn,omitempty"`
- DefaultAction json.RawMessage `json:"defaultAction,omitempty"`
- VisibilityConfig json.RawMessage `json:"visibilityConfig,omitempty"`
- CustomResponseBodies json.RawMessage `json:"customResponseBodies,omitempty"`
- AssociationConfig json.RawMessage `json:"associationConfig,omitempty"`
- CaptchaConfig json.RawMessage `json:"captchaConfig,omitempty"`
- ChallengeConfig json.RawMessage `json:"challengeConfig,omitempty"`
- MonetizationConfig json.RawMessage `json:"monetizationConfig,omitempty"`
- DataProtectionConfig json.RawMessage `json:"dataProtectionConfig,omitempty"`
- ApplicationConfig json.RawMessage `json:"applicationConfig,omitempty"`
- OnSourceDDoSProtectionConfig json.RawMessage `json:"onSourceDDoSProtectionConfig,omitempty"`
+ DefaultAction jsontext.Value `json:"defaultAction,omitempty"`
+ VisibilityConfig jsontext.Value `json:"visibilityConfig,omitempty"`
+ CustomResponseBodies jsontext.Value `json:"customResponseBodies,omitempty"`
+ AssociationConfig jsontext.Value `json:"associationConfig,omitempty"`
+ CaptchaConfig jsontext.Value `json:"captchaConfig,omitempty"`
+ ChallengeConfig jsontext.Value `json:"challengeConfig,omitempty"`
+ MonetizationConfig jsontext.Value `json:"monetizationConfig,omitempty"`
+ DataProtectionConfig jsontext.Value `json:"dataProtectionConfig,omitempty"`
+ ApplicationConfig jsontext.Value `json:"applicationConfig,omitempty"`
+ OnSourceDDoSProtectionConfig jsontext.Value `json:"onSourceDDoSProtectionConfig,omitempty"`
ID string `json:"id"`
Name string `json:"name"`
Scope string `json:"scope"`
@@ -106,8 +108,8 @@ type RuleGroup struct {
Scope string `json:"scope"`
Description string `json:"description"`
VisibilityConfig string `json:"visibilityConfig"`
- CustomResponseBodies json.RawMessage `json:"customResponseBodies,omitempty"`
- MonetizationConfig json.RawMessage `json:"monetizationConfig,omitempty"`
+ CustomResponseBodies jsontext.Value `json:"customResponseBodies,omitempty"`
+ MonetizationConfig jsontext.Value `json:"monetizationConfig,omitempty"`
LockToken string `json:"lockToken"`
Rules []map[string]any `json:"rules,omitempty"`
Capacity int64 `json:"capacity"`
diff --git a/services/wafv2/multi_region_test.go b/services/wafv2/multi_region_test.go
index 689da195d5..e7675c80de 100644
--- a/services/wafv2/multi_region_test.go
+++ b/services/wafv2/multi_region_test.go
@@ -1,7 +1,7 @@
package wafv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/wafv2"
)
@@ -30,7 +31,7 @@ func regionCall(t *testing.T, h *wafv2.Handler, region, target, body string) map
require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
out := map[string]any{}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
return out
}
diff --git a/services/wafv2/persistence.go b/services/wafv2/persistence.go
index a2b1199870..37f067a9de 100644
--- a/services/wafv2/persistence.go
+++ b/services/wafv2/persistence.go
@@ -2,7 +2,7 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -74,13 +74,13 @@ func newDirtyDTORegistry() (
// they do not fit store.Table's keyed-by-identity-value shape and are
// unaffected by this conversion.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
- LoggingConfigs map[string]map[string]json.RawMessage `json:"loggingConfigs,omitempty"`
- PermissionPolicies map[string]map[string]string `json:"permissionPolicies,omitempty"`
- Associations map[string]map[string]string `json:"associations,omitempty"`
- AccountID string `json:"accountID"`
- Region string `json:"region"`
- Version int `json:"version"`
+ Tables map[string]jsontext.Value `json:"tables"`
+ LoggingConfigs map[string]map[string]jsontext.Value `json:"loggingConfigs,omitempty"`
+ PermissionPolicies map[string]map[string]string `json:"permissionPolicies,omitempty"`
+ Associations map[string]map[string]string `json:"associations,omitempty"`
+ AccountID string `json:"accountID"`
+ Region string `json:"region"`
+ Version int `json:"version"`
}
// Snapshot serializes the backend state to JSON.
@@ -150,7 +150,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
"gotVersion", snap.Version, "wantVersion", wafv2SnapshotVersion)
b.resetTablesLocked()
- b.loggingConfigs = make(map[string]map[string]json.RawMessage)
+ b.loggingConfigs = make(map[string]map[string]jsontext.Value)
b.permissionPolicies = make(map[string]map[string]string)
b.associations = make(map[string]map[string]string)
@@ -189,7 +189,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
if snap.LoggingConfigs != nil {
b.loggingConfigs = snap.LoggingConfigs
} else {
- b.loggingConfigs = make(map[string]map[string]json.RawMessage)
+ b.loggingConfigs = make(map[string]map[string]jsontext.Value)
}
if snap.PermissionPolicies != nil {
diff --git a/services/wafv2/realism_test.go b/services/wafv2/realism_test.go
index 9b66992da9..166a9548ac 100644
--- a/services/wafv2/realism_test.go
+++ b/services/wafv2/realism_test.go
@@ -1,7 +1,7 @@
package wafv2_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func decodeError(t *testing.T, body []byte) (string, string) {
@@ -18,7 +20,7 @@ func decodeError(t *testing.T, body []byte) (string, string) {
Type string `json:"__type"`
Message string `json:"message"`
}
- require.NoError(t, json.Unmarshal(body, &out))
+ require.NoError(t, json.Unmarshal(body, &out, jsonx.V1()))
return out.Type, out.Message
}
@@ -115,7 +117,7 @@ func TestLoggingDestinationNaming(t *testing.T) {
ARN string `json:"ARN"`
} `json:"Summary"`
}
- require.NoError(t, json.Unmarshal(acl.Body.Bytes(), &created))
+ require.NoError(t, json.Unmarshal(acl.Body.Bytes(), &created, jsonx.V1()))
rec := doWafv2Request(t, h, "PutLoggingConfiguration", map[string]any{
"LoggingConfiguration": map[string]any{
diff --git a/services/wafv2/rule_groups.go b/services/wafv2/rule_groups.go
index 9b160a6869..f1d9cc766c 100644
--- a/services/wafv2/rule_groups.go
+++ b/services/wafv2/rule_groups.go
@@ -2,7 +2,8 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"sort"
@@ -10,6 +11,7 @@ import (
"github.com/google/uuid"
"github.com/blackbirdworks/gopherstack/pkgs/arn"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (b *InMemoryBackend) buildRuleGroupARN(name, id, scope, region string) string {
@@ -46,7 +48,7 @@ func (b *InMemoryBackend) CreateRuleGroup(
name, scope, description, visibilityConfig string,
capacity int64,
rules []map[string]any,
- customResponseBodies, monetizationConfig json.RawMessage,
+ customResponseBodies, monetizationConfig jsontext.Value,
tags map[string]string,
) (*RuleGroup, error) {
b.mu.Lock("CreateRuleGroup")
@@ -197,7 +199,7 @@ func (b *InMemoryBackend) UpdateRuleGroup(
ctx context.Context,
id, description, visibilityConfig, lockToken string,
rules []map[string]any,
- customResponseBodies, monetizationConfig json.RawMessage,
+ customResponseBodies, monetizationConfig jsontext.Value,
) (*RuleGroup, error) {
b.mu.Lock("UpdateRuleGroup")
defer b.mu.Unlock()
@@ -242,13 +244,13 @@ func cloneRuleGroup(rg *RuleGroup) *RuleGroup {
cp.Rules = cloneRules(rg.Rules)
if rg.CustomResponseBodies != nil {
- crb := make(json.RawMessage, len(rg.CustomResponseBodies))
+ crb := make(jsontext.Value, len(rg.CustomResponseBodies))
copy(crb, rg.CustomResponseBodies)
cp.CustomResponseBodies = crb
}
if rg.MonetizationConfig != nil {
- mc := make(json.RawMessage, len(rg.MonetizationConfig))
+ mc := make(jsontext.Value, len(rg.MonetizationConfig))
copy(mc, rg.MonetizationConfig)
cp.MonetizationConfig = mc
}
@@ -278,13 +280,13 @@ func cloneRules(rules []map[string]any) []map[string]any {
return []map[string]any{}
}
- data, marshalErr := json.Marshal(rules)
+ data, marshalErr := json.Marshal(rules, jsonx.V1())
if marshalErr != nil {
return shallowCopyRules(rules)
}
var out []map[string]any
- if unmarshalErr := json.Unmarshal(data, &out); unmarshalErr != nil {
+ if unmarshalErr := json.Unmarshal(data, &out, jsonx.V1()); unmarshalErr != nil {
return shallowCopyRules(rules)
}
diff --git a/services/wafv2/store.go b/services/wafv2/store.go
index 2610c92402..514a861a0f 100644
--- a/services/wafv2/store.go
+++ b/services/wafv2/store.go
@@ -2,13 +2,15 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"maps"
"regexp"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/lockmetrics"
"github.com/blackbirdworks/gopherstack/pkgs/store"
)
@@ -128,7 +130,7 @@ type InMemoryBackend struct {
managedRuleSetsByRegion *store.Index[ManagedRuleSet]
apiKeys *store.Table[APIKey]
apiKeysByRegion *store.Index[APIKey]
- loggingConfigs map[string]map[string]json.RawMessage
+ loggingConfigs map[string]map[string]jsontext.Value
permissionPolicies map[string]map[string]string
associations map[string]map[string]string
mu *lockmetrics.RWMutex
@@ -161,7 +163,7 @@ func initRegion[V any](m map[string]map[string]V, region string) map[string]V {
func NewInMemoryBackend(accountID, region string) *InMemoryBackend {
b := &InMemoryBackend{
registry: store.NewRegistry(),
- loggingConfigs: make(map[string]map[string]json.RawMessage),
+ loggingConfigs: make(map[string]map[string]jsontext.Value),
permissionPolicies: make(map[string]map[string]string),
associations: make(map[string]map[string]string),
accountID: accountID,
@@ -173,7 +175,7 @@ func NewInMemoryBackend(accountID, region string) *InMemoryBackend {
return b
}
-func (b *InMemoryBackend) loggingConfigsStore(region string) map[string]json.RawMessage {
+func (b *InMemoryBackend) loggingConfigsStore(region string) map[string]jsontext.Value {
return initRegion(b.loggingConfigs, region)
}
func (b *InMemoryBackend) permissionPoliciesStore(region string) map[string]string {
@@ -219,13 +221,13 @@ func nameScope(name, scope string) string {
}
// validateVisibilityConfig parses and validates a VisibilityConfig JSON blob.
-func validateVisibilityConfig(raw json.RawMessage) error {
+func validateVisibilityConfig(raw jsontext.Value) error {
if len(raw) == 0 {
return nil
}
var vc VisibilityConfig
- if err := json.Unmarshal(raw, &vc); err != nil {
+ if err := json.Unmarshal(raw, &vc, jsonx.V1()); err != nil {
return fmt.Errorf("%w: invalid VisibilityConfig: %w", errInvalidRequest, err)
}
@@ -243,7 +245,7 @@ func toInt64(v any) (int64, bool) {
return int64(n), true
case float64:
return int64(n), true
- case json.Number:
+ case jsonx.Number:
i, err := n.Int64()
return i, err == nil
@@ -275,7 +277,7 @@ func (b *InMemoryBackend) Reset() {
defer b.mu.Unlock()
b.resetTablesLocked()
- b.loggingConfigs = make(map[string]map[string]json.RawMessage)
+ b.loggingConfigs = make(map[string]map[string]jsontext.Value)
b.permissionPolicies = make(map[string]map[string]string)
b.associations = make(map[string]map[string]string)
}
diff --git a/services/wafv2/web_acls.go b/services/wafv2/web_acls.go
index c95468ebfa..1250d0f7fc 100644
--- a/services/wafv2/web_acls.go
+++ b/services/wafv2/web_acls.go
@@ -2,7 +2,7 @@ package wafv2
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
"sort"
@@ -29,11 +29,11 @@ func (b *InMemoryBackend) WebACLARN(name, id, scope string) string {
func (b *InMemoryBackend) CreateWebACL(
ctx context.Context,
name, scope, description string,
- defaultAction, visibilityConfig json.RawMessage,
+ defaultAction, visibilityConfig jsontext.Value,
rules []map[string]any,
tokenDomains []string,
- customResponseBodies, associationConfig, captchaConfig, challengeConfig json.RawMessage,
- monetizationConfig, dataProtectionConfig, applicationConfig, onSourceDDoSProtectionConfig json.RawMessage,
+ customResponseBodies, associationConfig, captchaConfig, challengeConfig jsontext.Value,
+ monetizationConfig, dataProtectionConfig, applicationConfig, onSourceDDoSProtectionConfig jsontext.Value,
tags map[string]string,
) (*WebACL, error) {
b.mu.Lock("CreateWebACL")
@@ -134,11 +134,11 @@ func (b *InMemoryBackend) GetWebACLByARN(ctx context.Context, webACLARN string)
func (b *InMemoryBackend) UpdateWebACL(
ctx context.Context,
id, description, lockToken string,
- defaultAction, visibilityConfig json.RawMessage,
+ defaultAction, visibilityConfig jsontext.Value,
rules []map[string]any,
tokenDomains []string,
- customResponseBodies, associationConfig, captchaConfig, challengeConfig json.RawMessage,
- monetizationConfig, dataProtectionConfig, applicationConfig, onSourceDDoSProtectionConfig json.RawMessage,
+ customResponseBodies, associationConfig, captchaConfig, challengeConfig jsontext.Value,
+ monetizationConfig, dataProtectionConfig, applicationConfig, onSourceDDoSProtectionConfig jsontext.Value,
) (*WebACL, error) {
b.mu.Lock("UpdateWebACL")
defer b.mu.Unlock()
@@ -195,8 +195,8 @@ func (b *InMemoryBackend) UpdateWebACL(
// Extracted from UpdateWebACL to keep its cyclomatic complexity down.
func applyOptionalWebACLConfigs(
w *WebACL,
- customResponseBodies, associationConfig, captchaConfig, challengeConfig json.RawMessage,
- monetizationConfig, dataProtectionConfig, applicationConfig, onSourceDDoSProtectionConfig json.RawMessage,
+ customResponseBodies, associationConfig, captchaConfig, challengeConfig jsontext.Value,
+ monetizationConfig, dataProtectionConfig, applicationConfig, onSourceDDoSProtectionConfig jsontext.Value,
) {
if len(customResponseBodies) > 0 {
w.CustomResponseBodies = customResponseBodies
@@ -302,61 +302,61 @@ func cloneWebACL(w *WebACL) *WebACL {
// Clone RawMessage fields (byte slices).
if w.DefaultAction != nil {
- da := make(json.RawMessage, len(w.DefaultAction))
+ da := make(jsontext.Value, len(w.DefaultAction))
copy(da, w.DefaultAction)
cp.DefaultAction = da
}
if w.VisibilityConfig != nil {
- vc := make(json.RawMessage, len(w.VisibilityConfig))
+ vc := make(jsontext.Value, len(w.VisibilityConfig))
copy(vc, w.VisibilityConfig)
cp.VisibilityConfig = vc
}
if w.CustomResponseBodies != nil {
- crb := make(json.RawMessage, len(w.CustomResponseBodies))
+ crb := make(jsontext.Value, len(w.CustomResponseBodies))
copy(crb, w.CustomResponseBodies)
cp.CustomResponseBodies = crb
}
if w.AssociationConfig != nil {
- ac := make(json.RawMessage, len(w.AssociationConfig))
+ ac := make(jsontext.Value, len(w.AssociationConfig))
copy(ac, w.AssociationConfig)
cp.AssociationConfig = ac
}
if w.CaptchaConfig != nil {
- cc := make(json.RawMessage, len(w.CaptchaConfig))
+ cc := make(jsontext.Value, len(w.CaptchaConfig))
copy(cc, w.CaptchaConfig)
cp.CaptchaConfig = cc
}
if w.ChallengeConfig != nil {
- chc := make(json.RawMessage, len(w.ChallengeConfig))
+ chc := make(jsontext.Value, len(w.ChallengeConfig))
copy(chc, w.ChallengeConfig)
cp.ChallengeConfig = chc
}
if w.MonetizationConfig != nil {
- mc := make(json.RawMessage, len(w.MonetizationConfig))
+ mc := make(jsontext.Value, len(w.MonetizationConfig))
copy(mc, w.MonetizationConfig)
cp.MonetizationConfig = mc
}
if w.DataProtectionConfig != nil {
- dpc := make(json.RawMessage, len(w.DataProtectionConfig))
+ dpc := make(jsontext.Value, len(w.DataProtectionConfig))
copy(dpc, w.DataProtectionConfig)
cp.DataProtectionConfig = dpc
}
if w.ApplicationConfig != nil {
- ac := make(json.RawMessage, len(w.ApplicationConfig))
+ ac := make(jsontext.Value, len(w.ApplicationConfig))
copy(ac, w.ApplicationConfig)
cp.ApplicationConfig = ac
}
if w.OnSourceDDoSProtectionConfig != nil {
- osdc := make(json.RawMessage, len(w.OnSourceDDoSProtectionConfig))
+ osdc := make(jsontext.Value, len(w.OnSourceDDoSProtectionConfig))
copy(osdc, w.OnSourceDDoSProtectionConfig)
cp.OnSourceDDoSProtectionConfig = osdc
}
diff --git a/services/workmail/handler.go b/services/workmail/handler.go
index 50bea99fc4..fb9a5d10b9 100644
--- a/services/workmail/handler.go
+++ b/services/workmail/handler.go
@@ -2,7 +2,7 @@ package workmail
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"maps"
"net/http"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/idempotency"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
)
@@ -116,7 +117,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) precheck(action string, body []byte) error {
@@ -178,7 +179,7 @@ func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err
}
payload, marshalErr := json.Marshal(
- service.JSONErrorResponse{Type: code, Message: strings.TrimPrefix(err.Error(), code+": ")},
+ service.JSONErrorResponse{Type: code, Message: strings.TrimPrefix(err.Error(), code+": ")}, jsonx.V1(),
)
if marshalErr != nil {
return c.String(http.StatusInternalServerError, "internal server error")
diff --git a/services/workmail/handler_access_control_test.go b/services/workmail/handler_access_control_test.go
index d92d35a184..4736eea1a9 100644
--- a/services/workmail/handler_access_control_test.go
+++ b/services/workmail/handler_access_control_test.go
@@ -1,7 +1,7 @@
package workmail_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/workmail"
)
@@ -124,7 +125,7 @@ func TestGetAccessControlEffect_RuleEvaluation(t *testing.T) {
body["Name"] = name
body["Effect"] = effect
- jsonBody, err := json.Marshal(body)
+ jsonBody, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
rec := doOp(t, h, "PutAccessControlRule", string(jsonBody))
diff --git a/services/workmail/handler_availability_config.go b/services/workmail/handler_availability_config.go
index acec865643..050445a5af 100644
--- a/services/workmail/handler_availability_config.go
+++ b/services/workmail/handler_availability_config.go
@@ -2,8 +2,11 @@ package workmail
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- Availability Configurations ----
@@ -117,12 +120,12 @@ type listAvailabilityConfigsReq struct {
}
type availabilityConfigJSON struct {
- EwsProvider *json.RawMessage `json:"EwsProvider,omitempty"`
- LambdaProvider *json.RawMessage `json:"LambdaProvider,omitempty"`
- DomainName string `json:"DomainName"`
- ProviderType string `json:"ProviderType"`
- DateCreated int64 `json:"DateCreated"`
- DateModified int64 `json:"DateModified"`
+ EwsProvider *jsontext.Value `json:"EwsProvider,omitempty"`
+ LambdaProvider *jsontext.Value `json:"LambdaProvider,omitempty"`
+ DomainName string `json:"DomainName"`
+ ProviderType string `json:"ProviderType"`
+ DateCreated int64 `json:"DateCreated"`
+ DateModified int64 `json:"DateModified"`
}
type listAvailabilityConfigsResp struct {
@@ -153,12 +156,12 @@ func (h *Handler) handleListAvailabilityConfigurations(
raw, _ := json.Marshal(map[string]string{
"EwsEndpoint": c.EwsEndpoint,
"EwsUsername": c.EwsUsername,
- })
- rm := json.RawMessage(raw)
+ }, jsonx.V1())
+ rm := jsontext.Value(raw)
item.EwsProvider = &rm
} else {
- raw, _ := json.Marshal(map[string]string{"LambdaArn": c.LambdaARN})
- rm := json.RawMessage(raw)
+ raw, _ := json.Marshal(map[string]string{"LambdaArn": c.LambdaARN}, jsonx.V1())
+ rm := jsontext.Value(raw)
item.LambdaProvider = &rm
}
result = append(result, item)
diff --git a/services/workmail/handler_impersonation_test.go b/services/workmail/handler_impersonation_test.go
index 7e98058a5c..362d852300 100644
--- a/services/workmail/handler_impersonation_test.go
+++ b/services/workmail/handler_impersonation_test.go
@@ -1,7 +1,7 @@
package workmail_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/workmail"
)
@@ -167,7 +168,7 @@ func TestGetImpersonationRoleEffect(t *testing.T) {
h := newTestHandler(t)
orgID := createTestOrg(t, h, "imp-effect-org")
- targetJSON, _ := json.Marshal(tc.targetUsers)
+ targetJSON, _ := json.Marshal(tc.targetUsers, jsonx.V1())
roleBody := fmt.Sprintf(
`{"OrganizationId":%q,"Name":"test-role","Type":"FULL_ACCESS","Rules":[{"ImpersonationRuleId":"rule-1","Name":"rule-one","Effect":%q,"TargetUsers":%s}]}`, //nolint:lll // existing issue.
orgID,
diff --git a/services/workmail/handler_mobile_device_access_test.go b/services/workmail/handler_mobile_device_access_test.go
index 4b5805cdf3..b149f4ba37 100644
--- a/services/workmail/handler_mobile_device_access_test.go
+++ b/services/workmail/handler_mobile_device_access_test.go
@@ -1,13 +1,15 @@
package workmail_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---- Mobile Device Access Rules ----
@@ -35,11 +37,11 @@ func TestMobileDeviceAccessRuleLifecycle(t *testing.T) {
body := fmt.Sprintf(`{"OrganizationId":%q,"Name":%q,"Effect":%q,"Description":%q`,
orgID, tc.name, tc.effect, tc.description)
if len(tc.deviceModels) > 0 {
- modelsJSON, _ := json.Marshal(tc.deviceModels)
+ modelsJSON, _ := json.Marshal(tc.deviceModels, jsonx.V1())
body += fmt.Sprintf(`,"DeviceModels":%s`, string(modelsJSON))
}
if len(tc.deviceTypes) > 0 {
- typesJSON, _ := json.Marshal(tc.deviceTypes)
+ typesJSON, _ := json.Marshal(tc.deviceTypes, jsonx.V1())
body += fmt.Sprintf(`,"DeviceTypes":%s`, string(typesJSON))
}
body += "}"
@@ -163,7 +165,7 @@ func TestGetMobileDeviceAccessEffect(t *testing.T) {
h := newTestHandler(t)
orgID := createTestOrg(t, h, "effect-org-"+tc.name)
- modelsJSON, _ := json.Marshal(tc.deviceModels)
+ modelsJSON, _ := json.Marshal(tc.deviceModels, jsonx.V1())
doOp(t, h, "CreateMobileDeviceAccessRule", fmt.Sprintf(
`{"OrganizationId":%q,"Name":"test-rule","Effect":%q,"DeviceModels":%s}`,
orgID, tc.ruleEffect, string(modelsJSON),
diff --git a/services/workmail/handler_test.go b/services/workmail/handler_test.go
index ee212344e8..c549f819c9 100644
--- a/services/workmail/handler_test.go
+++ b/services/workmail/handler_test.go
@@ -1,7 +1,7 @@
package workmail_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/workmail"
)
@@ -41,7 +42,7 @@ func decodeJSON(t *testing.T, rec *httptest.ResponseRecorder) map[string]any {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &m, jsonx.V1()))
return m
}
@@ -190,7 +191,7 @@ func TestWorkMail_Protocol_GetRoot_Returns_OpList(t *testing.T) {
require.NoError(t, h.Handler()(c))
assert.Equal(t, http.StatusOK, rec.Code)
var ops []string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &ops, jsonx.V1()))
assert.NotEmpty(t, ops)
}
diff --git a/services/workmail/lifecycle.go b/services/workmail/lifecycle.go
index 8b8b71a48f..15865fb44f 100644
--- a/services/workmail/lifecycle.go
+++ b/services/workmail/lifecycle.go
@@ -2,12 +2,14 @@ package workmail
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"regexp"
"strconv"
"strings"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -95,7 +97,7 @@ func validatePageParams(body []byte) error {
NextToken *string `json:"NextToken"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if req.MaxResults != nil && (*req.MaxResults < minPageSize || *req.MaxResults > defaultPageSize) {
return fmt.Errorf("%w: MaxResults must be between %d and %d", ErrValidation, minPageSize, defaultPageSize)
@@ -188,7 +190,7 @@ func organizationIDOf(body []byte) string {
OrganizationID string `json:"OrganizationId"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
return req.OrganizationID
}
diff --git a/services/workmail/persistence.go b/services/workmail/persistence.go
index 4b595a9b45..84b1247c79 100644
--- a/services/workmail/persistence.go
+++ b/services/workmail/persistence.go
@@ -2,7 +2,7 @@ package workmail
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"maps"
@@ -149,7 +149,7 @@ func buildPersistenceDTORegistry() persistenceDTOTables {
// snapshot from an incompatible (older or newer) build of this backend as
// though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
OrgsByAlias map[string]string `json:"orgsByAlias"`
UsersByEmail map[string]map[string]string `json:"usersByEmail"`
GroupsByEmail map[string]map[string]string `json:"groupsByEmail"`
@@ -321,7 +321,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
// restoreResourceTables rebuilds every store.Table on b from snap's
// per-table JSON, factored out of Restore to keep Restore's own cognitive
// complexity low. Callers must hold b.mu.Lock.
-func (b *InMemoryBackend) restoreResourceTables(tables map[string]json.RawMessage) error {
+func (b *InMemoryBackend) restoreResourceTables(tables map[string]jsontext.Value) error {
if err := b.registry.RestoreAll(tables); err != nil {
return fmt.Errorf("workmail: restore clean tables: %w", err)
}
diff --git a/services/workspaces/bundles_test.go b/services/workspaces/bundles_test.go
index 5a2dbcaeee..6e72f19a38 100644
--- a/services/workspaces/bundles_test.go
+++ b/services/workspaces/bundles_test.go
@@ -1,7 +1,7 @@
package workspaces_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/workspaces"
)
@@ -27,7 +28,7 @@ func TestDescribeWorkspaceBundles_PaginatesResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
bundles, _ := resp["Bundles"].([]any)
assert.NotEmpty(t, bundles, "DescribeWorkspaceBundles must return at least one bundle")
@@ -79,7 +80,7 @@ func TestDescribeWorkspaceBundles_FiltersByBundleID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
bundles, _ := resp["Bundles"].([]any)
assert.Len(t, bundles, tc.wantCount)
@@ -95,7 +96,7 @@ func TestDescribeWorkspaceBundles_ComputeTypeAndStorage(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
bundles := resp["Bundles"].([]any)
require.NotEmpty(t, bundles)
@@ -169,7 +170,7 @@ func TestDescribeWorkspaceBundles_ByOwnerAmazon(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
bundles := resp["Bundles"].([]any)
for _, b := range bundles {
@@ -199,7 +200,7 @@ func TestCreateWorkspaceBundle_StoresStorageCapacity(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
bun := resp["WorkspaceBundle"].(map[string]any)
assert.Equal(t, "50", bun["UserStorage"].(map[string]any)["Capacity"])
@@ -222,7 +223,7 @@ func TestDescribeWorkspaceBundles_IncludesCustomBundle(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
customBundleID := createResp["WorkspaceBundle"].(map[string]any)["BundleId"].(string)
// Without owner: only the account's own (custom) bundles.
@@ -230,7 +231,7 @@ func TestDescribeWorkspaceBundles_IncludesCustomBundle(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
bundles := resp["Bundles"].([]any)
found := false
@@ -253,7 +254,7 @@ func TestDescribeWorkspaceBundles_FilterByID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
bundles := resp["Bundles"].([]any)
require.Len(t, bundles, 1)
assert.Equal(t, "wsb-bh8rsxt14", bundles[0].(map[string]any)["BundleId"])
@@ -475,7 +476,7 @@ func TestCreateWorkspaceBundle_UnknownImage_ConsumesNoState(t *testing.T) {
require.Equal(t, http.StatusOK, describeRec.Code)
var before map[string]any
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &before))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &before, jsonx.V1()))
countBefore := len(before["Bundles"].([]any))
rejectedRec := doTargetRequest(
@@ -487,7 +488,7 @@ func TestCreateWorkspaceBundle_UnknownImage_ConsumesNoState(t *testing.T) {
require.Equal(t, http.StatusOK, describeRec2.Code)
var after map[string]any
- require.NoError(t, json.Unmarshal(describeRec2.Body.Bytes(), &after))
+ require.NoError(t, json.Unmarshal(describeRec2.Body.Bytes(), &after, jsonx.V1()))
countAfter := len(after["Bundles"].([]any))
assert.Equal(t, countBefore, countAfter, "rejected create must not add a bundle")
diff --git a/services/workspaces/directories_test.go b/services/workspaces/directories_test.go
index f23186bd39..81ec2ebf42 100644
--- a/services/workspaces/directories_test.go
+++ b/services/workspaces/directories_test.go
@@ -2,13 +2,14 @@ package workspaces_test
import (
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/workspaces"
)
@@ -29,7 +30,7 @@ func TestDescribeWorkspaceDirectories_PaginatesResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dirs, _ := resp["Directories"].([]any)
assert.Len(t, dirs, 2, "both registered directories must be returned")
@@ -42,7 +43,7 @@ func TestDescribeWorkspaceDirectories_PaginatesResults(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
dirs2, _ := resp2["Directories"].([]any)
require.Len(t, dirs2, 1, "cursor should skip d-aaa and return only d-bbb")
@@ -61,7 +62,7 @@ func TestDescribeWorkspaceDirectories_AfterRegister(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dirs := resp["Directories"].([]any)
assert.Empty(t, dirs, "DescribeWorkspaceDirectories must be empty before registration")
@@ -77,7 +78,7 @@ func TestDescribeWorkspaceDirectories_AfterRegister(t *testing.T) {
require.Equal(t, http.StatusOK, rec3.Code)
var resp3 map[string]any
- require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp3))
+ require.NoError(t, json.Unmarshal(rec3.Body.Bytes(), &resp3, jsonx.V1()))
dirs3 := resp3["Directories"].([]any)
require.Len(t, dirs3, 1)
@@ -108,7 +109,7 @@ func TestDescribeWorkspaceDirectories_FilterByID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dirs := resp["Directories"].([]any)
require.Len(t, dirs, 1)
assert.Equal(t, "d-aaaa", dirs[0].(map[string]any)["DirectoryId"])
@@ -131,7 +132,7 @@ func TestDescribeWorkspaceDirectories_AfterDeregister(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
dirs := resp["Directories"].([]any)
assert.Empty(t, dirs, "deregistered directory must not appear")
}
@@ -156,7 +157,7 @@ func TestRegisterWorkspaceDirectory_DuplicateRejected(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec2.Code, "body: %s", rec2.Body)
var errOut map[string]string
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errOut))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errOut, jsonx.V1()))
assert.Equal(t, "ResourceAlreadyExistsException", errOut["__type"])
}
@@ -195,7 +196,7 @@ func TestDeregisterWorkspaceDirectory_BlockedByWorkspaces(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec2.Code, "body: %s", rec2.Body)
var errOut map[string]string
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errOut))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errOut, jsonx.V1()))
assert.Equal(t, "InvalidResourceStateException", errOut["__type"])
// The directory must still be registered -- deregistration was rejected,
diff --git a/services/workspaces/directory_registration.go b/services/workspaces/directory_registration.go
index 1afea6788f..8df52b9e15 100644
--- a/services/workspaces/directory_registration.go
+++ b/services/workspaces/directory_registration.go
@@ -1,12 +1,13 @@
package workspaces
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"slices"
"strings"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -239,7 +240,7 @@ func streamingPropertiesFromDS(ds *storedDirSettings) *StreamingProperties {
}
var sp StreamingProperties
- if err := json.Unmarshal([]byte(raw), &sp); err != nil {
+ if err := json.Unmarshal([]byte(raw), &sp, jsonx.V1()); err != nil {
return nil
}
diff --git a/services/workspaces/handler.go b/services/workspaces/handler.go
index df8a713bdb..ed5f74d649 100644
--- a/services/workspaces/handler.go
+++ b/services/workspaces/handler.go
@@ -2,7 +2,7 @@ package workspaces
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"maps"
"net/http"
@@ -11,6 +11,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/blackbirdworks/gopherstack/pkgs/awserr"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -94,7 +95,7 @@ func (h *Handler) dispatch(ctx context.Context, action string, body []byte) ([]b
return nil, err
}
- return json.Marshal(result)
+ return json.Marshal(result, jsonx.V1())
}
func (h *Handler) handleError(_ context.Context, c *echo.Context, _ string, err error) error {
diff --git a/services/workspaces/handler_test.go b/services/workspaces/handler_test.go
index f0ef791f73..28b054a9f4 100644
--- a/services/workspaces/handler_test.go
+++ b/services/workspaces/handler_test.go
@@ -3,7 +3,7 @@ package workspaces_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strconv"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/workspaces"
)
@@ -59,7 +60,7 @@ func doTargetRequest(
if body != nil {
var marshalErr error
- bodyBytes, marshalErr = json.Marshal(body)
+ bodyBytes, marshalErr = json.Marshal(body, jsonx.V1())
require.NoError(t, marshalErr)
}
@@ -79,7 +80,7 @@ func doTargetRequest(
func decodeJSON(t *testing.T, body []byte, dst any) {
t.Helper()
- if err := json.Unmarshal(body, dst); err != nil {
+ if err := json.Unmarshal(body, dst, jsonx.V1()); err != nil {
t.Fatalf("unmarshal response: %v (body=%s)", err, body)
}
}
@@ -122,7 +123,7 @@ func createWorkspace(t *testing.T, h *workspaces.Handler) string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pending, ok := resp["PendingRequests"].([]any)
require.True(t, ok, "PendingRequests missing from response")
@@ -173,7 +174,7 @@ func createWorkspaceWithRunningMode(t *testing.T, h *workspaces.Handler, running
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pending, ok := resp["PendingRequests"].([]any)
require.True(t, ok, "PendingRequests missing from response")
@@ -204,7 +205,7 @@ func createImage(t *testing.T, h *workspaces.Handler) string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
id, ok := resp["ImageId"].(string)
require.True(t, ok)
@@ -267,7 +268,7 @@ func TestWorkSpaces_Operations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
pending, ok := resp["PendingRequests"].([]any)
require.True(t, ok)
assert.Len(t, pending, 1)
@@ -291,7 +292,7 @@ func TestWorkSpaces_Operations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
wsList, ok := resp["Workspaces"].([]any)
require.True(t, ok)
assert.Len(t, wsList, 1)
@@ -308,7 +309,7 @@ func TestWorkSpaces_Operations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
wsList, ok := resp["Workspaces"].([]any)
require.True(t, ok)
assert.Len(t, wsList, 1)
@@ -329,7 +330,7 @@ func TestWorkSpaces_Operations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
statuses, ok := resp["WorkspacesConnectionStatus"].([]any)
require.True(t, ok)
assert.Len(t, statuses, 1)
@@ -418,7 +419,7 @@ func TestWorkSpaces_Operations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
failures, ok := resp["FailedRequests"].([]any)
require.True(t, ok)
assert.Empty(t, failures)
@@ -438,7 +439,7 @@ func TestWorkSpaces_Operations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
failures, ok := resp["FailedRequests"].([]any)
require.True(t, ok)
assert.Len(t, failures, 1)
@@ -461,7 +462,7 @@ func TestWorkSpaces_Operations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
failures, ok := resp["FailedRequests"].([]any)
require.True(t, ok)
assert.Empty(t, failures)
@@ -478,7 +479,7 @@ func TestWorkSpaces_Operations(t *testing.T) {
require.Equal(t, http.StatusOK, stopRec.Code)
var stopResp map[string]any
- require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopResp))
+ require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopResp, jsonx.V1()))
stopFailures, _ := stopResp["FailedRequests"].([]any)
require.Empty(t, stopFailures, "setup: StopWorkspaces must succeed to reach STOPPED")
@@ -495,7 +496,7 @@ func TestWorkSpaces_Operations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
failures, ok := resp["FailedRequests"].([]any)
require.True(t, ok)
assert.Empty(t, failures)
@@ -518,7 +519,7 @@ func TestWorkSpaces_Operations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
failures, ok := resp["FailedRequests"].([]any)
require.True(t, ok)
assert.Empty(t, failures)
@@ -541,7 +542,7 @@ func TestWorkSpaces_Operations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
failures, ok := resp["FailedRequests"].([]any)
require.True(t, ok)
assert.Empty(t, failures)
@@ -561,7 +562,7 @@ func TestWorkSpaces_Operations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
failures, ok := resp["FailedRequests"].([]any)
require.True(t, ok)
assert.Len(t, failures, 1)
@@ -624,7 +625,7 @@ func TestWorkSpaces_Operations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
tagList, ok := resp["TagList"].([]any)
require.True(t, ok)
assert.Len(t, tagList, 1)
@@ -641,7 +642,7 @@ func TestWorkSpaces_Operations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
bundles, ok := resp["Bundles"].([]any)
require.True(t, ok)
assert.NotEmpty(t, bundles)
@@ -655,7 +656,7 @@ func TestWorkSpaces_Operations(t *testing.T) {
check: func(t *testing.T, respBody []byte) {
t.Helper()
var resp map[string]any
- require.NoError(t, json.Unmarshal(respBody, &resp))
+ require.NoError(t, json.Unmarshal(respBody, &resp, jsonx.V1()))
dirs, ok := resp["Directories"].([]any)
require.True(t, ok)
assert.Empty(t, dirs)
diff --git a/services/workspaces/images_test.go b/services/workspaces/images_test.go
index 2d9b279b1c..988a093e85 100644
--- a/services/workspaces/images_test.go
+++ b/services/workspaces/images_test.go
@@ -1,7 +1,7 @@
package workspaces_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/workspaces"
)
@@ -309,7 +310,7 @@ func TestCreateWorkspaceImage_UnknownWorkspace_ConsumesNoState(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code, rec1.Body.String())
var out1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &out1, jsonx.V1()))
firstID, _ := out1["ImageId"].(string)
require.NotEmpty(t, firstID)
@@ -319,7 +320,7 @@ func TestCreateWorkspaceImage_UnknownWorkspace_ConsumesNoState(t *testing.T) {
var before struct {
Images []map[string]any `json:"Images"`
}
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &before))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &before, jsonx.V1()))
rejectedRec := doTargetRequest(
t, h, "CreateWorkspaceImage", createWorkspaceImageReq("ws-doesnotexist"),
@@ -332,7 +333,7 @@ func TestCreateWorkspaceImage_UnknownWorkspace_ConsumesNoState(t *testing.T) {
var after struct {
Images []map[string]any `json:"Images"`
}
- require.NoError(t, json.Unmarshal(describeRec2.Body.Bytes(), &after))
+ require.NoError(t, json.Unmarshal(describeRec2.Body.Bytes(), &after, jsonx.V1()))
assert.Len(t, after.Images, len(before.Images), "rejected create must not add an image")
@@ -340,7 +341,7 @@ func TestCreateWorkspaceImage_UnknownWorkspace_ConsumesNoState(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code, rec2.Body.String())
var out2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &out2, jsonx.V1()))
secondID, _ := out2["ImageId"].(string)
require.NotEmpty(t, secondID)
@@ -462,7 +463,7 @@ func TestCopyWorkspaceImage_UnknownSourceImage_ConsumesNoState(t *testing.T) {
var before struct {
Images []map[string]any `json:"Images"`
}
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &before))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &before, jsonx.V1()))
rejectedRec := doTargetRequest(
t, h, "CopyWorkspaceImage", createCopyImageReq("wsi-doesnotexist", "us-east-1"),
@@ -475,7 +476,7 @@ func TestCopyWorkspaceImage_UnknownSourceImage_ConsumesNoState(t *testing.T) {
var after struct {
Images []map[string]any `json:"Images"`
}
- require.NoError(t, json.Unmarshal(describeRec2.Body.Bytes(), &after))
+ require.NoError(t, json.Unmarshal(describeRec2.Body.Bytes(), &after, jsonx.V1()))
assert.Len(t, after.Images, len(before.Images), "rejected copy must not add an image")
@@ -577,7 +578,7 @@ func TestCreateUpdatedWorkspaceImage_UnknownSourceImage_ConsumesNoState(t *testi
var before struct {
Images []map[string]any `json:"Images"`
}
- require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &before))
+ require.NoError(t, json.Unmarshal(describeRec.Body.Bytes(), &before, jsonx.V1()))
rejectedRec := doTargetRequest(
t, h, "CreateUpdatedWorkspaceImage", createUpdatedImageReq("wsi-doesnotexist"),
@@ -590,7 +591,7 @@ func TestCreateUpdatedWorkspaceImage_UnknownSourceImage_ConsumesNoState(t *testi
var after struct {
Images []map[string]any `json:"Images"`
}
- require.NoError(t, json.Unmarshal(describeRec2.Body.Bytes(), &after))
+ require.NoError(t, json.Unmarshal(describeRec2.Body.Bytes(), &after, jsonx.V1()))
assert.Len(t, after.Images, len(before.Images), "rejected create must not add an image")
diff --git a/services/workspaces/multi_region_test.go b/services/workspaces/multi_region_test.go
index ee611ec970..db10b976b3 100644
--- a/services/workspaces/multi_region_test.go
+++ b/services/workspaces/multi_region_test.go
@@ -2,7 +2,8 @@ package workspaces_test
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net"
"net/http"
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/service"
svc "github.com/blackbirdworks/gopherstack/services/workspaces"
)
@@ -173,8 +175,8 @@ func TestHandler_MultiRegionPersistence(t *testing.T) {
snap := src.Snapshot(context.Background())
require.NotNil(t, snap)
- var doc map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(snap, &doc))
+ var doc map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(snap, &doc, jsonx.V1()))
if _, ok := doc["regions"]; ok != tc.remote {
t.Fatalf("regions key present = %v, want %v", ok, tc.remote)
diff --git a/services/workspaces/persistence.go b/services/workspaces/persistence.go
index 5d797d95a8..939b48eb49 100644
--- a/services/workspaces/persistence.go
+++ b/services/workspaces/persistence.go
@@ -2,7 +2,7 @@ package workspaces
import (
"context"
- "encoding/json"
+ "encoding/json/jsontext"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -43,7 +43,7 @@ const workspacesSnapshotVersion = 1
// against decoding a snapshot from an incompatible (older or newer) build of
// this backend as though it were the current shape; see Restore.
type backendSnapshot struct {
- Tables map[string]json.RawMessage `json:"tables"`
+ Tables map[string]jsontext.Value `json:"tables"`
Tags map[string]map[string]string `json:"tags"`
ClientProperties map[string]storedClientProps `json:"clientProperties,omitempty"`
ImagePermissions map[string]map[string]bool `json:"imagePermissions,omitempty"`
diff --git a/services/workspaces/pools_test.go b/services/workspaces/pools_test.go
index 23a429f902..1d8f95558d 100644
--- a/services/workspaces/pools_test.go
+++ b/services/workspaces/pools_test.go
@@ -1,7 +1,7 @@
package workspaces_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
@@ -10,6 +10,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/workspaces/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestWorkspacesPool_CapacityStatusAndRunningMode verifies fields that were
@@ -196,7 +198,7 @@ func TestWorkspacesPool_UpdateRunningModeRequiresStopped(t *testing.T) {
if tc.wantStatus == http.StatusBadRequest {
var errOut map[string]string
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errOut))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &errOut, jsonx.V1()))
assert.Equal(t, "InvalidResourceStateException", errOut["__type"])
}
diff --git a/services/workspaces/tags_test.go b/services/workspaces/tags_test.go
index 9c5d5b2537..5eb99df325 100644
--- a/services/workspaces/tags_test.go
+++ b/services/workspaces/tags_test.go
@@ -1,13 +1,16 @@
package workspaces_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -126,7 +129,7 @@ func TestCreateTags_VisibleInDescribeTags(t *testing.T) {
Value string `json:"Value"`
} `json:"TagList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags := make(map[string]string, len(resp.TagList))
for _, tag := range resp.TagList {
@@ -172,7 +175,7 @@ func TestDeleteTags_RemovedFromDescribeTags(t *testing.T) {
Value string `json:"Value"`
} `json:"TagList"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags := make(map[string]string, len(resp.TagList))
for _, tag := range resp.TagList {
@@ -206,9 +209,9 @@ func TestDescribeWorkspaces_NoTagsField(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp struct {
- Workspaces []map[string]json.RawMessage `json:"Workspaces"`
+ Workspaces []map[string]jsontext.Value `json:"Workspaces"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.Workspaces, 1)
assert.NotContains(t, resp.Workspaces[0], "Tags")
}
@@ -238,7 +241,7 @@ func TestCreateWorkspaces_Tags_VisibleInDescribeTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
pending, _ := createResp["PendingRequests"].([]any)
require.Len(t, pending, 1)
wsID := pending[0].(map[string]any)["WorkspaceId"].(string)
@@ -249,7 +252,7 @@ func TestCreateWorkspaces_Tags_VisibleInDescribeTags(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var tagResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &tagResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &tagResp, jsonx.V1()))
tagList, _ := tagResp["TagList"].([]any)
found := false
diff --git a/services/workspaces/workspace_properties.go b/services/workspaces/workspace_properties.go
index 61db4c1579..76c58c118f 100644
--- a/services/workspaces/workspace_properties.go
+++ b/services/workspaces/workspace_properties.go
@@ -1,8 +1,10 @@
package workspaces
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ModifyEndpointEncryptionMode stores the endpoint encryption mode for a
@@ -151,7 +153,7 @@ func (b *InMemoryBackend) ModifyStreamingProperties(directoryID string, props St
merged.GlobalAccelerator = props.GlobalAccelerator
}
- raw, err := json.Marshal(merged)
+ raw, err := json.Marshal(merged, jsonx.V1())
if err != nil {
return fmt.Errorf("encode streaming properties: %w", err)
}
diff --git a/services/workspaces/workspace_properties_test.go b/services/workspaces/workspace_properties_test.go
index 6f32d30f09..7fac31bfef 100644
--- a/services/workspaces/workspace_properties_test.go
+++ b/services/workspaces/workspace_properties_test.go
@@ -1,12 +1,14 @@
package workspaces_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestDirectoryModifyOps exercises the directory-scoped property-modify
@@ -193,7 +195,7 @@ func TestDirectoryModifyOps_RejectsLegacyDirectoryIdKey(t *testing.T) {
"DirectoryId": tc.body["DirectoryId"],
})
- rawBody, marshalErr := json.Marshal(tc.body)
+ rawBody, marshalErr := json.Marshal(tc.body, jsonx.V1())
require.NoError(t, marshalErr)
require.NotContains(
t, string(rawBody), `"ResourceId"`,
diff --git a/services/workspaces/workspaces_lifecycle_test.go b/services/workspaces/workspaces_lifecycle_test.go
index ced7d83a60..183482bf49 100644
--- a/services/workspaces/workspaces_lifecycle_test.go
+++ b/services/workspaces/workspaces_lifecycle_test.go
@@ -1,7 +1,7 @@
package workspaces_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/workspaces"
)
@@ -29,7 +30,7 @@ func TestConnectionStatus_Available_IsDisconnected(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
statuses := resp["WorkspacesConnectionStatus"].([]any)
require.Len(t, statuses, 1)
assert.Equal(t, "DISCONNECTED", statuses[0].(map[string]any)["ConnectionState"],
@@ -48,7 +49,7 @@ func TestConnectionStatus_Stopped_IsNotConnected(t *testing.T) {
require.Equal(t, http.StatusOK, stopRec.Code)
var stopResp map[string]any
- require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopResp))
+ require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopResp, jsonx.V1()))
stopFailures, _ := stopResp["FailedRequests"].([]any)
require.Empty(t, stopFailures, "StopWorkspaces must succeed to reach STOPPED")
@@ -58,7 +59,7 @@ func TestConnectionStatus_Stopped_IsNotConnected(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
statuses := resp["WorkspacesConnectionStatus"].([]any)
require.Len(t, statuses, 1)
assert.Equal(t, "NOT_CONNECTED", statuses[0].(map[string]any)["ConnectionState"],
@@ -80,7 +81,7 @@ func TestConnectionStatus_AllWorkspaces_NoFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
statuses := resp["WorkspacesConnectionStatus"].([]any)
assert.Len(t, statuses, 2, "empty WorkspaceIds must return status for all workspaces")
@@ -103,7 +104,7 @@ func TestConnectionStatus_UnknownID_Excluded(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
statuses := resp["WorkspacesConnectionStatus"].([]any)
assert.Empty(t, statuses, "unknown workspace ID must be silently excluded from status results")
}
@@ -205,7 +206,7 @@ func TestRebootRebuildWorkspaces_StatePrecondition(t *testing.T) {
ErrorMessage string `json:"ErrorMessage"`
} `json:"FailedRequests"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if !tc.wantFailed {
assert.Empty(t, out.FailedRequests)
@@ -247,7 +248,7 @@ func TestRebootWorkspaces_AllowsAvailableAndStopped(t *testing.T) {
require.Equal(t, http.StatusOK, stopRec.Code)
var stopResp map[string]any
- require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopResp))
+ require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopResp, jsonx.V1()))
stopFailures, _ := stopResp["FailedRequests"].([]any)
require.Empty(t, stopFailures, "StopWorkspaces must succeed to reach STOPPED")
}
@@ -262,7 +263,7 @@ func TestRebootWorkspaces_AllowsAvailableAndStopped(t *testing.T) {
ErrorCode string `json:"ErrorCode"`
} `json:"FailedRequests"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
if tc.stop {
require.Len(t, out.FailedRequests, 1)
@@ -292,7 +293,7 @@ func TestMigrateWorkspace_SourceRemovedTargetCreated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// Original workspace must no longer exist.
assert.Empty(t, workspaces.WorkspaceState(backend, srcID),
@@ -375,7 +376,7 @@ func TestDescribeWorkspaces_EmptyList_WhenNoWorkspaces(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wsList := resp["Workspaces"].([]any)
assert.Empty(t, wsList)
@@ -396,7 +397,7 @@ func TestDescribeWorkspaces_MultipleIDs_AllReturned(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wsList := resp["Workspaces"].([]any)
assert.Len(t, wsList, 2)
}
@@ -416,7 +417,7 @@ func TestDescribeWorkspaces_FilterByUserName(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wsList := resp["Workspaces"].([]any)
assert.Len(t, wsList, 2, "UserName filter must return only Alice's workspaces")
@@ -446,7 +447,7 @@ func TestCreateWorkspaces_VolumeEncryption_Propagated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
pending := createResp["PendingRequests"].([]any)
require.Len(t, pending, 1)
@@ -460,7 +461,7 @@ func TestCreateWorkspaces_VolumeEncryption_Propagated(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp, jsonx.V1()))
wsList := descResp["Workspaces"].([]any)
require.Len(t, wsList, 1)
descWs := wsList[0].(map[string]any)
@@ -512,7 +513,7 @@ func TestErrorMessages_AreNotGarbledByExtraFormatArgs(t *testing.T) {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.NotContains(t, resp["message"], "%!(EXTRA",
"error message must not contain a garbled fmt.Sprintf EXTRA marker")
assert.NotEmpty(t, resp["message"])
@@ -565,7 +566,7 @@ func TestCreateWorkspaces_PartialFailure_DoesNotAbortWholeBatch(t *testing.T) {
} `json:"PendingRequests"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
require.Len(t, resp.PendingRequests, 1, "the valid spec must succeed")
assert.Equal(t, "gooduser", resp.PendingRequests[0].UserName)
diff --git a/services/workspaces/workspaces_test.go b/services/workspaces/workspaces_test.go
index e88575ff7b..e6c74bd471 100644
--- a/services/workspaces/workspaces_test.go
+++ b/services/workspaces/workspaces_test.go
@@ -2,7 +2,7 @@ package workspaces_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"sort"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/workspaces"
)
@@ -39,7 +40,7 @@ func createWorkspaceWithSpec(t *testing.T, h *workspaces.Handler, userID, dirID
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pending, _ := resp["PendingRequests"].([]any)
require.Len(t, pending, 1)
@@ -64,7 +65,7 @@ func describeWorkspacesPage(
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wsList, _ := resp["Workspaces"].([]any)
ids := make([]string, 0, len(wsList))
@@ -236,7 +237,7 @@ func TestStopWorkspaces_TransitionsToStopped(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
failures, _ := resp["FailedRequests"].([]any)
assert.Empty(t, failures)
@@ -258,7 +259,7 @@ func TestStopWorkspaces_StateVisibleInDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, stopRec.Code)
var stopResp map[string]any
- require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopResp))
+ require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopResp, jsonx.V1()))
stopFailures, _ := stopResp["FailedRequests"].([]any)
require.Empty(t, stopFailures, "StopWorkspaces must succeed to reach STOPPED")
@@ -268,7 +269,7 @@ func TestStopWorkspaces_StateVisibleInDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wsList, _ := resp["Workspaces"].([]any)
require.Len(t, wsList, 1)
ws := wsList[0].(map[string]any)
@@ -299,7 +300,7 @@ func TestStartWorkspaces_ResumesFromStopped(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
failures, _ := resp["FailedRequests"].([]any)
assert.Empty(t, failures)
@@ -328,7 +329,7 @@ func TestStopWorkspaces_AlreadyStopped_Fails(t *testing.T) {
require.Equal(t, http.StatusOK, firstStopRec.Code)
var firstStopResp map[string]any
- require.NoError(t, json.Unmarshal(firstStopRec.Body.Bytes(), &firstStopResp))
+ require.NoError(t, json.Unmarshal(firstStopRec.Body.Bytes(), &firstStopResp, jsonx.V1()))
firstStopFailures, _ := firstStopResp["FailedRequests"].([]any)
require.Empty(t, firstStopFailures, "first StopWorkspaces call must succeed to reach STOPPED")
@@ -338,7 +339,7 @@ func TestStopWorkspaces_AlreadyStopped_Fails(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
failures, _ := resp["FailedRequests"].([]any)
require.Len(t, failures, 1, "stopping an already-STOPPED workspace must fail")
failed := failures[0].(map[string]any)
@@ -362,7 +363,7 @@ func TestStartWorkspaces_AlreadyAvailable_Fails(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
failures, _ := resp["FailedRequests"].([]any)
require.Len(t, failures, 1, "starting an already-AVAILABLE workspace must fail")
failed := failures[0].(map[string]any)
@@ -393,7 +394,7 @@ func TestStopWorkspaces_AlwaysOnRunningMode_Fails(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
failures, _ := resp["FailedRequests"].([]any)
require.Len(t, failures, 1,
"stopping an ALWAYS_ON workspace must fail even though its state is AVAILABLE")
@@ -430,7 +431,7 @@ func TestStartWorkspaces_AlwaysOnRunningMode_Fails(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
failures, _ := resp["FailedRequests"].([]any)
require.Len(t, failures, 1,
"starting an ALWAYS_ON workspace must fail even though its state is STOPPED")
@@ -458,7 +459,7 @@ func TestStopStartWorkspaces_AutoStopRunningMode_Succeeds(t *testing.T) {
require.Equal(t, http.StatusOK, stopRec.Code)
var stopResp map[string]any
- require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopResp))
+ require.NoError(t, json.Unmarshal(stopRec.Body.Bytes(), &stopResp, jsonx.V1()))
stopFailures, _ := stopResp["FailedRequests"].([]any)
assert.Empty(t, stopFailures, "AUTO_STOP workspace must be stoppable")
require.Equal(t, "STOPPED", workspaces.WorkspaceState(backend, wsID))
@@ -469,7 +470,7 @@ func TestStopStartWorkspaces_AutoStopRunningMode_Succeeds(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
startFailures, _ := startResp["FailedRequests"].([]any)
assert.Empty(t, startFailures, "AUTO_STOP workspace must be startable")
assert.Equal(t, "AVAILABLE", workspaces.WorkspaceState(backend, wsID))
@@ -492,7 +493,7 @@ func TestStopWorkspaces_UnknownID_ReturnsFailure(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
failures, _ := resp["FailedRequests"].([]any)
assert.Len(t, failures, 1, "unknown workspace must produce one FailedRequest")
@@ -514,7 +515,7 @@ func TestStartWorkspaces_UnknownID_ReturnsFailure(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
failures, _ := resp["FailedRequests"].([]any)
assert.Len(t, failures, 1, "unknown workspace must produce one FailedRequest")
}
@@ -576,7 +577,7 @@ func TestModifyWorkspaceProperties_VisibleInDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wsList, _ := resp["Workspaces"].([]any)
require.Len(t, wsList, 1)
@@ -606,7 +607,7 @@ func TestWorkspaceProperties_DefaultRunningModeBeforeModify(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wsList, _ := resp["Workspaces"].([]any)
require.Len(t, wsList, 1)
@@ -639,7 +640,7 @@ func TestTerminateWorkspaces_RemovedFromDescribe(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wsList, _ := resp["Workspaces"].([]any)
assert.Empty(t, wsList, "terminated workspace must not appear in DescribeWorkspaces")
}
@@ -771,7 +772,7 @@ func TestPagination_FilteredByDirectoryID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wsList := resp["Workspaces"].([]any)
assert.Len(t, wsList, 2, "filter by DirectoryId must return only matching workspaces")
}
@@ -809,7 +810,7 @@ func TestWorkspace_RealMembersPopulated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
wsList, ok := resp["Workspaces"].([]any)
require.True(t, ok)
require.Len(t, wsList, 1)
@@ -853,7 +854,7 @@ func TestWorkspace_WorkspaceNameThreadedThrough(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
pending, _ := createResp["PendingRequests"].([]any)
require.Len(t, pending, 1)
pendingItem := pending[0].(map[string]any)
@@ -867,7 +868,7 @@ func TestWorkspace_WorkspaceNameThreadedThrough(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
wsList, _ := descResp["Workspaces"].([]any)
require.Len(t, wsList, 1)
ws := wsList[0].(map[string]any)
@@ -901,7 +902,7 @@ func describeOneWorkspace(t *testing.T, h *workspaces.Handler, workspaceID strin
var out struct {
Workspaces []describedWorkspace `json:"Workspaces"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Len(t, out.Workspaces, 1)
return out.Workspaces[0]
@@ -956,7 +957,7 @@ func TestCreateStandbyWorkspaces_DataReplicationAndRelated(t *testing.T) {
WorkspaceID string `json:"WorkspaceId"`
} `json:"PendingStandbyRequests"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Empty(t, out.FailedStandbyRequests)
require.Len(t, out.PendingStandbyRequests, 1)
@@ -1001,7 +1002,7 @@ func TestCreateStandbyWorkspaces_UnknownPrimaryFails(t *testing.T) {
} `json:"FailedStandbyRequests"`
PendingStandbyRequests []any `json:"PendingStandbyRequests"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
require.Empty(t, out.PendingStandbyRequests)
require.Len(t, out.FailedStandbyRequests, 1)
diff --git a/services/workspaces/workspaces_validation_test.go b/services/workspaces/workspaces_validation_test.go
index 3408a7f01c..ec2e473e70 100644
--- a/services/workspaces/workspaces_validation_test.go
+++ b/services/workspaces/workspaces_validation_test.go
@@ -1,7 +1,7 @@
package workspaces_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,8 @@ import (
sdktypes "github.com/aws/aws-sdk-go-v2/service/workspaces/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// ---------------------------------------------------------------------------
@@ -113,7 +115,7 @@ func TestCreateWorkspaces_MaxAllowed_Returns200(t *testing.T) {
)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pending := resp["PendingRequests"].([]any)
assert.Len(t, pending, 25)
}
@@ -145,7 +147,7 @@ func TestCreateWorkspaces_WithProperties_Stored(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
pending := createResp["PendingRequests"].([]any)
require.Len(t, pending, 1)
@@ -172,7 +174,7 @@ func TestCreateWorkspaces_WithProperties_Stored(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp, jsonx.V1()))
wsList := descResp["Workspaces"].([]any)
require.Len(t, wsList, 1)
@@ -210,7 +212,7 @@ func TestCreateWorkspaces_DefaultsRunningModeToAlwaysOn(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
pending := createResp["PendingRequests"].([]any)
require.Len(t, pending, 1)
ws := pending[0].(map[string]any)
@@ -226,7 +228,7 @@ func TestCreateWorkspaces_DefaultsRunningModeToAlwaysOn(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
wsList := descResp["Workspaces"].([]any)
require.Len(t, wsList, 1)
@@ -259,7 +261,7 @@ func TestCreateWorkspaces_ExplicitRunningMode_NotOverwritten(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
pending := createResp["PendingRequests"].([]any)
require.Len(t, pending, 1)
ws := pending[0].(map[string]any)
@@ -273,7 +275,7 @@ func TestCreateWorkspaces_ExplicitRunningMode_NotOverwritten(t *testing.T) {
require.Equal(t, http.StatusOK, descRec.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(descRec.Body.Bytes(), &descResp, jsonx.V1()))
wsList := descResp["Workspaces"].([]any)
require.Len(t, wsList, 1)
assert.Equal(t, "AUTO_STOP", wsList[0].(map[string]any)["WorkspaceProperties"].(map[string]any)["RunningMode"],
@@ -320,7 +322,7 @@ func TestCreateWorkspaces_InvalidRunningMode_FailsPerItem(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "batch op must return 200 even with a failed item")
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
pending, _ := resp["PendingRequests"].([]any)
assert.Empty(t, pending, "the invalid item must not be created")
@@ -358,7 +360,7 @@ func TestCreateWorkspaces_SubnetId_Propagated(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &createResp, jsonx.V1()))
pending := createResp["PendingRequests"].([]any)
require.Len(t, pending, 1)
@@ -372,7 +374,7 @@ func TestCreateWorkspaces_SubnetId_Propagated(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var descResp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &descResp, jsonx.V1()))
wsList := descResp["Workspaces"].([]any)
require.Len(t, wsList, 1)
assert.Equal(t, "subnet-12345678", wsList[0].(map[string]any)["SubnetId"])
@@ -528,7 +530,7 @@ func TestModifyWorkspaceProperties_InvalidComputeType_MessageIsDescriptive(t *te
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp["message"], "NOT_A_REAL_TYPE",
"message should describe the actual invalid value, not just the error code")
assert.NotContains(t, resp["message"], "%!(EXTRA")
diff --git a/services/xray/handler.go b/services/xray/handler.go
index 9889c101b8..6cb3a16e84 100644
--- a/services/xray/handler.go
+++ b/services/xray/handler.go
@@ -2,7 +2,7 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"errors"
"fmt"
"net/http"
@@ -14,6 +14,7 @@ import (
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
"github.com/blackbirdworks/gopherstack/pkgs/config"
"github.com/blackbirdworks/gopherstack/pkgs/httputils"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/regionpeers"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -336,7 +337,7 @@ func (h *Handler) ExtractResource(c *echo.Context) string {
RuleName string `json:"RuleName"`
}
- _ = json.Unmarshal(body, &req)
+ _ = json.Unmarshal(body, &req, jsonx.V1())
if req.GroupName != "" {
return req.GroupName
@@ -498,9 +499,6 @@ func invalidParameterExceptionName(err error) string {
}
func (h *Handler) handleError(c *echo.Context, _ string, err error) error {
- var syntaxErr *json.SyntaxError
- var typeErr *json.UnmarshalTypeError
-
switch {
case errors.Is(err, awserr.ErrNotFound):
return c.JSON(http.StatusBadRequest, map[string]string{
@@ -518,7 +516,7 @@ func (h *Handler) handleError(c *echo.Context, _ string, err error) error {
keyMessageField: err.Error(),
})
case errors.Is(err, errInvalidRequest), errors.Is(err, errUnknownPath),
- errors.As(err, &syntaxErr), errors.As(err, &typeErr):
+ jsonx.IsSyntaxError(err), jsonx.IsTypeError(err):
return c.JSON(http.StatusBadRequest, map[string]string{
keyTypeField: errInvalidRequestException,
keyMessageField: err.Error(),
diff --git a/services/xray/handler_encryption_config.go b/services/xray/handler_encryption_config.go
index 64fb6f079d..cb92649375 100644
--- a/services/xray/handler_encryption_config.go
+++ b/services/xray/handler_encryption_config.go
@@ -2,11 +2,13 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"github.com/labstack/echo/v5"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type putEncryptionConfigInput struct {
@@ -30,13 +32,13 @@ func (h *Handler) handleGetEncryptionConfigBody(_ context.Context, _ []byte) ([]
return json.Marshal(map[string]any{
keyEncryptionConfig: cfg,
- })
+ }, jsonx.V1())
}
func (h *Handler) handlePutEncryptionConfig(_ context.Context, body []byte) ([]byte, error) {
var in putEncryptionConfigInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -65,5 +67,5 @@ func (h *Handler) handlePutEncryptionConfig(_ context.Context, body []byte) ([]b
return json.Marshal(map[string]any{
keyEncryptionConfig: cfg,
- })
+ }, jsonx.V1())
}
diff --git a/services/xray/handler_encryption_config_test.go b/services/xray/handler_encryption_config_test.go
index 8f17a085d2..a925b599d1 100644
--- a/services/xray/handler_encryption_config_test.go
+++ b/services/xray/handler_encryption_config_test.go
@@ -1,12 +1,14 @@
package xray_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_GetEncryptionConfig(t *testing.T) {
@@ -143,7 +145,7 @@ func TestEncryption_UpdatingStatusAfterPut(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
enc, ok := putResp["EncryptionConfig"].(map[string]any)
require.True(t, ok)
@@ -154,7 +156,7 @@ func TestEncryption_UpdatingStatusAfterPut(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
enc2, ok := getResp["EncryptionConfig"].(map[string]any)
require.True(t, ok)
@@ -200,7 +202,7 @@ func TestGetEncryptionConfig_DefaultIsNONE(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
enc, ok := resp["EncryptionConfig"].(map[string]any)
require.True(t, ok, "EncryptionConfig must be present in response")
@@ -294,7 +296,7 @@ func TestHandler_EncryptionConfig_KMSRoundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp, jsonx.V1()))
cfg, ok := resp["EncryptionConfig"].(map[string]any)
require.True(t, ok)
@@ -309,7 +311,7 @@ func TestHandler_EncryptionConfig_KMSRoundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec4.Code)
var resp4 map[string]any
- require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &resp4))
+ require.NoError(t, json.Unmarshal(rec4.Body.Bytes(), &resp4, jsonx.V1()))
cfg4, _ := resp4["EncryptionConfig"].(map[string]any)
assert.Equal(t, "NONE", cfg4["Type"])
}
@@ -358,7 +360,7 @@ func TestEncryptionConfig_UpdatingThenActiveStatus(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
enc, ok := putResp["EncryptionConfig"].(map[string]any)
require.True(t, ok)
@@ -368,7 +370,7 @@ func TestEncryptionConfig_UpdatingThenActiveStatus(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
enc2, ok := getResp["EncryptionConfig"].(map[string]any)
require.True(t, ok)
@@ -384,7 +386,7 @@ func TestEncryptionConfig_NoneTypeAccepted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
enc, ok := resp["EncryptionConfig"].(map[string]any)
require.True(t, ok)
diff --git a/services/xray/handler_groups.go b/services/xray/handler_groups.go
index 24619dcf00..fd6da66310 100644
--- a/services/xray/handler_groups.go
+++ b/services/xray/handler_groups.go
@@ -2,9 +2,10 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -42,7 +43,7 @@ type createGroupInput struct {
func (h *Handler) handleCreateGroup(_ context.Context, body []byte) ([]byte, error) {
var in createGroupInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -76,7 +77,7 @@ func (h *Handler) handleCreateGroup(_ context.Context, body []byte) ([]byte, err
return json.Marshal(map[string]any{
keyGroup: toGroupView(g),
- })
+ }, jsonx.V1())
}
type getGroupInput struct {
@@ -87,7 +88,7 @@ type getGroupInput struct {
func (h *Handler) handleGetGroup(_ context.Context, body []byte) ([]byte, error) {
var in getGroupInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -113,7 +114,7 @@ func (h *Handler) handleGetGroup(_ context.Context, body []byte) ([]byte, error)
return json.Marshal(map[string]any{
keyGroup: toGroupView(g),
- })
+ }, jsonx.V1())
}
// GetGroupsInput (xray@v1.39.4 api_op_GetGroups.go) has no page-size member,
@@ -125,7 +126,7 @@ type getGroupsInput struct {
func (h *Handler) handleGetGroups(_ context.Context, body []byte) ([]byte, error) {
var in getGroupsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -147,7 +148,7 @@ func (h *Handler) handleGetGroups(_ context.Context, body []byte) ([]byte, error
keyNextToken: pg.Next,
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
// updateGroupInput uses pointer fields for FilterExpression and InsightsConfiguration
@@ -165,7 +166,7 @@ type updateGroupInput struct {
func (h *Handler) handleUpdateGroup(_ context.Context, body []byte) ([]byte, error) {
var in updateGroupInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -194,7 +195,7 @@ func (h *Handler) handleUpdateGroup(_ context.Context, body []byte) ([]byte, err
return json.Marshal(map[string]any{
keyGroup: toGroupView(g),
- })
+ }, jsonx.V1())
}
type deleteGroupInput struct {
@@ -205,7 +206,7 @@ type deleteGroupInput struct {
func (h *Handler) handleDeleteGroup(_ context.Context, body []byte) ([]byte, error) {
var in deleteGroupInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -218,5 +219,5 @@ func (h *Handler) handleDeleteGroup(_ context.Context, body []byte) ([]byte, err
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
diff --git a/services/xray/handler_groups_test.go b/services/xray/handler_groups_test.go
index 5d99d35ea7..f1ccd8d43a 100644
--- a/services/xray/handler_groups_test.go
+++ b/services/xray/handler_groups_test.go
@@ -1,7 +1,7 @@
package xray_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/xray"
)
@@ -94,7 +95,7 @@ func TestHandler_GetGroups(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups, ok := resp["Groups"].([]any)
require.True(t, ok)
@@ -259,7 +260,7 @@ func TestHandler_UpdateGroup_AppliesInsightsConfiguration(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
group, ok := resp["Group"].(map[string]any)
require.True(t, ok)
@@ -299,7 +300,7 @@ func TestHandler_UpdateGroup_OmittedFieldsAreNotWiped(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &resp, jsonx.V1()))
group, ok := resp["Group"].(map[string]any)
require.True(t, ok)
assert.Equal(t, `service("original")`, group["FilterExpression"],
@@ -313,7 +314,7 @@ func TestHandler_UpdateGroup_OmittedFieldsAreNotWiped(t *testing.T) {
require.Equal(t, http.StatusOK, updRec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(updRec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(updRec2.Body.Bytes(), &resp2, jsonx.V1()))
group2, ok := resp2["Group"].(map[string]any)
require.True(t, ok)
ic, ok := group2["InsightsConfiguration"].(map[string]any)
@@ -353,7 +354,7 @@ func TestGroup_ARNBasedLookup(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
group, ok := createResp["Group"].(map[string]any)
require.True(t, ok)
@@ -369,7 +370,7 @@ func TestGroup_ARNBasedLookup(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
g2, ok := getResp["Group"].(map[string]any)
require.True(t, ok)
@@ -428,7 +429,7 @@ func TestHandler_GetGroups_Pagination(t *testing.T) {
require.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
groups, ok := resp["Groups"].([]any)
require.True(t, ok, "Groups field must be present and array")
@@ -462,7 +463,7 @@ func TestHandler_GetGroups_NextTokenContinuation(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
groups1 := resp1["Groups"].([]any)
assert.Len(t, groups1, defaultPageSize)
@@ -474,7 +475,7 @@ func TestHandler_GetGroups_NextTokenContinuation(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
groups2 := resp2["Groups"].([]any)
assert.Len(t, groups2, total-defaultPageSize)
@@ -490,7 +491,7 @@ func TestHandler_GetGroups_EmptyBodyAccepted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "Groups")
assert.Contains(t, resp, "NextToken")
}
@@ -504,7 +505,7 @@ func TestGroups_ARNBasedLookup(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
group, ok := createResp["Group"].(map[string]any)
require.True(t, ok)
@@ -517,7 +518,7 @@ func TestGroups_ARNBasedLookup(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
g2, ok := getResp["Group"].(map[string]any)
require.True(t, ok)
@@ -533,7 +534,7 @@ func TestGroups_UpdateByARN(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
group, _ := createResp["Group"].(map[string]any)
groupARN, _ := group["GroupARN"].(string)
@@ -545,7 +546,7 @@ func TestGroups_UpdateByARN(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var updResp map[string]any
- require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updResp))
+ require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updResp, jsonx.V1()))
g2, ok := updResp["Group"].(map[string]any)
require.True(t, ok)
@@ -561,7 +562,7 @@ func TestGroups_DeleteByARN(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
group, _ := createResp["Group"].(map[string]any)
groupARN, _ := group["GroupARN"].(string)
@@ -603,7 +604,7 @@ func TestGroups_FilterExpressionStoredAndReturned(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
g, ok := resp["Group"].(map[string]any)
require.True(t, ok)
@@ -621,7 +622,7 @@ func TestGroups_ARNPresentInCreateResponse(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
group, ok := resp["Group"].(map[string]any)
require.True(t, ok)
@@ -647,7 +648,7 @@ func TestGroups_ListGroups(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
groups, ok := resp["Groups"].([]any)
require.True(t, ok)
diff --git a/services/xray/handler_indexing_rules.go b/services/xray/handler_indexing_rules.go
index d42eae5934..68a614344c 100644
--- a/services/xray/handler_indexing_rules.go
+++ b/services/xray/handler_indexing_rules.go
@@ -2,9 +2,10 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -32,7 +33,7 @@ type updateIndexingRuleInput struct {
func (h *Handler) handleUpdateIndexingRule(_ context.Context, body []byte) ([]byte, error) {
var in updateIndexingRuleInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -54,7 +55,7 @@ func (h *Handler) handleUpdateIndexingRule(_ context.Context, body []byte) ([]by
return json.Marshal(map[string]any{
"IndexingRule": toIndexingRuleView(rule),
- })
+ }, jsonx.V1())
}
type indexingRuleView struct {
@@ -90,7 +91,7 @@ type getIndexingRulesInput struct {
func (h *Handler) handleGetIndexingRules(_ context.Context, body []byte) ([]byte, error) {
var in getIndexingRulesInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -111,5 +112,5 @@ func (h *Handler) handleGetIndexingRules(_ context.Context, body []byte) ([]byte
return json.Marshal(map[string]any{
"IndexingRules": pg.Data,
keyNextToken: pg.Next,
- })
+ }, jsonx.V1())
}
diff --git a/services/xray/handler_indexing_rules_test.go b/services/xray/handler_indexing_rules_test.go
index 71fee3c341..8c41189302 100644
--- a/services/xray/handler_indexing_rules_test.go
+++ b/services/xray/handler_indexing_rules_test.go
@@ -1,12 +1,14 @@
package xray_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_GetIndexingRules(t *testing.T) {
@@ -33,7 +35,7 @@ func TestHandler_GetIndexingRules(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rules, ok := resp["IndexingRules"].([]any)
require.True(t, ok)
@@ -80,7 +82,7 @@ func TestHandler_GetIndexingRules_Pagination(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rules, _ := resp["IndexingRules"].([]any)
assert.GreaterOrEqual(t, len(rules), tt.wantMin)
@@ -98,7 +100,7 @@ func TestIndexingRules_GetDefault(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rules, ok := resp["IndexingRules"].([]any)
require.True(t, ok)
@@ -114,7 +116,7 @@ func TestIndexingRules_UpdateModifiesTimestamp(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rule, ok := resp["IndexingRule"].(map[string]any)
require.True(t, ok)
@@ -147,7 +149,7 @@ func TestIndexingRules_UpdateAppliesProbabilisticRule(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
rule, ok := resp["IndexingRule"].(map[string]any)
require.True(t, ok)
@@ -165,7 +167,7 @@ func TestIndexingRules_UpdateAppliesProbabilisticRule(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
rules, ok := getResp["IndexingRules"].([]any)
require.True(t, ok)
@@ -197,7 +199,7 @@ func TestHandler_UpdateIndexingRule_NotFoundReturnsResourceNotFoundException(t *
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// UpdateIndexingRule's modeled error set uses ResourceNotFoundException, unlike
// most other X-Ray not-found cases (which use InvalidRequestException).
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
diff --git a/services/xray/handler_insights.go b/services/xray/handler_insights.go
index e25ab7968e..7ddbdcbd42 100644
--- a/services/xray/handler_insights.go
+++ b/services/xray/handler_insights.go
@@ -2,10 +2,11 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -64,7 +65,7 @@ func toInsightView(i *Insight) insightView {
func (h *Handler) handleGetInsight(_ context.Context, body []byte) ([]byte, error) {
var in getInsightInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -80,7 +81,7 @@ func (h *Handler) handleGetInsight(_ context.Context, body []byte) ([]byte, erro
return json.Marshal(map[string]any{
"Insight": toInsightView(i),
- })
+ }, jsonx.V1())
}
type getInsightEventsInput struct {
@@ -97,7 +98,7 @@ type insightEventView struct {
func (h *Handler) handleGetInsightEvents(_ context.Context, body []byte) ([]byte, error) {
var in getInsightEventsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -128,7 +129,7 @@ func (h *Handler) handleGetInsightEvents(_ context.Context, body []byte) ([]byte
return json.Marshal(map[string]any{
"InsightEvents": pg.Data,
keyNextToken: pg.Next,
- })
+ }, jsonx.V1())
}
type getInsightImpactGraphInput struct {
@@ -141,7 +142,7 @@ type getInsightImpactGraphInput struct {
func (h *Handler) handleGetInsightImpactGraph(_ context.Context, body []byte) ([]byte, error) {
var in getInsightImpactGraphInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -163,7 +164,7 @@ func (h *Handler) handleGetInsightImpactGraph(_ context.Context, body []byte) ([
"ServiceGraphStartTime": in.StartTime,
"ServiceGraphEndTime": in.EndTime,
keyNextToken: "",
- })
+ }, jsonx.V1())
}
type getInsightSummariesInput struct {
@@ -215,7 +216,7 @@ func toInsightSummaryView(i *Insight) insightSummaryView {
func (h *Handler) handleGetInsightSummaries(_ context.Context, body []byte) ([]byte, error) {
var in getInsightSummariesInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -266,5 +267,5 @@ func (h *Handler) handleGetInsightSummaries(_ context.Context, body []byte) ([]b
return json.Marshal(map[string]any{
"InsightSummaries": pg.Data,
keyNextToken: pg.Next,
- })
+ }, jsonx.V1())
}
diff --git a/services/xray/handler_insights_test.go b/services/xray/handler_insights_test.go
index 5ec29b498c..e0193feae5 100644
--- a/services/xray/handler_insights_test.go
+++ b/services/xray/handler_insights_test.go
@@ -1,7 +1,7 @@
package xray_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"maps"
"net/http"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/xray"
)
@@ -45,7 +46,7 @@ func TestGetInsightSummaries_ALLState(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["InsightSummaries"].([]any)
require.True(t, ok)
@@ -117,7 +118,7 @@ func TestGetInsightSummariesStateFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["InsightSummaries"].([]any)
require.True(t, ok)
@@ -145,7 +146,7 @@ func TestGroupInsightsConfigurationRoundtrip(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
group, ok := resp["Group"].(map[string]any)
require.True(t, ok)
@@ -237,7 +238,7 @@ func TestHandler_GetInsight_ResponseFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
insight, ok := resp["Insight"].(map[string]any)
require.True(t, ok)
@@ -296,7 +297,7 @@ func TestHandler_GetInsightEvents(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
events, ok := resp["InsightEvents"].([]any)
require.True(t, ok)
@@ -362,7 +363,7 @@ func TestHandler_GetInsightImpactGraph(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "insight-xyz", resp["InsightId"])
@@ -426,7 +427,7 @@ func TestHandler_GetInsightSummaries(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["InsightSummaries"].([]any)
require.True(t, ok)
@@ -491,7 +492,7 @@ func TestHandler_GetInsightEvents_Pagination(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
nextToken, _ := resp["NextToken"].(string)
if tt.wantHasNext {
@@ -524,7 +525,7 @@ func TestHandler_GetInsightEvents_NextTokenContinuation(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
events1, _ := resp1["InsightEvents"].([]any)
assert.Len(t, events1, 4)
@@ -540,7 +541,7 @@ func TestHandler_GetInsightEvents_NextTokenContinuation(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
events2, _ := resp2["InsightEvents"].([]any)
assert.Len(t, events2, 2)
@@ -604,7 +605,7 @@ func TestHandler_GetInsightSummaries_Pagination(t *testing.T) {
}
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["InsightSummaries"].([]any)
assert.Len(t, summaries, tt.wantCount)
@@ -630,7 +631,7 @@ func TestHandler_GetInsight_ResponseShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
insightData, ok := resp["Insight"].(map[string]any)
require.True(t, ok)
@@ -657,7 +658,7 @@ func TestHandler_GetInsightSummaries_ResponseShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["InsightSummaries"].([]any)
require.True(t, ok)
@@ -699,7 +700,7 @@ func TestGetInsightSummaries_LastUpdateTimeReflectsActivity(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["InsightSummaries"].([]any)
require.True(t, ok)
require.Len(t, summaries, 1)
@@ -717,7 +718,7 @@ func TestGetInsightSummaries_LastUpdateTimeReflectsActivity(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
insight, ok := getResp["Insight"].(map[string]any)
require.True(t, ok)
assert.NotContains(t, insight, "LastUpdateTime")
@@ -770,7 +771,7 @@ func TestGetInsight_FieldsReturned(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
insight, ok := resp["Insight"].(map[string]any)
require.True(t, ok)
@@ -813,7 +814,7 @@ func TestGetInsightEvents_ReturnsEventsForInsight(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
events, ok := resp["InsightEvents"].([]any)
require.True(t, ok)
@@ -868,7 +869,7 @@ func TestGetInsightImpactGraph_ReturnsInsightId(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "i-impact-001", resp["InsightId"])
}
})
@@ -919,7 +920,7 @@ func TestGetInsightSummaries_StatesValidation(t *testing.T) {
if tt.wantStatus == http.StatusOK && tt.wantCount > 0 {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["InsightSummaries"].([]any)
require.True(t, ok)
diff --git a/services/xray/handler_resource_policies.go b/services/xray/handler_resource_policies.go
index a62dde5c9d..0e6a6c7aaf 100644
--- a/services/xray/handler_resource_policies.go
+++ b/services/xray/handler_resource_policies.go
@@ -2,10 +2,11 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -17,7 +18,7 @@ type deleteResourcePolicyInput struct {
func (h *Handler) handleDeleteResourcePolicy(_ context.Context, body []byte) ([]byte, error) {
var in deleteResourcePolicyInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -30,7 +31,7 @@ func (h *Handler) handleDeleteResourcePolicy(_ context.Context, body []byte) ([]
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
type resourcePolicyView struct {
@@ -55,7 +56,7 @@ func (h *Handler) handleListResourcePolicies(_ context.Context, body []byte) ([]
MaxResults int `json:"MaxResults"`
}
if len(body) > 0 {
- _ = json.Unmarshal(body, &in)
+ _ = json.Unmarshal(body, &in, jsonx.V1())
}
policies := h.Backend.ListResourcePolicies()
@@ -71,7 +72,7 @@ func (h *Handler) handleListResourcePolicies(_ context.Context, body []byte) ([]
pg := page.New(views, in.NextToken, in.MaxResults, defaultResourcePoliciesPageSize)
resp := map[string]any{"ResourcePolicies": pg.Data, keyNextToken: pg.Next}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type putResourcePolicyInput struct {
@@ -84,7 +85,7 @@ type putResourcePolicyInput struct {
func (h *Handler) handlePutResourcePolicy(ctx context.Context, body []byte) ([]byte, error) {
var in putResourcePolicyInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -107,5 +108,5 @@ func (h *Handler) handlePutResourcePolicy(ctx context.Context, body []byte) ([]b
return json.Marshal(map[string]any{
"ResourcePolicy": toResourcePolicyView(p),
- })
+ }, jsonx.V1())
}
diff --git a/services/xray/handler_resource_policies_test.go b/services/xray/handler_resource_policies_test.go
index 843e19caea..ac469ff0fb 100644
--- a/services/xray/handler_resource_policies_test.go
+++ b/services/xray/handler_resource_policies_test.go
@@ -1,7 +1,7 @@
package xray_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/xray"
)
@@ -67,7 +68,7 @@ func TestPutAndListResourcePolicies(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policies, ok := resp["ResourcePolicies"].([]any)
require.True(t, ok)
@@ -82,7 +83,7 @@ func TestPutAndListResourcePolicies(t *testing.T) {
require.Equal(t, http.StatusOK, putRec.Code)
var putResp map[string]any
- require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp))
+ require.NoError(t, json.Unmarshal(putRec.Body.Bytes(), &putResp, jsonx.V1()))
policyObj, ok := putResp["ResourcePolicy"].(map[string]any)
require.True(t, ok)
@@ -94,7 +95,7 @@ func TestPutAndListResourcePolicies(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
policies, ok = listResp["ResourcePolicies"].([]any)
require.True(t, ok)
@@ -148,7 +149,7 @@ func TestListResourcePolicies_NextTokenInResponse(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp["NextToken"]
assert.True(t, ok, "NextToken must be present in ListResourcePolicies response")
@@ -314,7 +315,7 @@ func TestResourcePolicy_ListAfterPut(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
policies, ok := resp["ResourcePolicies"].([]any)
require.True(t, ok)
@@ -339,7 +340,7 @@ func TestResourcePolicy_DeleteExistingPolicy(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
policies, _ := resp["ResourcePolicies"].([]any)
assert.Empty(t, policies, "policy must be removed after delete")
@@ -372,7 +373,7 @@ func TestResourcePolicy_MaxCountUsesCorrectErrorCode(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "PolicyCountLimitExceededException", resp["__type"])
}
@@ -393,7 +394,7 @@ func TestResourcePolicy_SizeLimitExceeded(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "PolicySizeLimitExceededException", resp["__type"])
}
@@ -413,7 +414,7 @@ func TestResourcePolicy_LastUpdatedTimeIsEpochSeconds(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
policy, ok := resp["ResourcePolicy"].(map[string]any)
require.True(t, ok)
@@ -447,7 +448,7 @@ func TestHandler_DeleteResourcePolicy_RevisionIDEnforced(t *testing.T) {
require.Equal(t, http.StatusBadRequest, wrongRec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(wrongRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(wrongRec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "InvalidPolicyRevisionIdException", resp["__type"])
// The policy must still exist after the rejected delete.
@@ -455,7 +456,7 @@ func TestHandler_DeleteResourcePolicy_RevisionIDEnforced(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
policies, _ := listResp["ResourcePolicies"].([]any)
assert.Len(t, policies, 1, "policy must survive a delete with the wrong PolicyRevisionId")
}
diff --git a/services/xray/handler_sampling_rules.go b/services/xray/handler_sampling_rules.go
index 15ea6b120a..1f6fa8b716 100644
--- a/services/xray/handler_sampling_rules.go
+++ b/services/xray/handler_sampling_rules.go
@@ -2,9 +2,10 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -94,7 +95,7 @@ type createSamplingRuleInput struct {
func (h *Handler) handleCreateSamplingRule(_ context.Context, body []byte) ([]byte, error) {
var in createSamplingRuleInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -143,7 +144,7 @@ func (h *Handler) handleCreateSamplingRule(_ context.Context, body []byte) ([]by
return json.Marshal(map[string]any{
keySamplingRuleRecord: toSamplingRuleRecord(r),
- })
+ }, jsonx.V1())
}
type getSamplingRulesInput struct {
@@ -153,7 +154,7 @@ type getSamplingRulesInput struct {
func (h *Handler) handleGetSamplingRules(_ context.Context, body []byte) ([]byte, error) {
var in getSamplingRulesInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -174,7 +175,7 @@ func (h *Handler) handleGetSamplingRules(_ context.Context, body []byte) ([]byte
return json.Marshal(map[string]any{
"SamplingRuleRecords": pg.Data,
keyNextToken: pg.Next,
- })
+ }, jsonx.V1())
}
// samplingRuleUpdateInput uses json.RawMessage so we can detect which fields
@@ -204,7 +205,7 @@ type updateSamplingRuleInput struct {
func (h *Handler) handleUpdateSamplingRule(_ context.Context, body []byte) ([]byte, error) {
var in updateSamplingRuleInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -246,7 +247,7 @@ func (h *Handler) handleUpdateSamplingRule(_ context.Context, body []byte) ([]by
return json.Marshal(map[string]any{
keySamplingRuleRecord: toSamplingRuleRecord(r),
- })
+ }, jsonx.V1())
}
type deleteSamplingRuleInput struct {
@@ -257,7 +258,7 @@ type deleteSamplingRuleInput struct {
func (h *Handler) handleDeleteSamplingRule(_ context.Context, body []byte) ([]byte, error) {
var in deleteSamplingRuleInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -273,5 +274,5 @@ func (h *Handler) handleDeleteSamplingRule(_ context.Context, body []byte) ([]by
return json.Marshal(map[string]any{
keySamplingRuleRecord: toSamplingRuleRecord(r),
- })
+ }, jsonx.V1())
}
diff --git a/services/xray/handler_sampling_rules_test.go b/services/xray/handler_sampling_rules_test.go
index 6eed4ea626..8b2b885149 100644
--- a/services/xray/handler_sampling_rules_test.go
+++ b/services/xray/handler_sampling_rules_test.go
@@ -1,7 +1,7 @@
package xray_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/xray"
)
@@ -84,7 +85,7 @@ func TestHandler_GetSamplingRules(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
records, ok := resp["SamplingRuleRecords"].([]any)
require.True(t, ok)
@@ -172,7 +173,7 @@ func TestHandler_UpdateSamplingRule_Attributes(t *testing.T) {
} `json:"SamplingRule"`
} `json:"SamplingRuleRecord"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, map[string]string{"env": "prod"}, resp.SamplingRuleRecord.SamplingRule.Attributes)
}
@@ -238,7 +239,7 @@ func TestUpdateSamplingRule_ZeroFixedRate(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
record, ok := resp["SamplingRuleRecord"].(map[string]any)
require.True(t, ok)
@@ -277,7 +278,7 @@ func TestSamplingRuleAttributes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
record, ok := resp["SamplingRuleRecord"].(map[string]any)
require.True(t, ok)
@@ -310,7 +311,7 @@ func TestSamplingRuleModifiedAtInRecord(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
records, ok := resp["SamplingRuleRecords"].([]any)
require.True(t, ok)
@@ -425,7 +426,7 @@ func TestHandler_GetSamplingRules_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
records, ok := resp["SamplingRuleRecords"].([]any)
require.True(t, ok)
@@ -545,7 +546,7 @@ func TestSamplingRule_DefaultRulePresentAtStart(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
records, ok := resp["SamplingRuleRecords"].([]any)
require.True(t, ok)
@@ -630,7 +631,7 @@ func TestUpdateSamplingRule_ZeroValues(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
record, ok := resp["SamplingRuleRecord"].(map[string]any)
require.True(t, ok)
@@ -762,7 +763,7 @@ func TestSamplingRule_SamplingRateBoostRoundTrips(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
record, ok := createResp["SamplingRuleRecord"].(map[string]any)
require.True(t, ok)
rule, ok := record["SamplingRule"].(map[string]any)
@@ -785,7 +786,7 @@ func TestSamplingRule_SamplingRateBoostRoundTrips(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var updResp map[string]any
- require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updResp))
+ require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updResp, jsonx.V1()))
updRecord, ok := updResp["SamplingRuleRecord"].(map[string]any)
require.True(t, ok)
updRule, ok := updRecord["SamplingRule"].(map[string]any)
@@ -811,7 +812,7 @@ func TestSamplingRule_UpdateAndDeleteByARN(t *testing.T) {
require.Equal(t, http.StatusOK, createRec.Code)
var createResp map[string]any
- require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp))
+ require.NoError(t, json.Unmarshal(createRec.Body.Bytes(), &createResp, jsonx.V1()))
record, ok := createResp["SamplingRuleRecord"].(map[string]any)
require.True(t, ok)
rule, ok := record["SamplingRule"].(map[string]any)
@@ -826,7 +827,7 @@ func TestSamplingRule_UpdateAndDeleteByARN(t *testing.T) {
require.Equal(t, http.StatusOK, updRec.Code)
var updResp map[string]any
- require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updResp))
+ require.NoError(t, json.Unmarshal(updRec.Body.Bytes(), &updResp, jsonx.V1()))
updRecord, ok := updResp["SamplingRuleRecord"].(map[string]any)
require.True(t, ok)
updRule, ok := updRecord["SamplingRule"].(map[string]any)
@@ -840,7 +841,7 @@ func TestSamplingRule_UpdateAndDeleteByARN(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var getResp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &getResp, jsonx.V1()))
records, ok := getResp["SamplingRuleRecords"].([]any)
require.True(t, ok)
for _, r := range records {
diff --git a/services/xray/handler_sampling_statistics.go b/services/xray/handler_sampling_statistics.go
index cd8e7ec60e..099ad14c43 100644
--- a/services/xray/handler_sampling_statistics.go
+++ b/services/xray/handler_sampling_statistics.go
@@ -2,9 +2,10 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -23,7 +24,7 @@ type getSamplingStatisticSummariesInput struct {
func (h *Handler) handleGetSamplingStatisticSummaries(_ context.Context, body []byte) ([]byte, error) {
var in getSamplingStatisticSummariesInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -50,7 +51,7 @@ func (h *Handler) handleGetSamplingStatisticSummaries(_ context.Context, body []
return json.Marshal(map[string]any{
"SamplingStatisticSummaries": pg.Data,
keyNextToken: pg.Next,
- })
+ }, jsonx.V1())
}
type samplingStatisticsDocumentInput struct {
@@ -104,7 +105,7 @@ type unprocessedStatisticsView struct {
func (h *Handler) handleGetSamplingTargets(_ context.Context, body []byte) ([]byte, error) {
var in getSamplingTargetsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -156,5 +157,5 @@ func (h *Handler) handleGetSamplingTargets(_ context.Context, body []byte) ([]by
"UnprocessedStatistics": unprocessedViews,
"UnprocessedBoostStatistics": unprocessedBoostViews,
"LastRuleModification": lastModTS,
- })
+ }, jsonx.V1())
}
diff --git a/services/xray/handler_sampling_statistics_test.go b/services/xray/handler_sampling_statistics_test.go
index 589434ba70..34e6e40af8 100644
--- a/services/xray/handler_sampling_statistics_test.go
+++ b/services/xray/handler_sampling_statistics_test.go
@@ -1,7 +1,7 @@
package xray_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/xray"
)
@@ -26,7 +27,7 @@ func TestGetSamplingTargets_LastRuleModAndTTL(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// LastRuleModification should be a timestamp.
lastMod, ok := resp["LastRuleModification"].(float64)
@@ -65,7 +66,7 @@ func TestGetSamplingStatisticSummaries_AfterPut(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["SamplingStatisticSummaries"].([]any)
require.True(t, ok)
@@ -92,7 +93,7 @@ func TestGetSamplingTargets_UnprocessedWithErrorCode(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
unprocessed, ok := resp["UnprocessedStatistics"].([]any)
require.True(t, ok)
@@ -126,7 +127,7 @@ func TestGetSamplingTargets_EmptyClientID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
unprocessed, ok := resp["UnprocessedStatistics"].([]any)
require.True(t, ok)
@@ -159,7 +160,7 @@ func TestHandler_GetSamplingStatisticSummaries(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["SamplingStatisticSummaries"].([]any)
require.True(t, ok)
@@ -233,7 +234,7 @@ func TestHandler_GetSamplingTargets(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
targets, ok := resp["SamplingTargetDocuments"].([]any)
require.True(t, ok)
@@ -260,7 +261,7 @@ func TestHandler_GetSamplingTargets_TargetFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
targets, ok := resp["SamplingTargetDocuments"].([]any)
require.True(t, ok)
@@ -305,7 +306,7 @@ func TestHandler_GetSamplingStatisticSummaries_Pagination(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "SamplingStatisticSummaries")
assert.Contains(t, resp, "NextToken")
})
@@ -326,7 +327,7 @@ func TestGetSamplingTargets_LastRuleModification(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
lastMod, ok := resp["LastRuleModification"].(float64)
require.True(t, ok, "LastRuleModification must be present")
@@ -347,7 +348,7 @@ func TestGetSamplingTargets_ReservoirQuotaTTL(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
targets, ok := resp["SamplingTargetDocuments"].([]any)
require.True(t, ok)
@@ -417,7 +418,7 @@ func TestGetSamplingTargets_UnprocessedForUnknownRules(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
targets, _ := resp["SamplingTargetDocuments"].([]any)
assert.Len(t, targets, tt.wantTargets)
@@ -490,7 +491,7 @@ func TestGetSamplingTargets_BoostStatistics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
unprocessedBoost, _ := resp["UnprocessedBoostStatistics"].([]any)
assert.Len(t, unprocessedBoost, tt.wantUnprocessed)
@@ -524,7 +525,7 @@ func TestGetSamplingStatisticSummaries_AccumulationFromTargets(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["SamplingStatisticSummaries"].([]any)
require.True(t, ok)
@@ -547,7 +548,7 @@ func TestGetSamplingStatisticSummaries_EmptyWithoutStats(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["SamplingStatisticSummaries"].([]any)
require.True(t, ok)
@@ -573,7 +574,7 @@ func TestGetSamplingStatisticSummaries_AccumulatesAcrossCalls(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["SamplingStatisticSummaries"].([]any)
require.Len(t, summaries, 1)
diff --git a/services/xray/handler_service_graph.go b/services/xray/handler_service_graph.go
index a3f4713d32..ce134da179 100644
--- a/services/xray/handler_service_graph.go
+++ b/services/xray/handler_service_graph.go
@@ -2,10 +2,11 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -20,7 +21,7 @@ type getServiceGraphInput struct {
func (h *Handler) handleGetServiceGraph(_ context.Context, body []byte) ([]byte, error) {
var in getServiceGraphInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -49,7 +50,7 @@ func (h *Handler) handleGetServiceGraph(_ context.Context, body []byte) ([]byte,
"ContainsOldGroupVersions": false,
keyStartTime: in.StartTime,
keyEndTime: in.EndTime,
- })
+ }, jsonx.V1())
}
type getTimeSeriesServiceStatisticsInput struct {
@@ -66,7 +67,7 @@ type getTimeSeriesServiceStatisticsInput struct {
func (h *Handler) handleGetTimeSeriesServiceStatistics(_ context.Context, body []byte) ([]byte, error) {
var in getTimeSeriesServiceStatisticsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -104,7 +105,7 @@ func (h *Handler) handleGetTimeSeriesServiceStatistics(_ context.Context, body [
"TimeSeriesServiceStatistics": pg.Data,
"ContainsOldGroupVersions": false,
keyNextToken: pg.Next,
- })
+ }, jsonx.V1())
}
type getTraceGraphInput struct {
@@ -115,7 +116,7 @@ type getTraceGraphInput struct {
func (h *Handler) handleGetTraceGraph(_ context.Context, body []byte) ([]byte, error) {
var in getTraceGraphInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -135,7 +136,7 @@ func (h *Handler) handleGetTraceGraph(_ context.Context, body []byte) ([]byte, e
return json.Marshal(map[string]any{
keyServices: pg.Data,
keyNextToken: pg.Next,
- })
+ }, jsonx.V1())
}
// resolveGroupFilterExpression resolves an optional GroupName/GroupARN (both
diff --git a/services/xray/handler_service_graph_test.go b/services/xray/handler_service_graph_test.go
index c573e3909a..83827b2486 100644
--- a/services/xray/handler_service_graph_test.go
+++ b/services/xray/handler_service_graph_test.go
@@ -1,7 +1,7 @@
package xray_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestGetServiceGraph_NonEmpty(t *testing.T) {
@@ -33,7 +35,7 @@ func TestGetServiceGraph_NonEmpty(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
services, ok := resp["Services"].([]any)
require.True(t, ok)
@@ -63,7 +65,7 @@ func TestGetTimeSeriesServiceStatistics_BucketedData(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stats, ok := resp["TimeSeriesServiceStatistics"].([]any)
require.True(t, ok)
@@ -99,7 +101,7 @@ func TestGetTraceGraph_ReturnsNodes(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
services, ok := resp["Services"].([]any)
require.True(t, ok)
@@ -117,7 +119,7 @@ func TestGetTraceGraph_EmptyForUnknownTraces(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
services, ok := resp["Services"].([]any)
require.True(t, ok)
@@ -177,7 +179,7 @@ func TestGetServiceGraph_ContainsOldGroupVersions(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp["ContainsOldGroupVersions"]
assert.True(t, ok, "ContainsOldGroupVersions must be present in GetServiceGraph response")
@@ -231,7 +233,7 @@ func TestHandler_GetServiceGraph_Pagination(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "NextToken")
assert.Contains(t, resp, "Services")
assert.Contains(t, resp, "StartTime")
@@ -279,7 +281,7 @@ func TestHandler_GetTraceGraph_Pagination(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "NextToken")
assert.Contains(t, resp, "Services")
}
@@ -339,7 +341,7 @@ func TestHandler_GetTimeSeriesServiceStatistics_Pagination(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Contains(t, resp, "NextToken")
assert.Contains(t, resp, "TimeSeriesServiceStatistics")
}
@@ -369,7 +371,7 @@ func TestGetServiceGraph_NodesAfterSegments(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
services, ok := resp["Services"].([]any)
require.True(t, ok)
@@ -394,7 +396,7 @@ func TestGetServiceGraph_NodeFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
services, _ := resp["Services"].([]any)
require.NotEmpty(t, services)
@@ -459,7 +461,7 @@ func TestGetServiceGraph_EmptyOutsideTimeWindow(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
services, _ := resp["Services"].([]any)
assert.Empty(t, services, "service graph must be empty when no segments fall in the time window")
@@ -510,7 +512,7 @@ func TestGetTimeSeriesServiceStatistics_ReturnsContainsOldGroupVersions(t *testi
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, ok := resp["ContainsOldGroupVersions"]
assert.True(t, ok, "ContainsOldGroupVersions must be present in response")
@@ -542,7 +544,7 @@ func TestGetTimeSeriesServiceStatistics_BucketedStatsAfterSegments(t *testing.T)
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
stats, ok := resp["TimeSeriesServiceStatistics"].([]any)
require.True(t, ok)
@@ -575,7 +577,7 @@ func TestGetTraceGraph_ReturnsNodesForTraceIDs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
services, ok := resp["Services"].([]any)
require.True(t, ok)
@@ -601,7 +603,7 @@ func TestGetTraceGraph_EmptyForUnknownTrace(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
services, _ := resp["Services"].([]any)
assert.Empty(t, services)
@@ -634,7 +636,7 @@ func TestGetServiceGraph_EdgeStatisticsAndDirection(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
services, ok := resp["Services"].([]any)
require.True(t, ok)
diff --git a/services/xray/handler_tags.go b/services/xray/handler_tags.go
index 03d73f34c6..ab79f19809 100644
--- a/services/xray/handler_tags.go
+++ b/services/xray/handler_tags.go
@@ -2,11 +2,12 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"strings"
"unicode/utf8"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -18,7 +19,7 @@ type listTagsForResourceInput struct {
func (h *Handler) handleListTagsForResource(_ context.Context, body []byte) ([]byte, error) {
var in listTagsForResourceInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -41,7 +42,7 @@ func (h *Handler) handleListTagsForResource(_ context.Context, body []byte) ([]b
return json.Marshal(map[string]any{
"Tags": pg.Data,
keyNextToken: pg.Next,
- })
+ }, jsonx.V1())
}
// tagWire mirrors types.Tag (xray@v1.39.4): a {Key, Value} object, one per
@@ -61,7 +62,7 @@ type tagResourceInput struct {
func (h *Handler) handleTagResource(_ context.Context, body []byte) ([]byte, error) {
var in tagResourceInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -78,7 +79,7 @@ func (h *Handler) handleTagResource(_ context.Context, body []byte) ([]byte, err
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
type untagResourceInput struct {
@@ -89,7 +90,7 @@ type untagResourceInput struct {
func (h *Handler) handleUntagResource(_ context.Context, body []byte) ([]byte, error) {
var in untagResourceInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -102,7 +103,7 @@ func (h *Handler) handleUntagResource(_ context.Context, body []byte) ([]byte, e
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
const (
diff --git a/services/xray/handler_tags_test.go b/services/xray/handler_tags_test.go
index 03e7ed2e15..23b0a1b12e 100644
--- a/services/xray/handler_tags_test.go
+++ b/services/xray/handler_tags_test.go
@@ -1,7 +1,7 @@
package xray_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"strconv"
"testing"
@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/xray"
)
@@ -23,7 +24,7 @@ func createTaggableGroup(t *testing.T, h *xray.Handler, name string) string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
group, ok := resp["Group"].(map[string]any)
require.True(t, ok)
@@ -83,7 +84,7 @@ func TestHandler_ListTagsForResource_Pagination(t *testing.T) {
if tt.wantStatus == http.StatusOK {
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tags, _ := resp["Tags"].([]any)
assert.Len(t, tags, tt.wantTags)
assert.Contains(t, resp, "NextToken")
@@ -111,7 +112,7 @@ func TestHandler_ListTagsForResource_WithTags(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
tagsList, _ := resp["Tags"].([]any)
assert.Len(t, tagsList, len(tags))
@@ -173,7 +174,7 @@ func TestTags_RoundTrip(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
tags, ok := resp["Tags"].([]any)
require.True(t, ok)
@@ -204,7 +205,7 @@ func TestTags_UntagResource(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &resp, jsonx.V1()))
tags, _ := resp["Tags"].([]any)
assert.Len(t, tags, 1, "only key2 should remain after untagging key1")
@@ -222,7 +223,7 @@ func TestTags_TagResource_UnknownResourceReturns400(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
}
@@ -238,7 +239,7 @@ func TestTags_UntagResource_UnknownResourceReturns400(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
}
@@ -260,6 +261,6 @@ func TestTags_TagResource_ExceedsMaxTagsReturns400(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "TooManyTagsException", resp["__type"])
}
diff --git a/services/xray/handler_telemetry.go b/services/xray/handler_telemetry.go
index 2f95403c4c..82469192f4 100644
--- a/services/xray/handler_telemetry.go
+++ b/services/xray/handler_telemetry.go
@@ -2,8 +2,10 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type telemetryRecordInput struct {
@@ -21,7 +23,7 @@ type putTelemetryRecordsInput struct {
func (h *Handler) handlePutTelemetryRecords(_ context.Context, body []byte) ([]byte, error) {
var in putTelemetryRecordsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -47,5 +49,5 @@ func (h *Handler) handlePutTelemetryRecords(_ context.Context, body []byte) ([]b
h.Backend.PutTelemetryRecords(records)
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
diff --git a/services/xray/handler_telemetry_test.go b/services/xray/handler_telemetry_test.go
index 6e069697bb..d3380b9ab2 100644
--- a/services/xray/handler_telemetry_test.go
+++ b/services/xray/handler_telemetry_test.go
@@ -1,13 +1,15 @@
package xray_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_PutTelemetryRecords(t *testing.T) {
@@ -37,7 +39,7 @@ func TestPutTelemetryRecords_Accepted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// Response should be an empty JSON object.
assert.Empty(t, resp)
}
@@ -93,7 +95,7 @@ func TestPutTelemetryRecords_AllFieldsAccepted(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Empty(t, resp, "response must be an empty JSON object")
})
}
diff --git a/services/xray/handler_test.go b/services/xray/handler_test.go
index 8f56b43bec..d6616a08cc 100644
--- a/services/xray/handler_test.go
+++ b/services/xray/handler_test.go
@@ -2,7 +2,7 @@ package xray_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/xray"
)
@@ -218,7 +219,7 @@ func TestHandler_ExtractResource(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- bodyBytes, err := json.Marshal(tt.body)
+ bodyBytes, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/CreateGroup", bytes.NewReader(bodyBytes))
@@ -275,7 +276,7 @@ func TestXRay_Handler_Reset(t *testing.T) {
var out struct {
Groups []any `json:"Groups"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &out, jsonx.V1()))
assert.Len(t, out.Groups, tt.wantAfter)
})
}
diff --git a/services/xray/handler_trace_retrieval.go b/services/xray/handler_trace_retrieval.go
index 513b1f6cdc..f622287d6a 100644
--- a/services/xray/handler_trace_retrieval.go
+++ b/services/xray/handler_trace_retrieval.go
@@ -2,10 +2,11 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -35,7 +36,7 @@ func buildTraceView(t *Trace) map[string]any {
for _, rawSeg := range t.Segments {
var doc segmentDoc
- if err := json.Unmarshal([]byte(rawSeg), &doc); err == nil {
+ if err := json.Unmarshal([]byte(rawSeg), &doc, jsonx.V1()); err == nil {
spans = append(spans, map[string]any{
"Document": rawSeg,
"Id": doc.ID,
@@ -66,7 +67,7 @@ func buildTraceView(t *Trace) map[string]any {
func (h *Handler) handleListRetrievedTraces(_ context.Context, body []byte) ([]byte, error) {
var in listRetrievedTracesInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -101,7 +102,7 @@ func (h *Handler) handleListRetrievedTraces(_ context.Context, body []byte) ([]b
resp[keyNextToken] = pg.Next
}
- return json.Marshal(resp)
+ return json.Marshal(resp, jsonx.V1())
}
type startTraceRetrievalInput struct {
@@ -113,7 +114,7 @@ type startTraceRetrievalInput struct {
func (h *Handler) handleStartTraceRetrieval(_ context.Context, body []byte) ([]byte, error) {
var in startTraceRetrievalInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -134,7 +135,7 @@ func (h *Handler) handleStartTraceRetrieval(_ context.Context, body []byte) ([]b
return json.Marshal(map[string]any{
"RetrievalToken": token,
- })
+ }, jsonx.V1())
}
const (
@@ -148,7 +149,7 @@ type cancelTraceRetrievalInput struct {
func (h *Handler) handleCancelTraceRetrieval(_ context.Context, body []byte) ([]byte, error) {
var in cancelTraceRetrievalInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -161,7 +162,7 @@ func (h *Handler) handleCancelTraceRetrieval(_ context.Context, body []byte) ([]
return nil, err
}
- return json.Marshal(map[string]any{})
+ return json.Marshal(map[string]any{}, jsonx.V1())
}
type getRetrievedTracesGraphInput struct {
@@ -172,7 +173,7 @@ type getRetrievedTracesGraphInput struct {
func (h *Handler) handleGetRetrievedTracesGraph(_ context.Context, body []byte) ([]byte, error) {
var in getRetrievedTracesGraphInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -196,5 +197,5 @@ func (h *Handler) handleGetRetrievedTracesGraph(_ context.Context, body []byte)
"RetrievalStatus": status,
keyServices: pg.Data,
keyNextToken: pg.Next,
- })
+ }, jsonx.V1())
}
diff --git a/services/xray/handler_trace_retrieval_test.go b/services/xray/handler_trace_retrieval_test.go
index b3e2101538..7d2ccc364a 100644
--- a/services/xray/handler_trace_retrieval_test.go
+++ b/services/xray/handler_trace_retrieval_test.go
@@ -1,7 +1,7 @@
package xray_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/xray"
)
@@ -31,7 +32,7 @@ func TestListRetrievedTraces_IncludesSegments(t *testing.T) {
"start_time": 1700000000.0,
"end_time": 1700000001.5,
}
- segJSON, err := json.Marshal(seg)
+ segJSON, err := json.Marshal(seg, jsonx.V1())
require.NoError(t, err)
unprocessed := b.PutTraceSegments([]string{string(segJSON)})
@@ -46,7 +47,7 @@ func TestListRetrievedTraces_IncludesSegments(t *testing.T) {
require.Equal(t, 200, startResp.Code)
var startResult map[string]any
- require.NoError(t, json.Unmarshal(startResp.Body.Bytes(), &startResult))
+ require.NoError(t, json.Unmarshal(startResp.Body.Bytes(), &startResult, jsonx.V1()))
token, ok := startResult["RetrievalToken"].(string)
require.True(t, ok, "expected RetrievalToken in response")
@@ -55,7 +56,7 @@ func TestListRetrievedTraces_IncludesSegments(t *testing.T) {
require.Equal(t, 200, listResp.Code)
var listResult map[string]any
- require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listResult))
+ require.NoError(t, json.Unmarshal(listResp.Body.Bytes(), &listResult, jsonx.V1()))
traces, ok := listResult["Traces"].([]any)
require.True(t, ok, "expected Traces array in response")
@@ -90,7 +91,7 @@ func startTestRetrieval(t *testing.T, h *xray.Handler) string {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
token, ok := resp["RetrievalToken"].(string)
require.True(t, ok)
@@ -130,7 +131,7 @@ func TestHandler_CancelTraceRetrieval(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
})
}
@@ -148,7 +149,7 @@ func TestHandler_GetRetrievedTracesGraph(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "COMPLETE", resp["RetrievalStatus"])
@@ -173,7 +174,7 @@ func TestHandler_GetRetrievedTracesGraph(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "ResourceNotFoundException", resp["__type"])
})
}
@@ -204,7 +205,7 @@ func TestHandler_GetRetrievedTracesGraph_ReflectsRetrievedTraces(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
token, _ := startResp["RetrievalToken"].(string)
require.NotEmpty(t, token)
@@ -212,7 +213,7 @@ func TestHandler_GetRetrievedTracesGraph_ReflectsRetrievedTraces(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
services, ok := resp["Services"].([]any)
require.True(t, ok)
@@ -241,7 +242,7 @@ func TestTraceRetrieval_StartAndList(t *testing.T) {
require.Equal(t, http.StatusOK, startRec.Code)
var startResp map[string]any
- require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp))
+ require.NoError(t, json.Unmarshal(startRec.Body.Bytes(), &startResp, jsonx.V1()))
token, ok := startResp["RetrievalToken"].(string)
require.True(t, ok)
@@ -253,7 +254,7 @@ func TestTraceRetrieval_StartAndList(t *testing.T) {
require.Equal(t, http.StatusOK, listRec.Code)
var listResp map[string]any
- require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp))
+ require.NoError(t, json.Unmarshal(listRec.Body.Bytes(), &listResp, jsonx.V1()))
assert.NotEmpty(t, listResp["RetrievalStatus"])
}
diff --git a/services/xray/handler_trace_segment_destination.go b/services/xray/handler_trace_segment_destination.go
index c54a3456cd..41d4befae4 100644
--- a/services/xray/handler_trace_segment_destination.go
+++ b/services/xray/handler_trace_segment_destination.go
@@ -2,8 +2,10 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func (h *Handler) handleGetTraceSegmentDestination(_ context.Context, _ []byte) ([]byte, error) {
@@ -12,7 +14,7 @@ func (h *Handler) handleGetTraceSegmentDestination(_ context.Context, _ []byte)
return json.Marshal(map[string]any{
"Destination": dest,
"Status": statusActive,
- })
+ }, jsonx.V1())
}
type updateTraceSegmentDestinationInput struct {
@@ -22,7 +24,7 @@ type updateTraceSegmentDestinationInput struct {
func (h *Handler) handleUpdateTraceSegmentDestination(_ context.Context, body []byte) ([]byte, error) {
var in updateTraceSegmentDestinationInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -44,5 +46,5 @@ func (h *Handler) handleUpdateTraceSegmentDestination(_ context.Context, body []
return json.Marshal(map[string]any{
"Destination": dest,
"Status": statusActive,
- })
+ }, jsonx.V1())
}
diff --git a/services/xray/handler_trace_segments.go b/services/xray/handler_trace_segments.go
index 946c9500ad..16188b9310 100644
--- a/services/xray/handler_trace_segments.go
+++ b/services/xray/handler_trace_segments.go
@@ -2,8 +2,10 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type putTraceSegmentsInput struct {
@@ -13,7 +15,7 @@ type putTraceSegmentsInput struct {
func (h *Handler) handlePutTraceSegments(_ context.Context, body []byte) ([]byte, error) {
var in putTraceSegmentsInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -45,5 +47,5 @@ func (h *Handler) handlePutTraceSegments(_ context.Context, body []byte) ([]byte
return json.Marshal(map[string]any{
"UnprocessedTraceSegments": out,
- })
+ }, jsonx.V1())
}
diff --git a/services/xray/handler_trace_segments_test.go b/services/xray/handler_trace_segments_test.go
index 5ded66f0be..ef153ad517 100644
--- a/services/xray/handler_trace_segments_test.go
+++ b/services/xray/handler_trace_segments_test.go
@@ -1,7 +1,7 @@
package xray_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"strings"
@@ -10,6 +10,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestHandler_PutTraceSegments(t *testing.T) {
@@ -55,7 +57,7 @@ func TestHandler_PutTraceSegments_Unprocessed(t *testing.T) {
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
unprocessed, ok := resp["UnprocessedTraceSegments"].([]any)
require.True(t, ok)
@@ -82,7 +84,7 @@ func TestPutAndGetTraceSummaries_Populated(t *testing.T) {
require.Equal(t, http.StatusOK, getRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(getRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["TraceSummaries"].([]any)
require.True(t, ok)
@@ -124,7 +126,7 @@ func TestPutTraceSegments_MalformedJSON(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code, "malformed segments should not crash the handler")
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
unprocessed, ok := resp["UnprocessedTraceSegments"].([]any)
require.True(t, ok)
@@ -145,7 +147,7 @@ func TestPutTraceSegments_MissingTraceID(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
unprocessed, ok := resp["UnprocessedTraceSegments"].([]any)
require.True(t, ok)
@@ -292,7 +294,7 @@ func TestHandler_PutTraceSegments_ExactLimitRespondsWithUnprocessedField(t *test
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
_, hasField := resp["UnprocessedTraceSegments"]
assert.True(t, hasField)
}
@@ -361,7 +363,7 @@ func TestPutTraceSegments_ParsesSegmentFields(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
unprocessed, _ := resp["UnprocessedTraceSegments"].([]any)
if tt.wantParsed {
@@ -400,7 +402,7 @@ func TestPutTraceSegments_IndexesSegmentsForDownstreamAPIs(t *testing.T) {
require.Equal(t, http.StatusOK, sumRec.Code)
var sumResp map[string]any
- require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &sumResp))
+ require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &sumResp, jsonx.V1()))
summaries, ok := sumResp["TraceSummaries"].([]any)
require.True(t, ok)
@@ -411,7 +413,7 @@ func TestPutTraceSegments_IndexesSegmentsForDownstreamAPIs(t *testing.T) {
require.Equal(t, http.StatusOK, batchRec.Code)
var batchResp map[string]any
- require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batchResp))
+ require.NoError(t, json.Unmarshal(batchRec.Body.Bytes(), &batchResp, jsonx.V1()))
traces, ok := batchResp["Traces"].([]any)
require.True(t, ok)
@@ -437,7 +439,7 @@ func TestPutTraceSegments_MultipleTracesIndexedSeparately(t *testing.T) {
require.Equal(t, http.StatusOK, sumRec.Code)
var sumResp map[string]any
- require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &sumResp))
+ require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &sumResp, jsonx.V1()))
summaries, _ := sumResp["TraceSummaries"].([]any)
assert.Len(t, summaries, 3, "three separate traces must be indexed separately")
diff --git a/services/xray/handler_traces.go b/services/xray/handler_traces.go
index 5306a88cf0..e1dde65996 100644
--- a/services/xray/handler_traces.go
+++ b/services/xray/handler_traces.go
@@ -2,10 +2,11 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/page"
)
@@ -222,7 +223,7 @@ func validateSamplingStrategy(ss *samplingStrategyInput) error {
func (h *Handler) handleGetTraceSummaries(_ context.Context, body []byte) ([]byte, error) {
var in getTraceSummariesInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -280,7 +281,7 @@ func (h *Handler) handleGetTraceSummaries(_ context.Context, body []byte) ([]byt
// per-TraceSummary field.
"ApproximateTime": float64(time.Now().Unix()),
keyNextToken: pg.Next,
- })
+ }, jsonx.V1())
}
type batchGetTracesInput struct {
@@ -302,7 +303,7 @@ type traceOutput struct {
func (h *Handler) handleBatchGetTraces(_ context.Context, body []byte) ([]byte, error) {
var in batchGetTracesInput
if len(body) > 0 {
- if err := json.Unmarshal(body, &in); err != nil {
+ if err := json.Unmarshal(body, &in, jsonx.V1()); err != nil {
return nil, err
}
}
@@ -341,7 +342,7 @@ func (h *Handler) handleBatchGetTraces(_ context.Context, body []byte) ([]byte,
ID string `json:"id"`
}
- if err := json.Unmarshal([]byte(rawDoc), &hdr); err == nil {
+ if err := json.Unmarshal([]byte(rawDoc), &hdr, jsonx.V1()); err == nil {
segViews = append(segViews, batchSegmentOutput{
ID: hdr.ID,
Document: rawDoc,
@@ -360,5 +361,5 @@ func (h *Handler) handleBatchGetTraces(_ context.Context, body []byte) ([]byte,
return json.Marshal(map[string]any{
"Traces": traces,
"UnprocessedTraceIds": unprocessed,
- })
+ }, jsonx.V1())
}
diff --git a/services/xray/handler_traces_test.go b/services/xray/handler_traces_test.go
index f4524ad238..ad4f66d9a0 100644
--- a/services/xray/handler_traces_test.go
+++ b/services/xray/handler_traces_test.go
@@ -1,7 +1,7 @@
package xray_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/xray"
)
@@ -73,7 +74,7 @@ func TestHandler_BatchGetTraces(t *testing.T) {
assert.Equal(t, tt.wantStatus, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
if tt.wantTraces > 0 {
traces, ok := resp["Traces"].([]any)
@@ -110,7 +111,7 @@ func TestBatchGetTraces_SegmentShape(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
traces, ok := resp["Traces"].([]any)
require.True(t, ok)
@@ -163,7 +164,7 @@ func TestGetTraceSummaries_FaultFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["TraceSummaries"].([]any)
require.True(t, ok)
@@ -197,7 +198,7 @@ func TestGetTraceSummaries_TimeRangeType(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
// Should still return the trace — TimeRangeType is accepted without error.
summaries, ok := resp["TraceSummaries"].([]any)
@@ -225,7 +226,7 @@ func TestTraceSummary_FaultAndErrorPropagation(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["TraceSummaries"].([]any)
require.True(t, ok)
@@ -270,7 +271,7 @@ func TestGetTraceSummaries_ThrottleFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["TraceSummaries"].([]any)
require.True(t, ok)
@@ -337,7 +338,7 @@ func TestGetTraceSummariesTimeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["TraceSummaries"].([]any)
require.True(t, ok)
@@ -366,7 +367,7 @@ func TestGetTraceSummaries_TimeWindowExclusion(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["TraceSummaries"].([]any)
require.True(t, ok)
@@ -393,7 +394,7 @@ func TestTraceSummary_UsersFromAnnotations(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["TraceSummaries"].([]any)
require.True(t, ok)
@@ -429,7 +430,7 @@ func TestTraceSummary_OmitsForecastStatistics(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
summaries, ok := resp["TraceSummaries"].([]any)
require.True(t, ok)
@@ -453,7 +454,7 @@ func TestBatchGetTraces_UnprocessedForMissingIDs(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
unprocessed, ok := resp["UnprocessedTraceIds"].([]any)
require.True(t, ok)
@@ -545,7 +546,7 @@ func TestHandler_GetTraceSummaries_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec1.Code)
var resp1 map[string]any
- require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1))
+ require.NoError(t, json.Unmarshal(rec1.Body.Bytes(), &resp1, jsonx.V1()))
summaries1, _ := resp1["TraceSummaries"].([]any)
assert.Len(t, summaries1, defaultPageSize)
@@ -556,7 +557,7 @@ func TestHandler_GetTraceSummaries_Pagination(t *testing.T) {
require.Equal(t, http.StatusOK, rec2.Code)
var resp2 map[string]any
- require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2))
+ require.NoError(t, json.Unmarshal(rec2.Body.Bytes(), &resp2, jsonx.V1()))
summaries2, _ := resp2["TraceSummaries"].([]any)
assert.Len(t, summaries2, total-defaultPageSize)
@@ -581,7 +582,7 @@ func TestGetTraceSummaries_DurationFromSegmentTimes(t *testing.T) {
require.Equal(t, http.StatusOK, sumRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["TraceSummaries"].([]any)
require.Len(t, summaries, 1)
@@ -644,7 +645,7 @@ func TestGetTraceSummaries_BooleanFlagFields(t *testing.T) {
require.Equal(t, http.StatusOK, sumRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["TraceSummaries"].([]any)
require.Len(t, summaries, 1)
@@ -710,7 +711,7 @@ func TestGetTraceSummaries_HTTPFieldsPopulated(t *testing.T) {
require.Equal(t, http.StatusOK, sumRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["TraceSummaries"].([]any)
require.Len(t, summaries, 1)
@@ -749,7 +750,7 @@ func TestGetTraceSummaries_ServiceIDsFromSegments(t *testing.T) {
require.Equal(t, http.StatusOK, sumRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["TraceSummaries"].([]any)
require.Len(t, summaries, 1)
@@ -781,7 +782,7 @@ func TestGetTraceSummaries_EntryPointFromRootSegment(t *testing.T) {
require.Equal(t, http.StatusOK, sumRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["TraceSummaries"].([]any)
require.Len(t, summaries, 1)
@@ -812,7 +813,7 @@ func TestGetTraceSummaries_IsPartialWithoutRootSegment(t *testing.T) {
require.Equal(t, http.StatusOK, sumRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["TraceSummaries"].([]any)
require.Len(t, summaries, 1)
@@ -839,7 +840,7 @@ func TestGetTraceSummaries_TracesProcessedCount(t *testing.T) {
require.Equal(t, http.StatusOK, sumRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp, jsonx.V1()))
count, ok := resp["TracesProcessedCount"].(float64)
require.True(t, ok)
@@ -928,7 +929,7 @@ func TestGetTraceSummaries_FilterExpressions(t *testing.T) {
require.Equal(t, http.StatusOK, sumRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["TraceSummaries"].([]any)
assert.Len(t, summaries, tt.wantCount, "filter=%q wantCount=%d", tt.filter, tt.wantCount)
@@ -961,7 +962,7 @@ func TestGetTraceSummaries_OmitsForecastStatistics(t *testing.T) {
require.Equal(t, http.StatusOK, sumRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["TraceSummaries"].([]any)
require.Len(t, summaries, 1)
@@ -996,7 +997,7 @@ func TestGetTraceSummaries_AnnotationFilter(t *testing.T) {
require.Equal(t, http.StatusOK, sumRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["TraceSummaries"].([]any)
require.Len(t, summaries, 1)
@@ -1026,7 +1027,7 @@ func TestGetTraceSummaries_ResponseTimeFilter(t *testing.T) {
require.Equal(t, http.StatusOK, sumRec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(sumRec.Body.Bytes(), &resp, jsonx.V1()))
summaries, _ := resp["TraceSummaries"].([]any)
require.Len(t, summaries, 1)
@@ -1076,7 +1077,7 @@ func TestBatchGetTraces_UnprocessedForMissingTraces(t *testing.T) {
require.Equal(t, http.StatusOK, rec.Code)
var resp map[string]any
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
traces, _ := resp["Traces"].([]any)
assert.Empty(t, traces, "missing traces produce no Traces entries")
diff --git a/services/xray/helpers_test.go b/services/xray/helpers_test.go
index 7305063dfa..632e5faf76 100644
--- a/services/xray/helpers_test.go
+++ b/services/xray/helpers_test.go
@@ -2,7 +2,7 @@ package xray_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -12,6 +12,7 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/xray"
)
@@ -34,7 +35,7 @@ func buildFaultSegmentJSON(t *testing.T, traceID, name string, fault bool) strin
"fault": fault,
}
- data, err := json.Marshal(seg)
+ data, err := json.Marshal(seg, jsonx.V1())
require.NoError(t, err)
return string(data)
@@ -61,7 +62,7 @@ func doXrayRequest(t *testing.T, h *xray.Handler, path string, body any) *httpte
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -126,7 +127,7 @@ func segJSONWithHTTP(traceID, segID, name string, startTime, endTime float64, st
// segFmt formats a float64 timestamp as a JSON value string (no quotes).
func segFmt(ts float64) string {
- out, _ := json.Marshal(ts)
+ out, _ := json.Marshal(ts, jsonx.V1())
return string(out)
}
diff --git a/services/xray/multi_region_test.go b/services/xray/multi_region_test.go
index 60643394d9..46b552876b 100644
--- a/services/xray/multi_region_test.go
+++ b/services/xray/multi_region_test.go
@@ -2,7 +2,7 @@ package xray_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"strings"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/services/xray"
)
@@ -41,7 +42,7 @@ func decode(t *testing.T, body string) map[string]any {
out := map[string]any{}
if body != "" {
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
}
return out
diff --git a/services/xray/persistence.go b/services/xray/persistence.go
index efc499678c..72053117d9 100644
--- a/services/xray/persistence.go
+++ b/services/xray/persistence.go
@@ -2,9 +2,10 @@ package xray
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"time"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/persistence"
)
@@ -85,7 +86,7 @@ func (b *InMemoryBackend) Snapshot(ctx context.Context) []byte {
ResourceTags: b.resourceTags,
}
- data, err := json.Marshal(snap)
+ data, err := json.Marshal(snap, jsonx.V1())
if err != nil {
logger.Load(ctx).WarnContext(ctx, "xray: failed to marshal snapshot", "error", err)
@@ -182,7 +183,7 @@ func (b *InMemoryBackend) Restore(ctx context.Context, data []byte) error {
for _, t := range b.traces.All() {
for _, rawSeg := range t.Segments {
var seg Segment
- if err := json.Unmarshal([]byte(rawSeg), &seg); err == nil {
+ if err := json.Unmarshal([]byte(rawSeg), &seg, jsonx.V1()); err == nil {
seg.Document = rawSeg
b.parsedSegments.Put(&seg)
}
diff --git a/services/xray/policy_lockout.go b/services/xray/policy_lockout.go
index 8df55a7ffc..1b0bb9f231 100644
--- a/services/xray/policy_lockout.go
+++ b/services/xray/policy_lockout.go
@@ -1,18 +1,21 @@
package xray
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"path"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
type lockoutStatement struct {
- Effect string `json:"Effect"`
- Principal json.RawMessage `json:"Principal"`
- Action json.RawMessage `json:"Action"`
- Condition json.RawMessage `json:"Condition"`
- NotPrincipal json.RawMessage `json:"NotPrincipal"`
- NotAction json.RawMessage `json:"NotAction"`
+ Effect string `json:"Effect"`
+ Principal jsontext.Value `json:"Principal"`
+ Action jsontext.Value `json:"Action"`
+ Condition jsontext.Value `json:"Condition"`
+ NotPrincipal jsontext.Value `json:"NotPrincipal"`
+ NotAction jsontext.Value `json:"NotAction"`
}
// policyLocksOutCaller reports whether policyDocument has an unconditional Deny of xray:PutResourcePolicy that
@@ -23,16 +26,16 @@ func policyLocksOutCaller(policyDocument, callerARN string) bool {
}
var doc struct {
- Statement json.RawMessage `json:"Statement"`
+ Statement jsontext.Value `json:"Statement"`
}
- if json.Unmarshal([]byte(policyDocument), &doc) != nil {
+ if json.Unmarshal([]byte(policyDocument), &doc, jsonx.V1()) != nil {
return false
}
var stmts []lockoutStatement
- if json.Unmarshal(doc.Statement, &stmts) != nil {
+ if json.Unmarshal(doc.Statement, &stmts, jsonx.V1()) != nil {
var one lockoutStatement
- if json.Unmarshal(doc.Statement, &one) != nil {
+ if json.Unmarshal(doc.Statement, &one, jsonx.V1()) != nil {
return false
}
@@ -53,19 +56,19 @@ func policyLocksOutCaller(policyDocument, callerARN string) bool {
return false
}
-func stringList(raw json.RawMessage) []string {
+func stringList(raw jsontext.Value) []string {
var one string
- if json.Unmarshal(raw, &one) == nil {
+ if json.Unmarshal(raw, &one, jsonx.V1()) == nil {
return []string{one}
}
var many []string
- _ = json.Unmarshal(raw, &many)
+ _ = json.Unmarshal(raw, &many, jsonx.V1())
return many
}
-func matchesAny(raw json.RawMessage, target string, fold bool) bool {
+func matchesAny(raw jsontext.Value, target string, fold bool) bool {
for _, p := range stringList(raw) {
if fold {
p = strings.ToLower(p)
@@ -79,12 +82,12 @@ func matchesAny(raw json.RawMessage, target string, fold bool) bool {
return false
}
-func principalMatches(raw json.RawMessage, callerARN string) bool {
+func principalMatches(raw jsontext.Value, callerARN string) bool {
var wrapper struct {
- AWS json.RawMessage `json:"AWS"`
+ AWS jsontext.Value `json:"AWS"`
}
- if json.Unmarshal(raw, &wrapper) == nil && len(wrapper.AWS) > 0 {
+ if json.Unmarshal(raw, &wrapper, jsonx.V1()) == nil && len(wrapper.AWS) > 0 {
raw = wrapper.AWS
}
diff --git a/services/xray/policy_lockout_test.go b/services/xray/policy_lockout_test.go
index 96e9847274..fb5c7e4552 100644
--- a/services/xray/policy_lockout_test.go
+++ b/services/xray/policy_lockout_test.go
@@ -2,7 +2,7 @@ package xray_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"net/http/httptest"
"testing"
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/awsmeta"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestPutResourcePolicy_LockoutPrevention(t *testing.T) {
@@ -70,7 +71,7 @@ func TestPutResourcePolicy_LockoutPrevention(t *testing.T) {
h := newTestHandler(t)
payload, err := json.Marshal(map[string]any{
"PolicyName": "p", "PolicyDocument": tt.doc, "BypassPolicyLockoutCheck": tt.bypass,
- })
+ }, jsonx.V1())
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/PutResourcePolicy", bytes.NewReader(payload))
@@ -89,7 +90,7 @@ func TestPutResourcePolicy_LockoutPrevention(t *testing.T) {
if tt.wantType != "" {
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, tt.wantType, resp["__type"])
}
})
diff --git a/services/xray/resource_policies.go b/services/xray/resource_policies.go
index 232aa42e6d..7d48b795a4 100644
--- a/services/xray/resource_policies.go
+++ b/services/xray/resource_policies.go
@@ -1,11 +1,14 @@
package xray
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"sort"
"strconv"
"time"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func cloneResourcePolicy(p *ResourcePolicy) *ResourcePolicy {
@@ -21,8 +24,8 @@ func cloneResourcePolicy(p *ResourcePolicy) *ResourcePolicy {
// Returns ErrMalformedPolicyDocument if policyDocument is not valid JSON.
func (b *InMemoryBackend) PutResourcePolicy(policyName, policyDocument, revisionID string) (*ResourcePolicy, error) {
// Validate JSON.
- var js json.RawMessage
- if err := json.Unmarshal([]byte(policyDocument), &js); err != nil {
+ var js jsontext.Value
+ if err := json.Unmarshal([]byte(policyDocument), &js, jsonx.V1()); err != nil {
return nil, fmt.Errorf("%w: policy document is not valid JSON: %w", ErrMalformedPolicyDocument, err)
}
diff --git a/services/xray/service_graph_histogram_test.go b/services/xray/service_graph_histogram_test.go
index ae416b14f4..90c231b82e 100644
--- a/services/xray/service_graph_histogram_test.go
+++ b/services/xray/service_graph_histogram_test.go
@@ -1,13 +1,15 @@
package xray_test
import (
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestGetServiceGraph_Histograms(t *testing.T) {
@@ -39,7 +41,7 @@ func TestGetServiceGraph_Histograms(t *testing.T) {
} `json:"Edges"`
} `json:"Services"`
}
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
byName := map[string]int{}
for i, s := range resp.Services {
diff --git a/services/xray/trace_segments.go b/services/xray/trace_segments.go
index cd8f4f229a..9fa5ffa087 100644
--- a/services/xray/trace_segments.go
+++ b/services/xray/trace_segments.go
@@ -1,11 +1,13 @@
package xray
import (
- "encoding/json"
+ "encoding/json/v2"
"math"
"time"
"github.com/google/uuid"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// PutTraceSegments stores raw segment JSON strings, parses them into typed Segment structs,
@@ -19,7 +21,7 @@ func (b *InMemoryBackend) PutTraceSegments(segments []string) []string {
for _, seg := range segments {
var hdr segmentHeader
- if err := json.Unmarshal([]byte(seg), &hdr); err != nil || hdr.TraceID == "" {
+ if err := json.Unmarshal([]byte(seg), &hdr, jsonx.V1()); err != nil || hdr.TraceID == "" {
unprocessed = append(unprocessed, uuid.NewString())
continue
@@ -46,7 +48,7 @@ func (b *InMemoryBackend) PutTraceSegments(segments []string) []string {
// Parse into typed Segment struct and index it.
var parsed Segment
- if err := json.Unmarshal([]byte(seg), &parsed); err == nil {
+ if err := json.Unmarshal([]byte(seg), &parsed, jsonx.V1()); err == nil {
parsed.Document = seg
b.parsedSegments.Put(&parsed)
diff --git a/services/xray/whitebox_test.go b/services/xray/whitebox_test.go
index 468ab4c42c..7c2b89f564 100644
--- a/services/xray/whitebox_test.go
+++ b/services/xray/whitebox_test.go
@@ -2,7 +2,7 @@ package xray
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"net/http"
"net/http/httptest"
@@ -11,12 +11,14 @@ import (
"github.com/labstack/echo/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func doWhiteboxRequest(t *testing.T, h *Handler, path string, body any) *httptest.ResponseRecorder {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
e := echo.New()
@@ -53,6 +55,6 @@ func TestCreateSamplingRule_RuleLimitExceeded(t *testing.T) {
require.Equal(t, http.StatusBadRequest, rec.Code)
var resp map[string]string
- require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp))
+ require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp, jsonx.V1()))
assert.Equal(t, "RuleLimitExceededException", resp["__type"])
}
diff --git a/test/guards/hardcoded_arn_partition_guard_test.go b/test/guards/hardcoded_arn_partition_guard_test.go
index acde1927e4..f3e23c5995 100644
--- a/test/guards/hardcoded_arn_partition_guard_test.go
+++ b/test/guards/hardcoded_arn_partition_guard_test.go
@@ -1,7 +1,7 @@
package guards_test
import (
- "encoding/json"
+ "encoding/json/v2"
"os"
"path/filepath"
"sort"
@@ -9,6 +9,8 @@ import (
"testing"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const hardcodedARNAllowlistPath = "test/guards/testdata/hardcoded_arn_allowlist.json"
@@ -23,7 +25,7 @@ func TestNoNewHardcodedAWSPartitionARNs(t *testing.T) {
require.NoError(t, err)
var allow map[string]int
- require.NoError(t, json.Unmarshal(raw, &allow))
+ require.NoError(t, json.Unmarshal(raw, &allow, jsonx.V1()))
got := countHardcodedARNs(t)
diff --git a/test/guards/jsonv1_import_guard_test.go b/test/guards/jsonv1_import_guard_test.go
new file mode 100644
index 0000000000..59b01b3ddb
--- /dev/null
+++ b/test/guards/jsonv1_import_guard_test.go
@@ -0,0 +1,110 @@
+package guards_test
+
+import (
+ "go/parser"
+ "go/token"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+const jsonV1Path = "encoding/json"
+
+// importsJSONV1 reports whether src imports encoding/json (v1); string
+// literals containing the path do not count.
+func importsJSONV1(name string, src any) (bool, error) {
+ f, err := parser.ParseFile(token.NewFileSet(), name, src, parser.ImportsOnly)
+ if err != nil {
+ return false, err
+ }
+
+ for _, imp := range f.Imports {
+ if p, uerr := strconv.Unquote(imp.Path.Value); uerr == nil && p == jsonV1Path {
+ return true, nil
+ }
+ }
+
+ return false, nil
+}
+
+func TestImportsJSONV1(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ src string
+ want bool
+ }{
+ {name: "single", src: "package p\nimport \"encoding/json\"\n", want: true},
+ {name: "grouped", src: "package p\nimport (\n\t\"fmt\"\n\t\"encoding/json\"\n)\n", want: true},
+ {name: "aliased", src: "package p\nimport j \"encoding/json\"\n", want: true},
+ {name: "blank", src: "package p\nimport _ \"encoding/json\"\n", want: true},
+ {name: "v2", src: "package p\nimport \"encoding/json/v2\"\n", want: false},
+ {name: "jsontext", src: "package p\nimport \"encoding/json/jsontext\"\n", want: false},
+ {name: "raw string", src: "package p\nconst s = `import \"encoding/json\"`\n", want: false},
+ {name: "comment", src: "package p\n// import \"encoding/json\"\n", want: false},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ got, err := importsJSONV1("x.go", tt.src)
+ require.NoError(t, err)
+ assert.Equal(t, tt.want, got)
+ })
+ }
+}
+
+// TestNoEncodingJSONV1Import forbids encoding/json outside pkgs/jsonx, which
+// owns the v1 option set; everything else uses encoding/json/v2 and jsontext.
+func TestNoEncodingJSONV1Import(t *testing.T) {
+ t.Parallel()
+
+ var offenders []string
+
+ err := filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+
+ if d.IsDir() {
+ switch {
+ case path == filepath.Join("pkgs", "jsonx"),
+ d.Name() == "testdata", d.Name() == "node_modules",
+ path != "." && strings.HasPrefix(d.Name(), "."):
+ return filepath.SkipDir
+ }
+
+ return nil
+ }
+
+ if !strings.HasSuffix(path, ".go") {
+ return nil
+ }
+
+ src, readErr := os.ReadFile(path)
+ if readErr != nil {
+ return readErr
+ }
+
+ bad, parseErr := importsJSONV1(path, src)
+ if parseErr != nil {
+ return nil //nolint:nilerr // unparsable files are the compiler's concern
+ }
+
+ if bad {
+ offenders = append(offenders, path)
+ }
+
+ return nil
+ })
+ require.NoError(t, err)
+ assert.Empty(t, offenders, "import encoding/json/v2 (+ jsontext) and pass jsonx.V1() instead of encoding/json")
+}
diff --git a/test/guards/sdk_source_test.go b/test/guards/sdk_source_test.go
index e7303d86e3..5b73b1b763 100644
--- a/test/guards/sdk_source_test.go
+++ b/test/guards/sdk_source_test.go
@@ -2,7 +2,8 @@ package guards_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"os"
@@ -15,6 +16,8 @@ import (
"testing"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -80,16 +83,16 @@ func downloadSDKDirs(mods []sdkModule) (map[string]string, error) {
out, runErr := cmd.Output()
dirs := map[string]string{}
- dec := json.NewDecoder(bytes.NewReader(out))
+ dec := jsontext.NewDecoder(bytes.NewReader(out))
- for dec.More() {
+ for dec.PeekKind() != 0 {
var r struct {
Path string `json:"Path"`
Version string `json:"Version"`
Dir string `json:"Dir"`
Error string `json:"Error"`
}
- if err := dec.Decode(&r); err != nil {
+ if err := json.UnmarshalDecode(dec, &r, jsonx.V1()); err != nil {
return nil, err
}
diff --git a/test/integration/acm_test.go b/test/integration/acm_test.go
index b66b887248..8236b85b7b 100644
--- a/test/integration/acm_test.go
+++ b/test/integration/acm_test.go
@@ -8,7 +8,8 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"encoding/pem"
"io"
"math/big"
@@ -20,13 +21,14 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
func acmPost(t *testing.T, action string, body any) *http.Response {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, endpoint,
@@ -57,7 +59,7 @@ func acmReadJSON(t *testing.T, resp *http.Response) map[string]any {
defer resp.Body.Close()
var m map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&m))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &m, jsonx.V1()))
return m
}
@@ -218,7 +220,7 @@ func TestIntegration_ACM_ImportCertificate(t *testing.T) {
importedARN := struct {
CertificateArn string `json:"CertificateArn"`
}{}
- require.NoError(t, json.Unmarshal([]byte(importBody), &importedARN))
+ require.NoError(t, json.Unmarshal([]byte(importBody), &importedARN, jsonx.V1()))
// Describe the imported cert
descImportedResp := acmPost(t, "DescribeCertificate", map[string]any{"CertificateArn": importedARN.CertificateArn})
diff --git a/test/integration/apigateway_test.go b/test/integration/apigateway_test.go
index e3874a005a..f79703523c 100644
--- a/test/integration/apigateway_test.go
+++ b/test/integration/apigateway_test.go
@@ -2,7 +2,8 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -10,13 +11,14 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
func apigwPost(t *testing.T, action string, body any) *http.Response {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, endpoint, bytes.NewReader(bodyBytes))
@@ -46,7 +48,7 @@ func apigwReadJSON(t *testing.T, resp *http.Response) map[string]any {
defer resp.Body.Close()
var m map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&m))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &m, jsonx.V1()))
return m
}
diff --git a/test/integration/appsync_test.go b/test/integration/appsync_test.go
index 108c515adb..f3592ec079 100644
--- a/test/integration/appsync_test.go
+++ b/test/integration/appsync_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -255,7 +256,7 @@ func executeGraphQL(
body["variables"] = variables
}
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, gqlEndpoint, bytes.NewReader(bodyBytes))
@@ -284,7 +285,7 @@ func executeGraphQL(
)
var result map[string]any
- require.NoError(t, json.Unmarshal(respBytes, &result))
+ require.NoError(t, json.Unmarshal(respBytes, &result, jsonx.V1()))
return result
}
diff --git a/test/integration/awsconfig_test.go b/test/integration/awsconfig_test.go
index 91ca4d57dc..ee579dbd68 100644
--- a/test/integration/awsconfig_test.go
+++ b/test/integration/awsconfig_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -10,13 +10,14 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
func awsconfigPost(t *testing.T, action string, body any) *http.Response {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, endpoint, bytes.NewReader(bodyBytes))
diff --git a/test/integration/azurearm_test.go b/test/integration/azurearm_test.go
index 1289bb97b6..6be7a08b56 100644
--- a/test/integration/azurearm_test.go
+++ b/test/integration/azurearm_test.go
@@ -20,7 +20,8 @@ package integration_test
import (
"crypto/tls"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"strings"
"testing"
@@ -29,6 +30,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -79,7 +81,7 @@ func armDecodeJSON(t *testing.T, resp *http.Response) map[string]any {
t.Helper()
var body map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&body))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &body, jsonx.V1()))
return body
}
@@ -96,7 +98,7 @@ func TestIntegration_AzureARM_MetadataAndToken(t *testing.T) {
require.Equal(t, http.StatusOK, resp.StatusCode)
var doc map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&doc))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &doc, jsonx.V1()))
assert.Equal(t, "gopherstack", doc["name"])
assert.NotEmpty(t, doc["resourceManagerEndpoint"])
diff --git a/test/integration/azureservicebus_test.go b/test/integration/azureservicebus_test.go
index b31dbb5727..5b36dcac16 100644
--- a/test/integration/azureservicebus_test.go
+++ b/test/integration/azureservicebus_test.go
@@ -16,7 +16,7 @@ package integration_test
// namespace's Brokered Messaging API.
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"strings"
@@ -27,6 +27,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -97,7 +98,7 @@ func TestIntegration_AzureServiceBus_QueueSendPeekLockComplete(t *testing.T) {
var bp sbBrokerProperties
- require.NoError(t, json.Unmarshal([]byte(resp.Header.Get("Brokerproperties")), &bp))
+ require.NoError(t, json.Unmarshal([]byte(resp.Header.Get("Brokerproperties")), &bp, jsonx.V1()))
require.NotEmpty(t, bp.MessageID)
require.NotEmpty(t, bp.LockToken)
diff --git a/test/integration/chaos_test.go b/test/integration/chaos_test.go
index ccb8436914..916a88eb66 100644
--- a/test/integration/chaos_test.go
+++ b/test/integration/chaos_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -19,6 +19,7 @@ import (
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -103,7 +104,7 @@ func newDynamoDBClientAt(t *testing.T, ep string) *dynamodb.Client {
func postChaosRules(t *testing.T, ep string, rules any) {
t.Helper()
- body, err := json.Marshal(rules)
+ body, err := json.Marshal(rules, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(
@@ -128,7 +129,7 @@ func postChaosRules(t *testing.T, ep string, rules any) {
func postChaosEffects(t *testing.T, ep string, effects chaosNetworkEffects) {
t.Helper()
- body, err := json.Marshal(effects)
+ body, err := json.Marshal(effects, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(
diff --git a/test/integration/cloudwatchlogs_test.go b/test/integration/cloudwatchlogs_test.go
index a955255ca4..98aba361ba 100644
--- a/test/integration/cloudwatchlogs_test.go
+++ b/test/integration/cloudwatchlogs_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"compress/gzip"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"testing"
"time"
@@ -16,6 +16,8 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// decompressGzip decompresses gzip-compressed bytes.
@@ -349,7 +351,7 @@ func TestIntegration_CloudWatchLogs_SubscriptionFilter_KinesisDelivery(t *testin
// Verify the payload matches the CloudWatch Logs subscription format.
var payload map[string]any
- require.NoError(t, json.Unmarshal(decompressed, &payload))
+ require.NoError(t, json.Unmarshal(decompressed, &payload, jsonx.V1()))
assert.Equal(t, "DATA_MESSAGE", payload["messageType"])
assert.Equal(t, groupName, payload["logGroup"])
assert.Equal(t, logStreamName, payload["logStream"])
diff --git a/test/integration/cognito_test.go b/test/integration/cognito_test.go
index 8c032dbfc1..12a06d052f 100644
--- a/test/integration/cognito_test.go
+++ b/test/integration/cognito_test.go
@@ -3,7 +3,7 @@ package integration_test
import (
"crypto/rsa"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"math/big"
@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -122,7 +123,7 @@ func TestIntegration_CognitoIDP_FullFlow(t *testing.T) {
Alg string `json:"alg"`
} `json:"keys"`
}
- require.NoError(t, json.Unmarshal(jwksBody, &jwks), "JWKS should be valid JSON")
+ require.NoError(t, json.Unmarshal(jwksBody, &jwks, jsonx.V1()), "JWKS should be valid JSON")
require.Len(t, jwks.Keys, 1, "JWKS should have exactly one key")
assert.Equal(t, "RSA", jwks.Keys[0].Kty)
assert.Equal(t, "sig", jwks.Keys[0].Use)
diff --git a/test/integration/cosmosdb_test.go b/test/integration/cosmosdb_test.go
index 6e092acb70..31efe7000a 100644
--- a/test/integration/cosmosdb_test.go
+++ b/test/integration/cosmosdb_test.go
@@ -1,13 +1,15 @@
package integration_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// cosmosDBDevMasterKey is the real Cosmos DB Local Emulator's published
@@ -81,7 +83,7 @@ func TestIntegration_CosmosDB_DatabaseContainerDocumentLifecycle(t *testing.T) {
}
doc := testDoc{ID: "doc1", PK: "partA", Value: 42}
- body, err := json.Marshal(doc)
+ body, err := json.Marshal(doc, jsonx.V1())
require.NoError(t, err)
pk := azcosmos.NewPartitionKeyString("partA")
@@ -95,7 +97,7 @@ func TestIntegration_CosmosDB_DatabaseContainerDocumentLifecycle(t *testing.T) {
require.NoError(t, err)
var gotDoc testDoc
- require.NoError(t, json.Unmarshal(readResp.Value, &gotDoc))
+ require.NoError(t, json.Unmarshal(readResp.Value, &gotDoc, jsonx.V1()))
assert.Equal(t, doc, gotDoc)
// Query via SQL.
@@ -113,7 +115,7 @@ func TestIntegration_CosmosDB_DatabaseContainerDocumentLifecycle(t *testing.T) {
for _, item := range page.Items {
var qd testDoc
- require.NoError(t, json.Unmarshal(item, &qd))
+ require.NoError(t, json.Unmarshal(item, &qd, jsonx.V1()))
if qd.ID == "doc1" {
found = true
@@ -125,7 +127,7 @@ func TestIntegration_CosmosDB_DatabaseContainerDocumentLifecycle(t *testing.T) {
// UpsertItem (replaces existing doc1).
upserted := testDoc{ID: "doc1", PK: "partA", Value: 100}
- upsertBody, err := json.Marshal(upserted)
+ upsertBody, err := json.Marshal(upserted, jsonx.V1())
require.NoError(t, err)
_, err = containerClient.UpsertItem(ctx, pk, upsertBody, nil)
@@ -135,7 +137,7 @@ func TestIntegration_CosmosDB_DatabaseContainerDocumentLifecycle(t *testing.T) {
require.NoError(t, err)
var afterUpsert testDoc
- require.NoError(t, json.Unmarshal(readResp.Value, &afterUpsert))
+ require.NoError(t, json.Unmarshal(readResp.Value, &afterUpsert, jsonx.V1()))
assert.Equal(t, 100, afterUpsert.Value)
// DeleteItem.
diff --git a/test/integration/ecrpublic_test.go b/test/integration/ecrpublic_test.go
index 06fb878e0f..411e38fd53 100644
--- a/test/integration/ecrpublic_test.go
+++ b/test/integration/ecrpublic_test.go
@@ -3,7 +3,7 @@ package integration_test
import (
"crypto/sha256"
"encoding/hex"
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -13,6 +13,8 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -102,7 +104,7 @@ func TestIntegration_ECRPublic_RepositoryImageLifecycle(t *testing.T) {
"mediaType": ecrPublicManifestMediaType,
"config": map[string]any{"mediaType": ecrPublicConfigMediaType, "digest": configDigest},
"layers": []map[string]any{{"digest": layerDigest}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
put, err := client.PutImage(ctx, &ecrpublicsdk.PutImageInput{
@@ -117,7 +119,7 @@ func TestIntegration_ECRPublic_RepositoryImageLifecycle(t *testing.T) {
"schemaVersion": 2,
"mediaType": ecrPublicIndexMediaType,
"manifests": []map[string]any{{"digest": childDigest}},
- })
+ }, jsonx.V1())
require.NoError(t, err)
_, err = client.PutImage(ctx, &ecrpublicsdk.PutImageInput{
diff --git a/test/integration/eks_irsa_test.go b/test/integration/eks_irsa_test.go
index b980cad68e..38744c8512 100644
--- a/test/integration/eks_irsa_test.go
+++ b/test/integration/eks_irsa_test.go
@@ -2,7 +2,8 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"io"
"net/http"
"os"
@@ -22,6 +23,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -33,7 +35,7 @@ type irsaToken struct {
func mintIRSAToken(t *testing.T, cluster string, body map[string]any) irsaToken {
t.Helper()
- raw, err := json.Marshal(body)
+ raw, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
resp, err := testclient.New(t).Post(endpoint+"/_gopherstack/eks/"+cluster+"/serviceaccount-token",
@@ -47,7 +49,7 @@ func mintIRSAToken(t *testing.T, cluster string, body map[string]any) irsaToken
require.Equal(t, http.StatusOK, resp.StatusCode, string(out))
var tok irsaToken
- require.NoError(t, json.Unmarshal(out, &tok))
+ require.NoError(t, json.Unmarshal(out, &tok, jsonx.V1()))
return tok
}
@@ -100,7 +102,7 @@ func TestIntegration_EKS_IRSA(t *testing.T) {
defer disc.Body.Close()
var discDoc map[string]any
- require.NoError(t, json.NewDecoder(disc.Body).Decode(&discDoc))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(disc.Body), &discDoc, jsonx.V1()))
assert.Equal(t, issuer, discDoc["issuer"])
provider, err := iamClient.CreateOpenIDConnectProvider(ctx, &iamsdk.CreateOpenIDConnectProviderInput{
diff --git a/test/integration/elasticsearch_test.go b/test/integration/elasticsearch_test.go
index 3218788db5..769dfe4d3b 100644
--- a/test/integration/elasticsearch_test.go
+++ b/test/integration/elasticsearch_test.go
@@ -2,7 +2,8 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -13,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -79,7 +81,7 @@ func doElasticsearchRequest(t *testing.T, method, path string, body any) (int, m
var reqBody io.Reader
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
reqBody = bytes.NewReader(b)
@@ -99,7 +101,7 @@ func doElasticsearchRequest(t *testing.T, method, path string, body any) (int, m
var result map[string]any
- if err = json.NewDecoder(resp.Body).Decode(&result); err != nil {
+ if err = json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &result, jsonx.V1()); err != nil {
result = nil
}
diff --git a/test/integration/firehose_delivery_test.go b/test/integration/firehose_delivery_test.go
index a270dc2cdf..4e7b019c07 100644
--- a/test/integration/firehose_delivery_test.go
+++ b/test/integration/firehose_delivery_test.go
@@ -4,7 +4,7 @@ import (
"bytes"
"context"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"log/slog"
"net/http"
@@ -18,6 +18,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
pkglogger "github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
@@ -73,7 +74,7 @@ func (keepOrDropLambdaInvoker) InvokeFunction(
payload []byte,
) ([]byte, int, error) {
var event transformEvent
- if err := json.Unmarshal(payload, &event); err != nil {
+ if err := json.Unmarshal(payload, &event, jsonx.V1()); err != nil {
return nil, 500, err
}
@@ -87,7 +88,7 @@ func (keepOrDropLambdaInvoker) InvokeFunction(
out[i] = transformRecord{RecordID: rec.RecordID, Result: result, Data: rec.Data}
}
- resp, err := json.Marshal(map[string]any{"records": out})
+ resp, err := json.Marshal(map[string]any{"records": out}, jsonx.V1())
if err != nil {
return nil, 500, err
}
@@ -440,7 +441,7 @@ func TestIntegration_Firehose_HTTP_UpdateDestination(t *testing.T) {
doPost := func(action string, body any) (int, string) {
t.Helper()
- bodyBytes, marshalErr := json.Marshal(body)
+ bodyBytes, marshalErr := json.Marshal(body, jsonx.V1())
require.NoError(t, marshalErr)
req, reqCreateErr := http.NewRequestWithContext(
t.Context(), http.MethodPost, srv.URL,
diff --git a/test/integration/firehose_test.go b/test/integration/firehose_test.go
index 79c5815720..155a48777a 100644
--- a/test/integration/firehose_test.go
+++ b/test/integration/firehose_test.go
@@ -3,7 +3,7 @@ package integration_test
import (
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -11,13 +11,14 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
func firehosePost(t *testing.T, action string, body any) *http.Response {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, endpoint, bytes.NewReader(bodyBytes))
diff --git a/test/integration/fis_test.go b/test/integration/fis_test.go
index 09879c9e05..0f8bf49784 100644
--- a/test/integration/fis_test.go
+++ b/test/integration/fis_test.go
@@ -2,7 +2,8 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -14,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -32,7 +34,7 @@ func fisRequest(
var bodyReader *bytes.Reader
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
bodyReader = bytes.NewReader(b)
} else {
@@ -55,7 +57,7 @@ func fisBody(t *testing.T, resp *http.Response, out any) {
defer func() { _ = resp.Body.Close() }()
- require.NoError(t, json.NewDecoder(resp.Body).Decode(out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), out, jsonx.V1()))
}
// ----------------------------------------
@@ -309,7 +311,7 @@ func TestIntegration_FIS_ExperimentLifecycle(t *testing.T) {
defer func() { _ = getResp.Body.Close() }()
- if err := json.NewDecoder(getResp.Body).Decode(&getResult); err != nil {
+ if err := json.UnmarshalDecode(jsontext.NewDecoder(getResp.Body), &getResult, jsonx.V1()); err != nil {
return false
}
@@ -591,7 +593,11 @@ func TestIntegration_FIS_KinesisThroughputException(t *testing.T) {
return false
}
- if decodeErr := json.NewDecoder(getResp.Body).Decode(&getResult); decodeErr != nil {
+ if decodeErr := json.UnmarshalDecode(
+ jsontext.NewDecoder(getResp.Body),
+ &getResult,
+ jsonx.V1(),
+ ); decodeErr != nil {
return false
}
@@ -642,7 +648,11 @@ func TestIntegration_FIS_KinesisThroughputException(t *testing.T) {
return false
}
- if decodeErr := json.NewDecoder(getResp.Body).Decode(&getResult); decodeErr != nil {
+ if decodeErr := json.UnmarshalDecode(
+ jsontext.NewDecoder(getResp.Body),
+ &getResult,
+ jsonx.V1(),
+ ); decodeErr != nil {
return false
}
diff --git a/test/integration/gcp_iam_test.go b/test/integration/gcp_iam_test.go
index 1c21758a5f..3248783535 100644
--- a/test/integration/gcp_iam_test.go
+++ b/test/integration/gcp_iam_test.go
@@ -1,7 +1,7 @@
package integration_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
iam "cloud.google.com/go/iam/admin/apiv1"
@@ -11,6 +11,8 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/fieldmaskpb"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func newIAMAdminClient(t *testing.T) *iam.IamClient {
@@ -57,7 +59,7 @@ func TestIntegration_GCP_IAM_ServiceAccountAndKey(t *testing.T) {
ClientEmail string `json:"client_email"`
}
- require.NoError(t, json.Unmarshal(key.GetPrivateKeyData(), &kf))
+ require.NoError(t, json.Unmarshal(key.GetPrivateKeyData(), &kf, jsonx.V1()))
assert.Equal(t, "service_account", kf.Type)
assert.Equal(t, sa.GetEmail(), kf.ClientEmail)
diff --git a/test/integration/glacier_test.go b/test/integration/glacier_test.go
index abd34e56a0..2d4761795f 100644
--- a/test/integration/glacier_test.go
+++ b/test/integration/glacier_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"net/http"
"testing"
"time"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -212,7 +213,7 @@ func glacierAddTags(t *testing.T, vaultName string, tags map[string]string) erro
Tags map[string]string `json:"Tags"`
}
- payload, err := json.Marshal(addTagsReq{Tags: tags})
+ payload, err := json.Marshal(addTagsReq{Tags: tags}, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost,
diff --git a/test/integration/iot_test.go b/test/integration/iot_test.go
index 52d96d6ad5..1809c7f436 100644
--- a/test/integration/iot_test.go
+++ b/test/integration/iot_test.go
@@ -2,7 +2,8 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -16,6 +17,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/sqs"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -26,7 +28,7 @@ func iotRequest(t *testing.T, method, path string, body any) *http.Response {
var reqBody io.Reader
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
reqBody = bytes.NewReader(b)
@@ -93,7 +95,7 @@ func TestIntegration_IoT_ThingLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]string
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
assert.Equal(t, thingName, out["thingName"])
assert.NotEmpty(t, out["thingArn"])
assert.NotEmpty(t, out["thingId"])
@@ -110,7 +112,7 @@ func TestIntegration_IoT_ThingLifecycle(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
assert.Equal(t, thingName, out["thingName"])
},
},
@@ -197,7 +199,7 @@ func TestIntegration_IoT_PolicyAndRule(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
// AWS GetTopicRule wraps fields under a "rule" object with "ruleArn" at top level.
rule, ok := out["rule"].(map[string]any)
require.True(t, ok, "response should contain a 'rule' object")
@@ -246,7 +248,7 @@ func TestIntegration_IoT_DescribeEndpoint(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
var out map[string]string
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&out))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &out, jsonx.V1()))
assert.NotEmpty(t, out["endpointAddress"])
})
}
diff --git a/test/integration/lambda_esm_test.go b/test/integration/lambda_esm_test.go
index 8ed06edc41..89bba917e8 100644
--- a/test/integration/lambda_esm_test.go
+++ b/test/integration/lambda_esm_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http/httptest"
"strconv"
@@ -23,6 +23,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/container"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc/portalloctest"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -207,7 +208,7 @@ func TestIntegration_Lambda_SQS_ESM(t *testing.T) {
"Code": map[string]string{"ImageUri": lambdaEchoImage},
"Role": "arn:aws:iam::000000000000:role/lambda-test",
"Timeout": int(esmInvokeTimeout.Seconds()),
- })
+ }, jsonx.V1())
require.NoError(t, err)
createFnResp, err := doLambdaRequest(ctx, "POST",
diff --git a/test/integration/lambda_new_ops_test.go b/test/integration/lambda_new_ops_test.go
index 1fb7cf5574..8bb2f65475 100644
--- a/test/integration/lambda_new_ops_test.go
+++ b/test/integration/lambda_new_ops_test.go
@@ -3,7 +3,7 @@ package integration_test
import (
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"log/slog"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc/portalloctest"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -58,7 +59,7 @@ func createLambdaFunction(ctx context.Context, t *testing.T, serverURL, fnName s
"PackageType": "Image",
"Code": map[string]string{"ImageUri": "test:latest"},
"Role": "arn:aws:iam::000000000000:role/test",
- })
+ }, jsonx.V1())
require.NoError(t, err)
resp, err := doLambdaRequest(ctx, http.MethodPost,
@@ -133,7 +134,7 @@ func TestLambda_AddPermission(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- body, err := json.Marshal(tt.body)
+ body, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
targetFn := fnName
@@ -153,7 +154,7 @@ func TestLambda_AddPermission(t *testing.T) {
require.NoError(t, readErr)
var out map[string]any
- require.NoError(t, json.Unmarshal(rawBody, &out))
+ require.NoError(t, json.Unmarshal(rawBody, &out, jsonx.V1()))
assert.NotEmpty(t, out["Statement"])
}
})
@@ -174,7 +175,7 @@ func TestLambda_AddPermission_DuplicateStatementId(t *testing.T) {
"StatementId": "AllowS3",
"Action": "lambda:InvokeFunction",
"Principal": "s3.amazonaws.com",
- })
+ }, jsonx.V1())
require.NoError(t, err)
url := fmt.Sprintf("%s/2015-03-31/functions/%s/policy", server.URL, fnName)
@@ -210,7 +211,7 @@ func TestLambda_GetAccountSettings(t *testing.T) {
require.NoError(t, err)
var out lambdapkg.AccountSettingsOutput
- require.NoError(t, json.Unmarshal(respBody, &out))
+ require.NoError(t, json.Unmarshal(respBody, &out, jsonx.V1()))
require.NotNil(t, out.AccountLimit)
assert.Positive(t, out.AccountLimit.ConcurrentExecutions)
@@ -238,7 +239,7 @@ func TestLambda_CodeSigningConfig(t *testing.T) {
},
},
"Description": "test config",
- })
+ }, jsonx.V1())
require.NoError(t, err)
createResp, err := doLambdaRequest(ctx, http.MethodPost,
@@ -251,7 +252,7 @@ func TestLambda_CodeSigningConfig(t *testing.T) {
require.NoError(t, err)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRespBody, &createOut))
+ require.NoError(t, json.Unmarshal(createRespBody, &createOut, jsonx.V1()))
cscData, ok := createOut["CodeSigningConfig"].(map[string]any)
require.True(t, ok, "expected CodeSigningConfig in response")
@@ -279,13 +280,13 @@ func TestLambda_CodeSigningConfig(t *testing.T) {
require.NoError(t, err)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listBody, &listOut))
+ require.NoError(t, json.Unmarshal(listBody, &listOut, jsonx.V1()))
configs, ok := listOut["CodeSigningConfigs"].([]any)
require.True(t, ok)
assert.Len(t, configs, 1)
// Put function code signing config
- putBody, err := json.Marshal(map[string]any{"CodeSigningConfigArn": cscARN})
+ putBody, err := json.Marshal(map[string]any{"CodeSigningConfigArn": cscARN}, jsonx.V1())
require.NoError(t, err)
putResp, err := doLambdaRequest(ctx, http.MethodPut,
@@ -307,7 +308,7 @@ func TestLambda_CodeSigningConfig(t *testing.T) {
require.NoError(t, err)
var getFnCSCOut map[string]any
- require.NoError(t, json.Unmarshal(getFnCSCBody, &getFnCSCOut))
+ require.NoError(t, json.Unmarshal(getFnCSCBody, &getFnCSCOut, jsonx.V1()))
assert.Equal(t, cscARN, getFnCSCOut["CodeSigningConfigArn"])
// List functions by code signing config
@@ -364,7 +365,7 @@ func TestLambda_CapacityProvider(t *testing.T) {
"SubnetIds": []string{"subnet-1"},
"SecurityGroupIds": []string{"sg-1"},
},
- })
+ }, jsonx.V1())
require.NoError(t, err)
createResp, err := doLambdaRequest(ctx, http.MethodPost,
@@ -377,7 +378,7 @@ func TestLambda_CapacityProvider(t *testing.T) {
require.NoError(t, err)
var createOut map[string]any
- require.NoError(t, json.Unmarshal(createRespBody, &createOut))
+ require.NoError(t, json.Unmarshal(createRespBody, &createOut, jsonx.V1()))
cpData, ok := createOut["CapacityProvider"].(map[string]any)
require.True(t, ok, "expected CapacityProvider in response")
@@ -410,7 +411,7 @@ func TestLambda_CapacityProvider(t *testing.T) {
require.NoError(t, err)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listBody, &listOut))
+ require.NoError(t, json.Unmarshal(listBody, &listOut, jsonx.V1()))
providers, ok := listOut["CapacityProviders"].([]any)
require.True(t, ok)
assert.Len(t, providers, 1)
@@ -418,7 +419,7 @@ func TestLambda_CapacityProvider(t *testing.T) {
// Update capacity provider
updateBody, err := json.Marshal(map[string]any{
"CapacityProviderScalingConfig": map[string]any{"MaxVCpuCount": 200},
- })
+ }, jsonx.V1())
require.NoError(t, err)
updateResp, err := doLambdaRequest(ctx, http.MethodPut,
@@ -432,7 +433,7 @@ func TestLambda_CapacityProvider(t *testing.T) {
require.NoError(t, err)
var updateOut map[string]any
- require.NoError(t, json.Unmarshal(updateRespBody, &updateOut))
+ require.NoError(t, json.Unmarshal(updateRespBody, &updateOut, jsonx.V1()))
cpUpdated, ok := updateOut["CapacityProvider"].(map[string]any)
require.True(t, ok)
scalingConfig, ok := cpUpdated["CapacityProviderScalingConfig"].(map[string]any)
@@ -453,7 +454,7 @@ func TestLambda_CapacityProvider(t *testing.T) {
require.NoError(t, err)
var delOut map[string]any
- require.NoError(t, json.Unmarshal(delRespBody, &delOut))
+ require.NoError(t, json.Unmarshal(delRespBody, &delOut, jsonx.V1()))
cpDeleted, ok := delOut["CapacityProvider"].(map[string]any)
require.True(t, ok, "expected CapacityProvider in response")
assert.Contains(t, cpDeleted["CapacityProviderArn"], "test-provider")
@@ -497,7 +498,7 @@ func TestLambda_FunctionUrlConfig_2021(t *testing.T) {
createLambdaFunction(ctx, t, server.URL, fnName)
// Create function URL config via 2021 path
- createBody, err := json.Marshal(map[string]any{"AuthType": "NONE"})
+ createBody, err := json.Marshal(map[string]any{"AuthType": "NONE"}, jsonx.V1())
require.NoError(t, err)
createResp, err := doLambdaRequest(ctx, http.MethodPost,
@@ -518,7 +519,7 @@ func TestLambda_FunctionUrlConfig_2021(t *testing.T) {
require.NoError(t, err)
var cfg lambdapkg.FunctionURLConfig
- require.NoError(t, json.Unmarshal(getBody, &cfg))
+ require.NoError(t, json.Unmarshal(getBody, &cfg, jsonx.V1()))
assert.Equal(t, "NONE", cfg.AuthType)
// List function URL configs via 2021 path
@@ -532,13 +533,13 @@ func TestLambda_FunctionUrlConfig_2021(t *testing.T) {
require.NoError(t, err)
var listOut map[string]any
- require.NoError(t, json.Unmarshal(listBody, &listOut))
+ require.NoError(t, json.Unmarshal(listBody, &listOut, jsonx.V1()))
urlConfigs, ok := listOut["FunctionUrlConfigs"].([]any)
require.True(t, ok)
assert.Len(t, urlConfigs, 1)
// Update function URL config via 2021 path
- updateBody, err := json.Marshal(map[string]any{"AuthType": "AWS_IAM"})
+ updateBody, err := json.Marshal(map[string]any{"AuthType": "AWS_IAM"}, jsonx.V1())
require.NoError(t, err)
updateResp, err := doLambdaRequest(ctx, http.MethodPut,
diff --git a/test/integration/lambda_test.go b/test/integration/lambda_test.go
index 61c5494e7e..be2efb94dd 100644
--- a/test/integration/lambda_test.go
+++ b/test/integration/lambda_test.go
@@ -4,7 +4,7 @@ import (
"archive/tar"
"bytes"
"context"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"log/slog"
@@ -23,6 +23,7 @@ import (
dockerapifilters "github.com/blackbirdworks/gopherstack/internal/dockercompat/api/types/filters"
dockerapiimage "github.com/blackbirdworks/gopherstack/internal/dockercompat/api/types/image"
dockerclient "github.com/blackbirdworks/gopherstack/internal/dockercompat/client"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/container"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -163,7 +164,7 @@ func TestLambdaIntegration_Invoke_DockerEchoContainer(t *testing.T) {
"Code": map[string]string{"ImageUri": lambdaEchoImage},
"Role": "arn:aws:iam::000000000000:role/lambda-test",
"Timeout": 15, // 15s gives enough time for container cold start in CI
- })
+ }, jsonx.V1())
require.NoError(t, err)
createResp, err := doLambdaRequest(ctx, http.MethodPost, server.URL+"/2015-03-31/functions",
@@ -277,7 +278,7 @@ func buildEchoLambdaImage(ctx context.Context, t *testing.T) {
Error string `json:"error"`
}
- if jsonErr := json.Unmarshal([]byte(line), &msg); jsonErr == nil && msg.Error != "" {
+ if jsonErr := json.Unmarshal([]byte(line), &msg, jsonx.V1()); jsonErr == nil && msg.Error != "" {
t.Skipf("Docker image build failed (infrastructure error — skipping test): %s", msg.Error)
}
}
diff --git a/test/integration/lambda_url_test.go b/test/integration/lambda_url_test.go
index 5b2d90431a..ed78d18226 100644
--- a/test/integration/lambda_url_test.go
+++ b/test/integration/lambda_url_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"io"
"log/slog"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/portalloc/portalloctest"
"github.com/blackbirdworks/gopherstack/pkgs/service"
@@ -57,7 +58,7 @@ func TestLambdaFunctionURL_CreateGetDelete(t *testing.T) {
"PackageType": "Image",
"Code": map[string]string{"ImageUri": "test:latest"},
"Role": "arn:aws:iam::000000000000:role/test",
- })
+ }, jsonx.V1())
require.NoError(t, err)
resp, err := doLambdaRequest(ctx, http.MethodPost, server.URL+"/2015-03-31/functions",
@@ -67,7 +68,7 @@ func TestLambdaFunctionURL_CreateGetDelete(t *testing.T) {
require.Equal(t, http.StatusCreated, resp.StatusCode)
// Create function URL config
- urlBody, err := json.Marshal(map[string]any{"AuthType": "NONE"})
+ urlBody, err := json.Marshal(map[string]any{"AuthType": "NONE"}, jsonx.V1())
require.NoError(t, err)
urlResp, err := doLambdaRequest(ctx, http.MethodPost,
@@ -80,7 +81,7 @@ func TestLambdaFunctionURL_CreateGetDelete(t *testing.T) {
var cfg lambdapkg.FunctionURLConfig
respBody, err := io.ReadAll(urlResp.Body)
require.NoError(t, err)
- require.NoError(t, json.Unmarshal(respBody, &cfg))
+ require.NoError(t, json.Unmarshal(respBody, &cfg, jsonx.V1()))
assert.NotEmpty(t, cfg.FunctionURL, "FunctionURL should not be empty")
assert.Equal(t, "NONE", cfg.AuthType)
@@ -100,7 +101,7 @@ func TestLambdaFunctionURL_CreateGetDelete(t *testing.T) {
var getCfg lambdapkg.FunctionURLConfig
getBody, err := io.ReadAll(getResp.Body)
require.NoError(t, err)
- require.NoError(t, json.Unmarshal(getBody, &getCfg))
+ require.NoError(t, json.Unmarshal(getBody, &getCfg, jsonx.V1()))
assert.Equal(t, cfg.FunctionURL, getCfg.FunctionURL)
// Delete function URL config
diff --git a/test/integration/lambda_zip_test.go b/test/integration/lambda_zip_test.go
index 0178191953..4a195a7864 100644
--- a/test/integration/lambda_zip_test.go
+++ b/test/integration/lambda_zip_test.go
@@ -4,7 +4,7 @@ import (
"archive/zip"
"bytes"
"encoding/base64"
- "encoding/json"
+ "encoding/json/v2"
"io"
"log/slog"
"net/http"
@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
pkglogger "github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
lambdapkg "github.com/blackbirdworks/gopherstack/services/lambda"
@@ -75,7 +76,7 @@ func TestIntegration_Lambda_Zip_CRUD(t *testing.T) {
"Handler": "index.handler",
"Code": map[string]string{"ZipFile": zipB64},
"Role": "arn:aws:iam::000000000000:role/test",
- })
+ }, jsonx.V1())
require.NoError(t, err)
createResp, err := doLambdaRequest(ctx, http.MethodPost,
@@ -87,7 +88,7 @@ func TestIntegration_Lambda_Zip_CRUD(t *testing.T) {
var created lambdapkg.FunctionConfiguration
body, err := io.ReadAll(createResp.Body)
require.NoError(t, err)
- require.NoError(t, json.Unmarshal(body, &created))
+ require.NoError(t, json.Unmarshal(body, &created, jsonx.V1()))
assert.Equal(t, "zip-crud-fn", created.FunctionName)
assert.Equal(t, lambdapkg.PackageTypeZip, created.PackageType)
assert.Equal(t, "python3.12", created.Runtime)
@@ -103,7 +104,7 @@ func TestIntegration_Lambda_Zip_CRUD(t *testing.T) {
getBody, err := io.ReadAll(getResp.Body)
require.NoError(t, err)
var getOut lambdapkg.GetFunctionOutput
- require.NoError(t, json.Unmarshal(getBody, &getOut))
+ require.NoError(t, json.Unmarshal(getBody, &getOut, jsonx.V1()))
assert.Equal(t, lambdapkg.PackageTypeZip, getOut.Configuration.PackageType)
require.NotNil(t, getOut.Code)
assert.Equal(t, "S3", getOut.Code.RepositoryType) // Zip locations use S3 repository type
@@ -120,7 +121,7 @@ func TestIntegration_Lambda_Zip_CRUD(t *testing.T) {
}
listBody, err := io.ReadAll(listResp.Body)
require.NoError(t, err)
- require.NoError(t, json.Unmarshal(listBody, &listOut))
+ require.NoError(t, json.Unmarshal(listBody, &listOut, jsonx.V1()))
require.Len(t, listOut.Functions, 1)
assert.Equal(t, "zip-crud-fn", listOut.Functions[0].FunctionName)
@@ -128,7 +129,7 @@ func TestIntegration_Lambda_Zip_CRUD(t *testing.T) {
newZipB64 := makeZipPayload(t, "index.py", `def handler(event, context): return "v2"`)
updateCodeBody, err := json.Marshal(map[string]any{
"ZipFile": newZipB64,
- })
+ }, jsonx.V1())
require.NoError(t, err)
updateCodeResp, err := doLambdaRequest(ctx, http.MethodPut,
server.URL+"/2015-03-31/functions/zip-crud-fn/code", "application/json",
@@ -142,7 +143,7 @@ func TestIntegration_Lambda_Zip_CRUD(t *testing.T) {
"Runtime": "python3.13",
"Handler": "main.handler",
"Description": "updated",
- })
+ }, jsonx.V1())
require.NoError(t, err)
updateCfgResp, err := doLambdaRequest(ctx, http.MethodPut,
server.URL+"/2015-03-31/functions/zip-crud-fn/configuration", "application/json",
@@ -154,7 +155,7 @@ func TestIntegration_Lambda_Zip_CRUD(t *testing.T) {
var updatedCfg lambdapkg.FunctionConfiguration
cfgBody, err := io.ReadAll(updateCfgResp.Body)
require.NoError(t, err)
- require.NoError(t, json.Unmarshal(cfgBody, &updatedCfg))
+ require.NoError(t, json.Unmarshal(cfgBody, &updatedCfg, jsonx.V1()))
assert.Equal(t, "python3.13", updatedCfg.Runtime)
assert.Equal(t, "main.handler", updatedCfg.Handler)
@@ -235,7 +236,7 @@ func TestIntegration_Lambda_Zip_ValidationErrors(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- bodyJSON, err := json.Marshal(tt.body)
+ bodyJSON, err := json.Marshal(tt.body, jsonx.V1())
require.NoError(t, err)
resp, err := doLambdaRequest(ctx, http.MethodPost,
@@ -278,7 +279,7 @@ func TestIntegration_Lambda_Zip_S3Code(t *testing.T) {
"S3Key": "functions/my-fn.zip",
},
"Role": "arn:aws:iam::000000000000:role/test",
- })
+ }, jsonx.V1())
require.NoError(t, err)
createResp, err := doLambdaRequest(ctx, http.MethodPost,
@@ -297,7 +298,7 @@ func TestIntegration_Lambda_Zip_S3Code(t *testing.T) {
getBody, err := io.ReadAll(getResp.Body)
require.NoError(t, err)
var getOut lambdapkg.GetFunctionOutput
- require.NoError(t, json.Unmarshal(getBody, &getOut))
+ require.NoError(t, json.Unmarshal(getBody, &getOut, jsonx.V1()))
assert.Equal(t, "nodejs20.x", getOut.Configuration.Runtime)
require.NotNil(t, getOut.Code)
assert.Equal(t, "S3", getOut.Code.RepositoryType)
diff --git a/test/integration/opensearch_test.go b/test/integration/opensearch_test.go
index 0031681007..dbd84d42ab 100644
--- a/test/integration/opensearch_test.go
+++ b/test/integration/opensearch_test.go
@@ -2,7 +2,8 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"fmt"
"io"
"net/http"
@@ -11,6 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -23,7 +25,7 @@ func doOpenSearchRequest(t *testing.T, method, path string, body any) (int, map[
var reqBody io.Reader
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
reqBody = bytes.NewReader(b)
@@ -43,7 +45,7 @@ func doOpenSearchRequest(t *testing.T, method, path string, body any) (int, map[
var result map[string]any
- if err = json.NewDecoder(resp.Body).Decode(&result); err != nil {
+ if err = json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &result, jsonx.V1()); err != nil {
result = nil
}
diff --git a/test/integration/pipes_sqs_lambda_test.go b/test/integration/pipes_sqs_lambda_test.go
index 2c409e1c84..0c0319095d 100644
--- a/test/integration/pipes_sqs_lambda_test.go
+++ b/test/integration/pipes_sqs_lambda_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"log/slog"
"net/http/httptest"
"strings"
@@ -21,6 +21,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
"github.com/blackbirdworks/gopherstack/pkgs/service"
pipespkg "github.com/blackbirdworks/gopherstack/services/pipes"
@@ -219,7 +220,7 @@ func TestIntegration_Pipes_SQS_To_Lambda(t *testing.T) {
EventSource string `json:"eventSource"`
} `json:"Records"`
}
- require.NoError(t, json.Unmarshal(payload, &event))
+ require.NoError(t, json.Unmarshal(payload, &event, jsonx.V1()))
assert.NotEmpty(t, event.Records, "payload should have at least one record")
assert.Equal(t, "aws:sqs", event.Records[0].EventSource)
diff --git a/test/integration/pipes_test.go b/test/integration/pipes_test.go
index badca5b3eb..e4cfe31c4a 100644
--- a/test/integration/pipes_test.go
+++ b/test/integration/pipes_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -20,7 +21,7 @@ func pipesRequest(t *testing.T, method, path string, body any) *http.Response {
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
diff --git a/test/integration/ram_test.go b/test/integration/ram_test.go
index 2c311260e9..18a47025d1 100644
--- a/test/integration/ram_test.go
+++ b/test/integration/ram_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -20,7 +21,7 @@ func ramRequest(t *testing.T, method, path string, body any) *http.Response {
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -91,7 +92,7 @@ func TestIntegration_RAM_UpdateResourceShare(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.StatusCode, "create body: %s", createBody)
var createResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createBody), &createResult))
+ require.NoError(t, json.Unmarshal([]byte(createBody), &createResult, jsonx.V1()))
shareARN := createResult["resourceShare"].(map[string]any)["resourceShareArn"].(string)
@@ -116,7 +117,7 @@ func TestIntegration_RAM_DeleteResourceShare(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.StatusCode, "create body: %s", createBody)
var createResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createBody), &createResult))
+ require.NoError(t, json.Unmarshal([]byte(createBody), &createResult, jsonx.V1()))
shareARN := createResult["resourceShare"].(map[string]any)["resourceShareArn"].(string)
@@ -139,7 +140,7 @@ func TestIntegration_RAM_AssociateResourceShare(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.StatusCode)
var createResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createBody), &createResult))
+ require.NoError(t, json.Unmarshal([]byte(createBody), &createResult, jsonx.V1()))
shareARN := createResult["resourceShare"].(map[string]any)["resourceShareArn"].(string)
@@ -164,7 +165,7 @@ func TestIntegration_RAM_TagResource(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.StatusCode)
var createResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createBody), &createResult))
+ require.NoError(t, json.Unmarshal([]byte(createBody), &createResult, jsonx.V1()))
shareARN := createResult["resourceShare"].(map[string]any)["resourceShareArn"].(string)
diff --git a/test/integration/rdsdata_test.go b/test/integration/rdsdata_test.go
index 6e064344df..da5dc8d7e2 100644
--- a/test/integration/rdsdata_test.go
+++ b/test/integration/rdsdata_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -20,7 +21,7 @@ func rdsdataRequest(t *testing.T, path string, body any) *http.Response {
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -96,7 +97,7 @@ func TestIntegration_RDSData_TransactionLifecycle(t *testing.T) {
// Parse the transaction ID.
var beginResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(beginBody), &beginResult))
+ require.NoError(t, json.Unmarshal([]byte(beginBody), &beginResult, jsonx.V1()))
txID, ok := beginResult["transactionId"].(string)
require.True(t, ok, "transactionId should be a string")
require.NotEmpty(t, txID)
@@ -135,7 +136,7 @@ func TestIntegration_RDSData_RollbackTransaction(t *testing.T) {
require.Equal(t, http.StatusOK, beginResp.StatusCode, "body: %s", beginBody)
var beginResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(beginBody), &beginResult))
+ require.NoError(t, json.Unmarshal([]byte(beginBody), &beginResult, jsonx.V1()))
txID, ok := beginResult["transactionId"].(string)
require.True(t, ok)
require.NotEmpty(t, txID)
diff --git a/test/integration/redshiftdata_test.go b/test/integration/redshiftdata_test.go
index 8bac0ad4ab..cd0a36967d 100644
--- a/test/integration/redshiftdata_test.go
+++ b/test/integration/redshiftdata_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -20,7 +21,7 @@ func redshiftDataRequest(t *testing.T, target string, body any) *http.Response {
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -62,7 +63,7 @@ func TestIntegration_RedshiftData_ExecuteStatement(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode, "body: %s", body)
var respObj map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &respObj))
+ require.NoError(t, json.Unmarshal([]byte(body), &respObj, jsonx.V1()))
assert.NotEmpty(t, respObj["Id"], "expected statement ID in response")
}
@@ -79,7 +80,7 @@ func TestIntegration_RedshiftData_DescribeStatement(t *testing.T) {
require.Equal(t, http.StatusOK, execResp.StatusCode, "body: %s", execBody)
var execObj map[string]any
- require.NoError(t, json.Unmarshal([]byte(execBody), &execObj))
+ require.NoError(t, json.Unmarshal([]byte(execBody), &execObj, jsonx.V1()))
id := execObj["Id"].(string)
@@ -89,7 +90,7 @@ func TestIntegration_RedshiftData_DescribeStatement(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode, "body: %s", body)
var descObj map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &descObj))
+ require.NoError(t, json.Unmarshal([]byte(body), &descObj, jsonx.V1()))
assert.Equal(t, id, descObj["Id"])
assert.Equal(t, "FINISHED", descObj["Status"])
}
@@ -107,7 +108,7 @@ func TestIntegration_RedshiftData_GetStatementResult(t *testing.T) {
require.Equal(t, http.StatusOK, execResp.StatusCode, "body: %s", execBody)
var execObj map[string]any
- require.NoError(t, json.Unmarshal([]byte(execBody), &execObj))
+ require.NoError(t, json.Unmarshal([]byte(execBody), &execObj, jsonx.V1()))
id := execObj["Id"].(string)
@@ -149,7 +150,7 @@ func TestIntegration_RedshiftData_BatchExecuteStatement(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode, "body: %s", body)
var respObj map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &respObj))
+ require.NoError(t, json.Unmarshal([]byte(body), &respObj, jsonx.V1()))
assert.NotEmpty(t, respObj["Id"])
}
diff --git a/test/integration/resourcegroups_test.go b/test/integration/resourcegroups_test.go
index 6dea026c60..a5d485cf53 100644
--- a/test/integration/resourcegroups_test.go
+++ b/test/integration/resourcegroups_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -10,13 +10,14 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
func resourceGroupsPost(t *testing.T, action string, body any) *http.Response {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, endpoint, bytes.NewReader(bodyBytes))
diff --git a/test/integration/resourcegroupstaggingapi_test.go b/test/integration/resourcegroupstaggingapi_test.go
index ef2941bd90..f3d41d1862 100644
--- a/test/integration/resourcegroupstaggingapi_test.go
+++ b/test/integration/resourcegroupstaggingapi_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -21,7 +22,7 @@ import (
func taggingAPIPost(t *testing.T, action string, body any) *http.Response {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, endpoint, bytes.NewReader(bodyBytes))
@@ -115,7 +116,7 @@ func TestIntegration_TaggingAPI_TagResources(t *testing.T) {
// Decode the response and assert no failures.
var tagOut map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &tagOut))
+ require.NoError(t, json.Unmarshal([]byte(body), &tagOut, jsonx.V1()))
assert.Empty(t, tagOut["FailedResourcesMap"], "expected no tag failures")
// Verify via GetResources that the tagged queue appears when filtering by tag.
diff --git a/test/integration/route53resolver_test.go b/test/integration/route53resolver_test.go
index 182c4bb681..48b50ebc55 100644
--- a/test/integration/route53resolver_test.go
+++ b/test/integration/route53resolver_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -10,13 +10,14 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
func route53resolverPost(t *testing.T, action string, body any) *http.Response {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, endpoint, bytes.NewReader(bodyBytes))
@@ -91,7 +92,7 @@ func TestIntegration_Route53Resolver_GetEndpoint(t *testing.T) {
var createBody map[string]any
createData, _ := io.ReadAll(createResp.Body)
createResp.Body.Close()
- require.NoError(t, json.Unmarshal(createData, &createBody))
+ require.NoError(t, json.Unmarshal(createData, &createBody, jsonx.V1()))
ep, _ := createBody["ResolverEndpoint"].(map[string]any)
id, _ := ep["Id"].(string)
@@ -120,7 +121,7 @@ func TestIntegration_Route53Resolver_DeleteEndpoint(t *testing.T) {
var createBody map[string]any
createData, _ := io.ReadAll(createResp.Body)
createResp.Body.Close()
- require.NoError(t, json.Unmarshal(createData, &createBody))
+ require.NoError(t, json.Unmarshal(createData, &createBody, jsonx.V1()))
ep, _ := createBody["ResolverEndpoint"].(map[string]any)
id, _ := ep["Id"].(string)
@@ -179,7 +180,7 @@ func TestIntegration_Route53Resolver_DeleteRule(t *testing.T) {
var createBody map[string]any
createData, _ := io.ReadAll(createResp.Body)
createResp.Body.Close()
- require.NoError(t, json.Unmarshal(createData, &createBody))
+ require.NoError(t, json.Unmarshal(createData, &createBody, jsonx.V1()))
rule, _ := createBody["ResolverRule"].(map[string]any)
id, _ := rule["Id"].(string)
@@ -206,7 +207,7 @@ func TestIntegration_Route53Resolver_GetRule(t *testing.T) {
var createBody map[string]any
createData, _ := io.ReadAll(createResp.Body)
createResp.Body.Close()
- require.NoError(t, json.Unmarshal(createData, &createBody))
+ require.NoError(t, json.Unmarshal(createData, &createBody, jsonx.V1()))
rule, _ := createBody["ResolverRule"].(map[string]any)
id, _ := rule["Id"].(string)
diff --git a/test/integration/s3_eventbridge_test.go b/test/integration/s3_eventbridge_test.go
index 0e2cbd2738..4349ab6718 100644
--- a/test/integration/s3_eventbridge_test.go
+++ b/test/integration/s3_eventbridge_test.go
@@ -1,7 +1,8 @@
package integration_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strings"
"testing"
"time"
@@ -16,6 +17,8 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestIntegration_S3_NotificationToEventBridge verifies that when EventBridgeConfiguration
@@ -144,37 +147,37 @@ func TestIntegration_S3_NotificationToEventBridge(t *testing.T) {
}, 10*time.Second, 500*time.Millisecond, "should receive EventBridge event in SQS within 10s")
// Validate the EventBridge event envelope.
- var event map[string]json.RawMessage
- require.NoError(t, json.Unmarshal([]byte(receivedMsg), &event), "event must be valid JSON")
+ var event map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal([]byte(receivedMsg), &event, jsonx.V1()), "event must be valid JSON")
var source string
- require.NoError(t, json.Unmarshal(event["source"], &source))
+ require.NoError(t, json.Unmarshal(event["source"], &source, jsonx.V1()))
assert.Equal(t, "aws.s3", source)
var detailType string
- require.NoError(t, json.Unmarshal(event["detail-type"], &detailType))
+ require.NoError(t, json.Unmarshal(event["detail-type"], &detailType, jsonx.V1()))
assert.Equal(t, "Object Created", detailType)
// Validate the event detail.
- var detail map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(event["detail"], &detail))
+ var detail map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(event["detail"], &detail, jsonx.V1()))
- var bucketDetail map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(detail["bucket"], &bucketDetail))
+ var bucketDetail map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(detail["bucket"], &bucketDetail, jsonx.V1()))
var bucketName string
- require.NoError(t, json.Unmarshal(bucketDetail["name"], &bucketName))
+ require.NoError(t, json.Unmarshal(bucketDetail["name"], &bucketName, jsonx.V1()))
assert.Equal(t, bucket, bucketName)
- var objectDetail map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(detail["object"], &objectDetail))
+ var objectDetail map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(detail["object"], &objectDetail, jsonx.V1()))
var key string
- require.NoError(t, json.Unmarshal(objectDetail["key"], &key))
+ require.NoError(t, json.Unmarshal(objectDetail["key"], &key, jsonx.V1()))
assert.Equal(t, objectKey, key)
var reason string
- require.NoError(t, json.Unmarshal(detail["reason"], &reason))
+ require.NoError(t, json.Unmarshal(detail["reason"], &reason, jsonx.V1()))
assert.Equal(t, "PutObject", reason)
t.Logf("S3→EventBridge event delivered: source=%s detail-type=%s bucket=%s key=%s reason=%s",
diff --git a/test/integration/s3_notification_test.go b/test/integration/s3_notification_test.go
index 3ce2a51233..887d6cf38a 100644
--- a/test/integration/s3_notification_test.go
+++ b/test/integration/s3_notification_test.go
@@ -1,7 +1,8 @@
package integration_test
import (
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"strings"
"testing"
"time"
@@ -14,6 +15,8 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// TestIntegration_S3_NotificationToSQS verifies that S3 bucket notifications
@@ -117,41 +120,45 @@ func TestIntegration_S3_NotificationToSQS(t *testing.T) {
}, 10*time.Second, 500*time.Millisecond, "should receive SQS notification within 10s")
// Validate the notification payload format.
- var payload map[string]json.RawMessage
- require.NoError(t, json.Unmarshal([]byte(receivedMsg), &payload), "notification body must be valid JSON")
+ var payload map[string]jsontext.Value
+ require.NoError(
+ t,
+ json.Unmarshal([]byte(receivedMsg), &payload, jsonx.V1()),
+ "notification body must be valid JSON",
+ )
records, ok := payload["Records"]
require.True(t, ok, "notification must contain a Records array")
- var recordList []map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(records, &recordList))
+ var recordList []map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(records, &recordList, jsonx.V1()))
require.NotEmpty(t, recordList)
record := recordList[0]
var eventSource string
- require.NoError(t, json.Unmarshal(record["eventSource"], &eventSource))
+ require.NoError(t, json.Unmarshal(record["eventSource"], &eventSource, jsonx.V1()))
assert.Equal(t, "aws:s3", eventSource)
var eventName string
- require.NoError(t, json.Unmarshal(record["eventName"], &eventName))
+ require.NoError(t, json.Unmarshal(record["eventName"], &eventName, jsonx.V1()))
assert.True(t, strings.HasPrefix(eventName, "s3:ObjectCreated:"), "eventName should be ObjectCreated")
- var s3Block map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(record["s3"], &s3Block))
+ var s3Block map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(record["s3"], &s3Block, jsonx.V1()))
- var bucketBlock map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(s3Block["bucket"], &bucketBlock))
+ var bucketBlock map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(s3Block["bucket"], &bucketBlock, jsonx.V1()))
var bucketName string
- require.NoError(t, json.Unmarshal(bucketBlock["name"], &bucketName))
+ require.NoError(t, json.Unmarshal(bucketBlock["name"], &bucketName, jsonx.V1()))
assert.Equal(t, bucket, bucketName)
- var objectBlock map[string]json.RawMessage
- require.NoError(t, json.Unmarshal(s3Block["object"], &objectBlock))
+ var objectBlock map[string]jsontext.Value
+ require.NoError(t, json.Unmarshal(s3Block["object"], &objectBlock, jsonx.V1()))
var key string
- require.NoError(t, json.Unmarshal(objectBlock["key"], &key))
+ require.NoError(t, json.Unmarshal(objectBlock["key"], &key, jsonx.V1()))
assert.Equal(t, objectKey, key)
t.Logf("S3 notification delivered: eventName=%s bucket=%s key=%s", eventName, bucketName, key)
diff --git a/test/integration/scheduler_test.go b/test/integration/scheduler_test.go
index 1a1c5a58d0..b4ac70736e 100644
--- a/test/integration/scheduler_test.go
+++ b/test/integration/scheduler_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -10,13 +10,14 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
func schedulerPost(t *testing.T, action string, body any) *http.Response {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, endpoint, bytes.NewReader(bodyBytes))
diff --git a/test/integration/secretsmanager_rotation_test.go b/test/integration/secretsmanager_rotation_test.go
index 2c694c4496..b2d016e017 100644
--- a/test/integration/secretsmanager_rotation_test.go
+++ b/test/integration/secretsmanager_rotation_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"context"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net"
"net/http/httptest"
@@ -23,6 +23,7 @@ import (
dockerapibuild "github.com/blackbirdworks/gopherstack/internal/dockercompat/api/types/build"
dockerclient "github.com/blackbirdworks/gopherstack/internal/dockercompat/client"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/container"
"github.com/blackbirdworks/gopherstack/pkgs/logger"
@@ -384,7 +385,7 @@ func buildRotationLambdaImage(ctx context.Context, t *testing.T) {
Error string `json:"error"`
}
- if jsonErr := json.Unmarshal([]byte(line), &event); jsonErr == nil && event.Error != "" {
+ if jsonErr := json.Unmarshal([]byte(line), &event, jsonx.V1()); jsonErr == nil && event.Error != "" {
buildErrorMsg = event.Error
break
diff --git a/test/integration/servicediscovery_test.go b/test/integration/servicediscovery_test.go
index 129815659c..51fcb962fd 100644
--- a/test/integration/servicediscovery_test.go
+++ b/test/integration/servicediscovery_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -20,7 +21,7 @@ func servicediscoveryRequest(t *testing.T, op string, body any) *http.Response {
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -71,7 +72,7 @@ func TestIntegration_ServiceDiscovery_NamespaceLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.StatusCode, "body: %s", createBody)
var createResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createBody), &createResult))
+ require.NoError(t, json.Unmarshal([]byte(createBody), &createResult, jsonx.V1()))
opID, ok := createResult["OperationId"].(string)
require.True(t, ok)
require.NotEmpty(t, opID)
@@ -82,7 +83,7 @@ func TestIntegration_ServiceDiscovery_NamespaceLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, opResp.StatusCode, "body: %s", opBody)
var opResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(opBody), &opResult))
+ require.NoError(t, json.Unmarshal([]byte(opBody), &opResult, jsonx.V1()))
operation := opResult["Operation"].(map[string]any)
targets := operation["Targets"].(map[string]any)
nsID, ok := targets["NAMESPACE"].(string)
@@ -114,7 +115,7 @@ func TestIntegration_ServiceDiscovery_ServiceLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.StatusCode, "body: %s", createBody)
var createResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createBody), &createResult))
+ require.NoError(t, json.Unmarshal([]byte(createBody), &createResult, jsonx.V1()))
svcData := createResult["Service"].(map[string]any)
svcID, ok := svcData["Id"].(string)
require.True(t, ok)
@@ -162,14 +163,14 @@ func TestIntegration_ServiceDiscovery_UpdateHttpNamespace(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.StatusCode, "body: %s", createBody)
var createResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createBody), &createResult))
+ require.NoError(t, json.Unmarshal([]byte(createBody), &createResult, jsonx.V1()))
opID := createResult["OperationId"].(string)
// Get the operation to find the namespace ID.
opResp := servicediscoveryRequest(t, "GetOperation", map[string]any{"OperationId": opID})
opBody := servicediscoveryReadBody(t, opResp)
var opResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(opBody), &opResult))
+ require.NoError(t, json.Unmarshal([]byte(opBody), &opResult, jsonx.V1()))
nsID := opResult["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
// Update the namespace.
@@ -193,13 +194,13 @@ func TestIntegration_ServiceDiscovery_UpdatePrivateDnsNamespace(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.StatusCode, "body: %s", createBody)
var createResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createBody), &createResult))
+ require.NoError(t, json.Unmarshal([]byte(createBody), &createResult, jsonx.V1()))
opID := createResult["OperationId"].(string)
opResp := servicediscoveryRequest(t, "GetOperation", map[string]any{"OperationId": opID})
opBody := servicediscoveryReadBody(t, opResp)
var opResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(opBody), &opResult))
+ require.NoError(t, json.Unmarshal([]byte(opBody), &opResult, jsonx.V1()))
nsID := opResult["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
updateResp := servicediscoveryRequest(t, "UpdatePrivateDnsNamespace", map[string]any{
@@ -222,13 +223,13 @@ func TestIntegration_ServiceDiscovery_UpdatePublicDnsNamespace(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.StatusCode, "body: %s", createBody)
var createResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createBody), &createResult))
+ require.NoError(t, json.Unmarshal([]byte(createBody), &createResult, jsonx.V1()))
opID := createResult["OperationId"].(string)
opResp := servicediscoveryRequest(t, "GetOperation", map[string]any{"OperationId": opID})
opBody := servicediscoveryReadBody(t, opResp)
var opResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(opBody), &opResult))
+ require.NoError(t, json.Unmarshal([]byte(opBody), &opResult, jsonx.V1()))
nsID := opResult["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
updateResp := servicediscoveryRequest(t, "UpdatePublicDnsNamespace", map[string]any{
@@ -251,7 +252,7 @@ func TestIntegration_ServiceDiscovery_UpdateService(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.StatusCode, "body: %s", createBody)
var createResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createBody), &createResult))
+ require.NoError(t, json.Unmarshal([]byte(createBody), &createResult, jsonx.V1()))
svcID := createResult["Service"].(map[string]any)["Id"].(string)
updateResp := servicediscoveryRequest(t, "UpdateService", map[string]any{
@@ -281,7 +282,7 @@ func TestIntegration_ServiceDiscovery_ServiceAttributesLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.StatusCode, "body: %s", createBody)
var createResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createBody), &createResult))
+ require.NoError(t, json.Unmarshal([]byte(createBody), &createResult, jsonx.V1()))
svcID := createResult["Service"].(map[string]any)["Id"].(string)
// UpdateServiceAttributes.
@@ -332,7 +333,7 @@ func TestIntegration_ServiceDiscovery_UpdateInstanceCustomHealthStatus(t *testin
require.Equal(t, http.StatusOK, createResp.StatusCode, "body: %s", createBody)
var createResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createBody), &createResult))
+ require.NoError(t, json.Unmarshal([]byte(createBody), &createResult, jsonx.V1()))
svcID := createResult["Service"].(map[string]any)["Id"].(string)
regResp := servicediscoveryRequest(t, "RegisterInstance", map[string]any{
@@ -374,13 +375,13 @@ func TestIntegration_ServiceDiscovery_DiscoverInstancesRevision(t *testing.T) {
require.Equal(t, http.StatusOK, createNsResp.StatusCode, "body: %s", createNsBody)
var createNsResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createNsBody), &createNsResult))
+ require.NoError(t, json.Unmarshal([]byte(createNsBody), &createNsResult, jsonx.V1()))
opID := createNsResult["OperationId"].(string)
opResp := servicediscoveryRequest(t, "GetOperation", map[string]any{"OperationId": opID})
opBody := servicediscoveryReadBody(t, opResp)
var opResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(opBody), &opResult))
+ require.NoError(t, json.Unmarshal([]byte(opBody), &opResult, jsonx.V1()))
nsID := opResult["Operation"].(map[string]any)["Targets"].(map[string]any)["NAMESPACE"].(string)
createSvcResp := servicediscoveryRequest(t, "CreateService", map[string]any{
diff --git a/test/integration/sesv2_test.go b/test/integration/sesv2_test.go
index 8cb337a866..56df2741dd 100644
--- a/test/integration/sesv2_test.go
+++ b/test/integration/sesv2_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"net/url"
@@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -21,7 +22,7 @@ func sesv2Do(t *testing.T, method, path string, body any) *http.Response {
var bodyReader io.Reader
if body != nil {
- b, err := json.Marshal(body)
+ b, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
bodyReader = bytes.NewReader(b)
@@ -62,7 +63,7 @@ func TestIntegration_SESv2_CreateEmailIdentity(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode, "body: %s", body)
var out map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
assert.Equal(t, "EMAIL_ADDRESS", out["IdentityType"])
assert.Equal(t, true, out["VerifiedForSendingStatus"])
}
@@ -83,7 +84,7 @@ func TestIntegration_SESv2_GetEmailIdentity(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode, "body: %s", body)
var out map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
assert.Equal(t, "EMAIL_ADDRESS", out["IdentityType"])
}
@@ -105,7 +106,7 @@ func TestIntegration_SESv2_ListEmailIdentities(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode, "body: %s", body)
var out map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
identities, ok := out["EmailIdentities"].([]any)
require.True(t, ok)
@@ -165,7 +166,7 @@ func TestIntegration_SESv2_SendEmail(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode, "body: %s", body)
var out map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
assert.NotEmpty(t, out["MessageId"])
}
@@ -197,7 +198,7 @@ func TestIntegration_SESv2_GetConfigurationSet(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode, "body: %s", body)
var out map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
assert.Equal(t, name, out["ConfigurationSetName"])
}
@@ -218,7 +219,7 @@ func TestIntegration_SESv2_ListConfigurationSets(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode, "body: %s", body)
var out map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
sets, ok := out["ConfigurationSets"].([]any)
require.True(t, ok)
@@ -267,7 +268,7 @@ func TestIntegration_SESv2_URLEncodedIdentity(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode, "GET with percent-encoded identity should succeed; body: %s", body)
var out map[string]any
- require.NoError(t, json.Unmarshal([]byte(body), &out))
+ require.NoError(t, json.Unmarshal([]byte(body), &out, jsonx.V1()))
assert.Equal(t, "EMAIL_ADDRESS", out["IdentityType"])
// DELETE with percent-encoded path.
diff --git a/test/integration/sns_roundtrip_test.go b/test/integration/sns_roundtrip_test.go
index 0a13441dcf..de8a8d75ee 100644
--- a/test/integration/sns_roundtrip_test.go
+++ b/test/integration/sns_roundtrip_test.go
@@ -1,7 +1,7 @@
package integration_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -12,6 +12,8 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// snsEnvelope is the JSON body SNS delivers to a subscribed SQS queue
@@ -64,7 +66,7 @@ func TestIntegration_SNS_AddRemovePermissionRoundTrip(t *testing.T) {
require.NoError(t, err)
var policy snsPolicyDocument
- require.NoError(t, json.Unmarshal([]byte(attrsOut.Attributes["Policy"]), &policy))
+ require.NoError(t, json.Unmarshal([]byte(attrsOut.Attributes["Policy"]), &policy, jsonx.V1()))
var granted *snsPolicyStatement
@@ -90,7 +92,7 @@ func TestIntegration_SNS_AddRemovePermissionRoundTrip(t *testing.T) {
require.NoError(t, err)
var policyAfter snsPolicyDocument
- require.NoError(t, json.Unmarshal([]byte(attrsOut2.Attributes["Policy"]), &policyAfter))
+ require.NoError(t, json.Unmarshal([]byte(attrsOut2.Attributes["Policy"]), &policyAfter, jsonx.V1()))
for _, stmt := range policyAfter.Statement {
assert.NotEqual(t, label, stmt.Sid, "RemovePermission should drop the statement from Policy")
@@ -244,7 +246,7 @@ func TestIntegration_SNS_PublishBatchRoundTrip(t *testing.T) {
for _, msg := range recvOut.Messages {
var env snsEnvelope
- require.NoError(t, json.Unmarshal([]byte(aws.ToString(msg.Body)), &env))
+ require.NoError(t, json.Unmarshal([]byte(aws.ToString(msg.Body)), &env, jsonx.V1()))
assert.Equal(t, topicARN, env.TopicArn)
priority := ""
diff --git a/test/integration/sns_sqs_cross_test.go b/test/integration/sns_sqs_cross_test.go
index 9a42496ac2..ba08b9eb91 100644
--- a/test/integration/sns_sqs_cross_test.go
+++ b/test/integration/sns_sqs_cross_test.go
@@ -1,7 +1,7 @@
package integration_test
import (
- "encoding/json"
+ "encoding/json/v2"
"testing"
"github.com/aws/aws-sdk-go-v2/aws"
@@ -12,6 +12,8 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
func TestIntegration_SNS_SQS_CrossService(t *testing.T) {
@@ -71,7 +73,7 @@ func TestIntegration_SNS_SQS_CrossService(t *testing.T) {
// Message body should be SNS envelope JSON
var envelope map[string]any
- require.NoError(t, json.Unmarshal([]byte(*recvOut.Messages[0].Body), &envelope))
+ require.NoError(t, json.Unmarshal([]byte(*recvOut.Messages[0].Body), &envelope, jsonx.V1()))
assert.Equal(t, "Notification", envelope["Type"])
// Cleanup
@@ -286,7 +288,7 @@ func TestIntegration_SNS_SQS_RedrivePolicy(t *testing.T) {
// DLQ receives the same body that was attempted to the failed queue.
// Without RawMessageDelivery=true, that body is the SNS envelope JSON.
var dlqEnvelope map[string]any
- require.NoError(t, json.Unmarshal([]byte(*recvOut.Messages[0].Body), &dlqEnvelope))
+ require.NoError(t, json.Unmarshal([]byte(*recvOut.Messages[0].Body), &dlqEnvelope, jsonx.V1()))
assert.Equal(t, "Notification", dlqEnvelope["Type"])
assert.Equal(t, msgBody, dlqEnvelope["Message"])
diff --git a/test/integration/stepfunctions_asl_test.go b/test/integration/stepfunctions_asl_test.go
index a511cc442b..b33b479ff1 100644
--- a/test/integration/stepfunctions_asl_test.go
+++ b/test/integration/stepfunctions_asl_test.go
@@ -1,7 +1,7 @@
package integration_test
import (
- "encoding/json"
+ "encoding/json/v2"
"fmt"
"testing"
"time"
@@ -12,6 +12,8 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
// passThroughDefinition is a minimal ASL state machine that passes input through.
@@ -335,7 +337,7 @@ func TestIntegration_StepFunctions_FullExecution(t *testing.T) {
assert.Equal(t, "SUCCEEDED", string(descOut.Status))
var output map[string]any
- require.NoError(t, json.Unmarshal([]byte(aws.ToString(descOut.Output)), &output))
+ require.NoError(t, json.Unmarshal([]byte(aws.ToString(descOut.Output)), &output, jsonx.V1()))
require.Contains(t, output, "original", "output should contain the original input field")
assert.Equal(t, "data", output["original"])
meta, ok := output["meta"].(map[string]any)
diff --git a/test/integration/swf_test.go b/test/integration/swf_test.go
index 9301f160ea..f5679ccf73 100644
--- a/test/integration/swf_test.go
+++ b/test/integration/swf_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -10,13 +10,14 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
func swfPost(t *testing.T, action string, body any) *http.Response {
t.Helper()
- bodyBytes, err := json.Marshal(body)
+ bodyBytes, err := json.Marshal(body, jsonx.V1())
require.NoError(t, err)
req, err := http.NewRequestWithContext(t.Context(), http.MethodPost, endpoint, bytes.NewReader(bodyBytes))
diff --git a/test/integration/verifiedpermissions_test.go b/test/integration/verifiedpermissions_test.go
index 26f7cabf2d..31bbc1f750 100644
--- a/test/integration/verifiedpermissions_test.go
+++ b/test/integration/verifiedpermissions_test.go
@@ -2,7 +2,7 @@ package integration_test
import (
"bytes"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"testing"
@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -20,7 +21,7 @@ func verifiedPermissionsPost(t *testing.T, action string, body any) *http.Respon
if body != nil {
var err error
- bodyBytes, err = json.Marshal(body)
+ bodyBytes, err = json.Marshal(body, jsonx.V1())
require.NoError(t, err)
}
@@ -72,7 +73,7 @@ func TestIntegration_VerifiedPermissions_PolicyStoreLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, createResp.StatusCode, "create body: %s", createBody)
var createResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createBody), &createResult))
+ require.NoError(t, json.Unmarshal([]byte(createBody), &createResult, jsonx.V1()))
storeID, ok := createResult["policyStoreId"].(string)
require.True(t, ok, "policyStoreId should be a string")
require.NotEmpty(t, storeID)
@@ -127,7 +128,7 @@ func TestIntegration_VerifiedPermissions_PolicyLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, createStoreResp.StatusCode, "create store body: %s", createStoreBody)
var storeResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createStoreBody), &storeResult))
+ require.NoError(t, json.Unmarshal([]byte(createStoreBody), &storeResult, jsonx.V1()))
storeID := storeResult["policyStoreId"].(string)
// Create a policy.
@@ -143,7 +144,7 @@ func TestIntegration_VerifiedPermissions_PolicyLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, createPolicyResp.StatusCode, "create policy body: %s", createPolicyBody)
var policyResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createPolicyBody), &policyResult))
+ require.NoError(t, json.Unmarshal([]byte(createPolicyBody), &policyResult, jsonx.V1()))
policyID := policyResult["policyId"].(string)
require.NotEmpty(t, policyID)
@@ -198,7 +199,7 @@ func TestIntegration_VerifiedPermissions_PolicyTemplateLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, createStoreResp.StatusCode, "create store body: %s", createStoreBody)
var storeResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createStoreBody), &storeResult))
+ require.NoError(t, json.Unmarshal([]byte(createStoreBody), &storeResult, jsonx.V1()))
storeID := storeResult["policyStoreId"].(string)
// Create a policy template.
@@ -211,7 +212,7 @@ func TestIntegration_VerifiedPermissions_PolicyTemplateLifecycle(t *testing.T) {
require.Equal(t, http.StatusOK, createTplResp.StatusCode, "create template body: %s", createTplBody)
var tplResult map[string]any
- require.NoError(t, json.Unmarshal([]byte(createTplBody), &tplResult))
+ require.NoError(t, json.Unmarshal([]byte(createTplBody), &tplResult, jsonx.V1()))
templateID := tplResult["policyTemplateId"].(string)
require.NotEmpty(t, templateID)
diff --git a/test/internal/tofu/download.go b/test/internal/tofu/download.go
index b08d0492f8..74096f6076 100644
--- a/test/internal/tofu/download.go
+++ b/test/internal/tofu/download.go
@@ -8,7 +8,8 @@ import (
"archive/zip"
"bytes"
"context"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"io"
@@ -16,6 +17,8 @@ import (
"os"
"runtime"
"strings"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
var (
@@ -48,7 +51,7 @@ func DownloadBinary(ctx context.Context, logf func(format string, args ...any))
} `json:"versions"`
}
- if err = json.NewDecoder(resp.Body).Decode(&api); err != nil {
+ if err = json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &api, jsonx.V1()); err != nil {
return "", fmt.Errorf("decoding OpenTofu version list: %w", err)
}
diff --git a/test/terraform/azure/cosmosdb_test.go b/test/terraform/azure/cosmosdb_test.go
index de81ca4702..af77bb373d 100644
--- a/test/terraform/azure/cosmosdb_test.go
+++ b/test/terraform/azure/cosmosdb_test.go
@@ -1,7 +1,7 @@
package azure_test
import (
- "encoding/json"
+ "encoding/json/v2"
"os"
"os/exec"
"strings"
@@ -10,6 +10,8 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const azureCosmosFixture = `
@@ -150,7 +152,7 @@ func TestTerraform_Azure_CosmosDB(t *testing.T) {
require.NoError(t, err)
pk := azcosmos.NewPartitionKeyString("p1")
- item, err := json.Marshal(map[string]any{"id": "item1", "pk": "p1", "email": "a@example.com"})
+ item, err := json.Marshal(map[string]any{"id": "item1", "pk": "p1", "email": "a@example.com"}, jsonx.V1())
require.NoError(t, err)
ctx := t.Context()
@@ -182,7 +184,7 @@ func TestTerraform_Azure_CosmosDB(t *testing.T) {
func mustJSON(t *testing.T, v any) []byte {
t.Helper()
- b, err := json.Marshal(v)
+ b, err := json.Marshal(v, jsonx.V1())
require.NoError(t, err)
return b
@@ -192,7 +194,7 @@ func stripSystemFields(t *testing.T, raw []byte) string {
t.Helper()
var m map[string]any
- require.NoError(t, json.Unmarshal(raw, &m))
+ require.NoError(t, json.Unmarshal(raw, &m, jsonx.V1()))
for k := range m {
if strings.HasPrefix(k, "_") {
diff --git a/test/terraform/azure/eventgrid_test.go b/test/terraform/azure/eventgrid_test.go
index 45cf2d2606..76ea1c018a 100644
--- a/test/terraform/azure/eventgrid_test.go
+++ b/test/terraform/azure/eventgrid_test.go
@@ -5,7 +5,7 @@ import (
"context"
"crypto/tls"
"crypto/x509"
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"os"
@@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/azureauth"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
)
const (
@@ -146,7 +147,7 @@ func armCallAPI(ctx context.Context, t *testing.T, c *http.Client, method, path,
var out map[string]any
- require.NoError(t, json.Unmarshal(raw, &out))
+ require.NoError(t, json.Unmarshal(raw, &out, jsonx.V1()))
return out
}
@@ -209,7 +210,7 @@ func verifyEventGridRoundTrip(t *testing.T) {
var got map[string]any
- require.NoError(t, json.Unmarshal([]byte(text), &got))
+ require.NoError(t, json.Unmarshal([]byte(text), &got, jsonx.V1()))
assert.Equal(t, "from-terraform", got["id"])
assert.Equal(t, "App.Thing", got["eventType"])
assert.Contains(t, got["topic"], "Microsoft.EventGrid/topics/"+azureEventGridTopicName)
diff --git a/test/terraform/azure/servicebus_test.go b/test/terraform/azure/servicebus_test.go
index 7d24f16945..5fe5016a7c 100644
--- a/test/terraform/azure/servicebus_test.go
+++ b/test/terraform/azure/servicebus_test.go
@@ -11,7 +11,7 @@
package azure_test
import (
- "encoding/json"
+ "encoding/json/v2"
"io"
"net/http"
"strings"
@@ -20,6 +20,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -162,7 +163,7 @@ func verifyServiceBusQueueRoundTrip(t *testing.T) {
assert.Equal(t, messageBody, string(gotBody))
var bp sbBrokerProperties
- require.NoError(t, json.Unmarshal([]byte(resp.Header.Get("Brokerproperties")), &bp))
+ require.NoError(t, json.Unmarshal([]byte(resp.Header.Get("Brokerproperties")), &bp, jsonx.V1()))
require.NotEmpty(t, bp.LockToken)
location := resp.Header.Get("Location")
diff --git a/test/terraform/terraform_test.go b/test/terraform/terraform_test.go
index 066923c93d..3656410958 100644
--- a/test/terraform/terraform_test.go
+++ b/test/terraform/terraform_test.go
@@ -5,7 +5,8 @@ import (
"bytes"
"context"
"embed"
- "encoding/json"
+ "encoding/json/jsontext"
+ "encoding/json/v2"
"errors"
"fmt"
"hash/fnv"
@@ -152,6 +153,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/blackbirdworks/gopherstack/pkgs/httpclient"
+ "github.com/blackbirdworks/gopherstack/pkgs/jsonx"
"github.com/blackbirdworks/gopherstack/pkgs/testclient"
)
@@ -689,7 +691,7 @@ func downloadTofuBinary(logger *slog.Logger) (string, error) {
ID string `json:"id"`
} `json:"versions"`
}
- err = json.NewDecoder(resp.Body).Decode(&api)
+ err = json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &api, jsonx.V1())
if err != nil {
return "", fmt.Errorf("decoding OpenTofu version list: %w", err)
}
@@ -3558,7 +3560,7 @@ func TestTerraform_SESv2(t *testing.T) {
"email identity %q should exist after terraform apply", email)
var identityOut map[string]any
- require.NoError(t, json.NewDecoder(resp.Body).Decode(&identityOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp.Body), &identityOut, jsonx.V1()))
assert.Equal(t, "EMAIL_ADDRESS", identityOut["IdentityType"])
// Verify configuration set was created.
@@ -3574,7 +3576,7 @@ func TestTerraform_SESv2(t *testing.T) {
"configuration set %q should exist after terraform apply", cfgName)
var cfgOut map[string]any
- require.NoError(t, json.NewDecoder(resp2.Body).Decode(&cfgOut))
+ require.NoError(t, json.UnmarshalDecode(jsontext.NewDecoder(resp2.Body), &cfgOut, jsonx.V1()))
assert.Equal(t, cfgName, cfgOut["ConfigurationSetName"])
},
},
@@ -4706,7 +4708,7 @@ func TestTerraform_CloudControl(t *testing.T) {
for _, rd := range out.ResourceDescriptions {
var props map[string]any
propsJSON := []byte(aws.ToString(rd.Properties))
- if unmarshalErr := json.Unmarshal(propsJSON, &props); unmarshalErr == nil {
+ if unmarshalErr := json.Unmarshal(propsJSON, &props, jsonx.V1()); unmarshalErr == nil {
if props["LogGroupName"] == expectedName {
foundIdentifier = aws.ToString(rd.Identifier)
@@ -6612,7 +6614,7 @@ func TestTerraform_ServerlessRepo(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode, "expected 200 OK, body: %s", string(body))
var result map[string]any
- require.NoError(t, json.Unmarshal(body, &result))
+ require.NoError(t, json.Unmarshal(body, &result, jsonx.V1()))
assert.Equal(t, appName, result["name"])
},
},
@@ -6668,7 +6670,7 @@ func TestTerraform_Shield(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode, "expected 200 OK, body: %s", string(respBody))
var result map[string]string
- require.NoError(t, json.Unmarshal(respBody, &result))
+ require.NoError(t, json.Unmarshal(respBody, &result, jsonx.V1()))
assert.Equal(t, "ACTIVE", result["SubscriptionState"])
},
},
@@ -6786,7 +6788,7 @@ func TestTerraform_Textract(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode, "expected 200 OK, body: %s", string(respBody))
var result map[string]any
- require.NoError(t, json.Unmarshal(respBody, &result))
+ require.NoError(t, json.Unmarshal(respBody, &result, jsonx.V1()))
blocks, ok := result["Blocks"].([]any)
assert.True(t, ok, "expected Blocks in response")
assert.NotEmpty(t, blocks, "expected non-empty Blocks")
@@ -6848,7 +6850,7 @@ func TestTerraform_TimestreamWrite(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode, "expected 200 OK, body: %s", string(respBody))
var result map[string]any
- require.NoError(t, json.Unmarshal(respBody, &result))
+ require.NoError(t, json.Unmarshal(respBody, &result, jsonx.V1()))
db, ok := result["Database"].(map[string]any)
assert.True(t, ok, "expected Database in response")
assert.Equal(t, databaseName, db["DatabaseName"])
@@ -7105,7 +7107,7 @@ func TestTerraform_Wafv2(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode, "expected 200 OK, body: %s", string(respBody))
var result map[string]any
- require.NoError(t, json.Unmarshal(respBody, &result))
+ require.NoError(t, json.Unmarshal(respBody, &result, jsonx.V1()))
webACLs, ok := result["WebACLs"].([]any)
require.True(t, ok)